fix(general): route supported-languages field through LocaleSyncService, guard unsupported default locale
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
General's free-text 'Supported Languages' field overwrote tenant/localization.supportedLocales directly, skipping LocaleSyncService's propagation to per-locale nav/static-page translation entries - the exact sync Languages' add/remove buttons already go through correctly. Now diffs against the current list and routes each added/removed locale through facade.addLocale()/removeLocale(). Also: 'Default Language' was a free-text input with no guard against typing a locale that isn't in the supported list - every label[defaultLocale] lookup across nav/static-page content would then silently return undefined. Added a validator rule (default-locale-not-supported) wired to the existing fieldError() display, consistent with every other field-level check. Verified live via window.ng.getComponent(): typing an unsupported code shows the new inline error; adding 'de' via this field seeded an empty 'de' translation entry on an existing static page, matching what Languages' add-locale button already produces. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
<small class="field-desc">{{ 'builder.descriptionFieldDesc' | translate }}</small>
|
<small class="field-desc">{{ 'builder.descriptionFieldDesc' | translate }}</small>
|
||||||
<textarea rows="3" [ngModel]="bootstrap.seo.default.description" (ngModelChange)="updateDescription($event)"></textarea>
|
<textarea rows="3" [ngModel]="bootstrap.seo.default.description" (ngModelChange)="updateDescription($event)"></textarea>
|
||||||
</label>
|
</label>
|
||||||
<app-form-field [label]="'builder.defaultLanguage' | translate" [hint]="'builder.defaultLanguageDesc' | translate">
|
<app-form-field [label]="'builder.defaultLanguage' | translate" [hint]="'builder.defaultLanguageDesc' | translate" [error]="fieldError('localization.defaultLocale')">
|
||||||
<app-input [ngModel]="bootstrap.localization.defaultLocale" (ngModelChange)="updateDefaultLanguage($event)" />
|
<app-input [ngModel]="bootstrap.localization.defaultLocale" (ngModelChange)="updateDefaultLanguage($event)" />
|
||||||
</app-form-field>
|
</app-form-field>
|
||||||
<app-form-field [label]="'builder.supportedLanguages' | translate" [hint]="'builder.supportedLanguagesDesc' | translate">
|
<app-form-field [label]="'builder.supportedLanguages' | translate" [hint]="'builder.supportedLanguagesDesc' | translate">
|
||||||
|
|||||||
@@ -61,12 +61,25 @@ export class ProjectEditorGeneralSectionComponent {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routes through facade.addLocale()/removeLocale() (LocaleSyncService)
|
||||||
|
* instead of overwriting supportedLocales directly - a direct overwrite
|
||||||
|
* skipped seeding/cleaning up per-locale nav and static-page translation
|
||||||
|
* entries, leaving them out of sync with what this field claims is
|
||||||
|
* supported (and Languages' add/remove buttons already do it correctly).
|
||||||
|
*/
|
||||||
updateSupportedLanguages(value: string): void {
|
updateSupportedLanguages(value: string): void {
|
||||||
const locales = value.split(',').map(item => item.trim()).filter(Boolean);
|
const next = new Set(value.split(',').map(item => item.trim().toLowerCase()).filter(Boolean));
|
||||||
this.facade.updateBootstrap(current => ({
|
const current = this.bootstrap()?.localization.supportedLocales ?? [];
|
||||||
...current,
|
for (const locale of current) {
|
||||||
tenant: { ...current.tenant, supportedLocales: locales },
|
if (!next.has(locale)) {
|
||||||
localization: { ...current.localization, supportedLocales: locales }
|
this.facade.removeLocale(locale);
|
||||||
}));
|
}
|
||||||
|
}
|
||||||
|
for (const locale of next) {
|
||||||
|
if (!current.includes(locale)) {
|
||||||
|
this.facade.addLocale(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,9 +70,17 @@ export class ProjectValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private languageIssues(bootstrap: BootstrapConfig): ProjectValidationIssue[] {
|
private languageIssues(bootstrap: BootstrapConfig): ProjectValidationIssue[] {
|
||||||
return bootstrap.localization.supportedLocales.length > 0
|
if (bootstrap.localization.supportedLocales.length === 0) {
|
||||||
? []
|
return [error('no-languages', 'builder.validationNoLanguages', 'languages', 'localization.supportedLocales')];
|
||||||
: [error('no-languages', 'builder.validationNoLanguages', 'languages', 'localization.supportedLocales')];
|
}
|
||||||
|
// General's free-text "Default Language" field (unlike Languages' set-default
|
||||||
|
// buttons, which only ever target an already-supported locale) can set this
|
||||||
|
// to any string. A default locale that isn't itself supported breaks every
|
||||||
|
// `label[defaultLocale]` lookup across nav/static-page content.
|
||||||
|
if (!bootstrap.localization.supportedLocales.includes(bootstrap.localization.defaultLocale)) {
|
||||||
|
return [error('default-locale-not-supported', 'builder.validationDefaultLocaleNotSupported', 'general', 'localization.defaultLocale')];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private urlIssues(bootstrap: BootstrapConfig): ProjectValidationIssue[] {
|
private urlIssues(bootstrap: BootstrapConfig): ProjectValidationIssue[] {
|
||||||
|
|||||||
@@ -538,6 +538,7 @@ export const en: Translations = {
|
|||||||
featureFlags: 'Feature Flags',
|
featureFlags: 'Feature Flags',
|
||||||
validationMissingLogo: 'Branding is missing a logo.',
|
validationMissingLogo: 'Branding is missing a logo.',
|
||||||
validationNoLanguages: 'No languages are configured.',
|
validationNoLanguages: 'No languages are configured.',
|
||||||
|
validationDefaultLocaleNotSupported: 'The default language is not in the supported languages list.',
|
||||||
validationInvalidUrl: 'The marketplace URL is invalid.',
|
validationInvalidUrl: 'The marketplace URL is invalid.',
|
||||||
validationDuplicateSlugs: 'Two or more static pages share the same slug.',
|
validationDuplicateSlugs: 'Two or more static pages share the same slug.',
|
||||||
validationEmptyHomepage: 'The homepage has no sections.',
|
validationEmptyHomepage: 'The homepage has no sections.',
|
||||||
|
|||||||
@@ -538,6 +538,7 @@ export const hy: Translations = {
|
|||||||
featureFlags: 'Feature flags',
|
featureFlags: 'Feature flags',
|
||||||
validationMissingLogo: 'Բրենդինգում բացակայում է լոգոն։',
|
validationMissingLogo: 'Բրենդինգում բացակայում է լոգոն։',
|
||||||
validationNoLanguages: 'Կարգավորված լեզուներ չկան։',
|
validationNoLanguages: 'Կարգավորված լեզուներ չկան։',
|
||||||
|
validationDefaultLocaleNotSupported: 'Կանխադրված լեզուն աջակցվող լեզուների ցանկում չէ։',
|
||||||
validationInvalidUrl: 'Մարքեթփլեյսի URL-ը սխալ է։',
|
validationInvalidUrl: 'Մարքեթփլեյսի URL-ը սխալ է։',
|
||||||
validationDuplicateSlugs: 'Երկու կամ ավելի ստատիկ էջեր ունեն նույն slug-ը։',
|
validationDuplicateSlugs: 'Երկու կամ ավելի ստատիկ էջեր ունեն նույն slug-ը։',
|
||||||
validationEmptyHomepage: 'Գլխավոր էջում սեկցիաներ չկան։',
|
validationEmptyHomepage: 'Գլխավոր էջում սեկցիաներ չկան։',
|
||||||
|
|||||||
@@ -538,6 +538,7 @@ export const ru: Translations = {
|
|||||||
featureFlags: 'Фичи-флаги',
|
featureFlags: 'Фичи-флаги',
|
||||||
validationMissingLogo: 'В брендинге отсутствует логотип.',
|
validationMissingLogo: 'В брендинге отсутствует логотип.',
|
||||||
validationNoLanguages: 'Не настроены языки.',
|
validationNoLanguages: 'Не настроены языки.',
|
||||||
|
validationDefaultLocaleNotSupported: 'Язык по умолчанию отсутствует в списке поддерживаемых языков.',
|
||||||
validationInvalidUrl: 'Некорректный URL маркетплейса.',
|
validationInvalidUrl: 'Некорректный URL маркетплейса.',
|
||||||
validationDuplicateSlugs: 'Две или более статические страницы имеют одинаковый slug.',
|
validationDuplicateSlugs: 'Две или более статические страницы имеют одинаковый slug.',
|
||||||
validationEmptyHomepage: 'На главной странице нет секций.',
|
validationEmptyHomepage: 'На главной странице нет секций.',
|
||||||
|
|||||||
@@ -536,6 +536,7 @@ export interface Translations {
|
|||||||
featureFlags: string;
|
featureFlags: string;
|
||||||
validationMissingLogo: string;
|
validationMissingLogo: string;
|
||||||
validationNoLanguages: string;
|
validationNoLanguages: string;
|
||||||
|
validationDefaultLocaleNotSupported: string;
|
||||||
validationInvalidUrl: string;
|
validationInvalidUrl: string;
|
||||||
validationDuplicateSlugs: string;
|
validationDuplicateSlugs: string;
|
||||||
validationEmptyHomepage: string;
|
validationEmptyHomepage: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user