HomeGuides → Translation missing

"Translation missing" in a theme app extension

Your app block renders Translation missing: en.Find my size on the storefront — with your own text sitting after the locale prefix. That detail tells you exactly what's wrong, and the fix is one line of Liquid.

From shipping a production app block · August 2026 · by SPELL

What the error is actually telling you

Liquid's t filter outputs Translation missing: <locale>.<key> when it can't resolve a key. So if you're seeing your own English sentence after the en., then that sentence was passed to t as a translation key.

Which means somewhere you wrote this:

{{ block.settings.button_label | t }}   {# WRONG — merchant text is data, not a key #}

The rule

Never pipe a merchant-editable setting through | t. A settings value is content the merchant typed. The t filter is for keys you defined in your locale files. The moment a merchant types anything, the lookup fails and their own words get echoed back inside an error string.

The fix that keeps every language working

You want a default that's translated in all your locales, but which a merchant can override in their own words. Leave the setting's default blank, and fall back to a real translation key when it's empty:

{%- liquid
  assign label = block.settings.button_label | strip
  if label == blank
    assign label = 'sizecast.button' | t
  endif
-%}
<button type="button">{{ label }}</button>

With the schema leaving the default empty:

{
  "type": "text",
  "id": "button_label",
  "label": "t:settings.button_label",
  "info": "Leave empty to use the translated default."
}

Now every storefront language gets its own translation for free, and a merchant override wins everywhere — with no error string possible in either direction.

Why "default": "t:..." doesn't save you

In theme sections, translatable defaults in the schema work. In theme app extensions they do not resolve — the editor shows a translation-missing value even when the identical key works correctly for a label. This has been reported since app blocks launched and is still the behaviour.

So: t: is for editor-facing strings only (name, label, info, select options), resolved from your *.schema.json. Storefront strings use | t from *.json. Defaults are either a plain string or blank.

The two locale files, and which is which

FileAudienceConsumed by
locales/en.default.jsonYour customers{{ 'key' | t }} in block Liquid, at storefront render
locales/en.default.schema.jsonThe merchant"t:..." references inside {% schema %}

Ship both from day one — the CLI will only let you add a storefront locale file if a schema locale file already exists. Exactly one .default per type, and the | t lookup is scoped to your extension: the theme's locale files are invisible to your block, and yours are invisible to the theme.

Related: the real Built for Shopify requirements, and five metafield and metaobject traps that fail silently.

This came out of shipping a real app

We build theme app extensions that render in pure Liquid on the storefront and localise properly — including Arabic with full RTL, where most extensions quietly break.

Book a call →