前端国际化方案

前端国际化方案

通用型前端国际化方案,在Vue、React中都适用,已落地实践。

一、常见的国际化方案

1.1 通过 i18n 插件配置语种

这是最传统的国际化方案,使用成熟的国际化插件(如 vue-i18nreact-i18next 等)进行配置。

核心流程:

  1. 安装并配置国际化插件
  2. 创建语言包文件(如 zh-CN.jsonen-US.json
  3. 在代码中使用 key 调用翻译函数

示例配置:

javascript 复制代码
// i18n.js
import { createI18n } from 'vue-i18n'
import zhCN from './locales/zh-CN.json'
import enUS from './locales/en-US.json'

const i18n = createI18n({
  locale: 'zh-CN',
  fallbackLocale: 'en-US',
  messages: {
    'zh-CN': zhCN,
    'en-US': enUS
  }
})

export default i18n

语言包示例:

json 复制代码
// locales/zh-CN.json
{
  "nav.home": "首页",
  "nav.about": "关于我们",
  "form.submit": "提交"
}
json 复制代码
// locales/en-US.json
{
  "nav.home": "Home",
  "nav.about": "About Us",
  "form.submit": "Submit"
}

代码中使用:

vue 复制代码
<template>
  <div>
    <h1>{{ $t('nav.home') }}</h1>
    <button>{{ $t('form.submit') }}</button>
  </div>
</template>

<script setup>
import { useI18n } from 'vue-i18n'

const { t } = useI18n()
console.log(t('nav.home'))
</script>

优点:

  • 生态成熟,文档完善
  • 支持复数、日期格式化等高级功能
  • 社区活跃,问题容易解决

缺点:

  • 需要手动维护所有 key
  • 新增文本时需要同时修改代码和语言包
  • 容易出现 key 命名冲突

1.2 使用云文档维护语言包

通过在线文档(如腾讯文档、飞书文档、Google Sheets)维护语言包,通过 API 请求动态加载。

核心流程:

  1. 在云文档中创建多语言对照表
  2. 开发前置请求接口,获取语言包数据
  3. 注册到国际化系统中
  4. 代码中使用 key 调用

云文档结构示例:

key zh-CN en-US ja-JP
nav.home 首页 Home ホーム
nav.about 关于我们 About Us 私たちについて

代码实现:

javascript 复制代码
// i18n-loader.js
export async function loadLocaleFromCloud(locale) {
  const response = await fetch(`/api/i18n/${locale}`)
  const messages = await response.json()
  
  i18n.setLocaleMessage(locale, messages)
  i18n.locale = locale
}

// 应用启动时加载
await loadLocaleFromCloud('zh-CN')

优点:

  • 非技术人员可参与翻译维护
  • 支持实时更新的翻译内容
  • 便于多语言协作翻译

缺点:

  • 依赖网络请求,首屏加载可能变慢
  • 需要处理加载状态和错误情况
  • 云文档的稳定性影响应用

1.3 使用自动国际化插件

通过 AST(抽象语法树)分析代码中的中文文本,自动生成 hash 值作为 key,并建立多语言映射。

核心原理:

  1. 在构建阶段扫描代码中的中文字符串
  2. 对中文内容进行 hash 计算生成唯一 key
  3. 自动调用翻译 API 生成多语言版本
  4. 运行时根据 hash 值映射对应语种

工作流程图:

复制代码
源代码(含中文)
    ↓
AST 语法树分析
    ↓
提取中文字符串 → 生成 hash 值
    ↓
调用翻译 API → 生成多语言包
    ↓
替换代码中的中文为 hash key
    ↓
构建输出

示例:

javascript 复制代码
// 原始代码
const title = '药物研发'
const placeholder = '输入药品名称'

// 构建后代码
const title = $t('h24lnb4')  // hash 值
const placeholder = $t('k25g8h4')

自动生成的语言包:

json 复制代码
{
  "h24lnb4": {
    "zh-CN": "药物研发",
    "en": "drug development"
  },
  "k25g8h4": {
    "zh-CN": "输入药品名称",
    "en": "Enter drug name"
  }
}

优点:

  • 开发体验好,直接写中文
  • 自动化程度高,减少人工成本
  • 不需要手动维护 key 命名

缺点:

  • 机翻质量可能不准确
  • 无法处理上下文相关的翻译
  • 需要处理 hash 冲突

二、自动化插件 + 自定义 key 结合

结合自动化和手动配置的优势,先通过自动化插件扫描翻译,再对特殊内容进行自定义配置。

2.1 自动化翻译流程

开发过程中 95% 的场景可以直接写中文,构建时自动翻译:

配置文件中使用:

javascript 复制代码
export const baseConfig = [{
  id: "mixed",
  title: "综合检索",
  group: "基础信息",
  type: "single",
  fieldKey: "mixed",
  placeholder: "输入药品名称/研发企业/适应症/靶点/工艺技术/药物类型/药物标签"
}]

模板中使用:

vue 复制代码
<a-checkbox
  v-if="current === 1"
  :checked="indicationProgressChina"
  @change="handleChinaChange"
  >仅看全球、中国研发阶段</a-checkbox>

自动生成的语言包:

构建工具会自动扫描并生成 src/lang/index.json

json 复制代码
{
  "h24lnb4": {
    "zh-CN": "药物研发",
    "en": "drug development"
  },
  "k25g8h4": {
    "zh-CN": "高级检索",
    "en": "Advanced Search"
  },
  "ag1z3r4": {
    "zh-CN": "下载记录",
    "en": "Download History"
  }
}

注意: 自动生成的语言包文件无需手动修改,在多人协作中应避免直接修改,防止合并冲突。


2.2 重置机翻内容

对于机翻不准确的内容,通过自定义文件进行覆盖:

创建 src/lang/locales/custom.json

json 复制代码
{
  "h24lnb4": {
    "zh-CN": "药物研发",
    "en": "Drug R&D"
  },
  "k25g8h4": {
    "zh-CN": "高级检索",
    "en": "Advanced Search"
  },
  "phz52": {
    "zh-CN": "重置",
    "en": "Reset"
  }
}

建议:

  • 由专人负责维护自定义翻译内容
  • 保持术语的一致性
  • 定期审查和更新翻译质量

2.3 自定义语言包配置

当自动解析未被识别或识别错误时,采用手动配置方式。

(1)业务模块级配置

在任何业务模块目录下创建语言包文件,会被自动注册:

复制代码
src/views/database/developdb/
├── locales/
│   ├── zh-CN.ts
│   └── en-US.ts
└── components/

zh-CN.ts:

typescript 复制代码
export default {
  "developdb.list.title": "药物研发",
  "developdb.list.des": "描述信息",
  "developdb.btn.add": "添加记录"
}

en-US.ts:

typescript 复制代码
export default {
  "developdb.list.title": "Drug Development",
  "developdb.list.des": "Description",
  "developdb.btn.add": "Add Record"
}

建议: 定义 key 时带上模块前缀,避免与其他模块冲突。

(2)通用组件级配置

将通用组件、工具函数的语言包配置在统一目录:

复制代码
src/lang/locales/
├── zh-CN.ts
├── en-US.ts
└── locales/
    ├── zh-CN.ts
    └── en-US.ts

src/lang/locales/locales/zh-CN.ts:

typescript 复制代码
export default {
  "component.title": "组件名称",
  "common.confirm": "确认",
  "common.cancel": "取消"
}
(3)拆分文件管理

当语言包内容较多时,可以拆分到多个文件:

复制代码
src/lang/locales/locales/zh-CN/
├── index.ts
├── common.ts
├── component.ts
└── hooks.ts

index.ts:

typescript 复制代码
import common from './common'
import component from './component'
import hooks from './hooks'

export default {
  common,
  component,
  hooks,
  "component.title": "组件名称"
}

2.4 使用方式

在代码中使用 $t 函数调用自定义 key:

在 script setup 中:

vue 复制代码
<script setup lang="ts">
const title = $t("developdb.list.title")
const confirmText = $t("common.confirm")
</script>

在模板中:

vue 复制代码
<template>
  <div>
    <h1>{{ $t('component.title') }}</h1>
    <button>{{ $t('common.confirm') }}</button>
  </div>
</template>

2.5 优先级规则

系统按照以下优先级加载语言包:

  1. 自定义语言包优先级最高

    • src/lang/locales/locales 目录
    • 业务模块 locales 目录
  2. 自动生成语言包优先级较低

    • 构建时扫描生成的 key
  3. 同级别后定义的覆盖先定义的

    • 配置相同 key 时,后面的会覆盖前面的

优先级示意图:

复制代码
┌─────────────────────────┐
│   自定义语言包 (最高)    │
│   src/lang/locales/     │
├─────────────────────────┤
│   业务模块语言包         │
│   views/*/locales/      │
├─────────────────────────┤
│   自动生成语言包 (最低)  │
│   src/lang/index.json   │
└─────────────────────────┘

三、封装通用组件、hook

3.1 语种状态管理 Hook

封装语种管理的通用逻辑,提供统一的 API:

src/store/lang.ts:

typescript 复制代码
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useLangModel = defineStore('lang', () => {
  // 当前语种
  const currentLang = ref<'zh-CN' | 'en-US'>('zh-CN')
  
  // 语种列表
  const langList = [
    { label: '中文', value: 'zh-CN' },
    { label: 'English', value: 'en-US' }
  ]
  
  // 切换语种
  const setLang = (lang: 'zh-CN' | 'en-US') => {
    currentLang.value = lang
    localStorage.setItem('lang', lang)
    // 通知 i18n 更新
    i18n.global.locale = lang
  }
  
  // 获取存储的语种
  const initLang = () => {
    const saved = localStorage.getItem('lang') as 'zh-CN' | 'en-US'
    if (saved) {
      currentLang.value = saved
    }
  }
  
  return {
    currentLang,
    langList,
    setLang,
    initLang
  }
})

使用示例:

vue 复制代码
<script setup lang="ts">
import { useLangModel } from '@/store/lang'

const langStore = useLangModel()

// 获取当前语种
console.log(langStore.currentLang)

// 切换语种
langStore.setLang('en-US')
</script>

<template>
  <a-select v-model:value="langStore.currentLang" @change="langStore.setLang">
    <a-select-option 
      v-for="lang in langStore.langList" 
      :key="lang.value"
      :value="lang.value"
    >
      {{ lang.label }}
    </a-select-option>
  </a-select>
</template>

3.2 LangProvider 组件

封装语种条件渲染组件,用于不同语种显示不同内容:

src/components/LangProvider/Index.vue:

vue 复制代码
<script setup lang="ts">
import { computed } from 'vue'
import { useLangModel } from '@/store/lang'

const langStore = useLangModel()
const currentLang = computed(() => langStore.currentLang)
</script>

<template>
  <slot :name="currentLang"></slot>
  <slot v-if="!$slots[currentLang]"></slot>
</template>

使用示例:

vue 复制代码
<script setup>
import LangProvider from '@/components/LangProvider/Index.vue'
</script>

<template>
  <LangProvider>
    <template #zh-CN>
      <img src="/imgs/logo.svg" alt="中文 Logo">
    </template>
    <template #en-US>
      <img src="/imgs/logo-en.svg" alt="English Logo">
    </template>
  </LangProvider>
</template>

适用场景:

  • 不同语种使用不同的图片资源
  • 不同语种布局差异较大
  • 特定语种的特殊内容

3.3 通用翻译 Hook

封装翻译相关的通用函数:

src/hooks/useTranslation.ts:

typescript 复制代码
import { useI18n } from 'vue-i18n'

export function useTranslation() {
  const { t, locale } = useI18n()
  
  // 翻译文本
  const translate = (key: string, params?: Record<string, any>) => {
    return t(key, params)
  }
  
  // 判断是否为中文
  const isZhCN = computed(() => locale.value === 'zh-CN')
  
  // 判断是否为英文
  const isEnUS = computed(() => locale.value === 'en-US')
  
  // 获取当前语种标签
  const currentLangLabel = computed(() => {
    return isZhCN.value ? '中文' : 'English'
  })
  
  return {
    translate,
    t,
    locale,
    isZhCN,
    isEnUS,
    currentLangLabel
  }
}

使用示例:

vue 复制代码
<script setup lang="ts">
import { useTranslation } from '@/hooks/useTranslation'

const { t, isZhCN, currentLangLabel } = useTranslation()
</script>

<template>
  <div>
    <h1>{{ t('page.title') }}</h1>
    <p>当前语言:{{ currentLangLabel }}</p>
    <div v-if="isZhCN">
      <!-- 仅中文显示的内容 -->
    </div>
  </div>
</template>

四、涉及的范围

4.1 字符长度适配

英文文本长度通常比中文长 30%-50%,需要特别注意 UI 适配:

常见问题:

图:中文"药物研发"与英文"Drug Research and Development"的长度对比

解决方案:

  1. 使用弹性布局

    vue 复制代码
    <template>
      <button class="i18n-btn">{{ $t('action.submit') }}</button>
    </template>
    
    <style scoped>
    .i18n-btn {
      min-width: 80px;
      padding: 0 16px;
      white-space: nowrap;
    }
    </style>
  2. 设置最大宽度 + 换行

    css 复制代码
    .i18n-text {
      max-width: 200px;
      word-break: break-word;
    }
  3. 使用 CSS Grid 自适应

    css 复制代码
    .i18n-grid {
      display: grid;
      grid-template-columns: auto 1fr;
      gap: 16px;
    }
  4. 针对不同语种调整样式

    vue 复制代码
    <template>
      <div :class="['container', `lang-${currentLang}`]">
        <!-- 内容 -->
      </div>
    </template>
    
    <style>
    .container.lang-zh-CN {
      font-size: 14px;
    }
    .container.lang-en-US {
      font-size: 12px;
    }
    </style>

4.2 三方包的国际化

检查引入的三方包是否内置中文,是否提供切换语种的 API。

已适配的常见三方包:

javascript 复制代码
// 1. Ant Design Vue
import zhCN from 'ant-design-vue/es/locale/zh_CN'
import enUS from 'ant-design-vue/es/locale/en_US'

const locale = computed(() => currentLang.value === 'zh-CN' ? zhCN : enUS)

// 2. Element Plus
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import en from 'element-plus/es/locale/lang/en'

const locale = computed(() => currentLang.value === 'zh-CN' ? zhCn : en)

// 3. Day.js
import 'dayjs/locale/zh-cn'
import 'dayjs/locale/en'

watch(currentLang, (lang) => {
  dayjs.locale(lang === 'zh-CN' ? 'zh-cn' : 'en')
})

// 4. 绘图库(如 tqsk-draw)
import { setLocale } from 'tqsk-draw'
setLocale(currentLang.value)

检查清单:

  • 日期时间库(Day.js、Moment.js)
  • UI 组件库(Ant Design、Element Plus)
  • 图表库(ECharts、AntV)
  • 富文本编辑器
  • 表格组件
  • 表单验证库

未内置国际化的处理:

javascript 复制代码
// 通过全局配置覆盖
const customLocale = {
  ...defaultLocale,
  lang: {
    ...defaultLocale.lang,
    locale: currentLang.value,
    // 自定义翻译
    submitText: $t('form.submit'),
    cancelText: $t('form.cancel')
  }
}

4.3 图片资源国际化

图片上的文字也需要进行国际化处理。

处理方案:

  1. 使用 SVG Icon 替代图片文字

    vue 复制代码
    <template>
      <!-- 不推荐:图片包含文字 -->
      <img src="/imgs/banner-cn.png" />
      
      <!-- 推荐:使用图标 + 文字分离 -->
      <div class="banner">
        <IconLogo />
        <span>{{ $t('banner.title') }}</span>
      </div>
    </template>
  2. 分语种提供图片资源

    vue 复制代码
    <template>
      <LangProvider>
        <template #zh-CN>
          <img src="/imgs/guide-cn.png" />
        </template>
        <template #en-US>
          <img src="/imgs/guide-en.png" />
        </template>
      </LangProvider>
    </template>
  3. 使用 CSS 动态替换

    css 复制代码
    .logo {
      background-image: url('/imgs/logo.svg');
    }
    
    html[lang="en"] .logo {
      background-image: url('/imgs/logo-en.svg');
    }

检查清单:

  • Logo 图片
  • Banner 图
  • 引导页图片
  • 帮助文档截图
  • 流程图、架构图
  • 空状态插图

4.4 其他注意事项

1. 日期时间格式

不同地区的日期格式不同:

javascript 复制代码
// 中国:2024 年 1 月 15 日
// 美国:01/15/2024
// 欧洲:15/01/2024

const formatDate = (date) => {
  return new Intl.DateTimeFormat(currentLang.value).format(date)
}

2. 数字格式

javascript 复制代码
// 中国:1,234,567.89
// 德国:1.234.567,89
// 法国:1 234 567,89

const formatNumber = (num) => {
  return new Intl.NumberFormat(currentLang.value).format(num)
}

3. 货币格式

javascript 复制代码
const formatCurrency = (amount) => {
  return new Intl.NumberFormat(currentLang.value, {
    style: 'currency',
    currency: currentLang.value === 'zh-CN' ? 'CNY' : 'USD'
  }).format(amount)
}

4. 文本方向(RTL 支持)

css 复制代码
/* 支持从右到左的语言 */
html[dir="rtl"] {
  direction: rtl;
}

五、vite-auto-i18n-plugin

5.1 插件介绍

vite-auto-i18n-plugin 是专为 Vite 构建工具设计的自动化国际化插件,通过 AST 分析实现中文自动翻译。

5.2 安装
bash 复制代码
npm install vite-auto-i18n-plugin -D
5.3 基础配置

vite.config.ts:

typescript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vitePluginsAutoI18n, { YoudaoTranslator } from 'vite-auto-i18n-plugin'

export default defineConfig({
  plugins: [
    vue(),
    vitePluginsAutoI18n({
        translator: new YoudaoTranslator({
            appId: '4cdb9baea8066fef',
            appKey: 'ONI6AerZnGRyDqr3w7UM730mPuF8mB3j'
        })
    })
  ]
})

六、webpack-auto-i18n-plugin

6.1 插件介绍

webpack-auto-i18n-plugin 是为 Webpack 构建工具设计的自动化国际化插件,功能与 Vite 插件类似。

6.2 安装
bash 复制代码
npm install webpack-auto-i18n-plugin -D
6.3 基础配置

webpack.config.js:

javascript 复制代码
const path = require('path')
const webpackPluginsAutoI18n = require('webpack-auto-i18n-plugin')

const i18nPlugin = new webpackPluginsAutoI18n.default({
    translator: new YoudaoTranslator({
        appId: 'appId',
        appKey: 'appKey'
    })
})

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true
  },
  plugins: [
    i18nPlugin
  ],
}

