Pro module9 abilities

Polylang ability reference

Reference for the GoBuildWP Pro 1.12.4 source. Installed schemas and policy remain authoritative.

The Polylang module lets an agent see which language each post is in, assign languages, link posts into translation sets, and read and write the string translations shown on Polylang's Languages > Translations screen. This Polylang ability reference covers that language layer; writing the translated posts themselves uses the ordinary post abilities.

Read-only, write and destructive groups describe the source annotations. They do not establish a minimum safety profile, human approval or undo support. Those controls are separate: check the installed ability, the connected user and the active site policy before calling it. A read can still return sensitive data. These pages are not evidence that every operation passed an end-to-end runtime test.

What Polylang needs before these load

Polylang 3.6.0 or newer must be active; the gate checks POLYLANG_VERSION and the pll_languages_list and pll_get_post_translations functions. Every call needs manage_options, and polylang-check-setup also reports whether the user has edit_posts. Content calls work on post types enabled for translation in Polylang's settings and return polylang_post_type_not_translated for others; trashed posts and auto-drafts count as not found. Pro ships a polylang-integration skill for the agent.

Minimum (base)
3.6.0 WPPILOT_PRO_POLYLANG_MIN_VERSION
Polylang editions
free, pro
Agent skills shipped
polylang-integration

Why language changes cannot be rolled back

A post's language is a taxonomy term, but changing it also rewrites the post_translations term whose description maps every post in the set. Restoring one post would leave that map pointing the wrong way, so the change ledger records polylang-set-content-language and polylang-link-content-translations as irreversible. Both are marked destructive, so they need confirm=true, or a person's approval when the site's confirmation mode is set to human. The language write is read back afterwards and fails with polylang_language_verification_failed if it did not stick.

Linking takes between 2 and 50 language-to-post pairs of one post type, and replaces the mapping for that set. Each post must already carry the language it is listed under, otherwise the call stops with polylang_translation_language_mismatch.

String translations are keyed by their original text

Polylang keeps one list of original and translated pairs per language, in the _pll_strings_translations term meta of that language's term. A string is named by its original text, or by name plus group when the name is shared. Polylang records pll_register_string() registrations on admin screens, not on REST or MCP requests, so polylang-list-strings also returns every original that already has a translation, marked registered: false, and check-setup's registration_complete says whether the list is whole.

Each value passes through the pll_sanitize_string_translation filter as it would on Polylang's screen, and an empty translation removes the entry. These writes are irreversible in the ledger, so each result returns previous. Replacing or clearing an existing translation needs confirm=true, and the bulk call (up to 100 items) needs dry_run or confirm.

Representative call sequence

This sequence describes the documented calls and checks to perform. It is not a recorded result from an installed WordPress site. Inspect returned errors and partial results, then verify the saved values and affected output before proceeding.

Connect an English page to the French copy a translator created as a separate page.

  1. 01 wppilot/polylang-get-content-language Shows the English page's language and that its translation set has no French member yet.
  2. 02 wppilot/polylang-set-content-language Assigns fr to the copy after the user confirms, returning before and after.
  3. 03 wppilot/polylang-link-content-translations Saves the en and fr pair as one translation set, so the language switcher links the two pages.

Polylang in practice

Polylang's model: a language term per post and one shared map per set

Polylang attaches a language to a post as a taxonomy term, and records a translation set as a separate post_translations term whose description maps each language to a post id. That second term is shared by every member of the set. A change to one post therefore rewrites data the others depend on, which is the reason the ledger declines to treat a Polylang language change as a single-post restore.

Every call names a language by its Polylang slug, as returned by wppilot/polylang-list-languages, never by locale or display name; an unknown value is refused with polylang_language_not_found. The language list also carries each language's locale, W3C code, right-to-left flag, term id and its position in Polylang's own ordering, which helps when an agent has to match a hreflang tag or lay out a switcher in the order editors chose.

wppilot/polylang-list-content runs WP_Query with Polylang's lang query variable, so the language filter is applied by Polylang itself. Each row's translations map leaves out members that are trashed or auto-drafts, so a set can look incomplete in the listing while Polylang's own map still names the trashed post. The status filter accepts publish, draft, pending, private and future, with no single value for all of them.

wppilot/polylang-link-content-translations assigns nothing. Every post in the map must already carry the language it is listed under, and the first one that does not stops the call with polylang_translation_language_mismatch, naming the post and language. A Polylang task therefore runs in two passes: wppilot/polylang-set-content-language for any post that is unassigned or wrong, then the link.

There is no source language and no option to keep or leave a group. The map goes to Polylang's pll_save_post_translations() as the whole relationship, and GoBuildWP compares what Polylang hands back with what was sent; any difference returns polylang_translation_link_failed. Because the map stands for the complete set, include every member that should stay linked, and on staging check whether a post you left out has dropped out of the switcher. Other refusals are polylang_invalid_translation_set (fewer than two or more than 50 pairs, an unconfigured slug, or a repeated post id) and polylang_translation_type_mismatch when the posts are of different types.

wppilot/polylang-set-content-language calls pll_set_post_language() and then reads the language back. A false return from Polylang gives polylang_language_assignment_failed, and asking for the language a post already holds returns changed: false without writing anything.

How a Polylang string write is planned, filtered and saved

