Skip to main content

Translations

njMule's interface speaks 36 languages, in the desktop window and in the web interface alike. Most of its words come from eMule's own translations, converted so that the interface keeps eMule's familiar wording; the texts njMule adds are translated in the same terms. This page explains where the texts live, how to correct one, and everything a new language needs.

The languages​

English, Catalan, Czech, Danish, German, Estonian, Spanish, French, Galician, Italian, Latvian, Lithuanian, Hungarian, Dutch, Norwegian Bokmål, Polish, Brazilian and European Portuguese, Romanian, Albanian, Slovenian, Finnish, Swedish, Vietnamese, Turkish, Greek, Bulgarian, Russian, Ukrainian, Arabic, Persian, Hebrew, Simplified and Traditional Chinese, Japanese and Korean.

Arabic, Persian and Hebrew are written right to left: the page is mirrored (dir="rtl"), while paths, links, addresses and sizes keep left to right, and names keep their own direction.

The interface speaks the language chosen in its options, kept for the next windows; a web address may also ask for one with ?lang=<tag>, such as ?lang=it. Otherwise it takes the best match of the system's languages (the browser's for the web interface): the language with its region, then the language alone, then its first regional variant (pt gets Brazilian Portuguese), and English when none matches.

Where the texts live​

All texts are Java properties files in UTF-8, in njmule-ui/i18n/. Each language has two files; <lang> is a language code, with the region for the two Portuguese and the two Chinese (pt_BR, pt_PT, zh_CN, zh_TW):

FileKeysWhat it holds
emule.properties, emule_<lang>.propertiesIDS_*eMule's interface strings and their translations, generated from the string tables of eMule's resource scripts by tools/Rc2Properties.java. Never edit them by hand: regenerate them
nj.properties, nj_<lang>.propertiesnj.*, and IDS_* in translationsnjMule's own texts, in English and in each language; a translation may also reword some of eMule's texts in that language

The files without a suffix are English, which is also the fallback: a text missing in a language shows in English. The interface looks a text up by its key, t('IDS_DL_FILENAME') or t('nj.wizard.stepOf', step, steps); an unknown key shows itself in brackets, [nj.some.key]. The build converts the files into one JSON file per language, <tag>.json with a hyphen in the tag (pt-BR), holding only the keys the interface's sources name (Interface).

For an IDS_* key, the language's nj_<lang>.properties wins over its emule_<lang>.properties when it holds the key: that is how a translation rewords one of eMule's texts. The rewordings sit at the end of each nj_<lang>.properties, under a comment that says why that language rewords them (for example, Italian keeps "upload" and "download" in English, as Italian software usually does). The English nj.properties never holds IDS_* keys.

Regenerating eMule's texts​

Rc2Properties reads eMule's resource scripts (the English one, then one per language), keeps the IDS_* string tables, drops the menu mnemonics (&O, and (&O) in Chinese, Japanese and Korean) and turns C format specifiers into Java ones:

java tools/Rc2Properties.java <english .rc> njmule-ui/i18n <lang>=<translated .rc>...

It writes emule.properties and one emule_<lang>.properties per language given, keeping only the keys the English file has, and prints which strings each language lacks.

Fixing a text​

  1. Find the key: search the English bundles for the text as it appears.
  2. Choose the file:
    • an nj.* text, in any language: edit it in nj.properties (English) or in nj_<lang>.properties;
    • an IDS_* text in one language: add the key with the new wording to that language's nj_<lang>.properties, among the rewordings at its end; never edit emule_<lang>.properties;
    • an IDS_* text in English: the English eMule texts are not reworded. Give the code a new nj.* key with the better text instead, translated in every language.
  3. Keep the placeholders (see Placeholders).
  4. Look at it in the interface (Interface), in its language: choose it in the options, or add ?lang=<tag> to the page's address.
  5. Commit with the scope i18n, for example fix(i18n): word the upload counter as the download one in German.

A new text​

A new nj.* key goes into nj.properties and every nj_<lang>.properties: each translation holds exactly the English set of nj.* keys, none blank. Write the key literally in the interface's sources, in quotes: the build keeps only the keys it finds there, so a key built at run time (t('nj.mode.' + mode)) must also be written in full somewhere in the sources, as the tables of settings.ts and network.ts do.

Placeholders​

The texts use Java's format specifiers, which format() in njmule-ui/src/i18n.ts fills in:

SpecifierMeaning
%s, %da text, a whole number
%.1fa number with one decimal
%1$s, %2$dan argument by its position, to change their order
%%a percent sign

A translation must keep, for every argument, the same conversion as the English text: it may move them around with positions (%2$s … %1$s), but not drop, add or change one. Whole numbers are formatted with the interface language's separators.

Adding a language​

A new language needs eMule's translation of its texts, so that emule_<lang>.properties exists. Then:

  1. Convert eMule's translation with Rc2Properties, adding <lang>=<translated .rc> to the command above.
  2. Translate nj.properties into a new nj_<lang>.properties, every key included, with the SPDX header of the other files.
  3. Offer it: add its tag (pt-BR style, with a hyphen) to LANGUAGES in njmule-ui/src/i18n.ts, and its own name, as its speakers write it ("Deutsch", "Italiano"), to LANGUAGE_NAMES in njmule-ui/src/settings.ts, which the language boxes show. Keep both lists in the same order: English first, then the languages by their own names, as the lists are.
  4. Right to left? Add its code to the list in loadLanguage() of i18n.ts, which sets the page's direction.
  5. Update the documents: the count of languages in the README.md, on the home page (website/src/pages/index.tsx) and in the user guide (search its pages for it), the list of languages on this page and in the user guide's Appearance and language page, and a line in CHANGELOG.md.

The rest follows by itself: the build makes a JSON file for every nj_<lang>.properties, with that language's emule_<lang>.properties beneath it.

The tests that guard them​

The interface's Vitest tests check how the texts are used (Testing):

TestWhat it checks
i18n.test.tsplaceholders are filled in, by order and by position, and decimals rounded; the browser's languages pick a translation, a regional variant or English
language.test.tsa language chosen in the options shows at once and is kept for the next windows; with none chosen, the best match of the system's languages shows

No test checks the bundles themselves: keep the keys and the placeholders of every translation in step with English by hand.