总结

本文介绍了前端国际化的完整方案,从常见的三种方案对比,到自动化插件与自定义 key 的结合使用,再到通用组件和 Hook 的封装,最后详细说明了在 Vite 和 Webpack 中的具体实现,并重点介绍了在 React 项目中的落地实践。

核心要点:

  1. 选择合适的方案:根据项目规模和技术栈选择最适合的国际化方案
  2. 自动化优先:优先使用自动化工具减少人工成本
  3. 自定义补充:对特殊内容进行手动配置保证翻译质量
  4. 组件化封装:通过通用组件和 Hook 提高代码复用
  5. 全面考虑:注意字符长度、三方包、图片等细节问题

最佳实践:

  • 开发阶段直接写中文,构建时自动翻译
  • 由专人维护自定义翻译,保证术语一致性
  • 使用 Hash 映射避免 key 命名冲突
  • 针对不同语种进行 UI 适配
  • 检查并适配三方包的国际化配置

技术栈无关:

  • ✅ Vue 2/3 + Vite/Webpack
  • ✅ React + Webpack
  • ✅ 其他支持 AST 分析的前端框架

通过这套方案,可以高效地实现前端国际化,并在长期维护中保持代码的可读性和可维护性。

核心要点:

  1. 选择合适的方案:根据项目规模和技术栈选择最适合的国际化方案
  2. 自动化优先:优先使用自动化工具减少人工成本
  3. 自定义补充:对特殊内容进行手动配置保证翻译质量
  4. 组件化封装:通过通用组件和 Hook 提高代码复用
  5. 全面考虑:注意字符长度、三方包、图片等细节问题

