uni-app工程化实战:基于vue-i18n和i18n-ally的国际化方案 (下)

5. 处理动态内容的国际化

5.1 平台限制与解决方案

根据uni-app官方文档,由于运行平台限制,目前在小程序和App端不支持插值方式定义国际化。这意味着以下方式在小程序和App端无法正常工作:

复制代码
// 这种方式在小程序和App端不支持
t('hello', { name: '小明' }) // 使用命名参数
t('hello', ['小明']) // 使用数组参数

为了解决这个问题,我们需要使用自定义的插值方法来处理带参数的翻译。

5.2 带参数的翻译

复制代码
// 在语言文件中定义带占位符的文本
// zh-CN.json
{
  "greeting": "你好,{0}!",
  "welcome": "欢迎{0}来到{1}"
}

// 使用时传入参数
t('greeting', ['小明'])
// 输出:你好,小明!

t('welcome', ['小明', 'wot-design-uni'])
// 输出:欢迎小明来到wot-design-uni

注意:我们使用{0}, {1}这样的数字索引占位符,而不是使用命名参数如{name}。这是因为小程序和App端不支持命名参数的插值方式。

5.3 实现插值工具函数

复制代码
// src/locale/utils.ts
/**
 * 替换字符串中的占位符
 * @param template 模板字符串,如 "Hello {0}, welcome to {1}"
 * @param values 要替换的值数组
 * @returns 替换后的字符串
 */
export function interpolateTemplate(template: string, values: any[]): string {
  return template.replace(/{(\d+)}/g, (_, index) => values[index] ?? '')
}

5.4 扩展t函数支持数组参数

由于小程序和App端的限制,我们需要扩展vue-i18n的t函数,使其能够处理数组参数并应用我们的插值方法:

复制代码
// src/locale/index.ts
import { createI18n } from "vue-i18n"
import zhCN from "./zh-CN.json"
import enUS from "./en-US.json"
import { Locale } from "@wot-ui/ui"
import WotEnUS from "@wot-ui/ui/locale/lang/en-US"
import { interpolateTemplate } from "./utils"

Locale.add({ "en-US": WotEnUS })

const messages = {
  "zh-CN": {
    ...zhCN
  },
  "en-US": {
    ...enUS
  }
}

// 创建i18n实例
const i18n = createI18n({
  locale: uni.getStorageSync("currentLang") || "zh-CN", // 默认语言
  fallbackLocale: "zh-CN", // 回退语言
  messages, // 语言包
  legacy: false, // 启用Composition API模式
  globalInjection: true // 全局注入 $t 等方法到模板
})

// 同步组件库语言
Locale.use(i18n.global.locale.value)
uni.setLocale(i18n.global.locale.value)

// 扩展t函数,支持数组参数插值
// 这是解决小程序和App端不支持插值方式的关键步骤
const originalT = i18n.global.t
i18n.global.t = (key, param1, param2) => {
  const result = originalT(key, param1, param2)
  // 检测是否传入了数组参数,如果是则使用我们的插值方法处理
  if (Array.isArray(param1)) {
    return interpolateTemplate(result, param1)
  }
  return result
}

export default i18n

这种扩展方式的优点是:

  1. 保持了与vue-i18n原有API的兼容性
  2. 在小程序和App端也能使用类似的参数传递方式
  3. 统一了不同平台的国际化使用体验

需要注意的是我们仅实现了数组参数的插值,如果需要支持更多参数类型,可以进一步扩展。

5.5 使用示例

复制代码
<template>
  <!-- 在模板中使用 -->
  <view>{{ $t('greeting', [username]) }}</view>
  <view>{{ $t('welcome', [username, 'wot-design-uni']) }}</view>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()
const username = ref('小明')

// 在JS/TS代码中使用
const message = t('greeting', [username.value])
</script>

注意:这种方法在H5、App和小程序等所有平台上都能正常工作,因为我们使用了自定义的插值方法来处理参数,绕过了小程序和App端的限制。

