Saltar al contenido principal

Translations

aMule has three areas that can be translated: the application interface strings, the man pages, and the website documentation.

Code Translations

aMule's interface strings are managed with GNU gettext. Translations are stored as .po files in po/ and compiled to binary .mo catalogs at build time. The interface is currently maintained in several dozen languages — po/LINGUAS is the canonical list of the codes that get compiled. This document explains how to contribute a new or updated translation.

Overview

FilePurpose
po/amule.potTemplate with all translatable strings extracted from source. Empty translations. Committed to git.
po/xx.poOne file per language. Contains the actual translations. Committed to git.
build/po/xx.gmoCompiled binary catalog, generated at build time. Not committed.

At runtime, the program loads the installed amule.mo catalog for the user's locale. The catalog is always named amule.mo, but its install location depends on the platform: Windows uses bin/locale/xx/LC_MESSAGES/, macOS bundles it inside the app at Contents/Resources/locale/xx/LC_MESSAGES/, and Linux/BSD use share/locale/xx/LC_MESSAGES/.

po/
├── amule.pot # master template (regenerated by update-po.sh)
├── POTFILES.in # list of source files scanned for translatable strings
├── LINGUAS # list of language codes that get compiled
├── Makevars # xgettext options (keyword list, package metadata)
├── de.po # German
├── fr.po # French
├── pt_BR.po # Portuguese (Brazil)
└── ... # one .po file per language

Prerequisites

Install the gettext tools. They provide xgettext, msgmerge, msgfmt, and msgattrib:

# Debian / Ubuntu
sudo apt install gettext

# Fedora / RHEL
sudo dnf install gettext

# Arch / Manjaro
sudo pacman -S gettext

# macOS (Homebrew)
brew install gettext

A dedicated PO editor makes the translation process much easier. Poedit is the most widely used cross-platform option. Lokalize (KDE) is another good choice. Plain text editors also work — .po files are standard UTF-8 text.

Checking Translation Status

Summary — how many strings are translated, fuzzy, or missing:

msgfmt --statistics po/pt_BR.po -o /dev/null

Example output:

1823 translated messages, 42 fuzzy translations, 17 untranslated messages.

List only untranslated strings:

msgattrib --untranslated po/pt_BR.po

List only fuzzy strings:

msgattrib --only-fuzzy po/pt_BR.po

Updating an Existing Translation

After source code changes, the .pot template and all .po files must be refreshed. Run this from the repository root:

./scripts/update-po.sh

