Claude Skill

polylang-language-api

Use Polylang 3.8.5 safely from WordPress plugins or classic themes. Covers guards, current/default language lookup, language fields and objects, language lists, localized home URLs, language switchers, translated post type/taxonomy registration, and common mistakes such as readin

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

Polylang Language API

Use this skill when plugin or classic theme code needs to detect, list, switch, or route by Polylang languages.

Polylang's public API lives in wp-content/plugins/polylang/src/api.php. Prefer those functions over PLL()->... internals. The PLL() accessor exists, but the source itself says API functions are preferred because internals may change.

Load and guard

Polylang may be inactive, may not have languages yet, or may be running in admin/REST/CLI without a current language. Always guard public API use:

if ( ! function_exists( 'pll_current_language' ) ) {
    return;
}

$lang = pll_current_language();
if ( ! $lang ) {
    $lang = pll_default_language();
}

if ( ! $lang ) {
    return;
}

Do not read $_GET['lang'] directly as your language model. Let Polylang define the current language, then read it through pll_current_language().

Current and default language

pll_current_language( $field = 'slug' ) returns the current language on frontend, the admin language filter in admin, and false if no current language exists. Useful fields include:

$slug   = pll_current_language();          // e.g. "en"
$locale = pll_current_language( 'locale' ); // e.g. "en_US"
$name   = pll_current_language( 'name' );
$lang   = pll_current_language( \OBJECT ); // PLL_Language object.

pll_default_language( $field = 'slug' ) has the same field behavior and returns false if no default language exists yet.

Since Polylang 3.4, composite language term properties are accepted:

$term_taxonomy_id = pll_current_language( 'language:term_taxonomy_id' );
$term_language_tt = pll_default_language( 'term_language:term_taxonomy_id' );

Use composite fields only when you need SQL joins or taxonomy term IDs. For most code, use slugs.

List languages

Use pll_languages_list() for language lists:

$slugs = pll_languages_list();

$locales = pll_languages_list( array(
    'fields' => 'locale',
) );

$active_slugs = pll_languages_list( array(
    'fields'     => 'slug',
    'hide_empty' => true,
) );

hide_empty removes languages without posts. hide_default is also accepted by the implementation even though it is not documented in the short API block.

Do not derive languages from installed .mo files or locales. Polylang languages are stored as language terms and carry URL, flag, ordering, active/default, and term property data.

URLs and switchers

Use pll_home_url( $lang ) for localized home URLs:

$url = pll_home_url( 'fr' );

If no language or links model is available, the function falls back to home_url( '/' ). Do not build URLs by concatenating /$lang/; Polylang supports query-arg, directory, subdomain, and domain modes.

For a rendered switcher:

if ( function_exists( 'pll_the_languages' ) ) {
    pll_the_languages( array(
        'show_flags' => 1,
        'show_names' => 1,
    ) );
}

For custom markup, ask for raw data and escape output yourself:

$items = pll_the_languages( array(
    'raw'  => 1,
    'echo' => 0,
) );

foreach ( $items as $item ) {
    printf(
        '<a href="%s" lang="%s"%s>%s</a>',
        esc_url( $item['url'] ),
        esc_attr( $item['locale'] ),
        ! empty( $item['current_lang'] ) ? ' aria-current="true"' : '',
        esc_html( $item['name'] )
    );
}

pll_the_languages() returns an empty string or empty array outside the frontend if the links model is not available.

Translated post types and taxonomies

Check whether Polylang manages a type before adding language-dependent logic:

if ( function_exists( 'pll_is_translated_post_type' ) && pll_is_translated_post_type( 'book' ) ) {
    // Language-aware code for the book CPT.
}

if ( function_exists( 'pll_is_translated_taxonomy' ) && pll_is_translated_taxonomy( 'genre' ) ) {
    // Language-aware taxonomy code.
}

To opt in programmatically, hook early:

add_filter( 'pll_get_post_types', static function ( array $types, bool $is_settings ): array {
    $types['book'] = 'book';
    return $types;
}, 10, 2 );