Each item is resolved before anything is written. Naming it by original text always resolves, even when the string was never registered on this request. Naming it by name searches only the registrations Polylang recorded, so it fails with polylang_string_not_found when registration_complete is false, and with polylang_string_ambiguous when several groups use the name and no group was given.

The translation is trimmed and then passed to the pll_sanitize_string_translation filter with the name, group, original and previous value, in the order Polylang's Languages > Translations screen uses. The plan is built from the filtered result, so a filter that strips markup can turn an intended update into a no-op, and the result shows what was actually stored. An empty result removes the entry with delete_entry() rather than saving an empty string, because Polylang would export an empty entry as the original and count it as translated.

wppilot/polylang-bulk-set-string-translations groups items by language and saves each language once: it imports that language's pairs, applies every change, exports them to term meta, then re-reads the meta and checks each item, returning polylang_string_write_unverified for any that did not stick. Two items for the same string and language are rejected as polylang_duplicate_item rather than letting the second silently win. If Polylang's PLL_MO class or its model is not loaded on the request, every string call returns polylang_strings_unavailable.

Polylang under a restricted profile, and putting a change back

Under the Read Only safety profile the Polylang module shrinks to its reads. Under Production Safe every call loads, since none is classed as critical. The language and link writes are annotated destructive and go through the confirmation gate. The string writes are ordinary writes, so the gate does not stop them; their own checks do, and the bulk call is left unannotated on purpose so its dry run stays reachable without approval.

None of this can be undone with wppilot/rollback-change (see change ledger and rollback). To reverse a language change, call wppilot/polylang-set-content-language with the before value it returned, then relink the set from a wppilot/polylang-get-content-language read taken earlier. To reverse a string write, send its previous value back; a null previous means there was no translation, and sending an empty translation removes the one that was written.

Request: translate the footer tagline and copyright line into French. The agent runs wppilot/polylang-check-setup and notes string_translation.registration_complete, searches wppilot/polylang-list-strings with language fr and status untranslated, and copies each original verbatim. It sends both items to wppilot/polylang-bulk-set-string-translations with dry_run: true, shows the per-item plan, then repeats the same items with confirm: true.

On staging, filter Languages > Translations by French to see the stored values, load a French URL and read the footer, and compare each stored translation with what was sent, since the sanitising filter may have changed it. If a string the theme prints never appears in the list, open Languages > Translations once in wp-admin so Polylang records the theme's registrations, or pass the exact original text.

The module does not translate taxonomy terms, create translated posts, register new strings, or reach Polylang Pro services such as machine translation. Build the translated content with the ordinary post abilities first, then assign and link it here.

Questions about Polylang

Why does polylang-list-strings miss strings I can see in wp-admin?

Themes and plugins register strings with Polylang during admin page loads, which an MCP request is not. Pass the exact original text to polylang-set-string-translation and the write works regardless.

Can a string translation be undone?

Not through wppilot/rollback-change. Every write returns the value it replaced as previous, so writing that value back restores it.

Polylang reads

AbilityWhat it does
wppilot/polylang-check-setupCheck Polylang SetupReports the Polylang version, configured languages, default language, translated post types, and relevant administration capabilities. Call this before any Polylang operation.
wppilot/polylang-get-content-languageGet Content Language and TranslationsReads one post or page language and its complete Polylang translation set.
wppilot/polylang-list-contentList Polylang ContentLists translated WordPress content with its assigned language and translation-set summary. Supports post type, language, status, search, and bounded pagination.
wppilot/polylang-list-languagesList Polylang LanguagesLists the real languages configured in Polylang with slug, locale, W3C code, RTL state, ordering, and default-language state.
wppilot/polylang-list-stringsList Polylang StringsLists the strings in Polylang's Languages > Translations table — texts themes and plugins registered with pll_register_string, widget titles, and anything already translated — with each string's group, name, original text, and its translation in every language. Filter by group, name, search (original text or name), and language + status (untranslated or translated). Polylang keys translations by the original text, so the original returned here is what the write abilities need. registration_complete: false means Polylang is not running its admin side on this request and only records registrations there, so strings that are registered but never translated can be missing; strings that already have a translation are always listed.

Polylang writes

AbilityWhat it does
wppilot/polylang-bulk-set-string-translationsBulk Set Polylang String TranslationsWrites up to 100 Polylang string translations in one call, each item shaped like polylang-set-string-translation (original or name + optional group, language, translation). dry_run: true writes nothing and returns, per item, the current translation, the one it would become, and whether it is a create, update, clear or no-op. A real run requires confirm=true, saves each language once, and reports success or failure per item. Not reversible from the change ledger: each item's result carries previous.
wppilot/polylang-set-string-translationSet Polylang String TranslationWrites the translation of one Polylang string into one language, the same way the Languages > Translations screen saves it. Name the string by original (its exact source text, from polylang-list-strings), or by name (plus group if the name is not unique) when it is registered. An empty translation removes the translation so the original shows. The change ledger cannot undo this — Polylang keeps these in term meta that the ledger does not restore — so the result carries previous, and replacing or clearing an existing different translation is refused without confirm=true.

Destructive Polylang calls

AbilityWhat it does
wppilot/polylang-set-content-languageSet Content LanguageAssigns an existing Polylang language to a translated post type. This can detach or alter an existing translation relationship and therefore requires confirmation.

For the task-level view of Polylang, with worked requests and what to check afterwards, see Polylang integration page.