Tutorials

Complete Translation Workflow: POT to PO to MO

wordpresstranslation-workflowpot-filespo-filesmo-filesgettext

Master the full WordPress translation workflow from POT file creation to MO file deployment. A comprehensive guide for developers and translators.

Complete Translation Workflow: POT to PO to MO

If you're new to WordPress internationalization, the POT → PO → MO workflow can feel like alphabet soup. What are these files? Why do you need all three? How do they work together?

Let me walk you through the complete translation workflow from start to finish, explaining every step, every file type, and how they all connect.

By the end of this guide, you'll understand exactly how to take a WordPress theme or plugin from English-only to fully multilingual.

The Big Picture: Why Three File Types?

Before we dive into the workflow, let's understand why WordPress uses three different file formats.

POT (Portable Object Template)

  • Purpose: The master template containing all translatable strings
  • Content: Source strings only, no translations
  • Who uses it: Developers and translators
  • Human-readable: Yes (plain text)

PO (Portable Object)

  • Purpose: Language-specific translation file
  • Content: Source strings + translations
  • Who uses it: Translators
  • Human-readable: Yes (plain text)

MO (Machine Object)

  • Purpose: Compiled binary for WordPress to use
  • Content: Same as PO, but optimized for speed
  • Who uses it: WordPress (automatically)
  • Human-readable: No (binary format)

Why not just use one file?

Because each serves a different purpose:

  • POT: The reusable template for creating translations in any language
  • PO: The editable source of truth for translators
  • MO: The performance-optimized file WordPress loads on every page

Think of it like source code vs. compiled binaries. You write source code (PO), compile it (MO), and distribute templates (POT).

Step-by-Step Workflow

Let's walk through the complete workflow with a real example: translating a WordPress plugin into Spanish and French.

Step 1: Prepare Your Code for Translation

Before you can create translation files, your code needs to be internationalized (i18n).

This means wrapping all user-facing strings in WordPress translation functions:

Before (not translatable):

echo "Welcome to my plugin";

After (translatable):

echo __('Welcome to my plugin', 'my-plugin');

Common translation functions:

  • __() - Returns translated string
  • _e() - Echoes translated string
  • _n() - Handles plural forms
  • _x() - Provides context for ambiguous strings

Example plugin code:

<?php
// functions.php

function my_plugin_welcome_message() {
    echo '<h1>' . __('Welcome to My Plugin', 'my-plugin') . '</h1>';
    echo '<p>' . __('Click the button below to get started.', 'my-plugin') . '</p>';
    echo '<button>' . __('Get Started', 'my-plugin') . '</button>';
}

function my_plugin_comment_count($count) {
    return sprintf(
        _n('%d comment', '%d comments', $count, 'my-plugin'),
        $count
    );
}

Notice the text domain ('my-plugin'). This is critical—it tells WordPress which translation file to load.

Step 2: Generate the POT File

Once your code is internationalized, you extract all the translatable strings into a POT file.

Method A: Using WP-CLI (Recommended)

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

This scans all your PHP files, finds translation functions, and generates a POT file.

Method B: Using Poedit

  1. Open Poedit
  2. File → New from source code
  3. Select your plugin directory
  4. Poedit scans and creates a POT file

What the POT file looks like:

# Translation template for My Plugin
# Copyright (C) 2024
# This file is distributed under the same license as the plugin.

msgid ""
msgstr ""
"Project-Id-Version: My Plugin 1.0\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"

#: functions.php:5
msgid "Welcome to My Plugin"
msgstr ""

#: functions.php:6
msgid "Click the button below to get started."
msgstr ""

#: functions.php:7
msgid "Get Started"
msgstr ""

#: functions.php:12
msgid "%d comment"
msgid_plural "%d comments"
msgstr[0] ""
msgstr[1] ""

Key points:

  • Each msgid is a source string
  • All msgstr fields are empty (this is just a template)
  • File references show where each string appears in your code
  • Plural forms are defined with msgid_plural

Step 3: Create Language-Specific PO Files

Now you take the POT template and create a PO file for each language you want to support.

Option 1: Manual Creation

Copy the POT file and rename it:

  • my-plugin.potmy-plugin-es_ES.po (Spanish)
  • my-plugin.potmy-plugin-fr_FR.po (French)

Option 2: Using Poedit

  1. Open Poedit
  2. File → New from POT file
  3. Select your POT file
  4. Choose target language (e.g., Spanish)
  5. Save as my-plugin-es_ES.po

Option 3: Using POForge (Automated)

  1. Upload POT file to POForge
  2. Select target language (Spanish)
  3. Let AI translate
  4. Download my-plugin-es_ES.po

POForge automatically creates the PO file and fills in all translations—no manual work.

Step 4: Translate the Strings

This is where the actual translation happens.

Option A: Manual Translation

Open the PO file in a text editor and fill in each msgstr:

#: functions.php:5
msgid "Welcome to My Plugin"
msgstr "Bienvenido a Mi Plugin"

#: functions.php:6
msgid "Click the button below to get started."
msgstr "Haga clic en el botón de abajo para comenzar."

#: functions.php:7
msgid "Get Started"
msgstr "Comenzar"

Time required: Hours to days for large projects.

Option B: Using POForge AI

Upload POT → Choose language → Download translated PO file.

Time required: 5-10 minutes for most projects.

Step 5: Handle Plural Forms

Plural forms are tricky because different languages have different rules.

English has 2 forms:

  • 1 comment
  • 2 comments

Spanish also has 2 forms:

  • 1 comentario
  • 2 comentarios

But Russian has 3 forms, and Arabic has 6 forms!

The PO file header defines the plural rule for each language:

"Plural-Forms: nplurals=2; plural=(n != 1);\n"