This script:

  1. Runs xgettext over all files listed in po/POTFILES.in to regenerate po/amule.pot.
  2. Updates the copyright year in the .pot header.
  3. Runs msgmerge --update on every po/*.po so each language file receives new and changed strings.

After running update-po.sh, each .po file may contain:

  • Untranslated entriesmsgstr "" is empty; the string is new and has not yet been translated.
  • Fuzzy entries — marked #, fuzzy; msgmerge found a similar existing translation as a starting point, but it requires human review. The program falls back to the English original until the fuzzy marker is removed.
  • Translated entriesmsgstr is filled in with no fuzzy marker; ready to ship.

Open the target .po file in Poedit (or any PO editor), fill in or correct the msgstr fields, remove fuzzy markers after reviewing them, and commit the result.

Adding a New Language

  1. Create the initial .po file from the template:

    msginit --input=po/amule.pot --locale=xx --output=po/xx.po

    Replace xx with the locale code (e.g. nl, ko_KR, pt_BR).

  2. Add the locale code to po/LINGUAS (one code per line, alphabetically sorted).

  3. Translate the strings in po/xx.po.

  4. Verify the file compiles without errors:

    msgfmt --check po/xx.po -o /dev/null
  5. Commit both po/xx.po and the updated po/LINGUAS, then open a pull request.

Testing Your Translation

To verify a translation at runtime, set LANG before launching aMule. Use LANG, not LANGUAGE — the latter does not work reliably with wxWidgets:

LANG=pt_BR.UTF-8 amule

The program must be installed first so the .mo catalog is discoverable. See the Compilation guide for the full build and install workflow. During development, install locally without sudo:

cmake --install build --prefix=$HOME/.local
LANG=pt_BR.UTF-8 amule

To uninstall when done:

xargs rm -f < build/install_manifest.txt

Build Integration

The CMake build compiles every enabled .po into a .gmo binary catalog via msgfmt. The Compilation guide documents the full set of build options; translation support requires -DENABLE_NLS=YES (the default):

cmake -B build -DENABLE_NLS=YES

To build only a subset of languages:

cmake -B build -DENABLE_NLS=YES -DTRANSLATIONS="de,fr,pt_BR"

Marking Strings for Translation in C++ Source

Use the standard gettext macros:

MacroUse case
_("text")Most common — translates a single string
wxTRANSLATE("text")Marks a string for extraction without translating it at that point
wxPLURAL("one item", "%d items", n)Plural forms

After adding new uses of these macros, run ./scripts/update-po.sh to pull the new strings into the .pot and all .po files.

When adding a new .cpp source file with translatable strings, add it to po/POTFILES.in. xgettext only scans files listed there — the omission is completely silent. New source changes should also follow the project Coding Style.

Format Specifiers Reference

aMule translatable strings sometimes contain format specifiers and escape codes. These must be preserved exactly as-is in translations — only the surrounding natural-language text should change.

Escape Codes

Escape codes are character sequences that represent special characters:

CodeMeaning
\nNew line
\tHorizontal tab
\rCarriage return
\\Literal backslash (\)
\"Literal double quote (")
\'Literal single quote (')
\aAudible alert (beep)
\bBackspace
\fForm feed
\?Question mark (avoids trigraph interpretation)
\<octal>Character with the given octal value
\x<hex>Character with the given hexadecimal value

Format Specifiers

Format specifiers are replaced at runtime by variable values:

SpecifierReplaced by
%sA string
%d or %iA signed decimal integer
%uAn unsigned decimal integer
%fA floating-point number (normal notation)
%e / %EA floating-point number (exponential notation)
%g / %GA floating-point number (automatic notation)
%x / %XA hexadecimal integer (lower/uppercase)
%oAn octal integer
%cA single character
%pA memory address
%%A literal % character

Type extensions that may appear between % and the type letter:

ExtensionEffect
hShort integer (hd, hu, hx, etc.)
lLong integer (ld, lu, lx, etc.)
LLong double (Lf, Lg, etc.)

Output tweaks between % and the type letter:

TweakEffect
-Left-align
+Always print sign
0Zero-pad the number
#Prefix 0 for octal, 0x/0X for hex
<N>Minimum field width
.<N>Precision (decimal places for floats; max chars for strings)
Important

You must never change the order of format specifiers. The order in the translation must match the order in the English original. Swapping %s and %d will cause aMule to crash when displaying the string.

The & Accelerator Character

An & before a letter in a menu or button string marks the keyboard accelerator (the underlined letter). The & must be placed before the same letter in the translation (not necessarily the same position in the sentence):

English: "Insert eD2k &Link to clipboard" → accelerator: L
German: "eD2k-&Link in die Zwischenablage" → accelerator: L (same letter)

Leading and Trailing Spaces

Some strings begin or end with a space. This is intentional — the space may be used to separate the translated string from other text appended at runtime. Do not remove it:

English: " aMule version "
Good: " aMule versión "
Bad: "aMule versión" ← missing leading/trailing spaces

Example

msgid "I am %s and I am %d years old."
msgstr "Ich bin %s und ich bin %d Jahre alt."

Both %s and %d are preserved in the same order.

Plural Forms

Plurals are handled differently across languages. English and German use singular/plural; Turkish has no distinction; Polish uses different forms for different numbers.

The Plural-Forms header in the .po file declares the plural rules for the language. When creating a new .po with msginit, this header is set automatically. If in doubt, consult the gettext plural forms reference.

Example (German):

"Plural-Forms: nplurals=2; plural=n != 1;\n"

Plural strings in the .po file:

#: src/kademlia/routing/RoutingZone.cpp:141
#, c-format
msgid "Read %u Kad contact"
msgid_plural "Read %u Kad contacts"
msgstr[0] "Lese %u Kad-Kontakt"
msgstr[1] "Lese %u Kad-Kontakte"

Common .po Compilation Errors

ErrorCauseFix
end-of-line within stringA string opened with " was not closed.Find the line in the error and close the string.
format specifications in 'msgid' and 'msgstr' for argument N are not the sameA %s/%d was changed or missing.Check all % sequences in the translation match those in the English.
number of format specifications does not matchThe count of % sequences differs.Count carefully — you may have typed &s instead of %s.
'msgid' and 'msgstr' entries do not both end with '\n'Different number of newlines.Match the number of \n sequences exactly.

Utility Script: Find the msgid Containing a Given Error Line

When msgfmt reports an error at a specific line number, this Python 3 script tells you which msgid block contains that line:

#!/usr/bin/env python3
"""Find the msgid number that contains a given line number in a .po file."""
import sys

if len(sys.argv) < 3:
print(f"usage: {sys.argv[0]} <po_file> <line_number>")
sys.exit(1)

file_name = sys.argv[1]
line_number = int(sys.argv[2])
msgid_count = 0

with open(file_name, encoding="utf-8") as po_file:
for i, line in enumerate(po_file, start=1):
if line.startswith("msgid"):
msgid_count += 1
if i == line_number:
print(f"error at line {line_number} is in msgid no. {msgid_count - 1}")
sys.exit(0)

print(f"line {line_number} not found in {file_name}")
sys.exit(1)

Save this as find_msgid.py and run:

python3 find_msgid.py po/de.po 142

Man Page Translations

aMule's man pages can also be translated using po4a. The translation files live in docs/man/po/. The man pages are currently translated into a smaller set of languages than the interface — the [po4a_langs] line in docs/man/po4a.config is the canonical list.

File Layout

docs/man/
├── amule.1 # English man page (master)
├── amuled.1
├── amulecmd.1
├── amulegui.1
├── amuleweb.1
├── ed2k.1
├── po4a.config # po4a configuration
├── amule.de.1 # Generated translated man pages
├── amule.es.1
└── po/
├── manpages.pot # master template
├── manpages-de.po # German translation
├── manpages-es.po # Spanish translation
├── manpages-de.add # German addendum (translator credit)
└── ...

The same po4a.config and manpages-<lang>.po files also drive the man pages of the standalone utilities, whose masters live next to their code: alc and alcc (src/utils/aLinkCreator/docs/), cas (src/utils/cas/docs/), and wxcas (src/utils/wxCas/docs/). Their translated pages (alc.<lang>.1, …) are generated in those same directories. There is one set of .po files for all man pages — utilities included.

Prerequisites

Install po4a (optional but strongly recommended — it automates file generation):

# Debian / Ubuntu
sudo apt install po4a

# Fedora / RHEL
sudo dnf install po4a

Rules

  • Do not add or remove information compared to the English original. If you believe the English text is wrong or incomplete, fix the English source first.
  • Your editor must produce UTF-8 files.
  • Do not remove special characters (B<...>, I<...>, etc.) — they are formatting markers.
  • po4a ignores files where fewer than 80% of strings are translated.
  • Do not remove trailing spaces.

Starting a New Man Page Translation

If po4a is installed:

cd docs/man
# Add your language code to the [po4a_langs] line in po4a.config (alphabetically).
# Current line: "de es fr hu it pt_BR ro ru tr zh_TW"
# Example: to add Dutch, change it to "de es fr hu it nl pt_BR ro ru tr zh_TW"
po4a po4a.config
# Your new .po file is now in po/manpages-nl.po

If po4a is not installed:

cd docs/man/po
cp manpages.pot manpages-nl.po
# Start translating. Mention in the PR that you didn't use po4a.

po4a Formatting Codes

The formatting characters in po4a files differ from raw man page markup:

CodeMeaning
E<lt>Prints <
E<gt>Prints >
B<text>Text printed in bold
I<text>Text printed in italic
R<text>Text printed in normal (roman) font

Example (keep B<> and I<> wrappers, only translate the text inside E<lt>pathE<gt>):

msgid "B<[ -w> I<E<lt>pathE<gt>>, B<--use-amuleweb>=I<E<lt>pathE<gt>> B<]>"
msgstr "B<[ -w> I<E<lt>PfadE<gt>>, B<--use-amuleweb>=I<E<lt>PfadE<gt>> B<]>"

Addendum File

The addendum is an optional file docs/man/po/manpages-<lang>.add that adds a translator credit at the bottom of every translated man page.

Copy an existing addendum (e.g. manpages-de.add) and adapt the content. Do not change the first line — it is a po4a header that specifies the insertion point.

Add a line such as:

Translated by Your Name <your@email.example>

Use an empty line between paragraphs. Two consecutive non-empty lines are printed without a line break between them.

Generating and Verifying the Translated Pages

With po4a installed, after finishing your .po file:

cd docs/man
po4a po4a.config

Check the generated pages:

man docs/man/amule.pt_BR.1
man docs/man/amuled.pt_BR.1
man docs/man/amulecmd.pt_BR.1
man docs/man/amulegui.pt_BR.1
man docs/man/amuleweb.pt_BR.1
man docs/man/ed2k.pt_BR.1

Regenerating with CMake

Maintainers can regenerate all translated man pages from within the build system (requires po4a at configure time):

cmake --build build --target po4a-update

This rewrites docs/man/po/manpages.pot, syncs each manpages-LANG.po, and regenerates the translated *.LANG.1 files. Commit the resulting changes.

Submitting a Man Page Translation

Open a pull request with:

  • docs/man/po/manpages-<lang>.po — the translation file.
  • docs/man/po/manpages-<lang>.add — the addendum (optional but encouraged).
  • The generated *.LANG.1 man pages (if po4a is installed and they look correct).

Documentation Translations

The website documentation (this site) is internationalized through Docusaurus's i18n system. Translations cover two areas: UI strings (navbar, sidebar labels, homepage text) stored in i18n/<locale>/code.json, and documentation pages stored as Markdown files under i18n/<locale>/docusaurus-plugin-content-docs/current/.

Full instructions for adding or updating a documentation translation are in Contributing to Documentation.