import { createI18n } from 'vue-i18n' import zhCN from './locales/zh-CN' import enUS from './locales/en-US' import jaJP from './locales/ja-JP' import koKR from './locales/ko-KR' /** * 国际化(i18n)基础设施。 * * 支持语言:简体中文 / 英语 / 日语 / 韩语。 * 语言判定优先级:本地存储(用户手动选择) > 浏览器语言 > 默认简体中文。 * * 使用方式: * 模板中 {{ $t('tool.uuid.name') }} * 脚本中 const { t } = useI18n() → t('tool.uuid.name') * * 新增工具/页面文案时,请同时补齐 4 个语言包的同一个 key, * 否则缺失语言会回退到 fallbackLocale(简体中文),不会报错但会出现中英混排。 */ /** 站点支持的语言列表 */ export const SUPPORTED_LOCALES = ['zh-CN', 'en-US', 'ja-JP', 'ko-KR'] as const export type AppLocale = (typeof SUPPORTED_LOCALES)[number] /** 语言选择本地存储 key(index.html 内联脚本会读取它,用于首屏防闪烁) */ export const LOCALE_STORAGE_KEY = 'tool-locale' /** 兜底语言:简体中文(语言包最完整,作为所有缺失 key 的回退) */ export const FALLBACK_LOCALE: AppLocale = 'zh-CN' const messages = { 'zh-CN': zhCN, 'en-US': enUS, 'ja-JP': jaJP, 'ko-KR': koKR, } /** 判断给定值是否为受支持的语言代码 */ export function isAppLocale(value: unknown): value is AppLocale { return typeof value === 'string' && (SUPPORTED_LOCALES as readonly string[]).includes(value) } /** * 把浏览器语言标签(如 zh-Hant-TW、en-GB)归一化到站点支持的语言。 * 匹配规则:先精确匹配,再按主语言前缀匹配,最后回退默认语言。 */ export function normalizeLocale(raw: string | null | undefined): AppLocale { if (!raw) return FALLBACK_LOCALE if (isAppLocale(raw)) return raw const primary = raw.toLowerCase().split('-')[0] const matched = SUPPORTED_LOCALES.find((l) => l.toLowerCase().split('-')[0] === primary) return matched ?? FALLBACK_LOCALE } /** 解析初始语言:本地存储优先,其次浏览器语言 */ export function resolveInitialLocale(): AppLocale { try { const saved = localStorage.getItem(LOCALE_STORAGE_KEY) if (isAppLocale(saved)) return saved } catch { // 隐私模式等场景下 localStorage 不可用,忽略即可 } return normalizeLocale(typeof navigator !== 'undefined' ? navigator.language : null) } export const i18n = createI18n({ // Composition API 模式(Vue 3 推荐),同时开启全局注入让模板可直接用 $t legacy: false, globalInjection: true, locale: resolveInitialLocale(), fallbackLocale: FALLBACK_LOCALE, // 缺失 key 时不在控制台刷警告(开发期可临时改回 true 排查) missingWarn: false, fallbackWarn: false, messages, }) /** * 同步语言到 DOM:写入 html[lang]。 * html[lang] 会影响字体栈选择(见 tokens.scss)与搜索引擎/读屏软件的语言判断, * 因此切语言时必须一并更新。 */ export function applyLocaleToDocument(locale: AppLocale) { document.documentElement.setAttribute('lang', locale) } /** 切换语言:更新 i18n 实例、DOM 属性与本地存储 */ export function setAppLocale(locale: AppLocale) { i18n.global.locale.value = locale applyLocaleToDocument(locale) try { localStorage.setItem(LOCALE_STORAGE_KEY, locale) } catch { // 忽略存储失败 } }