Your goal was to make your extension multilingual! That's why you didn't enter the texts directly into the program code. Specifically, I mean the texts that are displayed in the browser. You had prepared everything so that you use special files. These are uncomplicated exchangeable. So far you have seen cryptic texts because of that. In this part we translate the unattractive language strings.
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 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
Step by step
In the following overview, the newly added files are marked with a background and the changed ones are outlined.
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 -
*.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
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.
Side Note: Special features
Would you like to see exactly how the ini file is parsed? At php.net you will find the description of the function that does this work.
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 ....
There are characters that have a special meaning - for example, the prefix characters. This meaning can be removed with a backslash
... COM_CONTACT_CONTACT_REQUIRED="<strong class=\"red\">*</strong> Required field" ...
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
... 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
Unfortunately, you cannot determine which variable
$checkoutUser->namebelongs to which language string
%s. The values are assigned in order.
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
2 the language string
COM_FOOS_N_ITEMS_FEATURED_2 is used. If
count($ids) has neither
COM_FOOS_N_ITEMS_FEATURED is used as the fallback position.
... 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." ...
Create the following six files to support the German language in addition to English.
The left side of the equal sign in the language string, for example
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.
We add the German language version for the administration area with the files 'administrator/components/comfoos/ language/en-DE/comfoos.ini' and 'administrator/components/comfoos/ language/en-DE/comfoos.sys.ini'.
Don't be confused if you see a lot of texts. These are not all used at the moment. I'm already inserting the text for the future chapters here.
<!-- https://raw.githubusercontent.com/astridx/boilerplate/t8/src/administrator/components/com_foos/language/de-DE/com_foos.ini --> COM_FOOS="[PROJECT_NAME]" COM_FOOS_CONFIGURATION="Foo Optionen" COM_FOOS_MANAGER_FOO_NEW="Neu" COM_FOOS_MANAGER_FOO_EDIT="Bearbeiten" COM_FOOS_MANAGER_FOOS="Foo Manager" COM_FOOS_TABLE_TABLEHEAD_NAME="Name" COM_FOOS_TABLE_TABLEHEAD_ID="ID" COM_FOOS_ERROR_FOO_NOT_FOUND="Foo nicht gefunden" COM_FOOS_FIELD_NAME_LABEL="Name" COM_FOOS_FIELD_FOO_SHOW_CATEGORY_LABEL="Namensschild anzeigen" COM_FOOS_FIELD_CONFIG_INDIVIDUAL_FOO_DESC="Diese Einstellungen gelten für alle foo." COM_FOOS_FIELD_CONFIG_INDIVIDUAL_FOO_DISPLAY="Foo"
After the hyphen, the two capital letters indicate the country. For example, Swiss German can be distinguished from
CH or Austrian by
AT. A frolder named
de-CH contains the translation for Switzerland and
de-AT the Austrian variant.
As mentioned before, you need two language files: one ending with
.ini and one ending with
sys.ini is primarily used during installation and for displaying the menu items and the
sys.ini for everything else.
The reason for this dichotomy is performance. When installing or setting up the navigation in the backend, it is thus not necessary to load all language strings. In small extensions this advantage of two language files is not big in my opinion. Therefore I partially fill both files with the same language strings. This way I am on the safe side. This is not professional, but it has proven to be suitable for me in practice.
<!-- https://raw.githubusercontent.com/astridx/boilerplate/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>" COM_FOOS_FOO_VIEW_DEFAULT_TITLE="Ein einzelnes Foo" COM_FOOS_FOO_VIEW_DEFAULT_DESC="Dies ist ein Link zu den Informationen für ein Foo." COM_FOOS_SELECT_FOO_LABEL="Wählen Sie ein foo aus" COM_FOOS_CHANGE_FOO="Ändern Sie ein foo" COM_FOOS_SELECT_A_FOO="Wählen Sie ein foo aus"
I had already written it: The English versions of the language files are mandatory.
<!-- https://raw.githubusercontent.com/astridx/boilerplate/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" COM_FOOS_MANAGER_FOO_NEW="New" COM_FOOS_MANAGER_FOO_EDIT="Edit" COM_FOOS_MANAGER_FOOS="Foo Manager" COM_FOOS_TABLE_TABLEHEAD_NAME="Name" COM_FOOS_TABLE_TABLEHEAD_ID="ID" COM_FOOS_ERROR_FOO_NOT_FOUND="Foo not found" COM_FOOS_FIELD_NAME_LABEL="Name" COM_FOOS_FIELD_FOO_SHOW_CATEGORY_LABEL="Show name label" COM_FOOS_FIELD_CONFIG_INDIVIDUAL_FOO_DESC="These settings apply for all foo." COM_FOOS_FIELD_CONFIG_INDIVIDUAL_FOO_DISPLAY="Foo" COM_FOOS_FIELD_PUBLISH_DOWN_LABEL="Finish Publishing" COM_FOOS_FIELD_PUBLISH_UP_LABEL="Start Publishing" COM_FOOS_N_ITEMS_PUBLISHED="%d foos published." COM_FOOS_N_ITEMS_PUBLISHED_1="%d foo published." COM_FOOS_N_ITEMS_UNPUBLISHED="%d foos unpublished." COM_FOOS_N_ITEMS_UNPUBLISHED_1="%d foo unpublished." COM_FOOS_N_ITEMS_CHECKED_IN_1="%d foo checked in." COM_FOOS_N_ITEMS_CHECKED_IN_MORE="%d foos checked in." COM_FOOS_N_ITEMS_FEATURED="%d foos featured." COM_FOOS_N_ITEMS_FEATURED_1="Foo featured." COM_FOOS_N_ITEMS_UNFEATURED="%d foos unfeatured." COM_FOOS_N_ITEMS_UNFEATURED_1="Foo unfeatured." COM_FOOS_EDIT_FOO="Edit Foo" COM_FOOS_NEW_FOO="New Foo" COM_FOOS_HEADING_ASSOCIATION="Association" COM_FOOS_CHANGE_FOO="Change a foo" COM_FOOS_SELECT_A_FOO="Select a foo" COM_FOOS_TABLE_CAPTION="Foo Table Caption" COM_FOOS_N_ITEMS_ARCHIVED="%d foos archived." COM_FOOS_N_ITEMS_ARCHIVED_1="%d foo archived." COM_FOOS_N_ITEMS_DELETED="%d foos deleted." COM_FOOS_N_ITEMS_DELETED_1="%d foo deleted." COM_FOOS_N_ITEMS_TRASHED="%d foos trashed." COM_FOOS_N_ITEMS_TRASHED_1="%d foo trashed." COM_FOO_MANAGER_FOOS="Foos" COM_FOOS_FIELD_PARAMS_NAME_LABEL="Show Name" COM_FOOS_FILTER_SEARCH_DESC="Search in foo name." COM_FOOS_FILTER_SEARCH_LABEL="Search Foos"
Also the file is
administrator/components/com_foos/ language/en-GB/com_foos.sys.ini mandatory.
<!-- https://raw.githubusercontent.com/astridx/boilerplate/t8/src/administrator/components/com_foos/language/en-GB/com_foos.sys.ini --> COM_FOOS="[PROJECT_NAME]" COM_FOOS_XML_DESCRIPTION="Foo Component" 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>" COM_FOOS_FOO_VIEW_DEFAULT_TITLE="Single Foo" COM_FOOS_FOO_VIEW_DEFAULT_DESC="This links to the information for one foo." COM_FOOS_SELECT_FOO_LABEL="Select a foo" COM_FOOS_CHANGE_FOO="Change a foo" COM_FOOS_SELECT_A_FOO="Select a foo" COM_FOOS_FOO_VIEW_WITHHEAD_TITLE="Single Foo with a headertext" COM_FOOS_FOO_VIEW_WITHHEAD_DESC="This links to the information for one foo with a headertext." COM_FOOS_FEATURED_VIEW_DEFAULT_TITLE="Featured Foos" COM_FOOS_FEATURED_VIEW_DEFAULT_DESC="This view lists the featured foos." COM_FOOS_FORM_VIEW_DEFAULT_DESC="Create a new foo." COM_FOOS_FORM_VIEW_DEFAULT_TITLE="Create Foo" COM_FOOS_CATEGORY_VIEW_DEFAULT_TITLE="Category"
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.
<!-- https://raw.githubusercontent.com/astridx/boilerplate/t8/src/components/com_foos/language/de-DE/com_foos.ini --> COM_FOOS_NAME="Name: "
We add the mandatory English version in the file
<!-- https://raw.githubusercontent.com/astridx/boilerplate/t8/src/components/com_foos/language/en-GB/com_foos.ini --> COM_FOOS_NAME="Name: " COM_FOOS_FOO_NAME_LABEL="Name" COM_FOOS_FIELD_NAME_LABEL="Name" COM_FOOS_NEW_FOO="New" COM_FOOS_EDIT_FOO="Edit" JGLOBAL_FIELDSET_DISPLAY_OPTIONS="Display" JGLOBAL_FIELDSET_PUBLISHING="Publishing" COM_FOOS_FIELD_PARAMS_NAME_LABEL="Label" JFIELD_ALT_LAYOUT_LABEL="Layout" COM_FOOS_FIELD_PUBLISH_UP_LABEL="Publishing start" COM_FOOS_FIELD_PUBLISH_DOWN_LABEL="Publishing end"
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 various chapters on Github, which I refer to here.
So that the language files are copied during an installation, we add the
<folder>language</folder> entry for the frontend and the backend.
</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
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 directory
/administrator/language/en-GB / and
/ language/en-GB respectively.
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 where you need to adjust the value of the
folder parameter to your structure:
... <files folder="components/com_foos"> ... </files> <languages folder="site"> <language tag="en-GB">language/en-GB.com_foos.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> ...
So far we have output the name without a label in the frontend
echo $this->item->name;. Now we add a label. For this we consider the different languages. The following code causes that instead of
Text::_('COM_FOOS_NAME') in the frontend the string is output, which is entered in the corresponding language file. 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 output. If the German language is active and there is the German language file with the entry
COM_FOOS_FIELD_NAME_LABEL="Name", the word
Name is displayed.
\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 the
administrator folder of your Joomla 4 installation.
Copy the files in the
components folder into the
components 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.
- open the view of your component in the administration area and frontend and make sure that the texts are readable and not cryptic anymore.
- sample the innovation. Create language files for different languages and change the default language in Joomla. Make sure that Joomla translates correctly.