The localization I skipped

J
John Doe
|
Jan 15, 2024
|
5 min read
#react
#javascript
#frontend

I have a standard operating procedure for new products, and near the top of it is one rule I have never regretted following and just spent weeks regretting breaking. Build localization on day one. Not after the first market, not when the first non-English customer asks. Day one, alongside identity and permissions, before there is a single string in the database to convert.

This time I skipped it. Giant Context launched into the world speaking only English, because I was heads-down on proving the thing worked at all and localization felt like a problem for later. Later arrived, and the realization with it. A product built on an LLM has to get language right, and get it global.

Why a platform is always multilingual

The question of whether to localize has an easy answer for a platform, and the answer is always yes. A single app for a single market can sometimes get away with one language. A platform cannot, because a platform's whole point is to scale past the room it started in. Giant Context builds websites for businesses, and businesses have customers who do not all read English. The moment a French bakery or a Mexican firm wants a site, the product either speaks their language or it does not serve them. I also live in a bilingual country, where serving one language serves half of it.

On anything meant to scale beyond its immediate market, localization is a certainty deferred rather than a feature request that might come, and deferring a certainty is just choosing to pay more for it later. That is why it sits in my standard operating procedure next to role-based access control. Both are foundational, both are miserable to retrofit, and both should shape the schema before the schema has any data in it.

The shape of the tax

Doing it late hurts for a specific reason. Localization is not code you add beside your app. It is a change to the type of your data.

Localization is not code you add beside your app. It is a change to the type of your data.

A localized field is a different kind of thing, an object holding one value per language.

A localized field

heading: {  en: "Book a table",  fr: "Réserver une table",  es: "Reservar una mesa"}

Every user-facing field in the product has to make that jump, from string to object. And when you retrofit, you are not making that change in one place. You are making it in the database schema, in the API contracts that describe every field, in the generated code that materializes those contracts, in the frontend that renders every field, in the backend that reads them, and in all the data that already exists as bare strings and now has to be migrated. One conceptual decision, made too late, becomes a change that touches every layer at once.

Had I made it on day one, none of that would have been a migration. The fields would have been objects from the first commit, the generator would have known it, the frontend would have rendered them that way from the start, and there would have been no old string data to convert because there would never have been any string data. The few days it costs at the beginning buys you out of a change that otherwise spreads across the entire codebase.

How it works once it is in

The retrofit was a pain, but the system it left behind is clean, and it runs on the same machinery as everything else here.

Translation reaches across the whole stack. The frontend translates its own interface, the buttons and labels and menus. The backend translates the content it serves, the actual localized fields on a customer's pages. One approach, both sides of the wire.

The generator does the tedious half. Every package declares its own translation strings, and a generation step merges them into the locale files the application loads, so adding a translatable string is a declaration, not a hunt through a central file that everyone edits and no one owns. The app ships one merged locale file per language, but rather than maintaining one enormous file, I store locales/{lang} beside the code in each component, so translations stay in their own domain instead of an unruly central config.

How this translated to the product

Localizing the system meant covering two surfaces at once. The business software, both its interface and its API, and the final product, the customers' websites, socials, emails and everything else the platform generates. Building it into the business software made the entire platform multilingual.

Tied into AI, the customer's own content gets translated on save. A customer writes their homepage in English, saves, and the system generates the French and Spanish versions of that content automatically, filling the other languages of every localized field. They author once. The platform speaks the rest.

The same page, English and French, from one set of localized fields.

The part that keeps it honest

A localized system has a failure mode that ordinary code does not. It can be half-translated. A field can have its English value and be silently missing its French one, and nobody notices until a French visitor lands on a page with an English sentence sitting in the middle of it, which reads as broken to exactly the customer localization was supposed to win.

So translation completeness is tested, the same way everything here is tested. A check runs across the codebase, finds every string that is supposed to be translated, compares it against the generated locale files, and reports anything missing in any language and any place the languages have fallen out of parity. If a string exists in English and not in French, the check says so before the code ships, not after a customer finds the gap.

That test is the reason I can trust the feature. Localization is not one decision you make and forget. It is an obligation you have to meet on every new string forever, and the only way to meet an obligation that big is to have a machine check it on every push.

What I would tell myself in December

Build localization on day one, with identity and permissions, as a decision about the shape of your data rather than a feature you will get to. A platform is meant to scale, and it cannot scale without language at the core of it.

Have questions?

If you're interested in my work or Giant Context, contact me!

Contact Jesse
Legal
Privacy Policy
Terms of Service
Cookie Policy

© 2026 Giant Context. All rights reserved.

The localization I skipped