Claude Skill

wpml-language-api

Use WPML's runtime language hook API from plugin/theme code

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-language-api-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-language-api
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: the runtime language API

For plugin/theme code that must behave per language — know the current language, show the correct translation of a post/term, query in a specific language, or build language-aware URLs. WPML exposes this as a set of wpml_* hooks (not global functions), all registered in SitePress::api_hooks() (sitepress.class.php:316-407). The legacy icl_* global functions still exist but are deprecated since 3.2 in favour of these hooks.

Why hooks, not function calls

WPML deliberately exposes its API as filters/actions so your code works whether or not WPML is active: apply_filters('wpml_current_language','') returns your passed default ('') when WPML is absent, instead of a fatal undefined function. Always call through the hook, and treat the WPML-absent return as the graceful fallback.

Current / active / default language

// Current language code (2-letter). Pass '' — it's the ignored default.
$current = apply_filters( 'wpml_current_language', '' );          // e.g. 'hu'

// Or the constant (defined once per request at sitepress.class.php:485-487):
$current = defined( 'ICL_LANGUAGE_CODE' ) ? ICL_LANGUAGE_CODE : '';

// Default (site) language:
$default = apply_filters( 'wpml_default_language', null );        // e.g. 'en'

// All active languages, keyed by code:
$langs = apply_filters( 'wpml_active_languages', null, [ 'skip_missing' => 0, 'orderby' => 'code' ] );
// $langs['hu'] => [ 'code','native_name','translated_name','url','flag','active', ... ]

Verified registrations: wpml_current_language sitepress.class.php:341, wpml_default_language :380, wpml_active_languages :374 (callback wpml_get_active_languages_filter, inc/template-functions.php:124).

wpml_object_id — resolve the translation of a post/term

The workhorse. Given an ID in one language, return its counterpart in another (default: current) language. Use it whenever you have a hardcoded/stored ID and must show the right translation.

$translated_id = apply_filters(
    'wpml_object_id',
    $element_id,                 // post_id or term_id
    'post',                      // 'post' | 'page' | CPT slug | 'category' | 'post_tag' | taxonomy | 'nav_menu' | 'any'
    true,                        // $return_original_if_missing: true → original id when no translation; false → null
    null                         // target language (null = current)
);

Verified at sitepress.class.php:378 (callback wpml_object_id_filter, inc/template-functions.php:381; underlying SitePress::get_object_id() sitepress.class.php:4644). Legacy icl_object_id() (template-functions.php:353) is deprecated 3.2 and just wraps this filter.

Rule: pass $return_original_if_missing = true for display (so you never render a blank when a translation is missing), false when you specifically need "is there a translation?".

wpml_switch_language — run code in a specific language

To query or render as another language, switch, do the work, then restore by passing null:

do_action( 'wpml_switch_language', 'de' );   // switch to German

$posts = get_posts( [ 'post_type' => 'product', 'numberposts' => 5 ] ); // now returns German posts

do_action( 'wpml_switch_language', null );   // ALWAYS restore

Verified at sitepress.class.php:406 → wpml_switch_language_action (template-functions.php:1380) → SitePress::switch_lang() (sitepress.class.php:1203); null restores the cached original_language (:1214-1219). Passing 'all' widens queries to every active language. WPML fires do_action('wpml_language_has_switched', $code, $cookie_lang, $original) after each switch (:1234) if you need to react.

Rule: every switch must be paired with a restore (null), even on early return / exception — otherwise the rest of the request runs in the wrong language.

Language-aware URLs

// Home URL in the current language:
$home = apply_filters( 'wpml_home_url', '' );

// Convert any URL to a target language (null = current):
$url = apply_filters( 'wpml_permalink', $url, $lang_code );
// 3rd arg $absolute_url = true resolves the object behind the URL and finds the
// matching translation's URL — heavier; use only when you don't already know the language.

// Ready-made <a> link to the translation of an element:
$link = apply_filters( 'wpml_element_link', $element_id, 'post' );

wpml_home_url sitepress.class.php:373; wpml_permalink is registered in a class action (classes/API/Hooks/class-wpml-api-hook-permalink.php:17,29) and runs the result through esc_url_raw(); wpml_element_link sitepress.class.php:377. WPML emits hreflang, canonical, and the switcher itself — you don't build those.

Language details of a post / element

// Post: array with language_code, locale, text_direction, display_name, native_name, different_language (or WP_Error)
$info = apply_filters( 'wpml_post_language_details', null, $post_id );

