import { createI18n } from 'vue-i18n' type LocaleCode = 'en' | 'zh' type LocaleMessages = Record const LOCALE_KEY = 'sub2api_locale' const DEFAULT_LOCALE: LocaleCode = 'en' const localeLoaders: Record Promise<{ default: LocaleMessages }>> = { en: () => import('./locales/en'), zh: () => import('./locales/zh') } function isLocaleCode(value: string): value is LocaleCode { return value === 'en' || value === 'zh' } function getDefaultLocale(): LocaleCode { const saved = localStorage.getItem(LOCALE_KEY) if (saved && isLocaleCode(saved)) { return saved } const browserLang = navigator.language.toLowerCase() if (browserLang.startsWith('zh')) { return 'zh' } return DEFAULT_LOCALE } export const i18n = createI18n({ legacy: false, locale: getDefaultLocale(), fallbackLocale: DEFAULT_LOCALE, messages: {}, // 禁用 HTML 消息警告 - 引导步骤使用富文本内容(driver.js 支持 HTML) // 这些内容是内部定义的,不存在 XSS 风险 warnHtmlMessage: false }) const loadedLocales = new Set() export async function loadLocaleMessages(locale: LocaleCode): Promise { if (loadedLocales.has(locale)) { return } const loader = localeLoaders[locale] const module = await loader() i18n.global.setLocaleMessage(locale, module.default) loadedLocales.add(locale) } export async function initI18n(): Promise { const current = getLocale() await loadLocaleMessages(current) document.documentElement.setAttribute('lang', current) } export async function setLocale(locale: string): Promise { if (!isLocaleCode(locale)) { return } await loadLocaleMessages(locale) i18n.global.locale.value = locale localStorage.setItem(LOCALE_KEY, locale) document.documentElement.setAttribute('lang', locale) } export function getLocale(): LocaleCode { const current = i18n.global.locale.value return isLocaleCode(current) ? current : DEFAULT_LOCALE } export const availableLocales = [ { code: 'en', name: 'English', flag: '🇺🇸' }, { code: 'zh', name: '中文', flag: '🇨🇳' } ] as const export default i18n