Skip to content

@wxt-dev/i18n

更新日志

@wxt-dev/i18n 是对 browser.i18n API 的简单、类型安全的封装。相比标准 API,它提供了以下优势:

  1. 简洁的消息文件格式
  2. 复数形式支持
  3. I18n Ally VS Code 扩展 集成

相比其他非浏览器扩展专用的 I18n 包,它还具有以下优势:

  1. 不会将本地化文件打包到每个入口点中
  2. 无需异步获取本地化文件
  3. 可以在 manifest 和 CSS 文件中进行本地化

不过,它有一个主要缺点:

  1. browser.i18n API 一样,要更改语言,用户必须更改浏览器的语言

IMPORTANT

你不必使用 wxt 也能使用这个包——它可以在任何打包工具配置中工作。详情请参阅不使用 WXT 安装

安装

使用 WXT

  1. 通过包管理器安装 @wxt-dev/i18n

    sh
    pnpm i @wxt-dev/i18n
  2. wxt.config.ts 文件中添加 WXT 模块并设置默认语言:

    ts
    export default defineConfig({
      modules: ['@wxt-dev/i18n/module'],
      manifest: {
        default_locale: 'en',
      },
    });
  3. <srcDir>/locales/<default_locale>.yml 创建本地化文件,或将现有的本地化文件移动到该位置。

    yml
    # <srcDir>/locales/en.yml
    helloWorld: Hello world!

    @wxt-dev/i18n 支持标准消息格式,因此如果你已经在 <rootDir>/public/_locale/<lang>/messages.json 有本地化文件,无需将它们转换为 YAML 或重构——只需将它们移动到 <srcDir>/locales/<lang>.json,即可直接使用!

  4. 要获取翻译,使用自动导入的 i18n 对象或手动导入:

    ts
    import { i18n } from '#i18n';
    
    i18n.t('helloWorld'); // "Hello world!"

大功告成!使用 WXT,你可以开箱即用地获得类型安全。

不使用 WXT

  1. 通过包管理器安装 @wxt-dev/i18n

    sh
    pnpm i @wxt-dev/i18n
  2. _locales/<lang>/messages.json 创建消息文件,或将现有的翻译文件移动到该位置:

    json
    {
      "helloWorld": {
        "message": "Hello world!"
      }
    }

    NOTE

    为了获得最佳的开发体验,你应该将 @wxt-dev/i18n 集成到你的构建流程中。这将启用:

    1. 复数形式
    2. 简洁的消息文件格式
    3. 类型安全

    请参阅构建集成进行设置。

  3. 创建 i18n 对象,导出它,然后在任何地方使用!

    ts
    import { createI18n } from '@wxt-dev/i18n';
    
    export const i18n = createI18n();
    
    i18n.t('helloWorld'); // "Hello world!";

配置

可以通过 i18n 配置来配置该模块:

ts
export default defineConfig({
  modules: ['@wxt-dev/i18n'],
  i18n: {
    // ...
  },
});

选项在编辑器中有 JSDocs 可用,你也可以在源码中查看:I18nOptions

消息文件格式

DANGER

只有将 @wxt-dev/i18n 集成到构建流程后,你才能使用本页讨论的文件格式。如果没有集成到构建流程中,你必须像普通浏览器扩展一样使用 _locales 目录中的 JSON 文件。

文件扩展名

你可以使用以下几种文件类型来定义消息:

  • .yml
  • .yaml
  • .json
  • .jsonc
  • .json5
  • .toml

嵌套键

你可以将翻译放在顶层或嵌套分组:

yml
ok: OK
cancel: Cancel
welcome:
  title: Welcome to XYZ
dialogs:
  confirmation:
    title: 'Are you sure?'

要访问嵌套键,使用 .

ts
i18n.t('ok'); // "OK"
i18n.t('cancel'); // "Cancel"
i18n.t('welcome.title'); // "Welcome to XYZ"
i18n.t('dialogs.confirmation.title'); // "Are you sure?"

替换

因为 @wxt-dev/i18n 基于 browser.i18n,你可以用相同的方式定义替换,使用 $1-$9

yml
hello: Hello $1!
order: Thanks for ordering your $1

转义 $

要转义美元符号,在其前面加上另一个 $

yml
dollars: $$$1
ts
i18n.t('dollars', ['1.00']); // "$1.00"

复数形式

WARNING

目前不支持像阿拉伯语这样对"少数"或"多数"有不同形式的语言的复数支持。如果你对此感兴趣,欢迎提交 PR!

要根据数量获取不同的翻译:

yml
items:
  1: 1 item
  n: $1 items

然后将数量作为第二个参数传递给 i18n.t

ts
i18n.t('items', 0); // "0 items"
i18n.t('items', 1); // "1 item"
i18n.t('items', 2); // "2 items"

要为 0 个项目添加自定义翻译:

yml
items:
  0: No items
  1: 1 item
  n: $1 items
ts
i18n.t('items', 0); // "No items"
i18n.t('items', 1); // "1 item"
i18n.t('items', 2); // "2 items"

