Menu
What is a .pot File? A Beginner's Guide to Translation Templates
December 13, 2025

What is a .pot File? A Beginner's Guide to Translation Templates

What is a .pot File?


If you're a developer working on making your software available in multiple languages, you've probably encountered files with extensions like .pot, .po, and .mo. These files are the backbone of software internationalization, yet many developers find them confusing at first.

In this comprehensive guide, we'll break down everything you need to know about .pot files, how they work with .po and .mo files, and how to use them effectively in your projects.


Understanding Translation File Formats


Before diving deep into .pot files, let's understand the three main file types used in the gettext translation system:

POT (Portable Object Template)


A .pot file is a template file that contains all the original translatable strings extracted from your source code. Think of it as a master list of everything that needs to be translated. The "T" in POT stands for "Template" because this file serves as the starting point for all translations.


Key characteristics of .pot files:


  1. Contains only the original source strings (usually in English)
  2. Does not contain any translations
  3. Serves as a template for creating language-specific .po files
  4. Should be regenerated whenever source code changes
  5. Is not used directly by the application at runtime


PO (Portable Object)


A .po file is a language-specific translation file created from a .pot template. Each language you want to support requires its own .po file. For example, you might have fr_FR.po for French, de_DE.po for German, and es_ES.po for Spanish.


Key characteristics of .po files:


  1. Contains both original strings and their translations
  2. One file per language
  3. Human-readable and editable
  4. Created by copying a .pot file and adding translations
  5. Named using locale codes (e.g., pt_BR.po for Brazilian Portuguese)


MO (Machine Object)


A .mo file is the compiled, binary version of a .po file. This is what your application actually reads at runtime to display translated text.


Key characteristics of .mo files:


  1. Binary format, not human-readable
  2. Optimized for fast lookups
  3. Generated by compiling .po files
  4. Required for the application to use translations
  5. Smaller file size than .po files


The Translation Workflow


Understanding how these files work together is crucial. Here's the typical workflow:


Source Code → .pot file → .po files → .mo files → Application
     ↓            ↓            ↓            ↓
  Extract     Template    Translate     Compile
  strings     created     strings       for use


Step 1: Extract strings from source code


Tools scan your code for translatable strings (functions like __(), _e(), gettext(), etc.) and generate a .pot file.


Step 2: Create language-specific .po files


Translators receive the .pot file and create .po files for their target languages, adding translations for each string.


Step 3: Compile .po to .mo


The .po files are compiled into .mo files that the application can read efficiently.


Step 4: Application loads translations


At runtime, the application loads the appropriate .mo file based on the user's language preference.


Anatomy of a .pot File


Let's look at what's inside a .pot file. Here's a typical example:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: My Plugin 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-15 10:30+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: includes/class-main.php:45
msgid "Welcome to our application"
msgstr ""

#: includes/class-main.php:52
msgid "Settings"
msgstr ""

#: includes/class-main.php:67
#, php-format
msgid "Hello, %s!"
msgstr ""

#: includes/class-main.php:89
msgid "Save Changes"
msgstr ""

Breaking Down the Structure


Header Section


The header contains metadata about the translation project:

  • Project-Id-Version: Name and version of your project
  • POT-Creation-Date: When the .pot file was generated
  • Content-Type: Character encoding (usually UTF-8)


Translation Entries


Each translatable string has:


  • #: Reference to where the string appears in source code
  • #, Flags (like php-format for strings with placeholders)
  • msgid The original string
  • msgstr Empty in .pot files, filled in .po files


How .pot Files Are Generated


Different programming languages and frameworks have their own tools for extracting translatable strings.


WordPress


WordPress developers typically use WP-CLI or tools like Poedit:

wp i18n make-pot . languages/my-plugin.pot


In WordPress, translatable strings are wrapped in functions:


// Simple string
__( 'Hello World', 'my-plugin' );

// Echo directly
_e( 'Welcome', 'my-plugin' );

// With placeholders
sprintf( __( 'Hello, %s!', 'my-plugin' ), $name );

// Plural forms
_n( '%d item', '%d items', $count, 'my-plugin' );


Python/Django


Django uses the makemessages command:

python manage.py makemessages -l en


Python code uses gettext functions:

from django.utils.translation import gettext as _

message = _("Welcome to our site")


JavaScript


For JavaScript projects, tools like i18next-parser can extract strings:

i18next-parser 'src/**/*.js'

JavaScript translation functions:

// Using i18next
t('welcome_message')

// Using react-intl
<FormattedMessage id="welcome" defaultMessage="Welcome" />


PHP (General)


The xgettext command extracts strings from PHP:


