polib

A pure Python library for parsing, creating, and editing gettext PO, POT, and MO translation catalog files.

Library
PyPI
v1.2.0
135stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
38/100Needs Attention
Development Activity0
Maintenance0
Community72
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture72
Code Quality68
Innovation55
Learning Curve85

polib is a small, focused Python library for working with gettext translation catalogs. It parses existing .po, .pot, and .mo files into Python objects, lets you iterate, add, modify, or remove entries and metadata, and writes the results back out in the correct format — including compiling PO/POT source files down to binary MO catalogs.

The library exposes two convenience entry points, polib.pofile() and polib.mofile(), that auto-detect file encoding and return POFile/MOFile objects behaving like Python lists of entries. Each entry (POEntry/MOEntry) models the full gettext entry structure — msgid, msgstr, plural forms, msgctxt, comments, occurrences, flags, and obsolete markers — so scripts that generate translation reports, merge catalogs, or build custom i18n tooling can work with structured objects instead of hand-rolling a PO-file parser.

polib has shipped since 2006 and is widely vendored inside other projects’ build and localization tooling (Django’s makemessages-adjacent scripts, Sphinx’s gettext builder, various static-site generators) as the de facto Python way to touch PO/MO files without shelling out to GNU gettext utilities.

What You Get

  • polib.pofile() and polib.mofile() convenience functions that parse a path or in-memory string/bytes and auto-detect file encoding
  • POFile and MOFile classes that behave like Python lists of entries, with helpers like find(), save(), save_as_mofile(), percent_translated(), and unshift()/insert()
  • POEntry/MOEntry objects exposing msgid, msgstr, plural msgstr indices, msgctxt, translator/extracted comments, source occurrences, flags (e.g. fuzzy), and obsolete status
  • Metadata dict access for catalog headers (Project-Id-Version, POT-Creation-Date, Language, Plural-Forms, etc.) parsed and re-serialized automatically
  • escape()/unescape() and detect_encoding() utility functions for lower-level PO string handling
  • Optional duplicate-entry detection via check_for_duplicates=True when parsing

Common Use Cases

  • Generating translation-completeness reports by loading a PO file and calling percent_translated() or iterating untranslated entries
  • Batch-editing or scripting fixes across many .po files (renaming msgctxt values, stripping fuzzy flags, normalizing wrapping) as part of a localization pipeline
  • Compiling PO/POT source catalogs into binary MO files at build time without invoking the external msgfmt binary
  • Merging or diffing two catalog versions programmatically, e.g. when reconciling translations after a source-string extraction pass
  • Building custom i18n tooling (linting untranslated strings, extracting stats, syncing catalogs across services) on top of a stable, typed-in-spirit entry model

Under The Hood

Architecture polib is a single flat module (polib.py, ~1,800 lines) organized around a small class hierarchy: _BaseFile (a list subclass shared by POFile and MOFile), _BaseEntry (shared by POEntry and MOEntry), and two internal parser classes, _POFileParser and _MOFileParser, that do line-by-line/binary-structure parsing and hand back populated file objects. The public API is deliberately narrow — pofile() and mofile() funnel through a shared _pofile_or_mofile() helper that picks the right parser and applies encoding detection — so callers rarely touch the parser classes directly. Because file objects subclass list, editing a catalog is just list manipulation (append, insert, remove) plus attribute edits on entries, which keeps the mental model close to native Python rather than introducing a custom collection type. The main risk surface if this core abstraction changed is the PO/MO grammar handling inside the parser classes, since every downstream method assumes entries are fully-formed by the time they reach _BaseFile.

Tech Stack The library has zero third-party runtime dependencies, relying only on the Python standard library — re for PO grammar tokenizing, struct and array for binary MO file encoding/decoding, codecs/io for encoding-aware file I/O, and textwrap for PO line-wrapping on save. It still carries Python 2/3 compatibility shims (PY3 flag, b()/u() helpers) reflecting its 2006 origins, though the package now advertises support through Python 3.12 and PyPy in its classifiers and CI matrix. Packaging is classic setuptools/setup.py with no build backend beyond that, and tox.ini drives the multi-version test matrix.

Code Quality Tests live in a single tests/tests.py (~880 lines) using the standard-library unittest framework, with dozens of fixture .po/.mo files under tests/ covering encodings, malformed/edge-case entries (indentation, BOM, fuzzy headers, obsolete previous-msgid, syntax errors), and round-trip save/reload behavior. CI (.github/workflows/ci.yml plus a legacy .travis.yml) runs the suite via tox across Python 2.7 through 3.12 and PyPy, with codecov coverage reporting wired in. There are no type hints and no linter/formatter config in the repo — the codebase predates and doesn’t adopt modern typed-Python conventions — but naming and structure are consistent, and error handling around encoding detection and malformed PO/MO input is explicit rather than silently swallowed.

What Makes It Unique polib’s distinguishing choice is being a pure-Python, dependency-free implementation of both the PO/POT text-grammar parser and the MO binary-format reader/writer in one small module — most alternatives either shell out to GNU gettext’s msgfmt/msgunfmt or only support one direction (parse-only, or generate-only). Making POFile/MOFile subclass list and giving entries plain attribute access is a deliberately unglamorous but ergonomic design that lets scripts manipulate catalogs with ordinary Python idioms instead of a bespoke query API.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search