feat(client): add Traditional Chinese (zh-TW) language support (#612)
* feat(client): add Traditional Chinese (zh-TW) language support Update language switcher and i18n configuration to include Traditional Chinese. Modify locale resolution to prioritize exact matches (zh-TW) over base codes (zh), ensuring accurate browser language detection. Add complete translation set. Co-authored-by: Copilot <copilot@github.com> * feat(i18n): improve BCP-47 locale resolution and browser languages Refactor resolveLocale to use a normalize helper that correctly maps BCP-47 tags to supported locale keys, properly distinguishing Traditional vs Simplified Chinese variants. Additionally, iterate over navigator.languages instead of relying solely on navigator.language to respect the browser's full language preference list. Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -5,7 +5,8 @@ import { NSelect } from 'naive-ui'
|
||||
const { locale } = useI18n()
|
||||
|
||||
const options = [
|
||||
{ label: '中文', value: 'zh' },
|
||||
{ label: '简体中文', value: 'zh' },
|
||||
{ label: '繁體中文', value: 'zh-TW' },
|
||||
{ label: 'English', value: 'en' },
|
||||
{ label: '日本語', value: 'ja' },
|
||||
{ label: '한국어', value: 'ko' },
|
||||
|
||||
@@ -2,24 +2,45 @@ import { createI18n } from 'vue-i18n'
|
||||
import { messages } from './messages'
|
||||
|
||||
const saved = localStorage.getItem('hermes_locale')
|
||||
const detected = navigator.language.slice(0, 2)
|
||||
|
||||
const supportedLocales = ['en', 'zh', 'ja', 'ko', 'fr', 'es', 'de', 'pt'] as const
|
||||
const supportedLocales = ['en', 'zh', 'zh-TW', 'ja', 'ko', 'fr', 'es', 'de', 'pt'] as const
|
||||
type SupportedLocale = (typeof supportedLocales)[number]
|
||||
|
||||
function resolveLocale(saved: string | null, detected: string): SupportedLocale {
|
||||
function resolveLocale(saved: string | null): SupportedLocale {
|
||||
if (saved && (supportedLocales as readonly string[]).includes(saved)) {
|
||||
return saved as SupportedLocale
|
||||
}
|
||||
if ((supportedLocales as readonly string[]).includes(detected)) {
|
||||
return detected as SupportedLocale
|
||||
|
||||
// Normalize a single BCP-47 tag to a supported locale key.
|
||||
// Covers zh-Hant-TW, zh-TW, zh-HK, zh-MO, zh-Hant → zh-TW
|
||||
// zh-Hans-*, zh-CN, zh-SG, zh → zh
|
||||
function normalize(tag: string): SupportedLocale | null {
|
||||
const lower = tag.toLowerCase()
|
||||
if (lower.startsWith('zh')) {
|
||||
const isTraditional =
|
||||
lower.includes('hant') ||
|
||||
lower.includes('-tw') ||
|
||||
lower.includes('-hk') ||
|
||||
lower.includes('-mo')
|
||||
return isTraditional ? 'zh-TW' : 'zh'
|
||||
}
|
||||
const short = tag.slice(0, 2)
|
||||
if ((supportedLocales as readonly string[]).includes(tag)) return tag as SupportedLocale
|
||||
if ((supportedLocales as readonly string[]).includes(short)) return short as SupportedLocale
|
||||
return null
|
||||
}
|
||||
|
||||
for (const lang of navigator.languages) {
|
||||
const resolved = normalize(lang)
|
||||
if (resolved) return resolved
|
||||
}
|
||||
|
||||
return 'en'
|
||||
}
|
||||
|
||||
export const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: resolveLocale(saved, detected),
|
||||
locale: resolveLocale(saved),
|
||||
fallbackLocale: 'en',
|
||||
messages,
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,18 +6,20 @@ import ja from './locales/ja'
|
||||
import ko from './locales/ko'
|
||||
import pt from './locales/pt'
|
||||
import zh from './locales/zh'
|
||||
import zhTW from './locales/zh-TW'
|
||||
|
||||
export type LocaleMessages = Record<string, unknown>
|
||||
|
||||
export const rawMessages = {
|
||||
en,
|
||||
zh,
|
||||
ja,
|
||||
ko,
|
||||
fr,
|
||||
es,
|
||||
de,
|
||||
pt,
|
||||
'en': en,
|
||||
'zh': zh,
|
||||
'zh-TW': zhTW,
|
||||
'ja': ja,
|
||||
'ko': ko,
|
||||
'fr': fr,
|
||||
'es': es,
|
||||
'de': de,
|
||||
'pt': pt,
|
||||
} satisfies Record<string, LocaleMessages>
|
||||
|
||||
function isPlainObject(value: unknown): value is LocaleMessages {
|
||||
|
||||
Reference in New Issue
Block a user