add_filter( 'pll_get_taxonomies', static function ( array $taxonomies, bool $is_settings ): array {
    $taxonomies['genre'] = 'genre';
    return $taxonomies;
}, 10, 2 );

The source comments explicitly say these filters must be added early: in plugins_loaded for plugins or directly in functions.php for themes. Polylang caches the translated type lists after after_setup_theme, so late filters can appear to work in settings but fail at runtime.

Use $is_settings if you want a type forced on but hidden from the Polylang settings UI:

add_filter( 'pll_get_post_types', static function ( array $types, bool $is_settings ): array {
    if ( ! $is_settings ) {
        $types['internal_doc'] = 'internal_doc';
    }
    return $types;
}, 10, 2 );

Admin, REST, and CLI caveats

  • In admin, pll_current_language() can be the admin language filter or false when "all languages" is selected.
  • In REST, Polylang sets the current language from the lang request parameter if present.
  • In cron/CLI/background jobs, there may be no current language. Pass an explicit language slug to downstream APIs instead of relying on current language.
  • Invalid or missing language setup returns false or safe fallbacks. Treat that as a real state, not an exceptional edge case.

Common mistakes

  • Do not call PLL()->curlang->slug without checking that PLL()->curlang is a language object.
  • Do not concatenate language slugs into URLs.
  • Do not use flag codes as locales or locale strings as language slugs.
  • Do not translate all public CPTs/taxonomies blindly. Some post types are operational data, not content.
  • Do not assume pll_current_language() returns a string. It can return false, int, array values, or a PLL_Language object depending on $field.
  • In namespaced PHP, pass \OBJECT when requesting a PLL_Language object.

Cross-references

  • Use polylang-object-translations to resolve translated post/term IDs or save translation groups.
  • Use polylang-rest-headless for REST lang parameters and Pro REST fields.
  • Use polylang-wc-compatibility for products, variations, orders, SKU uniqueness, and Woo REST.

Verification

Local source checked against:

  • Polylang public API: wp-content/plugins/polylang/src/api.php
  • Type opt-in filters: wp-content/plugins/polylang/src/translated-post.php and translated-term.php
  • Switcher filters and raw output: wp-content/plugins/polylang/src/switcher.php
  • REST language detection: wp-content/plugins/polylang/src/rest-request.php

References

  • Official documentation: https://polylang.pro/doc/function-reference/
  • Official documentation: https://polylang.pro/doc/developpers-how-to/
  • Verified source paths:
    • wp-content/plugins/polylang/polylang.php
    • wp-content/plugins/polylang/src/api.php
    • wp-content/plugins/polylang/src/switcher.php
    • wp-content/plugins/polylang/src/translated-post.php
    • wp-content/plugins/polylang/src/translated-term.php
    • wp-content/plugins/polylang/src/filter-rest-routes.php
    • wp-content/plugins/polylang/src/frontend/choose-lang.php
