Skip to main content

Why Use Locales?

Elemental’s locale system lets you create a single template that automatically adapts to your users’ preferred languages. Instead of maintaining separate templates for each language, define translations once and Courier handles the rest.
  • Single template, multiple languages: One template structure with translations for all supported locales
  • Automatic selection: Courier uses the recipient’s locale from their profile or the message.to.locale field
  • Fallback support: If a translation is missing, Courier uses the default content
  • Consistent structure: Keep the same layout and logic across all languages

Supported Elements and Properties

Locales can be applied to any Elemental element that has localizable properties. The following properties support localization:
  • content: Text content in text, action, quote, and html elements
  • title: Title in meta elements (email subject lines, push notification titles)
  • href: URLs in action and image elements
  • src: Image source URLs in image elements
  • elements: Nested elements in channel and group elements
  • template: Template strings in jsonnet elements

How Locales Work

When you specify a locale in the message.to field, Courier automatically replaces the default property values with locale-specific translations for each element that has a matching locale definition. The locale interface follows this structure:
interface Locales {
  [locale: string]: {
    content?: string;    // For text, action, quote, html elements
    title?: string;      // For meta elements
    href?: string;       // For action, image elements
    src?: string;        // For image elements
    elements?: Element[]; // For channel, group elements
    template?: string;   // For jsonnet elements
  };
}
If a locale translation is missing for a specific element, Courier falls back to the default property value. This ensures your notifications always render, even if some translations are incomplete.

Basic Example

Here’s a simple example showing how to localize a text element:
curl -X POST https://api.courier.com/send \
  -H "Authorization: Bearer $COURIER_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": {
      "to": {
        "email": "[email protected]",
        "locale": "fr"
      },
      "content": {
        "version": "2022-01-01",
        "elements": [
          {
            "type": "text",
            "content": "Hello",
            "locales": {
              "fr": {
                "content": "Bonjour"
              },
              "es": {
                "content": "Hola"
              }
            }
          }
        ]
      }
    }
  }'
In this example, when the recipient’s locale is "fr", the text "Hello" is replaced with "Bonjour". If the locale is "es", it becomes "Hola". For any other locale (or if no locale is specified), the default "Hello" is used.

Localizing Multiple Elements

You can localize multiple elements in a single template. Here’s a comprehensive example showing localization for different element types:
curl -X POST https://api.courier.com/send \
  -H "Authorization: Bearer $COURIER_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": {
      "to": {
        "email": "[email protected]",
        "locale": "es"
      },
      "content": {
        "version": "2022-01-01",
        "elements": [
          {
            "type": "meta",
            "title": "Welcome to our platform",
            "locales": {
              "es": {
                "title": "Bienvenido a nuestra plataforma"
              },
              "fr": {
                "title": "Bienvenue sur notre plateforme"
              }
            }
          },
          {
            "type": "text",
            "content": "Thanks for signing up, {{user_name}}!",
            "locales": {
              "es": {
                "content": "¡Gracias por registrarte, {{user_name}}!"
              },
              "fr": {
                "content": "Merci de vous être inscrit, {{user_name}} !"
              }
            }
          },
          {
            "type": "action",
            "content": "Get Started",
            "href": "https://app.example.com/dashboard",
            "locales": {
              "es": {
                "content": "Comenzar",
                "href": "https://app.example.com/es/dashboard"
              },
              "fr": {
                "content": "Commencer",
                "href": "https://app.example.com/fr/dashboard"
              }
            }
          }
        ]
      }
    }
  }'
This example demonstrates:
  • Meta element: Localizing the email subject line (title property)
  • Text element: Localizing body content with Handlebars variables
  • Action element: Localizing both button text (content) and URL (href)

Locale Sources

Courier determines the recipient’s locale from the following sources, in order of precedence:
  1. message.to.locale: Explicitly set in the to object of the send request (highest priority)
  2. profile.locale: Set in the top-level profile object of the send request
  3. User profile locale: Stored in the user’s profile via the Profiles API
  4. Default fallback: If no locale is found, the default content is used
When message.to.locale is provided, it takes precedence over all other locale sources. The locale from message.to is merged into the profile object during processing, so it will override any locale stored in the user’s profile or passed in the top-level profile object.
You can specify the locale in multiple ways:
# Option 1: In message.to.locale (highest priority)
curl -X POST https://api.courier.com/send \
  -H "Authorization: Bearer $COURIER_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "message": {
      "to": {
        "email": "[email protected]",
        "locale": "fr"
      },
      "content": { ... }
    }
  }'
Pro Tip: Set the locale in user profiles (via the Profiles API) to automatically localize all notifications for that user. This way, you don’t need to specify the locale in every send request. The locale from the user profile will be used unless overridden by message.to.locale or profile.locale in the send request.

Best Practices

Use Consistent Locale Codes

Use standard locale codes (e.g., en-US, es-ES, fr-FR) or simple language codes (e.g., 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. This ensures your notifications render correctly even if:
  • 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, consider localizing the href and src properties to point to localized versions of your website or app:
{
  "type": "action",
  "content": "View Dashboard",
  "href": "https://app.example.com/dashboard",
  "locales": {
    "es": {
      "content": "Ver Panel",
      "href": "https://app.example.com/es/dashboard"
    }
  }
}

Combine with Channel-Specific Customization

You can combine locales with channel-specific customization to create fully localized, channel-optimized notifications:
{
  "type": "channel",
  "channels": ["email"],
  "elements": [
    {
      "type": "text",
      "content": "Check your email for details",
      "locales": {
        "es": {
          "content": "Revisa tu correo para más detalles"
        }
      }
    }
  ]
}

Test All Locales

Before deploying, test your templates with all supported locales to ensure:
  • 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.)
Elemental locales are defined directly in your template JSON, making them ideal for templates where translations are known at template creation time. However, Courier offers other internationalization approaches that may better suit your needs: