Machine Translation
Machine translation (MT) lets you auto-translate new posts, existing posts in bulk, or untranslated strings with one click. PerfLocale supports six providers; pick the one that fits your quality + budget + compliance needs at Settings → Addons → Machine Translation.
Providers
- DeepL - best quality for European languages + Japanese. Paid (with a free tier: 500k chars/month). Supports glossaries (native + ours). EU data-residency by default. Start: deepl.com/pro.
- Google Cloud Translation - broadest language coverage (130+). Paid, metered. Google Cloud account required. Higher throughput than DeepL.
- Microsoft Azure Translator - comparable quality to Google; cheaper for high volume; requires Azure account. Custom Translator models supported.
- LibreTranslate - open-source, self-hosted. No per-character cost; you run the server. Quality is decent for EU languages, variable for non-EU. Best for privacy-sensitive or offline sites.
- External Translation Agency - generic HTTP endpoint for sending content to a human-translation service or custom workflow. You configure the URL + auth; PerfLocale POSTs the payload and expects the translated text in the immediate JSON response (
{"translation": "…"}). Asynchronous callback workflows are not implemented in core; addons can hook theperflocale/mt/agency_async_responsefilter to supply a deferred translation (for example by matching therequest_idagainst a callback received out-of-band). - WordPress AI Client (WP 7.0+) - delegates translation to whatever AI provider the host site already has configured under WordPress 7.0’s core AI Client. No separate MT API key — PerfLocale reuses the OpenAI / Anthropic / Ollama / etc. provider you already pay for. Feature-detected via
function_exists('wp_ai_client_prompt')+wp_supports_ai()(so a host disabling AI per-request via theWP_AI_SUPPORTconstant or thewp_supports_aifilter is honoured). The wrapper appliesusingTemperature/usingMaxTokens/usingProvider/usingSystemInstructionvia the WP 7.0 fluent-builder pattern and finalises with->generateText(). When no underlying AI provider is configured the builder returns aWP_Error; PerfLocale converts that to aRuntimeExceptionso the circuit breaker classifies it correctly. Custom routing is available viaperflocale/mt/wp_ai_client_resolver— see the hook docs. MT quality scoring (MtQualityScoreJob) auto-detects on the same surface.
API keys
Every paid provider requires an API key. PerfLocale resolves it from three sources in priority order — env var > wp-config.php constant > database setting. The first non-empty source wins.
- Environment variable — e.g.
PERFLOCALE_DEEPL_API_KEY. Recommended for containers, CI, and managed hosts. Highest priority. wp-config.phpconstant — same canonical name (define( 'PERFLOCALE_DEEPL_API_KEY', '…' )). Recommended when env vars aren’t available.- Settings → Addons → Machine Translation — paste into the corresponding field. Stored in the
perflocale_settingsoption. Used when neither an env var nor a constant is set.
See API Keys: Environment Variables & Constants for the full canonical-name table and examples.
Auto-translate on publish
Toggle Auto-Translate on Publish to trigger MT for every non-default language when a source post is published. PerfLocale creates a translation shell for each language and populates it via the MT provider in the background (Action Scheduler). Failures don’t block the source publish; they’re logged and retryable from the Translations page.
Bulk translate
From the Translations page, multi-select rows and choose Bulk → Translate with MT. Small batches run inline; larger ones queue as a bulk_translate background job visible under PerfLocale → Jobs. The sync/async split is governed by the bulk_translate threshold under Settings → Performance → Background Thresholds (default 25, counted as source posts × target languages). The monthly character limit and per-user hourly cap apply either way. For very large batches you can also use WP-CLI: wp perflocale translate --all --to=de --post-type=post --skip-existing.
Monthly character limit
Optional cap on MT usage per calendar month. Hit the cap → auto-translate + bulk translate pause until next month (manual translate via the editor button still works, since you’re explicitly opting in). Raises a warning at 80% on the Dashboard.
Glossary integration
Enforce consistent translation of brand names, product SKUs, and industry terms across every MT provider. Toggle Glossary Integration; manage terms at PerfLocale → Glossary. Covered in detail at Translation Glossary.
Translation Memory
PerfLocale caches every translation it produces and reuses exact / fuzzy matches before spending MT credits on the same content twice. Transparent - no configuration required. Inspect via the Translation Memory API.
Privacy
When you trigger MT, the plugin sends the post’s title + content + excerpt to the provider you chose. See the External Services section of the plugin’s readme for each provider’s ToS / Privacy links + what data is sent. For fully-local translation, use self-hosted LibreTranslate.
Reliability under provider failure
Every MT call goes through a per-provider circuit breaker. When the active provider starts returning 401 / 403 (bad key), 429 (rate limit), or 5xx (transient), the breaker trips after N failures in a sliding window and subsequent calls short-circuit instantly — no more burning 3 retries × up to 30s timeout per row when DeepL or Google is having a bad day.
The default thresholds:
- Auth errors (rotated / revoked API key) — breaker opens on the FIRST hit. No point retrying.
- Rate-limit / transient errors — breaker opens after 5 failures in 5 minutes, stays open for 5 minutes, then probes once.
Operator visibility: open breakers appear in Tools → Site Health under "PerfLocale circuit breakers" with a one-click reset link per breaker. The PHP error log also gets a single line on each transition (no spam).
Custom fallback: catch \PerfLocale\Concurrency\BreakerOpenException in your own MT call sites to route to a fallback (cached translation, translation memory, "service unavailable" notice) instead of conflating with genuine downstream errors.
try {
$translated = $mt_service->translate_text( $text, 'en', 'de' );
} catch ( \PerfLocale\Concurrency\BreakerOpenException $e ) {
// MT provider currently in cooldown — degrade gracefully.
$translated = my_translation_memory_lookup( $text ) ?? $text;
}Tune trip thresholds + cooldown via the breaker hooks. Disable the whole subsystem (for debugging) via add_filter( 'perflocale/breaker/disabled', '__return_true' ).