---
url: /en/docs/i18n/structures.md
description: >-
  Choose how Sveltia CMS stores localized content on disk, from a single file
  per entry to a folder per locale, for entry and file collections.
---

# I18n Content Structures

The `structure` i18n option defines how localized content is stored on disk. It is set in the [top-level or collection-level configuration](/en/docs/i18n/options). Sveltia CMS supports five different structures:

* `single_file`: All locales are stored in a single file.
* `single_file_default_root`: Like `single_file`, but the default locale’s fields are stored at the root level of the file (without a locale key), while non-default locales are nested under their locale key. This is designed to support [Lume’s Multilanguage plugin](https://lume.land/plugins/multilanguage/#multilanguage-data).
* `multiple_files`: Each locale has its own file with the locale code in the filename.
* `multiple_folders`: Each locale has its own folder containing the localized files.
* `multiple_root_folders`: Each locale has its own root folder containing all collections.

::: warning Deprecation Notice

The `multiple_folders_i18n_root` structure is deprecated and will be removed in Sveltia CMS v1.0.0. Use `multiple_root_folders` instead, which has a more intuitive name and the same file structure.

:::

Some frameworks and static site generators may have specific requirements for multilingual content organization. Choose the structure that best fits your project’s needs.

When using a structure other than `single_file`, Sveltia CMS automatically links localized entries and files based on their filenames or folder paths. From the Content Editor perspective, they appear as a single entry or file with multiple locales, regardless of the underlying file structure. There’s no need to manually link translations.

## Entry Collections

The `structure` i18n option defines how localized content is stored for entry collections. Below are examples of each structure type.

### Single File

::: code-group

```yaml [YAML]{2}
i18n:
  structure: single_file
  locales: [en, de, fr]
```

```toml [TOML]{2}
[i18n]
structure = "single_file"
locales = ["en", "de", "fr"]
```

```json [JSON]{3}
{
  "i18n": {
    "structure": "single_file",
    "locales": ["en", "de", "fr"]
  }
}
```

```js [JavaScript]{3}
{
  i18n: {
    structure: "single_file",
    locales: ["en", "de", "fr"],
  },
}
```

:::

The `single_file` structure stores all locales in a single file:

```
/<folder>/<path>.<extension>
```

The file path remains the same for all locales:

```
.
└─ content/
   └─ pages/
      └─ about.md
```

And the file contains all locales in a single file, with locale keys as top-level properties:

```yaml
de:
  title: Über uns
  content: Dies ist die deutsche Version der Seite.
en:
  title: About Us
  content: This is the English version of the page.
fr:
  title: À propos de nous
  content: Ceci est la version française de la page.
```

### Single File (Default Root)

::: code-group

```yaml [YAML]{2}
i18n:
  structure: single_file_default_root
  locales: [en, de, fr]
```

```toml [TOML]{2}
[i18n]
structure = "single_file_default_root"
locales = ["en", "de", "fr"]
```

```json [JSON]{3}
{
  "i18n": {
    "structure": "single_file_default_root",
    "locales": ["en", "de", "fr"]
  }
}
```

```js [JavaScript]{3}
{
  i18n: {
    structure: "single_file_default_root",
    locales: ["en", "de", "fr"],
  },
}
```

:::

The `single_file_default_root` structure stores all locales in a single file, just like `single_file`. The difference is that the **default locale’s fields are written at the root level** of the file, while non-default locales are nested under their locale key:

```yaml
lang: [en, de, fr] # auto-generated list of all enabled locales
title: About Us
content: This is the English version of the page.
de:
  title: Über uns # default locale (de) at root
  content: Dies ist die deutsche Version der Seite.
fr:
  title: À propos de nous
  content: Ceci est la version française de la page.
```

This format is compatible with [Lume’s Multilanguage plugin](https://lume.land/plugins/multilanguage/#multilanguage-data), which resolves field values from the root level for the default locale and from the nested locale keys for other locales. The `lang` field is automatically maintained by Sveltia CMS and lists all currently enabled locales.

::: tip Limitation

Unlike the examples in Lume’s documentation, all non-default locales will have a complete set of fields, even if their values are the same as the default locale. Make sure to keep the non-default locale fields in sync with the default locale when making changes manually, otherwise you may end up with inconsistent content across locales.

:::

### Multiple Files

::: code-group

```yaml [YAML]{2}
i18n:
  structure: multiple_files
  locales: [en, de, fr]
```

```toml [TOML]{2}
[i18n]
structure = "multiple_files"
locales = ["en", "de", "fr"]
```

```json [JSON]{3}
{
  "i18n": {
    "structure": "multiple_files",
    "locales": ["en", "de", "fr"]
  }
}
```

```js [JavaScript]{3}
{
  i18n: {
    structure: "multiple_files",
    locales: ["en", "de", "fr"],
  },
}
```

:::

The `multiple_files` structure creates separate files for each locale, with the locale code included in the filename:

```yaml
/<folder>/<path>.<locale>.<extension>
```

The file paths for each locale would look like this:

```yaml
.
└─ content/
   └─ pages/
      ├─ about.de.md  # German
      ├─ about.en.md  # English (default locale)
      └─ about.fr.md  # French
```

::: info Omitting Default Locale from File Path

When the `omit_default_locale_from_file_path` option is set to `true`, the path depends on the locale being the default locale or not:

```yaml
/<folder>/<path>.<extension> # default locale
/<folder>/<path>.<locale>.<extension> # other locales
```

For example, with `en` as the default locale, the English file would be named `about.md`, while the German and French files would retain the locale suffix:

```yaml
.
└─ content/
   └─ pages/
      ├─ about.md     # English (default locale)
      ├─ about.de.md  # German
      └─ about.fr.md  # French
```

:::

### Multiple Folders

::: code-group

```yaml [YAML]{2}
i18n:
  structure: multiple_folders
  locales: [en, de, fr]
```

```toml [TOML]{2}
[i18n]
structure = "multiple_folders"
locales = ["en", "de", "fr"]
```

```json [JSON]{3}
{
  "i18n": {
    "structure": "multiple_folders",
    "locales": ["en", "de", "fr"]
  }
}
```

```js [JavaScript]{3}
{
  i18n: {
    structure: "multiple_folders",
    locales: ["en", "de", "fr"],
  },
}
```

:::

The `multiple_folders` structure creates separate folders for each locale, containing the localized files:

```yaml
/<folder>/<locale>/<path>.<extension>
```

The file paths for each locale would look like this:

```yaml
.
└─ content/
   └─ pages/
      ├─ de/
      │  └─ about.md  # German
      ├─ en/
      │  └─ about.md  # English (default locale)
      └─ fr/
         └─ about.md  # French
```

::: info Omitting Default Locale from File Path

When the `omit_default_locale_from_file_path` option is set to `true`, the path depends on the locale being the default locale or not:

```yaml
/<folder>/<path>.<extension> # default locale
/<folder>/<locale>/<path>.<extension> # other locales
```

For example, with `en` as the default locale, the English file would be located at `content/pages/about.md`, while the German and French files would be located in their respective locale folders:

```yaml
.
└─ content/
   └─ pages/
      ├─ about.md     # English (default locale)
      ├─ de/
      │  └─ about.md  # German
      └─ fr/
         └─ about.md  # French
```

:::

### Multiple Root Folders

::: code-group

```yaml [YAML]{2}
i18n:
  structure: multiple_root_folders
  locales: [en, de, fr]
```

```toml [TOML]{2}
[i18n]
structure = "multiple_root_folders"
locales = ["en", "de", "fr"]
```

```json [JSON]{3}
{
  "i18n": {
    "structure": "multiple_root_folders",
    "locales": ["en", "de", "fr"]
  }
}
```

```js [JavaScript]{3}
{
  i18n: {
    structure: "multiple_root_folders",
    locales: ["en", "de", "fr"],
  },
}
```

:::

The `multiple_root_folders` structure creates separate root folders for each locale, containing all collections:

```yaml
/<locale>/<folder>/<path>.<extension>
```

The file paths for each locale would look like this:

```yaml
.
├─ de/
│  └─ pages/
│     └─ about.md  # German
├─ en/
│  └─ pages/
│     └─ about.md  # English (default locale)
└─ fr/
   └─ pages/
      └─ about.md  # French
```

::: info Omitting Default Locale from File Path

When the `omit_default_locale_from_file_path` option is set to `true`, the path depends on the locale being the default locale or not:

```yaml
/<folder>/<path>.<extension> # default locale
/<locale>/<folder>/<path>.<extension> # other locales
```

For example, with `en` as the default locale, the English file would be located at `pages/about.md`, while the German and French files would be located in their respective locale root folders:

```yaml
.
├─ pages/
│  └─ about.md     # English (default locale)
├─ de/
│  └─ pages/
│     └─ about.md  # German
└─ fr/
   └─ pages/
      └─ about.md  # French
```

:::

### Nested Collections

A [nested collection](/en/docs/collections/entries/nested) organizes entries into a folder tree, which is a separate thing from the folders and file names i18n adds. The two combine: the locale is taken off the file path before the tree is worked out, so the same tree appears whichever structure you choose, and the localized files of one entry are linked as usual.

The examples below use a `pages` collection with a `products/hardware` entry. In the default `subfolders` mode, where each entry is an index file in a folder of its own:

```yaml
# single_file
content/pages/products/hardware/_index.md

# multiple_files
content/pages/products/hardware/_index.en.md
content/pages/products/hardware/_index.de.md

# multiple_folders
content/pages/en/products/hardware/_index.md
content/pages/de/products/hardware/_index.md

# multiple_root_folders
en/content/pages/products/hardware/_index.md
de/content/pages/products/hardware/_index.md
```

And with `subfolders: false`, where each entry is a regular file that keeps its own name:

```yaml
# single_file
content/pages/products/hardware.md

# multiple_files
content/pages/products/hardware.en.md
content/pages/products/hardware.de.md

# multiple_folders
content/pages/en/products/hardware.md
content/pages/de/products/hardware.md

# multiple_root_folders
en/content/pages/products/hardware.md
de/content/pages/products/hardware.md
```

In every case the entry sits at `products/hardware` in the tree, and the sidebar shows one `products` folder rather than one per locale. The `omit_default_locale_from_file_path` option works as it does elsewhere, dropping the locale from the default locale’s path only.

A few things worth knowing:

* The `depth` option counts the path segments below the collection folder, and the locale is not one of them. A locale folder or file-name suffix therefore never uses up part of the depth budget.
* Moving an entry with the [path editor](/en/docs/collections/entries/nested#choosing-a-parent-folder) moves every locale’s file in the same commit. In the `subfolders` mode it takes the entry’s child entries along, in every locale.
* [Entry-relative media](/en/docs/media/internal#using-entry-relative-folders) is stored once, not once per locale. With the `multiple_folders` and `multiple_root_folders` structures, where each locale has a folder of its own, the file is saved in the default locale’s folder and every locale’s entry refers to it by the same relative path. The other structures keep each locale’s file in the folder holding the entry, so the media already sits beside all of them.

#### Localized Folder Names

The folder names can be localized as well, giving nested content fully localized permalinks. In the `subfolders` mode, a folder is named after the slug of the entry stored in it, so [localizing the entry slugs](/en/docs/i18n/slugs#localizing-entry-slugs) with the `localize` filter localizes the whole folder chain above an entry along with the entry itself. With the `multiple_folders` structure, a `products/hardware` entry in English could then be stored as follows:

```yaml
content/pages/en/products/hardware/_index.md
content/pages/de/produkte/hardware/_index.md
```

The tree in the sidebar and the [path editor](/en/docs/collections/entries/nested#choosing-a-parent-folder) still go by the default locale’s names, since that’s how the entries are identified, but the other locales’ path editor panes show the folder as it’s named in their own locale. The localized folder names are taken from the entries stored in the folders, including unpublished ones with the Editorial Workflow, so a section and its sub-pages can be created in one sitting. A folder whose entry lacks a locale keeps its default name in that locale.

When an entry is moved to a different folder, each locale’s file goes below the localized folder chain, and its child entries are moved along in every locale. Renaming the folder is done with the [Slug Editor](/en/docs/ui/content-editor#slug-editor), which offers a folder name per locale when the slugs are localized.

This requires the `multiple_files` or `multiple_folders` structure, like localized slugs in general. With `subfolders: false`, entries are files rather than folders, so a folder takes its localized name from the entry it belongs to: its index file, as named with the `meta.path.index_file` option or conventionally `index` or `_index`, or else a file of the same name stored beside it, such as `about.md` next to the `about` folder in an Eleventy or Jekyll project. A folder with neither keeps the same name in every locale.

Because an entry in a nested collection is identified by its path within the collection folder rather than by a bare slug, that path is what the `translationKey` property holds there, e.g. `products/hardware/_index`, so two entries with the same slug in different folders stay apart. When an entry is moved, the property is updated in every file it takes along.

### Page Bundles

A [page bundle](https://gohugo.io/content-management/page-bundles/) keeps an entry’s content file and its media together in one folder. Give a collection a [`path`](/en/docs/collections/entries/slugs#using-subfolders) option ending in a fixed file name to store each entry as a leaf bundle, or use the `subfolders` mode of a [nested collection](/en/docs/collections/entries/nested#nesting-page-bundles) to store each one as a branch bundle. Combined with [entry-relative media](/en/docs/media/internal#using-entry-relative-folders), uploads land in the bundle instead of a shared media folder.

The examples below use a `posts` collection with `path: '{{slug}}/index'`, `media_folder: ''` and `public_folder: ''`, holding a `my-first-post` entry with a `cover` image.

The `single_file` and `multiple_files` structures keep every locale in the same bundle, so the media sits beside all of them. The latter is how Hugo’s own translation by file name is organized:

```yaml
# single_file
content/posts/my-first-post/index.md      # cover: image1.jpg, under each locale key
content/posts/my-first-post/image1.jpg

# multiple_files
content/posts/my-first-post/index.en.md   # cover: image1.jpg
content/posts/my-first-post/index.de.md   # cover: image1.jpg
content/posts/my-first-post/image1.jpg
```

The `multiple_folders` and `multiple_root_folders` structures give each locale a bundle of its own. The media is not copied into each one: it’s stored in the default locale’s bundle, and every locale’s entry refers to it by the same relative path:

```yaml
# multiple_folders
content/posts/en/my-first-post/index.md   # cover: image1.jpg
content/posts/de/my-first-post/index.md   # cover: image1.jpg
content/posts/en/my-first-post/image1.jpg

# multiple_root_folders
en/content/posts/my-first-post/index.md   # cover: image1.jpg
de/content/posts/my-first-post/index.md   # cover: image1.jpg
en/content/posts/my-first-post/image1.jpg
```

Sveltia CMS resolves that path across locales, so the image appears in the content editor whichever locale you are editing. Hugo resolves it as well, because [a page bundle inherits the resources of its translated pages](https://gohugo.io/content-management/multilingual/) — but only where the two are linked as translations, which Hugo determines from each language’s `contentDir`. If your framework has no equivalent mechanism, prefer `single_file` or `multiple_files`, which keep the media in the same bundle as every locale’s content file.

## File Collections

The `structure` i18n option only applies to entry collections, except for the `single_file_default_root` structure as [described below](#single-file-default-root-structure-for-file-collections). Other `structure` option values are ignored for file collections, and the organization of localized files is determined by the `file` path option in the file collection configuration.

You can use the `{{locale}}` placeholder in the `file` path option to create separate files or folders for each locale. If the placeholder is not used, the structure defaults to `single_file`, meaning the same file is shared across all locales.

The following example demonstrates how to set up a file collection with different i18n structures using the `{{locale}}` placeholder:

::: code-group

```yaml [YAML]{4,8-9,12-13,16-17,20-21}
collections:
  - name: pages
    label: Pages
    i18n: true
    files:
      - name: contact
        label: Contact Page
        i18n: true
        file: content/contact.md # equivalent to single_file
      - name: about
        label: About Page
        i18n: true
        file: content/about.{{locale}}.md # multiple_files
      - name: products
        label: Products Page
        i18n: true
        file: content/{{locale}}/products.md # multiple_folders
      - name: settings
        label: Site Settings
        i18n: true
        file: '{{locale}}/settings.yaml' # multiple_root_folders
```

```toml [TOML]{4,9-10,15-16,21-22,27-28}
[[collections]]
name = "pages"
label = "Pages"
i18n = true

[[collections.files]]
name = "contact"
label = "Contact Page"
i18n = true
file = "content/contact.md"

[[collections.files]]
name = "about"
label = "About Page"
i18n = true
file = "content/about.{{locale}}.md"

[[collections.files]]
name = "products"
label = "Products Page"
i18n = true
file = "content/{{locale}}/products.md"

[[collections.files]]
name = "settings"
label = "Site Settings"
i18n = true
file = "{{locale}}/settings.yaml"
```

```json [JSON]{6,11-12,17-18,23-24,29-30}
{
  "collections": [
    {
      "name": "pages",
      "label": "Pages",
      "i18n": true,
      "files": [
        {
          "name": "contact",
          "label": "Contact Page",
          "i18n": true,
          "file": "content/contact.md"
        },
        {
          "name": "about",
          "label": "About Page",
          "i18n": true,
          "file": "content/about.{{locale}}.md"
        },
        {
          "name": "products",
          "label": "Products Page",
          "i18n": true,
          "file": "content/{{locale}}/products.md"
        },
        {
          "name": "settings",
          "label": "Site Settings",
          "i18n": true,
          "file": "{{locale}}/settings.yaml"
        }
      ]
    }
  ]
}
```

```js [JavaScript]{6,11-12,17-18,23-24,29-30}
{
  collections: [
    {
      name: "pages",
      label: "Pages",
      i18n: true,
      files: [
        {
          name: "contact",
          label: "Contact Page",
          i18n: true,
          file: "content/contact.md",
        },
        {
          name: "about",
          label: "About Page",
          i18n: true,
          file: "content/about.{{locale}}.md",
        },
        {
          name: "products",
          label: "Products Page",
          i18n: true,
          file: "content/{{locale}}/products.md",
        },
        {
          name: "settings",
          label: "Site Settings",
          i18n: true,
          file: "{{locale}}/settings.yaml",
        },
      ],
    },
  ],
}
```

:::

The resulting file structure would be:

```yaml
.
├─ content/
│  ├─ contact.md      # shared across all locales (single_file)
│  ├─ about.de.md     # German
│  ├─ about.en.md     # English (default locale)
│  ├─ about.fr.md     # French
│  ├─ de/
│  │  └─ products.md  # German
│  ├─ en/
│  │  └─ products.md  # English (default locale)
│  └─ fr/
│     └─ products.md  # French
├─ de/
│  └─ settings.yaml   # German
├─ en/
│  └─ settings.yaml   # English (default locale)
└─ fr/
   └─ settings.yaml   # French
```

::: info Omitting Default Locale from File Path

As with entry collections, the `omit_default_locale_from_file_path` option can be used to omit the locale code from the file path for the default locale when using the `{{locale}}` placeholder in file collections. The above example would result in the following file structure if `en` is the default locale and `omit_default_locale_from_file_path` is set to `true`:

```yaml
.
├─ content/
│  ├─ contact.md      # shared across all locales (single_file)
│  ├─ about.md        # English (default locale)
│  ├─ about.de.md     # German
│  ├─ about.fr.md     # French
│  ├─ products.md     # English (default locale)
│  ├─ de/
│  │  └─ products.md  # German
│  └─ fr/
│     └─ products.md  # French
├─ settings.yaml      # English (default locale)
├─ de/
│  └─ settings.yaml   # German
└─ fr/
   └─ settings.yaml   # French
```

:::

### Single File (Default Root) Structure for File Collections

The [`single_file_default_root` structure](#single-file-default-root) can also be used for file collections. The example below shows how to configure a file collection with this structure:

::: code-group

```yaml [YAML]{2}
i18n:
  structure: single_file_default_root
  locales: [en, de, fr]

collections:
  - name: pages
    label: Pages
    files:
      - name: about
        label: About Page
        file: content/about.md
```

```toml [TOML]{2}
[i18n]
structure = "single_file_default_root"
locales = ["en", "de", "fr"]

[[collections]]
name = "pages"
label = "Pages"

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

```json [JSON]{3}
{
  "i18n": {
    "structure": "single_file_default_root",
    "locales": ["en", "de", "fr"]
  },
  "collections": [
    {
      "name": "pages",
      "label": "Pages",
      "files": [
        {
          "name": "about",
          "label": "About Page",
          "file": "content/about.md"
        }
      ]
    }
  ]
}
```

```js [JavaScript]{3}
{
  i18n: {
    structure: "single_file_default_root",
    locales: ["en", "de", "fr"],
  },
  collections: [
    {
      name: "pages",
      label: "Pages",
      files: [
        {
          name: "about",
          label: "About Page",
          file: "content/about.md",
        },
      ],
    },
  ],
}
```

:::
