Internationalizing WooCommerce at scale: SaaS (Weglot) vs database-heavy (WPML)
Disclosure: some links in this guide are affiliate links. If you sign up through them, I may earn a commission at no extra cost to you. How this site makes money.
WPML (and Polylang) store every translation as its own WordPress post, so each translated variable product copies the product, all of its variations and their meta. In our measurement on WooCommerce 11.2.1, one variable product with 12 variations took 13 posts, about 270 postmeta rows and 24 attribute-lookup rows, and every extra language added the same again: a 10,000-product catalog in 4 languages grows from about 2.7 million to about 10.8 million postmeta rows. Weglot keeps translations in its cloud: WordPress renders the original page, Weglot's plugin catches the HTML in an output buffer and translates it through cdn-api-weglot.com, so MySQL stays the size of one language. The price is a network call on every uncached translated page view, which makes a full-page cache in front of the translated URLs mandatory, and a word quota: Weglot counts unique words across all languages, so a large catalog can outgrow its 5-million-word top plan. Pick Weglot for performance and a lean database; pick WPML when translations must live on your server, when you need per-language prices or stock rules, or when the word count makes SaaS too expensive. Polylang is the open-source version of WPML's model.
Adding a second language to a 200-product shop is a plugin install. Adding four languages to a 10,000-product catalog is an architecture decision, because the two big approaches put the cost in different places. WPML puts it in your database: every translation is another set of rows that every query, backup and stock update has to carry. Weglot puts it on the network: your database stays the same size, but every translated page has to be translated somewhere before a visitor sees it. This guide measures both costs and shows how to keep each one under control.
We read the source code of the current wordpress.org releases: Weglot 6.4.3, WooCommerce Multilingual (WCML, WPML's WooCommerce add-on) 5.6.3, Polylang 3.9.0 and Linguise 2.2.70. We measured database growth on a real WordPress 7.1.3 and WooCommerce 11.2.1 site by creating variable products and copying each one per language with WooCommerce's own duplicator. That copies the posts and meta the way WPML's model does, but it's a lower bound: WPML also adds its own translation-link rows, translated terms and sync meta. We didn't run WPML's core plugin (it's paid and not on wordpress.org) or a live Weglot account. Prices are from the vendors' pricing pages, checked in October 2026.
1. The short answer
| Weglot (SaaS) | WPML + WCML (database) | Polylang (database, open source) | |
|---|---|---|---|
| Where translations live | Weglot's cloud | Your MySQL: one post per language | Your MySQL: one post per language |
| Database growth per language | Almost none | Roughly a full copy of the catalog | Roughly a full copy of the catalog |
| Work per uncached translated page | Render the original, then an API call to Weglot | Render from translated posts, plus language filters on queries | Same as WPML |
| If the vendor is down | Original-language HTML, marked uncacheable | Nothing changes | Nothing changes |
| Cost driver | Unique words across all languages, and languages per plan | Yearly licence; AI translation credits | Free core; WooCommerce needs a paid add-on |
| Best for | Lean database, fast launch, a page cache you control | Data on your server, per-language prices and stock, huge word counts | Same as WPML on a smaller budget |
2. Two ways to serve /fr/
3. What one-post-per-language does to MySQL
WPML's WooCommerce add-on creates each translated variation with wp_insert_post, links it to the original through WPML's translation table, and records _wcml_duplicate_of_variation in its meta (that's from WCML 5.6.3's variation sync code). Polylang works the same way. So a translated catalog is, row for row, another catalog. We measured how big that is:
| Table | Rows per variable product (12 variations), one language | Rows added per extra language |
|---|---|---|
wp_posts | 13 | 13 |
wp_postmeta | 270 | 269 |
wp_wc_product_meta_lookup | 13 | 13 |
wp_wc_product_attributes_lookup | 24 | 24 |
wp_term_relationships | 9 | 10 |
Measured on WooCommerce 11.2.1 with global attributes (colour and size), managed stock and prices on each variation; numbers were the same for 2 and 100 products. Scaled to 10,000 such products:
Row count alone doesn't make a site slow; MySQL handles tens of millions of indexed rows. What hurts is everything that touches every row:
- Writes multiply. WCML keeps stock in sync across translations, so a sale updates the stock of every language's copy. Product imports and price updates do the same work once per language.
- Backups, staging copies and migrations grow with the database. A 4-language catalog roughly quadruples the product part of every dump and restore.
- Meta queries scan more. Admin product lists, layered-nav filters (the attribute lookup table) and plugins that query postmeta without good indexes work over four times the rows.
- Every query gets a language filter. WPML joins its translation table to keep French products off English pages. That's cheap with an object cache and warm buffers, and noticeable without them.
To see where your own WPML store stands, count translated rows per language:
-- Products and variations per language in a WPML store (adjust the wp_ prefix)
SELECT t.language_code,
SUM(t.element_type = 'post_product') AS products,
SUM(t.element_type = 'post_product_variation') AS variations
FROM wp_icl_translations t
WHERE t.element_type IN ('post_product', 'post_product_variation')
GROUP BY t.language_code;
-- And how much meta those rows carry
SELECT t.language_code, COUNT(*) AS postmeta_rows
FROM wp_icl_translations t
JOIN wp_postmeta m ON m.post_id = t.element_id
WHERE t.element_type IN ('post_product', 'post_product_variation')
GROUP BY t.language_code;
4. What SaaS does to every request
Weglot's WordPress plugin doesn't store translations in WordPress. Reading its source (6.4.3) shows the path:
- Output buffer. On a translated URL, the plugin starts
ob_start()and lets WordPress render the original page as usual. - One API call per uncached view. The parser extracts the text, skips excluded blocks, and posts it to
cdn-api-weglot.com/translatein chunks of up to 600 words. Weglot's servers return the stored translation (or translate new strings). The plugin has no local translation cache: without a page cache, every French page view is a round trip. - Timeouts are generous. The bundled HTTP client defaults to an 80-second timeout and a 30-second connect timeout, and the plugin doesn't lower them. A slow API means slow pages and busy PHP workers.
- Failures fail open. On an API error the plugin returns the original-language HTML on the translated URL, defines
DONOTCACHEPAGEand sends no-cache headers, so the untranslated page isn't cached under the French URL. Visitors see English until the API recovers. - Billing follows views. Weglot plans include a monthly number of translation requests that Weglot says roughly equals translated page views. Your cache hit ratio is your bill.
So the database stays small, but uncached translated pages cost a network round trip and a little quota each. At scale, Weglot only works well with a full-page cache in front of the translated URLs. Linguise, the other SaaS covered in our multilingual SEO guide, does keep a local file cache, but it's keyed on the rendered original HTML, so WordPress still renders the page every time.
5. Caching translated pages
Weglot serves languages in subdirectories (/fr/, /de/), which is the friendliest layout for caches: each language is a different URL, so it gets its own cache entry with no extra rules. Three things still go wrong:
- Don't vary on
Accept-Languageor cookies. The language is in the path. Varying on headers splits the cache into hundreds of copies and drops the hit rate, which with Weglot means more API calls. - Browser-language redirects aren't cacheable. If you enable Weglot's automatic redirect, the redirect on the original URL depends on the visitor's browser. Use the JavaScript variant (
weglot_autoredirect_js) or exclude that response from the cache, or a French visitor's redirect will be served to everyone. - Purge every language. Most cache plugins purge the URL of the product you edited, which is the English one. The French copy keeps the old price. Purge all of a product's URLs when it changes:
<?php
/**
* Plugin Name: Purge translated product URLs
* When a product changes, purge its URL in every Weglot language, not just the original.
* Replace store_purge() with your cache's purge call; the PURGE request below suits Varnish.
*/
function store_purge( $url ) {
wp_remote_request( $url, [ 'method' => 'PURGE', 'blocking' => false, 'timeout' => 2 ] );
}
add_action( 'woocommerce_update_product', function ( $product_id ) {
$url = get_permalink( $product_id );
if ( ! $url ) {
return;
}
if ( ! function_exists( 'weglot_create_url_object' ) ) {
store_purge( $url );
return;
}
// getAllUrls() is what Weglot uses to print hreflang: one entry per language, translated slugs included.
foreach ( weglot_create_url_object( $url )->getAllUrls() as $alt ) {
if ( empty( $alt['excluded'] ) ) {
store_purge( $alt['url'] );
}
}
} );
Stock changes from orders don't fire woocommerce_update_product. If you show stock levels on cached product pages, hook woocommerce_product_set_stock and woocommerce_variation_set_stock too, or keep stock out of the cached HTML and load it with JavaScript. Our WooCommerce caching guide covers the cache side in depth.
6. Hreflang in each model
Both plugins print the <link rel="alternate" hreflang> set for you. From Weglot's source, two behaviours are worth knowing:
- It drops the hreflang set on non-canonical pages. If a page's canonical points elsewhere (a filtered or paginated URL), Weglot removes its alternates, which is correct: hreflang belongs on canonical URLs only.
- It leaves out noindexed languages. A comment in the code explains why: an hreflang pointing at a noindex page is a contradictory signal and can invalidate the whole cluster.
With WPML the alternates come from the translated posts, so a product that isn't translated into one language simply has no alternate for it. The classic WPML problem is the other way round: an SEO plugin printing a canonical that points every language to the original URL, which cancels hreflang. Whichever you choose, crawl a sample of product clusters after launch. The multilingual SEO guide has a script that checks return links, canonicals, status codes and noindex across each cluster.
7. The word bill at 10,000 products
SaaS translation is priced by words, and catalogs are wordy. Weglot's plan limit is "total unique words translated across all your languages", and it doesn't renew: it's the size of what's stored.
| Weglot plan | Words | Languages | Per year |
|---|---|---|---|
| Business | 50,000 | 3 | €290 |
| Pro | 200,000 | 5 | €790 |
| Advanced | 1,000,000 | 10 | €2,990 |
| Extended | 5,000,000 | 20 | €6,990 |
| Enterprise | Custom | Custom | On request |
A catalog of 10,000 products at 120 unique words each is 1.2 million words per language. Four target languages make 4.8 million: the top of the Extended plan. Product descriptions that share boilerplate (care instructions, shipping notes) count once, which helps, but a catalog with long unique descriptions can outgrow every published plan. WPML's model has no word quota: the Multilingual CMS plan is €99 a year (WooCommerce support included, with 22,500 AI-translation words, topped up at renewal), and more automatic translation is bought as credits. You can also translate with your own team or agency at no per-word platform fee.
Measure before you choose: the multilingual SEO guide includes a script that counts your catalog's total and unique words through the WooCommerce REST API and names the smallest plan that fits.
8. When each one wins
- Weglot when you want the catalog database to stay one language, you can put a page cache in front of every translated URL, and your unique word count fits a plan. Launching is fast: no translated posts to create, sync or keep in step.
- WPML when translations must stay on your server (contracts, data residency, offline resilience), when languages need different prices, stock rules or products, or when the word count makes SaaS pricing painful. Budget for a stronger database and an object cache.
- Polylang when you like WPML's model but want an open-source core. The free plugin doesn't translate WooCommerce products: it shows a notice recommending the paid Polylang for WooCommerce add-on.
9. Our opinion: trade-offs
- For a catalog in the thousands, the database cost of WPML is real but manageable; the operational cost (four copies to import, sync and back up) is what teams feel. Weglot removes that work entirely.
- Weglot without a page cache is the worst of both worlds: a network call on every translated view and a request quota that follows your traffic. With a good cache it's the fastest setup to run.
- Do the word maths first. It's the one thing that can rule Weglot out for a very large catalog, and it takes ten minutes to measure.
- Don't translate private pages. Account and order-received pages have no search value and carry customer data. Keep them in the original language; the SEO guide has a mu-plugin for that.
10. Tools
Top picks
| Tool | Role | Why it fits |
|---|---|---|
| SaaS website translation | Our pick for performance: translations live in Weglot's cloud, the catalog database stays one language, and translated pages are ordinary subdirectory URLs your cache can hold. WooCommerce price and address blocks are excluded by default. | |
| Self-hosted WordPress multilingual | For stores that need translations on their own server, per-language product control or no word quota. The CMS plan includes WooCommerce support. |
Alternatives: free tools and references
We don't earn anything from these links.
- Polylang: open-source multilingual plugin with WPML's one-post-per-language model; WooCommerce product translation needs the paid add-on.
- WooCommerce Multilingual: WPML's free WooCommerce add-on (requires WPML), the source of the variation-sync details above.
- Google: localized versions of your pages: the hreflang rules both plugins implement.
11. Pitfalls
- Testing on a 50-product staging site. Both models look free there. Measure on a copy of the real catalog.
- Purging only the original URL. The translated copies keep stale prices until their cache expires.
- Caching the auto-redirect. One visitor's language becomes everyone's.
- Translating everything. SKUs, prices, addresses and account pages don't need translating; excluding them saves words and keeps customer data at home.
- Switching models later. Moving from WPML to a SaaS (or back) means re-creating every translation and every translated URL. Decide with the full catalog in mind.
WPML and Polylang turn every language into another copy of the catalog in MySQL: in our measurement about 270 postmeta rows per variable product per language, so 10,000 products in four languages means roughly 10.8 million postmeta rows. Weglot keeps MySQL at one language and translates in its cloud, at the cost of a network call on every uncached translated page and a quota of unique words. Choose Weglot for performance, put a full-page cache in front of every language and purge all of them together. Choose WPML when the data must live on your server or your word count is too big for SaaS pricing.