> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/zh/development/i18n.md).

# i18n

MyIP 提供四种语言，且这四种语言都是一等公民。没有一个“主”区域设置会优先获得功能。

| 代码   | 语言     | 文件                         |
| ---- | ------ | -------------------------- |
| `en` | 英文（回退） | `frontend/locales/en.json` |
| `zh` | 简体中文   | `frontend/locales/zh.json` |
| `fr` | 法语     | `frontend/locales/fr.json` |
| `ru` | 俄语     | `frontend/locales/ru.json` |

## 设置

应用使用 **vue-i18n** 采用 Composition API 模式。实例创建于 `frontend/locales/i18n.js` 并使用 `legacy: false` 和 `fallbackLocale: 'en'`，然后注册到 `main.js`.

在组件中，你会取 `t()` 出 `useI18n()`:

```vue
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>

<template>
  <p>{{ t('macchecker.Note') }}</p>
</template>
```

没有任何对用户可见的内容是硬编码的。每个字符串都会经过 `t()`.

## 区域设置文件

每个区域设置文件都是一个 JSON 对象，并按功能命名空间划分。顶层命名空间包括 `nav`, `advancedtools`, `page`, `changelog`，以及每个工具各一个—— `macchecker`, `whois`, `dnsresolver`, `censorshipcheck`，等等。

工具的命名空间通常与其注册表 slug 相匹配：

{% code title="frontend/locales/en.json" %}

```json
"macchecker": {
  "Title": "MAC 查询",
  "Note": "查询物理地址（MAC 地址）的制造商……",
  "Note2": "请输入物理地址以开始查询：",
  "Placeholder": "F0:2F:4B:01:0A:AA",
  "invalidMAC": "无效的物理地址",
  "fetchError": "无法获取查询结果"
}
```

{% endcode %}

首页卡片描述单独放在共享的 `advancedtools` 命名空间中，因为工具注册表指向的就是它 `noteKey` ：

```json
"advancedtools": {
  "MacChecker": "查询物理地址的信息"
}
```

**四个文件中的键名完全相同。只有值不同。**

## 按需加载

四个包从不会一起加载。将它们提前打包会额外造成约 44 KB 的 gzip 冗余——每次页面加载都带上三个未使用的区域设置。

相反 `frontend/locales/i18n.js` 保存着一个动态导入映射：

{% code title="frontend/locales/i18n.js" %}

```js
const localeLoaders = {
  en: () => import('./en.json'),
  zh: () => import('./zh.json'),
  fr: () => import('./fr.json'),
  ru: () => import('./ru.json'),
};
```

{% endcode %}

i18n 实例以 **空** 消息。 `loadActiveLocaleMessages()` 注入当前启用的语言以及 `en` 回退项（并行、带缓存），并且 `main.js` 在挂载前等待其完成——因此首屏渲染就已经是翻译后的。切换语言会持久化选择并重新启动应用，这意味着每次页面加载时始终只会启用一个区域设置。

### 语言如何被选择

`setLanguage()` 在 `frontend/locales/i18n.js` 按以下顺序解析：

