Supported Locales
Total CMS ships a curated registry of locale codes used by:
- Default locale (Settings → Internationalization → Default Locale) — drives PHP
intl, CakePHP I18n, and Faker for date / number / currency formatting, the admin UI translation language, and (Pro) the default tab on localized content fields. This single setting is the source of truth for$config->locale— there is no separate formatting-locale override. - Available content locales (Settings → Internationalization → Available Content Locales) — Pro edition. List of codes that show up as tabs in
localizedtext,localizedtextarea, andlocalizedstyledtextfields.
All codes use the mixed-case POSIX format (lowercase language, uppercase region, underscore separator: en_US, pt_BR, zh_CN). Bare language codes (de, fr, pt) are also valid and stand for "any region speaking that language" — useful when a site doesn't care about regional variants.
Label conventions
The native name column is what operators see on locale-picker tabs and in the admin form. Two patterns:
- Single-variant languages (Czech, Japanese, Italian, Polish, etc.) get just the language name —
Čeština,日本語,Italiano. The country is redundant when only one variant exists. - Multi-variant languages (English, German, French, Spanish, Portuguese, Arabic, Chinese) get the language name plus the ISO 3166 alpha-2 country code in parens —
English (US),Português (BR),Deutsch (AT). Keeps the localized-field tab strip compact while still distinguishing variants.
The settings UI picker appends the full POSIX code in square brackets to every label so operators see the code when configuring (English (US) [en_US]). The English name column is for documentation reference and English-speaking integrators who don't recognize a native script.
Registry
| Code | Native name (UI) | English name | Direction |
|---|---|---|---|
af_ZA |
Afrikaans | Afrikaans (South Africa) | LTR |
ar |
العربية | Arabic | RTL |
ar_SA |
العربية (SA) | Arabic (Saudi Arabia) | RTL |
bn_BD |
বাংলা | Bengali (Bangladesh) | LTR |
cs_CZ |
Čeština | Czech (Czechia) | LTR |
da_DK |
Dansk | Danish (Denmark) | LTR |
de |
Deutsch | German | LTR |
de_AT |
Deutsch (AT) | German (Austria) | LTR |
de_CH |
Deutsch (CH) | German (Switzerland) | LTR |
de_DE |
Deutsch (DE) | German (Germany) | LTR |
el_GR |
Ελληνικά | Greek (Greece) | LTR |
en |
English | English | LTR |
en_AU |
English (AU) | English (Australia) | LTR |
en_CA |
English (CA) | English (Canada) | LTR |
en_GB |
English (GB) | English (United Kingdom) | LTR |
en_SG |
English (SG) | English (Singapore) | LTR |
en_US |
English (US) | English (United States) | LTR |
es |
Español | Spanish | LTR |
es_ES |
Español (ES) | Spanish (Spain) | LTR |
es_MX |
Español (MX) | Spanish (Mexico) | LTR |
fa_IR |
فارسی | Persian (Iran) | RTL |
fi_FI |
Suomi | Finnish (Finland) | LTR |
fr |
Français | French | LTR |
fr_CA |
Français (CA) | French (Canada) | LTR |
fr_FR |
Français (FR) | French (France) | LTR |
he_IL |
עברית | Hebrew (Israel) | RTL |
hi_IN |
हिन्दी | Hindi (India) | LTR |
hu_HU |
Magyar | Hungarian (Hungary) | LTR |
id_ID |
Bahasa Indonesia | Indonesian (Indonesia) | LTR |
it_IT |
Italiano | Italian (Italy) | LTR |
ja_JP |
日本語 | Japanese (Japan) | LTR |
jv_ID |
Basa Jawa | Javanese (Indonesia) | LTR |
km_KH |
ខ្មែរ | Khmer (Cambodia) | LTR |
ko_KR |
한국어 | Korean (South Korea) | LTR |
ms_MY |
Bahasa Melayu | Malay (Malaysia) | LTR |
nl_NL |
Nederlands | Dutch (Netherlands) | LTR |
no_NO |
Norsk | Norwegian (Norway) | LTR |
pa_IN |
ਪੰਜਾਬੀ | Punjabi (India) | LTR |
pl_PL |
Polski | Polish (Poland) | LTR |
pt |
Português | Portuguese | LTR |
pt_BR |
Português (BR) | Portuguese (Brazil) | LTR |
pt_PT |
Português (PT) | Portuguese (Portugal) | LTR |
ro_RO |
Română | Romanian (Romania) | LTR |
ru_RU |
Русский | Russian (Russia) | LTR |
sr_Latn_RS |
Srpski (Latn) | Serbian (Latin, Serbia) | LTR |
sr_Cyrl_RS |
Српски (Ћирилица) | Serbian (Cyrillic, Serbia) | LTR |
sv_SE |
Svenska | Swedish (Sweden) | LTR |
sw_KE |
Kiswahili | Swahili (Kenya) | LTR |
ta_IN |
தமிழ் | Tamil (India) | LTR |
th_TH |
ไทย | Thai (Thailand) | LTR |
tl_PH |
Tagalog | Tagalog (Philippines) | LTR |
tr_TR |
Türkçe | Turkish (Turkey) | LTR |
uk_UA |
Українська | Ukrainian (Ukraine) | LTR |
ur_PK |
اردو | Urdu (Pakistan) | RTL |
vi_VN |
Tiếng Việt | Vietnamese (Vietnam) | LTR |
zh_CN |
中文 (CN) | Chinese (Mainland) | LTR |
zh_TW |
中文 (TW) | Chinese (Taiwan) | LTR |
How the registry is used
| Surface | Reads from | What it shows |
|---|---|---|
| Settings UI dropdowns (default locale) | LocaleRegistry::options() |
Native name [code] — e.g. Deutsch (DE) [de_DE] |
| Settings UI list field (available content locales) | LocaleRegistry::options() |
Same — autocomplete suggests matching codes as the operator types |
| Localized field tabs in the admin form | LocaleRegistry::expand() (driven by Config::$i18n['available']) |
Native name on the tab; dir propagates to inputs |
Twig: cms.locale.languages() |
LocaleRegistry::all() |
{ "Deutsch": "de", ... } (label → code map for custom pickers) |
The registry is the single source of truth for what locales T3 understands. Adding a new locale anywhere means adding to the registry (src/Domain/Locale/LocaleRegistry.php) — every consumer picks it up automatically.