最佳实践:

  • 开发阶段直接写中文,构建时自动翻译
  • 由专人维护自定义翻译,保证术语一致性
  • 使用 Hash 映射避免 key 命名冲突
  • 针对不同语种进行 UI 适配
  • 检查并适配三方包的国际化配置

通过这套方案,可以高效地实现前端国际化,并在长期维护中保持代码的可读性和可维护性。

相关推荐
向上的车轮1 小时前
React 19 快速入门:拥抱服务端组件与新特性的现代化开发
前端·javascript·react.js
Smile_2542204182 小时前
vue3 + ts reactive方式清空表单对象
开发语言·前端·javascript
多租户观察室2 小时前
信通院标准体系2.0深度解读:低代码管理平台进入“精品竞争”时代
前端·低代码·程序员
云水一下2 小时前
CSS3从零基础到精通(四):终章大项目——纯CSS构建企业品牌展示网站
前端·css3
147API2 小时前
Claude Opus 4.8 接口与工程落地分析:长任务调用链应该怎么设计
java·前端·数据库
李子琪。2 小时前
Web 漏洞与防御机制实验报告
前端·经验分享
JustNow_Man2 小时前
“失败后自动拉起修复 Agent”的闭环流水线
前端·人工智能·chrome·python
Dxy12393102162 小时前
HTML中如何写键盘事件
前端·html·计算机外设
霍格沃兹测试学院-小舟畅学2 小时前
接口自动化测试的下一个十年:从脚本到Skills,让AI学会“如何测”
java·前端·人工智能