/** * MetonaEditor i18n - 国际化管理 * @module i18n * @version 0.1.4 * @description 多语言支持、语言切换和翻译管理 */ import { LOCALES } from './constants.js'; let currentLocale = 'zh-CN'; let localeListeners = new Set(); let fallbackLocale = 'zh-CN'; /** * 获取当前语言 */ export const getCurrentLocale = () => currentLocale; /** * 设置当前语言 */ export const setCurrentLocale = (locale) => { let target = locale; if (!LOCALES[target]) { console.warn(`Locale "${target}" not found, falling back to "${fallbackLocale}"`); target = fallbackLocale; } currentLocale = target; notifyLocaleListeners(target); saveLocale(target); }; /** * 翻译函数 */ export const t = (key, params = {}) => { const currentTranslation = getTranslation(currentLocale, key); if (currentTranslation !== undefined) { return interpolate(currentTranslation, params); } if (currentLocale !== fallbackLocale) { const fallbackTranslation = getTranslation(fallbackLocale, key); if (fallbackTranslation !== undefined) { return interpolate(fallbackTranslation, params); } } console.warn(`Translation missing for key "${key}" in locale "${currentLocale}"`); return key; }; /** * 获取翻译 */ const getTranslation = (locale, key) => { const localeData = LOCALES[locale]; if (!localeData) return undefined; const keys = key.split('.'); let result = localeData; for (const k of keys) { if (result && typeof result === 'object' && k in result) { result = result[k]; } else { return undefined; } } return typeof result === 'string' ? result : undefined; }; /** * 插值函数 */ const interpolate = (str, params) => { return str.replace(/\{(\w+)\}/g, (match, key) => { return params[key] !== undefined ? params[key] : match; }); }; /** * 检查翻译是否存在 */ export const hasTranslation = (key) => { return getTranslation(currentLocale, key) !== undefined || getTranslation(fallbackLocale, key) !== undefined; }; /** * 获取所有翻译 */ export const getTranslations = (locale) => { return LOCALES[locale] || {}; }; /** * 添加翻译 */ export const addTranslations = (locale, translations) => { if (!LOCALES[locale]) { LOCALES[locale] = {}; } deepMerge(LOCALES[locale], translations); }; /** * 深度合并对象 */ const deepMerge = (target, source) => { for (const key in source) { if (source[key] instanceof Object && key in target && target[key] instanceof Object) { deepMerge(target[key], source[key]); } else { target[key] = source[key]; } } return target; }; /** * 获取支持的语言列表 */ export const getSupportedLocales = () => { return Object.keys(LOCALES); }; /** * 检查语言是否支持 */ export const isLocaleSupported = (locale) => { return locale in LOCALES; }; /** * 获取语言名称 */ export const getLocaleName = (locale) => { const names = { 'zh-CN': '简体中文', 'zh-TW': '繁體中文', 'en-US': 'English (US)', 'en-GB': 'English (UK)', 'ja': '日本語', 'ko': '한국어', 'fr': 'Français', 'de': 'Deutsch', 'es': 'Español', 'pt': 'Português', 'ru': 'Русский', 'ar': 'العربية', 'hi': 'हिन्दी', 'th': 'ไทย', 'vi': 'Tiếng Việt', 'id': 'Bahasa Indonesia', 'ms': 'Bahasa Melayu', 'tr': 'Türkçe', 'it': 'Italiano', 'nl': 'Nederlands', 'pl': 'Polski', }; return names[locale] || locale; }; /** * 获取语言方向 */ export const getLocaleDirection = (locale) => { const rtlLocales = ['ar', 'he', 'fa', 'ur', 'yi', 'ps', 'sd', 'ug']; return rtlLocales.includes(locale) ? 'rtl' : 'ltr'; }; /** * 格式化数字 */ export const formatNumber = (number, options = {}) => { try { return new Intl.NumberFormat(currentLocale, options).format(number); } catch (e) { return number.toString(); } }; /** * 格式化货币 */ export const formatCurrency = (amount, currency = 'USD', options = {}) => { try { return new Intl.NumberFormat(currentLocale, { style: 'currency', currency, ...options, }).format(amount); } catch (e) { return amount.toString(); } }; /** * 格式化日期 */ export const formatDate = (date, options = {}) => { try { const dateObj = date instanceof Date ? date : new Date(date); return new Intl.DateTimeFormat(currentLocale, options).format(dateObj); } catch (e) { return date.toString(); } }; /** * 添加语言监听器 */ export const addLocaleListener = (listener) => { localeListeners.add(listener); return () => { localeListeners.delete(listener); }; }; /** * 移除语言监听器 */ export const removeLocaleListener = (listener) => { localeListeners.delete(listener); }; /** * 通知语言监听器 */ const notifyLocaleListeners = (locale) => { localeListeners.forEach((listener) => { try { listener(locale); } catch (e) { console.error('Locale listener error:', e); } }); }; /** * 清除所有语言监听器 */ export const clearLocaleListeners = () => { localeListeners.clear(); }; /** * 保存语言到本地存储 */ export const saveLocale = (locale) => { if (typeof localStorage !== 'undefined') { try { localStorage.setItem('metona-editor-locale', locale); } catch (e) { console.warn('Failed to save locale:', e); } } }; /** * 从本地存储加载语言 */ export const loadLocale = () => { if (typeof localStorage !== 'undefined') { try { return localStorage.getItem('metona-editor-locale') || getDefaultLocale(); } catch (e) { console.warn('Failed to load locale:', e); } } return getDefaultLocale(); }; /** * 获取默认语言 */ export const getDefaultLocale = () => { if (typeof navigator !== 'undefined') { const browserLocale = navigator.language || navigator.userLanguage; if (browserLocale && isLocaleSupported(browserLocale)) { return browserLocale; } const shortLocale = browserLocale?.split('-')[0]; if (shortLocale && isLocaleSupported(shortLocale)) { return shortLocale; } } return fallbackLocale; }; /** * 初始化国际化系统 */ export const initI18n = () => { const savedLocale = loadLocale(); setCurrentLocale(savedLocale); }; /** * 切换语言 */ export const switchLocale = (locale) => { setCurrentLocale(locale); }; /** * 国际化工具 — 代理层 */ export const i18nUtils = { t, getCurrentLocale, setCurrentLocale, switchLocale, getFallbackLocale: () => fallbackLocale, setFallbackLocale: (locale) => { fallbackLocale = locale; }, hasTranslation, getTranslations, addTranslations, getSupportedLocales, isLocaleSupported, getLocaleName, getLocaleDirection, formatNumber, formatCurrency, formatDate, addLocaleListener, removeLocaleListener, clearLocaleListeners, initI18n, saveLocale, loadLocale, getDefaultLocale, }; /** * 预设语言包 */ export const presetLocales = { 'zh-CN': { name: '简体中文', nativeName: '简体中文', direction: 'ltr', translations: LOCALES['zh-CN'], }, 'en-US': { name: 'English (US)', nativeName: 'English (US)', direction: 'ltr', translations: LOCALES['en-US'], }, }; /** * 创建国际化管理器 */ export const createI18nManager = () => { return { t, getCurrentLocale, setCurrentLocale, switchLocale, getFallbackLocale: () => fallbackLocale, setFallbackLocale: (locale) => { fallbackLocale = locale; }, hasTranslation, getTranslations, addTranslations, getSupportedLocales, isLocaleSupported, getLocaleName, getLocaleDirection, formatNumber, formatCurrency, formatDate, addLocaleListener, removeLocaleListener, clearLocaleListeners, initI18n, saveLocale, loadLocale, getDefaultLocale, }; }; export { i18nUtils as default };