6. 组件库的国际化

WotUI组件库本身支持国际化,我们只需要参考国际化文档进行配置即可。

6.1 同步应用和组件库的语言

使用useI18nSync钩子可以同步应用和组件库的语言设置:

复制代码
// 同步组件库语言设置
if (syncComponentLib) {
  Locale.use(locale)
}

7. pages.json的国际化

注意:建议仔细阅读uni-app国际化文档

7.1 页面标题国际化

在uni-app中,pages.json中的页面标题可以通过占位符实现国际化:

复制代码
// pages.json
{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "%index-title%"
      }
    }
  ]
}

7.2 语言文件配置

在语言文件中添加对应的翻译:

复制代码
// zh-CN.json
{
  "index-title": "首页"
}

// en-US.json
{
  "index-title": "Home"
}

7.3 平台差异处理

  • H5平台:直接支持占位符方式
  • 其他平台:可以使用自定义Tabbar和Navbar实现

注意:小程序下不支持这种国际化方案,也可以使用设置tabbar和navigationbar的API来设置文字。或者废弃原生tabbar和navigationbar,使用自定义方式,详情参考uni-app官方文档

8. i18n-ally插件

8.1 插件概述

i18n-ally是VSCode的一款国际化插件,提供以下核心功能:

  • 自动检测项目中的硬编码文本
  • 一键提取待翻译内容
  • 多语言文件管理
  • 翻译辅助工具集成

8.2 安装与配置

  1. 安装插件

    • 在VSCode扩展市场搜索并安装i18n-ally
    • 安装完成后重启VSCode
  2. 基本配置

    • 在项目根目录创建.vscode/settings.json文件
    • 添加以下基础配置:

    // .vscode/settings.json
    {
    "i18n-ally.sourceLanguage": "zh-CN", // 项目的源语言(基准语言),用来作为自动翻译的参考基准
    "i18n-ally.displayLanguage": "zh-CN", // VS Code 插件界面中默认显示的语言(如悬浮提示中显示的翻译预览)
    "i18n-ally.localesPaths": ["src/locale"], // 翻译文件(如 zh-CN.json, en-US.json)的存放目录
    "i18n-ally.keystyle": "flat", // 翻译键的样式:flat(扁平单层,如 "settings.title")或 nested(嵌套 JSON 对象)
    "i18n-ally.sortKeys": true, // 保存翻译文件时,是否按字母顺序对多语言键名进行自动排序
    }

8.3 核心功能详解

8.3.1 自动检测
  • 扫描范围:HTML标签内容、Vue模板、JS/TS代码

  • 支持配置检测规则:

    复制代码
    "i18n-ally.extract.parsers.html": {
      "attributes": ["text", "title", "alt", "placeholder", "label", "aria-label"], // 提取文本时,允许扫描和提取的 HTML/组件属性
      "ignoredTags": ["script", "style"], // 提取文本时忽略的 HTML 标签,避免误提取
      "vBind": true, // 是否支持提取 Vue 绑定属性(如 :title="xxx")
      "inlineText": true // 是否支持提取 HTML 标签内的普通行内文本
    },
8.3.2 一键提取
  • 自动生成翻译键
  • 保持语言文件结构一致
  • 支持批量处理

注意:提取的翻译键会自动添加到语言文件中,无需手动添加,但是批量提取的翻译键值会丢失插值参数,例如:哈哈哈${232}应当生成为t('hahaha', [232]),但实际上生成的是t('ha-ha-ha-232-0'),可以选择手动添加插值参数,或者结合8.4.1进行优化

8.3.3 翻译辅助

i18n-ally内置翻译API支持,这里我们使用百度翻译API作为示例:

  1. 注册账号 :访问百度翻译开放平台注册账号
  2. 创建应用:获取APP ID和密钥
  3. 配置插件

8.4 高级配置

