This guide explains how to contribute translations to a Bengal site using the standard gettext PO workflow.
PO File Structure
Translations live in:
i18n/
├── en/LC_MESSAGES/messages.po
├── es/LC_MESSAGES/messages.po
└── ar/LC_MESSAGES/messages.po
Each.pofile contains:
- Header: Metadata (charset, plural forms)
- Entries:
msgid(source) andmsgstr(translation)
PO File Format
# English translations for My Site
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"MIME-Version: 1.0\n"
msgid "Home"
msgstr "Home"
msgid "About"
msgstr "About"
msgid "Read more"
msgstr "Read more"
- msgid: The source string (from templates)
- msgstr: The translation. Empty
msgstr ""means untranslated (fallback to key)
Conventions
- Use UTF-8 for all PO files. Set
Content-Type: text/plain; charset=UTF-8in the header. - Keep msgid unchanged — it's the lookup key. Never translate the msgid.
- Preserve placeholders — if the source has
{name}, keep it in the translation:Hola {name}. - Plural forms — use
msgid_pluralandmsgstr[0],msgstr[1]for languages with plural rules.
Workflow for Contributors
- Get the template: Ask the maintainer for
messages.potor runbengal i18n extractin the repo. - Create your locale:
mkdir -p i18n/XX/LC_MESSAGES(e.g.XX=frfor French). - Copy and translate: Copy
messages.pottoi18n/XX/LC_MESSAGES/messages.poand fill inmsgstrvalues. - Submit: Open a PR with your new or updated
.pofile.
Tools
- Poedit: GUI editor for PO files, handles plural forms
- Lokalize: KDE translation tool
- VS Code: Extensions like "Gettext" for PO editing
Checking Your Work
After adding translations:
bengal i18n compile
bengal build
bengal i18n status
bengal i18n statusshows coverage. Aim for 100% for your locale.
Plural Forms
Bengal supports plural-aware translation through thent()template function.
Using nt() in Templates
{# Basic plural — {n} is automatically replaced with the count #}
{{ nt('1 item', '{n} items', count) }}
{# With extra parameters #}
{{ nt('1 {thing}', '{n} {thing}s', count, {'thing': 'file'}) }}
When no gettext catalog is loaded, nt() uses English-style selection (singular when n=1, plural otherwise). With a catalog, it uses ngettext()for correct plural rules.
PO File Plural Entries
For languages with complex plural rules, usemsgid_plural and indexed msgstr:
# Spanish (2 forms: singular, plural)
msgid "1 item"
msgid_plural "{n} items"
msgstr[0] "1 elemento"
msgstr[1] "{n} elementos"
# Polish (3 forms: singular, few, many)
msgid "1 item"
msgid_plural "{n} items"
msgstr[0] "1 element"
msgstr[1] "{n} elementy"
msgstr[2] "{n} elementów"
# Arabic (6 forms: zero, one, two, few, many, other)
msgid "1 item"
msgid_plural "{n} items"
msgstr[0] "لا عناصر"
msgstr[1] "عنصر واحد"
msgstr[2] "عنصران"
msgstr[3] "{n} عناصر"
msgstr[4] "{n} عنصرًا"
msgstr[5] "{n} عنصر"
Plural Rules Reference
The correct number of forms depends on the language:
| Language | Forms | Rule |
|---|---|---|
| English, Spanish, French | 2 | n == 1 ? singular : plural |
| Polish, Czech, Russian | 3 | Complex (singular, few, many) |
| Arabic | 6 | Complex (zero through other) |
| Japanese, Chinese, Korean | 1 | No plural distinction |
See Unicode CLDR Plural Rules for the full reference.