Skip to content

国际化提供者

国际化提供者是 DVHA 的核心概念之一,它是一个抽象层,负责处理应用程序的多语言和国际化功能。通过国际化提供者,您可以轻松地为应用添加多语言支持,提供翻译、语言切换等功能。

什么是国际化提供者?

国际化提供者是一个实现了特定接口的对象,它定义了如何处理应用的国际化需求。DVHA 通过国际化提供者来执行所有的国际化操作,包括:

  • 文本翻译 (t)
  • 语言切换 (changeLocale)
  • 语言包加载 (loadLocale)
  • 语言包合并 (mergeLocale)
  • 获取当前语言 (getLocale)
  • 获取支持的语言列表 (getLocales)

内置国际化提供者

DVHA 内置了 i18nProvider,基于 vue-i18n 实现:

typescript
import { i18nProvider } from '@duxweb/dvha-core'

const provider = i18nProvider({
  locale: 'zh-CN',
  fallbackLocale: 'en-US',
  messages: {
    'zh-CN': {
      welcome: '欢迎',
      hello: '你好 {name}'
    },
    'en-US': {
      welcome: 'Welcome',
      hello: 'Hello {name}'
    }
  }
})

const config: IConfig = {
  i18nProvider: provider,
  // ... 其他配置
}

TIP

内置的 i18nProvider 基于成熟的 vue-i18n 库,提供完整的国际化功能,包括插值、复数、日期时间格式化等特性。它使用 Composition API 模式,支持 TypeScript 类型推导。

国际化提供者接口

typescript
interface I18nProvider {
  // 翻译文本
  t: (key: string, options?: any, defaultMessage?: string) => string
  // 切换语言
  changeLocale: (lang: string, options?: any) => Promise<any>
  // 加载语言包
  loadLocale: (lang: string, files: Record<string, unknown>) => Promise<any>
  // 合并语言包
  mergeLocale: (lang: string, messages: Record<string, unknown>) => void
  // 获取当前语言
  getLocale: () => string
  // 获取支持的语言列表
  getLocales: () => string[]
}

配置选项

I18nOptions

基于 vue-i18n 的配置选项:

typescript
interface I18nOptions {
  locale?: string // 默认语言
  fallbackLocale?: string | string[] // 回退语言
  messages?: Record<string, any> // 语言包
  legacy?: boolean // 是否使用 legacy API(默认 false)
  missingWarn?: boolean // 缺失翻译警告(默认 false)
  fallbackWarn?: boolean // 回退警告(默认 false)
  [key: string]: any // 其他 vue-i18n 选项
}

配置说明

内置的 i18nProvider 自动设置了以下默认值:

  • legacy: false - 使用 Composition API 模式
  • missingWarn: false - 关闭缺失翻译警告
  • fallbackWarn: false - 关闭回退警告

使用示例

基本配置

typescript
import { i18nProvider } from '@duxweb/dvha-core'
import enUS from './locales/en-US.json'
import zhCN from './locales/zh-CN.json'

const config: IConfig = {
  i18nProvider: i18nProvider({
    locale: 'zh-CN',
    fallbackLocale: 'en-US',
    messages: {
      'zh-CN': zhCN,
      'en-US': enUS
    }
  }),
  // ... 其他配置
}

语言包结构

推荐的语言包结构:

json
// locales/zh-CN.json
{
  "common": {
    "save": "保存",
    "cancel": "取消",
    "confirm": "确认",
    "delete": "删除"
  },
  "user": {
    "profile": "用户资料",
    "name": "姓名",
    "email": "邮箱"
  },
  "message": {
    "saveSuccess": "保存成功",
    "deleteConfirm": "确定要删除 {name} 吗?"
  }
}
json
// locales/en-US.json
{
  "common": {
    "save": "Save",
    "cancel": "Cancel",
    "confirm": "Confirm",
    "delete": "Delete"
  },
  "user": {
    "profile": "User Profile",
    "name": "Name",
    "email": "Email"
  },
  "message": {
    "saveSuccess": "Save successfully",
    "deleteConfirm": "Are you sure to delete {name}?"
  }
}

高级配置

typescript
import { i18nProvider } from '@duxweb/dvha-core'

