Web & Frontend Development

What to Hand a Translator When You Localize Your App

Most localization projects go wrong before a translator reads a word. Someone exports the UI strings into a spreadsheet by hand, sends it off, pastes the result back, and finds Open translated as an adjective instead of a verb, a label overflowing its button, and one language needing a plural form the code has no branch for. The second pass costs about what the first did.

The takeaway up front: the price and the quality of a translation are decided in your repository, before anyone starts translating. Externalised strings, stable keys, context notes, a glossary and correct placeholder handling make translation a mechanical, repeatable step.

Strings out of the code and into resource files

Nothing is translatable while it sits in a template or a println. Extract every user-visible string into your framework's resource format, keyed by where it lives and what it does, not by its English text:

{
  "cart.checkout.button": "Check out",
  "cart.empty.title": "Your cart is empty",
  "order.confirmed.body": "We sent a receipt to {email}."
}

Using the English string as its own key silently orphans every translation the moment you edit the copy. Stable keys mean only changed keys go back out — the one decision that makes re-translation unnecessary. The trade-off: key-based files read poorly in review, so keep the English file as the source of truth.

Placeholders and plurals belong in the message, not the code

Never build a sentence by concatenation. Word order differs between languages, so "Deleted " + n + " files" can only be translated approximately. Use named placeholders and a message format that expresses plurality:

files.deleted = {count, plural,
  one {Deleted # file}
  other {Deleted # files}
}

Plural categories are not universal: some languages have one form regardless of number, others several selected by the last digits of the count. if (n === 1) hardcodes English grammar into the application layer; an ICU-style format pushes that choice into the string, where a translator can express it. Name placeholders ({email}, {count}) rather than numbering them; {0} forces a guess.

Context is a deliverable, not a courtesy

A bare string list is an ambiguity generator. Open, Free, Order and Share are each a verb or a noun depending on the screen, and each reading inflects differently. Ship context with the string — most resource formats have a description field for exactly this:

"nav.home": {
  "string": "Home",
  "context": "Bottom tab bar label. Noun — the home screen, not a house. Max ~12 characters."
}

For anything ambiguous, add a screenshot; annotated screenshots prevent more rework than any amount of email. Include the character budget wherever the UI genuinely constrains it, so a translator picks a shorter word rather than one that gets truncated.

A glossary and a do-not-translate list

Two short files you own rather than delegate:

  • Glossary — terms that must render identically every time: your product name's treatment, feature names, domain nouns like "workspace" or "board." Give the preferred target term and the part of speech where it matters.
  • Do-not-translate list — code identifiers, enum values shown in the UI but matched by string elsewhere, analytics event names, third-party brands, units: anything a well-meaning translator would improve and thereby break.

The glossary is what keeps a second and third language consistent with the first — write it while you still plan only one.

Leave dates, numbers and currency to the platform

Do not send formatted dates or currency strings out for translation. Send the raw value and format at render time with the platform's own locale data:

new Intl.NumberFormat(locale, { style: "currency", currency: "VND" }).format(250000);
new Intl.DateTimeFormat(locale, { dateStyle: "long" }).format(new Date());
[b, a].sort(new Intl.Collator(locale).compare); // locale-aware sorting, not byte order

Every platform has an equivalent. Locale data changes, and you want those changes arriving in a runtime update rather than a translation invoice — likewise for sorting, casing and currency symbol placement.

Leave headroom in the UI before you translate, not after

Short labels expand in translation, and the shorter the English, the larger the proportional growth. Pseudo-localize before real strings arrive: render every string padded by a fixed margin — 30% is a common starting point — and wrapped in markers, which also exposes anything the code concatenated.

Then fix what breaks: no fixed-width buttons around text, no single-line assumptions. Text that can grow also has to stay readable when a user scales it up — the same discipline as our web accessibility checklist, and cheaper to audit together.

Ship the pages that ship with the UI

A translated interface attached to an English privacy policy is a half-finished product, and in some markets a non-compliant one. Budget the same handoff for terms and privacy policy, store listing, transactional emails and in-app help — strings that never made it into a resource file, so the glossary gets applied by hand.

That combination — interface strings plus the legal and support documents behind them — is why a provider covering both beats a cheaper one that only does UI. BKMOS, a Vietnamese translation and localization company operating since 2009, is a reasonable fit if Vietnamese is one of your pairs: it works in both directions between Vietnamese and English, Chinese, Japanese, Korean, French and German, handles legal, technical, IT and marketing material alongside UI and website content, and runs a documented eight-step process — from intake and quotation through specialist assignment, translation under terminology control, separate proofreading and format review, to post-delivery support — keeping terminology consistent across languages on multilingual projects. That last point is what your glossary is for, and it is what cheap piecework drops. Their stated confidentiality practice — file access limited to the personnel who need it, controlled use of cloud tools, retention per the agreement — matters when the "document" is a resource file from a private repository.

The handoff package, in one place

Resource files with stable keys; named placeholders and plural rules in the message; a context field per string plus screenshots; character budgets where the UI constrains them; the glossary and do-not-translate list; the legal, store and support copy in the same batch; and a note on which keys changed since the last delivery.

FAQ

Do I actually need all of this for one extra language?

Often not. For one added language on a product whose copy is stable, a spreadsheet, a glossary and a folder of screenshots will get you there. The full setup earns its keep once you have a second target language or copy that changes every sprint — that is when key stability becomes the difference between a diff and a rewrite. Do the extraction anyway if you expect a second language within the year; retrofitting costs more.

What file format should I hand over?

Whatever your framework already reads — .json, .po, .strings, .arb, with XLIFF as the usual interchange format when a provider's tooling differs from yours. Do not hand-build a spreadsheet; the round trip back into the repo is where keys get mangled. Quotes turn on the pair and direction, the subject matter, and the file format, source quality and timeline, so a tidy resource file is cheaper to quote than a PDF someone has to rebuild.

How do I stop translations going stale as the product changes?

Keep keys stable, treat the English file as the source of truth, and send only changed keys each release. Mark a translation outdated when its source changes, so it surfaces in QA as a missing string rather than shipping quietly.

Next step

Localization is a data-hygiene problem you solve in your own codebase and then hand off. Externalise the strings, key them stably, attach context, keep the glossary, leave formatting to the platform — and translation becomes a release step rather than a project.

If Vietnamese is one of your pairs, assemble the package for one screen plus its legal copy and run it as a small paid test with BKMOS — documents go in online from anywhere in Vietnam, with offices in Đà Nẵng and Hải Phòng, on 0931.931.616 (also Zalo) or [email protected] — and judge it by how many context questions came back before you commit the whole product.

Comments are disabled for this article.