For Spanish, the translation looks like:

msgid "%d comment"
msgid_plural "%d comments"
msgstr[0] "%d comentario"
msgstr[1] "%d comentarios"

POForge handles this automatically—you don't need to know the plural rules for every language.

Step 6: Compile PO to MO

Once your PO file is fully translated, you need to compile it into an MO file.

Why? Because WordPress can't use PO files directly. It needs the binary MO format for performance.

Option A: Command Line

msgfmt my-plugin-es_ES.po -o my-plugin-es_ES.mo

Option B: WP-CLI

wp i18n make-mo my-plugin-es_ES.po

Option C: Poedit

Save the PO file in Poedit → It automatically generates the MO file.

Option D: POForge

POForge automatically generates both PO and MO files when you download. No manual compilation needed.

Step 7: Deploy to WordPress

Upload both files to your WordPress site:

wp-content/
  languages/
    plugins/
      my-plugin-es_ES.po
      my-plugin-es_ES.mo
      my-plugin-fr_FR.po
      my-plugin-fr_FR.mo

Or place them in your plugin's languages/ directory:

wp-content/
  plugins/
    my-plugin/
      languages/
        my-plugin-es_ES.po
        my-plugin-es_ES.mo

Step 8: Test Your Translations

  1. Go to Settings → General in WordPress
  2. Change Site Language to Spanish (Español)
  3. Visit your plugin's page
  4. All text should now appear in Spanish

If something's not translating:

  • Check that the text domain matches
  • Verify file naming (e.g., my-plugin-es_ES.mo)
  • Clear WordPress cache
  • Check file permissions

Real-World Example: Full Workflow with POForge

Let's put it all together with a real example.

Goal: Translate a WordPress plugin into Spanish, French, and German.

Traditional Workflow (Without POForge)

  1. Internationalize code: Wrap strings in __(), _e(), etc.
  2. Generate POT file: wp i18n make-pot
  3. Create PO files: Copy POT 3 times, rename for each language
  4. Translate manually: 1,000 strings × 3 languages = 3,000 translations (weeks of work)
  5. Compile to MO: msgfmt for each language
  6. Upload to WordPress: 6 files (3 PO + 3 MO)
  7. Test: Switch language in WordPress and verify

Time: Days to weeks
Cost: $500-2,000 (if hiring translators)

POForge Workflow

  1. Internationalize code: Same as above
  2. Generate POT file: wp i18n make-pot
  3. Upload POT to POForge
  4. Select Spanish → AI translates in 5 minutes
  5. Download Spanish PO + MO
  6. Repeat for French → 5 minutes
  7. Repeat for German → 5 minutes
  8. Upload 6 files to WordPress
  9. Test: Everything works

Time: 20-30 minutes
Cost: $5-10 (depending on string count)

POForge doesn't just make it faster—it makes it accessible to anyone.

What If You Update Your Plugin?

Here's where the workflow really shines.

You release version 2.0 of your plugin. You added 50 new strings but kept 950 old ones.

Traditional Workflow:

  1. Regenerate POT (now has 1,000 strings)
  2. Update PO files (manually translate 50 new strings × 3 languages)
  3. Recompile to MO

Time: Hours
Cost: $50-200

POForge Workflow:

  1. Regenerate POT (1,000 strings)
  2. Upload to POForge
  3. Translation memory recognizes 950 existing strings (0 credits)
  4. AI translates 50 new strings (50 credits per language)
  5. Download updated PO + MO files

Time: 10 minutes
Cost: $1-2 (only new strings)

Translation memory is a huge cost saver over time.

Common Mistakes to Avoid

1. Wrong File Naming

WordPress expects specific filename patterns:

✅ Correct: my-plugin-es_ES.mo
❌ Wrong: my-plugin-spanish.mo
❌ Wrong: es_ES.mo

The filename must match: {text-domain}-{locale}.mo

2. Missing Text Domain

// ❌ Wrong (no text domain)
__('Hello World');

// ✅ Correct
__('Hello World', 'my-plugin');

Without the text domain, WordPress won't know which translation file to load.

3. Hard-Coded Strings

// ❌ Wrong (not translatable)
echo "Click here";

// ✅ Correct
echo __('Click here', 'my-plugin');

If it's not wrapped in a translation function, it won't appear in the POT file.

4. Forgetting to Compile PO to MO

WordPress only uses MO files. Even if you have a perfect PO file, without the MO file, nothing will translate.

Tools Summary

Here's a quick reference for all the tools mentioned:

Task Tool Options
Internationalize code Manual (WordPress functions)
Generate POT WP-CLI, Poedit, Grunt/Gulp plugins
Create PO files Poedit, POForge
Translate strings Manual, Poedit, professional translators, POForge AI
Compile to MO msgfmt, WP-CLI, Poedit, POForge
Test translations WordPress language switcher

POForge handles 3 out of 7 steps automatically (create PO, translate, compile MO)—saving you the most time-consuming parts.

Final Thoughts

The POT → PO → MO workflow is the backbone of WordPress internationalization. It's designed to:

  • Separate code from content
  • Enable community translation
  • Optimize performance with compiled binaries

The workflow is simple:

  1. Write internationalized code
  2. Generate a POT file
  3. Create PO files for each language
  4. Translate the strings
  5. Compile to MO files
  6. Deploy to WordPress

With POForge, steps 3-5 become:

  1. Upload POT
  2. Download PO + MO

No command-line tools. No desktop software. No weeks of manual translation.

Whether you're translating a small theme or a massive plugin, POForge makes the workflow fast, simple, and affordable.

Ready to translate your WordPress project? Upload your POT file to POForge and complete the workflow in minutes.

Complete Translation Workflow: POT to PO to MO - POForge Blog