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 the perflocale/mt/agency_async_response filter to supply a deferred translation (for example by matching the request_id against 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 the WP_AI_SUPPORT constant or the wp_supports_ai filter is honoured). The wrapper applies usingTemperature / usingMaxTokens / usingProvider / usingSystemInstruction via the WP 7.0 fluent-builder pattern and finalises with ->generateText(). When no underlying AI provider is configured the builder returns a WP_Error; PerfLocale converts that to a RuntimeException so the circuit breaker classifies it correctly. Custom routing is available via perflocale/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.

  1. Environment variable — e.g. PERFLOCALE_DEEPL_API_KEY. Recommended for containers, CI, and managed hosts. Highest priority.
  2. wp-config.php constant — same canonical name (define( 'PERFLOCALE_DEEPL_API_KEY', '…' )). Recommended when env vars aren’t available.
  3. Settings → Addons → Machine Translation — paste into the corresponding field. Stored in the perflocale_settings option. 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' ).

← Back to Docs