1.2.1 · Dev Platform

How to Localize Your App — Externalize the Strings, Let AI Translate

2026.07.12~4 min

00Overview

01Story

Situation

As set out in 1.2, the first goal is two languages. The first thing to touch is the app's display strings.

Complication

But if strings are hard-coded into every screen, replacing them per language later becomes hell. You end up hunting for text across the codebase, touching every screen for every language you add.

Question

How do you structure display strings so localization stays cheap?

02Solution

Criteria

  • Display strings live in one place
  • AI can translate them wholesale
  • Adding a language later is easy

Answer

Pull the display strings out as "language files." Code references keys, not the strings themselves; the actual wording lives in per-language files. Translation then is just handing the file to AI: "translate this language file into X." You still do the final review, but AI finishes most of it.

Reason

Because when strings live in one place, translation and replacement happen once. Scattered through the code, even AI can't tell what to translate, and every added language means touching every screen. Collected into a language file, AI translates in one pass, and adding a language means adding one file.

What matters is not the translation technique.
It is implementing a localization-friendly structure from the start.

One pitfall, though. Hand AI only keys and values, and it can't tell what screen a word lives on or what it means — is "Home" a house, or the first screen? So attach a comment to each string: which screen, and what for. That context decides most of the translation quality.

From these two — predicting meaning from the two-language pass, and leaving meaning as context at development time — the accuracy jumps dramatically.

Options

  • Hard-code strings into screens — fastest at first, but come localization day you touch every screen. So, not taken.
  • Call a machine-translation API each time — weak on context, more mistranslations, and an ongoing cost. Having AI translate whole files with context wins on quality.

03Result

Good

Once the structure is right, two languages scale straight to five, or fifty. The steps stay the same; only the number of language files grows. The vast, unglamorous translating is now AI's job.

Bad

AI, too, mistranslates without context. Domain terms and wordplay need special care, and the final review can't be skipped. Preparing structure and context remains human work.

Follow-up

With the app localized, next comes the store listing that carries it into the world. Continued in 1.2.2 How to localize your store listing.

■ Reproduce it yourself

A note for anyone who wants to reproduce this setup. Not meant to be read straight through — the prompts show a few lines and scroll.

First, paste the following.

I want to localize this app, but the UI is still being built, so for now set up
the foundation in just two languages, Japanese and English.
(I'll expand to all languages later, so make the structure easy to grow then.)

[What to build]
1. Set up i18n-js (i18n.js)
   - detect the device language with expo-localization
   - fall back to English for unsupported languages (defaultLocale = 'en')
   - for now import only locales/ja.json and locales/en.json
   - leave a comment noting you can add languages just by adding imports here
2. Translation files
   - locales/ja.json (Japanese = master)
   - locales/en.json (English)
   - use snake_case keys that express meaning
   - use %{variable} form for interpolation
3. Replace the strings in the app code with i18n.t('key')
   - key every Japanese/English string currently hard-coded on screens

[Rules]
- treat ja.json as the source of truth; en.json is its counterpart
- make sure no hard-coded display strings remain
  (check that no raw string bypassing i18n.t() shows on screen)

When you're done, briefly summarize the procedure for adding a key when I add UI
text later (add to ja.json → add to en.json → reference via i18n.t).

Then, when it's time to localize, paste the following.

The UI is mostly fixed, so expand the localization to all languages.
Translate from ja.json as the master into each language.

[What to do]
1. Expand the UI strings (i18n) to 44 locales
   - target languages:
     zh, zh-TW, ko, es, fr, de, pt, it, nl, ar, ru, tr, hi, uk, he,
     id, ms, th, vi, da, sv, no, fi, pl, cs, sk, hu, ro, hr, el, ca,
     bn, gu, kn, ml, mr, or, pa, ta, te, ur, sl
     (= ja / en plus the above = 44)
   - provide locales/<lang>.json for each language. Follow ja.json's key
     structure exactly and translate only the values (no missing or extra keys)
   - line up the imports in i18n.js for every language
   - don't mix up Traditional (zh-TW / zh-HK etc.) and Simplified (zh)

2. Expand the app name (plist) to 50 locales
   - locales/plist/<lang>.json holds only CFBundleDisplayName
   - target 50 locales:
     ja, en-US, en-AU, en-CA, en-GB, zh-Hans, zh-Hant, ko, es-ES, es-MX,
     fr-FR, fr-CA, de-DE, it, pt-BR, pt-PT, nl-NL, ru, uk, tr, ar-SA, he,
     hi, id, ms, th, vi, da, sv, no, fi, pl, cs, sk, hu, ro, hr, el, ca,
     bn-BD, gu-IN, kn-IN, ml-IN, mr-IN, or-IN, pa-IN, ta-IN, te-IN, ur-PK,
     sl-SI

3. Expand the language settings in app.json
   - list the above locales in ios.infoPlist.CFBundleLocalizations
   - list the path to each plist/<lang>.json in the locales section

[Rules / notes]
- translate everything with ja.json / plist/ja.json as the source of truth
- keep %{variable} interpolation intact in every language (don't translate/break it)
- keep the fallback-to-English policy for unsupported languages
- prepare strings for RTL languages like Arabic and Hebrew too
  (point out any places that need RTL layout support)

When you're done, report whether the key counts match across all languages
(nothing missing or extra).