Translating tabs with WPML and Polylang
The plugin ships the configuration both translation plugins read, and a small amount of runtime code that maps ids between languages. This page describes what that configuration actually declares, so you can judge whether it covers your setup before you install anything.
Certification. Product Tabs Studio has not been certified by WPML or by Polylang. Neither vendor has reviewed it and it appears on neither compatibility list. What follows is a description of code you can read, not a claim of endorsement. If a certificate is a requirement for your project, treat this plugin as uncertified.
What has to be translated
Tabs are stored in two different shapes, and the translation plugins treat them differently.
- Global tabs
- Ordinary posts of the
ptstudio_tabtype. Their title and content are post fields, which both plugins already know how to translate. Their key, priority, heading and rules are post meta. - A product's own tabs
- One post meta value on the product,
_ptstudio_tabs, holding a JSON string with every tab's title, content, key and flags. It is a single field, so translating it means translating strings inside a structured value.
That second shape is what makes tab translation awkward in general, and it is where the two plugins diverge.
WPML
The plugin's root directory contains wpml-config.xml. WPML scans plugin directories for that file and applies it on its own; there is nothing to import or switch on.
What the file declares
ptstudio_tabis a translatable custom post type, so global tabs get the usual language column, the translation editor and the language switcher._ptstudio_tabsis declaredtranslatewithencoding="json", and the fields inside it are mapped: for every entry in the tabs array, itstitleand itscontent, the latter marked as visual so the editor shows it as rich text rather than raw markup. This is what exposes a product's own tabs to the translation editor.- The tab's key, priority, admin label, heading, content mode, rules, origin and migration marks are declared
copy. They are settings, not text: a translated tab keeps the same key and the same rules as its original. _ptstudio_tabs_historyis declaredignore. Version history belongs to the post it was written on and is not carried across languages.- Under admin texts, the titles you give the renamed default WooCommerce tabs on the Tab order screen are exposed, so they can be translated in String Translation.
What the runtime code does
A second piece, includes/Compat/WPML.php, is inert unless WPML's own API is present. When it is, it does four things.
- When a product links to a global tab, the tab's id is mapped through WPML's
wpml_object_id, so the visitor sees the tab in the language they are browsing. If no translation exists, the original is used rather than nothing. - When rules are evaluated, the product's category, tag and brand term ids and its own product id are supplemented with their default-language counterparts. A rule written once in the default language therefore matches translated products too, and a rule written in the product's own language keeps matching. Ids are added, never replaced.
- Internal caches are keyed by the current language, so two languages cannot serve each other's resolved tabs within one request.
- When a translation of a product is saved and has no tab data of its own, the original product's tabs are copied to it once. It never overwrites anything: if the field already exists, even empty, the copy is skipped.
That last point is a fallback, not the intended path. The intended path is the JSON field translation described above. The copy exists because a JSON custom field is a demanding thing for a translation editor to handle, and versions differ in how well they parse it; without the fallback, a translated product could end up with no tabs at all rather than untranslated ones. Untranslated tabs are the better failure. Developers who would rather have the empty result can return false from ptstudio_wpml_copy_tabs_to_translation.
How to check it on your site
- Translate a product that has its own tabs. Open the translation editor and look for the tab titles and bodies among the fields. If they are there, the JSON path is working on your WPML version and you translate them like any other field.
- If they are not there, save the translation once. The original's tabs are copied over, and you edit them in the translated product's own Product Tabs panel, in that language.
- Translate a global tab from Products → Product Tabs. Title and content go through the normal post translation; the key and the rules come across unchanged.
- Switch the storefront language and check a product page. The tab labels should be in the visitor's language and the anchors unchanged.
Polylang
includes/Compat/Polylang.php is inert unless Polylang's API is loaded. When it is, it registers four things.
ptstudio_tabis added to Polylang's translatable post types, but only whenproductis already translatable. If you have not made products multilingual, global tabs stay monolingual too, which is almost always what you want._ptstudio_tabs, together with the key, priority, admin label, heading, content mode, rules and internal metadata, is added to the list of meta Polylang copies to a new translation.- The version history and the migration marks are explicitly excluded from that list. They are not copied and not synchronised.
- Saved-tab ids, term ids and product ids are mapped through
pll_get_postandpll_get_term, the same way as under WPML: the tab shown is the one for the current language, and rules written in the default language also match translations.
The honest difference
Polylang copies; it does not translate. When you create the Spanish version of a product, its tabs arrive as an exact copy of the English ones — same titles, same content, same keys — and you translate them by editing the Spanish product's Product Tabs panel. There is no field-level translation editor for the tabs JSON, because Polylang has no equivalent of WPML's JSON field mechanism. Global tabs are better served: they are posts, so each language has its own post with its own title and content.
This means tab content on a Polylang site is maintained per language. A correction to an English size guide does not propagate to the Spanish one. If the same text really must appear on many products in many languages, a global tab per language, each with its own rules, is less work than per-product tabs.
The mapping described here is deliberately basic. Deeper Polylang integration — synchronising rules across languages, translating rules term by term — is not implemented and is not claimed.
What is shared between languages
Under both plugins, the tab key is copied rather than translated. A tab keyed size-guide keeps that key in every language, so #tab-size-guide is a valid anchor on every translated product page and theme CSS written against the key matches everywhere. Translating keys would be more idiomatic and would break every existing link, so they are not translated.
Priorities, headings and rules are copied for the same reason: they describe placement, and placement should not drift between languages.
Version history is not shared. Each translated product keeps its own, on the post it belongs to.
Confirming what is detected
Products → Tabs Tools & Health → System report has a Compatibility section that reports whether WPML or Polylang was detected, with the version and the current and default language codes. If a translation plugin is installed but shows as not detected there, the runtime mapping is not running, and that is the first thing to resolve.
What is not supported
- No automatic or machine translation of any kind. Nothing is sent anywhere.
- No translation plugins other than these two. Others may work through the same WordPress mechanisms, but nothing in the code addresses them and we have not tested them.
- No per-language rules. A global tab's rules are one set, shared by its translations.
- The plugin's own interface and messages are translatable in the usual WordPress way, through the text domain
product-tabs-studio. Translations for it are contributed on the WordPress.org translation site, not inside the plugin.