Benchmarks
PerfLocale’s own code time, measured on real multilingual sites — not the full page-load number, which depends on your theme, other plugins, hosting and caching too.
- About 1 ms median on the minimal reference install (2 languages, stock theme, no other plugins)
- ~7–10 ms on a full real-world WooCommerce store (3 languages, large menus, Redis) — verified by A/B against the plugin deactivated
- Large catalogs (over ~2,000 translation links) need a persistent object cache; without one, plan for a much higher floor
Summary
Two honest numbers, because they answer two different questions:
On the minimal reference install (2 languages, 20 translation groups, stock theme, no other plugins — the April 2026 lab matrix below), PerfLocale’s callback time is about 1 millisecond median, and none of the 1,440 minimal-install samples crossed 5 ms. That isolates the plugin’s own algorithms from everything else.
On a full real-world site (June 2026 re-measurement: a WooCommerce store with 3 languages, large navigation menus, SEO + addons active, Redis object cache), the plugin’s true end-to-end cost is roughly 7–10 ms per request — measured both with per-callback instrumentation and A/B wall-clock against the plugin deactivated, which also captures hook-dispatch and bootstrap costs that callback instrumentation can’t see. Every language, page type (home, posts, categories, products, cart, checkout, account) and topology (single-site, multisite subdirectory, multisite subdomain) was measured; details in the June section below.
For sites with more than roughly 2,000 translation links, a persistent object cache (Redis, Memcached, Object Cache Pro, etc.) is required. Our large-site rig (10,000+ posts, 6,800 links, 4 languages, no object cache) measures ~19 ms instrumented / ~40 ms A/B — that is the configuration to avoid, and one free Redis plugin away from fixed.
What we measured
An mu-plugin profiler wraps every callback whose class lives in the PerfLocale\ namespace and records the time spent inside. We subtract the profiler’s own overhead so the published numbers are the plugin’s actual code time, not the measurement artifact.
Not in scope: WordPress’s filter-dispatch overhead, your theme, other plugins, database, PHP boot, network. Those are still in your real TTFB — the plugin is one slice of the whole stack.
What this number is and isn’t. The figure above is wall-clock time inside PerfLocale’s filter and action callbacks — the per-request feature work. It excludes the plugin’s bootstrap (class autoloading and hook registration), which fires once before any callback runs and is amortised across the rest of the request alongside core, theme, and other plugins. Bootstrap is a fixed cost per install: it doesn’t grow when you add posts, languages, or visitors. We benchmark the callback work because that’s the part that scales with content and traffic — the part that shows up in TTFB and matters for capacity planning.
Caveat we learned the hard way (June 2026): callback instrumentation under-counts on heavy sites. When a PerfLocale callback itself fires WordPress hooks, the dispatch overhead of those nested hooks isn’t attributable, and the plugin’s bootstrap and option I/O sit outside any callback. That’s why the June section below adds an A/B wall-clock measurement (plugin active vs. deactivated) — on the Redis store the two methods agree (~9.4 vs ~9.7 ms); on the large no-cache rig the A/B truth (~40 ms) is twice the instrumented number (~19 ms). Treat instrumented numbers as a lower bound.
Lab test setup (April 2026 matrix below): a minimal multilingual site (20 translation groups, 40 linked posts, two active languages) on a stock WordPress install with no extra plugins. Numbers below are the median (typical request) and worst-observed value across 40 measured samples per cell, after 5 warm-ups.
Typical sites (under ~2,000 translation links)
This covers the vast majority of real installs — blogs, small to medium business sites, shops with a few hundred multilingual products. PerfLocale’s built-in caching keeps every translation link in memory; no object cache plugin needed.
| Site type | Cache | Median | Worst observed | Over 5 ms |
|---|---|---|---|---|
| Single-site | No object cache | ~0.6 ms | 2.0 ms | 0 / 240 |
| Single-site | Redis cold (after cache flush) | ~1.3 ms | 4.9 ms | 0 / 240 |
| Single-site | Redis hot (steady-state) | ~0.5 ms | 1.8 ms | 0 / 240 |
| Multisite (subdirectory) | No object cache | < 0.1 ms* | 1.6 ms | 0 / 240 |
| Multisite | Redis cold | ~0.1 ms | 1.4 ms | 0 / 240 |
| Multisite | Redis hot | < 0.1 ms* | 1.4 ms | 0 / 240 |
* On the multisite topology the plugin’s code time on a warm request is below our profiler’s own measurement floor — effectively unmeasurable on this hardware. The numbers above are the average of all six tested configurations per row.
Bottom line for the minimal lab rig: the median is roughly 1 millisecond, almost every request lands under 2 ms, and across 1,440 measured samples we did not record a single one over 5 ms in any cache state. On a full production stack the plugin’s share is higher — see the June 2026 real-world section below.
Real-world re-measurement (June 2026)
We re-benchmarked on four live test installs with real content — every page type (homepage, pages, posts, category and tag archives, products, product categories, shop, cart, checkout, account) in every active language, 12 samples per page after warm-up, sequentially on quiet hardware. Same mu-plugin profiler as the lab matrix, cross-checked with A/B wall-clock (median of 30 requests, plugin active vs. deactivated).
| Install | Stack | Median (instrumented) | p95 | True cost (A/B) |
|---|---|---|---|---|
| WooCommerce store (single-site) | 3 languages, 18 products, large menus, SEO + addons, Redis | ~9 ms | ~16 ms | ~10 ms |
| Multisite, subdomain (small blog) | 4 languages, WooCommerce active, no object cache | ~7 ms | ~10 ms | — |
| Multisite, subdirectory (small blog) | 4 languages, WooCommerce active, no object cache | ~7 ms | ~10 ms | — |
| Large site (single-site) | 10,594 posts, 6,800 links, 4 languages, no object cache | ~19 ms | ~24 ms | ~40 ms |
Why higher than the lab? Real stacks multiply the per-request work: more languages means more alternates per page; WooCommerce and large menus mean hundreds of permalink conversions per render; every additional plugin lengthens hook chains. None of that exists on a minimal rig. The A/B column is the most honest single number: it is simply how much faster the same page is with the plugin off.
wp-admin overhead (same store, June 2026): WordPress dashboard ~4 ms, core post/page/term list screens ~7–10 ms, PerfLocale’s own admin pages ~8–18 ms (the Strings page is the heaviest — it is rendering your string table). Admin numbers include the plugin’s admin UI itself, not just passive overhead.
This re-measurement also paid for itself: it surfaced (and we fixed) a cache-freshness bug that made large no-cache sites re-query translation links on every request, a missing batch-prime for block-theme navigation menus (classic menus were primed; FSE menus were not), and two admin hot-paths — together worth roughly a 2× improvement on the large-site rig and ~50% on core admin list screens.
Large sites (more than ~2,000 translation links)
Past a few thousand linked translations the plugin can no longer keep the full link table in memory and falls back to a per-link cache. For those sites a persistent object cache is required to stay under 5 ms:
| Site type | Cache | Median | Worst observed | Over 5 ms |
|---|---|---|---|---|
| Single-site | No object cache | ~7.8 ms | 11.1 ms | 240 / 240 |
| Single-site | Redis cold | ~4.4 ms | 8.7 ms | 46 / 240 |
| Single-site | Redis hot | ~2.2 ms | 3.9 ms | 0 / 240 |
| Multisite | No object cache | ~5.1 ms | 8.8 ms | 135 / 240 |
| Multisite | Redis cold | ~2.2 ms | 4.2 ms | 0 / 240 |
| Multisite | Redis hot | ~0.9 ms | 2.0 ms | 0 / 240 |
Recommendation for large sites: install a free Redis Cache plugin (or whatever your host provides). With Redis warm, even the largest tested configuration sits comfortably under 5 ms. Without one, plan for a higher floor.
Across the six tested configurations
We measure six combinations of the most impactful settings: URL prefix style (slug like /en/ or locale like /en-us/), whether the default language hides its prefix, whether strings translate from files or the database, and whether the language fallback chain is enabled. Numbers below are the median plugin code time with a hot Redis cache — the configuration most production sites run.
| Configuration | Prefix | Hide default | Strings | Fallbacks | Single‑site | Multisite |
|---|---|---|---|---|---|---|
baseline | slug | off | files | off | ~1.5 ms | ~0.7 ms |
locale_prefix | locale | off | files | off | ~1.5 ms | ~0.7 ms |
hide_default | slug | on | files | off | ~1.5 ms | ~0.7 ms |
db_strings | slug | off | database | off | ~1.5 ms | ~0.7 ms |
db_hide_default | slug | on | database | off | ~1.5 ms | ~0.6 ms |
fallback_chain | slug | off | files | on | ~1.5 ms | ~0.6 ms |
The configuration column makes almost no difference. Flipping URL prefix style, hiding the default-language prefix, switching strings from files to the database, or turning on the fallback chain — each costs at most a few microseconds of plugin code time. The dominant factor is your cache state (no-cache vs. cold vs. hot Redis), shown in the tables above; the per-feature column is essentially flat.
That means: pick the URL style and string mode that fit your workflow, your SEO strategy, and your migration plans. Don’t pick them for performance — PerfLocale doesn’t charge you for any of them.
Does it stay fast at scale?
We re-measured with 10,000 posts seeded across two languages on the same test sites. With a persistent object cache, internal hot-path operations stay sub-millisecond even at 10× the data volume:
| Internal hot-path operation | Mean at 10K scale | DB queries |
|---|---|---|
| Detect a post’s language (warm) | ~0 ms | 0 |
| Filtered WP_Query (20 posts in one language) | 0.07 ms | 0 |
| Batch-prime translations for 50 IDs | 0.01 ms | 0 |
| Get 200 strings from the database | ~0.05 ms | 1 |
What this doesn’t mean
- This is the overhead PerfLocale adds to a request, not the total request time. WordPress core, your theme, other plugins, the database and PHP boot all still contribute.
- Numbers from one hardware configuration don’t transfer 1:1 to another. A host with slow MySQL or missing OPcache shows worse numbers across the board — for PerfLocale and for everything else on the site.
- Production sites usually keep caches warm all day. The cold-Redis row above represents what happens immediately after a cache flush, which is rare in normal operation.
- The 5 ms target is a self-imposed bar for the plugin’s own code time. The plugin will function on any WordPress install regardless — it just may be a millisecond or two slower on a large site without an object cache.