# Translation Pipeline

> How translatable strings flow from source to runtime (xgettext → Weblate → PO → PHP)

---

The module ships its own translations through the standard gettext chain, hosted on [**Codeberg Weblate**](<https://translate.codeberg.org/projects/wt-modules/linkenhancer/>).

## Flow




<pre class="mermaid">flowchart TB
    subgraph Extraction[&#34;1️⃣ Code → POT&#34;]
        direction LR
        PHP[&#34;📝 src/*.php&#34;] --&gt;|npx gulp updatepo| POT[&#34;📄 messages.pot&#34;]
    end

    subgraph Translation[&#34;2️⃣ Translation&#34;]
        direction LR
        WEBLATE[&#34;🌐 Weblate&#34;] --&gt;|download| PO[&#34;🗝 &lt;lang&gt;.po&#34;]
    end

    subgraph Rendering[&#34;3️⃣ Webtrees&#34;]
        direction LR
        PHPARR[&#34;📦 lang.php&#34;] --&gt;|I18N| RENDER[&#34;✅ Render&#34;]
    end

    POT --&gt;|upload| Translation
    PO --&gt;|npx gulp po2php| Rendering

    classDef sourceStyle fill:#e3f2fd,stroke:#1565C0,stroke-width:2px
    classDef fileStyle fill:#e8f5e9,stroke:#2E7D32,stroke-width:2px
    classDef weblateStyle fill:#f3e5f5,stroke:#7B1FA2,stroke-width:2px
    classDef renderStyle fill:#fce4ec,stroke:#C2185B,stroke-width:2px

    class PHP sourceStyle
    class POT,PO,PHPARR fileStyle
    class WEBLATE weblateStyle
    class RENDER renderStyle
</pre>

## Extracting strings

<a href="https://codeberg.org/bschwede/linkenhancer/src/branch/main/util/update-po-files.sh" target="_blank" rel="noopener"><code>update-po-files.sh</code></a>
 runs `xgettext` over every `*.php` / `*.phtml` except `util/`, `vendor/`, `node_modules/` and `tests/`:

```bash
xgettext -L PHP \
  --keyword=translate --keyword=plural:1,2 --keyword=translateContext:1c,2 \
  --add-comments=I18N --from-code=utf-8 \
  --output=resources/lang/messages.pot …
```

Two marker helpers control what gets extracted:

- `I18N::translate()` / `MoreI18N::translate()` — **module-specific** strings. `translate` is an xgettext keyword, so these are picked up.
- `MoreI18N::xlate()` — strings **already covered by the webtrees core** POT. Same runtime behaviour, different name, therefore invisible to xgettext. After a core update, re-compare `messages.pot` with the core POT and switch any newly-covered msgid to `xlate()`.

Add `/* I18N: … */` comments to give translators context (picked up via `--add-comments=I18N`).

## PO → PHP

`.po` files are **not** merged locally — that is Weblate's job, to prevent conflicts. Once Weblate delivers updated `.po` files, compile them:

```bash
npx gulp po2php
```

> **Note:** a former `.po` → `.mo` step is obsolete (translation format switch, [issue #88](<https://codeberg.org/bschwede/linkenhancer/issues/88>)).
