Skip to content
New: Customize your theme with an AI assistant. Read the guide

Internationalization

Files: src/config/language.json, src/config/config.toml, src/i18n/, src/config/menu.*.json, src/content/**/<language>/ · Skill level: Beginner

Ecozen ships in English (the default, at /), French (at /fr/), and Arabic (at /ar/). Every page, menu, and label exists in every language. Arabic is right to left, and the whole layout mirrors: see RTL & Arabic. The Arabic text is a machine translation, so have a native speaker review it before you publish.

In the Tina edition, each language has its own folder or file under each entry at /admin/, including Menus and Interface labels. In the Sanity edition, each language is an entry under Content in the Studio, with its own Menus and Interface labels. language.json stays in the project in every edition.

What Makes Up A Language

PartFiles
The language itselfan entry in src/config/language.json
Page and section texta folder per language in every content folder: src/content/sections/french/, src/content/services/french/, …
Interface textsrc/i18n/fr.json (buttons, form messages, footer, 404 page, labels)
Menussrc/config/menu.fr.json

In src/config/language.json, each language has an entry like this:

{
"languageName": "Français",
"languageCode": "fr",
"contentDir": "french",
"dir": "ltr",
"ogLocale": "fr_FR",
"dateLocale": "fr-FR",
"weight": 2
}

languageName is the name in the language switch. languageCode is the URL prefix and the file suffix (fr.json, menu.fr.json), and contentDir is the folder name inside each content folder. dir is ltr or rtl. ogLocale is for social previews, dateLocale sets how dates and calculator numbers are written, and weight is the language’s place in the switch.

The language switch shows the current language’s name with a small icon, and opens a list of the others. It sits in the top bar of the classic header and the info row of the transparent one, and in the main bar of the others (see Header). On a blog category or tag page, it links to the other language’s blog page, because category and tag names differ between languages.

Language Settings

src/config/config.toml
[settings.multilingual]
enable = true
defaultLanguage = "en"
disableLanguages = [""] # for example ["fr"]
showDefaultLangInUrl = false # true gives /en/pricing/ instead of /pricing/

Restart npm run dev after you change these values, and after you add a language.

Remove A Language

  • Turn one language off, keep its files. Add its code to disableLanguages, for example ["ar"]. It stops being built, and the language switch stays. This works the same in all three editions.
  • Turn every other language off, keep the files. Set enable = false. Only the default language is built and the language switch disappears. You can turn them back on any time.
  • Keep only your default language, for good. Save a checkpoint first (a git commit or a copy of the folder). Then:
    • Astro: set enable = false, run npm run dev once, then run npm run remove-multilingual.
    • Tina: the same, then run npm run tina:generate.
    • Sanity: run npm run remove-multilingual. The other languages’ documents stay in your project unless you add -- --delete-online, which deletes them and can’t be undone.

In the Astro and Tina editions, remove-multilingual deletes the content folders, src/i18n/ file, and src/config/menu.*.json file of every language except the default one, and removes them from language.json. This can’t be undone without your checkpoint. On Netlify, also delete their blocks in netlify.toml.

Add A Language

  1. Run the language script. German is the example here. In the Astro and Tina editions:

    Terminal window
    npm run generate-multilingual-content -- --lang de --name Deutsch --dir-name german --locale de-DE --og de_DE

    In the Sanity edition, the last flag is --og-locale:

    Terminal window
    npm run generate-multilingual-content -- --lang de --name Deutsch --dir-name german --locale de-DE --og-locale de_DE

    For a right-to-left language, add --dir rtl. See RTL & Arabic for what else it needs.

  2. In the Tina edition, run npm run tina:generate, and later commit tina/tina-lock.json.

  3. Translate the copy. In the Astro and Tina editions, the script adds the language to language.json and copies the default language’s content folders, src/i18n/en.json, and src/config/menu.en.json. It keeps files you already translated, unless you add --force. In the Sanity edition, it adds the language and creates a copy of every default-language document in your project to translate. Your other documents are not touched.

  4. On Netlify, add a /de/* block to netlify.toml, like the French one.

  5. Restart npm run dev and open /de/.

  • Directorysrc/content/
    • Directorysections/
      • Directoryenglish/
        • …
      • Directoryfrench/
        • …
      • Directoryarabic/
        • …
      • Directorygerman/
        • …
    • Directoryservices/
      • Directoryenglish/
        • …
      • Directoryfrench/
        • …
      • Directoryarabic/
        • …
      • Directorygerman/
        • …

Translating a whole site is a perfect job for an AI assistant: see the prompt at the end of this page.

Replace A Language

In the Astro and Tina editions, you can also turn the French copy into another language, instead of adding one:

  1. In language.json, change the French entry: "languageName": "Español", "languageCode": "es", "contentDir": "spanish", "ogLocale": "es_ES", "dateLocale": "es-ES".

  2. Rename every src/content/**/french/ folder to spanish/.

  3. Rename src/i18n/fr.json to es.json and src/config/menu.fr.json to menu.es.json.

  4. Translate the text values in those files and folders. Keep every key as it is, and change internal links in Markdown text from /fr/ to /es/.

  5. In netlify.toml, change the /fr/* redirect to /es/* and /es/404/.

  6. In the Tina edition, run npm run tina:generate.

  7. Restart npm run dev and open /es/.

  • In menus and in url fields of buttons in content files, write the English path (/pricing/). Ecozen adds the language prefix for you.
  • In Markdown text (the body of a post or page, or a Markdown link in a label), write the full path for that language yourself: [voir les tarifs](/fr/pricing/) in a French file.
  • Give customSlug the same value in every language, so the language switch finds the matching page.

Text That Lives In config.toml

A few config.toml values are the same in every language: [settings.contactInfo] (use value in that language’s contact-section-two.md to translate the opening hours), the theme credit, and any label you fill in instead of leaving it empty, such as [settings.announcementBar] label. Leave those labels empty and use the i18n files for a site in several languages.

Translated 404 Pages

Each language has its own 404 page (/fr/404/, /ar/404/). On Netlify, the redirects in netlify.toml send a missing /fr/... or /ar/... address to it; add one block per extra language. Other hosts may show the default language’s 404 page unless you add a rule: see Deployment.

Do It With AI

Add [LANGUAGE] to the site. Run npm run generate-multilingual-content with --lang [xx], --name
[NAME], --dir-name [folder], --locale [DATE LOCALE], and the Open Graph flag from this edition's
README, and add --dir rtl if it reads right to left. Then translate every text value in the new
folders, menu file, and i18n file from the English versions. Keep all keys, option values, icon
names, URLs, and customSlug values unchanged. In Markdown text, change internal links to /[xx]/.
On Netlify, add a /[xx]/* block to netlify.toml. Work folder by folder and tell me when each is done.
Type something to search...