CLAUDE LABJP
VERSION — v2.1.227 landed on August 10 with no new features, just fixes around plan detection and CI behaviorAUTO — Two days remain until August 14, when auto mode becomes the default in Claude Code for Pro, Max, and TeamCI — Bash commands no longer fail across the board under claude-code-action with allowed_non_write_users on GitHub-hosted runnersBILLING — Sessions started with an expired token could misread your plan and nudge Max users toward usage credits; that is now fixedSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, five days outPRICE — Sonnet 5 promo pricing at $2/$10 per Mtok runs through August 31, moving to $3/$15 on September 1VERSION — v2.1.227 landed on August 10 with no new features, just fixes around plan detection and CI behaviorAUTO — Two days remain until August 14, when auto mode becomes the default in Claude Code for Pro, Max, and TeamCI — Bash commands no longer fail across the board under claude-code-action with allowed_non_write_users on GitHub-hosted runnersBILLING — Sessions started with an expired token could misread your plan and nudge Max users toward usage credits; that is now fixedSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, five days outPRICE — Sonnet 5 promo pricing at $2/$10 per Mtok runs through August 31, moving to $3/$15 on September 1
Articles/API & SDK
API & SDK/2026-06-17Advanced

Stop Terminology Drift in Localized Apps: A Consistent Localizable.strings Pipeline with the Batch API and a Cached Glossary

Translating UI strings one at a time invites inconsistency. Pair Claude's Message Batches API with a prompt-cached glossary to translate Localizable.strings across 10+ languages consistently, with measured costs and the pitfalls I hit in production.

api-sdk13batch-api3prompt-caching13localization3ios14

Premium Article

I learned this the hard way through an App Store rejection. The iOS wallpaper app I run as an indie developer supports more than ten languages, and on the settings screen I had translated "壁紙" as Wallpaper, but in the widget hint it had come out as Background. The cause was mundane: every time I added a string, I translated just that one string. Translate them one at a time and each one is done at a different moment, in a different context, so the same concept ends up with different words.

This kind of drift is less a translation-quality problem than a translation-procedure problem. Hire a human translator and the same thing happens if you never hand them a glossary. So the approach I settled on was to let Claude cache a glossary and a style guide, then translate every entry in Localizable.strings in one pass through the Message Batches API. This article walks through that design and implementation, the cost I actually measured when I pushed thousands of strings through it, and the pitfalls I hit when writing the results back.

Why I stopped translating one string at a time

A Localizable.strings file is just a flat list of key-value pairs:

"settings.wallpaper.title" = "壁紙の品質";
"widget.hint.background" = "ウィジェットに壁紙を設定";
"paywall.cta.primary" = "プレミアムを始める";

The trouble is that translating these in separate requests gives Claude (or a person) the freedom to render "壁紙" as Wallpaper here and Background there. UI consistency is not the sum of individually correct translations. The constraint "how do we unify the word wallpaper across the whole app" has to be fixed at the input stage of translation, not patched up afterward.

I chose to express that constraint in three layers. First, a glossary that declares "this source term must always map to this target term." Second, a style guide that sets tone — buttons in the imperative, settings titles as noun phrases. Third, an explicit placeholder-protection rule: never move %@ or %1$d. All three layers are identical for every string, so resending them with each request is pure waste. That is exactly where prompt caching earns its keep.

Turning the glossary and style guide into a cacheable system prompt

Prompt caching lets you attach cache_control to a system block so the prefix up to that point is cached. A glossary can run to hundreds of lines, but once it is cached, later requests reuse it at cache-read pricing — far cheaper than ordinary input tokens. Even with thousands of strings to translate, the glossary is read at full cost exactly once and then referenced repeatedly at the discounted rate.

# glossary.py — build the glossary and style guide as a cacheable system block
import anthropic
 
client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from the environment
 
# glossary: source term -> fixed translation per language; the app must always use these
GLOSSARY = {
    "壁紙": {"en": "Wallpaper", "fr": "Fond d'écran", "de": "Hintergrundbild"},
    "プレミアム": {"en": "Premium", "fr": "Premium", "de": "Premium"},
    "広告を非表示": {"en": "Remove Ads", "fr": "Supprimer les pubs", "de": "Werbung entfernen"},
}
 
STYLE_GUIDE = """\
- Buttons and CTAs: imperative and short (e.g. Remove Ads, Start Premium).
- Settings titles: noun phrases (e.g. Wallpaper Quality).
- Match politeness level to each language's conventions; do not over-formalize.
- Never alter format specifiers like %@ %1$@ %d %1$d, including their order.
- Preserve \\n and \\t exactly as they appear.
"""
 
def build_cached_system(target_lang: str) -> list:
    glossary_lines = "\n".join(
        f'  "{src}" => "{langs.get(target_lang, "")}"'
        for src, langs in GLOSSARY.items()
        if langs.get(target_lang)
    )
    instructions = (
        f"You translate app UI strings from Japanese into {target_lang}.\n\n"
        f"## Glossary (always use these target terms)\n{glossary_lines}\n\n"
        f"## Style guide\n{STYLE_GUIDE}"
    )
    # the prefix of a block carrying cache_control becomes cacheable
    return [{
        "type": "text",
        "text": instructions,
        "cache_control": {"type": "ephemeral"},
    }]

The key idea is to keep the glossary and style guide as a fixed, unchanging preamble that is separate from the strings being translated. By placing the variable part — the actual strings — in messages rather than system, the system block keeps hitting the cache. Rewrite system per string and the cache misses every time. I failed to honor that separation at first and spent a frustrating afternoon convinced "caching doesn't work" while my hit rate sat at zero.

The pricing dynamics of Message Batches and prompt caching move quickly, so I build my cost reasoning on top of async cost design for the Batch API and halving your monthly bill with prompt caching.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
If your translations drift from screen to screen, you'll learn to cache a glossary that forces the same term everywhere across every string
You can decide how far the Batch API's 50% discount stacked with prompt caching actually cuts the cost of thousands of strings, from measured numbers
You'll get a validated pipeline that writes translations back into Localizable.strings without breaking %@, plural variants, or newline codes
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
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 →

Related Articles

API & SDK2026-06-26
Generating localized App Store listing metadata within character limits using the Claude API
Raw translations overflow App Store Connect's character limits. Generate per-locale listing metadata within them using Claude API structured output and a repair loop.
API & SDK2026-05-15
Cutting Claude API Costs in Half with Messages Batches API — Design Patterns from an Indie Developer
How to reduce Claude API costs by up to 50% using the Messages Batches API. Includes async design patterns, real cost calculations, and production-ready error handling from an indie developer who runs four AI blogs on autopilot.
API & SDK2026-03-26
Claude API Cost Optimization Production Guide — Combining Batch API, Prompt Caching, and Adaptive Thinking for Up to 90% Savings
Learn practical implementation patterns to cut Claude API costs by up to 90%. Covers Batch API, Prompt Caching, and Adaptive Thinking strategies, plus production monitoring and budget management.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →