# @deriv-com/translations 中文文档

规范地址：https://npmcn.com/package/%40deriv-com/translations/

版本：1\.8\.1
npm 元数据更新时间：2026-11-11T15:35:50.168Z
中文译文更新时间：2026-11-12T00:36:41.979Z
软件包许可：未声明

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40deriv-com/translations
原文对照：https://npmcn.com/package/%40deriv-com/translations/?lang=original
项目主页：https://github.com/deriv-com/translations#readme
源码仓库：https://github.com/deriv-com/translations

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：90.3 kB，36 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：4 个
- peer 依赖：react ^17\.x \|\| ^18\.x \|\| ^19\.x，i18next \>=23\.0\.0，react-dom ^17\.x \|\| ^18\.x \|\| ^19\.x，react-i18next \>=14\.0\.0
- 首次发布：2024-04-16T08:11:10.245Z
- 最新版本发布：2026-11-11T15:35:49.696Z
- 近 30 天下载量：21440（2026-10-12 至 2026-11-10，npm 官方下载统计）
- 已发布版本数：54（近 12 个月 21 个）

## 中文简介

这是一个本地化库,使用 `i18next`, (中文(简体) ), `react-i18next`,以及用于翻译的自定义OTA SDK.

## 安装

```sh
npm install @deriv-com/translations
```

也可以使用：`pnpm add @deriv-com/translations`、`yarn add @deriv-com/translations`、`bun add @deriv-com/translations`

## README

# `@deriv-com/translations`

这是一个本地化库,使用 `i18next`, (中文(简体) ), `react-i18next`,以及用于翻译的自定义OTA SDK.

**在本文件中**

