Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

Content languages: adding, showing, translating and removing them

This guide teaches everything about the languages content is written in: what a content language is and how it differs from the language of the interface, how to add one, how to make a siteaccess show it, how editors…

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/guides/content-languages.md

On this page

Content languages: adding, showing, translating and removing them

This guide teaches everything about the languages content is written in: what a content language is and how it differs from the language of the interface, how to add one, how to make a siteaccess show it, how editors translate into it, what "always available" and "main language" mean, how to move content from one language into another, and how to remove a translation or a whole language without losing anything you meant to keep. It ends with complete workflows, from adding German to an English site to retiring a language that was used by mistake, and with the problems people run into.

It is written for administrators and site builders. Read sections 1 and 2 first; after that every section stands on its own. Every setting, button and command below was checked against the code of Exponential 6.0, and every number shown is real: it was read on the demonstration server on 5 October 2026. Nothing in this guide was tried out by adding or removing a language on that server, because removing a language is not something to practise on content people use; section 9 shows how to try it safely.

Guides · Every language at a glance: Languages · Feature reference: Translations and languages · In the installation book: 10.14 Multi-language sites

In short

  • Setup > Languages (content/translations) lists the content languages: for each one how many objects and classes have a translation in it, which siteaccesses show it, whether it can be removed, and a hint when few objects use it or no siteaccess lists it.
  • A content language is not the interface language. Content languages hold your articles; the interface language (Locale) and the .ts translation files decide the words of buttons and menus.
  • Adding a language translates nothing and changes no siteaccess. Translate content in the editor, then add the language to site.ini [RegionalSettings] SiteLanguageList of each siteaccess that should show it.
  • Each siteaccess shows an object in the first language of its SiteLanguageList that the object has. Objects in none of them are hidden, unless they are always available.
  • A language can only be removed when no object and no class has a translation in it, unsaved class edits included. The kernel refuses anything else, so removing a language never deletes content.
  • Removing an object's translation does delete content, for good: all its fields in that language, in every version. Translate first, make the new language the main one, then remove the old translation.
  • An installation holds at most 62 content languages (on 64-bit PHP). Each one is a bit; an object records all its translations in one number.

Contents

1. Content languages and interface languages

Exponential has two kinds of language, and they are set in different places.

Content languageInterface language
What it isA language objects (articles, folders, products, users) can be written inThe language of the texts of the administration and of the templates: buttons, menus, messages
Where the texts areIn the database, one translation per object and languageIn translation files: share/translations/<locale>/translation.ts and the translations/ folder of each extension
Where it is managedSetup > Languages (content/translations), this guidesite.ini [RegionalSettings] Locale per siteaccess, and the user preference of an administrator
What chooses it for a pageSiteLanguageList of the siteaccessLocale of the siteaccess (with TextTranslation=enabled)
How you check itThe content languages pagephp bin/php/ezchecktranslation.php ger-DE

The two are independent. A siteaccess can show German content with an English interface and the other way round. On the demonstration server the test siteaccess admintest_ger does exactly that: its Locale is ger-DE, so the administration speaks German, while its SiteLanguageList is eng-US, so the content it lists is the English content.

This guide is about content languages. For the interface, see Translations and languages.

2. How languages are stored

A content language is one row of the table ezcontent_language: an ID, a locale code such as ger-DE, and a name.

The ID is a single bit. The first language gets ID 2, the next 4, then 8, 16 and so on; ID 1 is reserved. When a language is added, it gets the lowest power of two that no other language has. The demonstration server has:

IDBitLocaleName
21eng-USEnglish (American)
42ger-DEGerman

Until 5 October 2026 it also had eng-GB (English (United Kingdom)) with ID 8, bit 3; it was removed that day (section 3). A language added next would get ID 8 again, the lowest free bit.

An object records its languages in one number, its language mask: the sum of the IDs of the languages it has a translation in. An object in American English and German has the mask 2 + 4 = 6. Bit 0 (the value 1) is the always available flag (section 6), so an always-available object in American English alone has the mask 3. A class has a language mask too, for the translations of its name and description.

