A native Swift command-line tool for automatically localizing Xcode .xcstrings files using AI-powered translation via OpenAI's API.
- ✅ Auto-Discovery: Automatically finds all
.xcstringsfiles in your project - ✅ Xcode Project Integration: Reads target languages from your project's
knownRegions - ✅ Multi-File Support: Process one, multiple, or all
.xcstringsfiles at once - ✅ Localized Extras: Automatically translates markdown/text files in
localized-extras/directory - ✅ Automatic Translation: Translates strings to all target languages
- ✅ AI-Powered Suggestions: Interactive review of existing translations with improvement suggestions
- ✅ Batch Processing: Groups strings for efficient API calls (15 strings at a time)
- ✅ App Context Support: Optional app description for better translation quality
- ✅ Context-Aware: Uses comments for better translation accuracy
- ✅ Smart Skipping: Respects
shouldTranslate: falseand skips already translated strings - ✅ Selective Translation: Translate specific keys or the entire file
- ✅ Preview Mode: Dry-run to see changes before applying
- ✅ Placeholder Preservation: Maintains format specifiers (
%@,%.0f, etc.) - ✅ Translation Caching: Avoids redundant API calls
- ✅ Self-Updating: Built-in update command and version notifications
- ✅ Zero Dependencies: Pure Swift, no external packages except ArgumentParser
One command to download and install the latest version:
curl -fsSL https://raw.githubusercontent.com/thillsman/XCStringsLocalizer/main/install.sh | bashThis will:
- Download the latest release from GitHub
- Install to
/usr/local/bin - Make it available globally as
xcstrings-localizer
Build from Source
git clone https://github.com/thillsman/XCStringsLocalizer.git
cd XCStringsLocalizer
swift build -c release
sudo cp .build/release/xcstrings-localizer /usr/local/bin/Use Swift Package Manager (Development)
git clone https://github.com/thillsman/XCStringsLocalizer.git
cd XCStringsLocalizer
swift run xcstrings-localizer --helpKeep your tool up to date with:
xcstrings-localizer updateOr re-run the installation script:
curl -fsSL https://raw.githubusercontent.com/thillsman/XCStringsLocalizer/main/install.sh | bashThe tool will also notify you when new versions are available.
You have three options:
Option 1: .env file (Recommended)
cd XCStringsLocalizer
echo "OPENAI_API_KEY='your-api-key-here'" > .envOption 2: Environment variable
export OPENAI_API_KEY='your-api-key-here'Option 3: Command line flag
xcstrings-localizer input.xcstrings --api-key 'your-api-key-here'Get your API key from: https://platform.openai.com/api-keys
Adding context about your app significantly improves translation quality. Add this to your .env file:
APP_DESCRIPTION='These are user-facing strings for an app called Sticky Widgets. The app allows users to create new notes, customize their appearance, and place them as widgets on the user'\''s iOS home screen.'Why this helps:
- ✅ LLM understands the domain (e.g., "note" means a written note, not a musical note)
- ✅ Maintains consistent terminology across translations
- ✅ Better context for ambiguous words
- ✅ More natural translations that fit your app's tone
Example .env file:
OPENAI_API_KEY='sk-proj-...'
APP_DESCRIPTION='A productivity app for managing daily tasks and reminders on iOS'# Auto-discover and translate all .xcstrings files in your project
xcstrings-localizer
# Translate a specific file
xcstrings-localizer Localizable.xcstrings
# Translate multiple specific files
xcstrings-localizer Localizable.xcstrings InfoPlist.xcstrings
# Or if not installed globally
swift run xcstrings-localizer- Language Detection: Checks your Xcode project's
knownRegionsfirst, then falls back to languages in the catalog - File Discovery: If no files specified, automatically finds all
.xcstringsfiles in current directory and subdirectories - Smart Translation: Only translates missing or new strings (use
--forceto retranslate all) - Localized Extras: Automatically translates files in
localized-extras/directory (see below)
# Auto-discover and preview changes (dry run)
xcstrings-localizer --dry-run
# Auto-discover and force re-translation of all strings
xcstrings-localizer --force
# Translate specific keys in discovered files
xcstrings-localizer --keys "Welcome" --keys "Goodbye"
# Translate a specific file with preview
xcstrings-localizer Localizable.xcstrings --dry-run
# Get AI suggestions for improving existing translations (interactive)
xcstrings-localizer --suggest
# Analyze only French translations
xcstrings-localizer --suggest --language fr
# Analyze French and German translations in a specific file
xcstrings-localizer Localizable.xcstrings --suggest --language fr --language de
# Analyze specific keys for improvement suggestions
xcstrings-localizer --suggest --keys "Welcome"
# Specify output file (only works with single input file)
xcstrings-localizer Localizable.xcstrings --output output.xcstrings
# Use different model
xcstrings-localizer --model gpt-5xcstrings-localizer --helpThe tool automatically translates additional files beyond .xcstrings catalogs!
Create a localized-extras/ directory in your project root and place any files you want translated:
mkdir localized-extras
cp appstore-description.md localized-extras/
cp release-notes.txt localized-extras/- Place source files (without language suffixes) in
localized-extras/ - Run the localizer as normal
- Translated versions are automatically created with language suffixes
Example:
localized-extras/
├── appstore.md # Source file
├── appstore.fr.md # Auto-generated French
├── appstore.de.md # Auto-generated German
├── appstore.ja.md # Auto-generated Japanese
└── release-notes.txt # Source file
├── release-notes.fr.txt # Auto-generated
└── ...
Any text file format:
- Markdown (
.md) - Plain text (
.txt) - HTML (
.html) - JSON (
.json) - And more!
- Smart Detection: Files with language suffixes (
.fr.md,.de.txt) are ignored as source files - Skip Existing: Already-translated files are skipped (use
--forceto retranslate) - Same Languages: Uses the same target languages as your
.xcstringsfiles - Preserves Formatting: Maintains markdown syntax, code blocks, and special characters
- Dry Run Support: Preview with
--dry-runbefore translating
Perfect for translating:
- 📱 App Store descriptions
- 📝 Release notes
- 📄 README files
- 🔒 Privacy policies
- 📋 Terms of service
- 📚 Documentation
# Navigate to your Xcode project directory
cd ~/MyApp
# Preview what will be translated
xcstrings-localizer --dry-run
# Perform the translation
xcstrings-localizerOutput:
Found 1 .xcstrings file(s):
• /Users/you/MyApp/Localizable.xcstrings
Loading: /Users/you/MyApp/Localizable.xcstrings
Found Xcode project with knownRegions: ar, de, es, fr, hi, it, ja, ko, pt, ru
Using app context: A productivity app for managing daily tasks and reminders...
Source language: en
Target languages: ar, de, es, fr, hi, it, ja, ko, pt, ru
Total keys in file: 247
Translating...
Translating 45 strings to fr...
Batch 1/3 (15 strings)
Batch 2/3 (15 strings)
Batch 3/3 (15 strings)
Translating 45 strings to de...
Batch 1/3 (15 strings)
Batch 2/3 (15 strings)
Batch 3/3 (15 strings)
...
Saving to: /Users/you/MyApp/Localizable.xcstrings
┌─────────────────────────────────────┬────────┐
│ Translation Summary │ │
├─────────────────────────────────────┼────────┤
│ Total keys │ 247 │
│ Translations created │ 450 │
│ Skipped (shouldTranslate=false) │ 20 │
│ Skipped (already translated) │ 2250 │
│ Errors │ 0 │
└─────────────────────────────────────┴────────┘
✓ Localization complete!
# Create localized-extras directory with marketing materials
mkdir localized-extras
cp marketing/appstore-description.md localized-extras/
cp marketing/release-notes.md localized-extras/
# Run localizer - translates both .xcstrings and extras
xcstrings-localizerOutput:
Found 1 .xcstrings file(s):
• Localizable.xcstrings
... (xcstrings translation) ...
Found localized-extras directory
Found 2 source file(s) to translate:
• appstore-description.md
• release-notes.md
Translating to: fr, de, es, ja
Processing: appstore-description.md
✓ appstore-description.fr.md
✓ appstore-description.de.md
✓ appstore-description.es.md
✓ appstore-description.ja.md
Processing: release-notes.md
✓ release-notes.fr.md
✓ release-notes.de.md
✓ release-notes.es.md
✓ release-notes.ja.md
# Auto-discover and suggest improvements
xcstrings-localizer --suggest
# Or for a specific file
xcstrings-localizer Localizable.xcstrings --suggestInteractive Output:
Found 1 .xcstrings file(s):
• /Users/you/MyApp/Localizable.xcstrings
Loading: Localizable.xcstrings
Found Xcode project with knownRegions: de, es, fr, ja
Source language: en
Target languages: de, es, fr, ja
Analyzing translations...
Analyzing 247 translations in de...
Batch 1/17 (15 strings)
Batch 2/17 (15 strings)
...
Found 3 high-confidence suggestion(s)
┌────────────────────────────────────────────────────────────┐
│ Found 8 suggestion(s) for improvement
└────────────────────────────────────────────────────────────┘
[1/8] Key: welcome_message
Language: French (Confidence: 5/5)
Current: Bienvenue à notre application
Suggested: Bienvenue dans notre application
Reason: More natural and idiomatic French expression. "dans" is more commonly used with applications than "à".
Accept this suggestion? [y/N/q] y
✓ Applied
[2/8] Key: settings_title
Language: German (Confidence: 4/5)
Current: Einstellungen Seite
Suggested: Einstellungen
Reason: More concise and natural. "Seite" (page) is redundant in this UI context.
Accept this suggestion? [y/N/q] n
✗ Skipped
...
Saving changes to: Localizable.xcstrings
✓ Successfully applied 5 suggestion(s)!
Rejected 3 suggestion(s).
Features:
- 🤖 AI analyzes existing translations for quality
- 🎯 Only suggests improvements with high confidence (4-5 out of 5)
- 📊 Shows reasoning for each suggestion
- ✋ Interactive approval - you decide what to apply
- 🔍 Can filter by specific keys using
--keys - 🌍 Can filter by specific languages using
--language - 💾 Changes only saved when you accept suggestions
Add a new "Run Script" phase in Xcode:
#!/bin/bash
cd "${SRCROOT}"
# Check for untranslated strings using auto-discovery
if /usr/local/bin/xcstrings-localizer --dry-run 2>&1 | grep -q "Translations created"; then
echo "warning: Untranslated strings detected. Run: xcstrings-localizer"
fiCreate .git/hooks/pre-commit:
#!/bin/bash
# Find modified .xcstrings files
CHANGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.xcstrings$')
if [ ! -z "$CHANGED" ]; then
echo "Auto-translating modified .xcstrings files..."
# Translate all modified files at once
xcstrings-localizer $CHANGED
# Re-add the translated files
git add $CHANGED
fi| Option | Short | Description | Default |
|---|---|---|---|
--output |
-o |
Output file path | Input file |
--keys |
-k |
Translate specific keys (repeatable) | All keys |
--language |
-l |
Specific languages to process (repeatable, e.g., fr, de) | All languages |
--force |
-f |
Re-translate already translated strings | false |
--dry-run |
-d |
Preview changes without saving | false |
--suggest |
-s |
Analyze and suggest improvements (interactive) | false |
--model |
-m |
OpenAI model to use | gpt-5-mini |
--api-key |
API key (overrides env var) | From env | |
--help |
-h |
Show help | |
--version |
Show version |
Available OpenAI models (2025):
-
gpt-5-nano: Lowest cost
- Input: $0.05 per 1M tokens
- Output: $0.40 per 1M tokens
- 128K context
-
gpt-5-mini (default): Best cost-performance balance for translation
- Input: $0.25 per 1M tokens
- Output: $2.00 per 1M tokens
- 128K context
-
gpt-5: Highest quality, recommended for premium content
- Input: $1.25 per 1M tokens
- Output: $10.00 per 1M tokens
- 128K context
The tool determines target languages in this priority order:
- Xcode Project's
knownRegions(primary) - Searches up to 3 parent directories for.xcodeprojfiles - Catalog Languages (fallback) - Uses languages already defined in the
.xcstringsfile
This ensures translations match your Xcode project's supported languages.
A string is translated if:
- ✅ It doesn't have
shouldTranslate: false - ✅ AND one of:
- No localization exists for target language
- Target language has
state: "new" - Target language value is empty
--forceflag is used
Priority order:
- English (
en) localization value - The key itself if no English localization
All format specifiers are preserved:
%@- String%d,%i- Integer%.0f,%.2f- Float with precision%1$@,%2$d- Positional\n- Newlines
XCStringsLocalizer/
├── Package.swift # Swift Package manifest
├── Sources/
│ ├── Main.swift # CLI entry point & argument parsing
│ ├── XCStringsModels.swift # Data models for .xcstrings format
│ ├── OpenAIClient.swift # OpenAI API client
│ ├── Localizer.swift # Core translation logic
│ ├── XcodeProjectParser.swift # Xcode project & file discovery
│ └── Config.swift # Configuration (.env loading)
└── README.md
swift buildswift testswift build
.build/debug/xcstrings-localizer --helpswift build -c release
.build/release/xcstrings-localizer --helpname: Auto-Translate Strings
on:
push:
paths:
- '**.xcstrings'
jobs:
translate:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Build localizer
run: |
cd XCStringsLocalizer
swift build -c release
- name: Translate strings (auto-discovery)
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
cd MyApp
../XCStringsLocalizer/.build/release/xcstrings-localizer
- name: Commit changes
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add *.xcstrings
git diff --staged --quiet || git commit -m "chore: auto-translate strings"
git pushlane :localize do
sh "xcstrings-localizer ../MyApp/Localizable.xcstrings"
build_app(scheme: "MyApp")
endThe tool uses intelligent batch processing to minimize API calls:
- Groups 15 strings per request (configurable in source)
- Reduces API calls by 93% compared to single-string requests
- Faster execution due to fewer network round-trips
- Lower costs by reducing redundant prompt overhead
The tool also caches translations within a session to minimize costs on re-runs.
Set your API key:
echo "OPENAI_API_KEY='sk-...'" > .envMake sure you have Xcode command line tools:
xcode-select --installMake the binary executable:
chmod +x .build/release/xcstrings-localizerEither:
- Use full path:
.build/release/xcstrings-localizer - Install to PATH:
sudo cp .build/release/xcstrings-localizer /usr/local/bin/
Contributions welcome! This is a pure Swift project with minimal dependencies.
MIT License
Built with:
- Swift - Apple's programming language
- Swift Argument Parser - CLI interface
- OpenAI API - AI translations
Happy Localizing! 🌍
XCStringsLocalizer is available under the MIT License. See LICENSE for details.