If your product is starting to win customers outside the UK, or your sales team keeps
asking “can we support French?”, i18n for web apps — short for internationalisation — is
the engineering work that decides how painful that becomes. This guide is for founders
and product leads who want to understand what i18n actually involves, where teams get it
wrong, and how to build it in from the start rather than bolting it on in a panic before
a big enterprise deal. You don’t need to be technical to follow it, but you should come
away able to ask your engineers the right questions.
i18n for web apps: internationalisation vs localisation
These terms get used interchangeably, which causes real confusion in planning
meetings. Internationalisation is the engineering work: structuring your
codebase so that it can be adapted to any language or region without rewriting
core logic. Localisation is the content and configuration work that
follows: the actual translations, currency formats, payment methods and cultural
adjustments for a specific market.
You do internationalisation once, properly, as part of how you build software.
You do localisation repeatedly, market by market, as you expand. Get the order
backwards — translating strings into a codebase that was never designed for it — and
every new language becomes a mini rebuild. That’s the trap this article is about
avoiding.
Build it in from day one, not bolted on later
The single biggest mistake is treating i18n as a feature to add once you “need” it.
By then, user-facing text is scattered through templates, concatenated into sentences,
and hardcoded into validation messages, emails and PDFs. Untangling that is slow and
risky work on a live product — the same pattern we see whenever a SaaS product avoids a costly rebuild
by making a structural decision early rather than retrofitting it under pressure.
Three habits prevent most of the pain:
- Externalise every user-facing string from day one. Even if you only
ship in English for the first year, strings should live in translation files or a
dedicated store, referenced by semantic keys, not typed inline in components. - Never build sentences with string concatenation. Different languages
reorder words, inflect nouns, and change verb position. Use ICU MessageFormat (the
standard behind libraries like FormatJS and react-intl) so a translator controls the
whole sentence, including plural and gender variants, instead of your code assembling
fragments. - Keep translation keys semantic, not positional. A key like
checkout.confirm_button survives a redesign; a key like button_12
doesn’t.
ICU’s plural syntax looks like {count, plural, one {# item} other {# items}}.
The formatting engine picks the right branch using the Unicode CLDR plural rules for
each locale — rules that vary far more than English’s simple “one vs many” split, which
is exactly why hand-rolled “if count == 1” logic breaks the moment you add a second
language.
Dates, numbers and currency: let the platform do the work
Never format dates, numbers or currency by hand. Every modern browser and server
runtime implements the JavaScript Intl API, which formats values correctly
for a given locale — day/month order, decimal versus comma separators, currency symbol
placement — without you maintaining lookup tables. The same applies to sorting text
alphabetically: locale-aware collation produces a different, correct order per language,
which a simple string sort does not.
This matters commercially too. Prices, invoices and transactional emails should
reflect the customer’s currency and format, not just a translated label next to a
British date format. Half-translated checkout flows are a common reason international
conversion lags behind a product’s domestic numbers.
Storage: Unicode and schema decisions you can’t easily undo
Internationalisation starts lower than the UI — it starts in your database. Text
columns need to store and round-trip any Unicode character without truncation or
corruption, which usually means UTF-8 encoding and a collation that matches it,
decided before you have real data in production. Retrofitting character encoding on a
live table is exactly the kind of migration we cover in our guide to database schema design that survives growth — it’s
possible, but it’s downtime and risk you can avoid by getting it right at the start.
Name fields deserve a specific mention: don’t assume a single “first name, last name”
structure, fixed-length fields, or Latin-only characters. Plenty of real customers have
names that don’t fit an Anglo-American form design.
Right-to-left languages are a layout problem, not a translation problem
If Arabic, Hebrew or Urdu are anywhere on your roadmap, right-to-left (RTL) support
needs to be a layout decision, not an afterthought. The modern approach is CSS logical
properties — margin-inline-start instead of margin-left,
padding-inline-end instead of padding-right — paired with the
HTML dir attribute. Written this way, the browser flips the whole layout
automatically; written with hardcoded left/right values, you end up maintaining two
parallel stylesheets indefinitely. It’s far cheaper to use logical properties from the
first line of CSS than to convert a large app later.
Testing i18n before you have translators
You can validate most of your internationalisation work before a single real
translation exists, using pseudo-localisation: a build variant that
replaces English strings with longer, accented, or expanded fake text. It catches three
things reliably — hardcoded strings that were never externalised, UI truncation and
overflow when text runs longer than English, and layout that silently assumes a fixed
string length. Wiring a pseudo-localisation check into your pipeline follows the same
logic as the automated gates we recommend in our piece on CI/CD for startups — catch the regression in a build, not in front of a customer in Tokyo or Berlin. It also pairs well with the
risk-based test planning described in our software testing strategy guide, since i18n defects are exactly the kind of issue that’s cheap to catch early and expensive to catch late.
Rolling out market by market
You rarely need to localise everything at once. A sensible sequence is:
- Internationalise the core product — externalised strings, Intl
formatting, Unicode storage — regardless of which markets you’re targeting yet. - Translate the core user journey for one new market to test demand:
sign-up, onboarding and the primary workflow, not the entire marketing site and help
centre. - Localise the commercial layer once the market proves worth the
investment — local payment methods, currency display, transactional emails, invoices,
and any region-specific compliance text. - Keep a human in the loop on customer-facing copy. Machine
translation is good enough for drafts; it isn’t good enough for pricing pages, legal
notices or anything that shapes trust.
This sequencing avoids the two expensive failure modes: translating a product that
was never structurally ready for it, and over-investing in full localisation for a
market that was never going to convert.
If you’re scoping a new build or weighing up whether your current codebase can support
this without a rewrite, our team can help you assess it — book a free consultation and we’ll
give you a straight answer.
FAQ
Do we need full internationalisation if we only sell in the UK right now?
Not fully, but it’s worth externalising strings and using Unicode storage and the
Intl API from the start. Those decisions cost almost nothing when a product
is new and are expensive to retrofit once it’s live with real data.
Is machine translation good enough for a first international launch?
It’s good enough to validate demand quickly, but review by a fluent human is essential
for anything that affects trust or money — pricing, legal text, error messages during
checkout.
What’s the biggest i18n mistake you see in existing codebases?
Strings concatenated together to build sentences, and dates or currency formatted by
hand instead of through the Intl API. Both work fine in English and break
as soon as a second language arrives.
Does internationalisation slow down a new build?
Done properly it adds very little time to a new build, because it’s mostly about how
you structure strings and data rather than extra features. It’s retrofitting i18n onto
an existing, English-only codebase that takes real effort.