Your goal was that descriptive text in the views are not mixed with the program code. So they are uncomplicatedly changeable via the Joomla backend. This is possible for every user. Even the one who is not familiar with the program code. By the way, this is the prerequisite for your extension to be multilingual! For this reason you did not enter the real texts directly into the program code, but used language strings instead. Specifically, by descriptive texts I mean the texts that are displayed in the fronend of the website via browser, for example with the breadcrumb navigation "Current page". You had prepared everything so that you use special files. These are easily changeable. So far you have seen cryptic texts in the browser views. In this part we translate the unattractive language strings into human readable words.
Even if your target audience speaks English and you only support this language it is important to use a language file for texts you display in the front-end or back-end of the component. This way it is possible for users to overwrite texts via language override[^docs.joomla.org/j3.x:language_overrides_in_joomla] without editing the source code. Under some circumstances a user prefers to write first name instead of name in the column header.
For impatient people: View the changed program code in the Diff View[^codeberg.org/astrid/j4examplecode/compare/t7...t8] and copy these changes into your development version.
Step by step
The frontend view and the administration area each use their own language files. Unlike the frontend, where there is only one, the backend needs two - *.sys.ini
and *.ini
. Briefly explained: The file with the extension sys.ini
is used to translate the XML installation file as well as the menu elements. The ini
is responsible for the rest. This has the advantage that during the installation and for the construction of the menu only the loading of small text files is necessary. A disadvantage is that some language strings have to be entered twice. You can find out more in the article International Enhancements which has a section on the file *.sys.ini
[^docs.joomla.org/international_enhancements_for_version_1.6#the_new_.sys.ini].
The addition of the English language files is mandatory. All other languages are optional. The reason for this is that if a file is missing, the English version is used by default. If a Frenchman installs the extension - which contains German and English language files - on his Joomla with the default language French, the texts will be displayed in English. Note: This only applies to missing language files. A missing language key in a non-English language file will not be replaced with the key from the English file. This means that it is mandatory that each file is complete. Joomla does not search in different language version for a language string missing in a file.
Side Note: Special features
Would you like to see exactly how the ini file is parsed? At php.net[^php.net/manual/en/function.parse-ini-file.php] you will find the description of the function that does this work.
Commenting
You can mark a line as a comment using a semicolon ;
.
; Joomla Project
; (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
; License GNU General Public License version 2 or later; see LICENSE.txt
Note : All ini files need to be saved as UTF-8
....
Escaping
There are characters that have a special meaning - for example, the inverted commas "
that mark the beginning and end of the translation. This meaning can be cancelled with a backslash \
.
...
COM_CONTACT_CONTACT_REQUIRED="<strong class=\"red\">*</strong> Required field"
...
Language strings in Joomla have in the past used the string "QQ" to avoid double quotes within the language INI files. This was a short-term solution. Older PHP versions were not able to handle double quotes. For more information, see the PR 19024.[^github.com/joomla/joomla-cms/issues/19024]
Variables
Sometimes the output of the language string depends on a variable. The function Text::sprintf
ensures that you do not have to compose the text in a complicated way in the programme code. Instead of the variable in the language file, enter a character with the prefix %
. For example, you can use %s
.
Are you interested in the exact structure of the function
Text::sprintf
? You can find it in Joomla in the filelibraries/src/Language/Text.php
.
...
COM_CONTACT_CHECKED_OUT_BY="Checked out by %s"
...
In the PHP code, the call then looks like this.
...
Text::sprintf('COM_CONTACT_CHECKED_OUT_BY', $checkoutUser->name)
...
The value of $checkoutUser->name
is inserted instead of the first variable in the language string. Here in the example instead of %s
.
Is there more than one variable? You can specify which variable belongs to which language string. For example, use
%1$s, %2$s
as inJLIB_DATABASE_ERROR_STORE_FAILED="%1$s: :store failed<br>%2$s"
. The values are assigned in the order indicated in the number after the "%" character.
singular/singular
There is singular or singular and plural or plural and the Joomla language strings support this.
Let us take the call
...
$message = Text::plural('COM_FOOS_N_ITEMS_FEATURED', count($ids));
...
as an example.
Depending on whether count($ids)
has the value 1
or 2
the language string COM_FOOS_N_ ITEMS_FEATURED_1
or COM_FOOS_N_ ITEMS_FEATURED_2
is used. If count($ids)
has neither 1
nor 2
, COM_FOOS_N_ ITEMS_FEATURED
is used.
...
COM_FOOS_N_ITEMS_FEATURED="%d foos featured."
COM_FOOS_N_ITEMS_FEATURED_1="Foo featured."
COM_FOOS_N_ITEMS_FEATURED_2="Two foos featured."
...
New files
Create six files to support the German language in addition to English. Each file is structured as follows: One language string is inserted per line. The left side of the equal sign in the language string, for example COM_FOOS_ CONFIGURATION"
in COM_FOOS_ CONFIGURATION="Foo Options"
, is always in upper case. Normally the extension name is at the beginning, in our case it is COM_FOOS
. After that you ideally add a short description. Here you describe briefly what this string is used for. Make sure that you do not use spaces. Only letters and underscores are allowed. The right side of the language string, for example Foo Options"
in COM_FOOS_ CONFIGURATION = "Foo Options"
, is the actual text that will be displayed on the site. When your extension is translated into another language, the translator only changes this right side of the language string in his language file. The right side is enclosed in quotation marks.
administrator/components/com_foos/language/de-DE/com_foos.ini
We add the German language version for the administration area with the files 'administrator/components/com_foos/language/en-DE/com_foos.ini' and 'administrator/components/com_foos/language/en-DE/com_foos.sys.ini'.
Don't be confused if you see a lot of texts in the sample data. These are not all used at the moment. I'm already inserting the text for the future chapters here.
administrator/components/com_foos/language/de-DE/com_foos.ini
<!-- htttps://codeberg.org/astrid/j4examplecode/raw/branch/t8/src/administrator/components/com_foos/language/de-DE/com_foos.ini -->
COM_FOOS="[PROJECT_NAME]"
COM_FOOS_CONFIGURATION="Foo Optionen"
COM_FOOS_FOOS="Foos"
COM_FOOS_CATEGORIES="Kategorien"
...
Naming conventions: Each language file is marked with an abbreviation, which is defined in ISO-639[^en.wikipedia.org/wiki/iso_639] and ISO-3166[^en.wikipedia.org/wiki/iso_3166]: The first two lower case letters name the language. For German this is
de
anden
for English. After the hyphen, the two capital letters indicate the country. For example, Swiss German can be distinguished fromDE
byCH
or Austrian byAT
. A folder namedde-CH
contains the translation for Switzerland andde-AT
the Austrian variant.
administrator/components/com_foos/language/de-DE/com_foos.sys.ini
As mentioned before, you need two language files for the backend: one ending with .ini
and one ending with sys.ini
. The sys.ini
is primarily used during installation and for displaying the menu items and the ini
for everything else.
administrator/components/com_foos/language/de-DE/com_foos.sys.ini
<!-- htttps://codeberg.org/astrid/j4examplecode/raw/branch/t8/src/administrator/components/com_foos/language/de-DE/com_foos.sys.ini -->
COM_FOOS="[PROJECT_NAME]"
COM_FOOS_XML_DESCRIPTION="Foo Komponente"
...
COM_FOOS_INSTALLERSCRIPT_PREFLIGHT="<p>Alles hier passiert vor der Installation / Aktualisierung / Deinstallation der Komponente</p>"
COM_FOOS_INSTALLERSCRIPT_UPDATE="<p>TDie Komponente wurde aktualisiert</p>"
COM_FOOS_INSTALLERSCRIPT_UNINSTALL="<p>Die Komponente wurde deinstalliert</p>"
COM_FOOS_INSTALLERSCRIPT_INSTALL="<p>Die Komponente wurde installiert</p>"
COM_FOOS_INSTALLERSCRIPT_POSTFLIGHT="<p>Alles hier passiert nach der Installation / Aktualisierung / Deinstallation der Komponente</p>"
...
administrator/components/com_foos/language/en-GB/com_foos.ini
I had already written it: The English versions of the language files should always be available as a fallback.
administrator/components/com_foos/language/en-GB/com_foos.ini
<!-- htttps://codeberg.org/astrid/j4examplecode/raw/branch/t8/src/administrator/components/com_foos/language/en-GB/com_foos.ini -->
COM_FOOS="[PROJECT_NAME]"
COM_FOOS_CONFIGURATION="Foo Options"
COM_FOOS_FOOS="Foos"
COM_FOOS_CATEGORIES="Categories"
...
administrator/components/com_foos/language/en-GB/com_foos.sys.ini
We also add the file administrator/components/com_foos/language/en-GB/com_foos.sys.ini
as a fallback for all non-German or English Joomla installations.
administrator/components/com_foos/language/en-GB/com_foos.sys.ini
<!-- htttps://codeberg.org/astrid/j4examplecode/raw/branch/t8/src/administrator/components/com_foos/language/en-GB/com_foos.sys.ini -->
COM_FOOS="[PROJECT_NAME]"
COM_FOOS_CONFIGURATION="Foo Options"
...
COM_FOOS_INSTALLERSCRIPT_PREFLIGHT="<p>Anything here happens before the installation/update/uninstallation of the component</p>"
COM_FOOS_INSTALLERSCRIPT_UPDATE="<p>The component has been updated</p>"
COM_FOOS_INSTALLERSCRIPT_UNINSTALL="<p>The component has been uninstalled</p>"
COM_FOOS_INSTALLERSCRIPT_INSTALL="<p>The component has been installed</p>"
COM_FOOS_INSTALLERSCRIPT_POSTFLIGHT="<p>Anything here happens after the installation/update/uninstallation of the component</p>"
...
components/com_foos/language/de-DE/com_foos.ini
In the frontend there is only the .ini
- so no sys.ini
. The file components/com_foos/language/en-DE/com_foos.ini
implements the German language.
components/com_foos/language/de-DE/com_foos.ini
<!-- htttps://codeberg.org/astrid/j4examplecode/raw/branch/t8/src/components/com_foos/language/de-DE/com_foos.ini -->
COM_FOOS_NAME="Vorame: "
...
components/com_foos/language/en-GB/com_foos.ini
We add the English version to the file components/com_foos/language/en-GB/com_foos.ini
so that it is used as a fallback in all languages other as German.
components/com_foos/language/en-GB/com_foos.ini
<!-- htttps://codeberg.org/astrid/j4examplecode/raw/branch/t8/src/components/com_foos/language/en-GB/com_foos.ini -->
COM_FOOS_NAME="Surname: "
...
In the next chapters more language strings will be added. I will not mention them separately there. I have already integrated most of them into the sample files in this lesson. This way I avoid that the files appear in the diff view and blow it up unnecessarily. Specifically, I mean the diff view of the program code of the different chapters on Github, which I refer to here in the text.
Modified files
administrator/components/com_foos/foos.xml
To make sure that the language files are copied to Joomla Core when the extension is installed, we add the <folder>language</folder>
entry for the frontend and the backend to the manifest.
administrator/components/com_foos/foos.xml
</uninstall>
<!-- Frond-end files -->
<files folder="components/com_foos">
<folder>language</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<files folder="administrator/components/com_foos">
<filename>foos.xml</filename>
<folder>forms</folder>
<folder>language</folder>
<folder>services</folder>
<folder>sql</folder>
<folder>src</folder>
Where are the language files ideally stored?
Joomla's own components store the files for the administration area in the folder /administrator/language/en-GB/
and those for the site in the folder /language/en-GB/
. This is the first place Joomla looks for the language files. For this reason, it was common for extension developers to put their files here. Sometimes it is more straightforward to put them in your own component folder. In our example, this is the folder administrator/components/com_foos/language/en-GB/
for the backend and components/com_foos/language/en-GB/
for the frontend. This is the place where Joomla looks for the language file if it doesn't find anything suitable in the Joomla Core directories /administrator/language/en-GB /
and / language/en-GB
respectively.
You want to store your language files in the same directory as the Joomla core extensions? To place your files together with Joomla's own language files, you add the <language>
tag to the installation file. Here is an example from com_contact
...
<files folder="components/com_contact">
...
</files>
<languages folder="site">
<language tag="en-GB">language/en-GB.com_contact.ini</language>
</languages>
<administration>
...
<languages folder="admin">
<language tag="en-GB">language/en-GB.com_contact.ini</language>
<language tag="en-GB">language/en-GB.com_contact.sys.ini</language>
</languages>
</administration>
...
where you need to adjust the value of the folder
parameter to your structure:
...
<files folder="components/com_foos">
...
</files>
<languages folder="language">
<language tag="en-GB">language/en-GB.com_foos.ini</language>
</languages>
<administration>
...
<languages folder="administrator/language">
<language tag="en-GB">language/en-GB.com_foos.ini</language>
<language tag="en-GB">language/en-GB.com_foos.sys.ini</language>
</languages>
</administration>
...
components/com_foos/tmpl/foo/default.php
One last step is still missing. The own use of the language strings. So far we have printed the name without a label in the frontend via echo $this->item->name;
. Now we add a label that takes different languages into account. The following code causes the string that is entered in the corresponding language file to be printed in the frontend. This is done by the command Text::_('COM_FOOS_NAME')
. If there is a Spanish language file with the entry COM_FOOS_FIELD_NAME_LABEL="Nombre"
and the Spanish language is active in the frontend, then Nombre
is printed. If the German language is set and there is a German language file with the entry COM_FOOS_FIELD_NAME_LABEL="Name"
, the word Name
is displayed. If the Spanish language is active without a Spanish language file, the English language file is used.
components/com_foos/tmpl/foo/default.php
\defined('_JEXEC') or die;
?>
<?php
echo $this->item->name;
use Joomla\CMS\Language\Text;
echo Text::_('COM_FOOS_NAME') . $this->item->name;
Test your Joomla component
- install your component in Joomla version 4 to test it: Copy the files in the
administrator
folder into theadministrator
folder of your Joomla 4 installation. Copy the files in thecomponents
folder into thecomponents
folder of your Joomla 4 installation. A new installation is not necessary. Continue using the files from the previous part. If you do a new installation, you will notice that the hints in the installation script are now translated.
In case of discovery, the texts may be hidden[^github.com/joomla/joomla-cms/issues/36343].
- open the view of your component in the administration area and frontend and make sure that the texts are readable and not cryptic anymore.
-
try out the new feature. Create language files for different languages and change the default language in Joomla. Make sure that Joomla translates correctly.
-
create a language override and make sure that it is used.
Webmentions