How to generate .pot files with WP-CLI i18n in WordPress

Contents

Introduction

This tutorial covers everything needed to generate .pot files for WordPress projects using the WP-CLI i18n command. It explains prerequisites, installation, options, common problems, best practices, automation examples, and step-by-step commands for themes and plugins. Examples and copy-paste commands are provided in runnable form.

Prerequisites

  • WordPress project (theme or plugin code with gettext functions such as __(), _e(), _n(), _x(), _ex(), _nx(), esc_html__(), esc_attr__(), etc.).
  • WP-CLI installed and available on the command line. See WP-CLI.
  • WP-CLI i18n package (wp-cli/i18n-command) installed as a WP-CLI package (installation instructions below).
  • PO/MO tools for translators: Poedit, gettext utilities (msgfmt/msginit/xgettext), or any PO editor.
  • Files must be encoded as UTF-8 (without BOM). Non-UTF-8 or BOM may break extraction.

Install or enable the WP-CLI i18n command

If the i18n command is not available, install it as a WP-CLI package:

wp package install wp-cli/i18n-command

Verify the command is available:

wp i18n --help

Basic usage: generate a .pot file

The canonical command is:

wp i18n make-pot ltsource-dirgt ltpot-filegt [--domain=lttext-domaingt] [--slug=ltsluggt] [options]

Common practical examples follow.

Generate a .pot for a plugin

wp i18n make-pot ./wp-content/plugins/my-plugin ./wp-content/plugins/my-plugin/languages/my-plugin.pot --domain=my-plugin --slug=my-plugin

Run this from your project root (or adjust paths accordingly). This command scans PHP and JavaScript files (by default) and writes a POT to languages/my-plugin.pot.

Generate a .pot for a theme

wp i18n make-pot ./wp-content/themes/my-theme ./wp-content/themes/my-theme/languages/my-theme.pot --domain=my-theme --slug=my-theme

Key options and what they do

  • ltsource-dirgt — Directory to scan (file path or dot for current directory).
  • ltpot-filegt — Output POT file path.
  • –domain=lttext-domaingt — Text domain to use for creation and header (commonly your plugin/theme slug).
  • –slug=ltsluggt — Sets the Project-Id-Version and Report-Msgid-Bugs-To headers useful for naming/versioning in the POT header.
  • –exclude=ltpathsgt — Comma-separated paths to exclude (vendor,node_modules,tests,build,dist).
  • –include=ltpathsgt — Comma-separated list of file globs to include (if you need to limit scanning).
  • –skip-js — Do not scan JavaScript files.
  • –skip-php — Do not scan PHP files (rare).
  • –merge=ltfilegt — Merge existing POT file entries (useful to preserve translator comments).
  • –merge-domain=ltdomaingt — Specify the text domain of the POT being merged.
  • –keywords=ltkeywordgt — Add or override keywords (translation functions) and their argument positions—useful for custom wrapper functions.
  • –project=ltproject-namegt — Set Project-Id-Version header manually.
  • –buffer=ltbytesgt — Increase buffer when scanning huge projects.
  • –debug, –porcelain, –allow-root — Misc developer/debug options.

Common, practical examples

Exclude node_modules and vendor

wp i18n make-pot . languages/project.pot --domain=project --exclude=node_modules,vendor,tests

Only scan a plugin directory (safe, fast)

wp i18n make-pot wp-content/plugins/my-plugin wp-content/plugins/my-plugin/languages/my-plugin.pot --domain=my-plugin

Specify extra custom translation keywords

If you have custom wrapper functions (for example my_translate( text, domain )), tell the extractor how to treat them:

wp i18n make-pot . languages/project.pot --keywords=__ --keywords=_e --keywords=my_translate:1,2

Keyword formats: functionName, functionName:arg-num, functionName:arg-num,context (see xgettext format). Add as many –keywords flags as needed.

Merge with an existing POT to retain manual changes or comments

wp i18n make-pot ./wp-content/plugins/my-plugin ./languages/my-plugin.pot --merge=./languages/my-plugin.pot --domain=my-plugin

Merging is useful when you want to preserve translator notes or header fields from an existing POT while refreshing strings.

Generating POT for a multi-component project (plugins theme)

# Generate one consolidated POT for the entire repository, excluding third-party dirs:
wp i18n make-pot . languages/project.pot --domain=project --exclude=vendor,node_modules,tests

Example: Proper plugin file headers and loading text domain

Ensure your plugin or theme has a correct text domain and loads translations. This helps the extractor and translators.