-   [概览](https://npmcn.com/package/%40deriv-com/translations/#readme-overview)
-   [所需资源](https://npmcn.com/package/%40deriv-com/translations/#readme-requirements)
-   [设置](https://npmcn.com/package/%40deriv-com/translations/#readme-setup)
-   [使用量](https://npmcn.com/package/%40deriv-com/translations/#readme-usage)
    -   [`initializeI18n`](https://npmcn.com/package/%40deriv-com/translations/#readme-initializei18n)
    -   [`<Localize />`](https://npmcn.com/package/%40deriv-com/translations/#readme-localize-component-example)
    -   [`localize`](https://npmcn.com/package/%40deriv-com/translations/#readme-localize-example)
    -   [`useTranslations` 钩子](https://npmcn.com/package/%40deriv-com/translations/#readme-usetranslations-hook)
-   [同步翻译到 CDN](https://npmcn.com/package/%40deriv-com/translations/#readme-syncing-translations)
    -   [工作流程文件中动作的示例使用](https://npmcn.com/package/%40deriv-com/translations/#readme-example-usage-of-the-action-in-the-workflow-file)
-   [捐款](https://npmcn.com/package/%40deriv-com/translations/#readme-contributing)

## 概览

![image](https://github.com/amir-deriv/translations/assets/129206554/e303f0cb-e15f-41e9-92ad-f930e43c484b)

## 所需资源

所存储的翻译目录必须具有以下结构: `{{ BASE_URL }}/translations/{lang}.json`

base url 是 cdnUrl 传递到 `initializeI18n` 函数,而lang是语言代码。 参见 [实例](https://npmcn.com/package/%40deriv-com/translations/#readme-initializei18n)

## 开始

要开始,首先需要做的是:

1.  Crowdin 工程, 用于管理您的工程的翻译 .
2.  一个Cloudflare R2账户来存储翻译.
    -   在 R2 账户中创建新桶 .
    -   在 R2 账户中创建新访问密钥 .
3.  在您的仓库中添加以下秘密 :
    -   `CROWDIN_PROJECT_ID`: 打开您的群集项目并导航到 `Tools` 选项卡中,您可以获取项目ID, 存储在您的 github 机密中 .
    -   `CROWDIN_PERSONAL_TOKEN`: 可以通过登录到您的拥入账户并点击您的配置图标和去 `Settings > API` 并创建一个新的个人访问标志, 存储在您的 github 秘密.
    -   `R2_ACCOUNT_ID`: Cloudflare R2仪表板的R2账户ID.
    -   `R2_ACCESS_KEY_ID`: R2从Cloudflare R2仪表板访问密钥ID.
    -   `R2_SECRET_ACCESS_KEY`: Cloudflare R2仪表板的R2秘密访问密钥.
    -   `R2_BUCKET_NAME`: Cloudflare R2仪表板上的R2桶名.
4.  在您的仓库中添加以下环境变量:
    -   `PROJECT_NAME`: 在您的环境变量中添加工程名称, 用于 Cloudflare R2 文件夹名称进行翻译 .
    -   `CROWDIN_BRANCH_NAME`: 为您的 Crowtin 项目添加分支名称, 以区分基于环境的翻译, 这需要放入环境变量, 而不是秘密, 因为软件包的消费者在定义时需要它 `cdnUrl` 以便查阅翻译.
5.  设置 github 动作以同步对 CDN 的翻译, 请参考 [同步翻译](https://npmcn.com/package/%40deriv-com/translations/#readme-syncing-translations) 详细情况.

通过运行安装软件包 :

```bash
npm install @deriv-com/translations
```

### 设置

-   通过导入和调用初始化主要组成部分的翻译 `initializeI18n` 在组件函数之外.
-   将返回值传递到 `TranslationProvider` 构成部分从 `@deriv-com/translations`。 。 。 .
-   将默认语言传递到 `TranslationProvider` 组件.

```jsx
    import { initializeI18n, TranslationProvider } from '@deriv-com/translations';
    ...
    const i18nInstance = initializeI18n({ cdnUrl: 'https://cdn.example.com' })

    const App = () => {
        ...
        return (
            <TranslationProvider defaultLang={'EN'} i18nInstance={i18nInstance}>
                <App />
            </TranslationProvider>
        )
    }

```

## 使用量

### `initializeI18n`

这个 `initializeI18n` 函数初始化 `i18next` 与OTA SDK, `react-i18next`,还有语言探测器。 它需要一个带有 cdnUrl 属性的对象,这是存储翻译的 CDN URL .

```javascript
import initializeI18n from "@deriv-com/translations";

initializeI18n({ cdnUrl: "https://cdn.example.com" });
```

-   对字符串使用任意一种 `localize(...)` 或者说 `<Localize />`。 。 。 .

### `Localize` 组件示例 :

```jsx
import { Localize } from "@deriv-com/translations";

<Localize
  i18n_default_text="You cannot use your real money account with {{website_name}} at this time."
  values={{ website_name }}
/>;
```

### `localize` 示例:

> 注意 `localize` 函数不会在资源加载或语言变化等任何变化发生后从 i18n 实例中获得更新。 这个 `localize` 函数将不用于将字符串包入组件,而只适合未更新字符串而不会引起问题的 util/mapper 函数.

```jsx
import { localize } from "@deriv-com/translations";

const getNotification = () => ({
  all: localize("all notifications"),
});
```

### `useTranslations` 钩子

使用 Ctranslooks 钩子是一个自定义钩子, 它会在顶部添加一些更多的返回值 `useTranslation` 钩从 `react-i18next`。它可用于翻译组件中的字符串,切换应用程序的语言并查看当前语言等.

示例用法:

```javascript
import { useTranslations } from "@deriv-com/translations";

const MyComponent = () => {
  const { localize, switchLanguage, currentLang } = useTranslations();

  const handleLanguageChange = () => {
    switchLanguage(currentLang === "EN" ? "DE" : "EN");
  };

  return <p onClick={handleLanguageChange}>{localize("Change language")}</p>;
};
```

## 同步翻译

有一种github动作将 Crowdin 到CDN 的翻译同步.

行动包括:

-   `PROJECT_SOURCE_DIRECTORY`: 默认情况下您项目的源目录是 `src`。 。 。 .
-   `CROWDIN_BASE_PATH`: Crowdin 项目翻译的基础路径默认是 `.`。 。 。 .
-   `CROWDIN_BASE_URL`: 存储翻译的 CDN 的基础 URL,默认为 `https://api.crowdin.com`。 。 。 .
-   `CROWDIN_BRANCH_NAME`: 进行生产、试验或装配等.
-   `CROWDIN_PROJECT_ID`: Crowdin 工程 ID 可以在群集工程设置中找到.
-   `CROWDIN_PERSONAL_TOKEN`: Crowdin 个人代币,可以在众筹账户设置中找到.
-   `R2_ACCOUNT_ID`: Cloudflare R2仪表板的R2账户ID.
-   `R2_ACCESS_KEY_ID`: R2从Cloudflare R2仪表板访问密钥ID.
-   `R2_SECRET_ACCESS_KEY`: Cloudflare R2仪表板的R2秘密访问密钥.
-   `R2_BUCKET_NAME`: Cloudflare R2仪表板上的R2桶名.

参考动作文件 [这里](https://github.com/deriv-com/translations/blob/main/.github/actions/extract_and_sync_translations/action.yml)。 。 。 .

### 工作流程文件中动作的示例用途 :

在此路径中复制并粘贴您的仓库中的以下工作流程代码`.github/workflows/sync_translations.yml`。 。 。 .

```yaml
name: Sync translations

on:
  push:
    branches:
      - 'main'
  schedule:
    - cron: '0 */12 * * *'

jobs:
  sync_translations:
    runs-on: ubuntu-latest
    steps:
      - name: Sync translations
        uses: deriv-com/translations/.github/actions/extract_and_sync_translations@master
        with:
          PROJECT_NAME: ${{ env.PROJECT_NAME }}
          CROWDIN_BRANCH_NAME: ${{ env.CROWDIN_BRANCH_NAME }}
          CROWDIN_BASE_URL: ${{ env.CROWDIN_BASE_URL }}
          CROWDIN_BASE_PATH: ${{ env.CROWDIN_BASE_PATH }}
          CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
          CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
          R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
          R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
          R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}
```

## 与罗塞塔同步翻译

Crowdin正在退役 支持罗塞塔的内部翻译 API,它取代了慎之门. `extract_and_sync_translations_rosetta` 是一个滴入的兄弟姐妹 Crowdin 动作: 它运行相同的提取器并上传相同的 `translations/<locale>.json` 文件到相同的 R2 文件夹,所以运行时间需要 无变化。 只有中间步骤不同——新弦由罗塞塔翻译 而不是从克罗丁下载.

如何稳定翻译 :

-   **只有德尔塔.** 当前 `<locale>.json` 首先从CDN中获取,并且 用作翻译记忆。 键已经保留了逐字记录, 键没有 更长 `en.json` 被刷了,只有缺失的密钥被送到了Rosetta. 因为密钥是英文文本的crc32,所以重写字符串是新的密钥.
    
-   **被保护的标志.** `{{placeholders}}`, (中文(简体) ), `<0>…</0>` 组件标记和任何 所列术语 `PROTECTED_TERMS_FILE` (品牌和产品名称) 用于 HTML 令牌 Rosetta 保存、 事后恢复并核实出现 一次就是这样 检查失败的字符串, 或者其翻译仍然坚守 源代码没有的 HTML 实体,从文件中删除 运行时间回落为英语 任务摘要中列出.
    
-   **部分结果.** 如果罗塞塔语失败了, 成功的地方 已发布,失败的 locale 的文件将保留为 CDN 的 标记失败 _之后_ 上传,网站才能继续工作.
    
-   **干跑动.** 与 `DRY_RUN: "true"` 作为工作流程附上目录 文物而不是被上传; 用于每次运行的第一次运行 将文物与现场文件相对照 一定要保证 第一个运行包括几个新字符串 `&`, (中文(简体) ), `<`,引用和a `{{x}}` 占位符,以及以占位符或标记开头的占位符(例如: `{{count}} items`),所以门的处理 他们的处理在一切之前 现予公布.
    
-   **何等为所译.** CDN 上的任何字符串值都是 被再用,包括一个与英语相同的. 这是故意的(产品) 诸如"Wallet API"等名称被故意用英语保留,但也意味着 未翻译的密钥一旦发布,就永远不会怨恨; 从 cDN 文件需要翻译.
    
-   **审查数值介于环境之间.** 与 `FALLBACK_ENVIRONMENT: staging` 关于生产同步,关键产品 缺少在中转目录中取出数值(所审查的QA) 罗塞塔被问到;等键表示等同英语,因为键是 Crc32 的 文本。 翻译:. `FALLBACK_MODE: prefer` 更进一步:中转值也替换 一种不同的生产方式已经存在了,所以两个目录都汇合了起来. A 类 中转值必须通过同一占位符、标记和保护期检查 (b) 未在工作摘要和 关键在于生产本身的价值(偏好)或Rosetta的价值(偏差). 只装配的密钥从未添加过,生产密钥也不再包含 `en.json` 仍然被压抑.
    
-   **更正通过拉取请求.** `OVERRIDES_FILE` jSON 点数 在您的仓库中的文件, 由 locale 键入, 然后由 英文 源代码 :
    
    ```json
    { "fr": { "1 Change": "1 modification" }, "es": { "Edited {{time}} ago": "Editado hace {{time}}" } }
    ```
    
    征服者战胜了所有来源, 在每一个环境通过 文档。 无法进行符号检查的条目无法运行, 即使运行 不以语言为对象,而以两次命名的语言也是如此(`zh-CN` 财务报告和审定财务报表 `zh_cn`\- 其条目 英语不再被取出( 字符串被重写或去除) 为 在任务摘要中跳出并列出,所以删除或更新.
    

行动包括:

-   `PROJECT_NAME`, (中文(简体) ), `PROJECT_SOURCE_DIRECTORY`, (中文(简体) ), `R2_ACCOUNT_ID`, (中文(简体) ), `R2_ACCESS_KEY_ID`, (中文(简体) ), `R2_SECRET_ACCESS_KEY`, (中文(简体) ), `R2_BUCKET_NAME`: 至于克罗丁行动.
-   `ENVIRONMENT`数字 : `staging` 或者说 `production` ——R2子文件夹(什么 `CROWDIN_BRANCH_NAME` 也是为了这个.
-   `TARGET_LOCALES`:逗号分出地语代码来翻译,例如. `es,fr,pt`。 。 。 . 中文也接受运行时间 `zh_cn` 编号 : `zh_tw` (单位:千美元) `ZH_CN` 编号 : `ZH_TW` 语言 密码,文件保留着这个名字.
-   `ROSETTA_API_TOKEN`: 为罗塞塔(一个秘密)的"熊"(Bearer sorder). 创建时间 rosetta 网络应用( 请Rosetta 团队访问); 它只显示一次 .
-   `ROSETTA_API_BASE`:Rosetta API的基础URL:仅由它的https主机,来自Rosetta团队. 需要,没有默认,所以消费者总是给寄出信物的主机取名.
-   `ROSETTA_CF_ACCESS_CLIENT_ID`, (中文(简体) ), `ROSETTA_CF_ACCESS_CLIENT_SECRET`: 可选云旗 CI跑者访问服务符;参见"跑者网络".
-   `CDN_BASE_URL`: 提供当前目录的地方(默认) `https://translations.deriv.com`) (中文(简体) ).
-   `PROTECTED_TERMS_FILE`: JSON 数组字符串的可选路径, 绝对不能被翻译 .
-   `FALLBACK_ENVIRONMENT`: 可选 `staging` 或者说 `production`,另一个环境的目录在罗塞塔之前被查阅. 必须不同于 `ENVIRONMENT`。 。 。 .
-   `FALLBACK_MODE`数字 : `missing` (默认)只对此环境所缺少的密钥使用倒计时; `prefer` 还让它取代了这个环境本身的价值观.
-   `OVERRIDES_FILE`: 已审校的可选路径,形状 `{"<locale>": {"<English text>": "<translation>"}}`。 。 。 .
-   `DRY_RUN`数字 : `true` 来写一件艺术品,而不是上传.

它暴露出四项产出: `status` (单位:千美元)`noop` 当目录不变的时候, `completed` 当新翻译、倒置、 覆盖或修补更改时 他们,或 `partial`, (中文(简体) ), `kept_back` (符号留下的字符串数) 检查或为所剩的 HTML 实体, `from_fallback` (数值取自 `FALLBACK_ENVIRONMENT`和 `overridden` (价值) `OVERRIDES_FILE` 更改或添加; 已覆盖 已公布的重新使用计数).

将动作固定为 SHA 的释放承诺(`…_rosetta@<sha> # v1.x.y`更喜欢 比起来 `@master`Crowdin和Rosetta的动作 运行他们的脚本 自己的承诺,与依赖 从该承诺 `package-lock.json`,永远 安装 `@deriv-com/translations` 从npm。 所以针也覆盖了取出器 和运行与 `ROSETTA_API_TOKEN`不只是动作的步骤. (被贬去的道由活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活

### 运行者网络

罗塞塔 API 可能需要 Cloudflare 访问 **服务符号** 从 CI 调取 赛跑者。 若罗塞塔团队为GitHub Actions发行一款,则通过如下方式:

-   `ROSETTA_CF_ACCESS_CLIENT_ID`:指使牌的客户端ID(存储为秘密).
-   `ROSETTA_CF_ACCESS_CLIENT_SECRET`:指使牌的客户端秘密(一个秘密).

两者均设或无;它们作为 `CF-Access-Client-Id` 财务报告和审定财务报表 `CF-Access-Client-Secret` 每一个罗塞塔的要求都写着头条 从来没有记录过. 没有他们,一个被阻挡的跑步失败 `Rosetta submit was blocked by Cloudflare (HTTP 403, Ray ID …)` 或者说 `… redirected to Cloudflare Access login`。如果一个带有标志的跑步仍然 与罗塞塔队私下分享错误的雷ID.

### 远离 Makima 动作

在此重命名之前, 动作是 `extract_and_sync_translations_makima`,与 页:1 `MAKIMA_API_KEY` 财务报告和审定财务报表 `MAKIMA_API_BASE` 投入和 `deriv-makima-sync` {\\fn黑体\\fs22\\bord1\\shad0\\3aHBE\\4aH00\\fscx67\\fscy66\\2cHFFFFFF\\3cH808080}真岛在2026年11月18日落地了 移动,改变 `uses:` 路径到 `extract_and_sync_translations_rosetta`,被钉在一个释放 包含 文件夹, 通过 `ROSETTA_API_TOKEN` 财务报告和审定财务报表 `ROSETTA_API_BASE` (罗塞塔API主机) 只有罗塞塔队的球员 跑 先干一场 在安装任何设备前检查设置: 基础必须是没有路径的 https 主机 . 2026年11月18日真麻关机前,老道还行:通了通 v1.6.1行动并警告。 日出后的首次发布计划 把它变成一个只用指针失败的步骤。 这个 `deriv-makima-sync` bin 仅以指针失败到 `deriv-rosetta-sync`。 。 。 . 直接装入脚本的重播器 `src/scripts/deriv-rosetta-sync.cjs`, (中文(简体) ), 地点 `makimaNameFor` 现在 `languageNameFor` 财务报告和审定财务报表 `buildSummary` 带 `rosettaErrors` 改为 `makimaErrors`。 。 。 .

### 工作流程文件中罗塞塔动作的示例用途 :

```yaml
name: Sync translations

on:
  push:
    branches:
      - 'main'
  schedule:
    - cron: '0 */12 * * *'

# Two runs reading the same CDN snapshot would both upload, last writer wins;
# serialise them.
concurrency:
  group: sync-translations-rosetta
  cancel-in-progress: false

jobs:
  sync_translations:
    runs-on: ubuntu-latest
    steps:
      - name: Sync translations
        # Pin the release commit; never @master (see above).
        uses: deriv-com/translations/.github/actions/extract_and_sync_translations_rosetta@<release-commit-sha> # v1.x.y
        with:
          PROJECT_NAME: ${{ vars.R2_PROJECT_NAME }}
          PROJECT_SOURCE_DIRECTORY: './src'
          ENVIRONMENT: staging
          TARGET_LOCALES: es,fr,pt
          ROSETTA_API_TOKEN: ${{ secrets.ROSETTA_API_TOKEN }}
          ROSETTA_API_BASE: ${{ vars.ROSETTA_API_BASE }}
          ROSETTA_CF_ACCESS_CLIENT_ID: ${{ secrets.ROSETTA_CF_ACCESS_CLIENT_ID }}
          ROSETTA_CF_ACCESS_CLIENT_SECRET: ${{ secrets.ROSETTA_CF_ACCESS_CLIENT_SECRET }}
          R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
          R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
          R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
          R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}
```

同一站台可在本地运行, 之后进行干燥运行 `npx deriv-extract-translations ./src`数字 :

```bash
PROJECT_NAME=my-project ENVIRONMENT=staging TARGET_LOCALES=es,fr,pt \
ROSETTA_API_BASE=https://<rosetta-api-host> ROSETTA_API_TOKEN=… DRY_RUN=true npx deriv-rosetta-sync
```

## 捐款

欢迎捐款。 请随您的更改打开拉动请求 .

## 常见问题

### @deriv-com/translations 是什么？

这是一个本地化库,使用 i18next, \(中文\(简体\) \), react-i18next,以及用于翻译的自定义OTA SDK\.

### 如何安装 @deriv-com/translations？

在项目目录运行 npm install @deriv-com/translations，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（^17\.x \|\| ^18\.x \|\| ^19\.x）、i18next（\>=23\.0\.0）、react-dom（^17\.x \|\| ^18\.x \|\| ^19\.x）、react-i18next（\>=14\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。它还提供命令行工具 deriv-makima-sync、deriv-rosetta-sync、deriv-extract-translations，可以用 npx deriv-makima-sync 直接运行。

### @deriv-com/translations 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @deriv-com/translations 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @deriv-com/translations 可以商用吗？

@deriv-com/translations 使用 未声明 许可。package\.json 没有声明许可证。没有许可证的代码默认保留全部权利，商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括，不构成法律意见，请以许可原文为准。

### @deriv-com/translations 的下载量有多少？

根据 npm 官方下载统计，@deriv-com/translations 在 2026/10/12 – 2026/11/10 这 30 天内被下载 21,440 次，日均约 715 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @deriv-com/translations 还在维护吗？

最新版本 1\.8\.1 发布于 2026/11/11（今天）；至今共发布 54 个版本，其中过去 12 个月发布了 21 个；首次发布于 2024/04/16。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

