sync
Sur cette page
Compare your codebase's t() calls with translations stored in Better i18n (remote) and audit your project for consistency.
When to use sync #
The sync command is a powerful auditing tool. Common scenarios include:
- Before opening a PR: Ensure all new translation keys are present in Better i18n.
- After adding new pages: Automatically identify and log missing keys.
- After refactors: Detect "dead" keys that are no longer referenced in code.
- Before shipping a new locale: Verify that the source keys are stable.
- Debugging: Use
--verboseto trace why certain keys are not being picked up. - CI Gating: Use
--format jsonto fail builds if missing keys are detected.
Workflow #
- Run
sync: See the tree of missing or unused keys. - Dashboard/AI: Add the missing keys via the Better i18n dashboard or AI suggestions.
- Verify: Run
sync --summaryto confirmmissingCountis 0. - Enforce: Integrate into CI to prevent missing keys from reaching production.
Usage #
Bash
better-i18n sync # Grouped tree output (default)
better-i18n sync --summary # High-level metrics only
better-i18n sync --verbose # Deep audit & verification
better-i18n sync --format json # JSON output for automation
better-i18n sync -d ./src # Scan specific directoryOptions #
| Option | Description |
|---|---|
--format <type> | eslint (default) for humans, json for machines. |
--verbose | Detailed audit log: invariant checks (PASS/FAIL), scoping summary, dynamic key skips, and specific key probes. |
--summary | Show only top-level percentage coverage and counts without the detailed tree. |
-d, --dir <path> | Specify the directory to scan (default: current directory). |
Text
📊 Translation Keys Comparison
Source locale: en
Coverage:
Local → Remote: 59%
Remote Used: 63%
⊕ Missing in Remote (473 keys)
Keys used in code but not uploaded to Better i18n
pages (300)
affordableEnglishLearning (meta.title, meta.description, meta.keywords, hero.badge, ...+12)
bestApps (hero.badge, title_prefix, title_accent, subtitle)
hero (5)
hero (ariaLabel, imageAlt, ...)
⊖ Unused in Code (386 keys)
Keys in Better i18n but not detected in code usage
features (25)
practiceSpeaking (title, subtitle, icon)
Scanned 246 files in 0.85s
✓ Comparison completeHow namespace binding works #
The CLI uses Lexical Scope Tracking to resolve namespaces automatically for both client and server components.
Client Components (React Hooks) #
TSX
// All t() calls inside this scope become 'hero.key'
const t = useTranslations('hero');
return <h1>{t('title')}</h1>; // Detected as: hero.titleServer Components (Async Functions) #
TSX
// ✅ Direct string namespace
const t = await getTranslations('welcome');
return <h1>{t('title')}</h1>; // Detected as: welcome.title
// ✅ Object with namespace property
const t = await getTranslations({
locale: params.locale,
namespace: 'maintenance'
});
return <p>{t('message')}</p>; // Detected as: maintenance.message
// ⚠️ Root scoped (no namespace)
const t = await getTranslations();
return <p>{t('global.title')}</p>; // Detected as: global.title (root scope)Supported Patterns #
useTranslations('namespace')- Client component with namespacegetTranslations('namespace')- Server component with namespacegetTranslations({ locale, namespace: 'namespace' })- Server with localeuseTranslations()/getTranslations()- Root scoped (no namespace prefix)- Standard Methods: All patterns support
t(),t.raw(),t.rich(), andt.has().
Dynamic Namespaces #
If a namespace is a variable or template literal (e.g., useTranslations(`pages.${slug}`)), it is reported as unknown-scoped in --verbose mode and skipped from metrics to prevent false positives.
Flattening rules #
To ensure CLI metrics match the Better i18n UI:
- Primitives are leaves: Strings, numbers, and booleans are counted as keys.
- Arrays are leaves:
meta.keywords: ["i18n", "cli"]is counted as one key, not two. This matches the UI's "Base Key" counting. - Objects are containers: Keys like
pages.home.herorepresenting a grouping of sub-keys are ignored in the final key count.
JSON Output #
Use --format json for CI scripts and automation.
JSON
{
"project": {
"workspace": "my-org",
"slug": "my-app",
"sourceLocale": "en"
},
"localKeys": {
"total": 1223
},
"comparison": {
"missingInRemote": { "pages.bestApps": ["pages.bestApps.meta.title"] },
"unusedInCode": { "old": ["old.unused_key"] },
"missingCount": 473,
"unusedCount": 386
},
"coverage": {
"local": 59,
"remote": 63
},
"files": 246,
"duration": 850
}Bash
# Using jq to check missingCount
better-i18n sync --format json | jq -e '.comparison.missingCount == 0' > /dev/null || exit 1List missing keys in CI logs #
Bash
better-i18n sync --format json | jq '.comparison.missingInRemote'
Better I18N