wp-i18n-audit

Audits WordPress plugin or theme PHP code for internationalization (i18n) correctness — text-domain consistency, use of escaped translation helpers (esc_html__, esc_attr__, esc_html_e), correct placeholder helpers (sprintf with translator comments, _n for plurals, _x for context), no variable text-domains, no concatenation inside __() calls, presence of load_plugin_textdomain or load_theme_textdomain, and matching declared Text Domain in the plugin/theme header. Use before plugin/theme release, when reviewing contributor PRs, when adding new strings, when migrating to a new text domain, or when a translator reports issues with the .pot file.

Best use case

wp-i18n-audit is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Audits WordPress plugin or theme PHP code for internationalization (i18n) correctness — text-domain consistency, use of escaped translation helpers (esc_html__, esc_attr__, esc_html_e), correct placeholder helpers (sprintf with translator comments, _n for plurals, _x for context), no variable text-domains, no concatenation inside __() calls, presence of load_plugin_textdomain or load_theme_textdomain, and matching declared Text Domain in the plugin/theme header. Use before plugin/theme release, when reviewing contributor PRs, when adding new strings, when migrating to a new text domain, or when a translator reports issues with the .pot file.

Teams using wp-i18n-audit should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/wp-i18n-audit/SKILL.md --create-dirs "https://raw.githubusercontent.com/nvdigitalsolutions/mcp-ai-wpoos/main/.agents/skills/wp-i18n-audit/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/wp-i18n-audit/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How wp-i18n-audit Compares

Feature / Agentwp-i18n-auditStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Audits WordPress plugin or theme PHP code for internationalization (i18n) correctness — text-domain consistency, use of escaped translation helpers (esc_html__, esc_attr__, esc_html_e), correct placeholder helpers (sprintf with translator comments, _n for plurals, _x for context), no variable text-domains, no concatenation inside __() calls, presence of load_plugin_textdomain or load_theme_textdomain, and matching declared Text Domain in the plugin/theme header. Use before plugin/theme release, when reviewing contributor PRs, when adding new strings, when migrating to a new text domain, or when a translator reports issues with the .pot file.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# WordPress i18n audit

A focused review of translation correctness in WP plugin or theme PHP. Catches the boring mistakes that cause `.pot` extraction to break, translators to ship wrong files, or strings to silently fail to translate at runtime.

## When to use this skill

Trigger when:

- The user is preparing a plugin or theme for release, especially for wp.org.
- A translator has reported missing or broken strings.
- The user is adding new user-facing strings.
- The user is migrating from one text-domain to another.
- The diff contains: `__(`, `_e(`, `_n(`, `_x(`, `_nx(`, `_ex(`, `esc_html__(`, `esc_html_e(`, `esc_attr__(`, `esc_attr_e(`, `translate(`, `load_plugin_textdomain`, `load_theme_textdomain`, `Text Domain:`.

## How to run the audit

Work through the checks below. For each finding, produce:

1. File and line.
2. The offending code (1–3 lines).
3. The fix.
4. Severity: **HIGH** (translation broken), **MEDIUM** (will break .pot extraction or one locale), **LOW** (best practice).

Do NOT silently rewrite. Produce a report; only edit on user request.

## Determine the canonical text-domain first

Before any other check, find the project's declared text-domain:

1. Open the main plugin file or `style.css` (theme).
2. Read the header — look for `Text Domain: <slug>`.
3. If absent, **derive the expected domain from the plugin folder
   slug** — since WP 4.6 wp.org-distributed plugins auto-load
   translations using the folder slug as the domain, and a missing
   header is not by itself a runtime breakage. Severity for the
   missing header alone:
   - LOW (or no finding) when the strings consistently use the folder
     slug as the domain — runtime works.
   - MEDIUM when wp.org distribution is intended (the header is still
     recommended for clarity and tooling).
   - HIGH only when the header is missing AND the strings use a
     different domain than the folder slug — runtime translation will
     not work.
4. Use the derived/declared slug as the **expected domain** for every subsequent check.

If the codebase contains multiple plugins/themes, repeat per project — do NOT mix domains across folders.

## Audit checks

### 1. Text-domain consistency

Every translation call must pass the expected domain as the last argument.

```php
// WRONG — wrong domain
__( 'Save', 'some-other-plugin' )

// WRONG — missing domain (defaults to WP core, broken extraction)
__( 'Save' )

// RIGHT
__( 'Save', 'myplugin' )
```

