{"id":371817,"date":"2026-10-07T03:46:49","date_gmt":"2026-10-07T03:46:49","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/effortless-multisite-auto-translate\/"},"modified":"2026-10-19T15:01:45","modified_gmt":"2026-10-19T15:01:45","slug":"effortless-multisite-auto-translate","status":"publish","type":"plugin","link":"https:\/\/fi.wordpress.org\/plugins\/effortless-multisite-auto-translate\/","author":23148025,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"3.3.57","stable_tag":"3.3.57","tested":"7.1.3","requires":"5.4","requires_php":"7.4","requires_plugins":null,"header_name":"EffortLess Multisite Auto Translate","header_author":"domclic","header_description":"Translate Gutenberg posts and titles across a WordPress multisite using GPT. Includes retries, error logging, live log highlighting, and automatic sync of trash\/delete operations.","assets_banners_color":"9e9e9e","last_updated":"2026-10-19 15:01:45","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/id7.dev\/donate\/","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":852,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"3.3.10":{"tag":"3.3.10","author":"domclic","date":"2026-10-13 23:45:07","revision":3745064},"3.3.11":{"tag":"3.3.11","author":"domclic","date":"2026-10-14 13:11:52","revision":3746443},"3.3.13":{"tag":"3.3.13","author":"domclic","date":"2026-10-15 08:57:03","revision":3748003},"3.3.14":{"tag":"3.3.14","author":"domclic","date":"2026-10-15 09:23:19","revision":3748050},"3.3.16":{"tag":"3.3.16","author":"domclic","date":"2026-10-15 11:18:03","revision":3748268},"3.3.17":{"tag":"3.3.17","author":"domclic","date":"2026-10-15 11:25:12","revision":3748283},"3.3.18":{"tag":"3.3.18","author":"domclic","date":"2026-10-15 11:31:57","revision":3748294},"3.3.20":{"tag":"3.3.20","author":"domclic","date":"2026-10-15 11:36:46","revision":3748309},"3.3.21":{"tag":"3.3.21","author":"domclic","date":"2026-10-15 11:49:41","revision":3748344},"3.3.22":{"tag":"3.3.22","author":"domclic","date":"2026-10-15 13:02:24","revision":3748533},"3.3.24":{"tag":"3.3.24","author":"domclic","date":"2026-10-15 14:18:44","revision":3748679},"3.3.25":{"tag":"3.3.25","author":"domclic","date":"2026-10-15 23:46:28","revision":3749487},"3.3.26":{"tag":"3.3.26","author":"domclic","date":"2026-10-16 01:53:11","revision":3749539},"3.3.27":{"tag":"3.3.27","author":"domclic","date":"2026-10-16 01:59:04","revision":3749546},"3.3.28":{"tag":"3.3.28","author":"domclic","date":"2026-10-16 03:10:26","revision":3749604},"3.3.29":{"tag":"3.3.29","author":"domclic","date":"2026-10-16 10:51:40","revision":3750213},"3.3.30":{"tag":"3.3.30","author":"domclic","date":"2026-10-16 15:00:20","revision":3750619},"3.3.31":{"tag":"3.3.31","author":"domclic","date":"2026-10-16 15:20:55","revision":3750671},"3.3.32":{"tag":"3.3.32","author":"domclic","date":"2026-10-17 00:50:07","revision":3751293},"3.3.33":{"tag":"3.3.33","author":"domclic","date":"2026-10-17 00:57:10","revision":3751299},"3.3.34":{"tag":"3.3.34","author":"domclic","date":"2026-10-17 01:17:08","revision":3751307},"3.3.35":{"tag":"3.3.35","author":"domclic","date":"2026-10-17 01:42:06","revision":3751320},"3.3.36":{"tag":"3.3.36","author":"domclic","date":"2026-10-17 03:04:43","revision":3751350},"3.3.37":{"tag":"3.3.37","author":"domclic","date":"2026-10-17 03:33:46","revision":3751365},"3.3.38":{"tag":"3.3.38","author":"domclic","date":"2026-10-17 04:09:04","revision":3751392},"3.3.39":{"tag":"3.3.39","author":"domclic","date":"2026-10-17 10:54:39","revision":3751806},"3.3.40":{"tag":"3.3.40","author":"domclic","date":"2026-10-17 11:13:24","revision":3751842},"3.3.41":{"tag":"3.3.41","author":"domclic","date":"2026-10-17 13:48:29","revision":3751976},"3.3.42":{"tag":"3.3.42","author":"domclic","date":"2026-10-17 14:52:05","revision":3752035},"3.3.43":{"tag":"3.3.43","author":"domclic","date":"2026-10-18 02:23:02","revision":3752479},"3.3.44":{"tag":"3.3.44","author":"domclic","date":"2026-10-18 02:31:27","revision":3752482},"3.3.45":{"tag":"3.3.45","author":"domclic","date":"2026-10-18 02:50:28","revision":3752490},"3.3.46":{"tag":"3.3.46","author":"domclic","date":"2026-10-18 23:10:21","revision":3753698},"3.3.48":{"tag":"3.3.48","author":"domclic","date":"2026-10-19 02:36:14","revision":3753831},"3.3.49":{"tag":"3.3.49","author":"domclic","date":"2026-10-19 02:52:13","revision":3753845},"3.3.50":{"tag":"3.3.50","author":"domclic","date":"2026-10-19 03:54:12","revision":3753922},"3.3.51":{"tag":"3.3.51","author":"domclic","date":"2026-10-19 04:27:54","revision":3753953},"3.3.52":{"tag":"3.3.52","author":"domclic","date":"2026-10-19 04:51:09","revision":3753989},"3.3.54":{"tag":"3.3.54","author":"domclic","date":"2026-10-19 05:43:24","revision":3754053},"3.3.55":{"tag":"3.3.55","author":"domclic","date":"2026-10-19 06:04:48","revision":3754076},"3.3.56":{"tag":"3.3.56","author":"domclic","date":"2026-10-19 08:04:11","revision":3754241},"3.3.57":{"tag":"3.3.57","author":"domclic","date":"2026-10-19 15:01:45","revision":3755046},"3.3.6":{"tag":"3.3.6","author":"domclic","date":"2026-10-10 14:27:47","revision":3738153},"3.3.7":{"tag":"3.3.7","author":"domclic","date":"2026-10-12 13:34:10","revision":3741733},"3.3.8":{"tag":"3.3.8","author":"domclic","date":"2026-10-13 23:16:51","revision":3745022},"3.3.9":{"tag":"3.3.9","author":"domclic","date":"2026-10-13 23:32:33","revision":3745049}},"upgrade_notice":{"3.3.0":"<p>Adds DeepL as a second provider, per-post-type opt-out, draft-first review mode, and a per-post status box. Fixes automatic translation silently ignoring the post-type opt-out. Removes the &quot;Translation Temperature&quot; slider, which never reached the API. No database changes.<\/p>","3.2.66":"<p>Fixes the settings page&#039;s asset enqueue (was registered on a WordPress hook that never fires) and a checkbox that never reflected its saved state; internal rename ahead of first WordPress.org submission (ms_gpt\/MS_GPT prefix renamed to elmat\/ELMAT). No database changes.<\/p>","3.2.50":"<p>Fixes the Translation Glossary, which never actually worked due to a reversed placeholder-substitution bug. Safe to upgrade, no DB changes.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.jpg":{"filename":"icon-128x128.jpg","revision":3738158,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.jpg":{"filename":"icon-256x256.jpg","revision":3738158,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.jpg":{"filename":"banner-1544x500.jpg","revision":3738158,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.jpg":{"filename":"banner-772x250.jpg","revision":3738158,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["3.3.10","3.3.11","3.3.13","3.3.14","3.3.16","3.3.17","3.3.18","3.3.20","3.3.21","3.3.22","3.3.24","3.3.25","3.3.26","3.3.27","3.3.28","3.3.29","3.3.30","3.3.31","3.3.32","3.3.33","3.3.34","3.3.35","3.3.36","3.3.37","3.3.38","3.3.39","3.3.40","3.3.41","3.3.42","3.3.43","3.3.44","3.3.45","3.3.46","3.3.48","3.3.49","3.3.50","3.3.51","3.3.52","3.3.54","3.3.55","3.3.56","3.3.57","3.3.6","3.3.7","3.3.8","3.3.9"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"Main configuration screen with API settings","2":"Site selection for source and destination","3":"Real-time translation logs","4":"Advanced tools for bulk translation and emergency stop","5":"Status badges showing configuration state","6":"Bulk translation progress"}},"plugin_section":[],"plugin_tags":[216551,148076,441,194533,2333],"plugin_category":[48,51],"plugin_contributors":[241557],"plugin_business_model":[],"class_list":["post-371817","plugin","type-plugin","status-publish","hentry","plugin_tags-gpt","plugin_tags-gutenberg","plugin_tags-multisite","plugin_tags-openai","plugin_tags-translation","plugin_category-language-tools","plugin_category-multisite","plugin_contributors-domclic","plugin_committers-domclic"],"banners":{"banner":"https:\/\/ps.w.org\/effortless-multisite-auto-translate\/assets\/banner-772x250.jpg?rev=3738158","banner_2x":"https:\/\/ps.w.org\/effortless-multisite-auto-translate\/assets\/banner-1544x500.jpg?rev=3738158","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/effortless-multisite-auto-translate\/assets\/icon-128x128.jpg?rev=3738158","icon_2x":"https:\/\/ps.w.org\/effortless-multisite-auto-translate\/assets\/icon-256x256.jpg?rev=3738158","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>EffortLess Multisite Auto Translate is a Network-Activated plugin for WordPress Multisite. When a post, page, or custom post type is published or updated on one site in the network (the \"source\" site), the plugin sends its title, content, categories, and tags to your chosen translation provider (OpenAI or DeepL) and creates or updates a matching translated copy on each of the network's other sites you've selected as a \"destination\". Translation runs in the background via WordPress Cron (or Action Scheduler, if another active plugin provides it), not during the page save itself.<\/p>\n\n<p>Content is translated block by block: each Gutenberg block is sent to the API and reassembled individually, so block structure, attributes, and nested layouts (columns, groups, galleries) survive translation. Shortcodes and <code>core\/html<\/code> blocks are left untouched rather than translated. A Glossary lets you pin specific terms (brand names, proper nouns) to a fixed translation, or to \"do not translate,\" per destination site if needed.<\/p>\n\n<h4>What gets translated<\/h4>\n\n<ul>\n<li>Post titles and content (Gutenberg blocks)<\/li>\n<li>Pages and public custom post types you haven't unticked in \"Post Types to Translate\"<\/li>\n<li>Categories, tags, and other public taxonomies<\/li>\n<li>Post metadata you explicitly add to the \"Extra translatable meta keys\" setting<\/li>\n<\/ul>\n\n<h4>What is left untouched<\/h4>\n\n<ul>\n<li>Shortcodes (preserved exactly, including their attributes)<\/li>\n<li><code>core\/html<\/code> blocks (skipped by default; a <code>elmat_translate_html_block<\/code> filter exists for developers who want to opt them in)<\/li>\n<li>Private post metadata<\/li>\n<\/ul>\n\n<h4>Settings (Network Admin \u2192 EffortLess Multisite Auto Translate: API configuration, Site configuration and Translation glossary tabs)<\/h4>\n\n<ul>\n<li><strong>Translation Provider<\/strong> \u2014 OpenAI (GPT), DeepL, or (WordPress 7.0+ only) the built-in WordPress AI Client, which uses whichever provider your site has configured under Settings \u2192 Connectors instead of a key entered here. Only the selected provider's key below is used.<\/li>\n<li><strong>OpenAI API Key<\/strong> \/ <strong>DeepL API Key<\/strong> \u2014 required for whichever provider is selected. A \"Test API Key\" button verifies the active one before you save.<\/li>\n<li><strong>Enable automatic translations<\/strong> \u2014 checkbox. When off, translation only happens if triggered manually from the block editor's EffortLess Multisite Auto Translate panel.<\/li>\n<li><strong>Publish new translations as draft<\/strong> \u2014 checkbox, off by default. When on, a translation's <em>first<\/em> creation is saved as a draft instead of published, so a human can review it before it goes live. Only affects first creation \u2014 updating an already-published translation keeps updating it in place, never unpublishing it automatically.<\/li>\n<li><strong>GPT Model<\/strong> \u2014 <code>gpt-6-luna<\/code>, <code>gpt-4o-mini<\/code> (default), <code>gpt-5-nano<\/code> (the cheapest), <code>gpt-5.4-mini<\/code> (several times the price of the others) or <code>gpt-5-mini<\/code>. OpenAI only. The menu lists those your OpenAI API key may use, read from OpenAI's model list (<code>GET \/v1\/models<\/code>, kept 12 hours per key and read again when the key is saved and with \"Test API Key\"); when that list cannot be read, every model is listed. The GPT-5 and GPT-6 models are reasoning models: they are sent <code>max_completion_tokens<\/code> and their lowest reasoning effort (<code>minimal<\/code> for gpt-5-nano and gpt-5-mini, <code>none<\/code> for gpt-5.4-mini, <code>low<\/code> for gpt-6-luna) instead of <code>max_tokens<\/code> and a temperature, and their reasoning is billed as output tokens. <code>gpt-4o<\/code> is no longer offered; a site already using it keeps it until another model is chosen. Changing the model clears the translation memory, so the next run translates everything again.<\/li>\n<li><strong>Use OpenAI's Batch API<\/strong> \u2014 off by default, OpenAI only. Automatic translations (a saved post, Translate All Posts, Sync Design) send their texts to OpenAI as one batch per post, billed at half price and answered within 24 hours (often within minutes). Meanwhile the post shows \"\ud83d\udce6 in batch\" on the Status tab and its copies keep their previous version; the batches are checked every 10 minutes by WP-Cron, and when one ends its answers go into the translation memory and the post is translated from them. \"translate now\", retries and texts translated after a copy is saved (category and tag names, SEO fields) are sent directly at the normal price. A batch that OpenAI cannot finish (failed or expired) is replaced by a direct translation.<\/li>\n<li><strong>Translation Style<\/strong> \u2014 Literal, Natural (default), or Creative \u2014 controls how closely the translation sticks to the source wording. Applies to both providers (mapped internally for each).<\/li>\n<li><strong>Additional Instructions<\/strong> \u2014 free-text instructions appended to every translation request (tone, formality, house style). OpenAI only \u2014 DeepL has no prompt\/instruction mechanism.<\/li>\n<li><strong>Post Types to Translate<\/strong> \u2014 untick any post type to stop translating it, both automatically and via Bulk Translate. Lists every public post type registered on the source site.<\/li>\n<li><strong>Extra translatable meta keys<\/strong> \u2014 a plain-text list (one per line) of additional post-meta keys to translate, for meta fields added by other plugins (e.g. custom SEO title\/description fields).<\/li>\n<li><strong>Shared Media Library<\/strong> \u2014 optionally designate one site's Media Library as the single source of truth for images; other sites' translated content then links back to that site's files instead of duplicating them.<\/li>\n<li><strong>Source Site<\/strong> \u2014 the one site whose saves trigger translation.<\/li>\n<li><strong>Destination Sites<\/strong> \u2014 which of the network's other sites receive translated copies.<\/li>\n<li><strong>Glossary<\/strong> \u2014 terms with a fixed translation, so brand names, place names and words with several meanings translate consistently. Pick a language at the top to see and edit its values; a value under \"All languages (default)\" applies to every language without its own, \"Keep as is\" leaves a brand name unchanged, and an empty term is left to the AI. It can be exported to a CSV file, edited in a spreadsheet and imported again.<\/li>\n<\/ul>\n\n<p>A running character-usage total (this month + lifetime) is shown above the API key fields once translation has run at least once \u2014 a rough usage indicator, not exact billing; check your provider's own dashboard for that.<\/p>\n\n<p>A \"Translation Status\" box also appears in the post editor's sidebar (source site only) showing each destination's status for the post you're editing, without a trip to Network Admin.<\/p>\n\n<h4>Tools (Network Admin \u2192 EffortLess Multisite Auto Translate \u2192 Tools)<\/h4>\n\n<p>Bulk Translate All Posts, Force Re-translate All (clears retry\/failure counters and re-sends everything, skipping the translation memory: the most expensive action), Sync Design &amp; Templates (switches every destination site's active theme to match the source, network-enabling it first if needed, then copies global styles, site icon, logo, and queues templates\/patterns for translation \u2014 a confirmation dialog states the theme change before you proceed), Resync Media Links (also runs automatically whenever the Shared Media Library setting changes), Clean Destinations (removes orphaned translations and non-translation content added directly on a destination site), Flush Permalinks, and Emergency Stop (cancels every pending translation). Stuck queue entries clear themselves automatically within about an hour \u2014 there's no separate cleanup button for that.<\/p>\n\n<h4>Notes and limitations<\/h4>\n\n<ul>\n<li>Requires WordPress Multisite to do anything. It can be installed and activated on a single-site WordPress (for example before you enable Multisite), but there it only shows a notice on the Plugins screen and has no settings. After converting the site to a network, network-activate it from the Network Admin; the one-time network setup runs automatically.<\/li>\n<li>Translated content is saved through WordPress's normal sanitization; the plugin never disables it. Translations are saved by a background task, as the source post's author when that author and the user who last saved the source may post unfiltered HTML (on multisite: Super Admins) and the translation adds no code the source lacks. The copy then keeps the source's <code>&lt;style&gt;<\/code>, <code>&lt;script&gt;<\/code> and form fields. Otherwise WordPress removes that code from the copy, as it would for the author's own save, and the translation log says why. Theme templates and template parts copied by Design Sync are always saved with WordPress's filter.<\/li>\n<li>\"Sync Design &amp; Templates\" does not copy Additional CSS; each site keeps its own.<\/li>\n<li>Translation is asynchronous: after publishing, allow a few seconds (or up to a WP-Cron cycle on a low-traffic site) for the destination copies to appear.<\/li>\n<li>Trashing, permanently deleting, or restoring a post on the source site does the same on every destination site's translated copy.<\/li>\n<li>Adding a new destination site automatically switches that new site's theme to match the source site as part of its one-time setup (it also copies the design and starts translating existing content to it) \u2014 this only happens for a brand-new site being added, never for a site already in production; running \"Sync Design &amp; Templates\" manually against an existing destination will switch its theme too, and asks for confirmation first.<\/li>\n<li>A translation that fails is retried up to twice with a growing delay; after 10 total failures for a given post\/destination pair, the plugin stops retrying it (visible as \"abandoned\" in the Status tab with the last error, with a manual retry button). An abandoned post is not sent again, even by Translate All Posts, until the source post is edited.<\/li>\n<li>When the translation service itself fails (invalid API key, no credit left, unknown model, or 5 failures in a row where the service does not answer), every translation pauses at once, the network administrator gets one e-mail, and the settings page shows the error. Posts wait without any API call. Saving the API configuration, or clicking \"Resume translations\", queues them again. These failures do not count towards abandoning a post.<\/li>\n<li>The Status tab refreshes its badges and status cells every 20 seconds, only while the browser tab is visible.<\/li>\n<li>No custom database tables are created (except a translation-memory cache table, used to skip re-translating a block whose source content hasn't changed). Uninstalling the plugin removes it along with all <code>elmat_*<\/code> site options and the translation-linking postmeta.<\/li>\n<\/ul>\n\n<h3>External services<\/h3>\n\n<p>This plugin sends content to your chosen translation provider's API in order to translate it \u2014 this is the plugin's core function and cannot be disabled while automatic or manual translation is used. Only the provider you've selected in Settings is ever contacted.<\/p>\n\n<p><strong>OpenAI<\/strong> (<code>api.openai.com<\/code>) \u2014 used when Translation Provider is set to OpenAI. What is sent: the post title, the text content of each Gutenberg block (shortcodes and <code>core\/html<\/code> blocks are excluded), the names of categories\/tags\/taxonomies, and (if set) your \"Additional Instructions\" text. Terms of use: https:\/\/openai.com\/policies\/terms-of-use \u2014 Privacy policy: https:\/\/openai.com\/policies\/privacy-policy<\/p>\n\n<p><strong>OpenAI Batch API<\/strong> (<code>api.openai.com<\/code>, Files and Batches endpoints) \u2014 used only when \"Use OpenAI's Batch API\" is checked. To list the models in the settings, the API key is also sent to OpenAI's model list (<code>GET \/v1\/models<\/code>, no content) at most every 12 hours. The same texts as above are uploaded to OpenAI as a batch file (Files API, purpose \"batch\"), OpenAI translates them within 24 hours, and the plugin downloads the answers and then deletes the batch file, the answer file and any error file from your OpenAI account. Same terms of use and privacy policy as OpenAI above.<\/p>\n\n<p><strong>DeepL<\/strong> (<code>api.deepl.com<\/code> \/ <code>api-free.deepl.com<\/code>) \u2014 used when Translation Provider is set to DeepL. What is sent: the same title\/content\/taxonomy names as above (Additional Instructions does not apply to DeepL). Terms of use: https:\/\/www.deepl.com\/en\/pro-license \u2014 Privacy policy: https:\/\/www.deepl.com\/en\/privacy\/<\/p>\n\n<p><strong>WordPress AI Client<\/strong> (WordPress 7.0+ only) \u2014 used when Translation Provider is set to \"WordPress AI Client\". The same title\/content\/taxonomy names as above are sent, but to whichever external AI provider your site has configured under Settings \u2192 Connectors, not to this plugin or its developer \u2014 this plugin never sees that provider's identity, endpoint, or API key, only the result. Check your configured Connectors provider's own terms of use and privacy policy.<\/p>\n\n<p>Either way, this happens every time a post is published or updated on the configured source site, and whenever \"Bulk Translate All Posts\" or a manual per-post translation is triggered. No personal visitor data, and no data from any site other than the post being translated, is sent. Using OpenAI or DeepL requires your own API key for that provider (entered in the plugin's settings); using the WordPress AI Client instead defers entirely to whatever provider and credentials your site's Connectors screen has configured. Either way this is billed to your own account directly \u2014 the plugin developer has no access to your key, your account, or the content sent.<\/p>\n\n<!--section=installation-->\n<h4>Automatic Installation<\/h4>\n\n<ol>\n<li>Log in to your Network Admin.<\/li>\n<li>Navigate to Plugins \u2192 Add New.<\/li>\n<li>Search for \"EffortLess Multisite Auto Translate\".<\/li>\n<li>Click \"Install Now\", then <strong>Network Activate<\/strong> (not a per-site Activate) from the Network Admin's Plugins page.<\/li>\n<\/ol>\n\n<h4>Manual Installation<\/h4>\n\n<ol>\n<li>Download the plugin zip file.<\/li>\n<li>Upload it to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Extract the zip file.<\/li>\n<li>Network Activate the plugin from the Network Admin \u2192 Plugins page.<\/li>\n<\/ol>\n\n<h4>Configuration<\/h4>\n\n<ol>\n<li>Go to Network Admin \u2192 EffortLess Multisite Auto Translate.<\/li>\n<li>Enter your OpenAI API key and click \"Test API Key\" to confirm it works.<\/li>\n<li>Choose your Source Site and one or more Destination Sites.<\/li>\n<li>Leave \"Enable automatic translations\" checked (or uncheck it if you only want to translate manually per-post from the editor).<\/li>\n<li>Save.<\/li>\n<li>Publish or edit a post on the Source Site \u2014 its translation appears on each Destination Site within a few seconds to one WP-Cron cycle.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20an%20openai%20api%20key%3F\"><h3>Do I need an OpenAI API key?<\/h3><\/dt>\n<dd><p>Yes, an active OpenAI API key with available credit. Create one at platform.openai.com\/api-keys. The plugin does not work without one.<\/p><\/dd>\n<dt id=\"how%20much%20does%20translation%20cost%3F\"><h3>How much does translation cost?<\/h3><\/dt>\n<dd><p>Cost is billed by OpenAI directly, not by this plugin, and depends on your chosen model and the length of your content. Check OpenAI's own current pricing at openai.com\/api\/pricing before enabling automatic translation on a large site.<\/p>\n\n<p>What keeps it down: every translated sentence and block is kept in the translation memory, so a post saved again, a retry, or Translate All Posts only sends what changed. Editing a glossary term forgets only the saved translations that contain that term. Changing the model, the translation style or the Additional Instructions forgets them all, so the next run translates everything again. \"Force Re-translate All\" skips the memory on purpose and is the most expensive action. \"Use OpenAI's Batch API\" halves the price of automatic translations, which then take up to 24 hours. To cap spending whatever happens, set a monthly budget on the project of your API key in your OpenAI account.<\/p><\/dd>\n<dt id=\"will%20shortcodes%20be%20translated%3F\"><h3>Will shortcodes be translated?<\/h3><\/dt>\n<dd><p>No. Shortcodes are preserved exactly as written, including their attributes and values \u2014 only the surrounding text is translated.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20gutenberg%3F\"><h3>Does it work with Gutenberg?<\/h3><\/dt>\n<dd><p>Yes, translation is done block by block so structure and attributes are preserved. Classic-editor (non-block) content is translated as a single unit.<\/p><\/dd>\n<dt id=\"can%20i%20translate%20existing%20content%3F\"><h3>Can I translate existing content?<\/h3><\/dt>\n<dd><p>Yes \u2014 use \"Bulk Translate All Posts\" under Tools to translate everything already published on the source site.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20a%20translation%20fails%3F\"><h3>What happens if a translation fails?<\/h3><\/dt>\n<dd><p>It's retried automatically up to twice with an increasing delay. After 10 total failures for the same post on the same destination, the plugin stops retrying and marks it \"abandoned\" in the Status tab; hover the cell to see the last error. It is tried again when the source post is edited and saved, with \"Translate now\" in the editor or on its row of the Status tab, or with Status \u2192 Retry \u2192 Abandoned. A rate limit or a service that does not answer is waited for instead: retried after 1 and 3 minutes, then every 10 minutes (or when OpenAI says the limit resets), without counting as a failure; the Status tab shows the next try, and the API tab shows your OpenAI limits. If the error comes from the translation service (key, credit, model, or 5 failures in a row), translations are paused instead and resume when you save the API configuration or click \"Resume translations\".<\/p><\/dd>\n<dt id=\"how%20do%20i%20stop%20all%20translations%20immediately%3F\"><h3>How do I stop all translations immediately?<\/h3><\/dt>\n<dd><p>Click \"Emergency Stop\" under Tools. This cancels every pending translation job.<\/p><\/dd>\n<dt id=\"translated%20posts%20are%20showing%20404%20errors%20%E2%80%94%20what%20do%20i%20do%3F\"><h3>Translated posts are showing 404 errors \u2014 what do I do?<\/h3><\/dt>\n<dd><p>Click \"Flush Permalinks\" under Tools to regenerate rewrite rules on every site in the network.<\/p><\/dd>\n<dt id=\"can%20i%20disable%20automatic%20translation%20temporarily%20without%20deactivating%20the%20plugin%3F\"><h3>Can I disable automatic translation temporarily without deactivating the plugin?<\/h3><\/dt>\n<dd><p>Yes \u2014 uncheck \"Enable automatic translations\" in Settings. Manual translation from the block editor's EffortLess Multisite Auto Translate panel still works.<\/p><\/dd>\n<dt id=\"does%20it%20translate%20categories%20and%20tags%3F\"><h3>Does it translate categories and tags?<\/h3><\/dt>\n<dd><p>Yes, category and tag names (and other public taxonomies) are translated and kept in sync across sites.<\/p><\/dd>\n<dt id=\"what%20happens%20when%20i%20delete%20a%20post%20on%20the%20source%20site%3F\"><h3>What happens when I delete a post on the source site?<\/h3><\/dt>\n<dd><p>Its translated copies on every destination site are deleted (or trashed\/restored, matching whatever action was taken on the source).<\/p><\/dd>\n<dt id=\"can%20i%20choose%20which%20post%20types%20are%20translated%3F\"><h3>Can I choose which post types are translated?<\/h3><\/dt>\n<dd><p>Yes \u2014 untick any post type in the \"Post Types to Translate\" setting. This stops it being translated both automatically and via Bulk Translate; existing translations of an unticked type are left in place, they simply stop being updated.<\/p><\/dd>\n<dt id=\"are%20other%20plugins%27%20settings%20copied%20to%20the%20language%20sites%3F\"><h3>Are other plugins' settings copied to the language sites?<\/h3><\/dt>\n<dd><p>Sync Design copies WordPress's own reading, permalink and media settings. Other plugins' settings stay per site, so a new language site starts from that plugin's defaults. A plugin can list its text settings with the elmat_translatable_options filter (translated into each language) and its other settings with the elmat_synced_options filter (copied unchanged, for example an on\/off switch or a delay). Site addresses, e-mail, active plugins, theme, user roles and this plugin's own settings are never copied.<\/p><\/dd>\n<dt id=\"can%20i%20install%20it%20on%20a%20normal%20%28single-site%29%20wordpress%3F\"><h3>Can I install it on a normal (single-site) WordPress?<\/h3><\/dt>\n<dd><p>Yes, it installs and activates without errors, but it does nothing there and has no settings page \u2014 it needs a Multisite network. It shows a \"Requires Multisite\" link and a notice on the Plugins screen. Once you enable Multisite (see WordPress's \"Create a Network\" guide), network-activate the plugin from the Network Admin and the settings appear under Network Admin \u2192 EffortLess Multisite Auto Translate.<\/p><\/dd>\n<dt id=\"can%20i%20use%20deepl%20instead%20of%20openai%3F\"><h3>Can I use DeepL instead of OpenAI?<\/h3><\/dt>\n<dd><p>Yes \u2014 set \"Translation Provider\" to DeepL and enter a DeepL API key. DeepL is a pure translation engine rather than a prompt-driven model, so the \"Additional Instructions\" field has no effect when DeepL is selected (Translation Style, Glossary, and translation memory all still work the same way).<\/p><\/dd>\n<dt id=\"can%20i%20use%20the%20built-in%20wordpress%20ai%20client%20instead%20of%20entering%20my%20own%20api%20key%3F\"><h3>Can I use the built-in WordPress AI Client instead of entering my own API key?<\/h3><\/dt>\n<dd><p>On WordPress 7.0 and later, yes \u2014 set \"Translation Provider\" to \"WordPress AI Client\". No key field appears for it; it uses whatever AI provider your site already has configured under Settings \u2192 Connectors. This option is hidden entirely on WordPress versions before 7.0, since the AI Client doesn't exist yet there. Use \"Test Connection\" after saving to confirm it actually works.<\/p><\/dd>\n<dt id=\"how%20do%20i%20monitor%20what%20the%20plugin%20is%20doing%3F\"><h3>How do I monitor what the plugin is doing?<\/h3><\/dt>\n<dd><p>The Logs tab shows recent activity and auto-refreshes every 5 seconds. A local log file is also written to a folder of the uploads directory named <code>elmat-logs-<\/code> followed by random letters, which an .htaccess file closes to visitors on Apache (on nginx, the random name keeps it from being found). The API configuration tab also shows a rough character-usage total (this month + lifetime) once translation has run at least once.<\/p><\/dd>\n<dt id=\"i%20configured%20everything%20and%20nothing%20is%20being%20translated%20%E2%80%94%20what%20should%20i%20check%3F\"><h3>I configured everything and nothing is being translated \u2014 what should I check?<\/h3><\/dt>\n<dd><p>Confirm the plugin is <strong>Network Activated<\/strong>, not activated on an individual site (it does nothing per-site). Confirm \"Enable automatic translations\" is checked, that the post type isn't unticked in \"Post Types to Translate\", and that the post you're testing with was actually saved <em>after<\/em> configuration was completed \u2014 the plugin only reacts to a <code>save_post<\/code> event on the source site going forward, it doesn't retroactively pick up older posts (use Bulk Translate for those). Also confirm your provider's API key still has available credit \u2014 a billing failure surfaces as a translation failure in the Logs tab, not as a plugin error.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<p>Only the most recent versions are listed here to stay within WordPress.org's changelog length limit; see the plugin's own repository for the full history.<\/p>\n\n<h4>3.3.57<\/h4>\n\n<ul>\n<li>Fix: the hourly queue cleanup deleted every translation more than an hour late. After Translate All Posts or a retry, the posts waiting their turn behind long translations were removed and their copies stayed outdated with nothing queued. Only events whose source site or post was deleted are removed now.<\/li>\n<li>Fix: after a save that changed only post meta, the copies' AIOSEO Open Graph and X fields, SEOPress social fields and Slim SEO title and description were put back in the source language. Both paths now translate the same SEO fields, and this path also honours the elmat_excluded_meta_keys filter.<\/li>\n<li>Fix: Status \u2192 Retry \u2192 Missing (and All Problems) queued post types that are never translated, such as Additional CSS, Customizer drafts and the post types excluded in Settings. It now takes only the translated post types.<\/li>\n<li>Fix: when saving a translated copy failed, the failure was only logged: no retry and no error on the Status tab. It is now handled like any failed translation.<\/li>\n<li>Fix: when OpenAI refuses a request setting for the chosen model (HTTP 400, unsupported parameter), translations pause with a message naming the model, instead of every post failing in turn until it is abandoned.<\/li>\n<li>Fix: a navigation link to a category or tag now points to the translated term on each language site; it kept the source site's address when the translated term had a translated slug.<\/li>\n<li>Fix: escaped characters in a block's settings (a quote written \\u0022 by WordPress) were decoded in the translation, which broke the settings and left the block untranslated. They are now kept; JSON answers are decoded before their texts are cleaned.<\/li>\n<li>Fix: several translations running at the same time are each shown as \"translating now\" on the Status tab; one finishing no longer hides the other.<\/li>\n<li>Fix: retries, Translate now and bulk actions now release the queue lock on the source site, where it is kept.<\/li>\n<li>Change: a failed check of the models your API key may use is remembered for 10 minutes, so settings pages no longer wait up to 15 seconds each time while OpenAI cannot be reached.<\/li>\n<li>Change: Sync Design and the editor's block status use the same language code as translations (en_US for an English site, the elmat_target_language filter applied).<\/li>\n<li>Change: the Translate now REST route accepts only posts of the source site.<\/li>\n<\/ul>\n\n<h4>3.3.56<\/h4>\n\n<ul>\n<li>Fix: translations run one after another, and a big page takes minutes, so posts queued behind it showed \"queued \u00b7 due 2 minutes ago \u2014 scheduled tasks not running?\" although nothing was wrong. The post being translated now shows \"\u2699\ufe0f translating now\", the posts behind it \"waiting for the translation in progress\", and the warning appears only when a post is 5 minutes late with nothing running.<\/li>\n<li>Fix: a translation run asks PHP for up to 10 minutes (set_time_limit), as hosts often stop a request after 30 or 60 seconds and cut a long translation off halfway. When a run is still stopped by the server, the next one writes it in the log.<\/li>\n<\/ul>\n\n<h4>3.3.55<\/h4>\n\n<ul>\n<li>Fix: a Query Loop filtered by category or tag showed no posts on the destination sites. The source term IDs were never replaced by the destination's: the maps from source to destination term IDs were combined with array_merge(), which renumbers numeric keys, so every lookup missed (Latest Posts filters were affected too). In addition the current taxQuery format, <code>{\"include\":{\"category\":[8]},\"exclude\":{...}}<\/code>, was not read at all: its category was not translated and the filter was rewritten as <code>{\"include\":[1]}<\/code>. Both formats are now read and remapped, include and exclude lists alike. Pages translated before need \"translate now\" once (their content did not change, so an ordinary run only updates their settings).<\/li>\n<\/ul>\n\n<h4>3.3.54<\/h4>\n\n<ul>\n<li>Fix: the editor's \"Translation Status\" box showed \"Queued\" for every language site while the Status tab showed one retrying with its error. It now shows the same states as the Status tab for each site (abandoned, in batch, retrying, queued, outdated, translated), with the last error under a failing one.<\/li>\n<li>Change: on the Status tab the error of a failing copy is shown in full, on several lines, instead of being cut after one line.<\/li>\n<\/ul>\n\n<h4>3.3.53<\/h4>\n\n<ul>\n<li>Fix: on a subdirectory network, every column of the Status tab showed the same domain; each column now shows its site's address (for example example.com\/fr) and language, and the header shows the source site's address.<\/li>\n<li>New: a translation queued for more than 2 minutes without running shows how late it is (\"queued \u00b7 due 15 minutes ago\") and a hint that the source site's scheduled tasks may not be running.<\/li>\n<li>Fix: a translation queued for another source site (left from an earlier source setting) no longer marks the post with the same ID as queued, and a queued translation of a deleted site no longer causes database errors on the settings page or in the queue.<\/li>\n<li>Change: the Status legend explains that \"outdated\" can also mean that the last translation failed, its error shown below.<\/li>\n<\/ul>\n\n<h4>3.3.52<\/h4>\n\n<ul>\n<li>Fix: a page holding only a shortcode (for example <code>[elwhc_health_check]<\/code>) stayed \"outdated\" on the Status tab, even after \"translate now\". Its content was sent for translation; with nothing to translate, the model answered with an explanation, which was refused (the shortcode was missing), and after the retries the copy was left as it was. A text with no words to translate (only shortcodes, markup or numbers) is now kept as it is without being sent, which also saves those requests. Words people read inside markup still count: alt, title, placeholder and aria-label attributes and the text values of block settings.<\/li>\n<li>Change: an \"outdated\" copy whose last translation failed now shows that error on the Status tab, as retrying and abandoned ones do.<\/li>\n<\/ul>\n\n<h4>3.3.51<\/h4>\n\n<ul>\n<li>New: gpt-5.4-mini and gpt-5-mini can be chosen under GPT Model. The menu lists only the models your OpenAI API key may use, read from OpenAI's model list (kept 12 hours per key; read again when the key is saved and with \"Test API Key\"); a chosen model the key cannot use is shown as such.<\/li>\n<li>Change: each reasoning model is asked for its lowest documented reasoning effort (\"minimal\" for gpt-5-nano and gpt-5-mini, \"none\" for gpt-5.4-mini, \"low\" for gpt-6-luna) instead of \"low\" for all, so less reasoning is billed.<\/li>\n<\/ul>\n\n<h4>3.3.50<\/h4>\n\n<ul>\n<li>Fix: on a network whose main site is not the source site (for example a root site that only redirects visitors, with the source at \/tw\/), the Status tab still showed \"translated\" after \"translate now\", and the translation waited for the next visitor of the source site. Since 3.3.43 translations are queued on the source site, but the Status tab, the queue cleanup, the emergency stop and the deactivation read only the main site's queue. They now read both, and network admin pages (including each Status refresh) start the source site's due translations.<\/li>\n<\/ul>\n\n<h4>3.3.49<\/h4>\n\n<ul>\n<li>New: \"Use OpenAI's Batch API\" (API tab, off by default). Automatic translations send their texts to OpenAI as one batch per post, billed at half price and answered within 24 hours. The translation first runs as a rehearsal that records the requests without saving anything; when the batch ends (checked every 10 minutes), its answers fill the translation memory and the post is translated from them. The Status tab shows \"\ud83d\udce6 in batch\" meanwhile. \"translate now\" and retries are always direct; a batch that fails or expires is replaced by a direct translation; the batch files are deleted from the OpenAI account afterwards.<\/li>\n<\/ul>\n\n<h4>3.3.48<\/h4>\n\n<ul>\n<li>New: GPT-5 Nano (<code>gpt-5-nano<\/code>) can be chosen under GPT Model, the cheapest of the models offered. Like GPT-6 Luna it is a reasoning model and is sent <code>max_completion_tokens<\/code> and <code>reasoning_effort: low<\/code>.<\/li>\n<li>Change: GPT-4o is no longer offered (several times the price of the others for website text). A site already using it keeps it, shown as \"no longer offered\", until another model is chosen, so saving other settings never changes the model or clears the translation memory by itself.<\/li>\n<\/ul>\n\n<h4>3.3.47<\/h4>\n\n<ul>\n<li>New: GPT-6 Luna (<code>gpt-6-luna<\/code>) can be chosen under GPT Model. It is a reasoning model, so its requests send <code>max_completion_tokens<\/code> (with room for the reasoning) and <code>reasoning_effort: low<\/code> instead of <code>max_tokens<\/code> and a temperature; the API key test uses the same parameters. Requests to gpt-4o-mini and gpt-4o are unchanged. Like any model change, choosing it clears the translation memory.<\/li>\n<\/ul>\n\n<h4>3.3.46<\/h4>\n\n<ul>\n<li>Fewer tokens: editing the glossary no longer clears the whole translation memory. Only the saved translations that contain a term that was added, removed or changed are forgotten; every other sentence is reused. Before, one glossary change made every sentence of every site be translated (and paid for) again.<\/li>\n<li>Fewer tokens: blocks and paragraphs over 1 KB are now kept in the translation memory too (up to 32 KB), so a retry, Translate All Posts or a full re-translation no longer sends them again.<\/li>\n<li>Fewer tokens: the part of the instructions that varies with each text (the glossary terms it contains) now comes last, so the fixed part forms a longer identical beginning that OpenAI's prompt caching bills at half price.<\/li>\n<\/ul>\n\n<h4>3.3.45<\/h4>\n\n<ul>\n<li>Fix: \"Test API Key\" said \"Rate limit exceeded - Your API key works but quota is reached\" both when OpenAI's per-minute rate limit was reached (it clears by itself) and when the account had no credit left (it needs billing). The test now tells them apart, says which limit and when it resets, and quotes OpenAI's own message. It also updates the OpenAI rate limits box on the API tab.<\/li>\n<\/ul>\n\n<h4>3.3.44<\/h4>\n\n<ul>\n<li>New: when OpenAI refuses a request for going over a rate limit, the log and the Status tab say which limit (requests or tokens per minute), how much is left and when it resets, and the retry waits for that reset.<\/li>\n<li>New: a retrying translation shows its next try time on the Status tab (\"retrying (2\/2) \u00b7 next try 10:42\").<\/li>\n<li>New: the API tab shows OpenAI's latest reported limits (requests and tokens per minute, how much is left) and when a request was last refused, saved at most once a minute from OpenAI's answers.<\/li>\n<\/ul>\n\n<h4>3.3.43<\/h4>\n\n<ul>\n<li>Fix: a page that hit the translation service's rate limit three times in a row (often the largest page, during Translate All Posts) was dropped: nothing was queued any more, it was not marked abandoned, and its copies stayed untranslated while the Status tab kept showing \"retrying (2\/2)\". A busy service is now waited for: retries after 1 and 3 minutes, then every 10 minutes (or later if the service asks), until it answers. A service that fails 5 times in a row still pauses translations.<\/li>\n<li>Fix: retries were scheduled in the destination site's queue instead of the source site's. They then ran only when that site had a visitor, and each failing destination started its own run to every destination, multiplying requests while the service was already refusing them.<\/li>\n<\/ul>\n\n<h4>3.3.42<\/h4>\n\n<ul>\n<li>New: a \"translate now\" link on each source post of the Status tab, next to \"edit\" and \"view\". It queues a full translation of that post to every language site, even when it has not changed. Templates and template parts needed it most: the Site Editor does not show the editor sidebar's \"Translate now\" button.<\/li>\n<\/ul>\n\n<h4>3.3.41<\/h4>\n\n<ul>\n<li>Fix: a template that 3.3.40 found under its right copy could still show \"translated\" without the change: the earlier run had stored the source as translated while saving to a leftover copy, so the next run only updated the copy's settings. Each copy now records the source version it was translated from, and the quick \"content unchanged\" update runs only when that matches. Templates, template parts and patterns translated before this version are translated once in full on their next run.<\/li>\n<\/ul>\n\n<h4>3.3.40<\/h4>\n\n<ul>\n<li>Fix: when a language site held several copies of the same source post (a template copied for an earlier theme, a copy named \"single-post-2\", one in the trash), translations could keep updating a copy that is never shown, while the one shown stayed untranslated and \"outdated\" on the Status tab (for example a \"Single post\" template whose title was changed to H1). The copy WordPress uses is now the one updated and checked: not trashed, then for templates and template parts the one of the active theme with the template's own name, then a published one, then the newest.<\/li>\n<\/ul>\n\n<h4>3.3.39<\/h4>\n\n<ul>\n<li>New: a translated search-result title longer than 60 characters (32 in Chinese, Japanese or Korean) is sent once more to OpenAI or the WordPress AI provider, asking for a shorter version that keeps the subject, the place names and the brand. This covers the title fields of SEO plugins and the Open Graph and X titles of EffortLess Custom Meta Tags (other plugins can add theirs with the elmat_seo_title_options filter); a post's own title is never shortened. As for descriptions, the short version is kept in the translation memory, DeepL is not asked, and an answer that is longer, far too short or adds a number is not used.<\/li>\n<\/ul>\n\n<h4>3.3.38<\/h4>\n\n<ul>\n<li>Fix: every Sync Design put the source text back into translated templates, template parts and patterns, and the translation it queued then found the source unchanged and skipped them, so they stayed in the source language (for example a synced pattern with an address, a map and a \"Get directions\" link). A copy that was already translated now keeps its text; the queued translation updates it when the source changed. Once, after the update, the next Sync Design translates all templates, template parts and patterns again, mostly from the translation memory.<\/li>\n<li>Fix: the local log file was written to wp-content\/uploads\/elmat-translator.log, where any visitor could read it (post content, addresses, error messages). It is now written to a folder with a random name, closed by an .htaccess file, and the old file is deleted.<\/li>\n<li>New: embedded Google Maps (iframe embeds and maps links) show in the language of each site instead of the language saved in the source's map address. The region and the place are kept.<\/li>\n<\/ul>\n\n<h4>3.3.37<\/h4>\n\n<ul>\n<li>Fix: images inside a group, columns or gallery kept their caption and alternative text in the source language on every language site, and a gallery outside any group was sent whole to the provider. Each image's caption, alternative text and title are now translated with its markup, address and size kept as in the source.<\/li>\n<li>New: the elmat_synced_options filter lets a plugin list settings that Sync Design copies unchanged from the source site to every language site (an on\/off switch, a delay). A new language site previously kept that plugin's defaults; Effortless Carousel with Gallery 1.3.25 registers its settings. Site addresses, e-mail, active plugins, theme, user roles and this plugin's own settings are never copied.<\/li>\n<\/ul>\n\n<h4>3.3.36<\/h4>\n\n<ul>\n<li>Fix: a shortened search-result description could contain a price or a duration that the page never gave (for example \"2 hours, NT$1,200\" in a Spanish description whose source had neither), since the request asked to keep the duration, the price and the group size. It now keeps them only when the text mentions them and forbids adding facts, and a shortened text with a number that is not in the translation is not used. Descriptions shortened by 3.3.31 to 3.3.35 are asked again once.<\/li>\n<\/ul>\n\n<h4>3.3.35<\/h4>\n\n<ul>\n<li>Fix: a source post set back to draft, pending or private kept its translations published on the language sites when its content did not change, since that run only updated the copies' fields. The published copies now take the source's status, without saving their content again. When the source is published again, the copies unpublished this way are published again; a copy kept as a draft for review stays a draft.<\/li>\n<\/ul>\n\n<h4>3.3.34<\/h4>\n\n<ul>\n<li>Fix: a translation whose text attributes contain an apostrophe (common in French: \"s'il faut\u2026\") lost its CSS and JavaScript. WordPress's filter writes the apostrophe as ' where the translation wrote ', the same character, and that difference made the copy count as containing new code, so it was saved through the filter, which removes <\/li>\n<\/ul>\n\n<h4>3.3.33<\/h4>\n\n<ul>\n<li>Fix: in a translated Custom HTML block, the placeholder of a text area (<\/li>\n<\/ul>\n\n<h4>3.3.32<\/h4>\n\n<ul>\n<li>Fix: a Custom HTML block placed inside another Custom HTML block (for example code pasted with its own  lines into the block's editor) lost the inner block in every translation. A Form Creator form built this way kept its CSS and lost every field and button on the language sites, so only the captcha was shown. Each part of the outer block and each inner Custom HTML block is now translated in its place.<\/li>\n<\/ul>\n\n<h4>3.3.31<\/h4>\n\n<ul>\n<li>Fix: shortened Japanese and Korean descriptions came back a third of the length asked (30 to 44 characters instead of up to 90), without the duration, price or group size. The request now gives a length range (70 to 100% of the limit), the current length, and the details to keep. An answer under half the limit is not used: the translation is kept as it was, and the request is not repeated for that description. Descriptions shortened by 3.3.30 are asked again once.<\/li>\n<\/ul>\n\n<h4>3.3.30<\/h4>\n\n<ul>\n<li>New: a translated search-result description longer than 160 characters (90 in Chinese, Japanese or Korean) is sent once more to OpenAI or the WordPress AI provider, asking for a shorter version that keeps the place names, the activity and the price. Translations of short Chinese descriptions were often 230 to 370 characters long, and search engines cut them at about 155. The short version is kept in the translation memory, so an unchanged description is not sent again. DeepL, which only translates, is not asked; a failed or longer answer keeps the translation as it was.<\/li>\n<li>New: the same length rule applies to the description settings of EffortLess Custom Meta Tags (Description, Open Graph and X descriptions), used on the home page and translated by Sync Design. Other plugins can add their settings with the elmat_seo_description_options filter.<\/li>\n<\/ul>\n\n<h4>3.3.29<\/h4>\n\n<ul>\n<li>Fix: a translation could stay on \"retrying (1\/2)\" on the Status tab for good. When a forced or manual translation failed once and the retry found the content unchanged, the retry only updated the copy's fields and left the retry counter, the failure count and the last error behind. They are now cleared after that update too, and the Status tab shows \"retrying\" only while a retry is queued.<\/li>\n<li>Fix: a second translation run of the same post in one request (a retry, several queued posts handled together by Action Scheduler) could create a duplicate copy on a language site instead of updating the one just created, because \"no copy yet\" was remembered for the whole request.<\/li>\n<\/ul>\n\n<h4>3.3.28<\/h4>\n\n<ul>\n<li>Fix: an abandoned translation started again from zero on the next run (Translate All Posts, a sync), because the run that skipped it also deleted its failure counter. It now stays abandoned until the source post is edited, \"Translate now\" is used, or Status \u2192 Retry \u2192 Abandoned is clicked.<\/li>\n<li>Fix: the abandonment e-mail and the log said only \"Content block translation failed\". They now name the block and give the service's answer, and the Status tab shows the last error of each retrying or abandoned translation.<\/li>\n<li>New: when the translation service itself fails (invalid key, no credit left, unknown model, or 5 failures in a row with no answer), all translations pause, one e-mail is sent, and a notice on the settings page shows the error. Waiting posts make no API call; saving the API configuration or clicking \"Resume translations\" queues them again. Such failures no longer count towards abandoning a post.<\/li>\n<li>New: the Status tab updates its badges and cells every 20 seconds without reloading the page, only while the browser tab is visible.<\/li>\n<\/ul>\n\n<h4>3.3.27<\/h4>\n\n<ul>\n<li>Fix: glossary import read the second column as the default value whatever its name, so a table like \"source,fr,ko\" put the French values into the default for every language. Columns are now read by name, in any order: \"default\" (or \"all\") is the default, any other name a language (ko_KR, ko, \/ko\/ or site_ID).<\/li>\n<li>Fix: a pasted table with a single column (tabs turned into spaces when copied from a chat or a web page) was imported as whole lines of source terms; it is now refused with an explanation, and nothing is imported.<\/li>\n<\/ul>\n\n<h4>3.3.26<\/h4>\n\n<ul>\n<li>Fix: translated SEO descriptions and other multi-sentence texts could start with or contain \"<strong>ELMAT_SEG_1<\/strong>\": the rule about these internal markers (used only for Custom HTML blocks) was sent with every text, and the model sometimes added markers of its own. The rule is now sent only with texts that contain markers, and markers in a translation whose source has none are removed.<\/li>\n<li>New: SEO titles and descriptions of SEO plugins are translated within search-result length (about 60 and 155 characters), as translations from Chinese, Japanese or Korean came out several times longer than what search engines show.<\/li>\n<li>The built-in rules version changes: the translation memory is cleared once and the next translation of each post is a full one. Run Translate All Posts to correct existing translations.<\/li>\n<\/ul>\n\n<h4>3.3.25<\/h4>\n\n<ul>\n<li>New: Sync Design translates the text settings of other plugins from the source site into every destination, like the site title and tagline: options registered with the elmat_translatable_options filter (EffortLess Custom Meta Tags 1.0.22 registers its descriptions, titles and names) or listed in API configuration &gt; Extra translatable options. The destination's value is replaced at each Sync Design.<\/li>\n<li>New: the alt text of images in translated content is translated (it stayed in the source language). The built-in rules version changes, so the translation memory is cleared once and the next translation of each post is a full one; run Translate All Posts to update existing content.<\/li>\n<li>New: category, tag and other term pages get hreflang language links to their translated copies that have published posts (elmat_get_term_translation_links() for language switchers), cached like the post links.<\/li>\n<\/ul>\n\n<h4>3.3.24<\/h4>\n\n<ul>\n<li>Fix: translated posts could answer 404 on a destination site. After each translation (and in Sync Design, the front page setup, new-site setup and Flush Permalinks) ELMAT rebuilt the destination's rewrite rules while switched there from another site, with that other site's permalink settings; when the address formats differ, the destination's own post addresses no longer matched. ELMAT now deletes the destination's stored rules and the site rebuilds them on its next visit, in its own context. The rules of every site are reset once after this update.<\/li>\n<li>Change: translated addresses are built from the translated title as before 3.3.23, emoji included; the translated posts and pages whose address 3.3.23 had shortened get their former address back once after this update (the shortened address redirects to it).<\/li>\n<\/ul>\n\n<h4>3.3.23<\/h4>\n\n<ul>\n<li>New: Site configuration &gt; \"Default language for search engines\" chooses the page of the hreflang x-default link (the source site by default), for example the English site your home page sends other visitors to.<\/li>\n<li>New: every translation keeps prices in their original currency (1200\u5143 stays 1200\u5143 instead of becoming 1200\uc6d0 or 1200\u5186). Changing the built-in rules clears the translation memory once and makes the next translation of each post a full one.<\/li>\n<li>Change: each settings tab saves only its own fields, instead of every form also sending the other tab's values.<\/li>\n<li>Performance: the translation memory remembers that its table exists instead of checking on every request and site; glossary terms are matched with patterns built once per request, and a term whose first character is not in a block is skipped at once.<\/li>\n<li>Performance: the settings page's stylesheet and script are sent once, inline, instead of as files and again inline.<\/li>\n<\/ul>\n\n<h4>3.3.22<\/h4>\n\n<ul>\n<li>Performance: the links between a post and its translations (hreflang tags, and language switchers using elmat_get_translation_links()) are cached for the whole translation group, so a page view reads one stored value instead of switching to every language site. Saving, trashing, deleting or translating a post of the group clears the cache; it also expires after 12 hours.<\/li>\n<li>Fix: hreflang tags listed translations that were not published yet; they now list published copies only, like the language switcher.<\/li>\n<li>Performance: reusing a sentence from the translation memory no longer writes to the database (the unused hit counter is no longer updated), and the translation lookup compares its values as text so MySQL does not convert every stored value.<\/li>\n<li>Fix: network setup, Flush Permalinks, the diagnostics, the settings page's site lists and uninstall now cover every site of the network, not only the first 100 (or 200).<\/li>\n<\/ul>\n\n<h4>3.3.21<\/h4>\n\n<ul>\n<li>Change: the glossary editor shows one language at a time. Choose a language (or \"All languages (default)\") at the top; the table lists each source term with its value in that language, and an empty cell says what will happen: uses the default, kept as is, or left to the AI. Languages are named (for example \ud55c\uad6d\uc5b4 \u00b7 ko_KR \u00b7 \/ko\/) instead of \"Site #3\". A search box filters the terms, each language shows how many terms are left to the AI, a \"Keep as is\" button marks brand names, and leaving with unsaved changes asks for confirmation.<\/li>\n<li>Fix: the glossary is saved as one form field. Before, each term and language was its own field, so a large glossary could exceed PHP's max_input_vars and lose the terms past that limit without notice.<\/li>\n<\/ul>\n\n<h4>3.3.20<\/h4>\n\n<ul>\n<li>Change: the Shared Media Library setting moved from the API configuration tab to the Site configuration tab, next to the source and destination sites.<\/li>\n<\/ul>\n\n<h4>3.3.19<\/h4>\n\n<ul>\n<li>Change: the line under the settings page title names the supported providers (OpenAI, DeepL, WordPress AI client) instead of \"GPT-4\".<\/li>\n<\/ul>\n\n<h4>3.3.18<\/h4>\n\n<ul>\n<li>Change: the Network Admin menu, the settings page and the block editor panel are named \"EffortLess Multisite Auto Translate\", after the plugin, instead of \"GPT Translator\": translations can also use DeepL or the WordPress AI client.<\/li>\n<\/ul>\n\n<h4>3.3.17<\/h4>\n\n<ul>\n<li>Change: the Settings tab is split into three tabs, API configuration, Site configuration and Translation glossary, each showing one section. Saving a section returns to its own tab.<\/li>\n<\/ul>\n\n<h4>3.3.16<\/h4>\n\n<ul>\n<li>New: glossary import and export (Translation glossary tab &gt; Import \/ export). The CSV file has one row per term: source, default, then one column per destination site named by its language (for example ko_KR). Import a .csv\/.tsv file or paste cells copied from a spreadsheet; \"Add and update\" keeps the other terms, \"Replace\" keeps only the file's terms.<\/li>\n<\/ul>\n\n<h4>3.3.15<\/h4>\n\n<ul>\n<li>Fix: \"Translate All Posts\" and \"Force Re-translate\" left every post whose source had not changed as it was (only its meta was copied), so a glossary or instructions change never reached existing translations. A change of glossary, instructions, style, model or provider now makes the next translation of each post a full one, and \"Force Re-translate\" translates every post in full again without reusing the translation memory.<\/li>\n<li>Change: the glossary is given to the model as the word to use for a concept, with the sentence written naturally around it, instead of a term to insert as is.<\/li>\n<\/ul>\n\n<h4>3.3.14<\/h4>\n\n<ul>\n<li>Fix: with OpenAI (or the WordPress AI client), glossary terms were swapped for frozen placeholders and pasted back unchanged, so the sentence around them could not adapt: missing articles and prepositions (\"Location kayak\"), no plural, a lower-case word at the start of a title, and a missing space when the source language writes words together (\"kayakItin\u00e9raire\"). The glossary is now given to the model as required terminology, which it fits into the sentence. DeepL, which takes no instructions, keeps the placeholders.<\/li>\n<li>Change: the translation memory is cleared once after this update, since it holds translations made without the glossary. Run Sync Design and Translate All Posts to apply the glossary to content already translated.<\/li>\n<\/ul>","raw_excerpt":"Translates posts, pages, and custom post types across a WordPress Multisite network using OpenAI or DeepL, preserving Gutenberg blocks.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/371817","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=371817"}],"author":[{"embeddable":true,"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/domclic"}],"wp:attachment":[{"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=371817"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=371817"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=371817"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=371817"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=371817"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/fi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=371817"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}