1. **已存储的偏好** 在 `localStorage` ——先查当前偏好键，再查旧键，因此新升级的键仍能找到较早的选择。
2. **`?hl=` 查询参数**，如果它指定了一个受支持的区域设置。
3. **浏览器语言** (`navigator.language`），按前两个字符匹配。
4. **`en`.**

一个构建时 Vite 插件（`localePreloadPlugin` 在 `vite.config.js`）会在一个小型内联 `<head>` 脚本中复现该确切顺序，并发出一个 `<link rel="modulepreload">` ，在 HTML 仍在流式传输时，为所选包添加预加载——因此语言包会与主包并行下载，而不是在其后下载。猜错一次只会浪费一次预加载；真正的导入仍然会决定结果。

消息加载完成后， `updateMeta()` 会设置 `document.documentElement.lang` （其中 `zh` 声明为 `zh-CN`，因为该包仅限简体中文）并刷新 `标题`, `关键词`，以及 `描述` 中的 meta 标签，来源于 `page.*` 键。

## 子包

有两个数据集足够大，因此不会放入主区域设置包，而是只针对当前启用的语言按需加载：

<table><thead><tr><th width="300">子包</th><th>加载者</th></tr></thead><tbody><tr><td><code>frontend/locales/security-checklist/{en,zh,fr,ru}.json</code></td><td><code>SecurityChecklist.vue</code> ——它有自己独立的加载器映射；该数据集每种语言约 30 KB gzip，而且只有这一个工具会读取它。</td></tr><tr><td><code>frontend/locales/privacy/{en,zh,fr,ru}.json</code></td><td><code>PrivacyPolicy.vue</code> ——通过 <code>mergeLocaleMessage()</code> 将其合并到 i18n 中 <code>t()</code> 和 <code>tm()</code> 可以正常解析文案。</td></tr></tbody></table>

两者都遵循相同模式：一个 `{ en, zh, fr, ru }` 动态导入映射，回退到 `en` 用于未知区域设置。

{% hint style="info" %}
**何时添加子包：** 一个体积很大的数据集，且只属于一个懒加载视图，否则它就会出现在每个访客的首屏打包中。普通 UI 字符串始终放在主包中。
{% endhint %}

## 四语言规则

{% hint style="warning" %}
**任何会显示文案的修改，都必须在同一次变更中落到全部四种语言。** 不是后续 PR，也不是 TODO。
{% endhint %}

这意味着：

* 新键会加入到 `en.json`, `zh.json`, `fr.json`, **和** `ru.json`.
* 改写的字符串会在四种语言中都同步改写。
* 删除的键会从四种语言中同时删除。
* 随附的 `changelog.json` 条目包含四种语言的翻译。

`fallbackLocale: 'en'` 这意味着缺失的键会降级为英文，而不是显示原始键路径——这正是部分覆盖在审查中容易被忽略的原因。不要依赖它。

如果你确实无法提供翻译，请在 PR 中说明。维护者宁愿先修正文案，也不愿在发布后才发现缺少某个语言。

## 更新日志

发布说明位于 `frontend/data/changelog.json`，而不在区域设置文件中。该文件是一个版本块数组， **最旧在前** ——关于面板会反向渲染它，因此新条目会追加到最后一个块中。

{% code title="frontend/data/changelog.json" %}

```json
{
  "version": "v7.2.0",
  "date": "Beta",
  "content": [
    {
      "type": "add",
      "change": {
        "en": "重构了封锁测试：查看一个网站在全球哪些地方被屏蔽",
        "zh": "全面重构封锁测试，可以查询一个网站在全球的封锁情况",
        "fr": "彻底重做了审查检查：查看一个网站在全球哪些地方被封锁",
        "ru": "全面重做了审查检查：看看一个网站在全球哪些地方被封锁"
      }
    }
  ]
}
```

{% endcode %}

形状规则：

| 字段     | 规则                                |
| ------ | --------------------------------- |
| `版本`   | 字符串匹配 `vX.Y…`                     |
| `日期`   | 字符串——发布日期，或类似于 `"Beta"`           |
| `内容`   | 非空的变更项数组                          |
| `type` | 以下项中恰好一个： `add`, `improve`, `fix` |
| `变更`   | 包含以下内容的对象 **全部四个** 语言键，每个都是非空字符串  |

### 由测试强制执行

`tests/changelog.test.js` 会在每次 `pnpm test` 运行，并在以下情况下使构建失败：

* 任何以下项缺失或为空的翻译： `en` / `zh` / `fr` / `ru`
* 一个 `type` 超出允许的三个之外的
* 版本块缺失 `版本`, `日期`，或一个非空的 `内容` 数组
* 重新引入以下内容的区域设置文件： `changelog.versions` （该数据现在仅存在于 `changelog.json`)
* 丢失了以下内容的区域设置文件： `changelog.Title` / `add` / `improve` / `fix` UI 标签

最后这一对才是值得记住的分界： **发布说明文本位于 `changelog.json`；其周围的徽标标签和面板标题则保留在区域设置文件中** 作为普通 UI 装饰。

## 添加一种语言

代码库中没有什么禁止添加第五种语言，但这是一项实实在在的承诺——此后每一次文案变更都需要五种翻译，而且 `tests/changelog.test.js` 代码中硬编码的是所需的四种语言。在开始之前，请先开 issue 与维护者讨论。

## 下一步

* [添加新工具](/developer/zh/development/adding-a-new-tool.md) ——i18n 步骤在完整功能中的位置。
* [测试](/developer/zh/development/testing.md) ——还有什么 `pnpm test` 会强制执行。
* [如何贡献](/developer/zh/contributing/how-to-contribute.md) ——PR 期望。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/zh/development/i18n.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