const provider = i18nProvider({
  locale: 'zh-CN',
  fallbackLocale: ['en-US', 'zh-CN'],
  messages: {
    'zh-CN': {
      // 支持插值
      greeting: '你好,{name}!',
      // 支持复数
      item: '没有项目 | 1 个项目 | {count} 个项目',
      // 支持嵌套
      nav: {
        home: '首页',
        about: '关于'
      }
    },
    'en-US': {
      greeting: 'Hello, {name}!',
      item: 'no items | 1 item | {count} items',
      nav: {
        home: 'Home',
        about: 'About'
      }
    }
  }
})

动态语言包加载

typescript
import { i18nProvider } from '@duxweb/dvha-core'

const provider = i18nProvider({
  locale: 'zh-CN',
  fallbackLocale: 'en-US',
  messages: {
    'zh-CN': {},
    'en-US': {}
  }
})

// 动态加载语言包
async function loadLanguagePack(lang: string) {
  try {
    const messages = await import(`./locales/${lang}.json`)
    await provider.loadLocale(lang, messages.default)
    await provider.changeLocale(lang)
    console.log(`Language pack loaded: ${lang}`)
  } catch (error) {
    console.error(`Failed to load language pack: ${lang}`, error)
  }
}

// 动态合并语言包(用于插件或模块的额外翻译)
function mergeModuleTranslations(lang: string, moduleMessages: Record<string, unknown>) {
  provider.mergeLocale(lang, moduleMessages)
}

// 使用示例
loadLanguagePack('ja-JP')

// 合并额外的翻译
mergeModuleTranslations('zh-CN', {
  plugin: {
    name: '插件名称',
    settings: '插件设置'
  }
})

获取语言信息

typescript
// 获取当前语言
const currentLang = provider.getLocale() // 'zh-CN'

// 获取所有支持的语言
const availableLanguages = provider.getLocales() // ['zh-CN', 'en-US']

// 检查语言是否支持
function isLanguageSupported(lang: string): boolean {
  return provider.getLocales().includes(lang)
}

自定义国际化提供者

您也可以创建自定义的国际化提供者来适配特定的需求:

typescript
import type { I18nProvider } from '@duxweb/dvha-core'

// 基于其他 i18n 库的提供者
function customI18nProvider(options: any): I18nProvider {
  // 初始化您的 i18n 实例
  const i18n = createYourI18nInstance(options)

  return {
    t: (key: string, options?: any, defaultMessage?: string): string => {
      const result = i18n.translate(key, options)
      return result || defaultMessage || key
    },

    changeLocale: async (lang: string, options?: any): Promise<any> => {
      await i18n.setLocale(lang)
      return lang
    },

    loadLocale: async (lang: string, files: Record<string, unknown>): Promise<any> => {
      i18n.addResourceBundle(lang, 'translation', files)
      return lang
    },

    mergeLocale: (lang: string, messages: Record<string, unknown>): void => {
      i18n.mergeResourceBundle(lang, 'translation', messages)
    },

    getLocale: (): string => {
      return i18n.language
    },

    getLocales: (): string[] => {
      return i18n.languages || []
    }
  }
}

与管理端集成

国际化提供者可以在全局配置,也可以在特定管理端中配置:

typescript
const config: IConfig = {
  // 全局国际化提供者
  i18nProvider: i18nProvider({
    locale: 'zh-CN',
    fallbackLocale: 'en-US',
    messages: globalMessages
  }),

  manages: [
    {
      name: 'admin',
      title: '管理后台',
      // 管理端专用的国际化提供者
      i18nProvider: i18nProvider({
        locale: 'en-US',
        fallbackLocale: 'zh-CN',
        messages: adminMessages
      })
    },
    {
      name: 'merchant',
      title: '商户后台',
      // 商户端专用的国际化提供者
      i18nProvider: i18nProvider({
        locale: 'zh-CN',
        fallbackLocale: 'en-US',
        messages: merchantMessages
      })
    }
  ]
}

与 useI18n Hook 集成

国际化提供者通过 useI18n Hook 在组件中使用:

typescript
import { useI18n } from '@duxweb/dvha-core'

export default {
  setup() {
    const { t, changeLocale, getLocale, loadLocale, mergeLocale, getLocales } = useI18n()

    // 基本翻译
    const title = t('page.title', null, '默认标题')

    // 带参数的翻译
    const greeting = t('welcome.message', { name: 'John' })

    // 切换语言
    const switchLanguage = async (lang: string) => {
      await changeLocale(lang)
    }

    // 动态加载语言包
    const loadNewLanguage = async (lang: string) => {
      const messages = await import(`./locales/${lang}.json`)
      await loadLocale(lang, messages.default)
      await changeLocale(lang)
    }

    return {
      title,
      greeting,
      switchLanguage,
      loadNewLanguage,
      currentLang: getLocale(),
      availableLanguages: getLocales()
    }
  }
}

