What Is a .PO File?

Portable Object (PO) for i18n

📂Data
🏷️.po
🎯text/x-gettext-translation

Gettext Portable Object (.po)

Overview

A .po file is a Portable Object translation catalogue used by GNU gettext, the internationalisation system behind a very large share of open-source software. It pairs each translatable string in a program with its translation into one target language, along with the source locations where the string appears and any translator notes.

PO files are plain text and designed to be edited by translators who are not programmers. Before use at runtime they are compiled into binary .mo (Machine Object) files, which the application loads.

Technical Specifications

Format Details

  • MIME Type: text/x-gettext-translation
  • File Extensions: .po (editable catalogue), .pot (template), .mo (compiled binary)
  • Category: Data
  • Encoding: UTF-8 (declared in the header)
  • System: GNU gettext
  • Naming convention: de.po, fr_CA.po, pt_BR.po - language and optional region

Identification

PO files are recognisable from their entry structure - msgid and msgstr pairs, with # comments:

#: src/app.c:142
msgid "File not found"
msgstr "Datei nicht gefunden"

The first entry in every file is a special one with an empty msgid, carrying metadata as email-style headers.

File Structure

The header entry

# German translation of ExampleApp.
# Copyright (C) 2025 Example Project
# This file is distributed under the same licence as ExampleApp.
#
msgid ""
msgstr ""
"Project-Id-Version: ExampleApp 2.1\n"
"Report-Msgid-Bugs-To: bugs@example.com\n"
"POT-Creation-Date: 2025-03-04 10:12+0000\n"
"PO-Revision-Date: 2025-03-11 14:03+0100\n"
"Last-Translator: Jane Doe <jane@example.com>\n"
"Language-Team: German <de@example.com>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

The Plural-Forms line is the important one: it is a C-style expression that selects which plural form to use for a given number, and it differs per language.

Comment types

Each prefix means something specific:

#  translator-added note
#. comment extracted from the source code
#: src/main.c:88 src/ui.c:12      source references
#, fuzzy, c-format                flags
#| msgid "previous original"      the prior text of a fuzzy entry

The fuzzy flag marks an entry whose source string changed since it was translated. Fuzzy entries are not used at runtime: the original English shows instead - until a translator reviews and clears the flag.

Plural forms

Languages differ in how many plural categories they need. English has two, Polish has three, Arabic has six:

#: src/files.c:203
#, c-format
msgid "%d file deleted"
msgid_plural "%d files deleted"
msgstr[0] "%d Datei gelöscht"
msgstr[1] "%d Dateien gelöscht"

Contexts

When one English word needs different translations depending on where it appears, msgctxt disambiguates:

msgctxt "menu|File"
msgid "Open"
msgstr "Öffnen"

msgctxt "status|connection"
msgid "Open"
msgstr "Offen"

The Translation Workflow

source code
    │  xgettext - scan for translatable strings
    ▼
messages.pot   (template, no translations)
    │  msginit - create a new language catalogue
    ▼
de.po          (translator works here)
    │  msgfmt - compile
    ▼
de.mo          (binary, loaded at runtime)

When source strings change, msgmerge updates existing .po files against a regenerated .pot, preserving completed translations and marking changed ones fuzzy.

History and Development

GNU gettext was written by Ulrich Drepper in the mid-1990s, standardising an approach that Sun had pioneered. It solved the problem of separating user-visible text from code so that translation could happen independently of development.

The system spread far beyond C. PHP, Python, Ruby, Perl, JavaScript, and Rust all have gettext bindings, and the format underpins WordPress translations, Django's django.po catalogues, and the localisation of most Linux desktop software. Web platforms such as Weblate, Transifex, Crowdin, and Pootle use PO as their interchange format.

Common Use Cases

  • Open-source application localisation: GNOME, KDE, and most GNU software.
  • WordPress themes and plugins: the standard translation mechanism.
  • Django and Flask projects: via django.po and Flask-Babel.
  • Continuous localisation pipelines: PO as the exchange format between developers and translation platforms.
  • Community translation: the format is simple enough for non-technical volunteers.

How to Open a PO File

Dedicated editors

  • Poedit: the most widely used cross-platform PO editor, with translation memory and validation.
  • Lokalize (KDE) and Gtranslator (GNOME) - desktop editors integrated with their environments.
  • Weblate / Transifex / Crowdin: web platforms that import and export PO.
  • Any text editor: the format is plain UTF-8 text and perfectly readable.

Command-line tooling

# Extract translatable strings from source into a template
xgettext -o messages.pot --from-code=UTF-8 src/*.c

# Start a new language catalogue from the template
msginit -i messages.pot -l de_DE -o de.po

# Merge new source strings into an existing translation
msgmerge --update de.po messages.pot

# Compile for runtime use
msgfmt -o locale/de/LC_MESSAGES/app.mo de.po

# Check for errors and format-string mismatches
msgfmt --check --statistics -o /dev/null de.po

# Show completion statistics
msgfmt --statistics -o /dev/null de.po
# → 412 translated messages, 18 fuzzy translations, 7 untranslated messages.

Useful maintenance commands

# List only untranslated entries
msgattrib --untranslated de.po

# Strip obsolete entries
msgattrib --no-obsolete -o de.po de.po

# Compare two catalogues
msgcmp de.po messages.pot

Advantages

  • Human-readable and diffable: reviews well in version control.
  • Translator-friendly: no programming knowledge required, with strong editor support.
  • Rich context: source references, extracted comments, and translator notes travel with each string.
  • Proper plural handling: a per-language plural rule expression, not an English-shaped assumption.
  • Fuzzy tracking: changed strings are flagged rather than silently mistranslated.
  • Mature ecosystem: decades of tooling and platform support.

Limitations

  • Compilation step required: .po must become .mo before runtime.
  • English-as-key fragility: using the source string as the key means fixing a typo in English invalidates every translation of that string.
  • Merge conflicts: line-oriented diffs on frequently regenerated files can be noisy.
  • Limited rich content: designed for short UI strings, not documents or markup-heavy content.
  • Context is optional: without msgctxt, ambiguous short strings get mistranslated.
  • XML: the basis of XLIFF, the other major translation interchange format.
  • JSON: common for JavaScript i18n libraries.
  • YAML: used by Rails and some frontend frameworks for locale files.
  • MARKDOWN: often the source for translated documentation, converted via PO with po4a.

File Information

File Description

Portable Object (PO) for i18n

Category

Data

Extensions

.po

MIME Type

text/x-gettext-translation

Related File Types

Other file types in the Data category you might also need:

Start Analyzing PO Files Now

Use our free AI-powered tool to detect and analyze Portable Object (PO) for i18n files instantly with Google's Magika technology.

Try File Detection Tool