Documentation Index
Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
Use this file to discover all available pages before exploring further.
Locales
Every user-facing string in a LaunchMyStore theme — button labels, error messages, ARIA copy, schema names shown in the editor — lives in thelocales/ directory and is translatable across any number of languages.
This page covers:
- The file layout under
locales/. - The
{{ 'key' | t }}filter for translating storefront copy. - The
t:prefix for translating schema labels. - How translations are resolved at install time and at render time.
- How the active locale is selected per request.
- Translation for navigation menus (
linklists).
File layout
A theme’slocales/ directory holds two parallel families of files:
| Suffix | Content |
|---|---|
<lang>.json | Storefront copy — every string emitted via {{ 'key' | t }}. |
<lang>.schema.json | Schema labels — every "label", "info", "name", and "default" referenced via "t:..." keys in section/block schemas. |
| Marker | Meaning |
|---|---|
.default | Exactly one locale per theme is marked default (typically en.default.json + en.default.schema.json). Missing keys in other locales fall back to the default. |
(no .default) | Regular locale file. Used when the request’s active locale matches. |
en.default.json (or equivalent default-marked
file). It should have an en.default.schema.json for schema labels.
Other locales are optional and inherited from the default whenever a key is
missing.
Translating storefront copy: the t filter
In templates, snippets, sections, and blocks, translate a string with the
t filter:
Add to cart, Search the store, Search input, and Sold out respectively.
Interpolation
A translation value can include{{ name }} placeholders that the t
filter fills in from keyword arguments:
Pluralisation
Provide an object withone / other (CLDR-compliant) keys; pass the
count argument and the filter selects the right form:
count: 1 → 1 filter applied. count: 5 → 5 filters applied. Other
CLDR forms (zero, two, few, many) are supported for languages that
need them.
Fallback behavior
If the key doesn’t exist in the active locale, the renderer:- Looks in
<lang>.json. - Falls back to
<default>.default.json. - Renders the key string itself (
general.cart.add_to_cart) if still nothing found — visible but not crashed.
Translating schema labels: the t: prefix
Section and block schemas use a different syntax: any string value in the
schema can be replaced with "t:..." and the platform resolves it to the
corresponding translation.
"t:..." value can appear anywhere in the schema: name, label,
info, default, options[].label, blocks[].name, presets[].name,
paragraph content, header content. The resolver walks the whole schema
recursively and replaces every t:-prefixed string.
Resolution order: schema vs storefront
When resolving a"t:..." key, the renderer first looks in
<lang>.schema.json, then falls back to <lang>.json. This lets you keep
editor-only strings (section names, preset descriptions) separate from
storefront copy without duplicating them.
"t:names.featured_collection" schema label resolves out of the schema
file; a {{ 'products.product.sold_out' | t }} template call resolves out
of the storefront file.
Resolution lifecycle
Schemat: keys are resolved in two places:
1. At install time
When the theme is uploaded, the installer parses every section and block schema, runsexpandSchemaTranslations() over each one, and writes the
resolved values into config/schema-index.json. This is the index the
theme editor reads to populate its UI — the merchant sees real labels, not
t: keys.
The installer also flattens the active default locale into the index so
later lookups don’t have to re-parse the locale JSON.
For app extensions that ship their own blocks (storefront app blocks),
expandSchemaTranslationsForApp() performs the same expansion against the
app’s bundled locales/<lang>.json file. See
src/pages/api/apps/_localManifests.js for the implementation —
specifically expandSchemaTranslations() and the helper
expandSchemaTranslationsForApp(schema, domainSlug, appHandle, locale).
2. At render time
During a page render, the renderer readslocales/<lang>.schema.json and
locales/<lang>.json via AssetLoader.readJSON() (cached), merges them,
and caches the merged result keyed by theme directory in
AssetLoader.schemaTranslationsCache (12-hour TTL, invalidated on theme
republish). Any t: key still present in a schema is resolved against
this merged map.
The merge is { ...mainTranslations, ...schemaTranslations } —
schema-specific keys take precedence over storefront copy if there’s a
namespace collision.
A
t: key that can’t be resolved at install or render time will be
displayed in the editor as the literal key string. That’s the symptom to
watch for: if you see t:settings.heading.label in the theme editor’s
sidebar, it means a key is missing from your .schema.json files.Locale detection
The renderer picks the active locale for a request from the following sources, in order. The first match wins.| Source | Notes |
|---|---|
?locale=<code> query | Used by the theme editor and section-AJAX fetches. |
locale_code cookie | Set on first visit by middleware based on auto-detection. |
Accept-Language header | Falls back to the primary language tag (e.g. fr-CA → fr). |
| Theme default | The locale whose filename ends in .default.json. |
Auto-detection on first visit
For a brand-new visitor, middleware reads thecf-ipcountry header
(populated by Cloudflare’s edge), maps it to a default language, and
combines it with the user’s Accept-Language header. The result is
written to three cookies:
| Cookie | Holds |
|---|---|
country_code | Two-letter ISO country code (e.g. "US", "AE"). |
currency_code | ISO-4217 currency code (e.g. "USD", "AED"). |
locale_code | Two-letter language code (e.g. "en", "ar"). |
Exposing the locale in templates
The active locale is exposed viarequest.locale:
locale field, the
form_locale filter or a direct render of request.locale.iso_code will
both work.
Available locales
A theme’s available locales are inferred from the filenames present inlocales/. Use them to render a language switcher:
localization global is populated from the merchant’s enabled markets
and the locales the theme ships. See
localization for the full shape.
Navigation menu translations
Storefront navigation menus (linklists.main-menu, linklists.footer-menu,
etc.) are owned by the merchant — they’re not part of the theme bundle.
Translations are managed in the admin Navigation editor, not in
locales/ files.
In templates, render a linklist as you normally would:
link.title and link.url are returned in the request’s active locale —
the backend resolves translations server-side based on request.locale
before handing the linklist to the renderer. Theme code never has to call
| t on a linklist’s title.
If you ship a fallback menu that the merchant might not have configured,
guard with default_menu:
Worked example: a full set of locale files
A small theme that ships English (default) and French.locales/en.default.json
locales/en.default.schema.json
locales/fr.json
locales/fr.schema.json
fr, both storefront templates and editor
labels render in French. Any key missing from fr.json or
fr.schema.json falls back to en.default.json / en.default.schema.json
respectively.
Tips and gotchas
- One default per theme: only one file may end in
.default.json. Marking two locales as default is undefined behaviour and the installer may pick either. - JSON, with comments: locale files are parsed by the platform’s safe
JSON parser, which accepts
//-style comments. Use them generously — they’re stripped at parse time and don’t ship to the storefront. - Don’t translate IDs:
id,type, andvaluefields in schemas are identifiers, not labels. Thet:prefix is only meaningful on display strings. - Refresh after editing: when you change a
.schema.jsonfile, republish the theme (or callAssetLoader.invalidateTheme(themeId)) to rebuild the merged schema-translations cache. The editor pulls from the bakedschema-index.json, which is regenerated on install. - App-block translations: third-party app blocks ship their own
locales/<lang>.jsoninside the app extension. Theirt:keys are resolved byexpandSchemaTranslationsForApp()against that file, not the theme’s locales.
Next steps
Theme structure
Where
locales/ fits in the overall theme directory.Filters
The
t, format_address, and money filters for I18n.Schema
Where
t: keys can appear in a section schema.Objects
The
request, localization, and linklists globals.