Skip to content
Development
Skill

/wp-i18n-workflow

Use when managing the full translation workflow for a WordPress plugin — registering text domain with load_plugin_textdomain, wrapping strings with __(), _e(), esc_html__(), esc_attr__(), _n(), _x(), _ex(), _nx(), generating POT with wp i18n make-pot, compiling .po to .mo (wp

From plugin
wp-dev-skills
2719 skills1 command
Install
$ npx -y skills add mralaminahamed/wp-dev-skills --skill wp-i18n-workflow --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/wp-i18n-workflow

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when managing the full translation workflow for a WordPress plugin — registering text domain with load_plugin_textdomain, wrapping strings with __(), _e(), esc_html__(), esc_attr__(), _n(), _x(), _ex(), _nx(), generating POT with wp i18n make-pot, compiling .po to .mo (wp

SKILL.md

wp-i18n-workflow.SKILL.md
name: wp-i18n-workflow
description: "Use when managing the full translation workflow for a WordPress plugin — registering text domain with load_plugin_textdomain, wrapping strings with __(), _e(), esc_html__(), esc_attr__(), _n(), _x(), _ex(), _nx(), generating POT with wp i18n make-pot, compiling .po to .mo (wp i18n make-mo / msgfmt) and .json (wp i18n make-json), wiring JS translations with wp_set_script_translations and @wordpress/i18n (__, _n, _x, sprintf), adding translator comments, submitting to translate.wordpress.org / GlotPress, automating in GitHub Actions, or debugging missing translations. Triggers: \"make this string translatable\", \"generate POT file\", \"strings not translating\", \"wp i18n make-pot\", \"JS translations not loading\", \"wp_set_script_translations\", \"how do I translate my block\", \"RTL language support\", \"missing translation\", \"submit to GlotPress\", \"translation missing in JS\", \"update my .po files\", \"load_plugin_textdomain\", \"__ vs esc_html__\", \"_n() plural strings\", \"_x() context string\", \"translator comment format\", \"wp i18n make-json\", \"msgfmt compile\", \"translate.wordpress.org submission\", \"language pack\", \"text domain mismatch\", \"i18n automation in CI\". Not for: theme translation (paths differ, but logic is the same)."

WordPress Plugin i18n Workflow

> **Model note:** Fully mechanical — POT generation, PO/MO compilation, and script registration are tool invocations. `haiku` handles all steps; no cross-file reasoning needed.

Full translation pipeline for WordPress plugins: POT generation, PO/MO compilation, JavaScript translations, translate.wordpress.org GlotPress, and language pack distribution. Covers the coding conventions and the tooling workflow.

When to use

  • "Generate a POT file for my plugin", "update translation strings".
  • "Set up JavaScript translations", "translate strings in React/block editor".
  • "Submit to translate.wordpress.org", "set up language packs".
  • "Why aren't my translations loading?", "debug missing .mo file".
  • "Add translator comments", "handle plurals and context strings".

**Not for:** PHPCS i18n sniff violations — use `wp-coding-standards`. Checking i18n completeness in an audit — use `wp-plugin-audit` Dimension B.

Method

1. PHP i18n conventions

All translatable strings must use the plugin's **text domain** consistently. The text domain must match the `Text Domain:` header and the `load_plugin_textdomain()` call.

// Basic translation
__( 'Settings', 'my-plugin' )
_e( 'Save Changes', 'my-plugin' )        // echo version

// With HTML context (escape + translate combined)
esc_html__( 'Error message', 'my-plugin' )
esc_attr__( 'Tooltip text', 'my-plugin' )

// Plurals
_n( '%d item', '%d items', $count, 'my-plugin' )
sprintf( _n( '%d item', '%d items', $count, 'my-plugin' ), $count )

// Context strings (disambiguation for translators)
_x( 'Post', 'noun: a blog post', 'my-plugin' )
_ex( 'Draft', 'verb: save as draft', 'my-plugin' )

// Plural with context
_nx( '%d reply', '%d replies', $count, 'comment count', 'my-plugin' )

**Translator comments** — required for strings with placeholders:

/* translators: %s: plugin version number */
sprintf( __( 'Version %s', 'my-plugin' ), MY_PLUGIN_VERSION )

/* translators: 1: post title, 2: author name */
sprintf( __( '"%1$s" by %2$s', 'my-plugin' ), $title, $author )

Comment must be on the line immediately before the function call and start with `translators:`.

2. Load text domain

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

For WP.org plugins, language packs are auto-loaded from `translate.wordpress.org` — `load_plugin_textdomain()` only needed for bundled `.mo` files or local development.

3. Generate POT file

# WP-CLI (preferred)
wp i18n make-pot . languages/my-plugin.pot \
  --domain=my-plugin \
  --exclude=vendor,node_modules,tests,build \
  --headers='{"Project-Id-Version":"My Plugin 1.0.0","Report-Msgid-Bugs-To":"https://github.com/my-org/my-plugin/issues"}'

# Update existing POT (merges new strings, marks removed as obsolete)
wp i18n make-pot . languages/my-plugin.pot --domain=my-plugin

Commit `languages/my-plugin.pot` to git. Translators use this as the source.

4. Compile PO → MO

`.po` files are human-editable; `.mo` are compiled binary files loaded by PHP.

# Single file
wp i18n make-mo languages/my-plugin-fr_FR.po

# All PO files in the directory
wp i18n make-mo languages/

# Using msgfmt (gettext tools)
msgfmt languages/my-plugin-fr_FR.po -o languages/my-plugin-fr_FR.mo

File naming convention: `{text-domain}-{locale}.po` / `.mo` Examples: `my-plugin-fr_FR.mo`, `my-plugin-de_DE.mo`, `my-plugin-pt_BR.mo`

5. JavaScript translations

**Block editor / React components** — use `@wordpress/i18n`:

import { __, _n, _x, sprintf } from '@wordpress/i18n';

const label = __( 'Save settings', 'my-plugin' );
const count = sprintf( _n( '%d item', '%d items', total, 'my-plugin' ), total );
const ctx   = _x( 'Draft', 'button label', 'my-plugin' );

**Generate JSON translation files:**

# From PO file — produces my-plugin-fr_FR-{hash}.json
wp i18n make-json languages/my-plugin-fr_FR.po --no-purge

**Register JS translations in PHP:**

function my_plugin_set_script_translations() {
    wp_set_script_translations(
        'my-plugin-editor',   // script handle (must be enqueued)
        'my-plugin',          // text domain
        plugin_dir_path( __FILE__ ) . 'languages'
    );
}
add_action( 'init', 'my_plugin_set_script_translations' );

For blocks registered via `block.json`, WP auto-calls `wp_set_script_translations` if `textdomain` is set in `block.json`:

{
    "textdomain": "my-plugin",
    "editorScript": "file:./index.js"
}

6. translate.wordpress.org (GlotPress)

WP.or

Read more
Ships withwp-dev-skills

Covers the complete WordPress plugin development lifecycle — build, test, audit, release, and ship to WP.org — for Claude Code, Gemini CLI, Cursor, Windsurf, Cline, Codex, GitHub Copilot, opencode, and more.

Get the whole plugin
Stats
27
Stars
3
Forks
Maintained
Maintenance
PHP
Language
MIT
License
1mo ago
Last commit
3mo ago
Created

Repo: mralaminahamed/wp-dev-skills

Other skills on wp-dev-skills.