国际化

前言

为了支持多语言全球化运营,模板项目内置集成了 Vue I18n (v9+),默认支持简体中文、英文和俄语的切换。此外,系统设计了运行时持久化和 Cookie 联动机制,以确保前后端的多语言状态能够无缝保持一致。

国际化系统简介

项目中的国际化方案实现了以下两个维度的语言适配:

  1. 业务级语言包:存放于模板本地,管理所有页面表单、路由菜单、按钮及提示等业务相关的翻译字典。
  2. 组件库语言包:来自底层 Uniboot UI 依赖包(如表格分页文字、日期选择器提示等)。两者在初始化时进行混入合并,从而实现整站翻译的完整性。

语言编码统一使用 BCP 47 形式(带连字符),如 zh-CNen-USru-RU,与 src/locales/*.json 文件名一致。


语言文件目录结构

业务国际化翻译资源存放在 @/locales 目录下:

text
src/
└── locales/
    ├── zh-CN.json    # 简体中文翻译字典
    ├── en-US.json    # 英文翻译字典
    └── ru-RU.json    # 俄语翻译字典

字典顶层通常会包含语言名称本身(供语言切换菜单展示),例如:

json
{
  "en-US": "English",
  "zh-CN": "简体中文",
  "ru-RU": "Русский"
}

语言包分类说明

src/i18n.ts 中,本地翻译包与底层组件库自带的语言包进行解构混入:

ts
import enLocale from 'uniboot-ui/es/packages/locale/lang/en'
import ruLocale from 'uniboot-ui/es/packages/locale/lang/ru'
import zhCnLocale from 'uniboot-ui/es/packages/locale/lang/zh-cn'

import en from '@/locales/en-US.json'
import ru from '@/locales/ru-RU.json'
import zh from '@/locales/zh-CN.json'

在初始化时,底层依赖语言包(如 zhCnLocale)和本地语言包(如 zh)合并成同一套 messages,提供给应用程序消费。


国际化挂载与初始化

根组件插件注入

i18n 对象的创建和实例化逻辑位于 src/i18n.ts

ts
/** 项目实际支持的语言(与 src/locales/*.json 对应) */
export const SUPPORTED_LOCALES = ['zh-CN', 'en-US', 'ru-RU'] as const

export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number]

const i18n = createI18n({
  locale: getLocale() || 'zh-CN', // 默认从本地缓存加载已选语言,缺省为简体中文
  fallbackLocale: 'en-US', // 当前语言缺少词条时回退到英文
  legacy: false, // 必须为 false,才能在 Composition API 中使用
  messageResolver: undefined,
  messages: {
    'en-US': { ...enLocale, ...en },
    'zh-CN': { ...zhCnLocale, ...zh },
    'ru-RU': { ...ruLocale, ...ru },
  },
})

export const t = i18n.global.t

SUPPORTED_LOCALES 是项目语言白名单,语言切换组件与 setI18nLanguage 都会以它为准,避免误切到未提供业务字典的语言。

模板项目采用插件式自动挂载,无需在 main.ts 中手写注入。在 src/install/plugins/i18n.ts 中注册:

ts
import type { App } from 'vue'
import i18n from '@/i18n'

export default (app: App<Element>) => {
  app.use(i18n)
}

该文件会被 @/install/index.ts 自动扫描引入,并在程序引导时全局注入。


运行时语言切换机制

项目封装了统一的多语言切换辅助函数 setI18nLanguage(locale)。语言下拉组件(language-drop-down.vue)遍历 SUPPORTED_LOCALES,切换时调用该函数并刷新页面。

ts
export function setI18nLanguage(locale: string) {
  // 0. 白名单校验:不在 SUPPORTED_LOCALES 中的语言直接忽略
  if (!(SUPPORTED_LOCALES as readonly string[]).includes(locale)) {
    return
  }

  // 1. 更新全局 i18n 实例的 locale,触发表单 / 组件响应式翻译
  i18n.global.locale.value = locale as SupportedLocale

  // 2. 调用 setLocale 写入本地缓存,保证刷新后状态不丢失
  setLocale(locale)

  // 3. 修改 html 根标签的 lang 属性(如 <html lang="en-US">),兼顾 SEO 与无障碍
  document.querySelector('html')?.setAttribute('lang', locale)

  // 4. 与后端协同:SESSION_COOKIE 授权模式下写入名为 locale 的 Cookie,
  //    便于服务端读取并返回匹配语种的文案
  if (config.authMethod === AuthMethodEnum.SESSION_COOKIE) {
    Cookies.set('locale', locale)
  }
}

如何使用与新增翻译

在 Vue 模板中渲染

在 SFC 模板中可直接使用全局辅助函数 $t

vue
<template>
  <div class="card">
    <h2>{{ $t('login.welcome') }}</h2>
    <p>{{ $t('login.desc') }}</p>
  </div>
</template>

在脚本代码中调用

<script setup> 或通用 .ts 文件(路由守卫、网络拦截器、接口提示等)中,导入并使用全局导出的 t

vue
<script lang="ts" setup>
import { t } from '@/i18n'
import { feedback } from '@/utils/feedback'

function onSubmitSuccess() {
  feedback.msgSuccess(t('common.saveSuccess'))
}
</script>

添加新的国家语言包

若需引入新语种(例如繁体中文 zh-TW),按以下流程扩展:

  1. 新建字典文件:在 src/locales/ 下创建 zh-TW.json,填入文案(建议同时补充各现有语言包中对该语言名的翻译键):

    json
    {
      "zh-TW": "繁體中文",
      "login": {
        "welcome": "歡迎使用"
      }
    }
  2. 导入并挂载新包:打开 src/i18n.ts

    ts
    import zhTwLocale from 'uniboot-ui/es/packages/locale/lang/zh-tw'
    import tw from '@/locales/zh-TW.json'
    
    export const SUPPORTED_LOCALES = [
      'zh-CN',
      'en-US',
      'ru-RU',
      'zh-TW', 
    ] as const
    
    const i18n = createI18n({
      // ...
      messages: {
        'en-US': { ...enLocale, ...en },
        'zh-CN': { ...zhCnLocale, ...zh },
        'ru-RU': { ...ruLocale, ...ru },
        'zh-TW': { ...zhTwLocale, ...tw }, 
      },
    })
  3. 调用切换:语言菜单基于 SUPPORTED_LOCALES 自动展示新项;用户选择后调用 setI18nLanguage('zh-TW') 即可完成整站切换。