xgettext --language=PHP --keyword=__ --keyword=_e \
         --from-code=UTF-8 -o messages.pot src/*.php

Best Practices for Working with .pot Files


1. Use Meaningful Text Domains


A text domain helps identify which translations belong to your project:

// Good - specific text domain
__( 'Settings', 'my-awesome-plugin' );

// Bad - generic or missing text domain
__( 'Settings' );

2. Provide Context When Needed

Some strings might need context for accurate translation:

// Without context - "Post" could be a noun or verb
__( 'Post', 'my-plugin' );

// With context - much clearer
_x( 'Post', 'noun: a blog post', 'my-plugin' );
_x( 'Post', 'verb: to publish', 'my-plugin' );

3. Handle Plurals Correctly

Different languages have different plural rules. Some have two forms, others have many:

// Correct way to handle plurals
printf(
    _n( '%d comment', '%d comments', $count, 'my-plugin' ),
    $count
);

4. Don't Translate Dynamic Content


Never include variables directly in translatable strings:

// Wrong - translator sees "%s commented on your post"
// but doesn't know what %s represents
__( "$username commented on your post", 'my-plugin' );

// Correct - use placeholders
sprintf( __( '%s commented on your post', 'my-plugin' ), $username );

5. Keep Strings Complete


Don't break sentences into multiple translatable parts:

// Wrong - word order varies by language
echo __( 'Click ', 'my-plugin' ) . '<a href="#">' . __( 'here', 'my-plugin' ) . '</a>';

// Correct - keep the full sentence together
printf( __( 'Click %shere%s to continue', 'my-plugin' ), '<a href="#">', '</a>' );

6. Regenerate .pot Files Regularly


Whenever you add, remove, or modify translatable strings, regenerate your .pot file to keep it in sync with your code.


Common Challenges and Solutions


Challenge 1: Missing Translations at Runtime


Problem: Your translations aren't showing up in the application.


Solutions:

  • Ensure .mo files are compiled from .po files
  • Check that .mo files are in the correct directory
  • Verify the locale is set correctly
  • Confirm the text domain is loaded properly


Challenge 2: Encoding Issues


Problem: Special characters appear corrupted.


Solutions:


  • Always use UTF-8 encoding
  • Set the correct Content-Type header in your .pot file
  • Ensure your source files are saved as UTF-8


Challenge 3: Outdated Translations


Problem: New strings aren't being translated.


Solutions:

  • Regenerate your .pot file after code changes
  • Use tools that can merge new strings into existing .po files
  • Establish a workflow for updating translations


Challenge 4: Context Loss


Problem: Translators don't understand the context of a string.


Solutions:


  • Use translator comments
  • Provide context with _x() or pgettext() functions
  • Include screenshots or documentation for translators


Tools for Working with .pot Files


Desktop Applications


Poedit A popular cross-platform editor for .po files. Can also extract strings from source code.


Lokalize A KDE-based translation tool with advanced features for professional translators.


GTranslator A GNOME translation editor with a clean, simple interface.


Command Line Tools


xgettext The original GNU gettext tool for extracting strings.


msgfmt Compiles .po files to .mo files.


msgmerge Merges new .pot files with existing translations.


Online Services


PoGen An AI-powered online tool that extracts translatable strings from your project files and can automatically generate translations in multiple languages.


2Q==


Crowdin A localization management platform for teams.


Transifex Cloud-based translation management with collaboration features.


.pot Files in Different Frameworks


WordPress


WordPress has a standardized location for translation files:

my-plugin/
├── languages/
│   ├── my-plugin.pot
│   ├── my-plugin-fr_FR.po
│   ├── my-plugin-fr_FR.mo
│   ├── my-plugin-de_DE.po
│   └── my-plugin-de_DE.mo
└── my-plugin.php

Load translations in your plugin:

function my_plugin_load_textdomain() {
    load_plugin_textdomain(
        'my-plugin',
        false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages/'
    );
}
add_action( 'plugins_loaded', 'my_plugin_load_textdomain' );


Laravel


Laravel uses JSON files by default, but also supports gettext:

resources/
├── lang/
│   ├── en/
│   │   └── messages.php
│   ├── fr/
│   │   └── messages.php
│   └── vendor/


Django


Django expects translation files in specific locations:

myapp/
├── locale/
│   ├── fr/
│   │   └── LC_MESSAGES/
│   │       ├── django.po
│   │       └── django.mo
│   └── de/
│       └── LC_MESSAGES/
│           ├── django.po
│           └── django.mo


The Future of Translation Files


While .pot/.po/.mo files remain the standard for many applications, newer formats are emerging:


JSON-based translations are becoming popular in JavaScript applications due to their simplicity and native browser support.


XLIFF (XML Localization Interchange File Format) is gaining traction in enterprise applications for its standardization and tool support.


ICU MessageFormat provides more sophisticated handling of plurals, gender, and other language-specific features.


However, the gettext system and .pot files remain widely used because of their maturity, extensive tooling, and broad framework support.


Conclusion


The .pot file is the foundation of the gettext translation system. It serves as the template that captures all translatable strings from your source code, making it possible to create translations in any language without modifying your original code.


Understanding how .pot files work with .po and .mo files empowers you to:

  • Properly internationalize your applications
  • Establish efficient translation workflows
  • Collaborate effectively with translators
  • Maintain translations as your project evolves


Whether you're building a WordPress plugin, a Django application, or any other software, mastering .pot files is an essential skill for reaching a global audience.


Ready to generate .pot files for your project? Try our AI-powered extraction tool that automatically identifies translatable strings and can even provide instant translations in multiple languages.