@deriv-com/translations

中文文档
1.8.1发布于 2026/11/11TypeScriptES Module构建来源已验证近 30 天下载 2.1万

This is a localization library that uses i18next, react-i18next, and a custom OTA SDK for translations.

原始使用文档
文档目录16 节
  1. Overview
  2. Requirements
  3. Getting Started
  4. Setup
  5. Usage
  6. initializeI18n
  7. Localize component example:
  8. localize example:
  9. useTranslations Hook
  10. Syncing translations
  11. Example usage of the action in the workflow file:
  12. Syncing translations with Rosetta
  13. Runner network
  14. Moving from the Makima action
  15. Example usage of the Rosetta action in the workflow file:
  16. Contributing

@deriv-com/translations

This is a localization library that uses i18next, react-i18next, and a custom OTA SDK for translations.

In this document

Overview

image

Requirements

The stored translation directory must have the following structure: {{ BASE_URL }}/translations/{lang}.json

base_url is the cdnUrl passed to the initializeI18n function, and lang is the language code. Refer to the example

Getting Started

To get started, one would need few things to setup first which are:

  1. A Crowdin project which will be used to manage translations for your project.
  2. A Cloudflare R2 account to store the translations.
    • Create a new bucket in the R2 account.
    • Create a new access key in the R2 account.
  3. Add the following secrets to your repository:
    • CROWDIN_PROJECT_ID: Open your crowdin project and navigate to Tools tab, you will be able to get the Project ID, store that in your github secrets.
    • CROWDIN_PERSONAL_TOKEN: This can be obtained by logging in to your crowdin account and clicking on your profile icon and goto Settings > API and create a new personal access token, store that in your github secrets.
    • R2_ACCOUNT_ID: R2 account ID from the Cloudflare R2 dashboard.
    • R2_ACCESS_KEY_ID: R2 access key ID from the Cloudflare R2 dashboard.
    • R2_SECRET_ACCESS_KEY: R2 secret access key from the Cloudflare R2 dashboard.
    • R2_BUCKET_NAME: R2 bucket name from the Cloudflare R2 dashboard.
  4. Add the following environment variable in your repository:
    • PROJECT_NAME: Add project name in your environment variable which will be used for Cloudflare R2 folder name for translations.
    • CROWDIN_BRANCH_NAME: Add the branch name for your crowdin project to separate the translations based on environment, this needs to be put into the environment variable instead of secret because the consumer of the package needs it while defining the cdnUrl to access the translations.
  5. Setup the github action to sync the translations to the CDN, refer to the Syncing translations section for more details.

Install the package by running:

npm install @deriv-com/translations

Setup

  • initialize translations in main component by importing and calling initializeI18n outside of the component function.
  • pass the return value to the TranslationProvider component from @deriv-com/translations.
  • pass default language to the TranslationProvider component.
    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>
        )
    }

Usage

initializeI18n

The initializeI18n function initializes the i18next instance with the OTA SDK, react-i18next, and a language detector. It takes an object with a cdnUrl property, which is the URL of the CDN where the translations are stored.

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

initializeI18n({ cdnUrl: "https://cdn.example.com" });
  • For strings use either localize(...) or <Localize />.

Localize component example:

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 example:

Note that the localize function will not get the update from i18n instance once there is any changes like resource loaded or language change. This localize function is not to be used to wrap strings in components and only suitable for util/mapper functions which would not cause issues with string not getting updated.

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

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

useTranslations Hook

The useTranslations hook is a custom hook that adds some more returned values on top of the useTranslation hook from react-i18next. It can be used to translate strings in your components, toggle the language of the app and see the current language etc.

Example usage:

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>;
};

Syncing translations

There is a github action that syncs the translations from Crowdin to the CDN.

The action takes following inputs:

  • PROJECT_SOURCE_DIRECTORY: Source directory of your project by default it is src.
  • CROWDIN_BASE_PATH: Base path of the translations in the Crowdin project by default it is ..
  • CROWDIN_BASE_URL: Base URL of the CDN where the translations are stored, default is https://api.crowdin.com.
  • CROWDIN_BRANCH_NAME: Running on production, test or staging etc.
  • CROWDIN_PROJECT_ID: Crowdin project ID which can be found in the crowdin project settings.
  • CROWDIN_PERSONAL_TOKEN: Crowdin personal token which can be found in the crowdin account settings.
  • R2_ACCOUNT_ID: R2 account ID from the Cloudflare R2 dashboard.
  • R2_ACCESS_KEY_ID: R2 access key ID from the Cloudflare R2 dashboard.
  • R2_SECRET_ACCESS_KEY: R2 secret access key from the Cloudflare R2 dashboard.
  • R2_BUCKET_NAME: R2 bucket name from the Cloudflare R2 dashboard.