Each object has a main language (its initial language), the one it was created in or the one an editor chose later. It is shown where no better translation exists, and it cannot be removed from the object while it is the main one.

At most 62 languages fit: PHP's integers have 64 bits, one is the sign and one the always-available flag (eZContentLanguage::maxCount(), 30 on 32-bit PHP). The page tells you how many more can be added.

Because languages are bits, the kernel answers "which objects are in German?" with one bit test per row (language_mask & 4 > 0), on every database engine it supports.

3. The content languages page

Open Setup > Languages in the administration (/content/translations). The page needs the policy content/translations; administrators have it.

The overview

At the top, five figures:

  • Content languages: how many there are.
  • More can be added: how many of the 62 are left.
  • Need a look: languages few objects use (fewer than 10) or that no siteaccess lists. These are the cases worth a second look; section 8 explains what to do about them.
  • Unused, can be removed: languages no object and no class has.
  • Default for new content in this siteaccess: ContentObjectLocale of the siteaccess you are in, the language new content is created in when nothing else is chosen.

One card per language

Each language has a card with its flag, name and locale, and badges:

BadgeMeaning
Default for new contentIt is ContentObjectLocale of this siteaccess
Shown first by NN siteaccesses list it first in their SiteLanguageList: it is their main language
In no SiteLanguageListNo siteaccess lists it, so no visitor sees content in it (except always-available objects)
Only N objectsFewer than 10 objects have a translation in it
In use / UnusedWhether any object or class has a translation in it

Below the badges, four facts: the number of objects with a translation in the language, and of those how many have it as their main language and how many have nothing else; the number of classes with a name in it; the language ID and its bit; and the siteaccesses that show it, the ones that show it first highlighted.

When a language needs a look, the card says why and what to do, with a link to the right part of the language's own page. The last line of each card says whether it can be removed, and if not, what is in the way.

The checkbox Select for removal is only on for languages nothing uses. Remove selected removes the selected ones, after asking (with JavaScript on) and naming the siteaccesses that still list them. Add language opens the form of section 4.

Siteaccesses and their languages

Below the list, every siteaccess of site.ini [SiteAccessSettings] AvailableSiteAccessList with its SiteLanguageList in order, as that siteaccess reads it with its own overrides. The first language of each is highlighted. A locale a siteaccess lists that is not a content language is marked in red: that siteaccess asks for a language that does not exist, and shows nothing in it.

Only siteaccesses in AvailableSiteAccessList are counted. A settings folder settings/siteaccess/<name>/ that is not listed there serves no page, so its SiteLanguageList does not show anything either.

The page of one language

Click a language's name (/content/translations/<id>). The page shows the same figures larger, the siteaccesses that show it, a section Removing this language that says whether it can be removed and, if not, the steps to take it out of use, then three folding sections:

  • Objects with a translation in it: up to 25, the ones changed last first, each with its class, main language, all its languages and whether it is always available. The object names link to the objects.
  • Classes with a name in it: every class that has the language, including class edits that were started and never stored (marked unsaved edit); those count for the kernel too.
  • The locale: how dates, numbers and money are written in this language, from share/locale/<code>.ini.

The Remove button on this page is off while anything uses the language, and also for the language the administration interface itself runs in.

Worked example: the overview on the demonstration server

On 5 October 2026, after the clean-up described below, the page showed:

LanguageObjects (main, only)ClassesShown byCan be removed
English (American), eng-US367 (367, 324)81 (74 stored, 7 unsaved edits)14 siteaccesses, first in 13No
German, ger-DE53 (10, 10)6bold_ger, first thereNo

Earlier that day there was a third card, English (United Kingdom), eng-GB: the case this page was redesigned to make obvious. The newsletter extension's class packages named eng-GB, so importing them had created the language (section 12), and six classes had a name in it, while no siteaccess of AvailableSiteAccessList listed it (only the unused settings folder settings/siteaccess/eng did). Its card said In no SiteLanguageList and Not shown by any siteaccess and linked to the six classes. Once their eng-GB translations were moved to eng-US and the leftovers of section 9 were cleaned, the card read Unused, and the language was removed.

