A shop runs three languages on a WordPress network. Someone changes the price on 400 products: the German site is updated on Tuesday, the French one on Thursday, and on Friday the English site still shows the old number. No plugin fixes that, because the duplication sits above the plugin layer.
Multisite versus one site with translated pages usually gets argued as a question of taste. The costs are specific, and they arrive in a fixed order: the URL shape first, then the amount of duplicated data, then the operations bill that somebody pays every month.
The URL shape is the expensive thing to change later
The shapes that come up in practice:
-
example.com/de/— one install, one database, language is an attribute of a post. -
de.example.com— either a site in a network or a separate install behind a proxy. -
example.de— a separate install on its own domain, with its own DNS and certificate.
Two details decide how much freedom you keep.
On a single install, home and siteurl are one row in wp_options. If you want a different host per language, you are not moving the install — you are adding routing in front of it, and everything that builds a URL has to agree with that routing: canonical tags, sitemaps, the media library, redirects after a slug change.
In a network, WP_HOME and WP_SITEURL in wp-config.php apply to every site at once, which is why each site keeps its URLs in its own options row (wp_2_options, wp_3_options) instead. Set them there. Defining the constants is a reliable way to point every site in the network at the same host and spend an afternoon wondering why.
hreflang is the other piece that has to be right from day one. It has to be reciprocal and self-referencing, which makes the set of language URLs a property of the page rather than of the template. On a network, get_bloginfo( 'language' ) gives you the site's language without extra bookkeeping. On a single install, whatever stores the translation has to answer "which languages does this page exist in" — and if the answer varies per page, the hreflang output has to be generated per page too.
Count the shared records, not the translated words
The number that settles the argument is how many items exist in two or more languages while sharing one underlying record: price, SKU, stock level, spec table, PDF.
Multisite shares the user tables, the network tables, and nothing else. Posts, post meta, options and term relationships are per site — wp_2_posts, wp_2_postmeta. WooCommerce has no cross-site product sharing, so a catalogue that must stay identical across three network sites is a sync job you now own, monitor and debug at 2am.
Measure it before deciding. With WP-CLI pointed at the network:
wp site list --field=url | while read -r url; do
n=$(wp post list --post_type=post,page,product --post_status=any --format=count --url="$url")
printf '%-45s %s\n' "$url" "$n"
done
Then the duplication ratio, using SKUs as the shared key:
for url in $(wp site list --field=url); do
f="skus-$(printf '%s' "$url" | sed -E 's#https?://##; s#[/.]#_#g').txt"
wp post list --post_type=product --format=ids --url="$url" \
| tr ' ' '\n' \
| while read -r id; do wp post meta get "$id" _sku --url="$url" 2>/dev/null; done \
| sed '/^$/d' | sort -u > "$f"
done
printf 'SKUs total (with repeats): '; cat skus-*.txt | wc -l
printf 'SKUs in more than one site: '; sort skus-*.txt | uniq -d | wc -l
If the second number is close to the first, the network is storing the same fact three times and someone has to keep those copies honest. Run the equivalent count on a single install: how many posts have a translation versus how many do not. When only a handful of marketing pages are translated, the language layer costs little either way, because there are no duplicated records to keep in step.
What you actually operate
Updates. One core update covers every site in a network. A network-activated plugin that breaks breaks all of them at once; per-site activation gives isolation and multiplies the work.
Backups and restores. A network backup is one database holding every site's tables. Restoring a single site means a table-level restore, and a partial restore can leave the shared user tables out of step with the restored content.
Staging. Cloning a single site is a file copy plus a database dump. Cloning a network means cloning every site plus the shared tables, then rewriting every site's home and siteurl — there are more of them than you expect, and the ones you miss are the ones that redirect to production.
Media. Uploads land under wp-content/uploads/sites/{id}/ and are not reused across sites without a plugin or a CDN.
Search. A single install searches across languages unless the query is filtered by language; a network gives per-site search for free and no cross-site search at all.
Roles. Users are global, capabilities are per site. Granting someone editor access on the German site takes add_user_to_blog(); they will not see the French one until somebody does the same there.
The five-edit test
Take the edits that happen weekly and count the places each one has to be touched:
- a price change on a product sold in three languages;
- the phone number in the footer;
- a legal page that exists in every language;
- a navigation item added to the main menu;
- the hero image on the homepage.
On a single site with translated posts, most of these are one edit plus translation. On a network they are one edit per site plus a reminder list somewhere. Multiply the difference by how often the edits happen, convert to hours, and you have the monthly cost of the architecture — which is the number worth comparing, more than the one-off build cost.
Where each one wins
| One site, translations next to the source | Multisite network | Separate installs | |
|---|---|---|---|
| URL shape | subdirectory or subdomain | subdomain or mapped domain | own domain per language |
| A shared price lives in | one record | one record per site | one record per install |
| Cost of changing shared data | one edit | one edit per site, or a sync job | one edit per install, or an import job |
| Blast radius of a bad update | the whole site | the whole network | one site |
| Restore granularity | whole site | table-level surgery for one site | whole site |
| Cross-language search | needs language filtering | not available | not available |
| Ops surface | one site to keep healthy | one network, N sites of content | N installs, N of everything |
| Wins when | most content shares records, one team edits | languages run by different teams, want isolation but one login | different legal entities, data residency, different stacks |
If the answer is one site
Translations then live either as separate posts joined by a language taxonomy, or as post meta hanging off the source post. Either way the same layer owns routing, hreflang, the switcher and sitemap segmentation — the parts that are easy to leave half-finished. We publish a free plugin that takes the post-data route, AumLang, so that is the shape I would test first before committing to it.
Price the way back while you are still deciding
Single to network is a content migration with URL changes and redirects. Network to single is a merge with ID collisions and a language taxonomy that has to be backfilled onto content that never had one. Both are migrations rather than config changes, and the size of the job tracks the duplication you measured at the start. Which means the audit script is doing double duty: it picks the architecture and it tells you what leaving would cost.
Top comments (0)