◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-05-12Intermediate

Managing iOS Localizable.strings with Claude Code — A Solo Developer's Multilingual Workflow

How a solo developer managing multiple wallpaper apps used Claude Code to streamline iOS localization. From extracting missing keys to quality checks — a realistic workflow for one-person teams.

claude-code132iOS26localization4Localizable.stringsindie-dev14multilingual4

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.txt

Then 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.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⟐ Claude Code2026-05-27
11 Days in Crashlytics: A Claude Code Debug Loop Across Two Android Wallpaper Apps
After shipping Beautiful Wallpapers v2.0.0 and Ukiyo-e Wallpapers v1.7.0 in early May, Crashlytics and Play Console threw more than 30 new issues at me in 11 days. This is the operations log of how I drove the fix list down to v2.1.1 / v1.8.1 using Claude Code as a triage partner.
⟐ Claude Code2026-05-06
90 Days Building a Solo SaaS with Claude Code — And the Three Things I Got Wrong
A working log from 90 days of building a solo SaaS with Claude Code — design conversations, model routing, and Cloudflare Workers constraints. Revisited months later against primary docs, with three technical claims corrected in place.
⟐ Claude Code2026-10-12
After Moving to SPM, My App Died the Moment It Launched — Reading One dyld Line with Claude Code
Right after moving from CocoaPods to Swift Package Manager, the build passed but the app died on launch. Here is how I traced one dyld line to its cause, the fix, and how to verify it.