Skip to content

I18n Configuration Options ​

The i18n option can be defined at the top level of your configuration, on a collection, on a file in a file collection and on individual fields. This page explains what each level accepts. For a complete example that sets up all levels at once, see the Internationalization overview.

Top-Level Configuration ​

Let’s start by defining the i18n option at the top level of your configuration. The i18n option accepts the following properties:

yaml
i18n:
  structure: multiple_folders
  locales: [en, de, fr]
toml
[i18n]
structure = "multiple_folders"
locales = ["en", "de", "fr"]
json
{
  "i18n": {
    "structure": "multiple_folders",
    "locales": ["en", "de", "fr"]
  }
}
js
{
  i18n: {
    structure: "multiple_folders",
    locales: ["en", "de", "fr"],
  },
}
  • structure: (optional) Defines how localized content is stored. Default: single_file. See Content Structures for the available options. An entry collection can instead put the {{locale}} placeholder in its folder option to say where the locale folder goes, as in content/{{locale}}/posts. See Custom Locale Folder Placement.
  • locales: An array of locale codes representing the supported languages.
  • default_locale: (optional) Explicitly sets the default locale. If not provided, the first locale in the locales array is used.
  • initial_locales: (optional) Locales to be enabled when users create a new entry draft. Accepts a list of locale codes, default (default locale only) or all (all locales). When defined, users can enable or disable non-default locales in the Content Editor. See Disabling Non-Default Locale Content below.
  • omit_default_locale_from_file_path: (optional) When using a structure that creates separate files for each locale (e.g. multiple_files), setting this option to true will omit the locale code from the file path for the default locale. It also applies to file collections whose file path includes the {{locale}} placeholder. This is useful for frameworks like Zola that expect the default locale to have a specific filename. See Where the Placeholder Can Be Omitted for the positions where the locale can be removed.
  • omit_default_locale_from_preview_path: (optional) When set to true, the preview URL for the default locale will not include the locale code. This is useful for frameworks that expect the default locale to be served at the root path. The placeholder is removed in the same positions as with omit_default_locale_from_file_path, so /{{locale}}/posts/{{slug}} becomes /posts/{{slug}} and /{{locale}} becomes /. In any other position, the locale is kept.

Deprecation Notice

The omit_default_locale_from_filename option is deprecated and will be removed in Sveltia CMS v1.0.0. Use omit_default_locale_from_file_path instead. The old option is simply an alias of the new one, which takes precedence if both are defined.

Disabling Non-Default Locale Content ​

Developers can specify locales to be enabled by default when users create a new entry draft, using the initial_locales i18n option, which accepts a locale list, default (default locale only) or all (all locales).

The default locale is always enabled, even if it’s excluded from initial_locales, while other locales can be enabled or disabled by users in the Content Editor through the three-dot menu in the top-right corner, if this i18n option is defined.

The following example disables German by default, but users can manually enable it if needed. Users can also disable French, which is enabled by default.

yaml
i18n:
  structure: multiple_files
  locales: [en, fr, de]
  default_locale: en
  initial_locales: [en, fr]
toml
[i18n]
structure = "multiple_files"
locales = ["en", "fr", "de"]
default_locale = "en"
initial_locales = ["en", "fr"]
json
{
  "i18n": {
    "structure": "multiple_files",
    "locales": ["en", "fr", "de"],
    "default_locale": "en",
    "initial_locales": ["en", "fr"]
  }
}
js
{
  i18n: {
    structure: "multiple_files",
    locales: ["en", "fr", "de"],
    default_locale: "en",
    initial_locales: ["en", "fr"],
  },
}

Deprecation Notice

The save_all_locales option has been deprecated in favor of the more flexible initial_locales option and will be removed in Sveltia CMS v1.0.0. If you are upgrading from an older version, update your configuration accordingly:

  • save_all_locales: false is equivalent to initial_locales: all.
  • save_all_locales: true is equivalent to initial_locales being omitted (all locales enabled by default).

Collection-Level Configuration ​

You can opt into i18n for a specific collection by setting the i18n option to true. This will use the top-level i18n configuration for that collection.

yaml
collections:
  - name: posts
    i18n: true
toml
[[collections]]
name = "posts"
i18n = true
json
{
  "collections": [
    {
      "name": "posts",
      "i18n": true
    }
  ]
}
js
{
  collections: [
    {
      name: "posts",
      i18n: true,
    },
  ],
}

To override the global i18n settings for a specific collection, set the i18n option to an object with the desired configuration.

yaml
collections:
  - name: posts
    i18n:
      structure: multiple_files
      locales: [en, de, fr, ja]
toml
[[collections]]
name = "posts"