// Any element (post or term): object with trid, language_code, source_language_code
$details = apply_filters( 'wpml_element_language_details', null, [
    'element_id'   => $term_taxonomy_id,   // post_id for posts, term_taxonomy_id for terms
    'element_type' => 'post_product',      // WPML element type — see wpml_element_type
] );

// Translate a WP type to a WPML element type ('post_{cpt}' / 'tax_{taxonomy}'):
$wpml_type = apply_filters( 'wpml_element_type', 'product' );  // 'post_product'

wpml_post_language_details sitepress.class.php:381 (callback returns keys at template-functions.php:817-824); wpml_element_language_details :401; wpml_element_type :391.

Critical rules

  • Call through the hook, not the icl_* function. wpml_current_language / wpml_object_id / etc. are the supported API; icl_get_current_language / icl_object_id are @deprecated 3.2. The hooks also no-op safely when WPML is absent.
  • Never store or cache a cross-language ID / URL. They are per-language; resolve with wpml_object_id / wpml_permalink at the point of use.
  • Always restore after wpml_switch_language by calling it with null — wrap in try/finally if the work can throw.
  • wpml_object_id for display uses $return_original_if_missing = true so a missing translation falls back to the original instead of vanishing.
  • wpml_active_languages returns an array keyed by language code, not a list — iterate keys or array_keys().
  • Don't read ICL_LANGUAGE_CODE before WPML initialises it (sitepress.class.php:485, set during WPML init). In early hooks prefer apply_filters('wpml_current_language','').

Common mistakes

// WRONG — hardcoded ID renders the wrong language's object
echo get_the_title( 123 );

// RIGHT — resolve to the current language first
$id = apply_filters( 'wpml_object_id', 123, 'post', true );
echo get_the_title( $id );

// WRONG — switch without restore leaves the request in 'de'
do_action( 'wpml_switch_language', 'de' );
return $this->build_feed(); // rest of request now German

// RIGHT — restore, even on the early-return path
do_action( 'wpml_switch_language', 'de' );
try { return $this->build_feed(); }
finally { do_action( 'wpml_switch_language', null ); }

// WRONG — calling the deprecated function directly (fatals if WPML inactive)
$lang = icl_get_current_language();
// RIGHT
$lang = apply_filters( 'wpml_current_language', '' );

Cross-references

  • wpml-overview — the "WPML translates copies" model that makes wpml_object_id necessary.
  • wpml-config — declare which content types are translatable in the first place.
  • wpml-string-translation — translate dynamic strings (a different concern from resolving object IDs).

What this skill does NOT cover

  • String / option translation — wpml-string-translation.
  • Declaring translatable content — wpml-config.
  • Building language switchers / hreflang / sitemaps — WPML generates these; the SEO/URL internals are out of scope here.
  • The icl_translations table schema — interact via the hooks, not the tables.

References

