# aMule Translations

This document explains how aMule's translation system works and how to
contribute translations.

## Overview

aMule uses [GNU gettext](https://www.gnu.org/software/gettext/) for
internationalisation. The workflow involves three file types:

| File | Location | Purpose |
| ---- | -------- | ------- |
| `amule.pot` | `po/amule.pot` | Template: all translatable strings extracted from source, with empty translations. Committed to git. |
| `xx.po` | `po/xx.po` | One file per language. Contains the actual translations. Committed to git. |
| `xx.gmo` | `build/po/` | Compiled binary catalog, generated at build time. Not committed. |

At runtime the program loads the `.gmo` file (installed as
`share/locale/xx/LC_MESSAGES/amule.mo`) for the user's locale.

## File layout

```text
po/
├── amule.pot          # master template (regenerated by update-po.sh)
├── POTFILES.in        # list of source files to scan 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
```

## Adding or updating translations

### Prerequisites

Install the gettext tools (they provide `xgettext`, `msgmerge`,
`msgfmt`, and `msgattrib`):

```sh
# Debian / Ubuntu
sudo apt install gettext

# Fedora / RHEL
sudo dnf install gettext

# macOS (Homebrew)
brew install gettext
```

### Regenerating the template and merging into all .po files

After adding translatable strings to the C++ source, or to sync the
`.po` files with the current source, run from the repository root:

```sh
./scripts/update-po.sh
```

This script:

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

`update-po.sh` must be run from the repository root (same requirement
as `scripts/compile.sh`).

## Translator workflow

After `update-po.sh` has been run, each `.po` file may contain:

- **Untranslated entries** — `msgstr ""` is empty; the string is new
  and has never been translated.
- **Fuzzy entries** — marked `#, fuzzy`; `msgmerge` reused a similar
  existing translation as a starting point but it needs human review.
  The program will fall back to the English original until the fuzzy
  marker is removed.
- **Translated entries** — `msgstr` is filled in and has no fuzzy
  marker; these are ready to ship.

Open the target `.po` file in a text editor or a dedicated PO editor
(Poedit is the most widely used), fill in or correct the `msgstr`
fields, remove fuzzy markers after reviewing them, and commit the
result.

## Checking translation status

**Quick summary** — how many strings are translated, fuzzy, or missing:

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

Example output:

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

**List only untranslated strings:**

```sh
msgattrib --untranslated po/pt_BR.po
```

**List only fuzzy strings:**

```sh
msgattrib --only-fuzzy po/pt_BR.po
```

**Quick grep for empty msgstr** (rough check, no external tools needed):

```sh
grep -A1 'msgstr ""' po/pt_BR.po
```

Replace `pt_BR` with the language code you want to inspect.

## Adding a new language

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

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

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

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

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

4. Verify the file compiles without errors:

   ```sh
   msgfmt --check po/xx.po -o /dev/null
   ```

5. Commit both `po/xx.po` and the updated `po/LINGUAS`.

## Marking strings for translation in C++ source

Use the standard gettext macros:

| Macro | Use case |
| ----- | -------- |
| `_("text")` | Most common — 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.

### Adding a new source file

When adding a new `.cpp` file that contains translatable strings,
**add it to `po/POTFILES.in`** as well. `xgettext` only scans the
files listed there; if a file is missing from `POTFILES.in` its
strings will never appear in any translation and no warning is
emitted — the omission is completely silent.

## Testing a translation

To verify a translation works at runtime, set `LANG` to the locale
before launching aMule. Note that `LANGUAGE` does not work reliably
with wxWidgets — use `LANG`:

```sh
LANG=pt_BR.UTF-8 amule
```

The program must be installed first so the `.mo` catalog is in a
location wxWidgets can find. The easiest approach during development
is a local install (no `sudo` required):

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

To uninstall when done:

```sh
xargs rm -f < build/install_manifest.txt
```

## Build integration

The cmake build compiles every enabled `.po` into a `.gmo` binary
catalog via `msgfmt`. Translation support requires `-DENABLE_NLS=YES`
(the default). To build only a subset of languages pass a
comma-separated list:

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

To build all languages simply omit `-DTRANSLATIONS`.