4. Adding a language

  1. Open Setup > Languages and press Add language.
  2. Under Language and country, type part of the name, the code or the country into Find a language (german, ger, austria, deutsch all work); the list narrows as you type, and Enter picks the first match. Without JavaScript the list is a plain list box, sorted by name. The list holds the locales of share/locale without their variations (132 on a standard installation since 6.0.15; the full list with native names, regions and support levels is in Languages); languages already added are shown but cannot be chosen. Each entry reads English name · native name · country · code, for example Persian · فارسی · Iran · fas-IR; the country is left out where the name already gives it (German (Austria) · Deutsch (Österreich) · ger-AT), and the search still finds it.
  3. Press Add language.

The page returns to the overview with the message German (ger-DE) was added. The language gets the next free bit, its name is taken from the locale (German), and the content view cache is cleared, because lists that filter by language change.

A custom language

For a language that is not in the list, choose Custom at the top of the list and fill in:

  • Name of custom translation: the name the administration shows, such as Deutsch (Schweiz).
  • Locale for custom translation: three letters for the language, a dash, two for the country: ger-CH.

A file share/locale/<code>.ini must exist for that code, because the locale also decides how dates and numbers are written. If it does not, the form comes back with There is no locale ger-CH in share/locale and keeps what you entered. Copy the nearest locale file (ger-DE.ini) to the new name and adjust it, then try again. A code that is not in the form xxx-YY comes back with an explanation of the form.

What adding does not do

It translates nothing, and it changes no siteaccess: no visitor sees anything new yet. The form says so, and lists the next steps: translate content (section 7), and list the language in SiteLanguageList (section 5).

Adding a language that already exists does nothing and says German (ger-DE) is already a content language. When all 62 bits are taken the page says so and adds nothing.

5. Showing a language: SiteLanguageList and siteaccesses

What a visitor sees is decided per siteaccess, in settings/siteaccess/<siteaccess>/site.ini.append.php, group [RegionalSettings]:

SettingWhat it does
SiteLanguageList[]The content languages this siteaccess shows, most preferred first. An object is shown in the first of these it has.
ShowUntranslatedObjectsdisabled (shipped): objects in none of those languages are hidden, unless always available. enabled: they are shown in their main language.
ContentObjectLocaleThe default language for new content, and the fallback when SiteLanguageList is empty; shipped as eng-US. Do not change it on a site with content without reading the warning in settings/site.ini.
ContentObjectFallbackLocaleThe last resort when no content language is prioritized and no ContentObjectLocale is set, for example while the setup wizard or a script creates classes before the site has languages; shipped as eng-US (it used to be a hard-coded eng-GB). See Content language fallback.
LocaleThe interface language and the locale for dates and numbers.
TextTranslationenabled to translate interface texts into Locale.

A siteaccess for German content with English as the fallback:

# settings/siteaccess/site_de/site.ini.append.php
[RegionalSettings]
Locale=ger-DE
ContentObjectLocale=ger-DE
SiteLanguageList[]
SiteLanguageList[]=ger-DE
SiteLanguageList[]=eng-US
ShowUntranslatedObjects=disabled
TextTranslation=enabled

The empty SiteLanguageList[] line first clears what settings/site.ini or an override gave the list, so this siteaccess has exactly the languages listed. Here a German visitor sees the German translation where there is one and the American English one otherwise; an object only in British English is hidden.

A multi-language site usually has one public siteaccess per language, matched by a URL element (/de/) or a host name, all sharing one database and listed in site.ini [SiteAccessSettings] RelatedSiteAccessList[]. How to set up matching is in the installation book, 10.2 Siteaccesses and site addresses, and the multi-language settings in 10.14. For links from one language version of a page to another, [RegionalSettings] TranslationSA[] names the translation siteaccesses, and the template operator language_switcher with the module switchlanguage/to builds the links.

After changing a SiteLanguageList, clear the INI and content caches (section 10). The content languages page reads the lists fresh on every visit, so its siteaccess table shows the change at once.

6. Main language and always available

The main language of an object is its initial language: the language it was created in, or the one an editor chose later in the object's Translations window (radio button in the Main column, then Set main). The main translation cannot be removed from an object; another translation has to become the main one first. On the content languages page, the figure in it as main language counts objects with this language as their main one.

