Language files
Interface texts in Joomla are not written straight into code. The code holds a key, and the string itself lives in a language file — a separate one for each language. This way one extension works on a site in any language, and the site owner can adjust any label.
Where the files live
Create a language folder in the module by hand. Inside there is one folder per language; a language code consists of a language and a country:
modules/mod_hello/
├── language/
│ ├── en-GB/
│ │ ├── mod_hello.ini
│ │ └── mod_hello.sys.ini
│ └── de-DE/
│ ├── mod_hello.ini
│ └── mod_hello.sys.ini
├── mod_hello.xml
├── services/provider.php
├── src/...
└── tmpl/default.phpAdd the folder to the files section of the manifest:
<files>
<folder module="mod_hello">services</folder>
<folder>language</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>English en-GB is the default language: Joomla uses its strings when there is no translation into the site language. That is why the English file is always required.
A translation file
; modules/mod_hello/language/en-GB/mod_hello.ini
MOD_HELLO_LATEST="Latest articles"On the left is the key — in capitals, prefixed with the extension name. On the right is the string in double quotes.
Printing a string
<?php
// modules/mod_hello/tmpl/default.php
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
?>
<h4><?php echo Text::_('MOD_HELLO_LATEST'); ?></h4>Text::_() finds the string by its key in the current site language. The page shows the heading "Latest articles". Joomla loads the module language file by itself.
Substituting values
MOD_HELLO_GREETING="Hello, %s!"
MOD_HELLO_N_ARTICLES="%d articles"
MOD_HELLO_N_ARTICLES_1="One article"// modules/mod_hello/tmpl/default.php
echo Text::sprintf('MOD_HELLO_GREETING', $app->getIdentity()->name);
echo Text::plural('MOD_HELLO_N_ARTICLES', count($articles));Text::sprintf() puts values in place of %s and %d. Text::plural() uses the number to look for a key with a matching suffix — _1 for one — and falls back to the base key when there is none. A logged-in user sees "Hello, Anna!" and "5 articles".
The sys.ini file
The file ending in .sys.ini is for the control panel: in the extension list and in the module type picker Joomla loads only this one.
; modules/mod_hello/language/en-GB/mod_hello.sys.ini
MOD_HELLO="Greeting"
MOD_HELLO_XML_DESCRIPTION="A block with a greeting and the latest articles"Keys in the manifest
Labels in the manifest are replaced with keys as well — Joomla translates them itself:
<name>MOD_HELLO</name>
<description>MOD_HELLO_XML_DESCRIPTION</description>
<field name="greeting" type="text" label="MOD_HELLO_FIELD_GREETING_LABEL" default="Hello!" />The key MOD_HELLO_FIELD_GREETING_LABEL goes into mod_hello.ini: that file is loaded when the module form is open.
Finding untranslated strings
Turn on System → Global Configuration → System → Debug Language. Strings without a translation are printed as ??KEY??, and the debug bar shows which language files were loaded.
Common mistake
The page shows the key itself instead of the text: MOD_HELLO_LATEST. There are usually three reasons: a typo in the key, a line without the closing quote (the rest of the file is not read after it), or the language folder is missing from the manifest, so the files were not copied when installing from an archive.