Speaking your language: one localisation layer for every Existence app.
Every word our apps showed was once hardcoded in English, tied to the screen that displayed it. We pulled all of it into a single shared layer, so reaching a new audience in their own language is finally a content change, not a rebuild.
Until recently, every word our apps showed you was written once, in English, and nailed to the spot. A button label, a settings heading, a legal disclaimer, each lived as a plain string sitting inside the screen that displayed it. That is the most natural way to build the first version of anything, and it works right up until the moment you want the same app to greet someone in a second language. This month we pulled every one of those strings out of the screens and moved them into a single place: a central localisation layer that every Existence app now shares.
The quiet cost of hardcoded text
Hardcoded copy is deceptively comfortable. The text is right there next to the widget that shows it, easy to read and easy to change. The trouble is that it ties the words to the wiring. A screen that knows how to say “Account” in English is a screen that has to be rewritten to say it in any other language, and there is nowhere to add a translation without editing the logic around it. Multiply that across every app we ship and every screen inside them, and translating the product stops being a content task and becomes an engineering project, the kind nobody wants to start.
That is exactly the tax e_core exists to remove. If more than one app needs it, it belongs in a package: built once, tested once, owned once, and used everywhere. Localisation is a textbook case. Every app has text; every app will eventually have users who would rather read it in their own language. So we built the layer once.
The goal was never to translate a screen. It was to make sure no screen ever has to be rebuilt to change what language it speaks.
What we shipped
The first release gives every adopting app a single, shared source of truth for the words it shows. The strings that used to be scattered across the interface now live in structured translation files, and the app reads from them through generated, type-safe accessors rather than raw text. In practice that means:
- One place for every string: all display text lives in dedicated translation files, organised and named so it is obvious what each entry is for.
- Type-safe access: screens ask for a string by a generated key, so a missing or misspelled entry is caught while building, not by a user staring at a blank label.
- Region awareness: the app knows which locales it supports and can resolve the right one, laying the groundwork for the Language & Region controls in settings to actually change something.
- A clean seam: the logic that decides what to show is now separated from the words themselves, so adding a language touches translation files, not app behaviour.
Why one layer instead of one per app
We could have added a translation file to each app and called it done. It would have looked like progress. But it would have recreated the original problem one level up: every app owning its own slightly different way of naming, loading, and resolving strings, and every improvement to that machinery needing to be made several times over. A shared layer means the hard parts (how strings are keyed, how a locale is chosen, how a translation is generated into code) are solved in a single place. When we improve how we handle a fallback for a missing translation, every app inherits it for free.
There is a discipline to this that pays off quietly. Because keys are namespaced and generated, two apps can never quietly disagree about what a string is called. Because the words are separated from the logic, a translator or a product writer can improve the copy without going anywhere near the code that renders it. The interface stops being a place where English happens to be hardcoded and becomes a surface that renders whatever language it is handed.
Built for the second language, not just the first
The honest measure of this work is not that the app still shows English, it always did. It is that the second language is now a content change instead of a rebuild. Adding a locale means supplying a translation file and letting the generated accessors do the rest; the screens do not need to know it happened. That is the difference between localisation being a feature we support and localisation being a project we dread.
This is a foundation piece, and we are treating it like one. There is more to layer on top: wiring the in-app Language & Region controls fully through to the running interface, expanding the set of translated strings as new surfaces adopt the package, and decoupling the pieces further as more apps come on board. But the shape is set, and it is the shape we wanted: text lives in one place, screens ask for it by name, and reaching a new audience in their own language is finally the small, safe change it always should have been.