language settings: keep script-tagged locales checked

The selected-language indicator collapsed every BCP-47 tag down to its
base subtag before comparing against the supported list, so picking
'zh-Hant' set i18n.language to 'zh-Hant' (and the UI strings switched
to Traditional Chinese as expected) but the checkmark moved to the
'zh' (Simplified) row because the comparison only saw 'zh' on both
sides.

Match in three passes instead:

1. Exact case-insensitive match against SUPPORTED_LANGUAGES — keeps
   'zh-Hant' checked when active.
2. Alias map for zh-TW / zh-HK (registered as resource aliases in
   i18n.ts) so device locales from Taiwan and Hong Kong land on the
   zh-Hant row in the switcher.
3. Base-subtag fallback for en-US -> en, pt-BR -> pt, etc., preserved
   from the original behavior.
This commit is contained in:
mkfain
2026-05-24 19:05:47 -05:00
parent 9a3f1cac73
commit b851f42e6c
+21 -1
View File
@@ -13,7 +13,27 @@ export function LanguageSettingsPage() {
// Use the live i18n language so the selected indicator updates immediately
// after the user picks a new option (the component re-renders via
// `useTranslation` when the language changes).
const currentLng = (i18nInstance.language ?? 'en').split('-')[0].toLowerCase();
//
// Match the active language against `SUPPORTED_LANGUAGES` codes in two
// passes so script-tagged variants like `zh-Hant` keep their checkmark
// instead of collapsing to the base `zh` row:
// 1. Exact match (case-insensitive) — `zh-Hant` matches the `zh-Hant`
// switcher entry, `en` matches `en`.
// 2. Alias map — `zh-TW` and `zh-HK` both resolve to `zh-Hant` because
// i18n.ts registers them as resource aliases.
// 3. Base-code match — `en-US` matches `en`, `pt-BR` matches `pt`, etc.
// Only consulted when no script-tagged variant matched first.
const rawLng = i18nInstance.language ?? 'en';
const ZH_HANT_ALIASES = new Set(['zh-tw', 'zh-hk']);
const supportedCodesLower = new Set(SUPPORTED_LANGUAGES.map((l) => l.code.toLowerCase()));
const rawLngLower = rawLng.toLowerCase();
const currentLng = (() => {
if (supportedCodesLower.has(rawLngLower)) {
return SUPPORTED_LANGUAGES.find((l) => l.code.toLowerCase() === rawLngLower)!.code;
}
if (ZH_HANT_ALIASES.has(rawLngLower)) return 'zh-Hant';
return rawLng.split('-')[0].toLowerCase();
})();
useSeoMeta({
title: `${t('language.title')} | ${t('settings.title')} | ${config.appName}`,