Refer to the action file here.

Example usage of the action in the workflow file:

Copy and paste the following workflow code in your repository in this path.github/workflows/sync_translations.yml.

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 }}

Syncing translations with Rosetta

Crowdin is being decommissioned in favour of the internal Rosetta translation API, which replaced the Makima gateway. extract_and_sync_translations_rosetta is a drop-in sibling of the Crowdin action: it runs the same extractor and uploads the same translations/<locale>.json files to the same R2 folder, so the runtime needs no change. Only the middle step differs — new strings are translated by Rosetta instead of downloaded from Crowdin.

How it keeps translations stable:

  • Delta only. The current <locale>.json is fetched from the CDN first and used as the translation memory. Keys already there are kept verbatim, keys no longer in en.json are pruned, and only missing keys are sent to Rosetta. Because keys are the crc32 of the English text, a reworded string is a new key.

  • Protected tokens. {{placeholders}}, <0>…</0> component markers and any terms listed in PROTECTED_TERMS_FILE (brand and product names) are swapped for HTML tokens Rosetta preserves, restored afterwards, and verified to appear exactly once. A string that fails the check, or whose translation still holds an HTML entity the source did not have, is left out of the file — the runtime falls back to English for it and the next run retries it — and is listed in the job summary.

  • Partial results. If Rosetta fails a language, the locales that succeeded are published, the failed locale's file is left as the CDN had it, and the run is marked failed after the upload so the site keeps working.

  • Dry run. With DRY_RUN: "true" the catalogues are attached as a workflow artifact instead of being uploaded; use it for the first run on each environment and diff the artifact against the live files. Make sure that first run includes a few new strings with &, <, quotes and a {{x}} placeholder, and one that starts with a placeholder or marker (such as {{count}} items), so the gateway's handling of them is seen before anything is published.

  • What counts as translated. Any string value already on the CDN is reused, including one identical to the English. That is deliberate (product names such as "Wallet API" are kept in English on purpose), but it also means a key that was once published untranslated is never resent; delete it from the CDN file to have it translated.

  • Reviewed values travel between environments. With FALLBACK_ENVIRONMENT: staging on the production sync, a key production lacks takes the value in the staging catalogue (the one QA reviewed) before Rosetta is asked; equal keys mean equal English, because keys are the crc32 of the text. FALLBACK_MODE: prefer goes further: staging's value also replaces a different one production already has, so the two catalogues converge. A staging value must pass the same placeholder, marker and protected-term check as Rosetta's output; one that does not is listed in the job summary and the key falls through to production's own value (prefer) or Rosetta (missing). Keys only staging has are never added, and production keys no longer in en.json are still pruned.

  • Corrections go through a pull request. OVERRIDES_FILE points at a JSON file in your repository, keyed by locale and then by the English source text:

    { "fr": { "1 Change": "1 modification" }, "es": { "Edited {{time}} ago": "Editado hace {{time}}" } }
    

    Overrides win over every other source, in every environment that passes the file. An entry that fails the token check fails the run, even when the run does not target its locale, and so does a language named twice (zh-CN and zh_cn); an entry whose English is no longer extracted (the string was reworded or removed) is skipped and listed in the job summary, so remove or update it.