Always available is a flag per object, bit 0 of its language mask. An always-available object is shown in its main language on a siteaccess that lists none of its languages, where it would otherwise be hidden. Folders, users, user groups and media are usually always available, so that a German site still finds the user who wrote an article, or the image folder. Set it in the object's Translations window: Use the main language if there is no prioritized translation, then Update. A class has a default for new objects (Always available in the class edit).

On a language's own page, objects that are always available are marked in the object list.

7. Translating content in the editor

Editors need the policies content/edit (or content/translate) and content/create for the language; all three can be limited by Language in the role editor, so a translator may be allowed German and nothing else.

To translate an object:

  1. Open the object and press Edit. When it can be edited in more than one language, the page Edit &lt;name&gt; opens.
  2. Under New translation, choose the language to add. Under Translate based on, choose the existing translation to start from (its fields are copied into the new one), or None to start empty.
  3. Press OK, translate the fields and Publish.

The object now has one more language; the content languages page counts it at once.

Each object's Translations window (in the full view of the administration) lists its languages with the main one marked, and has Remove selected for translations, Set main for the main language and the always-available setting. Classes have the same window on their page under Setup > Classes, for the translations of the class name and description.

8. Moving content from one language into another

This is what to do when content was created in the wrong language, for example a set of newsletter objects created in British English (eng-GB) on a site that shows American English (eng-US), so the objects are in no SiteLanguageList and never show. The content languages page points such a language out: In no SiteLanguageList, Not shown by any siteaccess, or Only N objects, with a link to the list of objects on the language's page.

For each object on that list:

  1. Translate it into the right language. Edit, choose New translation eng-US, Translate based on eng-GB, adjust the text if needed, Publish. Nothing is lost: both translations exist now.
  2. Make the right language the main one. In the object's Translations window, select eng-US in the Main column and press Set main.
  3. Remove the wrong translation. In the same window, select eng-GB and press Remove selected, then confirm. This deletes the eng-GB fields of the object in every version (section 9), which is what you want once step 1 has copied them.

Then the classes: for each class on the language's page, open it under Setup > Classes; in its Translations window make another language the main one if eng-GB is (Set main), then remove the eng-GB translation. A class marked unsaved edit is a class edit that was started and never stored: open the class, press Edit, and Store or Cancel the edit; until then it counts as a class in that language.

When the language's page shows 0 objects and 0 classes, it can be removed (section 9), or kept for later.

No shipped console command moves translations in bulk; the steps above are the supported way, one object at a time, with the editor's permissions and workflows applying. For hundreds of objects, write a script against the kernel API, try it on a copy of the database, and keep a backup (10.9 Backups and restore).

The other way out, when the content was right and only the settings were missing, is to add the language to the SiteLanguageList of the siteaccess that should show it (section 5).

9. Removing translations and languages safely

What removes content, and what does not

ActionWhereWhat it deletesCan it be undone?
Remove a languageSetup > LanguagesOnly the language row. The kernel refuses while any object or class has a translation in it.Add it again; it may get a different bit, which is fine because nothing used the old one
Remove an object's translationThe object's Translations windowAll fields and names of the object in that language, in every version, and every version that was started in that languageNo, only from a backup
Remove a class's translationThe class's Translations windowThe name and description of the class in that languageTranslate the class again

So removing a language never deletes content: it is refused until nothing uses the language. The content that matters is removed one translation at a time, in each object's own window, after a confirmation page; the main translation cannot be removed at all.

What blocks removing a language

  • Objects with a translation in it (any status: published, draft, archived). The card shows how many.
  • Classes with a name in it, including unsaved class edits. The language's page lists them.

What the kernel does not count, but what still carries the language's bit or locale in the database:

  • Class texts stored per locale (serialized name lists of classes, class attributes, class groups and states), for example an empty eng-GB description next to the eng-US one.
  • Name rows of draft versions of objects whose published version has no translation in the language.
  • URL aliases whose language mask still has the bit, for example aliases of removed nodes.

Convert or clean these before removing a language, or a language added later with the same ID inherits them. How, with the cache list to clear afterwards, is in Content language fallback.

