Skip to content

Commit e50bc17

Browse files
committed
Add complete Traditional Chinese UI locale support
Add zh-TW locale resources and wire it through language selection, persistence, and system-locale detection. Also add static shape typing for zh-TW to prevent future key drift from zh-CN. Constraint: Keep changes minimal and localized to i18n + language pickers Rejected: Partial zh-TW translation only for language section | fails full locale support goal Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep zh-TW key shape locked to zh-CN when adding new translation keys Tested: npm run -s build (openless-all/app) Not-tested: Manual runtime walkthrough on all platform shells
1 parent 429780b commit e50bc17

6 files changed

Lines changed: 527 additions & 3 deletions

File tree

openless-all/app/src/components/SettingsModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ function LanguagePicker() {
333333
>
334334
<option value={FOLLOW_SYSTEM}>{t('settings.language.followSystem')}</option>
335335
<option value="zh-CN">{t('settings.language.zh')}</option>
336+
<option value="zh-TW">{t('settings.language.zhTW')}</option>
336337
<option value="en">{t('settings.language.en')}</option>
337338
</select>
338339
);

openless-all/app/src/i18n/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ export const en: typeof zhCN = {
391391
labelDesc: 'Choose "Follow system" to match the OS language at launch.',
392392
followSystem: 'Follow system',
393393
zh: '简体中文',
394+
zhTW: '繁體中文',
394395
en: 'English',
395396
restartHint: 'Some native menus (system tray, etc.) may require an app restart to fully switch.',
396397
},

openless-all/app/src/i18n/index.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import i18n from 'i18next';
1212
import { initReactI18next } from 'react-i18next';
1313
import { en } from './en';
1414
import { zhCN } from './zh-CN';
15+
import { zhTW } from './zh-TW';
1516

16-
export const SUPPORTED_LOCALES = ['zh-CN', 'en'] as const;
17+
export const SUPPORTED_LOCALES = ['zh-CN', 'zh-TW', 'en'] as const;
1718
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
1819

1920
export const LOCALE_STORAGE_KEY = 'ol.locale';
@@ -22,21 +23,25 @@ const FOLLOW_SYSTEM_VALUE = 'system';
2223
function detectSystemLocale(): SupportedLocale {
2324
if (typeof navigator === 'undefined') return 'zh-CN';
2425
const nav = (navigator.language || '').toLowerCase();
25-
if (nav.startsWith('zh')) return 'zh-CN';
26+
if (nav.startsWith('zh')) {
27+
if (nav.includes('hant') || nav.includes('tw') || nav.includes('hk') || nav.includes('mo')) return 'zh-TW';
28+
return 'zh-CN';
29+
}
2630
return 'en';
2731
}
2832

2933
function getStoredLocale(): SupportedLocale | null {
3034
if (typeof window === 'undefined') return null;
3135
const raw = window.localStorage.getItem(LOCALE_STORAGE_KEY);
32-
return raw === 'zh-CN' || raw === 'en' ? raw : null;
36+
return raw === 'zh-CN' || raw === 'zh-TW' || raw === 'en' ? raw : null;
3337
}
3438

3539
const initialLng: SupportedLocale = getStoredLocale() ?? detectSystemLocale();
3640

3741
void i18n.use(initReactI18next).init({
3842
resources: {
3943
'zh-CN': { translation: zhCN },
44+
'zh-TW': { translation: zhTW },
4045
en: { translation: en },
4146
},
4247
lng: initialLng,

openless-all/app/src/i18n/zh-CN.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ export const zhCN = {
389389
labelDesc: '选择「跟随系统」时按操作系统当前语言显示。',
390390
followSystem: '跟随系统',
391391
zh: '简体中文',
392+
zhTW: '繁體中文',
392393
en: 'English',
393394
restartHint: '部分原生菜单(系统托盘等)可能需要重启 App 才会切换。',
394395
},

0 commit comments

Comments
 (0)