The action takes the following inputs:

  • PROJECT_NAME, PROJECT_SOURCE_DIRECTORY, R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET_NAME: as for the Crowdin action.
  • ENVIRONMENT: staging or production — the R2 sub-folder (what CROWDIN_BRANCH_NAME was for).
  • TARGET_LOCALES: comma-separated locale codes to translate into, e.g. es,fr,pt. Chinese also accepts the runtime's zh_cn / zh_tw (the ZH_CN / ZH_TW language codes), and the file keeps that name.
  • ROSETTA_API_TOKEN: Bearer token for Rosetta (a secret). Create it at the Rosetta web app (ask the Rosetta team for access); it is shown only once.
  • ROSETTA_API_BASE: base URL of the Rosetta API: its https host only, from the Rosetta team. Required, with no default, so a consumer always names the host it sends the token to.
  • ROSETTA_CF_ACCESS_CLIENT_ID, ROSETTA_CF_ACCESS_CLIENT_SECRET: optional Cloudflare Access service token for CI runners; see "Runner network".
  • CDN_BASE_URL: where the current catalogues are served (default https://translations.deriv.com).
  • PROTECTED_TERMS_FILE: optional path to a JSON array of strings that must never be translated.
  • FALLBACK_ENVIRONMENT: optional staging or production, another environment whose catalogue is consulted before Rosetta. Must differ from ENVIRONMENT.
  • FALLBACK_MODE: missing (default) uses the fallback only for keys this environment lacks; prefer also lets it replace this environment's own values.
  • OVERRIDES_FILE: optional path to reviewed corrections, shaped {"<locale>": {"<English text>": "<translation>"}}.
  • DRY_RUN: true to write an artifact instead of uploading.

It exposes four outputs: status (noop when the catalogues are unchanged, completed when new translations, the fallback, overrides or pruning changed them, or partial), kept_back (the number of strings left out by the token check or for a leftover HTML entity), from_fallback (values taken from FALLBACK_ENVIRONMENT) and overridden (values OVERRIDES_FILE changed or added; an override already published counts as reused).

Pin the action to a release commit by SHA (…_rosetta@<sha> # v1.x.y) rather than @master. The Crowdin and Rosetta actions run their scripts from their own commit, with dependencies from that commit's package-lock.json, and never install @deriv-com/translations from npm. So the pin also covers the extractor and the sync that runs with ROSETTA_API_TOKEN, not only the action's steps. (The deprecated Makima path runs the v1.6.1 action, itself pinned by commit.)

Runner network

The Rosetta API may require a Cloudflare Access service token from CI runners. If the Rosetta team issues one for GitHub Actions, pass it as:

  • ROSETTA_CF_ACCESS_CLIENT_ID: the token's Client ID (store it as a secret).
  • ROSETTA_CF_ACCESS_CLIENT_SECRET: the token's Client Secret (a secret).

Set both or neither; they are sent as the CF-Access-Client-Id and CF-Access-Client-Secret headers on every Rosetta request, and never logged. Without them, a blocked run fails with Rosetta submit was blocked by Cloudflare (HTTP 403, Ray ID …) or … redirected to Cloudflare Access login. If a run with the token is still blocked, share the Ray ID from the error with the Rosetta team privately.

Moving from the Makima action

Before this rename the action was extract_and_sync_translations_makima, with the MAKIMA_API_KEY and MAKIMA_API_BASE inputs and the deriv-makima-sync bin. Makima is sunset on 18 Nov 2026. To move, change the uses: path to extract_and_sync_translations_rosetta, pinned to a release that contains that folder, pass ROSETTA_API_TOKEN and ROSETTA_API_BASE (the Rosetta API host only; both from the Rosetta team), and run a dry run first. The settings are checked before anything is installed: the base must be an https host with no path. Until Makima is switched off on 18 Nov 2026, the old path still works: it runs the v1.6.1 action and warns. The first release after the sunset is planned to turn it into a step that only fails with a pointer here. The deriv-makima-sync bin already only fails with a pointer to deriv-rosetta-sync. A repo that loads the script directly should load src/scripts/deriv-rosetta-sync.cjs, where makimaNameFor is now languageNameFor and buildSummary takes rosettaErrors instead of makimaErrors.

Example usage of the Rosetta action in the workflow file:

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 }}

The same bin can be run locally for a dry run after npx deriv-extract-translations ./src:

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

Contributing

Contributions are welcome. Please open a pull request with your changes.

版本与发布记录

@deriv-com/translations 共发布 54 个版本,首次发布于 2024/04/16;过去 12 个月发布了 21 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.8.1latest2026/11/11今天
1.8.02026/11/11今天
1.7.12026/11/092 天前
1.6.12026/11/029 天前
1.6.02026/10/3012 天前
1.5.62026/10/2319 天前

关于 @deriv-com/translations 的常见问题

@deriv-com/translations 是什么?

This is a localization library that uses i18next, react-i18next, and a custom OTA SDK for translations.

如何安装 @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 和提交记录判断。