diff --git a/web/src/lib/i18n.spec.ts b/web/src/lib/i18n.spec.ts index 43eabd5e22..ccc60a835b 100644 --- a/web/src/lib/i18n.spec.ts +++ b/web/src/lib/i18n.spec.ts @@ -50,5 +50,13 @@ describe('i18n', () => { expect(getClosestAvailableLocale(['zh'], allLocales)).toBeUndefined(); expect(getClosestAvailableLocale(['de', 'zh', 'en-US'], allLocales)).toBe('en-US'); }); + + it('matches underscore-based stored locale codes against normalized locale lists', () => { + const allLocales = ['de-CH', 'pt-BR', 'sr-Cyrl', 'zh-Hant']; + expect(getClosestAvailableLocale(['de_CH'], allLocales)).toBe('de_CH'); + expect(getClosestAvailableLocale(['pt_BR'], allLocales)).toBe('pt_BR'); + expect(getClosestAvailableLocale(['sr_Cyrl'], allLocales)).toBe('sr_Cyrl'); + expect(getClosestAvailableLocale(['zh_Hant'], allLocales)).toBe('zh_Hant'); + }); }); }); diff --git a/web/src/lib/utils/i18n.ts b/web/src/lib/utils/i18n.ts index ca7b091774..458d1762ae 100644 --- a/web/src/lib/utils/i18n.ts +++ b/web/src/lib/utils/i18n.ts @@ -19,20 +19,20 @@ const fileCodes = Object.keys(modules) .map((path) => path.match(/\/(\w+)\.json$/)?.[1]) .filter(Boolean) as string[]; -const convertBCP47 = (code: string) => code.replace('_', '-'); +const convertBCP47 = (code: string) => code.replaceAll('_', '-'); export const langCodes = fileCodes.map((code) => convertBCP47(code)); // https://github.com/kaisermann/svelte-i18n/blob/780932a3e1270d521d348aac8ba03be9df309f04/src/runtime/stores/locale.ts#L11 const getSubLocales = (locale: string) => { - return locale + return convertBCP47(locale) .split('-') .map((_, i, arr) => arr.slice(0, i + 1).join('-')) .reverse(); }; export const getClosestAvailableLocale = (locales: readonly string[], allLocales: readonly string[]) => { - const allLocalesSet = new Set(allLocales); + const allLocalesSet = new Set(allLocales.map((locale) => convertBCP47(locale))); return locales.find((locale) => getSubLocales(locale).some((subLocale) => allLocalesSet.has(subLocale))); };