What the page warns about but allows:

  • Siteaccesses that still list it. After removal they would ask for a language that does not exist; the page names them, and the confirmation asks you to take the language out of their SiteLanguageList too.
  • The interface language. The language's own page does not offer removal for the language the administration runs in.

Removing a language

  1. On Setup > Languages, check that the card says Unused and Can be removed: no object and no class has a translation in it.
  2. Tick Select for removal and press Remove selected. With JavaScript on, a confirmation lists the languages and the siteaccesses that still list them.
  3. The page reports English (United Kingdom) (eng-GB) was removed., and, when siteaccesses still list it, names them.
  4. Take the locale out of those siteaccesses' SiteLanguageList, clear the INI cache and the content_language cache (section 10), and clear Velocity's response cache too: both Apache and Velocity keep the list of languages.

If something used the language after all (an object was translated meanwhile), the page reports was not removed: N objects and M classes still have a translation in it. and nothing changes.

Trying it without risk

To see the add and remove forms work, use a language no content has and remove it again at once: add, for example, epo-EO (Esperanto), check that its card says Unused and In no SiteLanguageList, select it and remove it. Compare the list of languages before and after; nothing else changes. Never practise on a language your content uses.

To try translations themselves, use the test language Lorem Ipsum (qlo-ZZ): it is shipped for this, installed nowhere, and exp:loremipsum fills a translation in it with lorem ipsum text. See The test language Lorem Ipsum.

10. Commands for the shell

Languages are added and removed from the page; no shipped console command adds or removes a content language or moves translations. What the shell is for is caches, URL aliases and the interface translations. Run from the installation root; add --allow-root-user when you run as root.

CommandWhen
php bin/php/ezcache.php --clear-id=content_languageThe cached list of content languages: after removing a language, and after the table was changed by hand or by a script; then ./console exp:velocity cache clear --apply, so the pages on port 8080 drop it too
php bin/php/ezcache.php --clear-tag=iniAfter changing a SiteLanguageList or any [RegionalSettings] setting
php bin/php/ezcache.php --clear-tag=contentAfter a change to languages or translations, so lists and pages are drawn again (adding or removing a language from the page does this itself)
./console exp:updateniceurls --update-nodes or php bin/php/updateniceurls.php -s adminRebuild the URL aliases of all nodes, for example after many translations were added by a script; aliases are kept per language
./console exp:verify_aliasesCheck the URL alias table; --fix repairs what is safe to repair, --sql shows the queries
php bin/php/ezchecktranslation.php ger-DEHow complete the interface translation of a locale is (not content)
php bin/php/ezgeneratetranslationcache.phpBuild the cache of the interface translation files; --ts-list limits it to some locales
php bin/php/ezcache.php --clear-tag=i18nAfter changing a .ts interface translation file
./console exp:loremipsum <object id> --dry-runFill the translation of objects in the test language Lorem Ipsum with lorem ipsum text, once it is added; guide

Velocity (the application server on port 8080) keeps its own response cache: after any of these, also run ./console exp:velocity cache clear --apply.

11. Workflows, step by step

Adding German to an English site

The site has one public siteaccess, site, with SiteLanguageList[]=eng-US.

  1. Add the language. Setup > Languages > Add language, type german, choose German · Deutsch · Germany · ger-DE, press Add language. The overview shows a new card German, ger-DE, Not used yet, In no SiteLanguageList.
  2. Give German a siteaccess. Copy settings/siteaccess/site to settings/siteaccess/site_de, and in its site.ini.append.php set the [RegionalSettings] of section 5 (Locale=ger-DE, SiteLanguageList[]=ger-DE then eng-US). Add site_de to AvailableSiteAccessList[] and RelatedSiteAccessList[] in settings/override/site.ini.append.php, and set up the matching (a /de URL element or a host name, see 10.2).
  3. Clear the caches: php bin/php/ezcache.php --clear-tag=ini,content, then ./console exp:velocity cache clear --apply. The German card now shows Shown first by 1 and the chip site_de.
  4. Translate. Start with the front page and the main menu folders (section 7). Until a page is translated, site_de shows its English translation, because eng-US is the second language of its list.
  5. Mark the shared objects always available where they are not already (users, media folders), so they show on site_de even without a German translation.
  6. Give translators a role with content/translate and content/edit limited to the language German.
  7. Check: open /de/ (or the German host), and the German card's object count on the languages page.

