Claude Skill

wpml-string-translation

Register and translate a WordPress plugin's dynamic strings with WPML — option values, admin-entered labels, and other free-form text that is NOT a static gettext string. Register via do_action('wpml_register_single_string', $domain, $name, $value) and read back via apply_filters

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download lonsdale201-wp-agent-skills-wpml_wpml-string-translation-52f6020.zip · 3 KB
Part of lonsdale201/wp-agent-skills — 226 skills

Install

skills CLI npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/wpml/wpml-string-translation
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart
Git git clone https://github.com/Lonsdale201/wp-agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lonsdale201/wp-agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

WPML: string registration & translation

For free-form / dynamic strings your plugin emits that WPML can't reach otherwise — option values entered in your settings, admin-defined labels, messages built at runtime. Static UI text stays on normal WordPress i18n (__(), _e() + .po/.mo); this skill is only for strings that live in the database or are generated dynamically.

Read this first — it needs the String Translation add-on

The base plugin only FIRES the string hooks; the handlers that actually store and translate strings ship with the separate WPML String Translation add-on. Verified: there is no add_action('wpml_register_single_string', …) or add_filter('wpml_translate_single_string', …) anywhere in sitepress-multilingual-cms, and icl_register_string / icl_t / icl_gettext are not defined in the base plugin.

Standard WordPress hook semantics then give you a safe default only for the hook form:

  • apply_filters('wpml_translate_single_string', $value, …) with no handler → returns $value unchanged (original text; safe fallback).
  • do_action('wpml_register_single_string', …) with no handler → silent no-op.
  • icl_register_string() / icl_t() called directly with ST inactive → fatal undefined function unless guarded.

So: prefer the hook form (degrades safely), and if you use the legacy icl_* functions, guard every call with function_exists().

Detecting String Translation

// Preferred:
if ( defined( 'WPML_ST_VERSION' ) ) { /* ST active */ }

// Base ships a helper that does exactly this:
if ( function_exists( 'wpml_is_st_loaded' ) && wpml_is_st_loaded() ) { /* ST active */ }

// Also used internally:
class_exists( 'WPML_String_Translation' );

// Feature-detect the legacy API before calling it:
function_exists( 'icl_register_string' );

wpml_is_st_loaded() returns defined('WPML_ST_VERSION') (inc/functions.php:922-924); class_exists('WPML_String_Translation') is used at inc/translation-management/translation-management.class.php:1742.

Register and translate — the hook form (recommended)

Register the string once (typically when you save the option that holds it), then translate it on output:

$domain = 'my-plugin';               // WPML "context" that groups your strings
$name   = 'welcome_heading';         // stable, unique per string within the domain
$value  = get_option( 'my_plugin_welcome' );

// 1) Register (no-op if ST inactive):
do_action( 'wpml_register_single_string', $domain, $name, $value );

// 2) Translate on output (returns $value unchanged if ST inactive or untranslated):
echo esc_html( apply_filters( 'wpml_translate_single_string', $value, $domain, $name ) );

Verified signatures in the base plugin's own call sites:

(There is also a heavier package form do_action('wpml_register_string', $value, $name, $package, $title, $type) used by page-builder integrations — for ordinary plugin strings use the _single_string form above.)

Legacy icl_* form — guard it

icl_register_string( $context, $name, $value ) and icl_t( $context, $name, $value ) are the older API (provided by ST). They fatal if ST is inactive, so wrap them — this is exactly the pattern WPML ships in its theme-integration example (docs/theme-integration/wpml-integration.php:130-142):

function my_plugin_register_string( $context, $name, $value ) {
    if ( function_exists( 'icl_register_string' ) && trim( $value ) ) {
        icl_register_string( $context, $name, $value );
    }
}

function my_plugin_t( $context, $name, $original_value ) {
    return function_exists( 'icl_t' )
        ? icl_t( $context, $name, $original_value )
        : $original_value;   // graceful fallback
}