8.4.1 重构模板
复制代码
"i18n-ally.refactor.templates": [
    {
      "source": "html-inline",
      "template": "{{ $t('{key}'{args}) }}" // 行内普通文本提取后替换生成的模板格式
    },
    {
      "source": "html-attribute",
      "template": "$t('{key}'{args})" // HTML 属性中的文本提取后替换生成的模板格式
    },
    {
      "source": "js-string",
      "template": "t('{key}'{args})" // JS 字符串提取后替换生成的模板格式 (适用于 Vue3 script)
    },
    {
      "source": "js-template",
      "template": "t('{key}'{args})" // JS 模板字符串提取后替换生成的模板格式 (适用于 Vue3 script)
    }
  ],

重构模板后,提取的翻译键会自动添加插值参数,例如:哈哈哈${232}${111}会生成为携带插值参数的格式t('hahaha', 232, 111),不过vue-i18nt方法不支持这种格式,所以我们需要再展t函数,支持可变参数,并将其作为数组参数插值。

复制代码
// src/locale/index.ts
import { createI18n } from "vue-i18n"
import zhCN from "./zh-CN.json"
import enUS from "./en-US.json"
import { Locale } from "@wot-ui/ui"
import WotEnUS from "@wot-ui/ui/locale/lang/en-US"
import { interpolateTemplate } from "./utils"

Locale.add({ "en-US": WotEnUS })

const messages = {
  "zh-CN": {
    ...zhCN
  },
  "en-US": {
    ...enUS
  }
}

// 创建i18n实例
const i18n = createI18n({
  locale: uni.getStorageSync("currentLang") || "zh-CN", // 默认语言
  fallbackLocale: "zh-CN", // 回退语言
  messages, // 语言包
  legacy: false, // 启用Composition API模式
  globalInjection: true // 全局注入 $t 等方法到模板
})

// 同步组件库语言
Locale.use(i18n.global.locale.value)
uni.setLocale(i18n.global.locale.value)

// 这是解决小程序和App端不支持插值方式的关键步骤
const originalT = i18n.global.t

// 扩展t函数,支持数组参数插值
i18n.global.t = (key, ...args) => {
  // 处理对象参数场景: t(key, {key1: value1, key2: value2})
  if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0])) {
    const result = originalT(key, ...args)
    return result
  }

  // 处理数组参数场景: t(key, [arg1, arg2])
  if (args.length === 1 && Array.isArray(args[0])) {
    const result = originalT(key, args[0])
    return interpolateTemplate(result, args[0])
  }

  // 处理可变参数场景: t(key, arg1, arg2, ...)
  if (args.length > 1 && args.every(arg => typeof arg !== "object")) {
    return interpolateTemplate(originalT(key, args), args)
  }

  // 处理默认场景: t(key) 或 t(key, defaultMessage) 或 t(key, plural) 等
  const result = originalT(key, ...args)

  return result
}

export default i18n
8.4.2 忽略规则
复制代码
"i18n-ally.extract.ignored": [
  "特定文本",
  "正则表达式",
]

总结

至此,我们完成了国际化配置,并使用 i18n-ally 插件实现了对 Vue 组件的自动提取和翻译。如果本文对你有所帮助,请点赞、收藏、转发,让更多的人了解和使用国际化配置。

相关推荐
@zulnger1 小时前
selenium 操作浏览器
前端·javascript·selenium
xiaofeichaichai2 小时前
Symbol 与 Iterator / Generator
前端·javascript
维双云2 小时前
小程序店铺装修模板怎么选?从首页布局、商品展示到下单路径这样看更实际
前端·小程序
YHL2 小时前
📖前端 HTTP 请求 & LLM 接口开发
前端·https
西部荒野子2 小时前
4.JS Bundle 执行流程
前端
假如让我当三天老蒯2 小时前
State和Props区别和左右(自学用)
前端·react.js
西部荒野子2 小时前
1. 建立源码地图
前端
西部荒野子2 小时前
3.RCTRootView 加载 Bundle 流程
前端
西部荒野子2 小时前
2.iOS 启动到 RCTRootView
前端