{"slug":"polylang-language-api","title":"polylang-language-api","summary":"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","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-16T14:52:05.41344Z","repo":{"url":"https://github.com/Lonsdale201/wp-agent-skills","stars":22,"forks":2,"license":"MIT","updatedAt":"2026-09-21T19:53:59Z"},"bodyHtml":"<hr>\n<h2>name: polylang-language-api\ndescription: \"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.\"\nmetadata:\nwp-skills-author: \"Soczo Kristof\"\nwp-skills-contact: \"mailto:lonsdale201@hotmail.com\"\nwp-skills-plugin: \"polylang\"\nwp-skills-plugin-version-tested: \"Polylang 3.8.5\"\nwp-skills-wp-version-tested: \"7.0\"\nwp-skills-php-min: \"7.4\"\nwp-skills-last-updated: \"2026-07-01\"</h2>\n<h1>Polylang Language API</h1>\n<p>Use this skill when plugin or classic theme code needs to detect, list, switch, or route by Polylang languages.</p>\n<p>Polylang's public API lives in <code>wp-content/plugins/polylang/src/api.php</code>. Prefer those functions over <code>PLL()-&gt;...</code> internals. The <code>PLL()</code> accessor exists, but the source itself says API functions are preferred because internals may change.</p>\n<h2>Load and guard</h2>\n<p>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:</p>\n<pre><code>if ( ! function_exists( 'pll_current_language' ) ) {\n    return;\n}\n\n$lang = pll_current_language();\nif ( ! $lang ) {\n    $lang = pll_default_language();\n}\n\nif ( ! $lang ) {\n    return;\n}\n</code></pre>\n<p>Do not read <code>$_GET['lang']</code> directly as your language model. Let Polylang define the current language, then read it through <code>pll_current_language()</code>.</p>\n<h2>Current and default language</h2>\n<p><code>pll_current_language( $field = 'slug' )</code> returns the current language on frontend, the admin language filter in admin, and <code>false</code> if no current language exists. Useful fields include:</p>\n<pre><code>$slug   = pll_current_language();          // e.g. \"en\"\n$locale = pll_current_language( 'locale' ); // e.g. \"en_US\"\n$name   = pll_current_language( 'name' );\n$lang   = pll_current_language( \\OBJECT ); // PLL_Language object.\n</code></pre>\n<p><code>pll_default_language( $field = 'slug' )</code> has the same field behavior and returns <code>false</code> if no default language exists yet.</p>\n<p>Since Polylang 3.4, composite language term properties are accepted:</p>\n<pre><code>$term_taxonomy_id = pll_current_language( 'language:term_taxonomy_id' );\n$term_language_tt = pll_default_language( 'term_language:term_taxonomy_id' );\n</code></pre>\n<p>Use composite fields only when you need SQL joins or taxonomy term IDs. For most code, use slugs.</p>\n<h2>List languages</h2>\n<p>Use <code>pll_languages_list()</code> for language lists:</p>\n<pre><code>$slugs = pll_languages_list();\n\n$locales = pll_languages_list( array(\n    'fields' =&gt; 'locale',\n) );\n\n$active_slugs = pll_languages_list( array(\n    'fields'     =&gt; 'slug',\n    'hide_empty' =&gt; true,\n) );\n</code></pre>\n<p><code>hide_empty</code> removes languages without posts. <code>hide_default</code> is also accepted by the implementation even though it is not documented in the short API block.</p>\n<p>Do not derive languages from installed <code>.mo</code> files or locales. Polylang languages are stored as language terms and carry URL, flag, ordering, active/default, and term property data.</p>\n<h2>URLs and switchers</h2>\n<p>Use <code>pll_home_url( $lang )</code> for localized home URLs:</p>\n<pre><code>$url = pll_home_url( 'fr' );\n</code></pre>\n<p>If no language or links model is available, the function falls back to <code>home_url( '/' )</code>. Do not build URLs by concatenating <code>/$lang/</code>; Polylang supports query-arg, directory, subdomain, and domain modes.</p>\n<p>For a rendered switcher:</p>\n<pre><code>if ( function_exists( 'pll_the_languages' ) ) {\n    pll_the_languages( array(\n        'show_flags' =&gt; 1,\n        'show_names' =&gt; 1,\n    ) );\n}\n</code></pre>\n<p>For custom markup, ask for raw data and escape output yourself:</p>\n<pre><code>$items = pll_the_languages( array(\n    'raw'  =&gt; 1,\n    'echo' =&gt; 0,\n) );\n\nforeach ( $items as $item ) {\n    printf(\n        '&lt;a href=\"%s\" lang=\"%s\"%s&gt;%s&lt;/a&gt;',\n        esc_url( $item['url'] ),\n        esc_attr( $item['locale'] ),\n        ! empty( $item['current_lang'] ) ? ' aria-current=\"true\"' : '',\n        esc_html( $item['name'] )\n    );\n}\n</code></pre>\n<p><code>pll_the_languages()</code> returns an empty string or empty array outside the frontend if the links model is not available.</p>\n<h2>Translated post types and taxonomies</h2>\n<p>Check whether Polylang manages a type before adding language-dependent logic:</p>\n<pre><code>if ( function_exists( 'pll_is_translated_post_type' ) &amp;&amp; pll_is_translated_post_type( 'book' ) ) {\n    // Language-aware code for the book CPT.\n}\n\nif ( function_exists( 'pll_is_translated_taxonomy' ) &amp;&amp; pll_is_translated_taxonomy( 'genre' ) ) {\n    // Language-aware taxonomy code.\n}\n</code></pre>\n<p>To opt in programmatically, hook early:</p>\n<pre><code>add_filter( 'pll_get_post_types', static function ( array $types, bool $is_settings ): array {\n    $types['book'] = 'book';\n    return $types;\n}, 10, 2 );\n\nadd_filter( 'pll_get_taxonomies', static function ( array $taxonomies, bool $is_settings ): array {\n    $taxonomies['genre'] = 'genre';\n    return $taxonomies;\n}, 10, 2 );\n</code></pre>\n<p>The source comments explicitly say these filters must be added early: in <code>plugins_loaded</code> for plugins or directly in <code>functions.php</code> for themes. Polylang caches the translated type lists after <code>after_setup_theme</code>, so late filters can appear to work in settings but fail at runtime.</p>\n<p>Use <code>$is_settings</code> if you want a type forced on but hidden from the Polylang settings UI:</p>\n<pre><code>add_filter( 'pll_get_post_types', static function ( array $types, bool $is_settings ): array {\n    if ( ! $is_settings ) {\n        $types['internal_doc'] = 'internal_doc';\n    }\n    return $types;\n}, 10, 2 );\n</code></pre>\n<h2>Admin, REST, and CLI caveats</h2>\n<ul>\n<li>In admin, <code>pll_current_language()</code> can be the admin language filter or <code>false</code> when \"all languages\" is selected.</li>\n<li>In REST, Polylang sets the current language from the <code>lang</code> request parameter if present.</li>\n<li>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.</li>\n<li>Invalid or missing language setup returns <code>false</code> or safe fallbacks. Treat that as a real state, not an exceptional edge case.</li>\n</ul>\n<h2>Common mistakes</h2>\n<ul>\n<li>Do not call <code>PLL()-&gt;curlang-&gt;slug</code> without checking that <code>PLL()-&gt;curlang</code> is a language object.</li>\n<li>Do not concatenate language slugs into URLs.</li>\n<li>Do not use flag codes as locales or locale strings as language slugs.</li>\n<li>Do not translate all public CPTs/taxonomies blindly. Some post types are operational data, not content.</li>\n<li>Do not assume <code>pll_current_language()</code> returns a string. It can return <code>false</code>, <code>int</code>, array values, or a <code>PLL_Language</code> object depending on <code>$field</code>.</li>\n<li>In namespaced PHP, pass <code>\\OBJECT</code> when requesting a <code>PLL_Language</code> object.</li>\n</ul>\n<h2>Cross-references</h2>\n<ul>\n<li>Use <code>polylang-object-translations</code> to resolve translated post/term IDs or save translation groups.</li>\n<li>Use <code>polylang-rest-headless</code> for REST <code>lang</code> parameters and Pro REST fields.</li>\n<li>Use <code>polylang-wc-compatibility</code> for products, variations, orders, SKU uniqueness, and Woo REST.</li>\n</ul>\n<h2>Verification</h2>\n<p>Local source checked against:</p>\n<ul>\n<li>Polylang public API: <code>wp-content/plugins/polylang/src/api.php</code></li>\n<li>Type opt-in filters: <code>wp-content/plugins/polylang/src/translated-post.php</code> and <code>translated-term.php</code></li>\n<li>Switcher filters and raw output: <code>wp-content/plugins/polylang/src/switcher.php</code></li>\n<li>REST language detection: <code>wp-content/plugins/polylang/src/rest-request.php</code></li>\n</ul>\n<h2>References</h2>\n<ul>\n<li>Official documentation: <a href=\"https://polylang.pro/doc/function-reference/\">https://polylang.pro/doc/function-reference/</a></li>\n<li>Official documentation: <a href=\"https://polylang.pro/doc/developpers-how-to/\">https://polylang.pro/doc/developpers-how-to/</a></li>\n<li>Verified source paths:\n<ul>\n<li><code>wp-content/plugins/polylang/polylang.php</code></li>\n<li><code>wp-content/plugins/polylang/src/api.php</code></li>\n<li><code>wp-content/plugins/polylang/src/switcher.php</code></li>\n<li><code>wp-content/plugins/polylang/src/translated-post.php</code></li>\n<li><code>wp-content/plugins/polylang/src/translated-term.php</code></li>\n<li><code>wp-content/plugins/polylang/src/filter-rest-routes.php</code></li>\n<li><code>wp-content/plugins/polylang/src/frontend/choose-lang.php</code></li>\n</ul>\n</li>\n</ul>\n","files":[{"path":"agents/openai.yaml","sizeBytes":263,"isText":true},{"path":"SKILL.md","sizeBytes":8163,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-16T14:57:07.095146Z","sha256":"EDB8BBCD7C49DFCF5A9A38B3A4DA453D77BC9EB72B43E5A61DDF09D89ADA03E2","sizeBytes":3409},"review":null,"source":{"repositoryUrl":"https://github.com/Lonsdale201/wp-agent-skills","path":"polylang/polylang-language-api","license":"MIT","commit":"8820ff3c301066297e696611e3bc4ebeb47d1851","subtreeSha":"82FCB6405215FDD9F0DF85C5C6EA861295E30ED03660C1CC0B3A081D272C5AFE","lastSyncedAt":"2026-09-22T13:51:11.366991Z"},"reviewedAt":"2026-09-16T15:15:08.857882Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/polylang/polylang-language-api"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart"},{"target":"git","command":"git clone https://github.com/Lonsdale201/wp-agent-skills.git"}]}