Retiring a language that was used by mistake

The case of section 8: a language with few objects, or in no SiteLanguageList.

  1. Open the language's page from its card. Note the objects and classes listed.
  2. Move each object (section 8, steps 1 to 3) and each class into the right language. Store or cancel every unsaved edit class.
  3. Reload the page until it says No object and no class has a translation in ... so it can be removed.
  4. Remove the language on the overview (section 9) and take it out of every SiteLanguageList.
  5. Clear the INI and content caches.

Making a hidden language visible

When the objects are right and only no siteaccess lists their language: add the locale to the SiteLanguageList of the siteaccess that should show it, at the position you want (first means preferred), clear the INI and content caches, and check that the card now shows that siteaccess.

12. Troubleshooting

An object disappeared from a site after a language change. The siteaccess lists none of the object's languages and the object is not always available. Look at the object's languages on its Translations window and at the siteaccess table of the languages page. Add a translation, make the object always available, or add the language to the SiteLanguageList.

"Remove selected" does nothing for a language. Its checkbox is off because objects or classes still have it; the card's last line says how many. If the page says 0 objects but some classes, look for unsaved edit classes on the language's page: a class edit that was never stored counts.

The class count on the card is higher than the number of classes in Setup > Classes. The difference is unsaved class edits, which the kernel counts too. The language's page lists them marked unsaved edit.

The page says a language is in no SiteLanguageList, but a settings folder lists it. Only siteaccesses in AvailableSiteAccessList serve pages, so only they count. Either add the siteaccess there or treat the language as not shown.

A siteaccess lists a locale marked "not a content language". That language was never added, or was removed. Add it on the page, or take it out of the list.

The add form says the locale is not in share/locale. Custom languages need a locale file; copy the nearest one (section 4).

"No language was added: this installation already has the most languages it can hold." All 62 bits are taken. Remove a language nothing uses first.

A language appeared that nobody added. Importing a class package (an extension's install step, or Setup > Packages) creates every language its class definitions name. That is how eng-GB got onto the demonstration server: the cjw_newsletter class packages before version 4.2.1 named it. An old kickstart.ini with Languages[]=eng-GB, or a site created before the fallback became eng-US (Content language fallback), does the same. The language's card shows what uses it; move that content (section 8) and remove the language (section 9).

After adding a language the editor does not offer it. The editor offers only languages the user may use: check the Language limitation of the user's content/edit, content/translate and content/create policies.

German texts in the interface, English content (or the other way round). That is the separation of section 1: set Locale for the interface, SiteLanguageList for content.

The page looks different on port 8080 than through Apache. Velocity caches responses and keeps PHP classes loaded: run ./console exp:velocity cache clear --apply, and after an update of the kernel ./console exp:velocity deploy.

References

  • Languages: every locale and administration translation, support levels, adding a locale or a translation, right-to-left status.
  • Translations and languages: interface translations, HTML language tags, languages in a new installation.
  • 10.14 Multi-language sites and 10.2 Siteaccesses and site addresses in the installation book.
  • The content model and editing content: objects, versions and the editor.
  • Operating a site: which caches to clear when.
  • Admin list paging: the page size of the language list.
  • Settings: settings/site.ini [RegionalSettings] (every setting with its comment), [SiteAccessSettings] AvailableSiteAccessList and RelatedSiteAccessList.
  • Code: kernel/classes/ezcontentlanguage.php (adding, removing, the counts, the masks, maxCount()), kernel/private/classes/views/content/translations.php (the page: counts per language, siteaccess lists, the hints), kernel/classes/ezcontentobject.php (removeTranslation()), templates design/admin4/templates/content/translations.tpl, translationview.tpl and translationnew.tpl (the same in design/admin), tests tests/tests/kernel/classes/expContentLanguagesOverviewTest.php (no database) and expContentLanguagesCountsLiveTest.php (read-only, on an installation).

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x