From 76e9689e115be90db7e9f82dc417f0d17acdd902 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 17 Jul 2026 19:00:17 +0400 Subject: [PATCH] fix(general): route supported-languages field through LocaleSyncService, guard unsupported default locale 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 --- .../sections/general-section.component.html | 2 +- .../sections/general-section.component.ts | 25 ++++++++++++++----- .../services/project-validator.service.ts | 14 ++++++++--- src/app/i18n/en.ts | 1 + src/app/i18n/hy.ts | 1 + src/app/i18n/ru.ts | 1 + src/app/i18n/translations.ts | 1 + 7 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/app/features/project-editor/sections/general-section.component.html b/src/app/features/project-editor/sections/general-section.component.html index c3c26ef..c88c34d 100644 --- a/src/app/features/project-editor/sections/general-section.component.html +++ b/src/app/features/project-editor/sections/general-section.component.html @@ -12,7 +12,7 @@ {{ 'builder.descriptionFieldDesc' | translate }} - + diff --git a/src/app/features/project-editor/sections/general-section.component.ts b/src/app/features/project-editor/sections/general-section.component.ts index c8180a6..c303613 100644 --- a/src/app/features/project-editor/sections/general-section.component.ts +++ b/src/app/features/project-editor/sections/general-section.component.ts @@ -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 { - const locales = value.split(',').map(item => item.trim()).filter(Boolean); - this.facade.updateBootstrap(current => ({ - ...current, - tenant: { ...current.tenant, supportedLocales: locales }, - localization: { ...current.localization, supportedLocales: locales } - })); + const next = new Set(value.split(',').map(item => item.trim().toLowerCase()).filter(Boolean)); + const current = this.bootstrap()?.localization.supportedLocales ?? []; + for (const locale of current) { + if (!next.has(locale)) { + this.facade.removeLocale(locale); + } + } + for (const locale of next) { + if (!current.includes(locale)) { + this.facade.addLocale(locale); + } + } } } diff --git a/src/app/features/project-editor/services/project-validator.service.ts b/src/app/features/project-editor/services/project-validator.service.ts index 23f290d..da4b69f 100644 --- a/src/app/features/project-editor/services/project-validator.service.ts +++ b/src/app/features/project-editor/services/project-validator.service.ts @@ -70,9 +70,17 @@ export class ProjectValidator { } private languageIssues(bootstrap: BootstrapConfig): ProjectValidationIssue[] { - return bootstrap.localization.supportedLocales.length > 0 - ? [] - : [error('no-languages', 'builder.validationNoLanguages', 'languages', 'localization.supportedLocales')]; + if (bootstrap.localization.supportedLocales.length === 0) { + return [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[] { diff --git a/src/app/i18n/en.ts b/src/app/i18n/en.ts index f615664..22e8288 100644 --- a/src/app/i18n/en.ts +++ b/src/app/i18n/en.ts @@ -538,6 +538,7 @@ export const en: Translations = { featureFlags: 'Feature Flags', validationMissingLogo: 'Branding is missing a logo.', validationNoLanguages: 'No languages are configured.', + validationDefaultLocaleNotSupported: 'The default language is not in the supported languages list.', validationInvalidUrl: 'The marketplace URL is invalid.', validationDuplicateSlugs: 'Two or more static pages share the same slug.', validationEmptyHomepage: 'The homepage has no sections.', diff --git a/src/app/i18n/hy.ts b/src/app/i18n/hy.ts index 10af5cd..003379f 100644 --- a/src/app/i18n/hy.ts +++ b/src/app/i18n/hy.ts @@ -538,6 +538,7 @@ export const hy: Translations = { featureFlags: 'Feature flags', validationMissingLogo: 'Բրենդինգում բացակայում է լոգոն։', validationNoLanguages: 'Կարգավորված լեզուներ չկան։', + validationDefaultLocaleNotSupported: 'Կանխադրված լեզուն աջակցվող լեզուների ցանկում չէ։', validationInvalidUrl: 'Մարքեթփլեյսի URL-ը սխալ է։', validationDuplicateSlugs: 'Երկու կամ ավելի ստատիկ էջեր ունեն նույն slug-ը։', validationEmptyHomepage: 'Գլխավոր էջում սեկցիաներ չկան։', diff --git a/src/app/i18n/ru.ts b/src/app/i18n/ru.ts index e57f0a9..bf26a79 100644 --- a/src/app/i18n/ru.ts +++ b/src/app/i18n/ru.ts @@ -538,6 +538,7 @@ export const ru: Translations = { featureFlags: 'Фичи-флаги', validationMissingLogo: 'В брендинге отсутствует логотип.', validationNoLanguages: 'Не настроены языки.', + validationDefaultLocaleNotSupported: 'Язык по умолчанию отсутствует в списке поддерживаемых языков.', validationInvalidUrl: 'Некорректный URL маркетплейса.', validationDuplicateSlugs: 'Две или более статические страницы имеют одинаковый slug.', validationEmptyHomepage: 'На главной странице нет секций.', diff --git a/src/app/i18n/translations.ts b/src/app/i18n/translations.ts index 488dae0..ff4d576 100644 --- a/src/app/i18n/translations.ts +++ b/src/app/i18n/translations.ts @@ -536,6 +536,7 @@ export interface Translations { featureFlags: string; validationMissingLogo: string; validationNoLanguages: string; + validationDefaultLocaleNotSupported: string; validationInvalidUrl: string; validationDuplicateSlugs: string; validationEmptyHomepage: string;