Skip to content

en CONTRIBUTING_i18n

wiki-sync edited this page Apr 19, 2026 · 1 revision

Contributing Translations

SelenaCore ships in 16 languages (2 human-maintained + 14 machine-generated). This document explains how to improve an existing translation and how to propose a new one.

Tiers at a glance

Each target language resolves from up to four files, in priority order:

src/i18n/locales/{lang}.ts             ← manual (highest)
src/i18n/locales/{lang}.community.json ← community overrides
src/i18n/locales/auto/{lang}.auto.json ← auto-generated (lowest)
src/i18n/locales/en.ts                 ← fallback

When you open the UI in Polish, i18next merges them left-to-right — manual wins over community, community wins over auto. en.ts covers anything the target language is missing.

The same tier system applies to system-module widgets / settings pages (system_modules/*/locales/{lang}.{auto,community}.json) and the common strings served by /api/i18n/bundle/*.

Improving a machine-translated language

This is the most common contribution. Argos handles the bulk of the translation but mis-translates edge cases (time units, plural forms, technical terms with ambiguous senses). You fix those without touching the .auto.json file directly.

Workflow

  1. Fork the SelenaCore repo.

  2. Find the bad keys. Load the UI in the target language and note which strings read wrong. The auto.json file has the machine translations — useful as reference.

  3. Create a community override. In src/i18n/locales/, create {lang}.community.json:

    {
        "layout.total": "{{count}} łącznie",
        "integrityPage.metaLine": "SHA256 · {{checks}} kontroli · co 30 s"
    }

    Use the same flat key names as in en.ts (nested keys are joined with dots: layout.total, not nested {layout: {total: ...}}).

    You don't need to translate every key — anything you don't include falls back to the auto tier. Start with the 10-20 keys that annoy you most. Someone else will fix more next month.

  4. Preserve placeholders. {{count}}, {{name}}, {{lang}} etc. must appear in your translation exactly as they do in English. The generator respects them; humans should too.

  5. Check plural forms. Keys ending in _one, _few, _many, _other are CLDR plural variants — used when {{count}} takes different grammar in different quantities. For Slavic languages (ru, pl, cs, uk) there are 4 forms:

    {
        "usersPanel.userCount_one": "{{count}} użytkownik",
        "usersPanel.userCount_few": "{{count}} użytkowników",
        "usersPanel.userCount_many": "{{count}} użytkowników",
        "usersPanel.userCount_other": "{{count}} użytkownika"
    }

    If you fix one plural form, fix them all for that key.

  6. Validate locally.

    python scripts/i18n_diff.py

    Reports missing keys, orphan keys, placeholder drift between en.ts and uk.ts. Community files aren't checked for key coverage (that's the whole point — partial is fine) but they are checked for placeholder sanity on the keys you do include.

  7. Open a PR titled i18n({lang}): <what you fixed>. Link the relevant Translation improvement issue if one exists.

Contribution tier badge

When a .community.json file exists for a language, the LanguagePicker shows a "Community-improved" badge next to it. That signals to users that the language has moved past pure machine translation.

Adding a new language

  1. Open a Language request issue. A maintainer validates the request and adds the language code to scripts/i18n_config.py + src/i18n/languages.json.

  2. The next CI run generates {code}.auto.json automatically.

  3. You polish via {code}.community.json as above.

When the community file reaches substantial coverage (roughly >70% of keys, per our own judgement — we don't track it automatically), we may promote it to .manual.ts as a first-class human-maintained language. Open an issue tagged i18n, promote-to-manual to start that conversation.

What NOT to do

  • Don't edit {lang}.auto.json directly. It's regenerated by CI on every change to en.ts; your changes will be overwritten.

  • Don't translate technical glossary terms. The generator preserves SHA256, Piper, Ollama, Bluetooth, etc. verbatim per src/i18n/glossary.json. If you think a term should be localized (e.g. ProviderAnbieter in German), add it to per_language_overrides in the glossary, not your community file.

  • Don't invent new keys. If en.ts doesn't have it, the UI won't use it. For new keys you need to change the English source; open a separate PR for that.

  • Don't mix translation and behavior changes in the same PR. Makes review slower.

Running a local check

# Key parity between en.ts and all manual-tier files
python scripts/i18n_diff.py

# Scan for hardcoded strings missed by i18n coverage
python scripts/i18n_audit.py

# Lint gate used by CI (parity + hardcoded-string baseline)
python scripts/i18n_lint.py

Questions

Open a discussion on GitHub or ping a maintainer on an issue — the translation tooling is young, and we're happy to refine the workflow based on actual contributor feedback.

Clone this wiki locally