# Languages

Add target languages to a Comvi project, read the BCP-47 locale-code rules, create custom locales, and track progress per language.

Canonical: https://comvi.io/docs/platform/languages

Every project has one **source language**, the one your developers write in, and any number
of **target languages** you translate into. Translators see the source value next to the
target field.

## Add a language

1. Open the project and go to **Languages** in the sidebar.

2. Click **Add Language**. The dialog opens on a shortlist of popular languages; the
   search box covers the whole catalog of 619 locales.

3. Search by English name, native name or code ("French", "Français" or "fr") and select
   the language.

4. Confirm. The language appears in the list immediately.

Add languages at any time; no plan limits how many a project has. Existing translations are
untouched; every key shows an empty value for the new language, unless the language was
removed earlier, in which case its old values come back.

The source language is chosen at project creation and cannot be changed afterwards.
**Settings → General** shows it with a lock icon and "Cannot be changed after creation".

## Locale codes

| Code | Language |
|---|---|
| `en` | English |
| `en-US` | English (United States) |
| `pt-BR` | Portuguese (Brazil) |
| `zh-Hans` | Chinese (Simplified) |
| `zh-Hant-TW` | Chinese (Traditional, Taiwan) |
| `sr-Latn` | Serbian (Latin script) |

Codes are canonical **BCP-47**: subtags separated by a hyphen, the language lowercase, a
script subtag Titlecase, a region subtag UPPERCASE. The same code appears in the dashboard,
the API, the CLI, import/export and CDN URLs.

### Which spelling your app needs

The one Comvi stores. Every code in the built-in catalog is canonical, and a custom code is
canonicalized on the way in, so a project holds `pt-BR` however you spelled it when adding
it. Legacy spellings are accepted as **input** everywhere: the dashboard, the REST API, the
CLI and import/export resolve `pt_BR`, `pt-br` and `PT-BR` onto the stored `pt-BR`, and all
of them return the canonical form. Old CDN file names keep working for a deprecation window.

A canonical code is valid BCP-47 by construction. The Comvi i18n library's plurals and
formatters go through `Intl`, which throws on an underscored code, so use the project's
codes verbatim in app config and URLs.
[Add a locale end to end](/docs/i18n/guides/add-a-locale) walks the path from adding the
language to reading it in an app.

### Custom locales

Choose **Create custom language...** at the bottom of the Add Language dialog when no
built-in code fits: an internal pseudo-locale, a formality variant, a non-standard regional
split. Give it a code, a name, a native name and a flag emoji. The dialog suggests codes in
the shape `de-formal` or `en-simple`.

A custom code is canonicalized like any other, including uppercasing a region subtag:
`en_uk_legal` is stored as `en-UK-legal`. Separator and case do not make a code distinct,
so `pt_br` and `PT-BR` both resolve to the built-in `pt-BR` and are rejected as duplicates.
A custom code cannot shadow a built-in one: creating `fr` as a custom locale returns
`LOCALE_ALREADY_EXISTS`. Custom locales are scoped to their project, so two projects can
each define their own `de-formal`.

## Progress

The Languages page shows a three-part bar per language, and the table sorts by name or by
progress:

| Segment | Meaning |
|---|---|
| **Translated** | Values that have been reviewed and approved |
| **Needs Review** | Values that exist but have not been approved |
| **Not Translated** | Keys with no value for this language |

Each language row also carries the locale code, the English display name, the native name,
a flag derived from the code (or the emoji you gave a custom locale), and an optional
**AI translation context** sent to LLM machine-translation providers on top of the
project-level context. Plural rules come from the locale: English has `one` and `other`,
Arabic has six forms. ICU MessageFormat applies them at runtime in the consuming app.

## Right-to-left languages

The editor flips text direction for the language subtags `ar`, `he`, `fa`, `ur`, `yi`, `ps`
and `sd`. Direction is decided by the language subtag, so regional codes such as `ar-EG`
and `he-IL` are laid out right-to-left too. On the SDK side every i18n instance exposes a
reactive `dir` getter that returns `'rtl'` or `'ltr'` for the active locale; bind it to
your `<html>` or root layout element. See
[Text direction](/docs/i18n/formatting#text-direction).

## Remove a language

Open the actions menu on the language row and choose **Remove Language**. The dialog tells
you how many translations are affected. Removal archives rather than erases: the values are
kept, and adding the same language back restores them. The source language has no Remove
action.

> **Archived values are neither visible nor deployed**
>
> While a language is removed, its values are unreachable from the UI and absent from the
> CDN. Export first if you need the data outside Comvi.

## Next steps

- [Translations](/docs/platform/translations): Add and edit values for your languages.
- [Machine translation](/docs/platform/machine-translation): Fill first drafts for the new language.
- [Import & export](/docs/platform/import-export): Bring in translations from existing files.