Files (wp-agent-skills)
  • SKILL.md 10.6 KB
    ---
    name: wpml-language-api
    description: Use WPML's runtime language hook API from plugin/theme code
      — read the current/active/default language, resolve the translated
      ID of a post/term, switch language around a query, and build
      language-aware URLs. Covers apply_filters('wpml_current_language','')
      and the ICL_LANGUAGE_CODE constant, apply_filters('wpml_active_languages',
      null, $args), apply_filters('wpml_default_language', null),
      apply_filters('wpml_object_id', $id, $type, $return_original_if_missing,
      $lang), do_action('wpml_switch_language', $lang) with restore via null,
      apply_filters('wpml_permalink', $url, $lang), apply_filters('wpml_home_url',''),
      apply_filters('wpml_post_language_details', null, $post_id), and
      apply_filters('wpml_element_language_details', null, $args). All are
      registered in SitePress::api_hooks(); the legacy icl_* functions
      (icl_object_id, icl_get_current_language) are deprecated since 3.2.
      Use when code must behave per-language, show the right translation,
      run a query in a specific language, or link across translations.
    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: the runtime language API
    
    For plugin/theme code that must behave **per language** — know the current language, show the correct translation of a post/term, query in a specific language, or build language-aware URLs. WPML exposes this as a set of `wpml_*` **hooks** (not global functions), all registered in `SitePress::api_hooks()` ([sitepress.class.php:316-407](sitepress.class.php)). The legacy `icl_*` global functions still exist but are deprecated since 3.2 in favour of these hooks.
    
    ## Why hooks, not function calls
    
    WPML deliberately exposes its API as filters/actions so your code works whether or not WPML is active: `apply_filters('wpml_current_language','')` returns your passed default (`''`) when WPML is absent, instead of a fatal `undefined function`. Always call through the hook, and treat the WPML-absent return as the graceful fallback.
    
    ## Current / active / default language
    
    ```php
    // Current language code (2-letter). Pass '' — it's the ignored default.
    $current = apply_filters( 'wpml_current_language', '' );          // e.g. 'hu'
    
    // Or the constant (defined once per request at sitepress.class.php:485-487):
    $current = defined( 'ICL_LANGUAGE_CODE' ) ? ICL_LANGUAGE_CODE : '';
    
    // Default (site) language:
    $default = apply_filters( 'wpml_default_language', null );        // e.g. 'en'
    
    // All active languages, keyed by code:
    $langs = apply_filters( 'wpml_active_languages', null, [ 'skip_missing' => 0, 'orderby' => 'code' ] );
    // $langs['hu'] => [ 'code','native_name','translated_name','url','flag','active', ... ]
    ```
    
    Verified registrations: `wpml_current_language` [sitepress.class.php:341](sitepress.class.php), `wpml_default_language` [:380](sitepress.class.php), `wpml_active_languages` [:374](sitepress.class.php) (callback `wpml_get_active_languages_filter`, [inc/template-functions.php:124](template-functions.php)).
    
    ## `wpml_object_id` — resolve the translation of a post/term
    
    The workhorse. Given an ID in one language, return its counterpart in another (default: current) language. Use it whenever you have a hardcoded/stored ID and must show the right translation.
    
    ```php
    $translated_id = apply_filters(
        'wpml_object_id',
        $element_id,                 // post_id or term_id
        'post',                      // 'post' | 'page' | CPT slug | 'category' | 'post_tag' | taxonomy | 'nav_menu' | 'any'
        true,                        // $return_original_if_missing: true → original id when no translation; false → null
        null                         // target language (null = current)
    );
    ```
    
    Verified at [sitepress.class.php:378](sitepress.class.php) (callback `wpml_object_id_filter`, [inc/template-functions.php:381](template-functions.php); underlying `SitePress::get_object_id()` [sitepress.class.php:4644](sitepress.class.php)). Legacy `icl_object_id()` ([template-functions.php:353](template-functions.php)) is deprecated 3.2 and just wraps this filter.
    
    **Rule:** pass `$return_original_if_missing = true` for display (so you never render a blank when a translation is missing), `false` when you specifically need "is there a translation?".
    
    ## `wpml_switch_language` — run code in a specific language
    
    To query or render as another language, switch, do the work, then **restore by passing `null`**:
    
    ```php
    do_action( 'wpml_switch_language', 'de' );   // switch to German
    
    $posts = get_posts( [ 'post_type' => 'product', 'numberposts' => 5 ] ); // now returns German posts
    
    do_action( 'wpml_switch_language', null );   // ALWAYS restore
    ```
    
    Verified at [sitepress.class.php:406](sitepress.class.php) → `wpml_switch_language_action` ([template-functions.php:1380](template-functions.php)) → `SitePress::switch_lang()` ([sitepress.class.php:1203](sitepress.class.php)); `null` restores the cached `original_language` ([:1214-1219](sitepress.class.php)). Passing `'all'` widens queries to every active language. WPML fires `do_action('wpml_language_has_switched', $code, $cookie_lang, $original)` after each switch ([:1234](sitepress.class.php)) if you need to react.
    
    **Rule:** every switch must be paired with a restore (`null`), even on early return / exception — otherwise the rest of the request runs in the wrong language.
    
    ## Language-aware URLs
    
    ```php
    // Home URL in the current language:
    $home = apply_filters( 'wpml_home_url', '' );
    
    // Convert any URL to a target language (null = current):
    $url = apply_filters( 'wpml_permalink', $url, $lang_code );
    // 3rd arg $absolute_url = true resolves the object behind the URL and finds the
    // matching translation's URL — heavier; use only when you don't already know the language.
    
    // Ready-made <a> link to the translation of an element:
    $link = apply_filters( 'wpml_element_link', $element_id, 'post' );
    ```
    
    `wpml_home_url` [sitepress.class.php:373](sitepress.class.php); `wpml_permalink` is registered in a class action ([classes/API/Hooks/class-wpml-api-hook-permalink.php:17,29](class-wpml-api-hook-permalink.php)) and runs the result through `esc_url_raw()`; `wpml_element_link` [sitepress.class.php:377](sitepress.class.php). WPML emits `hreflang`, canonical, and the switcher itself — you don't build those.
    
    ## Language details of a post / element
    
    ```php
    // Post: array with language_code, locale, text_direction, display_name, native_name, different_language (or WP_Error)
    $info = apply_filters( 'wpml_post_language_details', null, $post_id );
    
    // Any element (post or term): object with trid, language_code, source_language_code
    $details = apply_filters( 'wpml_element_language_details', null, [
        'element_id'   => $term_taxonomy_id,   // post_id for posts, term_taxonomy_id for terms
        'element_type' => 'post_product',      // WPML element type — see wpml_element_type
    ] );
    
    // Translate a WP type to a WPML element type ('post_{cpt}' / 'tax_{taxonomy}'):
    $wpml_type = apply_filters( 'wpml_element_type', 'product' );  // 'post_product'
    ```
    
    `wpml_post_language_details` [sitepress.class.php:381](sitepress.class.php) (callback returns keys at [template-functions.php:817-824](template-functions.php)); `wpml_element_language_details` [:401](sitepress.class.php); `wpml_element_type` [:391](sitepress.class.php).
    
    ## Critical rules
    
    - **Call through the hook, not the `icl_*` function.** `wpml_current_language` / `wpml_object_id` / etc. are the supported API; `icl_get_current_language` / `icl_object_id` are `@deprecated 3.2`. The hooks also no-op safely when WPML is absent.
    - **Never store or cache a cross-language ID / URL.** They are per-language; resolve with `wpml_object_id` / `wpml_permalink` at the point of use.
    - **Always restore after `wpml_switch_language`** by calling it with `null` — wrap in try/finally if the work can throw.
    - **`wpml_object_id` for display uses `$return_original_if_missing = true`** so a missing translation falls back to the original instead of vanishing.
    - **`wpml_active_languages` returns an array keyed by language code**, not a list — iterate keys or `array_keys()`.
    - **Don't read `ICL_LANGUAGE_CODE` before WPML initialises it** ([sitepress.class.php:485](sitepress.class.php), set during WPML init). In early hooks prefer `apply_filters('wpml_current_language','')`.
    
    ## Common mistakes
    
    ```php
    // WRONG — hardcoded ID renders the wrong language's object
    echo get_the_title( 123 );
    
    // RIGHT — resolve to the current language first
    $id = apply_filters( 'wpml_object_id', 123, 'post', true );
    echo get_the_title( $id );
    
    // WRONG — switch without restore leaves the request in 'de'
    do_action( 'wpml_switch_language', 'de' );
    return $this->build_feed(); // rest of request now German
    
    // RIGHT — restore, even on the early-return path
    do_action( 'wpml_switch_language', 'de' );
    try { return $this->build_feed(); }
    finally { do_action( 'wpml_switch_language', null ); }
    
    // WRONG — calling the deprecated function directly (fatals if WPML inactive)
    $lang = icl_get_current_language();
    // RIGHT
    $lang = apply_filters( 'wpml_current_language', '' );
    ```
    
    ## Cross-references
    
    - **`wpml-overview`** — the "WPML translates copies" model that makes `wpml_object_id` necessary.
    - **`wpml-config`** — declare which content types are translatable in the first place.
    - **`wpml-string-translation`** — translate dynamic strings (a different concern from resolving object IDs).
    
    ## What this skill does NOT cover
    
    - **String / option translation** — `wpml-string-translation`.
    - **Declaring translatable content** — `wpml-config`.
    - **Building language switchers / hreflang / sitemaps** — WPML generates these; the SEO/URL internals are out of scope here.
    - **The `icl_translations` table schema** — interact via the hooks, not the tables.
    
    ## References
    
    - Hook registration hub: [sitepress.class.php:316-407](sitepress.class.php) — current(341), active(374), default(380), object_id(378), home_url(373), element_link(377), post_language_details(381), element_language_details(401), element_type(391), switch_language(406).
    - `switch_lang()` + restore + `wpml_language_has_switched`: [sitepress.class.php:1203-1234](sitepress.class.php).
    - `ICL_LANGUAGE_CODE` define: [sitepress.class.php:485-487](sitepress.class.php).
    - Callbacks + deprecated `icl_*` wrappers: [inc/template-functions.php](template-functions.php).
    - `wpml_permalink` class action: [classes/API/Hooks/class-wpml-api-hook-permalink.php:17-40](class-wpml-api-hook-permalink.php).
    - Official documentation: <https://wpml.org/documentation/support/wpml-coding-api/>
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related