← All releases

PerfLocale 1.0.5

— Full site editing, a class of disappearing content fixed, and translations where your add-ons live

  • Fixed: page-builder layouts were overwritten between languages, in both directions. Elementor, Beaver Builder, Bricks, Oxygen and Oxygen 6 keep a page's text inside their layout data, and PerfLocale had been keeping that data identical across a translation group — so a translator finishing the German page and clicking Update replaced the English original with German, and the next English save replaced the German translation with English. Reproduced end to end on Elementor 4.2.1 and 4.1.3, with the wrong language then served to visitors. A new translation is still created as a full copy of the original layout; from that point each language owns its own, and nothing you type in one language can be overwritten by a save in another. The trade-off, taken deliberately: a later structural change to the original no longer propagates to translations that already exist — keeping a translator's words is worth more than automatic layout parity.
  • Block-theme templates and template parts are translatable. The header, footer, 404 page and page layouts you customise in the Site Editor now appear on the Translations screen alongside your posts. Enable “Full Site Editing” under Settings → Translation; it is off by default. A translation takes effect only once published, because WordPress renders an empty area for a template part it cannot resolve, and a half-finished header would leave a blank strip across your site.
  • The Site Editor has a Translations panel. Open a template, template part, pattern or navigation menu and the settings sidebar lists every language alongside Content and Design: which one you are editing, which translations exist, and a link straight to each one. Drafts are marked “Draft”, with “Not used until published” beneath, because a draft template is ignored and the original renders in its place — a condition that is invisible from the canvas. Previously this panel existed only in the post editor, so templates and template parts, which cannot be opened there at all, had no way to show their translations while you worked on them.
  • Saving a block template now creates it in every language. Previously the Site Editor's Translations panel listed your languages, showed “None” beside each one, and gave you no way to create them — the Site Editor has no “+ Create” the way the post editor does. Now saving a template or template part fills in the missing languages automatically, each seeded with the original's blocks rather than left empty. They are created as drafts, so nothing changes for visitors until you publish one: WordPress ignores a draft template and renders the original. Add a language later and the next save picks it up. Developers can switch the behaviour off with the perflocale/fse/auto_create_translations filter. Only templates do this — ordinary posts and pages are untouched.
  • Contact Form 7's form editor has a Translations panel too. Contact Form 7 builds its own editor screen rather than using the WordPress post editor, so neither the classic panel nor the block-editor sidebar could appear there — the one add-on whose forms most often need translating was the one with no way to see a form's translations without leaving the page. The box now sits in the editor's right-hand column, listing every language with a link to each translation.
  • WPForms' form builder has a Translations panel too, in the Settings panel's sidebar — the same box now shown in Contact Form 7's editor, the Site Editor and the post editor.
  • Fixed: content that a plugin finds by identity rather than by language disappeared on every non-default language. This is one defect with several faces, and it was triggered by the setup step the plugin itself recommends — “Assign Default Language”. Until that button was pressed the affected content had no language attached and was visible everywhere; the moment it did, the language filter began excluding it from lookups that were never browsing by language in the first place.
  • Fixed: Contact Form 7 embeds rendered “Error: Contact form not found” on every language except the default. Modern Contact Form 7 resolves [contact-form-7 id="…"] through a database query by hash, and the title fallback runs the same query, so neither could find the form. Forms now resolve on every language, and the translated form content is still served — the translation is applied when the form renders, which is where it belonged.
  • Fixed: Contact Form 7 forms could not be translated from the Translations screen. Add-on content types were read before the add-ons had registered them, and that incomplete list was kept for the rest of the request, so the screen's content-type filter offered no “Contact Forms” entry at all — WooCommerce products were affected the same way unless they had also been ticked in Settings. “Translate the entire site” skipped forms too, because it only offered content types WordPress lists in its own menus. Forms now appear on the Translations screen, Edit opens Contact Form 7's own editor, and each translation carries the form's fields, both email templates and its messages.
  • Fixed: Oxygen sites rendered with no layout at all on non-default languages. Oxygen chooses which template to use by loading every published template and matching its rules; with those templates filtered out, nothing matched and the page rendered with no header, footer or layout. Templates are now visible to that lookup on every language, and where a translated template exists it is still the one used.
  • Fixed: Oxygen “full page blocks” rendered as nothing on non-default languages. The companion Gutenberg plugin registers one block per full-page-block page, and with those pages filtered by language the block was never registered — and an unregistered block renders as empty, so the page body silently disappeared.
  • Fixed: an Oxygen template could render in the wrong language once its translation group was reduced to a single translation. If the source and the current language's template were both deleted, the one remaining template — in whatever third language it happened to be — was served to everyone. A template may now fall back to its source, never sideways to another translation.
  • Fixed: block-theme navigation menus were replaced by an automatically generated page list on non-default languages — and WordPress reacted to finding no menu by creating a new one, on an ordinary visitor pageview, which then displaced the real menu on the default language too.
  • Fixed: the WPForms form picker in page builders showed “No forms” when editing a translated page. The form list is fetched through a front-end request, so it was being filtered by language even though forms are chosen by id.
  • Fixed: WPForms forms could not be translated by anyone at all. The plugin offered them as translatable, but every request to list, create or edit a form's translations was refused — for administrators too. WPForms registers its forms with a permission model WordPress cannot answer questions about on its own, so PerfLocale now asks WPForms itself whether you may edit a given form, which also means sites using its Access add-on keep their own per-form rules.
  • Fixed: on WooCommerce block themes, the entire product body — title, gallery, price, add-to-cart, tabs — could vanish on a non-default language while the header, breadcrumb and footer still rendered. WooCommerce re-queries the product it has already loaded, and that second query was being filtered by language.
  • Fixed: a translated grouped product silently became a simple product. WooCommerce stores the product type in a taxonomy the translation never received, so the translation lost its type, its child products and its add-to-cart form.
  • Fixed: grouped product children now point at the current language. Cross-sells and upsells already did; children were the one list left out, so a German grouped product offered the English child products.
  • Fixed: when the shop page is also the site's front page, the home page listed every language's products. WooCommerce converts that page request into a product listing after the language filter has already decided the request was a page.
  • Fixed: WooCommerce product-grid blocks cached their results for thirty days under a key that did not include the language, so whichever language was viewed first served its products to all the others.
  • Fixed: a “Needs update” badge could never be cleared. Editing a source flagged its translations, and nothing in the plugin ever unflagged them — so once a translation was marked out of date it stayed that way, even after it had been brought fully up to date. Editing the translation now clears it.
  • Fixed: clearing a “Needs update” badge could overwrite a newer status. If the source was edited again, or another part of the plugin set its own status, while a translation was being saved, the clear won and the newer state was lost. The clear now applies only while the badge it was told to remove is still the one on the record.
  • Fixed: the dashboard counted trashed and never-started translations as translated, overstating progress. On one real site it reported 32 translated where 21 were. The Translations screen and the dashboard now reconcile a translation's recorded state against the post's actual state in the same way, so the two screens agree.
  • Fixed: Media was offered as a translatable content type although nothing implemented it. PerfLocale translates media in place — per-language alternative text, captions and descriptions on the same attachment — so the checkbox only risked filtering attachments by language. Media translation itself is unchanged.
  • Fixed: Bricks did not rebuild its generated CSS after a layout was copied to a translation, which every other builder integration already did.
  • Fixed: a new Elementor translation inherited the original page's generated stylesheet and its cached rendered HTML. The copy told Elementor the CSS was already built, so it did not rebuild — the translated page could render unstyled, or serve the original language from the cache until it expired. Those generated caches are no longer copied; Elementor rebuilds them for the translation on demand.
  • Fixed: machine translation through the WordPress AI Client silently ignored every option it was given — temperature, maximum tokens, the system instruction and the provider choice. The guard used to detect those methods reports false for methods a class exposes dynamically, so all four were dropped on every request.
  • Numbers and currency now follow the language's own formatting conventions rather than the site's, so a German page shows 1.234,56 where an English one shows 1,234.56.
  • Language fallbacks can be reordered on phones and tablets, and can be set per language from code through a filter that receives the context it is being asked about.
  • Both bulk buttons on the Settings screen now state the order they must be run in. “Generate Missing Translations” only sees content that already has a language, so on a site that had content before PerfLocale it reports nothing to do until “Assign Default Language” has run.
  • On WordPress 6.9 and newer, four read-only AI-agent abilities now register by default: list languages, get a post's translations, detect a post's language, and convert a URL between languages. They let an AI assistant or agent ask the plugin questions without being able to change anything. The two abilities that write stay switched off. Both switches are under Settings → Advanced → AI Agent Abilities, and earlier versions registered nothing unless a developer opted in.
  • For developers: perflocale/query/never_scoped_post_types marks a post type as translatable but never filtered by language, for content a plugin resolves by identity. perflocale/cf7/source_mail_keys pins chosen Contact Form 7 mail settings, such as the recipient, to the source form instead of the translation. perflocale/panel/rows and perflocale/panel/render let an add-on change or replace the Translations panel wherever it appears, and add-ons can mount that same panel on a host plugin's own editor screen.
  • Fixed: on a network, one subsite's active add-ons could switch off language filtering on another. The list of content types exempt from language filtering was worked out once per request and reused after switching sites, so a type exempted by an add-on active on one subsite stayed exempt on subsites where that add-on was not active — and those subsites then served the original instead of the translation.
  • The admin is easier to use on phones and tablets: the Translations table no longer crushes its per-language columns, the Strings export panel sizes itself to the space available instead of overflowing, the Add-ons filter tabs stay on one row and scroll sideways instead of wrapping (which used to strand the selected tab's underline above the line it sits on), and the background-task threshold table is readable at narrow widths.
  • The admin was checked end to end in right-to-left languages, and the layout fixes above have matching right-to-left styles.
  • Fixed: a template translation created by the bulk button started EMPTY, so translating a template meant rebuilding its entire layout by hand — and publishing a nearly empty one silently replaced that language's header and footer with nothing. Template and template-part translations now start from the source's blocks, so the layout survives and only the text needs translating.
  • Fixed: on a WooCommerce store, running Generate Missing Translations could break the checkout funnel in every non-default language. Translations are created as drafts, and the store-page lookup swapped the cart, checkout and my-account page ids to those drafts without checking they were published — so those links pointed at pages a shopper cannot open, and the mini-cart and terms links lost their language prefix. The same rule now applies to the site's front page and posts page when the default language is changed: a draft never replaces a live page.