From 317afe9e3b471c4a938c5813a9f46157a342ef27 Mon Sep 17 00:00:00 2001 From: Junghwan <70629228+shaun0927@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:28:33 +0900 Subject: [PATCH] fix(web): normalize underscore locale codes in dynamic language selection (#27900) Co-authored-by: Daniel Dietzler --- web/src/lib/i18n.spec.ts | 8 ++++++++ web/src/lib/utils/i18n.ts | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) 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))); };