Skip to Content
FeaturesLocalization

Localization

Foir supports multi-language content out of the box. Configure locales for your project, add translations to content fields, and serve the right language to each visitor automatically.

Overview

Localization in Foir works by attaching per-locale translations to translatable fields on your records. The system uses BCP-47 language codes and supports a configurable fallback chain so content is always returned, even when translations are incomplete.

Key Concepts

  • Locale — A BCP-47 language code (e.g., en-US, fr-FR, de-DE) representing a language and region
  • Default locale — The locale used when no locale is specified in a request
  • Fallback chain — When a translation is missing for the requested locale, the system falls back through configured locales
  • Translatable fields — Text and rich text fields. Other field types (numbers, dates, booleans, references, media) share a single value across all locales

Fallback Chain

If a translation is missing for the requested locale, Foir follows the fallback chain:

Requested locale (fr-CA) -> Fallback locale (fr-FR) -> Default locale (en-US)

This ensures content is always returned. You configure the fallback locale per locale, so fr-CA can fall back to fr-FR, which falls back to the project default.

Locale Properties

Each locale has:

PropertyDescription
localeBCP-47 code (e.g., en-US)
displayNameHuman-readable name (e.g., “English (United States)“)
nativeNameName in the native script (e.g., “English (US)“)
isDefaultWhether this is the project’s default locale
isRtlWhether the language is right-to-left
fallbackLocaleLocale to fall back to when a translation is missing

In the Admin

Configuring locales

  1. Go to Settings > Locales
  2. Click Add Locale
  3. Select or enter the BCP-47 code (e.g., fr-FR)
  4. Set the display name
  5. Optionally configure a fallback locale
  6. Mark one locale as the default

Translating content

  1. Open any record in the editor
  2. Use the locale switcher at the top of the editor to switch between languages
  3. Translatable fields show separate inputs for each locale
  4. Save when translations are complete

Non-translatable fields (numbers, dates, booleans, references) share the same value across all locales.

Via the CLI

List locales

foir locales list

Get a locale

By ID:

foir locales get loc_abc123

By code:

foir locales get fr-FR

Get the default locale

foir locales default

Create a locale

foir locales create --data '{ "locale": "fr-FR", "displayName": "French (France)", "nativeName": "Fran\u00e7ais (France)", "fallbackLocale": "en-US", "isRtl": false }'

Update a locale

foir locales update loc_abc123 --data '{ "displayName": "French (France)", "fallbackLocale": "en-US" }'

Delete a locale

foir locales delete loc_abc123

Via the API

Requesting localized content

Pass the locale argument on the per-model query. The fallback chain is applied server-side and the resolved values for that locale (or its fallback) come back directly on the typed fields:

query { page(naturalKey: "about", locale: "fr-FR") { _id title body } }

Batch locale requests

Fetch multiple records in the same locale with the plural connection, filtering by natural key:

query { pages( where: { naturalKey: { in: ["about", "contact", "faq"] } } locale: "de-DE" ) { edges { node { _naturalKey title } } } }

Writing translations

The public per-model write surface is locale-agnostic: createPage / updatePage accept a single set of field values, and there is no translations input or locale argument on mutations. Author per-locale content in the admin editor with the locale switcher (see Translating content above). The public API’s role in localization is read-time resolution via the locale argument.

Reading locale configuration

There is no public GraphQL locales query. Manage and inspect a project’s configured locales in the admin (Settings > Locales) or via the CLI (foir locales list, shown above).

Localization vs Variants

Localization and variants work together but serve different purposes:

FeaturePurposeExample
LocalizationTranslate the same content into different languagesEnglish -> French translation
VariantsShow different content to different audiencesMobile users see a simplified layout

A single variant can have translations in multiple locales. For example, your “VIP” variant can have both English and French translations.

Best Practices

  • Set up fallback locales to prevent empty content. A missing translation should fall back gracefully, not show blank fields.
  • Use standard BCP-47 codes consistently (e.g., en-US not en_us or english).
  • Keep translatable copy (text and rich text fields) in dedicated fields so it can be localized per locale; values like alt text are part of the media value and shared across all locales.
  • Start with your primary market locales and expand as needed. You can add locales at any time.
  • Test right-to-left rendering if you support Arabic, Hebrew, or other RTL languages.
Last updated on