I've been building apps solo since 2014, and with over 50 million cumulative downloads across my library of wallpaper and wellness apps, one task consistently ate into my weekends: localization.
When you're managing multiple apps by yourself, localization tends to fall behind. You add a new UI component, make sure it works in English and Japanese, and leave a TODO comment for the other languages. Those TODOs accumulate. By the time you notice, there are dozens of untranslated keys sitting in your project — and syncing them manually across every language file feels like a whole separate job.
Bringing Claude Code into this workflow changed things. Not a complete hands-off solution, but the weight of the task shifted noticeably.
Why Localization Falls Behind in Solo Projects
When you're the only person on a project, your priorities almost always point toward shipping working features. A new screen gets built in English first, then a quick Japanese pass before release, and everything else gets deferred.
For my wallpaper apps, I support Japanese, English, Traditional Chinese, and Korean. That means four Localizable.strings files that need to stay in sync with every feature update. In practice, English moved first, and the other three languages often lagged by one or two releases.
App Store reviews occasionally pointed this out — "translation switches to English mid-screen" — and fixing those was always reactive. I wanted a proactive process instead.
The Core Approach: Passing Localizable.strings to Claude Code
The first approach I tried was straightforward: load the strings files into a Claude Code session and ask it to fill in missing translations.
claude "Read the following files.
Find keys that exist in en.lproj/Localizable.strings
but are missing from zh-Hant.lproj/Localizable.strings.
Generate translation candidates for those keys in natural Traditional Chinese.
Match the tone of the existing translations."This worked up to a point, but broke down when key counts got large. With hundreds of keys, context window pressure caused inconsistency in the later translations. The solution was to extract just the diff and pass that instead.
The Working Workflow: Extract the Diff First
Here's the shell script I now use to identify missing keys before involving Claude Code at all:
#!/bin/bash
# diff_strings.sh — extract untranslated keys from en.lproj
SOURCE="en.lproj/Localizable.strings"
TARGET="$1" # e.g., zh-Hant.lproj/Localizable.strings
# Extract all keys from source
grep -oE '"[^"]+"\s*=' "$SOURCE" | sed 's/\s*=//' | tr -d '"' > /tmp/source_keys.txt
# Extract all keys from target
grep -oE '"[^"]+"\s*=' "$TARGET" | sed 's/\s*=//' | tr -d '"' > /tmp/target_keys.txt
# Find keys in source but not in target
diff /tmp/source_keys.txt /tmp/target_keys.txt | grep '^<' | sed 's/^< //' > /tmp/missing_keys.txt
MISSING_COUNT=$(wc -l < /tmp/missing_keys.txt)
echo "Missing keys: $MISSING_COUNT"
# Extract the source values for missing keys
while IFS= read -r key; do
grep "\"$key\"" "$SOURCE"
done < /tmp/missing_keys.txtThen I pass the diff output to Claude Code:
# Get the diff
./diff_strings.sh zh-Hant.lproj/Localizable.strings > /tmp/missing_zh.txt
# Generate translations
claude "Below are English Localizable.strings entries from an iOS wallpaper app.
Please generate Traditional Chinese translations in Localizable.strings format.
The app is for browsing and downloading wallpapers. Keep the tone friendly and casual.
$(cat /tmp/missing_zh.txt)"Batching to around 50 keys at a time keeps quality consistent and avoids context overflow.
What I Didn't Expect: Context Quality Matters More Than Format
The bigger discovery wasn't the format of the input — it was how much an app description improved output quality.
Early on, I was passing strings files without context. "save_button_title" = "Save" is ambiguous: save what? In a wallpaper app, the right translation might lean toward "download" rather than "save" depending on the language and what's natural to the target audience. Claude Code was making reasonable guesses, but sometimes they didn't match the app's actual UI flow.
The fix was simple: a few lines in CLAUDE.md at the project root.
# App Context
This is an iOS wallpaper browsing and download app.
- Primary audience: teens to thirties, casual smartphone users
- Tone: friendly, informal — avoid formal honorifics
- Key features: wallpaper browse, category filter, image save, favorites
Claude Code reads CLAUDE.md at the start of each session. After adding this, translations came out with noticeably better contextual fit — no extra prompting needed for each session.
Quality Checks Before Shipping
After generating translations, I run a quick sanity check before adding them to the project:
# Check for empty values
grep '= ""' zh-Hant.lproj/Localizable.strings && echo "Empty values found" || echo "Clean"
# Compare key counts
SOURCE_COUNT=$(grep -c '"' en.lproj/Localizable.strings)
TARGET_COUNT=$(grep -c '"' zh-Hant.lproj/Localizable.strings)
echo "EN: $SOURCE_COUNT, ZH-HANT: $TARGET_COUNT"This catches obvious gaps, but it doesn't verify translation quality. For that, I build the app and walk through each language manually on a simulator. It's a step I don't skip — generating translations with Claude Code isn't the same as rubber-stamping them for production.
There's something from my background that shows up here. Both of my grandfathers were temple carpenters, and watching them work gave me an early sense that what's built carefully tends to last. That instinct carries over: generated output still gets a human pass before shipping.
Keeping the Backlog at Zero
Since building this workflow, I've stopped accumulating translation debt. The habit shift that made the difference: don't batch localization into a weekend task. When I add a new key to the English strings file, I run diff_strings.sh right then and queue the translations for Claude Code in the same session.
The next step I'm working toward is wiring this diff check into Xcode's build phase. A build warning when untranslated keys exist would make backlog accumulation visible before it grows.
If you want to try this, start with whichever language file is furthest behind. Run the diff script, see how many keys you're dealing with, and pass them to Claude Code in batches of 50. That's a workflow that scales from a single app to a multi-app library.