Gettext PO files are the lingua franca of legacy server-side localization — Python's gettext, PHP's gettext, Django, WordPress, and countless Ruby on Rails projects sit on top of them. Modern web applications, by contrast, have largely standardized on i18next-style JSON for runtime translation lookups. Migrating from a PO-based backend to a JavaScript or TypeScript front end usually means writing the same conversion twice: once for a one-time backfill, and again every time the translation files change.
i18n-convert reads each msgid/msgstr pair in the PO file and emits a JSON object whose keys are the msgid source strings and whose values are the msgstr translations. The PO header pseudo-entry (the one whose msgid is the empty string and which carries Content-Type and Language metadata) is correctly skipped rather than emitted as a "" key. Keys are sorted alphabetically for deterministic, review-friendly diffs. Because msgid strings often contain format placeholders like %s, the converter passes them through verbatim — your runtime, not the converter, decides how to interpret them. A data-loss warning fires for the discarded PO header metadata, requiring --force to acknowledge that target-side JSON cannot carry the Language header alongside the translations.
Command
i18n-convert simple.po --to i18next --force -o messages.json
Input
# Simple PO file for testing
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"MIME-Version: 1.0\n"
msgid "Hello"
msgstr "Hallo"
msgid "Goodbye"
msgstr "Auf Wiedersehen"
msgid "Welcome to %s"
msgstr "Willkommen bei %s"
Output
{
"Goodbye": "Auf Wiedersehen",
"Hello": "Hallo",
"Welcome to %s": "Willkommen bei %s"
}