Gettext - PO/POT

Last updated: September 07, 2026Author: Jakub Pomykała

po-pot
File format value
po-pot

PO files (Portable Object) are plain-text files used by the Gettext system for software localization and internationalization. PO file format was released initially in 1990, and it's still widely used in many Unix-like systems, and modern frameworks like Django, Ruby on Rails, WordPress, and many others.

PO files are used to store translations for a single language. They contain message ids (translation key), comments, and contexts, and translated messages. We can distinguish two different file formats:

  • POT file format (Portable Object Template) is used as a template for translating text, it does not contain translated messages, only message ids, comments, and contexts. You can treat it as a template.
  • PO file format is a translation record that contains the same values as a POT file, but it also contains translated strings for a single language.

Enable 'Use translation keys as translations' import option, if your PO files don't have translations under the msgstr values and your translations are in msgid. In CLI and API, this option is named USE_KEYS_AS_TRANSLATIONS.

Enable 'Import context as namespace' option for import and 'Use namespace as message context' option for export, if you use msgctxt in your PO/POT files. This will allow distinguishing different translations with the same msgid. In CLI and API, both options are named MSGCTXT_AS_NAMESPACE.

File format example

Each translation unit consists of a message id, a message string, and optional metadata like comments, contexts, and plural forms.

msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=3; plural=(n == 1 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2));\n"
"Language: pl\n"

# Single line
msgctxt "Translation context"
msgid "Translation key"
msgstr "Translation message"

# Multiple lines
msgid ""
"The input text "
"split to several "
"lines"
msgstr ""
"Translation "
"can also be splitted."

Many PO files contain metadata at the beginning of the file, which is used to describe the file format, language, and plural forms. The metadata is stored in the form of key-value pairs, separated by colons.

In the example above, the metadata contains the following keys:

  • Content-Type - specifies the content type of the file
  • Plural-Forms - specifies the plural forms used in the file
  • Language - specifies the language of the translations

Some frameworks and tools require the metadata to be present in the file, for example, the Plural-Forms and Content-Type headers are required by the Gettext library.

Message id

The message id is the original text that needs to be translated.

msgid "Hello, world!"

Message string

The message string is the translated text that corresponds to the message id.

msgstr "Witaj, świecie!"

Context

The context is used to provide additional information about the translation.

msgctxt "Greeting"
msgid "Hello"
msgstr "Witaj"

If you use msgctxt to distinguish different translations with the same msgid, consider importing the context as namespace in SimpleLocalize. You can find 'Import context as namespace' option in the import settings.

Plural forms

Plural forms are used to handle different grammatical forms of the same word. The plural form is defined in the Plural-Forms header, which contains a formula that determines the correct plural form based on the number of items.

msgid "There is one apple"
msgid_plural "There are %d apples"
msgstr[0] "Jest jedno jabłko"
msgstr[1] "Są %d jabłka"
msgstr[2] "Są %d jabłek"

SimpleLocalize fills the Plural-Forms header automatically for the language for which the translations are exported.

Comments

Comments in PO files can be single-line or multi-line. They can be used to describe the context of the translation, provide additional information. Comments are prefixed with a hash sign #, #., #: or #,.

# This is a translator comment
#. Translators: This message appears on the home page only
#: mysite/views.py:7 templates/home.html:12
#, python-format
  • # - translator comment, written by hand in the PO file
  • #. - extracted comment, added by xgettext from the source code to provide additional information about the message
  • #: - reference comment, used to specify the location of the message in the code
  • #, - flags, e.g. python-format, c-format or fuzzy

On import, the translator comment (#) becomes the key description. If there is no translator comment, the extracted comment (#.) is used instead. On export, the key description is written back as a # comment (or as #. with the REFERENCE_COMMENTS export option).

Metadata stored in key attributes

SimpleLocalize keeps the remaining gettext metadata in the translation key attributes under the po-pot key, so that it survives the round trip between your repository and the Translation Editor and can be used as context for auto-translation:

{
  "po-pot": {
    "msgctxt": "Greeting",
    "references": ["mysite/views.py:7", "templates/home.html:12"],
    "flags": ["python-format"],
    "translator-comment": "This is a translator comment",
    "extracted-comment": "Translators: This message appears on the home page only"
  }
}
  • msgctxt - message context, unless you import the context as namespace
  • references - all #: locations of the message in the code
  • flags - format flags from #,; the fuzzy flag is not stored, because it describes a single translation rather than the message
  • translator-comment and extracted-comment - both comments kept separately, with line breaks preserved

Attributes are stored per translation key, not per language, so the values from the most recently imported PO file are kept. Imports only replace the po-pot entry and leave attributes from other sources, such as Figma or other file formats, untouched. On export, all of them are written back to the PO file. Keys without any gettext metadata are exported exactly as before.

Example gettext usage

printf(gettext("Hello world! I'm %s.\n"), application_title);
printf(_("Hello world! I'm %s.\n"), application_title);

Upload with CLI

Upload source language translations to SimpleLocalize using CLI.

simplelocalize upload --apiKey PROJECT_API_KEY \
  --uploadFormat po-pot \
  --uploadLanguageKey en \
  --uploadPath ./messages_en.po

Learn more about SimpleLocalize CLI and translations upload feature.

Download with CLI

simplelocalize download --apiKey <PROJECT_API_KEY> \
  --downloadFormat po-pot \
  --downloadLanguageKey pl,fr,de \
  --downloadPath ./messages_{lang}.po

Learn more about SimpleLocalize CLI and translations download feature.

References