mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
48 changed files with 769 additions and 176 deletions
@ -0,0 +1,68 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Contents |
|||
{ |
|||
public class TranslationStatus : Dictionary<string, int> |
|||
{ |
|||
public TranslationStatus() |
|||
{ |
|||
} |
|||
|
|||
public TranslationStatus(int capacity) |
|||
: base(capacity) |
|||
{ |
|||
} |
|||
|
|||
public static TranslationStatus Create(ContentData data, Schema schema, LanguagesConfig languages) |
|||
{ |
|||
Guard.NotNull(data); |
|||
Guard.NotNull(schema); |
|||
Guard.NotNull(languages); |
|||
|
|||
var result = new TranslationStatus(languages.Languages.Count); |
|||
|
|||
var localizedFields = schema.Fields.Where(x => x.Partitioning == Partitioning.Language).ToList(); |
|||
|
|||
foreach (var language in languages.AllKeys) |
|||
{ |
|||
var percent = 0; |
|||
|
|||
foreach (var field in localizedFields) |
|||
{ |
|||
if (IsValidValue(data.GetValueOrDefault(field.Name)?.GetValueOrDefault(language))) |
|||
{ |
|||
percent++; |
|||
} |
|||
} |
|||
|
|||
if (localizedFields.Count > 0) |
|||
{ |
|||
percent = (int)Math.Round(100 * (double)percent / localizedFields.Count); |
|||
} |
|||
else |
|||
{ |
|||
percent = 100; |
|||
} |
|||
|
|||
result[language] = percent; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private static bool IsValidValue(JsonValue? value) |
|||
{ |
|||
return value != null && value.Value.Type != JsonValueType.Null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Model.Contents |
|||
{ |
|||
public class TranslationStatusTests |
|||
{ |
|||
private readonly LanguagesConfig languages = LanguagesConfig.English.Set(Language.DE).Set(Language.IT); |
|||
|
|||
[Fact] |
|||
public void Should_create_info_for_empty_schema() |
|||
{ |
|||
var schema = new Schema("my-schema"); |
|||
|
|||
var result = TranslationStatus.Create(new ContentData(), schema, languages); |
|||
|
|||
Assert.Equal(new TranslationStatus |
|||
{ |
|||
[Language.EN] = 100, |
|||
[Language.DE] = 100, |
|||
[Language.IT] = 100 |
|||
}, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_create_info_for_schema_without_localized_field() |
|||
{ |
|||
var schema = |
|||
new Schema("my-schema") |
|||
.AddString(1, "field1", Partitioning.Invariant); |
|||
|
|||
var result = TranslationStatus.Create(new ContentData(), schema, languages); |
|||
|
|||
Assert.Equal(new TranslationStatus |
|||
{ |
|||
[Language.EN] = 100, |
|||
[Language.DE] = 100, |
|||
[Language.IT] = 100 |
|||
}, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_create_info_for_schema_with_localized_field() |
|||
{ |
|||
var schema = |
|||
new Schema("my-schema") |
|||
.AddString(1, "field1", Partitioning.Language); |
|||
|
|||
var result = TranslationStatus.Create(new ContentData(), schema, languages); |
|||
|
|||
Assert.Equal(new TranslationStatus |
|||
{ |
|||
[Language.EN] = 0, |
|||
[Language.DE] = 0, |
|||
[Language.IT] = 0 |
|||
}, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_create_translation_info() |
|||
{ |
|||
var schema = |
|||
new Schema("my-schema") |
|||
.AddString(1, "field1", Partitioning.Language) |
|||
.AddString(2, "field2", Partitioning.Language) |
|||
.AddString(3, "field3", Partitioning.Language) |
|||
.AddString(4, "field4", Partitioning.Invariant); |
|||
|
|||
var data = |
|||
new ContentData() |
|||
.AddField("field1", |
|||
new ContentFieldData() |
|||
.AddLocalized(Language.EN, "en") |
|||
.AddLocalized(Language.DE, "de")) |
|||
.AddField("field2", |
|||
new ContentFieldData() |
|||
.AddLocalized(Language.EN, "en") |
|||
.AddLocalized(Language.DE, "de")) |
|||
.AddField("field3", |
|||
new ContentFieldData() |
|||
.AddLocalized(Language.EN, "en")); |
|||
|
|||
var result = TranslationStatus.Create(data, schema, languages); |
|||
|
|||
Assert.Equal(new TranslationStatus |
|||
{ |
|||
[Language.EN] = 100, |
|||
[Language.DE] = 67, |
|||
[Language.IT] = 0 |
|||
}, result); |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +1,38 @@ |
|||
<div class="btn-group btn-group-{{size}}" *ngIf="isSmallMode"> |
|||
<button type="button" class="btn btn-outline-secondary" *ngFor="let supported of languages; trackBy: trackByLanguage" title="{{supported.englishName}}" [class.active]="supported === language" (click)="selectLanguage(supported)" tabindex="-1"> |
|||
{{supported.iso2Code}} |
|||
</button> |
|||
</div> |
|||
|
|||
<ng-container *ngIf="isLargeMode"> |
|||
<button type="button" class="btn btn-outline-secondary btn-{{size}} dropdown-toggle" title="{{language.englishName}}" (click)="dropdown.toggle()" #button tabindex="-1"> |
|||
{{language.iso2Code}} |
|||
</button> |
|||
|
|||
<ng-container *sqxModal="dropdown;closeAlways:true"> |
|||
<sqx-dropdown-menu [sqxAnchoredTo]="button" [scrollY]="true"> |
|||
<table> |
|||
<tbody> |
|||
<tr class="dropdown-item" *ngFor="let supported of languages; trackBy: trackByLanguage" [class.active]="supported === language" (click)="selectLanguage(supported)"> |
|||
<td><strong class="iso-code">{{supported.iso2Code}}</strong></td> |
|||
<td>({{supported.englishName}})</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</sqx-dropdown-menu> |
|||
<ng-container *ngIf="languages.length > 1"> |
|||
<ng-container *ngIf="languages.length > 3 || percents; else smallMode"> |
|||
<button type="button" class="btn btn-outline-secondary btn-{{size}} dropdown-toggle" title="{{language.englishName}}" (click)="dropdown.toggle()" #button tabindex="-1"> |
|||
{{language.iso2Code}} |
|||
</button> |
|||
|
|||
<ng-container *sqxModal="dropdown;closeAlways:true"> |
|||
<sqx-dropdown-menu [sqxAnchoredTo]="button" [scrollY]="true" [position]="dropdownPosition"> |
|||
<table> |
|||
<tbody> |
|||
<tr class="dropdown-item" *ngFor="let supported of languages; trackBy: trackByLanguage" |
|||
[class.active]="supported === language" |
|||
[class.missing]="exists && !exists[supported.iso2Code]" |
|||
(click)="selectLanguage(supported)"> |
|||
<td class="text-language">{{supported.iso2Code}}</td> |
|||
<td>({{supported.englishName}})</td> |
|||
|
|||
<td *ngIf="percents" class="text-right"> |
|||
{{percents[supported.iso2Code] || 0}} % |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</sqx-dropdown-menu> |
|||
</ng-container> |
|||
</ng-container> |
|||
|
|||
<ng-template #smallMode> |
|||
<div class="btn-group btn-group-{{size}}"> |
|||
<button type="button" class="btn btn-outline-secondary" *ngFor="let supported of languages; trackBy: trackByLanguage" title="{{supported.englishName}}" |
|||
[class.active]="supported === language" |
|||
[class.missing]="exists && !exists[supported.iso2Code]" |
|||
(click)="selectLanguage(supported)" tabindex="-1"> |
|||
<span>{{supported.iso2Code}}</span> |
|||
</button> |
|||
</div> |
|||
</ng-template> |
|||
</ng-container> |
|||
@ -0,0 +1,135 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; |
|||
import { moduleMetadata } from '@storybook/angular'; |
|||
import { Meta, Story } from '@storybook/angular/types-6-0'; |
|||
import { LanguageSelectorComponent, SqxFrameworkModule } from '@app/framework'; |
|||
|
|||
export default { |
|||
title: 'Framework/Language-Selector', |
|||
component: LanguageSelectorComponent, |
|||
argTypes: { |
|||
size: { |
|||
control: 'enum', |
|||
options: [ |
|||
'sm', |
|||
'md', |
|||
'lg', |
|||
], |
|||
}, |
|||
}, |
|||
decorators: [ |
|||
moduleMetadata({ |
|||
imports: [ |
|||
BrowserAnimationsModule, |
|||
SqxFrameworkModule, |
|||
SqxFrameworkModule.forRoot(), |
|||
], |
|||
}), |
|||
], |
|||
} as Meta; |
|||
|
|||
const Template: Story<LanguageSelectorComponent> = (args: LanguageSelectorComponent) => ({ |
|||
props: args, |
|||
template: ` |
|||
<sqx-root-view> |
|||
<div class="text-center"> |
|||
<sqx-language-selector |
|||
[exists]="exists" |
|||
[language]="language" |
|||
[languages]="languages" |
|||
[percents]="percents"> |
|||
</sqx-language-selector> |
|||
</div> |
|||
</sqx-root-view> |
|||
`,
|
|||
}); |
|||
|
|||
export const Empty = Template.bind({}); |
|||
|
|||
Empty.args = { |
|||
languages: [], |
|||
}; |
|||
|
|||
export const OneLanguage = Template.bind({}); |
|||
|
|||
OneLanguage.args = { |
|||
languages: [ |
|||
{ iso2Code: 'en', englishName: 'English' }, |
|||
], |
|||
}; |
|||
|
|||
export const FewLanguages = Template.bind({}); |
|||
|
|||
FewLanguages.args = { |
|||
languages: [ |
|||
{ iso2Code: 'en', englishName: 'English' }, |
|||
{ iso2Code: 'it', englishName: 'Italian' }, |
|||
{ iso2Code: 'es', englishName: 'Spanish' }, |
|||
], |
|||
}; |
|||
|
|||
export const FewLanguagesWithExists = Template.bind({}); |
|||
|
|||
FewLanguagesWithExists.args = { |
|||
languages: [ |
|||
{ iso2Code: 'en', englishName: 'English' }, |
|||
{ iso2Code: 'it', englishName: 'Italian' }, |
|||
{ iso2Code: 'es', englishName: 'Spanish' }, |
|||
], |
|||
exists: { |
|||
en: true, |
|||
it: false, |
|||
es: true, |
|||
}, |
|||
}; |
|||
|
|||
export const ManyLanguages = Template.bind({}); |
|||
|
|||
ManyLanguages.args = { |
|||
languages: [ |
|||
{ iso2Code: 'en', englishName: 'English' }, |
|||
{ iso2Code: 'it', englishName: 'Italian' }, |
|||
{ iso2Code: 'es', englishName: 'Spanish' }, |
|||
{ iso2Code: 'de', englishName: 'German' }, |
|||
{ iso2Code: 'ru', englishName: 'Russian' }, |
|||
], |
|||
}; |
|||
|
|||
export const ManyLanguagesWithExists = Template.bind({}); |
|||
|
|||
ManyLanguagesWithExists.args = { |
|||
languages: [ |
|||
{ iso2Code: 'en', englishName: 'English' }, |
|||
{ iso2Code: 'it', englishName: 'Italian' }, |
|||
{ iso2Code: 'es', englishName: 'Spanish' }, |
|||
{ iso2Code: 'de', englishName: 'German' }, |
|||
{ iso2Code: 'ru', englishName: 'Russian' }, |
|||
], |
|||
exists: { |
|||
en: true, |
|||
it: false, |
|||
es: true, |
|||
de: false, |
|||
ru: true, |
|||
}, |
|||
}; |
|||
|
|||
export const WithPercents = Template.bind({}); |
|||
|
|||
WithPercents.args = { |
|||
languages: [ |
|||
{ iso2Code: 'en', englishName: 'English' }, |
|||
{ iso2Code: 'it', englishName: 'Italian' }, |
|||
{ iso2Code: 'es', englishName: 'Spanish' }, |
|||
], |
|||
percents: { |
|||
'en': 100, |
|||
'it': 67, |
|||
}, |
|||
}; |
|||
Loading…
Reference in new issue