跳到正文
cccskills
免费在 GitHub 公开

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.

中文简介正在准备中。当前显示原文说明。

集成工具WordPress

实测评分 未实测

查看安装方法

包含的文件(1)

  • SKILL.md10.6 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

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.

// 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

// 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

// 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:

// 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

// 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

// 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:

$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.

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:

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

In 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

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.

中文简介正在准备中。当前显示原文说明。

nvdigitalsolutions/mcp-ai-wpoos72026年10月23日 更新

Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.

中文简介正在准备中。当前显示原文说明。

nvdigitalsolutions/mcp-ai-wpoos72026年10月23日 更新

Control a headless browser to navigate URLs, click elements, fill forms, extract content from JavaScript-rendered pages, take screenshots, and automate end-to-end web workflows.

中文简介正在准备中。当前显示原文说明。

nvdigitalsolutions/mcp-ai-wpoos72026年10月23日 更新

Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.

中文简介正在准备中。当前显示原文说明。

nvdigitalsolutions/mcp-ai-wpoos72026年10月23日 更新

Review code for quality, simplicity, and maintainability. Identifies unnecessary abstractions, duplicated logic, oversized functions, missing error handling, and naming issues — then fixes them automatically before presenting the result.

中文简介正在准备中。当前显示原文说明。

nvdigitalsolutions/mcp-ai-wpoos72026年10月23日 更新

WooCommerce store operations through NV oOS Pro — product research, catalog cross-referencing, inventory lookups, and Shopify or Flowhub comparisons where integrations are configured.

中文简介正在准备中。当前显示原文说明。

nvdigitalsolutions/mcp-ai-wpoos72026年10月23日 更新

查看 nvdigitalsolutions 的全部技能

报告此技能的问题