Flag any call where the domain literal does not match the project domain. **HIGH** if the wrong domain is used (string lands in another project's `.pot`); **MEDIUM** if missing.

### 2. No variable text-domains

```php
// WRONG — wp i18n tools cannot extract this
$d = 'myplugin';
__( 'Save', $d );
__( 'Save', MY_TEXTDOMAIN );
```

Tools like `wp i18n make-pot` parse statically. The domain MUST be a string literal. Constants and variables are skipped. **HIGH**.

### 3. No variable strings

```php
// WRONG — extractor cannot follow
__( $message, 'myplugin' )
__( "Hello $name", 'myplugin' )       // double quotes with interpolation
__( 'Hello ' . $name, 'myplugin' )    // concatenation
```

The first argument must be a static string literal. For dynamic content use `sprintf` with placeholders (check 5). **HIGH** — string never appears in `.pot`.

### 4. Use the escaped helper at the point of output

If the translated string is echoed into HTML, escape AT translation time using the right helper:

```php
// WRONG
echo __( 'Save', 'myplugin' );

// RIGHT — HTML body
echo esc_html__( 'Save', 'myplugin' );
esc_html_e( 'Save', 'myplugin' );

// RIGHT — HTML attribute
echo '<button title="' . esc_attr__( 'Save', 'myplugin' ) . '">';
```

Map:
- `__()` → builds string only, no escape (use when passing to a function that escapes later, or in non-HTML contexts).
- `_e()` → echoes unescaped (rarely correct in HTML — flag as **MEDIUM** unless context is non-HTML or escape is applied around it).
- `esc_html__` / `esc_html_e` → for HTML body.
- `esc_attr__` / `esc_attr_e` → for HTML attributes.
- For URLs in `href`/`src`, translate with `__()` then wrap with `esc_url()`.

### 5. Placeholders: sprintf, not concatenation

```php
// WRONG — translators cannot reorder, can break grammar
echo __( 'Hello, ', 'myplugin' ) . $name . __( '!', 'myplugin' );

// RIGHT
printf(
    /* translators: %s: user display name */
    esc_html__( 'Hello, %s!', 'myplugin' ),
    esc_html( $name )
);
```

Rules:
- Single placeholder: `%s` (string), `%d` (int), `%1$s` `%2$s` for ordered.
- Multiple placeholders MUST be numbered (`%1$s`, `%2$s`) so translators can reorder.
- Always add a `/* translators: %1$s: ..., %2$s: ... */` comment immediately above the call. wp i18n tools surface this in the `.pot`.
- The placeholder VALUE still needs escaping (`esc_html( $name )`) — the translation helper escapes the template, not the inserted value.

### 6. Plurals: `_n` and `_nx`

```php
// WRONG — English-centric, breaks in languages with multiple plural forms
echo $count . ' items';
printf( __( '%d items', 'myplugin' ), $count );

// RIGHT
printf(
    /* translators: %s: number of items */
    esc_html( _n( '%s item', '%s items', $count, 'myplugin' ) ),
    number_format_i18n( $count )
);
```

Use `_n` for plurals, `_nx` when context is needed. Flag any `if ( $n === 1 ) ... else ...` pattern around translated strings — that's English plural logic, doesn't generalize.

### 7. Context: `_x` and `_ex`

When a single English word has multiple meanings (e.g. "Post" = noun vs. verb), use `_x` to disambiguate for translators:

```php
$label_noun = _x( 'Post', 'noun: a blog post',  'myplugin' );
$label_verb = _x( 'Post', 'verb: to publish',   'myplugin' );
```

Flag short, ambiguous strings (`'Post'`, `'View'`, `'Order'`, `'Address'`, `'Read'`) using `__` without context — **LOW** unless the project clearly mixes meanings, then **MEDIUM**.

### 8. `load_plugin_textdomain` / `load_theme_textdomain`

Plugins distributed via wp.org since WP 4.6 have translations
auto-loaded — an explicit `load_plugin_textdomain()` call is
**recommended for self-hosted .mo files** but not strictly required
for wp.org-distributed plugins. Do not treat its absence as a HIGH
finding without first establishing whether the plugin is shipped via
wp.org.

When you do call it, hook on `init` (recommended) — NOT
`plugins_loaded`. Since WP 6.7, calling `load_plugin_textdomain()`
before `init` triggers a `_doing_it_wrong` notice because
just-in-time translation loading runs at `init`. Themes hook on
`after_setup_theme`.

```php
add_action( 'init', function () {
    load_plugin_textdomain(
        'myplugin',
        false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages/'
    );
} );
```

Check:
- Domain string matches the expected domain (header or folder slug).
  **HIGH** mismatch.
- Hooked on `init` or later — not `plugins_loaded`. **MEDIUM** if too
  early (triggers `_doing_it_wrong` on WP 6.7+).
- Path points to a real directory in the package. **MEDIUM** if
  missing on a self-hosted plugin; **LOW** for wp.org plugins where
  auto-loading covers it.

### 9. Header consistency

Plugin header (main file) or theme header (style.css) must declare `Text Domain` and ideally `Domain Path`:

```
Plugin Name: My Plugin
Text Domain: myplugin
Domain Path: /languages
```

Severity of a missing `Text Domain` header depends on whether
strings still resolve at runtime — see the rule in *Determine the
canonical text-domain first* at the top of this skill. Missing
`Domain Path` is **LOW** if the directory is the default `/languages`.

### 10. JavaScript translations

If the plugin localizes strings to JS via `wp_set_script_translations`:

```php
wp_set_script_translations( 'myplugin-script', 'myplugin', plugin_dir_path( __FILE__ ) . 'languages' );
```

In JS:
```js
import { __, _n, sprintf } from '@wordpress/i18n';
const label = __( 'Save', 'myplugin' );
```

Flag mismatched domain between PHP and JS calls. Out of scope: deep JS audit (different skill).

## Severity guide

- **HIGH:** wrong / missing text-domain, variable text-domain, dynamic first argument to `__()`, missing `Text Domain` header.
- **MEDIUM:** unescaped `_e` in HTML, missing translator comment on `sprintf`, concatenation across translated strings, `load_plugin_textdomain` on the wrong hook, plural handled with English `if`.
- **LOW:** missing `Domain Path` header, ambiguous short strings without `_x` context, missing `load_plugin_textdomain` on a wp.org-distributed plugin.

## Report format

```
# i18n audit: <plugin/theme name>
Declared text-domain: <slug>
Header location: <file>:<line>
Date: <YYYY-MM-DD>

## HIGH
1. <file>:<line> — <issue>
   <code>
   Fix: <code>

## MEDIUM
...

## LOW
...

## Out of scope
- JS string audit (run separately if needed).
- .po / .mo file content (this skill only audits source code).
```

## Cross-references

- **`wp-security-audit`** — covers escape correctness in non-translation contexts; complementary, not redundant.
- For a release-readiness review, run `wp-security-audit`, `wp-security-secrets`, and this skill in sequence before submission.

## What this skill does NOT cover

- Quality of existing `.po` / `.mo` translations (linguistic review).
- Translation memory or glossary consistency.
- JavaScript-side i18n beyond a domain-match spot check.
- RTL / locale-specific CSS or layout.
- Date/number formatting beyond a `number_format_i18n` reminder.

## References

- WP Plugin i18n handbook: https://developer.wordpress.org/plugins/internationalization/
- WP Theme i18n handbook: https://developer.wordpress.org/themes/functionality/internationalization/
- `wp i18n make-pot`: https://developer.wordpress.org/cli/commands/i18n/make-pot/

Related Skills

wp-security-audit

5
from nvdigitalsolutions/mcp-ai-wpoos

Audits WordPress plugin or theme PHP code for the most common security mistakes — missing nonce checks, capability checks, input sanitization, output escaping, unslashing, SQL preparation, AJAX nopriv exposure, file/path traversal, and unsafe redirects. Use when reviewing pull requests, before releasing a plugin, when the user asks "is this secure", or when handling code that touches $_GET / $_POST / $_REQUEST / $_COOKIE / $_FILES / $_SERVER, admin-ajax / admin-post, REST endpoints, options, user meta, custom DB queries, or file uploads.

webapp-testing

5
from nvdigitalsolutions/mcp-ai-wpoos

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

web-artifacts-builder

5
from nvdigitalsolutions/mcp-ai-wpoos

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

valyu

5
from nvdigitalsolutions/mcp-ai-wpoos

Search the live web and 36+ specialised data sources including SEC filings, PubMed, ChEMBL, clinical trials, FRED economic indicators, and patent databases. Use when current, authoritative, or paywalled data is required.

ui-ux-pro-max

5
from nvdigitalsolutions/mcp-ai-wpoos

AI-powered design intelligence with 67 UI styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 15+ tech stacks. Generates complete design systems for any product type with industry-specific reasoning rules.

theme-factory

5
from nvdigitalsolutions/mcp-ai-wpoos

Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.

slack-gif-creator

5
from nvdigitalsolutions/mcp-ai-wpoos

Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."

skill-creator

5
from nvdigitalsolutions/mcp-ai-wpoos

Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.

shannon

5
from nvdigitalsolutions/mcp-ai-wpoos

Autonomous AI security pen testing. Executes real exploits against web applications to find SQL injection, XSS, SSRF, authentication flaws, and IDOR vulnerabilities. Reports only confirmed, reproducible findings — no false positives.

remotion

5
from nvdigitalsolutions/mcp-ai-wpoos

Create programmatic videos using React and Remotion. Translate natural language descriptions into working Remotion components for product demos, release announcements, explainer videos, and animated content.

planetscale

5
from nvdigitalsolutions/mcp-ai-wpoos

Design schemas and write queries for PlanetScale serverless MySQL using branch-based workflows. Ensures index coverage, avoids foreign key anti-patterns, and treats every schema change as a reviewable, reversible deploy request.

mcp-builder

5
from nvdigitalsolutions/mcp-ai-wpoos

Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).