Translate Apple's Localizable.xcstrings files using AI. Supports Claude, GPT, and Gemini.
xcstrings translate Localizable.xcstrings -l fr,es,it,ja,ko- Multi-provider - Choose Claude, GPT, or Gemini based on cost/quality needs
- Context-aware - Uses existing translations to understand meaning and tone
- Format-safe - Preserves
%@,%lld,%1$@and other specifiers - 35+ languages - All major App Store locales
- Recursive - Point at a directory to translate every nested
.xcstringsfile - Cost estimation - Preview costs before translating
- Validation - Detect missing translations and format mismatches
# Install
pip install xcstrings-translator
# Set API key (pick one)
export ANTHROPIC_API_KEY=sk-... # Claude
export OPENAI_API_KEY=sk-... # GPT
export GOOGLE_API_KEY=... # Gemini
export OPENROUTER_API_KEY=sk-or-... # OpenRouter (any vendor)
# Translate
xcstrings translate Localizable.xcstrings -l fr,de,ja| Model | Provider | Cost/1M tokens | Best for |
|---|---|---|---|
haiku |
Anthropic | $1 in / $5 out | Fast iteration |
sonnet |
Anthropic | $3 in / $15 out | Recommended (default) |
opus |
Anthropic | $5 in / $25 out | Highest quality |
gpt-5.4-nano |
OpenAI | $0.20 in / $1.25 out | Cheapest |
gpt-5.4 |
OpenAI | $2.50 in / $15 out | Quality |
gpt-5.5 |
OpenAI | $5 in / $30 out | Flagship |
gemini-3.5-flash |
$1.50 in / $9 out | Fast & balanced | |
gemini-3.1-pro |
$2 in / $12 out | Quality |
Older aliases still work:
gpt-5,gpt-5-mini,gpt-5-nano,o3,o4-mini,gemini-2.5-pro/flash,gemini-2.0-flash.
xcstrings translate input.xcstrings -l fr -m haiku # fast/cheap
xcstrings translate input.xcstrings -l fr -m sonnet # balanced (default)
xcstrings translate input.xcstrings -l fr -m gpt-5.4-nano # cheapestRoute through OpenRouter to reach any vendor with one key. Set
OPENROUTER_API_KEY, then pass an openrouter:vendor/model string or a shorthand alias:
| Alias | Model |
|---|---|
or-opus, or-sonnet, or-haiku |
Anthropic Claude (Opus 4.8 / Sonnet 4.6 / Haiku 4.5) |
or-gpt-5.5, or-gpt-5.4, or-gpt-5.4-mini, or-gpt-5.4-nano |
OpenAI GPT-5.5 / 5.4 family |
or-gpt-5, or-gpt-5-mini, or-gpt-5-nano |
OpenAI GPT-5 family |
or-gemini-3.5-flash, or-gemini-3.1-pro, or-gemini-3-flash |
Google Gemini 3.x |
or-gemini-pro, or-gemini-flash |
Google Gemini 2.5 Pro / Flash |
xcstrings translate input.xcstrings -l fr -m openrouter:anthropic/claude-sonnet-4.6
xcstrings translate input.xcstrings -l fr -m or-gpt-5.4-nanoCost estimates for openrouter:* models are fetched live from OpenRouter's public
model catalog (cached ~24h under ~/.cache/xcstrings-translator/), so any model — even
brand-new ones — gets accurate pricing in estimate/--dry-run. Pass --no-fetch to
skip the network and use cached/static prices only.
| Command | Description |
|---|---|
translate |
Translate to specified languages |
estimate |
Preview cost without translating |
info |
Show file stats and coverage |
validate |
Check for issues |
languages |
List all 35+ supported languages |
# Translate to multiple languages
xcstrings translate Localizable.xcstrings -l fr,es,it,de,ja
# Translate every .xcstrings under a project directory (recursive; asks first)
xcstrings translate ./MyApp -l fr,es,it
xcstrings translate ./MyApp --fill-missing -y # skip the confirmation prompt
# Estimate cost first
xcstrings translate Localizable.xcstrings -l fr,es,it --dry-run
# Use faster model
xcstrings translate Localizable.xcstrings -l fr -m haiku
# Parallel requests (faster for large files)
xcstrings translate Localizable.xcstrings -l fr,es -c 32
# Save to different file
xcstrings translate input.xcstrings -l fr -o translated.xcstrings
# Fill missing translations only
xcstrings translate Localizable.xcstrings --fill-missing
# Check translation coverage
xcstrings info Localizable.xcstrings
# Validate format specifiers
xcstrings validate Localizable.xcstringsProvide context for better translations. Create context.md next to your xcstrings file:
# My Fitness App
A workout tracking app for athletes.
Tone: Motivational, energetic.
Use informal "you" (du/tu).Or pass directly:
xcstrings translate input.xcstrings -l fr --context "A fitness app with motivational tone"from xcstrings_translator import XCStringsFile, XCStringsTranslator
# Load
xcstrings = XCStringsFile.from_file("Localizable.xcstrings")
# Translate
translator = XCStringsTranslator(model="sonnet")
xcstrings = translator.translate_file(xcstrings, ["fr", "es", "it"])
# Save
xcstrings.to_file("Localizable.xcstrings")
# Stats
print(f"Translated: {translator.stats.translated}")
# Rates below are sonnet's ($3 in / $15 out per 1M tokens); adjust for your model.
print(f"Cost: ${translator.stats.input_tokens * 3 / 1e6 + translator.stats.output_tokens * 15 / 1e6:.2f}")European: en, de, fr, es, it, pt, pt-BR, pt-PT, nl, pl, sv, da, nb, fi, cs, sk, hu, ro, bg, el, sq, uk, ru, tr
Asian: ja, ko, zh-Hans, zh-Hant, th, vi, id, ms, hi
Middle Eastern: ar, he
Other: ca, eu
| Option | Description | Default |
|---|---|---|
-l, --languages |
Target language codes | Required |
-m, --model |
AI model | sonnet |
-o, --output |
Output file | Overwrites input |
-b, --batch-size |
Strings per API call | 25 |
-c, --concurrency |
Parallel requests | 32 |
--context |
App description | context.md |
--overwrite |
Replace existing | false |
--dry-run |
Estimate only | false |
-f, --fill-missing |
Auto-detect languages | false |
-y, --yes |
Skip confirmation for directory runs | false |
"Unsupported language" - Use Apple codes: zh-Hans not zh-CN, pt-BR not pt_BR
Format mismatch - Run xcstrings validate to find issues
API errors - Check correct key is set for your model
Contributions are welcome! See CONTRIBUTING.md for dev setup, tests, and the release process. Please also review our Code of Conduct. To report a security issue, see SECURITY.md.
MIT
Made with ❤️ by Justin Lanfermann