如果你需要为 $1 传递自定义替换而不是数量,只需添加替换:

yml
items:
  0: No items
  1: $1 item
  n: $1 items
ts
i18n.t('items', 0, ['Zero']); // "No items"
i18n.t('items', 1, ['One']); // "One item"
i18n.t('items', 2, ['Multiple']); // "Multiple items"

详细键

@wxt-dev/i18n 兼容 browser.i18n 使用的消息格式。

IMPORTANT

这意味着如果你正在迁移到 @wxt-dev/i18n 并且已经在使用详细格式,你不需要做任何更改!

当一个键满足以下条件时,它被视为"详细"的:

  1. 在顶层(未嵌套)
  2. 仅包含以下属性:messagedescription 和/或 placeholder
json
{
  "appName": {
    "message": "GitHub - Better Line Counts",
    "description": "The app's name, should not be translated"
  },
  "ok": "OK",
  "deleteConfirmation": {
    "title": "Delete XYZ?",
    "message": "You cannot undo this action once taken."
  }
}

在这个例子中,只有 appName 被视为详细的。deleteConfirmation 不是详细的,因为它包含额外的属性 title

ts
i18n.t('appName'); // ✅ "GitHub - Better Line Counts"
i18n.t('appName.message'); // ❌
i18n.t('ok'); // ✅ "OK"
i18n.t('deleteConfirmation'); // ❌
i18n.t('deleteConfirmation.title'); // ✅ "Delete XYZ?"
i18n.t('deleteConfirmation.message'); // ✅ "You cannot undo this action once taken."

如果这让你感到困惑,不用担心!有了类型安全,如果你用错了会得到类型错误。如果类型安全被禁用,你会在开发者工具控制台中收到运行时警告。

WARNING

不建议使用详细格式。目前还没有看到开箱即用支持这种格式的翻译服务和软件。尽可能使用简洁格式。

构建集成

要使用自定义消息文件格式,你需要使用 @wxt-dev/i18n/build 将自定义格式转换为浏览器期望的格式。

WXT

请参阅使用 WXT 安装

简而言之,你只需要:

ts
// wxt.config.ts
export default defineConfig({
  modules: ['@wxt-dev/i18n/module'],
});

每次运行 wxt prepare 或其他构建命令时,类型都会自动生成。

自定义

如果你没有使用 WXT,你需要自己预处理本地化文件。以下是一个生成 _locales/.../messages.jsonwxt-i18n-structure.d.ts 文件的基本脚本:

ts
// build-i18n.js
import {
  parseMessagesFile,
  generateChromeMessagesFile,
  generateTypeFile,
} from '@wxt-dev/i18n/build';

// 读取你的本地化文件
const messages = {
  en: await parseMessagesFile('path/locales/en.yml'),
  de: await parseMessagesFile('path/locales/de.yml'),
  // ...
};

// 为扩展生成 JSON 文件
await generateChromeMessagesFile('dist/_locales/en/messages.json', messages.en);
await generateChromeMessagesFile('dist/_locales/de/messages.json', messages.de);
// ...

// 根据你的 default_locale 生成类型文件
await generateTypeFile('wxt-i18n-structure.d.ts', messages.en);

然后运行脚本:

sh
node generate-i18n.js

如果你的构建工具有开发模式,你还需要在更改本地化文件时重新运行该脚本。

类型安全

生成 wxt-i18n-structure.d.ts 后(参见自定义部分),你可以用它将生成的类型传递给 createI18n

ts
import type { WxtI18nStructure } from './wxt-i18n-structure';

export const i18n = createI18n<WxtI18nStructure>();

就这样,你就拥有了类型安全!

编辑器支持

为了获得更好的开发体验,你可以为编辑器配置插件和扩展。

VS Code

I18n Ally 扩展 为 VS Code 添加了以下功能:

  • 跳转到翻译定义
  • 内联预览文本
  • 悬停查看其他翻译

你需要配置该扩展,以便它知道你的本地化文件在哪里以及哪个函数表示获取翻译:

.vscode/i18n-ally-custom-framework.yml

yml
# 包含 VS Code 定义的语言 ID 的字符串数组
# 你可以在这里查看可用的语言 ID:https://code.visualstudio.com/docs/languages/identifiers
languageIds:
  - typescript
  - typescriptreact

# 查找 t("...")
usageMatchRegex:
  - "[^\\w\\d]t\\(['\"`]({key})['\"`]"

# 禁用其他内置的 I18n Ally 框架
monopoly: true

.vscode/settings.json

json
{
  "i18n-ally.localesPaths": ["src/locales"],
  "i18n-ally.keystyle": "nested"
}

Zed

截至撰写时(2024 年 8 月 18 日),Zed 尚无可用的 I18n 支持扩展。

Jetbrains IDE

安装 I18n Ally 插件。关于 Jetbrains 支持的文档有限,但你可能需要类似于 VS Code 的方式配置插件……不过不确定文件应该放在哪里。

如果你弄清楚了,欢迎提交 PR!