126 lines
3.8 KiB
TypeScript
126 lines
3.8 KiB
TypeScript
import i18n from 'i18next'
|
||
import { initReactI18next } from 'react-i18next'
|
||
import { NativeModules, Platform } from 'react-native'
|
||
import { storage } from './storage'
|
||
|
||
// 导入语言资源
|
||
import enUS from '../locales/en-US.json'
|
||
import zhCN from '../locales/zh-CN.json'
|
||
|
||
// 定义支持的语言
|
||
export const SUPPORTED_LANGUAGES = {
|
||
'zh-CN': '简体中文',
|
||
'en-US': 'English',
|
||
} as const
|
||
|
||
export type SupportedLanguage = keyof typeof SUPPORTED_LANGUAGES
|
||
|
||
// 获取系统语言(Android 和 iOS 兼容)
|
||
const getSystemLanguage = (): string => {
|
||
try {
|
||
let locale: string | undefined
|
||
|
||
if (Platform.OS === 'ios') {
|
||
// iOS: 从 SettingsManager 获取
|
||
const settings = NativeModules.SettingsManager?.settings
|
||
locale = settings?.AppleLocale || settings?.AppleLanguages?.[0]
|
||
} else if (Platform.OS === 'android') {
|
||
// Android: 从 I18nManager 获取
|
||
locale = NativeModules.I18nManager?.localeIdentifier
|
||
}
|
||
|
||
if (locale) {
|
||
// 处理语言代码格式,例如 'zh_CN' -> 'zh-CN', 'en_US' -> 'en-US'
|
||
const normalized = locale.replace('_', '-').toLowerCase()
|
||
if (normalized.startsWith('zh')) {
|
||
return 'zh-CN'
|
||
} else if (normalized.startsWith('en')) {
|
||
return 'en-US'
|
||
}
|
||
}
|
||
} catch (error) {
|
||
// 静默处理错误,使用默认语言
|
||
console.warn('Error getting system language:', error)
|
||
}
|
||
return 'zh-CN' // 默认返回中文
|
||
}
|
||
|
||
// AsyncStorage 语言检测器(兼容 Android 和 iOS)
|
||
const languageDetector = {
|
||
type: 'languageDetector' as const,
|
||
async: true,
|
||
detect: async (callback: (lng: string) => void) => {
|
||
try {
|
||
// 1. 优先使用用户保存的语言设置
|
||
const savedLanguage = await storage.getItem('app_language')
|
||
if (savedLanguage) {
|
||
// 兼容旧的语言代码格式
|
||
if (savedLanguage === 'zh' || savedLanguage === 'zh-CN') {
|
||
callback('zh-CN')
|
||
return
|
||
} else if (savedLanguage === 'en' || savedLanguage === 'en-US') {
|
||
callback('en-US')
|
||
return
|
||
}
|
||
}
|
||
|
||
// 2. 如果没有保存的语言,尝试使用系统语言
|
||
const systemLang = getSystemLanguage()
|
||
callback(systemLang)
|
||
} catch (error) {
|
||
console.error('Error detecting language:', error)
|
||
// 3. 如果出错,使用默认语言
|
||
callback('zh-CN')
|
||
}
|
||
},
|
||
init: () => { },
|
||
cacheUserLanguage: async (lng: string) => {
|
||
try {
|
||
// AsyncStorage 在 Android 和 iOS 上都正常工作
|
||
await storage.setItem('app_language', lng)
|
||
} catch (error) {
|
||
console.error('Error saving language:', error)
|
||
}
|
||
},
|
||
}
|
||
|
||
// i18n配置
|
||
i18n.use(languageDetector)
|
||
.use(initReactI18next)
|
||
.init({
|
||
// 语言资源
|
||
resources: {
|
||
'zh-CN': {
|
||
translation: zhCN,
|
||
},
|
||
'en-US': {
|
||
translation: enUS,
|
||
},
|
||
},
|
||
|
||
// 默认语言
|
||
fallbackLng: 'zh-CN',
|
||
|
||
// 调试模式(开发环境开启)
|
||
debug: false,
|
||
|
||
// 兼容性配置:使用 v3 格式处理复数,避免 Android 端 Intl.PluralRules 不支持的问题
|
||
compatibilityJSON: 'v3',
|
||
|
||
// 插值配置
|
||
interpolation: {
|
||
escapeValue: false, // React已经处理了XSS防护
|
||
},
|
||
|
||
// 命名空间配置
|
||
defaultNS: 'translation',
|
||
ns: ['translation'],
|
||
|
||
// 键值分隔符
|
||
keySeparator: '.',
|
||
nsSeparator: ':',
|
||
})
|
||
|
||
export default i18n
|
||
|