A GitHub Action that translates iOS apps using AI, built on top of string catalogs.
Xcode 15 introduced String Catalogs – a new unified, auto-updating translations file designed to set an alternative to the legacy .strings files pattern.
This GitHub Action leverages OpenAI's Completions API to automatically and continuously keep your string catalogs up-to-date with translations across multiple languages. This action runs a job that reviews your string catalog, looks for modifications, calls the OpenAI, and finally opens a Pull Request with the translated catalog.
- Automated Translation: Translates missing strings in your String Catalog (
.xcstrings) files using OpenAI's Completions API - Smart Detection: Only translates strings that are missing or need updates
- Batch Processing: Efficiently processes multiple strings and languages in single API calls
- Comment Support: Uses String Catalog comments as context for more accurate translations
- Selective Translation: Respects
shouldTranslate: false('DON'T TRANSLATE') flag to skip specific strings - Stale Cleanup: Automatically removes outdated strings marked as
stale - Pull Request Integration: Creates organized PRs with detailed change summaries
- Placeholder Preservation: Maintains string interpolation placeholders (e.g.,
%@,%d) in translations
Before using this action, you'll need:
- OpenAI API Key: Obtain an OpenAI API key and store it as a Repository secret (Under repo settings → Security → Secrets and variables → Actions)
- Actions Permissions: Enable 'Read and write permissions' and 'Allow GitHub Actions to create and approve pull requests' under Actions in the repo settings.
- String Catalog: An existing
.xcstringsfile in your iOS project (generated by Xcode 15 or newer). To get started with string catalogs, follow this Apple Developer article.
Add the following workflow file to your iOS project at .github/workflows/ios-vibe-localization.yml:
name: iOS Vibe Localization
on:
push:
branches:
- main
pull_request:
- main
jobs:
localize:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: iOS Vibe Localization
uses: eilonkr/[email protected]
with:
xcstrings_file_path: 'Localizable.xcstrings' # Path to your String Catalog in your project
source_language: 'en' # Optional: source locale for Text; when omitted, the key is used
target_languages: 'es,fr,de,ja,ko' # Customize as needed
openai_model: 'gpt-4o-mini' # Customize as needed
base_system_prompt: 'You are translating for a fitness app. Use casual, motivational language.' # An additional system prompt for LLM context (optional)
github_token: ${{ secrets.GITHUB_TOKEN }}
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}| Input | Required | Default | Description |
|---|---|---|---|
xcstrings_file_path |
No | Localizable.xcstrings |
Path to your String Catalog file |
source_language |
No | - | Language code to use as the source text (e.g., en). When omitted, the string key is used. |
target_languages |
Yes | - | Comma-separated language codes (e.g., es,fr,de) |
github_token |
Yes | - | GitHub token for creating PRs |
openai_model |
No | gpt-4o-mini |
OpenAI model to use |
base_system_prompt |
No | - | Additional system prompt for providing context to the LLM |
pr_branch_prefix |
No | ios-vibe-localization-updates/ |
Prefix for PR branch names |
commit_user_name |
No | github-actions[bot] |
Git commit author name |
commit_user_email |
No | github-actions[bot]@users.noreply.github.com |
Git commit author email |
commit_message |
No | i18n: Update translations by iOS Vibe Localization Action |
Commit message |
pr_title |
No | iOS Vibe Localization: Automated Localization Updates |
Pull request title |
pr_body |
No | Automated localization updates by the iOS Vibe Localization Action. |
Pull request body |
Any literal string in SwiftUI text components will be automatically added to the string catalog (.xcstrings) file:
Text("The sunrise was particularly beautiful today.") // Will be added to the string catalogTo provide context for the translation API, add comments that will be reflected in the string catalog and used as context for translations:
Text("Save your progress", comment: "Button text for saving user data")For strings outside of views (such as in view models or enums), use LocalizedStringResource:
enum Animal {
case rabbit
case dog
case goose
var name: LocalizedStringResource {
switch self {
case .rabbit:
"Rabbit"
case .dog:
"Dog"
case .goose:
LocalizedStringResource("Goose", comment: "Bird species name")
}
}
}Ignoring strings: In the string catalog, 'Mark as "Don't translate"' for strings will be ignored by this action
For UIKit projects, use the String(localized:) API, which also supports comments for context:
let welcomeMessage = String(localized: "Welcome to our app!", comment: "Greeting shown on app launch")Important: Make sure to build your project with Xcode after adding new strings to ensure your string catalog is synchronized with your project's localizable strings.
The developer of this action is not responsible for any OpenAI API charges incurred through its use. Please monitor your OpenAI usage and set appropriate billing limits in your OpenAI account to avoid unexpected charges.
Consider using this action judiciously by:
- Testing with a small subset of languages first
- Setting up OpenAI usage alerts
- Reviewing the number of strings to be translated before running the action
- Using specific file paths rather than translating entire catalogs when possible
🤖 Additional LLM Providers: Support for Claude, DeepSeek, and other language models
📚 Multiple Catalog Tables: Support for multiple string tables within a single catalog (learn more)
👥 Pluralization Support
This project welcomes contributions! Feel free to open a pull request for any of the roadmap features or other improvements you think would be useful.
This project is licensed under the MIT License - see LICENSE for details.