Post-generation: working with POT, PO and MO

  • Give the .pot file to translators or import into Poedit.
  • Translators create language-specific .po files (e.g., my-plugin-fr_FR.po).
  • Compile .po into .mo for distribution using msgfmt or Poedit’s export features.

Example using gettext utilities

# Initialize a new PO for French (creates my-plugin-fr_FR.po)
msginit --input=languages/my-plugin.pot --locale=fr_FR --output-file=languages/my-plugin-fr_FR.po

# Compile a PO into an MO that WP can load
msgfmt -o languages/my-plugin-fr_FR.mo languages/my-plugin-fr_FR.po

Best practices

  • Keep translations inside a languages/ folder at the root of your plugin or theme (common convention).
  • Name POT file after your project, e.g., languages/my-plugin.pot or languages/my-theme.pot.
  • Exclude third-party libraries (vendor/, node_modules/) to avoid noise in POT files.
  • Use consistent text domain matching plugin slug and header Text Domain value.
  • Avoid dynamic string concatenation in translation functions — use placeholders sprintf() instead. xgettext requires static string literals.
  • File encoding: UTF-8 without BOM for accurate extraction.
  • Document custom translation wrappers by adding --keywords flags to your generation script.
  • Automate POT generation in your release pipeline to keep translators up to date.

Automation examples

npm / package.json script

{
  scripts: {
    make-pot: wp i18n make-pot ./wp-content/plugins/my-plugin ./wp-content/plugins/my-plugin/languages/my-plugin.pot --domain=my-plugin --exclude=node_modules,vendor
  }
}

Composer script

{
  scripts: {
    make-pot: [
      wp i18n make-pot ./wp-content/themes/my-theme ./wp-content/themes/my-theme/languages/my-theme.pot --domain=my-theme --exclude=node_modules,vendor
    ]
  }
}

GitHub Actions step (example)

- name: Generate POT
  run: 
    wp package install wp-cli/i18n-command
    wp i18n make-pot ./wp-content/plugins/my-plugin ./wp-content/plugins/my-plugin/languages/my-plugin.pot --domain=my-plugin --exclude=node_modules,vendor

Troubleshooting common issues

  • No strings found — Verify your project uses WordPress gettext functions with a proper text domain, and that the extraction directory is correct. Check that strings are not concatenated at runtime (xgettext requires literal strings).
  • Wrong text domain — Ensure the Text Domain header in your plugin/theme file matches the domain you pass to --domain or the one used in gettext functions.
  • Non-UTF-8 or BOM in files — Convert files to UTF-8 without BOM. Many extractors fail on BOM.
  • Custom wrappers not detected — Add --keywords entries describing function argument positions.
  • Large projects memory or buffer issues — Use --buffer to increase memory buffer, exclude big directories, or scan subdirectories individually.
  • Header fields — Use --project and --slug to set Project-Id-Version and other header values if needed.

Advanced tips

  • Keep translators’ context — Use translators comments (/ translators: ... /) above the translation function to provide context. The extractor will include them in PO entries.
  • Plural forms — Use proper plural functions (_n(), _nx(), etc.) so plural forms are extracted correctly.
  • JavaScript translations — WP i18n command extracts strings from JavaScript (unless --skip-js). For modern JS using wp.i18n/tinyten, also consider wp i18n make-json to generate translation JSON for JS contexts.
  • Continuous integration — Regenerate POT on each release or when strings change. Commit POT together with release branch or publish it to a translation platform.

Example workflow (concise)

  1. Ensure plugin/theme has proper Text Domain and load__textdomain() call.
  2. Install WP-CLI and the i18n package:
    wp package install wp-cli/i18n-command
  3. Run extractor for your specific plugin/theme directory:
    wp i18n make-pot ./wp-content/plugins/my-plugin ./wp-content/plugins/my-plugin/languages/my-plugin.pot --domain=my-plugin --exclude=node_modules,vendor
  4. Distribute the .pot to translators or import it in Poedit/translation platform.
  5. Receive .po files, compile to .mo and ship in languages/ or push to translate.wordpress.org as appropriate.

Summary

WP-CLI i18n is a reliable, scriptable way to generate .pot files for WordPress projects. Key points: install the wp-cli/i18n-command package, keep a consistent text-domain, exclude third-party directories, add custom keywords when needed, automate the command in builds/releases, and ensure files are UTF-8. Use the examples above to integrate POT generation in your workflow.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

Your email address will not be published. Required fields are marked *