最佳实践

1. 语言包组织

locales/
├── zh-CN/
│   ├── common.json      # 通用翻译
│   ├── user.json        # 用户相关
│   └── admin.json       # 管理端
├── en-US/
│   ├── common.json
│   ├── user.json
│   └── admin.json
└── index.ts             # 语言包入口

2. 语言包合并

typescript
// locales/index.ts
import enUSAdmin from './en-US/admin.json'
import enUSCommon from './en-US/common.json'
import enUSUser from './en-US/user.json'

import zhCNAdmin from './zh-CN/admin.json'
import zhCNCommon from './zh-CN/common.json'
import zhCNUser from './zh-CN/user.json'

export const messages = {
  'zh-CN': {
    ...zhCNCommon,
    ...zhCNUser,
    ...zhCNAdmin
  },
  'en-US': {
    ...enUSCommon,
    ...enUSUser,
    ...enUSAdmin
  }
}

3. 类型安全

typescript
// types/i18n.ts
export interface LocaleMessages {
  common: {
    save: string
    cancel: string
    confirm: string
  }
  user: {
    profile: string
    name: string
    email: string
  }
}

declare module '@duxweb/dvha-core' {
  interface I18nProvider {
    t: <T extends keyof LocaleMessages>(
      key: T,
      options?: any,
      defaultMessage?: string
    ) => string
  }
}

4. 语言包模块化

typescript
// 模块化加载语言包
const loadModuleMessages = async (moduleName: string, lang: string) => {
  try {
    const module = await import(`./modules/${moduleName}/locales/${lang}.json`)
    provider.mergeLocale(lang, {
      [moduleName]: module.default
    })
  } catch (error) {
    console.warn(`Failed to load ${moduleName} messages for ${lang}`)
  }
}

// 批量加载模块
const loadAllModuleMessages = async (lang: string) => {
  const modules = ['user', 'product', 'order']
  await Promise.all(
    modules.map(module => loadModuleMessages(module, lang))
  )
}

5. 默认值策略

typescript
// 智能默认值函数
const smartTranslate = (key: string, options?: any, fallback?: string) => {
  // 尝试翻译
  const result = provider.t(key, options)

  // 如果翻译失败,使用策略生成默认值
  if (result === key) {
    if (fallback) return fallback

    // 从键值生成默认值
    const parts = key.split('.')
    const lastPart = parts[parts.length - 1]
    return lastPart.charAt(0).toUpperCase() + lastPart.slice(1)
  }

  return result
}

性能优化

1. 按需加载语言包

typescript
const lazyLoadLanguagePack = async (lang: string) => {
  // 检查是否已经加载
  if (provider.getLocales().includes(lang)) {
    return provider.changeLocale(lang)
  }

  // 动态导入语言包
  const [common, specific] = await Promise.all([
    import(`./locales/common/${lang}.json`),
    import(`./locales/specific/${lang}.json`)
  ])

  // 合并并加载
  const merged = { ...common.default, ...specific.default }
  await provider.loadLocale(lang, merged)
  return provider.changeLocale(lang)
}

2. 翻译缓存

typescript
// 创建翻译缓存
const translationCache = new Map<string, string>()

const cachedTranslate = (key: string, options?: any, defaultMessage?: string) => {
  const cacheKey = `${key}:${JSON.stringify(options)}`

  if (translationCache.has(cacheKey)) {
    return translationCache.get(cacheKey)!
  }

  const result = provider.t(key, options, defaultMessage)
  translationCache.set(cacheKey, result)

  return result
}

// 清理缓存(语言切换时)
const clearTranslationCache = () => {
  translationCache.clear()
}

小提示

  • 使用命名空间来组织翻译键值,避免冲突
  • 为常用的翻译文本提供默认值
  • 定期检查和清理未使用的翻译键值
  • 考虑使用工具来自动化翻译流程
  • 利用 mergeLocale 方法实现插件化的翻译扩展
  • 使用 getLocales 方法动态构建语言切换器

下一步

了解如何在组件中使用国际化功能: