A theme owns its own strings. Every piece of text a section renders through the t filter — and every editor-facing setting label — comes from locale files the theme ships in its bundle. The platform provides the mechanism (the files' contract, the t filter, the editor lookup); the theme author provides the strings. Platform locale files are never involved: your theme's text is yours to write and yours to translate.
The file layout#
locales/
en.json storefront strings — what {{ 'key' | t }} renders
en.schema.json editor labels — what the merchant sees on your settings
fr.json
fr.schema.json
de.json
de.schema.json
Two files per locale, both flat JSON objects of string → string.
{
"add_to_cart": "Add to cart",
"sold_out": "Sold out",
"greeting": "Welcome back, {{ name }}!"
}
Keys are yours to name (dots are allowed in keys — "cart.title" is one flat key, not nesting). Values are strings; a value may contain {{ placeholder }} slots filled by t's named arguments.
Resolution#
For a buyer whose locale is L, {{ 'key' | t }} resolves in exactly this order: 1) locales/L.json — the buyer's locale, if the theme ships it; 2) locales/<default_locale>.json — the theme's declared default; 3) neither has the key → the literal text "translation missing: L.key" renders. The marker is deliberate: a missing string is visible in preview and review instead of silently blank, and a shipped store never crashes over a translation. Ship your default-locale file complete; add other locales as you translate them — a locale you don't ship simply falls back to your default.
Merchant overrides#
Merchants can override the VALUE of any string from their admin without touching your theme — the platform stores those overrides separately and layers them over your files at render. Your files stay pristine (a theme update never fights a merchant's wording), and the KEYS are always yours: merchants can reword what you declared, never add or remove strings. If you remove a key in an update, any override of it simply stops applying.
Validation at import#
Rule |
|
|---|---|
1 |
Every file under locales/ must parse as flat string→string JSON. |
2 |
File names must be a valid locale + .json or .schema.json, directly under locales/. |
3 |
The default locale declared in theme.json should ship its files — that is the floor every buyer falls back to. |