Files (wp-agent-skills)
  • agents
    • openai.yaml 263 B
      interface:
        display_name: "Polylang Language API"
        short_description: "Polylang language guards and switchers"
        default_prompt: "Use $polylang-language-api to make this WordPress plugin or theme code safe for Polylang language detection, URLs, and switchers."
      
  • SKILL.md 8 KB
    ---
    name: polylang-language-api
    description: "Use Polylang 3.8.5 safely from WordPress plugins or classic themes. Covers guards, current/default language lookup, language fields and objects, language lists, localized home URLs, language switchers, translated post type/taxonomy registration, and common mistakes such as reading $_GET['lang'], assuming a current language exists in admin/REST/CLI, or hardcoding language URL prefixes. Use when code calls pll_current_language, pll_default_language, pll_languages_list, pll_the_languages, pll_home_url, pll_is_translated_post_type, pll_is_translated_taxonomy, pll_get_post_types, or pll_get_taxonomies."
    metadata:
      wp-skills-author: "Soczo Kristof"
      wp-skills-contact: "mailto:lonsdale201@hotmail.com"
      wp-skills-plugin: "polylang"
      wp-skills-plugin-version-tested: "Polylang 3.8.5"
      wp-skills-wp-version-tested: "7.0"
      wp-skills-php-min: "7.4"
      wp-skills-last-updated: "2026-07-01"
    ---
    
    # Polylang Language API
    
    Use this skill when plugin or classic theme code needs to detect, list, switch, or route by Polylang languages.
    
    Polylang's public API lives in `wp-content/plugins/polylang/src/api.php`. Prefer those functions over `PLL()->...` internals. The `PLL()` accessor exists, but the source itself says API functions are preferred because internals may change.
    
    ## Load and guard
    
    Polylang may be inactive, may not have languages yet, or may be running in admin/REST/CLI without a current language. Always guard public API use:
    
    ```php
    if ( ! function_exists( 'pll_current_language' ) ) {
        return;
    }
    
    $lang = pll_current_language();
    if ( ! $lang ) {
        $lang = pll_default_language();
    }
    
    if ( ! $lang ) {
        return;
    }
    ```
    
    Do not read `$_GET['lang']` directly as your language model. Let Polylang define the current language, then read it through `pll_current_language()`.
    
    ## Current and default language
    
    `pll_current_language( $field = 'slug' )` returns the current language on frontend, the admin language filter in admin, and `false` if no current language exists. Useful fields include:
    
    ```php
    $slug   = pll_current_language();          // e.g. "en"
    $locale = pll_current_language( 'locale' ); // e.g. "en_US"
    $name   = pll_current_language( 'name' );
    $lang   = pll_current_language( \OBJECT ); // PLL_Language object.
    ```
    
    `pll_default_language( $field = 'slug' )` has the same field behavior and returns `false` if no default language exists yet.
    
    Since Polylang 3.4, composite language term properties are accepted:
    
    ```php
    $term_taxonomy_id = pll_current_language( 'language:term_taxonomy_id' );
    $term_language_tt = pll_default_language( 'term_language:term_taxonomy_id' );
    ```
    
    Use composite fields only when you need SQL joins or taxonomy term IDs. For most code, use slugs.
    
    ## List languages
    
    Use `pll_languages_list()` for language lists:
    
    ```php
    $slugs = pll_languages_list();
    
    $locales = pll_languages_list( array(
        'fields' => 'locale',
    ) );
    
    $active_slugs = pll_languages_list( array(
        'fields'     => 'slug',
        'hide_empty' => true,
    ) );
    ```
    
    `hide_empty` removes languages without posts. `hide_default` is also accepted by the implementation even though it is not documented in the short API block.
    
    Do not derive languages from installed `.mo` files or locales. Polylang languages are stored as language terms and carry URL, flag, ordering, active/default, and term property data.
    
    ## URLs and switchers
    
    Use `pll_home_url( $lang )` for localized home URLs:
    
    ```php
    $url = pll_home_url( 'fr' );
    ```
    
    If no language or links model is available, the function falls back to `home_url( '/' )`. Do not build URLs by concatenating `/$lang/`; Polylang supports query-arg, directory, subdomain, and domain modes.
    
    For a rendered switcher:
    
    ```php
    if ( function_exists( 'pll_the_languages' ) ) {
        pll_the_languages( array(
            'show_flags' => 1,
            'show_names' => 1,
        ) );
    }
    ```
    
    For custom markup, ask for raw data and escape output yourself:
    
    ```php
    $items = pll_the_languages( array(
        'raw'  => 1,
        'echo' => 0,
    ) );
    
    foreach ( $items as $item ) {
        printf(
            '<a href="%s" lang="%s"%s>%s</a>',
            esc_url( $item['url'] ),
            esc_attr( $item['locale'] ),
            ! empty( $item['current_lang'] ) ? ' aria-current="true"' : '',
            esc_html( $item['name'] )
        );
    }
    ```
    
    `pll_the_languages()` returns an empty string or empty array outside the frontend if the links model is not available.
    
    ## Translated post types and taxonomies
    
    Check whether Polylang manages a type before adding language-dependent logic:
    
    ```php
    if ( function_exists( 'pll_is_translated_post_type' ) && pll_is_translated_post_type( 'book' ) ) {
        // Language-aware code for the book CPT.
    }
    
    if ( function_exists( 'pll_is_translated_taxonomy' ) && pll_is_translated_taxonomy( 'genre' ) ) {
        // Language-aware taxonomy code.
    }
    ```
    
    To opt in programmatically, hook early:
    
    ```php
    add_filter( 'pll_get_post_types', static function ( array $types, bool $is_settings ): array {
        $types['book'] = 'book';
        return $types;
    }, 10, 2 );
    
    add_filter( 'pll_get_taxonomies', static function ( array $taxonomies, bool $is_settings ): array {
        $taxonomies['genre'] = 'genre';
        return $taxonomies;
    }, 10, 2 );
    ```
    
    The source comments explicitly say these filters must be added early: in `plugins_loaded` for plugins or directly in `functions.php` for themes. Polylang caches the translated type lists after `after_setup_theme`, so late filters can appear to work in settings but fail at runtime.
    
    Use `$is_settings` if you want a type forced on but hidden from the Polylang settings UI:
    
    ```php
    add_filter( 'pll_get_post_types', static function ( array $types, bool $is_settings ): array {
        if ( ! $is_settings ) {
            $types['internal_doc'] = 'internal_doc';
        }
        return $types;
    }, 10, 2 );
    ```
    
    ## Admin, REST, and CLI caveats
    
    - In admin, `pll_current_language()` can be the admin language filter or `false` when "all languages" is selected.
    - In REST, Polylang sets the current language from the `lang` request parameter if present.
    - In cron/CLI/background jobs, there may be no current language. Pass an explicit language slug to downstream APIs instead of relying on current language.
    - Invalid or missing language setup returns `false` or safe fallbacks. Treat that as a real state, not an exceptional edge case.
    
    ## Common mistakes
    
    - Do not call `PLL()->curlang->slug` without checking that `PLL()->curlang` is a language object.
    - Do not concatenate language slugs into URLs.
    - Do not use flag codes as locales or locale strings as language slugs.
    - Do not translate all public CPTs/taxonomies blindly. Some post types are operational data, not content.
    - Do not assume `pll_current_language()` returns a string. It can return `false`, `int`, array values, or a `PLL_Language` object depending on `$field`.
    - In namespaced PHP, pass `\OBJECT` when requesting a `PLL_Language` object.
    
    ## Cross-references
    
    - Use `polylang-object-translations` to resolve translated post/term IDs or save translation groups.
    - Use `polylang-rest-headless` for REST `lang` parameters and Pro REST fields.
    - Use `polylang-wc-compatibility` for products, variations, orders, SKU uniqueness, and Woo REST.
    
    ## Verification
    
    Local source checked against:
    
    - Polylang public API: `wp-content/plugins/polylang/src/api.php`
    - Type opt-in filters: `wp-content/plugins/polylang/src/translated-post.php` and `translated-term.php`
    - Switcher filters and raw output: `wp-content/plugins/polylang/src/switcher.php`
    - REST language detection: `wp-content/plugins/polylang/src/rest-request.php`
    
    ## References
    
    - Official documentation: <https://polylang.pro/doc/function-reference/>
    - Official documentation: <https://polylang.pro/doc/developpers-how-to/>
    - Verified source paths:
      - `wp-content/plugins/polylang/polylang.php`
      - `wp-content/plugins/polylang/src/api.php`
      - `wp-content/plugins/polylang/src/switcher.php`
      - `wp-content/plugins/polylang/src/translated-post.php`
      - `wp-content/plugins/polylang/src/translated-term.php`
      - `wp-content/plugins/polylang/src/filter-rest-routes.php`
      - `wp-content/plugins/polylang/src/frontend/choose-lang.php`
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related