# Internationalization (Languages)

## 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](../../features/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
| Part                 | Files                                                    |
| -------------------- | -------------------------------------------------------- |
| The language itself  | an entry in `src/config/language.json`                   |
| Page and section text | a folder per language in every content folder: `src/content/sections/french/`, `src/content/services/french/`, … |
| Interface text       | `src/i18n/fr.json` (buttons, form messages, footer, 404 page, labels) |
| Menus                | `src/config/menu.fr.json`                                 |

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

```json
{
  "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](/ecozen/customization/header/#choose-a-header-layout)). 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
```toml
# 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
<Steps>

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

   ```bash
   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`:

   ```bash
   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](../../features/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/`.

</Steps>

<FileTree>

- src/content/
  - sections/
    - english/
    - french/
    - arabic/
    - **german/**
  - services/
    - english/
    - french/
    - arabic/
    - **german/**

</FileTree>

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:

<Steps>

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/`.

</Steps>

### Links Between Pages
- 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](../../deployment/).

### Do It With AI
```text
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.
```