[collections.i18n]
structure = "multiple_files"
locales = ["en", "de", "fr", "ja"]
json
{
  "collections": [
    {
      "name": "posts",
      "i18n": {
        "structure": "multiple_files",
        "locales": ["en", "de", "fr", "ja"]
      }
    }
  ]
}
js
{
  collections: [
    {
      name: "posts",
      i18n: {
        structure: "multiple_files",
        locales: ["en", "de", "fr", "ja"],
      },
    },
  ],
}

Note that the structure option at the collection level only applies to entry collections. For file collections, see File Collections on the Content Structures page.

File-Level Configuration ​

Each file in a file collection can also have the i18n option set to true to enable localization for that specific file. As with collections, the file will use the top-level i18n configuration. The file-level option has no effect unless i18n is also enabled at the top level and on the collection.

yaml
collections:
  - name: pages
    label: Pages
    i18n: true
    files:
      - name: about
        label: About Page
        file: content/about.md
        i18n: true
toml
[[collections]]
name = "pages"
label = "Pages"
i18n = true

[[collections.files]]
name = "about"
label = "About Page"
file = "content/about.md"
i18n = true
json
{
  "collections": [
    {
      "name": "pages",
      "label": "Pages",
      "i18n": true,
      "files": [
        {
          "name": "about",
          "label": "About Page",
          "file": "content/about.md",
          "i18n": true
        }
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: "pages",
      label: "Pages",
      i18n: true,
      files: [
        {
          name: "about",
          label: "About Page",
          file: "content/about.md",
          i18n: true,
        },
      ],
    },
  ],
}

If you want to override the global i18n settings for a specific file, set the i18n option to an object with the desired configuration. The following example enables localization only for English and French for the about file:

yaml
collections:
  - name: pages
    label: Pages
    i18n: true
    files:
      - name: about
        label: About Page
        file: content/about.md
        i18n:
          locales: [en, fr]
toml
[[collections]]
name = "pages"
label = "Pages"
i18n = true

[[collections.files]]
name = "about"
label = "About Page"
file = "content/about.md"

[collections.files.i18n]
locales = ["en", "fr"]
json
{
  "collections": [
    {
      "name": "pages",
      "label": "Pages",
      "i18n": true,
      "files": [
        {
          "name": "about",
          "label": "About Page",
          "file": "content/about.md",
          "i18n": {
            "locales": ["en", "fr"]
          }
        }
      ]
    }
  ]
}
js
{
  collections: [
    {
      name: "pages",
      label: "Pages",
      i18n: true,
      files: [
        {
          name: "about",
          label: "About Page",
          file: "content/about.md",
          i18n: {
            locales: ["en", "fr"],
          },
        },
      ],
    },
  ],
}

Note that the structure option does not apply to file collections. See File Collections on the Content Structures page for more details.

Field-Level Configuration ​

Each field can be localized individually by setting the i18n option. The i18n option for fields accepts the following values:

  • true
    • The field can be edited separately for each locale.
    • The values are stored separately for each locale.
  • false (default)
    • The field can only be edited in the default locale.
    • The field is hidden in non-default locales.
    • Only the default locale’s value is stored.
  • duplicate
    • The field can only be edited in the default locale.
    • The field is read-only in non-default locales, displaying the default locale’s value.
    • The same value is stored for all locales.
  • sync
    • The field can be edited in every locale.
    • A change made in any locale is copied to the other locales. See Keeping Values in Sync for details.
    • The same value is stored for all locales.
  • duplicate_if_empty
    • The field can be edited separately for each locale.
    • The default locale’s value is copied to non-default locales where the field is empty. See Filling in Empty Values for details.
    • The values are stored separately for each locale.

Legacy option values

For backward compatibility with Netlify/Decap CMS, Sveltia CMS also supports translate and none values for the field-level i18n option, which are equivalent to true and false, respectively.

Here’s how the i18n option behaves for different field types:

  • The Hidden field type does not have a visible UI representation, but you’ll still need to set the i18n option to true, duplicate or sync to store values in non-default locales.
  • The complex List field type:
    • true lets each locale have its own items, which can be added, removed and reordered separately. A subfield only becomes editable in the other locales with its own i18n: true option; a subfield without the option is hidden there. If no subfield has the option, items can only be added in the default locale.
    • duplicate makes all subfields duplicate. When the user adds, reorders or removes list items in the default locale, the same changes are reflected in other locales, where the items are shown read-only and can’t be added, removed or reordered. Subfields can be configured with their own i18n options.
    • sync keeps all subfields in sync. List items can be added, removed and reordered in any locale, and the same changes are reflected in the other locales. Subfields can be configured with their own i18n options.
  • The Object field type:
    • true lets the object appear in the other locales. A subfield only becomes editable there with its own i18n: true option; a subfield without the option is hidden in the other locales.
    • duplicate makes all subfields duplicate, and they are shown read-only in other locales. Subfields can be configured with their own i18n options.
    • sync keeps all subfields in sync, and they can be edited in every locale. Subfields can be configured with their own i18n options.
  • The KeyValue field type additionally accepts duplicate_keys, which makes the keys duplicate — they are copied from the default locale, in the same order, and read-only in non-default locales — while the values can be edited separately for each locale.
  • duplicate_if_empty is only available for the fields that hold a single, simple value: Code, DateTime, File, Image, Number, Relation, RichText, Select, String and Text. A File, Image, Relation or Select field that accepts multiple values works like one with i18n: true, and the CMS warns about it.

Here’s an example of how to configure fields with different i18n options:

yaml
fields:
  - name: title
    label: Title
    widget: string
    i18n: true
  - name: date
    label: Date
    widget: datetime
    i18n: duplicate
toml
[[fields]]
label = "Title"
name = "title"
widget = "string"
i18n = true

[[fields]]
label = "Date"
name = "date"
widget = "datetime"
i18n = "duplicate"
json
{
  "fields": [
    {
      "label": "Title",
      "name": "title",
      "widget": "string",
      "i18n": true
    },
    {
      "label": "Date",
      "name": "date",
      "widget": "datetime",
      "i18n": "duplicate"
    }
  ]
}
js
{
  fields: [
    {
      label: "Title",
      name: "title",
      widget: "string",
      i18n: true,
    },
    {
      label: "Date",
      name: "date",
      widget: "datetime",
      i18n: "duplicate",
    },
  ],
}

When the i18n option is true, all locales are required to have a value for the field. If you want to make the field required only for specific locales, you can use an array value for the required option, which defaults to true.

In the following example, the summary field is required for English and German locales only:

yaml
fields:
  - name: summary
    label: Summary
    widget: string
    i18n: true
    required: [en, de]
toml
[[fields]]
label = "Summary"
name = "summary"
widget = "string"
i18n = true
required = ["en", "de"]
json
{
  "fields": [
    {
      "label": "Summary",
      "name": "summary",
      "widget": "string",
      "i18n": true,
      "required": ["en", "de"]
    }
  ]
}
js
{
  fields: [
    {
      label: "Summary",
      name: "summary",
      widget: "string",
      i18n: true,
      required: ["en", "de"],
    },
  ],
}

Filling in Empty Values ​

The duplicate_if_empty option is useful for a value that is usually the same in every locale, but sometimes needs a localized version. Examples include a banner image with text that hasn’t been localized yet, or a product name that is only translated in some markets.

Unlike duplicate, the field can be edited in every locale. Unlike true, a non-default locale doesn’t start empty:

  • When an entry is opened, a non-default locale where the field is missing or empty gets the default locale’s value. This doesn’t count as a change, so the Save button stays disabled.
  • When a locale is enabled for an existing entry, it gets the default locale’s value.
  • As the user edits the field in the default locale, the change is copied to the non-default locales where the field is empty or still holds the default locale’s previous value. This way, a locale that follows the default locale stays in sync as the user types. A locale that has a value of its own is left alone. Reverting the change in the default locale restores the value in the locales that followed it.
  • Values are only copied from the default locale to the other locales, never the other way around.
  • Clearing the field in a non-default locale leaves it empty for now, but the next change in the default locale, or the next time the entry is opened, fills it in again. Clearing or restoring the field in the default locale with the field options menu doesn’t change the other locales.
  • In a List field with i18n: true, a subfield with duplicate_if_empty makes the items paired across locales, as a subfield with duplicate or sync does. Items are added, removed and reordered in the default locale, and the same changes are reflected in the other locales. The values are therefore copied to the matching item.
  • Translating all the fields of a locale at once also translates the fields that still hold the default locale’s value.
  • The field is validated in every locale, just like a field with i18n: true.

In the following example, the image field takes the English image in every locale until a localized image is selected:

yaml
fields:
  - name: image
    label: Banner Image
    widget: image
    i18n: duplicate_if_empty
toml
[[fields]]
label = "Banner Image"
name = "image"
widget = "image"
i18n = "duplicate_if_empty"
json
{
  "fields": [
    {
      "label": "Banner Image",
      "name": "image",
      "widget": "image",
      "i18n": "duplicate_if_empty"
    }
  ]
}
js
{
  fields: [
    {
      label: "Banner Image",
      name: "image",
      widget: "image",
      i18n: "duplicate_if_empty",
    },
  ],
}

Keeping Values in Sync ​

The sync option is useful for a value that must be the same in every locale, but that editors may want to change while working on any locale, such as a product price, an event date or a link. With duplicate, the field is read-only outside the default locale, so an editor working on a translation has to switch back to the default locale to change it.

As with duplicate, the same value is stored for all locales. Unlike duplicate, the field can be edited in every locale:

  • A change made in any locale is copied to the other locales as the user types. A Relation field value with a locale prefix, like {{locale}}/{{slug}}, and a DateTime field value with the localize_format option are converted for each locale.
  • When an entry is opened, a non-default locale where the field is missing gets the default locale’s value. A value that differs between locales, for example because a file was edited outside the CMS, is left as is until the field is next changed in any locale.
  • When a locale is enabled for an existing entry, it gets the default locale’s value.
  • Clearing, restoring or reverting the field with the field options menu in any locale does the same in every locale. Reverting all the changes to the entry restores each locale’s own original value.
  • A List field with sync lets the user add, remove and reorder items in any locale, and the same changes are reflected in the other locales. In a List field with i18n: true, a subfield with sync makes the items paired across locales, as a subfield with duplicate does. Items are then added, removed and reordered in the default locale only.
  • The field is validated in every locale, so an error is shown in whichever locale the user is editing.
  • The field is never translated or copied from another locale with the field options menu, since it already holds the same value everywhere.
  • A Compute field with sync is computed from the default locale’s values, and the result is copied to the other locales.
  • In a field of a rich text editor component, sync works like true, because the component’s values are stored in each locale’s text.

In the following example, the price field can be changed in any locale, while every locale keeps the same price:

yaml
fields:
  - name: price
    label: Price
    widget: number
    i18n: sync
toml
[[fields]]
label = "Price"
name = "price"
widget = "number"
i18n = "sync"
json
{
  "fields": [
    {
      "label": "Price",
      "name": "price",
      "widget": "number",
      "i18n": "sync"
    }
  ]
}
js
{
  fields: [
    {
      label: "Price",
      name: "price",
      widget: "number",
      i18n: "sync",
    },
  ],
}

Localized Field Options ​

A field’s label, help text and default value can be given for each locale with the following options, keyed by locale code. Each one takes precedence over its counterpart without the _localized suffix, which remains the fallback for the locales not listed:

  • label_localized: Label of the field. Also available for a variable type of a List or Object field.
  • hint_localized: Hint of the field. Also available for a variable type.
  • label_singular_localized: Singular label of a List or KeyValue field, shown on the Add button.
  • default_localized: Default value of the field, in the same format as the default option of the field type.

There are separate options for this rather than letting label or default take an object, because the default option of some field types, such as Object, KeyValue and Code, is already an object, so the two couldn’t be told apart.

In the following example, an English and German site has a list of categories with labels and default values in both languages:

yaml
fields:
  - name: categories
    label: Categories
    label_localized:
      de: Kategorien
    widget: list
    i18n: true
    default: [car repair, tuning]
    default_localized:
      de: [Autoreparatur, Tuning]
toml
[[fields]]
name = "categories"
label = "Categories"
label_localized = { de = "Kategorien" }
widget = "list"
i18n = true
default = ["car repair", "tuning"]
default_localized = { de = ["Autoreparatur", "Tuning"] }
json
{
  "fields": [
    {
      "name": "categories",
      "label": "Categories",
      "label_localized": { "de": "Kategorien" },
      "widget": "list",
      "i18n": true,
      "default": ["car repair", "tuning"],
      "default_localized": { "de": ["Autoreparatur", "Tuning"] }
    }
  ]
}
js
{
  fields: [
    {
      name: "categories",
      label: "Categories",
      label_localized: { de: "Kategorien" },
      widget: "list",
      i18n: true,
      default: ["car repair", "tuning"],
      default_localized: { de: ["Autoreparatur", "Tuning"] },
    },
  ],
}

How a localized value is picked depends on what it is:

  • A label or hint in the Content Editor follows the locale of the pane, so the German pane shows German labels.

    Some places have no content locale: the entry list, the sort and filter menus, the table and bulk edit views, and a collection without i18n. There, the label for the user’s UI locale is used instead, matched by language, e.g. de for German (Austria). If neither is found, the label or hint without the suffix is shown. This means label_localized can also be used to translate the editor UI for a team working in a language other than the content’s.

  • A default value is content, so it only follows the content locale. It only has an effect for a field with i18n: true, which has a value of its own in each locale. A field with i18n: duplicate or i18n: sync takes the default locale’s value in every locale, because it holds the same value everywhere. A value for a locale that isn’t configured, or the option in a collection without i18n, is never used, and the CMS warns about it.

Hardcoding default values that may change later, such as categories, is not always a good idea, though. If the categories are likely to be renamed or added, consider creating a separate collection for them and referring to it with a Relation field.