The hook form above needs no such guard (the hooks simply don't fire without ST), which is why it's preferred for new code.

When to use <admin-texts> in wpml-config.xml instead

For option values stored in wp_options, the cleaner path is often the declarative <admin-texts> section in wpml-config.xml — WPML String Translation reads it and exposes those options for translation automatically, no do_action('wpml_register_single_string', …) needed:

<admin-texts>
    <key name="my_plugin_options">
        <key name="welcome_heading" />
    </key>
</admin-texts>

Both paths require String Translation. Use <admin-texts> for settings that live in an option; use runtime registration for strings built dynamically (loops, per-item labels) that don't map to a fixed option key. See wpml-config for the <admin-texts> shape.

Critical rules

  • String registration/translation requires WPML String Translation. Detect with defined('WPML_ST_VERSION') and design so your plugin still works (untranslated) without it.
  • Prefer the hook form (wpml_register_single_string / wpml_translate_single_string) — it degrades to a no-op / passthrough with ST absent. Raw icl_register_string / icl_t fatal unguarded.
  • Guard every legacy icl_* call with function_exists().
  • $domain + $name must be stable. Changing them orphans existing translations (WPML keys strings by context + name). Namespace the domain to your plugin.
  • Register before you translate. A string that's never registered has no translation to return — the filter returns the original.
  • Don't register a static gettext string here. Use __()/_e() + a .pot for fixed UI text; reserve this for DB/dynamic values.
  • Escape on output as usual (esc_html, etc.) — the translated string is untrusted output like any other.

Common mistakes

// WRONG — direct legacy call; fatal if String Translation isn't active
echo icl_t( 'my-plugin', 'welcome', $value );

// RIGHT — hook form, safe without ST
do_action( 'wpml_register_single_string', 'my-plugin', 'welcome', $value );
echo esc_html( apply_filters( 'wpml_translate_single_string', $value, 'my-plugin', 'welcome' ) );

// WRONG — assuming translation "works" on base WPML with no ST add-on
// (the filter returns the ORIGINAL string, silently — verify WPML_ST_VERSION if you depend on it)

// WRONG — unstable name breaks saved translations on every save
do_action( 'wpml_register_single_string', 'my-plugin', 'welcome_' . time(), $value );
// RIGHT — stable name
do_action( 'wpml_register_single_string', 'my-plugin', 'welcome_heading', $value );

Cross-references

  • wpml-config — <admin-texts> for translating options declaratively (also needs ST).
  • wpml-overview — the base-vs-add-on split and when strings are even the right tool.
  • wpml-language-api — resolving translated object IDs (a different concern from translating strings).

What this skill does NOT cover

  • The String Translation admin UI / scanning — end-user workflow, not developer API.
  • .po/.mo gettext translation of static strings — that's core WordPress i18n; wpml-config.xml has no <gettext-domains> section (see wpml-config reference).
  • The page-builder package form (wpml_register_string with a $package) beyond a mention — that's for builder integrations.
  • WPML's own internal string storage schema — interact via the hooks.

References

Files (wp-agent-skills)
  • SKILL.md 9.9 KB
    ---
    name: wpml-string-translation
    description: Register and translate a WordPress plugin's dynamic strings with WPML — option values, admin-entered labels, and other free-form text that is NOT a static gettext string. Register via do_action('wpml_register_single_string', $domain, $name, $value) and read back via apply_filters('wpml_translate_single_string', $value, $domain, $name[, $lang]); legacy equivalents are icl_register_string( $context, $name, $value) and icl_t(). CRITICAL — these handlers live in the WPML String Translation add-on, NOT the base plugin — the base only FIRES the hooks, so without ST the translate filter returns the original (safe) and register is a silent no-op, while raw unguarded icl_register_string / icl_t calls FATAL. Covers detecting ST (defined('WPML_ST_VERSION'), class_exists('WPML_String_Translation'), function_exists('icl_register_string')), the function_exists fallback wrapper pattern, and when to use wpml-config admin-texts instead. Use for translatable settings, dynamic labels, or any registered string.
    metadata:
      wp-skills-author: "Soczó Kristóf"
      wp-skills-contact: "mailto:lonsdale201@hotmail.com"
      wp-skills-plugin: "sitepress-multilingual-cms"
      wp-skills-plugin-version-tested: "4.9.5"
      wp-skills-php-min: "7.4"
      wp-skills-last-updated: "2026-07-03"
    ---
    
    # WPML: string registration & translation
    
    For **free-form / dynamic strings** your plugin emits that WPML can't reach otherwise — option values entered in your settings, admin-defined labels, messages built at runtime. Static UI text stays on normal WordPress i18n (`__()`, `_e()` + `.po`/`.mo`); this skill is only for strings that live in the database or are generated dynamically.
    
    ## Read this first — it needs the String Translation add-on
    
    The **base plugin only FIRES** the string hooks; the handlers that actually store and translate strings ship with the separate **WPML String Translation** add-on. Verified: there is no `add_action('wpml_register_single_string', …)` or `add_filter('wpml_translate_single_string', …)` anywhere in `sitepress-multilingual-cms`, and `icl_register_string` / `icl_t` / `icl_gettext` are **not defined** in the base plugin.
    
    Standard WordPress hook semantics then give you a safe default **only for the hook form**:
    
    - `apply_filters('wpml_translate_single_string', $value, …)` with no handler → **returns `$value` unchanged** (original text; safe fallback).
    - `do_action('wpml_register_single_string', …)` with no handler → **silent no-op**.
    - `icl_register_string()` / `icl_t()` called directly with ST inactive → **fatal `undefined function`** unless guarded.
    
    So: prefer the hook form (degrades safely), and if you use the legacy `icl_*` functions, guard every call with `function_exists()`.
    
    ## Detecting String Translation
    
    ```php
    // Preferred:
    if ( defined( 'WPML_ST_VERSION' ) ) { /* ST active */ }
    
    // Base ships a helper that does exactly this:
    if ( function_exists( 'wpml_is_st_loaded' ) && wpml_is_st_loaded() ) { /* ST active */ }
    
    // Also used internally:
    class_exists( 'WPML_String_Translation' );
    
    // Feature-detect the legacy API before calling it:
    function_exists( 'icl_register_string' );
    ```
    
    `wpml_is_st_loaded()` returns `defined('WPML_ST_VERSION')` ([inc/functions.php:922-924](functions.php)); `class_exists('WPML_String_Translation')` is used at [inc/translation-management/translation-management.class.php:1742](translation-management.class.php).
    
    ## Register and translate — the hook form (recommended)
    
    Register the string once (typically when you save the option that holds it), then translate it on output:
    
    ```php
    $domain = 'my-plugin';               // WPML "context" that groups your strings
    $name   = 'welcome_heading';         // stable, unique per string within the domain
    $value  = get_option( 'my_plugin_welcome' );
    
    // 1) Register (no-op if ST inactive):
    do_action( 'wpml_register_single_string', $domain, $name, $value );
    
    // 2) Translate on output (returns $value unchanged if ST inactive or untranslated):
    echo esc_html( apply_filters( 'wpml_translate_single_string', $value, $domain, $name ) );
    ```
    
    Verified signatures in the base plugin's own call sites:
    - Register (simple form) `do_action('wpml_register_single_string', $domain, $name, $value)` — [class-wpml-ls-settings-strings.php:81-86](class-wpml-ls-settings-strings.php).
    - Translate `apply_filters('wpml_translate_single_string', $value, $domain, $name)` (3-arg) — [class-wpml-ls-settings-strings.php:108](class-wpml-ls-settings-strings.php); 4-arg form with an explicit target language `apply_filters('wpml_translate_single_string', $value, $domain, $name, $language_code)` — [class-wpml-endpoints-support.php:72](class-wpml-endpoints-support.php). Omit the 4th arg to let ST use the current language.
    
    (There is also a heavier package form `do_action('wpml_register_string', $value, $name, $package, $title, $type)` used by page-builder integrations — for ordinary plugin strings use the `_single_string` form above.)
    
    ## Legacy `icl_*` form — guard it
    
    `icl_register_string( $context, $name, $value )` and `icl_t( $context, $name, $value )` are the older API (provided by ST). They **fatal if ST is inactive**, so wrap them — this is exactly the pattern WPML ships in its theme-integration example ([docs/theme-integration/wpml-integration.php:130-142](wpml-integration.php)):
    
    ```php
    function my_plugin_register_string( $context, $name, $value ) {
        if ( function_exists( 'icl_register_string' ) && trim( $value ) ) {
            icl_register_string( $context, $name, $value );
        }
    }
    
    function my_plugin_t( $context, $name, $original_value ) {
        return function_exists( 'icl_t' )
            ? icl_t( $context, $name, $original_value )
            : $original_value;   // graceful fallback
    }
    ```
    
    The hook form above needs no such guard (the hooks simply don't fire without ST), which is why it's preferred for new code.
    
    ## When to use `<admin-texts>` in `wpml-config.xml` instead
    
    For **option values stored in `wp_options`**, the cleaner path is often the declarative `<admin-texts>` section in `wpml-config.xml` — WPML String Translation reads it and exposes those options for translation automatically, no `do_action('wpml_register_single_string', …)` needed:
    
    ```xml
    <admin-texts>
        <key name="my_plugin_options">
            <key name="welcome_heading" />
        </key>
    </admin-texts>
    ```
    
    Both paths require String Translation. Use `<admin-texts>` for settings that live in an option; use runtime registration for strings built dynamically (loops, per-item labels) that don't map to a fixed option key. See `wpml-config` for the `<admin-texts>` shape.
    
    ## Critical rules
    
    - **String registration/translation requires WPML String Translation.** Detect with `defined('WPML_ST_VERSION')` and design so your plugin still works (untranslated) without it.
    - **Prefer the hook form** (`wpml_register_single_string` / `wpml_translate_single_string`) — it degrades to a no-op / passthrough with ST absent. Raw `icl_register_string` / `icl_t` fatal unguarded.
    - **Guard every legacy `icl_*` call** with `function_exists()`.
    - **`$domain` + `$name` must be stable.** Changing them orphans existing translations (WPML keys strings by context + name). Namespace the domain to your plugin.
    - **Register before you translate.** A string that's never registered has no translation to return — the filter returns the original.
    - **Don't register a static gettext string here.** Use `__()/_e()` + a `.pot` for fixed UI text; reserve this for DB/dynamic values.
    - **Escape on output** as usual (`esc_html`, etc.) — the translated string is untrusted output like any other.
    
    ## Common mistakes
    
    ```php
    // WRONG — direct legacy call; fatal if String Translation isn't active
    echo icl_t( 'my-plugin', 'welcome', $value );
    
    // RIGHT — hook form, safe without ST
    do_action( 'wpml_register_single_string', 'my-plugin', 'welcome', $value );
    echo esc_html( apply_filters( 'wpml_translate_single_string', $value, 'my-plugin', 'welcome' ) );
    
    // WRONG — assuming translation "works" on base WPML with no ST add-on
    // (the filter returns the ORIGINAL string, silently — verify WPML_ST_VERSION if you depend on it)
    
    // WRONG — unstable name breaks saved translations on every save
    do_action( 'wpml_register_single_string', 'my-plugin', 'welcome_' . time(), $value );
    // RIGHT — stable name
    do_action( 'wpml_register_single_string', 'my-plugin', 'welcome_heading', $value );
    ```
    
    ## Cross-references
    
    - **`wpml-config`** — `<admin-texts>` for translating options declaratively (also needs ST).
    - **`wpml-overview`** — the base-vs-add-on split and when strings are even the right tool.
    - **`wpml-language-api`** — resolving translated object IDs (a different concern from translating strings).
    
    ## What this skill does NOT cover
    
    - **The String Translation admin UI / scanning** — end-user workflow, not developer API.
    - **`.po`/`.mo` gettext translation** of static strings — that's core WordPress i18n; `wpml-config.xml` has no `<gettext-domains>` section (see `wpml-config` reference).
    - **The page-builder package form** (`wpml_register_string` with a `$package`) beyond a mention — that's for builder integrations.
    - **WPML's own internal string storage schema** — interact via the hooks.
    
    ## References
    
    - Base FIRES the hooks (handlers are in ST): register [class-wpml-ls-settings-strings.php:81-86](class-wpml-ls-settings-strings.php), translate [:108](class-wpml-ls-settings-strings.php); 4-arg translate with language [class-wpml-endpoints-support.php:72](class-wpml-endpoints-support.php).
    - Graceful `function_exists` wrapper pattern: [docs/theme-integration/wpml-integration.php:130-142](wpml-integration.php).
    - ST detection helper `wpml_is_st_loaded()`: [inc/functions.php:922-924](functions.php); `class_exists('WPML_String_Translation')`: [inc/translation-management/translation-management.class.php:1742](translation-management.class.php).
    - WPML coding API docs: <https://wpml.org/documentation/support/wpml-coding-api/>.
    - Official documentation: <https://wpml.org/documentation/support/translate-texts-in-admin-screens-with-wpml-string-translation/>
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related