How it works
Whenmessage.to includes a locale, Courier replaces default property values with that locale’s translations. Elements with no matching locale definition keep their defaults.
The locale interface:
Text node resolution
Text elements accept two content formats:content (a plain or markdown string) and elements (a structured array of inline nodes like string, link, and img). Locales handle every combination:
If a locale translation is missing for an element, Courier falls back to the default property value. Notifications still render when translations are incomplete.
Why use locales
- Single template, multiple languages: One structure and logic, with translated text per locale
- Automatic selection: Courier uses the recipient’s locale from their profile or the
message.to.localefield - Fallback support: If a translation is missing, Courier uses the default content
Supported elements and properties
Which properties accept a locale override depends on the element type:content: Text content intext,action,quote, andhtmlelementstitle: Title inmetaelements (email subject lines, push notification titles)href: URLs inactionandimageelementssrc: Image source URLs inimageelementsraw: Provider-native channel overrides inchannelelements, such as a translatedsubjector an entire translatedhtmlbodyelements: Nested elements. The accepted shape varies by type:- inline nodes (string, link, img) in
textandlist-item - full nodes in
channel,group, andcolumn - column nodes in
columns - list-item nodes in
list
- inline nodes (string, link, img) in
divider, jsonnet, partial, and comment elements reject any locale override. Sending a property that an element type does not support returns a 400 naming the field.
Add translations
1
Add a locale in the designer
Open the template, add a locale (like
fr or pt-BR), and enter the translated content for each element. Courier stores per-locale content on the template, so the structure stays shared and only the text differs.2
Or set it via the API
Write a locale’s content with or the
put-locale CLI verb. Use this to keep translations in your own system and sync them in.3
Translate with AI
Courier’s AI translation generates a locale’s content from your base content, which you then review and edit. This is a console feature. Run it per template when you add or update a locale.AI translation needs the Business or Enterprise plan. Each translation costs 2.5 AI credits plus token overages, drawn from the same balance as . With no credits the console reports that translation requires them.
4
Send to a localized recipient
Set a test user’s
profile.locale to a locale you localized and send the template. Confirm the localized content renders in . Then send to a user without that locale and confirm the base content renders.Workspace translation strings
Workspace translations hold short strings reused across templates, like button labels and common phrases. They are keyed by locale and rendered with the{{t}} Handlebars helper. Manage them with and reference a string with {{t "welcome_headline"}}. Use this for a shared glossary, and per-locale template content for the body of a notification.
Three details decide how you integrate it:
- The payload is a
.pofile, not JSON. That is the gettext format most translation tooling already exports, so a vendor’s output usually needs no conversion. domainonly acceptsdefaulttoday. It exists for future namespacing, and any other value fails.- Read them back with , which returns the stored
.pocontent. Useful for diffing what is live against what your translation system holds.
Translations are per workspace, not per tenant. A cannot override a string, so a customer who needs different wording needs a different template rather than a different translation.
Basic example
Localize a text element:"fr", "Hello" becomes "Bonjour". When it is "es", it becomes "Hola". Any other locale, or none, renders the default "Hello".
Localizing multiple elements
Localize several element types in one template:- Meta element: Localizing the email subject line (
titleproperty) - Text element: Localizing body content with Handlebars variables
- Action element: Localizing both button text (
content) and URL (href)
Structured elements
When a text node uses theelements array (inline string, link, and img nodes), provide locale translations as elements arrays too. That preserves bold, italic, and inline links across languages.
Mixed locale formats
You can mixcontent and elements across locales in the same text node. Use it when some translations need formatting and others can be plain strings:
content string, which Courier wraps into a single text element at render time. The Spanish locale keeps the structured elements with an inline link.
When Both content and elements Are Present
If a locale entry includes both content and elements, only elements is used. Choose one format per locale entry. See the resolution table for all combinations.
Locale sources
Courier resolves the recipient’s locale in this order:message.to.locale: Set in thetoobject of the send request (highest priority)- User profile locale: Stored in the user’s profile via the
- Default fallback: If no locale is found, the default content is used
message.to.locale takes precedence over every other source. Courier merges it into the profile object during processing, so it overrides any locale stored in the user’s profile.Best practices
Use consistent locale codes
Use standard locale codes (en-US, es-ES, fr-FR) or simple language codes (en, es, fr) consistently across your templates. Courier supports any locale string format, but consistency makes maintenance easier.
Provide default content
Always provide default content for each element. Notifications then render even when:- A user’s locale isn’t supported
- A translation is missing
- The locale field is omitted
Localize URLs when needed
For action buttons and images, localize thehref and src properties to point at localized versions of your website or app:
Combine with channel customization
Combine locales with for localized, per-channel content:Test all locales
Before deploying, test your templates with every supported locale and check:- All translations are present
- Handlebars variables work correctly in all languages
- URLs and links are properly localized
- Text fits within UI constraints (button sizes, email widths, etc.)