This project uses an _automated_translation.json file to track context for every translatable string before translation. This ensures:
- Translations account for how strings are used, not just literal word meanings
- Consistency across languages
- Prevention of errors like translating "Left" as directional when it means currency symbol position
- A permanent record of why each string is translated a certain way
-
_automated_translation.json — Master context file
- Contains ALL keys from
assets/locales/en-US.json - For each key: context, page, component, meaning, notes, status
- Single source of truth for translation decisions
- Contains ALL keys from
-
assets/locales/en-US.json — English source strings
- The reference for all keys that need translation
- Updated via
python3 scripts/update_en_strings.py
-
.claude/skills/translate/SKILL.md — Translation workflow
- Step-by-step guide for translating strings
- Enforces context lookup via _automated_translation.json
-
Run the sync script to update all locale files:
python3 scripts/update_en_strings.py
-
Verify the new key exists in _automated_translation.json:
grep '"your_new_key"' _automated_translation.json- If missing, regenerate (see "Regenerating _automated_translation.json" below)
-
Research context for the new key:
- Search codebase:
grep -rn "your_new_key" lib/ --include="*.dart" - Read surrounding code (5-10 lines before/after)
- Understand: What page is it on? What UI component? What does the user see?
- Search codebase:
-
Update _automated_translation.json:
# Edit manually or use provided Python script # For the new key, fill in: # - context: Where it appears # - file: Which file(s) use it # - page: Which app page (e.g., "Settings > Currency") # - component: UI element type (e.g., "dropdown option", "button label") # - meaning: What it means to the end user # - notes: Any special translation considerations # - status: Set to "verified" once context is documented
-
Invoke the translate skill:
/translate
- The skill will check _automated_translation.json for context
- For keys with verified context, it can proceed with translations
- For keys with status="pending", it will research and add context
You add this to en-US.json:
"Email": "Email"Then:
- Run
python3 scripts/update_en_strings.py - Search:
grep -rn "Email" lib/→ find it inlib/settings/profile-page.dartline 42 - Read surrounding code → "Email" is a label above a text field for entering email
- Update _automated_translation.json:
"Email": {
"context": "User profile email field label",
"file": "lib/settings/profile-page.dart:42",
"page": "Settings > Profile",
"component": "TextFormField label",
"meaning": "Electronic mail address input",
"notes": "Keep as-is in many languages - email is universal term",
"status": "verified"
}- Run
/translate→ skill sees context and translates appropriately
If keys get out of sync with en-US.json (keys added/removed/renamed):
python3 << 'EOF'
import json
from pathlib import Path
# Read current en-US.json
with open('assets/locales/en-US.json', 'r', encoding='utf-8') as f:
en_us = json.load(f)
# Read current _automated_translation.json to preserve context
with open('_automated_translation.json', 'r', encoding='utf-8') as f:
existing = json.load(f)
# Merge: keep existing context, add new keys as pending
merged = {}
for key in sorted(en_us.keys()):
if key in existing['keys']:
# Preserve existing context
merged[key] = existing['keys'][key]
else:
# New key - pending context
merged[key] = {
"key": key,
"context": None,
"file": None,
"page": None,
"component": None,
"meaning": None,
"notes": None,
"status": "pending"
}
# Write updated file
output = existing['_meta']
output['total_keys'] = len(merged)
output['last_updated'] = "2026-04-21" # Update date
with open('_automated_translation.json', 'w', encoding='utf-8') as f:
json.dump({'_meta': output, 'keys': merged}, f, indent=2, ensure_ascii=False)
# Report removed keys
removed = set(existing['keys'].keys()) - set(en_us.keys())
if removed:
print(f"Removed {len(removed)} keys from tracking:")
for key in sorted(removed):
print(f" - {key}")
print(f"Total keys now: {len(merged)}")
EOF- pending — Context needs to be researched and documented before translation
- verified — Context is documented; ready to translate
- skip — Should NOT be translated (brand names like "Oinkoin Pro", technical terms, universal terms like "OK")
When /translate is invoked:
- Check _automated_translation.json for each untranslated key
- If status="verified": Use documented context to translate
- If status="pending":
- Research context (grep codebase, read surrounding code)
- Update _automated_translation.json with findings
- Set status to "verified" (or "skip" if not translatable)
- Then translate
- Write translations only after context is established
- Be specific: "Button label on Settings page" not just "Button"
- Include examples: "Currency symbol position dropdown" with options like "$100" vs "100$"
- Note language considerations: "May have same word in target language", "Check financial app conventions", etc.
- Don't skip lightly — only skip if truly untranslatable
- Mark as "pending" if unsure — better to slow down than guess
- Once "verified", context becomes the source of truth for all locales
- Update _automated_translation.json before translating
- Commit changes to this file with the translations
- Keep it in sync with en-US.json
- Review quarterly for outdated or unclear context entries
If a translation is discovered to be wrong:
- Root cause: Check _automated_translation.json
- Was context missing or wrong? → Update context
- Was context correct but translation missed it? → Retranslate
- Fix context in _automated_translation.json first
- Retranslate with corrected context
- Document lesson learned in notes field for future reference
Mistake: Translated "Left" as simple directional term "Sinistra" in Italian
Root cause: Didn't verify context in _automated_translation.json
Fix:
- Updated _automated_translation.json:
"Left": { "context": "Currency symbol position option", "file": "lib/settings/constants/preferences-options.dart:136", "page": "Settings > Currency", "component": "currencySymbolPosition dropdown", "meaning": "Position currency symbol to the LEFT of amount (e.g., $100)", "notes": "Positional context - use 'A sinistra' or equivalent in target language", "status": "verified" }
- Retranslated with positional context in mind
- Similar fix for "Right", "With space", "Without space" options
# Sync en-US.json and update all locales with new keys
python3 scripts/update_en_strings.py
# Find what needs translating
python3 scripts/find_missing_translations.py
# When ready to translate (after verifying context)
/translate
# Verify a specific key exists in tracking file
grep '"your_key"' _automated_translation.json
# View context for a key
python3 -c "import json; d=json.load(open('_automated_translation.json')); print(json.dumps(d['keys']['your_key'], indent=2))"