cherry-studio/docs/en/guides/i18n.md
George·Dong 2a31fa2ad5
refactor: switch yarn to pnpm (#12260)
* refactor: switch workflows from yarn to pnpm

Replace Yarn usage with pnpm in CI workflows to standardize package
management and leverage pnpm's store/cache behavior.

- Use pnpm/action-setup to install pnpm (v) instead of enabling corepack
  and preparing Yarn.
- Retrieve pnpm store path and update cache actions to cache the pnpm
  store and use pnpm-lock.yaml for cache keys and restores.
- Replace yarn commands with pnpm equivalents across workflows:
  install, i18n:sync/translate, format, build:* and tsx invocation.
- Avoid committing lockfile changes by resetting pnpm-lock.yaml instead
  of yarn.lock when checking for changes.
- Update install flags: use pnpm install --frozen-lockfile / --install
  semantics where appropriate.

These changes unify dependency tooling, improve caching correctness,
and ensure CI uses pnpm-specific lockfile and cache paths.

* build: switch pre-commit hook to pnpm lint-staged

Update .husky/pre-commit to run pnpm lint-staged instead of yarn.
This aligns the pre-commit hook with the project's package manager
and ensures lint-staged runs using pnpm's environment and caching.

* chore(ci): remove pinned pnpm version from GH Action steps

Remove the explicit `with: version: 9` lines from multiple GitHub Actions workflows
(auto-i18n.yml, nightly-build.yml, pr-ci.yml, update-app-upgrade-config.yml,
sync-to-gitcode.yml, release.yml). The workflows still call `pnpm/action-setup@v4`
but no longer hardcode a pnpm version.

This simplifies maintenance and allows the action to resolve an appropriate pnpm
version (or use its default) without needing updates whenever the pinned
version becomes outdated. It reduces churn when bumping pnpm across CI configs
and prevents accidental pin drift between workflow files.

* build: Update pnpm to 10.27.0 and add onlyBuiltDependencies config

* Update @cherrystudio/openai to 6.15.0 and consolidate overrides

* Add @langchain/core to overrides

* Add override for openai-compatible 1.0.27

* build: optimize pnpm config and add missing dependencies

- Comment out shamefully-hoist in .npmrc for better pnpm compatibility
- Add React-related packages to optimizeDeps in electron.vite.config.ts
- Add missing peer dependencies and packages that were previously hoisted

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* build: refine pnpm configuration and dependency management

- Simplify .npmrc to only essential electron mirror config
- Move platform-specific dependencies to devDependencies
- Pin sharp version to 0.34.3 for consistency
- Update sharp-libvips versions to 1.2.4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* reduce app size

* format

* build: remove unnecessary disableOxcRecommendation option from react plugin configuration

* docs: Replace yarn commands with pnpm in documentation and scripts

* Revert "build: optimize pnpm config and add missing dependencies"

This reverts commit acffad31f8.

* build: import dependencies from yarn.lock

* build: Add some phantom dependencies and reorganize dependencies

* build: Keep consistent by removing types of semver

It's not in the previous package.json

* build: Add some phantom dependencies

Keep same version with yarn.lock

* build: Add form-data dependency version 4.0.4

* Add chalk dependency

* build: downgrade some dependencies

Reference: .yarn-state-copy.yml. These phantom dependencies should use top-level package of that version in node_modules

* build: Add phantom dependencies

* build: pin tiptap dependencies to exact versions

Ensure consistent dependency resolution by removing caret ranges and pinning all @tiptap packages to exact version 3.2.0

* chore: pin embedjs dependencies to exact versions

* build: pin @modelcontextprotocol/sdk to exact version 1.23.0

Remove caret from version specifier to prevent automatic upgrades and ensure consistent dependencies

* chore: update @types/node dependency to 22.17.2

Update package.json and pnpm-lock.yaml to use @types/node version 22.17.2 instead of 22.19.3 to maintain consistency across dependencies

* build: move some dependencies to dev deps and pin dependency versions to exact numbers

Remove caret (^) from version ranges to ensure consistent dependency resolution across environments

* chore: move dependencies from prod to dev and update lockfile

Move @ant-design/icons, chalk, form-data, and open from dependencies to devDependencies
Update pnpm-lock.yaml to reflect dependency changes

* build: update package dependencies

- Add new dependencies: md5, @libsql/win32-x64-msvc, @strongtz/win32-arm64-msvc, bonjour-service, emoji-picker-element-data, gray-matter, js-yaml
- Remove redundant dependencies from devDependencies

* build: add cors, katex and pako dependencies

add new dependencies to support cross-origin requests, mathematical notation rendering and data compression

* move some js deps to dev deps

* test: update snapshot tests for Spinner and InputEmbeddingDimension

* chore: exclude .zed directory from biome formatting

* Update @ai-sdk/openai-compatible patch hash

* chore: update @kangfenmao/keyv-storage to version 0.1.3 in package.json and pnpm-lock.yaml

---------

Co-authored-by: icarus <eurfelux@gmail.com>
Co-authored-by: beyondkmp <beyondkmp@gmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: kangfenmao <kangfenmao@qq.com>
2026-01-05 22:16:34 +08:00

5.0 KiB

How to Do i18n Gracefully

Warning

This document is machine translated from Chinese. While we strive for accuracy, there may be some imperfections in the translation.

Enhance Development Experience with the i18n Ally Plugin

i18n Ally is a powerful VSCode extension that provides real-time feedback during development, helping developers detect missing or incorrect translations earlier.

The plugin has already been configured in the project — simply install it to get started.

Advantages During Development

  • Real-time Preview: Translated texts are displayed directly in the editor.
  • Error Detection: Automatically tracks and highlights missing translations or unused keys.
  • Quick Navigation: Jump to key definitions with Ctrl/Cmd + click.
  • Auto-completion: Provides suggestions when typing i18n keys.

Demo

demo-1

demo-2

demo-3

i18n Conventions

Avoid Flat Structure at All Costs

Never use flat structures like "add.button.tip": "Add". Instead, adopt a clear nested structure:

// Wrong - Flat structure
{
  "add.button.tip": "Add",
  "delete.button.tip": "Delete"
}

// Correct - Nested structure
{
  "add": {
    "button": {
      "tip": "Add"
    }
  },
  "delete": {
    "button": {
      "tip": "Delete"
    }
  }
}

Why Use Nested Structure?

  1. Natural Grouping: Related texts are logically grouped by their context through object nesting.
  2. Plugin Requirement: Tools like i18n Ally require either flat or nested format to properly analyze translation files.

Avoid Template Strings in t()

We strongly advise against using template strings for dynamic interpolation. While convenient in general JavaScript development, they cause several issues in i18n scenarios.

1. Plugin Cannot Track Dynamic Keys

Tools like i18n Ally cannot parse dynamic content within template strings, resulting in:

  • No real-time preview
  • No detection of missing translations
  • No navigation to key definitions
// Not recommended - Plugin cannot resolve
const message = t(`fruits.${fruit}`);

2. No Real-time Rendering in Editor

Template strings appear as raw code instead of the final translated text in IDEs, degrading the development experience.

3. Harder to Maintain

Since the plugin cannot track such usages, developers must manually verify the existence of corresponding keys in language files.

To avoid missing keys, all dynamically translated texts should first maintain a FooKeyMap, then retrieve the translation text through a function.

For example:

// src/renderer/src/i18n/label.ts
const themeModeKeyMap = {
  dark: "settings.theme.dark",
  light: "settings.theme.light",
  system: "settings.theme.system",
} as const;

export const getThemeModeLabel = (key: string): string => {
  return themeModeKeyMap[key] ? t(themeModeKeyMap[key]) : key;
};

By avoiding template strings, you gain better developer experience, more reliable translation checks, and a more maintainable codebase.

Automation Scripts

The project includes several scripts to automate i18n-related tasks:

i18n:check - Validate i18n Structure

This script checks:

  • Whether all language files use nested structure
  • For missing or unused keys
  • Whether keys are properly sorted
pnpm i18n:check

i18n:sync - Synchronize JSON Structure and Sort Order

This script uses zh-cn.json as the source of truth to sync structure across all language files, including:

  1. Adding missing keys, with placeholder [to be translated]
  2. Removing obsolete keys
  3. Sorting keys automatically
pnpm i18n:sync

i18n:translate - Automatically Translate Pending Texts

This script fills in texts marked as [to be translated] using machine translation.

Typically, after adding new texts in zh-cn.json, run i18n:sync, then i18n:translate to complete translations.

Before using this script, set the required environment variables:

API_KEY="sk-xxx"
BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1/"
MODEL="qwen-plus-latest"

Alternatively, add these variables directly to your .env file.

pnpm i18n:translate

Workflow

  1. During development, first add the required text in zh-cn.json
  2. Confirm it displays correctly in the Chinese environment
  3. Run pnpm i18n:sync to propagate the keys to other language files
  4. Run pnpm i18n:translate to perform machine translation
  5. Grab a coffee and let the magic happen!

Best Practices

  1. Use Chinese as Source Language: All development starts in Chinese, then translates to other languages.
  2. Run Check Script Before Commit: Use pnpm i18n:check to catch i18n issues early.
  3. Translate in Small Increments: Avoid accumulating a large backlog of untranslated content.
  4. Keep Keys Semantically Clear: Keys should clearly express their purpose, e.g., user.profile.avatar.upload.error