Skip to content

Latest commit

 

History

History
226 lines (169 loc) · 7.81 KB

File metadata and controls

226 lines (169 loc) · 7.81 KB

i18n Coverage Guide

This document explains how Claude Terminal handles internationalization (i18n), how to add a new language, and how the automated coverage badges work.


Table of Contents


Locale file format

All locale files live in:

src/renderer/i18n/locales/
├── en.json   ← reference/base (always 100 %)
├── fr.json   ← French translation
├── es.json   ← Spanish translation
├── id.json   ← Indonesian translation
└── zh-CN.json ← Simplified Chinese translation

Each file is a nested JSON object where leaf values are translated strings. Interpolation variables are wrapped in curly braces: {variable}.

Example

// en.json (base)
{
  "common": {
    "close": "Close",
    "loading": "Loading..."
  },
  "terminals": {
    "notifToolsDone": "Task complete — {count} tools used"
  }
}

// fr.json (translation)
{
  "common": {
    "close": "Fermer",
    "loading": "Chargement..."
  },
  "terminals": {
    "notifToolsDone": "Tâche terminée — {count} outils utilisés"
  }
}

Rules:

  • The structure (nesting, key names) must mirror en.json exactly.
  • Variable names inside {...} must not be translated.
  • Do not remove or rename top-level namespace keys (e.g., common, git).
  • Extra keys that don't exist in en.json are allowed but will be reported by the coverage checker.

Adding a new language

  1. Copy the base locale:

    cp src/renderer/i18n/locales/en.json src/renderer/i18n/locales/<lang>.json

    Replace <lang> with the ISO 639-1 code (e.g., de, es, ja, pt).

    Use a plain code unless the language genuinely needs different translations per region. When it does, use the full BCP 47 tag with an uppercase region (zh-CN, zh-TW, pt-BR): the loader tries the exact tag before the bare primary subtag, so both forms auto-detect correctly. Do not register a bare zh alongside zh-CN expecting them to be interchangeable, they are two separate locale files.

  2. Translate the values (not the keys) in the new file.

  3. Verify coverage:

    node scripts/check-i18n.js --locale=<lang>
  4. Register the new locale in the i18n loader:

    • Open src/renderer/i18n/index.js and add the code to SUPPORTED_LANGUAGES plus its display name to LANGUAGE_NAMES.
  5. Ship the locale file with the build — add the code to LAZY_LOCALES in scripts/build-renderer.js:

    const LAZY_LOCALES = ['fr', 'es', 'id', '<lang>'];

    Only en.json is bundled into renderer.bundle.js; every other locale is read at runtime from dist/locales/, which this list populates. Skipping this step is silent: the language shows up in the picker and is persisted, but the UI keeps rendering English, and the Jest suite still passes because it loads locales straight from the source tree.

  6. Translate the project-type locales (optional but recommended) — each type under src/project-types/*/i18n/ ships its own small locale file, and the type registers them in its getTranslations(). Without a <lang>.json there the FiveM / web app / API / Minecraft / Python / Discord panels fall back to English key by key while the rest of the UI is translated.

  7. Add a badge block to .github/workflows/i18n-badge.yml:

    - name: Update badge — German (de)
      if: ${{ steps.parse.outputs.message_de != '' && secrets.GIST_SECRET != '' && secrets.GIST_ID != '' }}
      uses: schneegans/dynamic-badges-action@e9a478b16159b4d31420099ba146cdc50f134483  # v1.7.0
      with:
        auth: ${{ secrets.GIST_SECRET }}
        gistID: ${{ secrets.GIST_ID }}
        filename: i18n_de.json
        label: "🌐 German (de)"
        message: ${{ steps.parse.outputs.message_de }}
        color: ${{ steps.parse.outputs.color_de }}
  8. Add the badge to README.md (in the badges block at the top):

    ![i18n de](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/bernardopg/8aa5c09aca432a7a39aefe32e8ed393a/raw/i18n_de.json)
  9. Update TRANSLATIONS.md at the project root with the new locale row.

  10. Open a Pull Request — the CI pipeline will run the coverage check automatically.


Running the coverage check locally

No extra dependencies needed — only the Node.js built-in modules are used.

# Check all locales (human-readable table)
node scripts/check-i18n.js

# Check a single locale
node scripts/check-i18n.js --locale=fr

# JSON output (for CI or badge generation)
node scripts/check-i18n.js --json

# Combined
node scripts/check-i18n.js --locale=fr --json

The script exits with code 0 when all locales meet the threshold, and 1 when any locale is below it.

Sample output:

────────────────────────────────────────────────────────────
 i18n Coverage Report — Claude Terminal
────────────────────────────────────────────────────────────
 Base locale : en.json  (800 keys — 100%)
 Threshold   : 80%
────────────────────────────────────────────────────────────

 Locale  : fr (fr.json)
 Status  : ✓ PASS
 Coverage: 800/800 keys (100%)
 Missing : 0 key(s)
 Extra   : 0 key(s) not in base

────────────────────────────────────────────────────────────
 ✓ All locales meet the 80% threshold.
────────────────────────────────────────────────────────────

Automated badge generation

The GitHub Actions workflow .github/workflows/i18n-badge.yml runs whenever a file under src/renderer/i18n/locales/** is pushed to the repository. It:

  1. Calls node scripts/check-i18n.js --json to compute coverage figures.
  2. For each non-English locale, calls schneegans/dynamic-badges-action to write a Shields.io endpoint payload to a GitHub Gist.
  3. The README badge image is served by Shields.io, pointing at the raw Gist URL.

One-time setup (maintainer only)

Step Action
1 Create a public Gist at https://gist.github.com (any file, any content).
2 Copy the Gist ID from the URL (the long hex string after your username).
3 Create a Personal Access Token (PAT) at https://github.com/settings/tokens with the gist scope.
4 Add two repository secrets under Settings → Secrets → Actions:
GIST_SECRET — the PAT
GIST_ID — the Gist ID
5 Trigger the workflow manually once via Actions → i18n Coverage Badges → Run workflow.

Coverage thresholds

Percentage Badge color Meaning
≥ 90 % 🟢 brightgreen Excellent coverage
60 – 89 % 🟡 yellow Work in progress
< 60 % 🔴 red Needs attention
< 80 % (any) CI warning is emitted

The threshold for CI warnings is configured at the top of scripts/check-i18n.js via the COVERAGE_THRESHOLD constant.