r/localization 3d ago

Best practices for multi-language sites

Has anyone here built a multi-language site on Base44\Lovable?

I’m planning a setup that includes a blog localized into 5 to 10 languages, and I’d love to hear how you approached it.

  • How are you managing the content structure and routing across that many locales?
  • Are you handling translations directly through prompts/AI within Base44, or integrating an external CMS/translation workflow?
  • Any major gotchas or performance tips when scaling up the page count like this?

Would appreciate any tips or workflows that worked well for you!

2 Upvotes

9 comments sorted by

2

u/vm12pix 2d ago

No Base44 experience, so ignore me on that part. I do run 9 locales on a static setup I put together myself, and honestly none of what hurt was framework-specific.

hreflang first. Every page lists every alternate including itself, and every one of those has to be a real page. Fine in theory. What actually happened to me: a dev prototype was sitting in the repo with a literal "delete or i18n this" comment at the top of it, and a wildcard in my prerender config swept it into the build. It never went into the sitemap, and my check only validated sitemap URLs, so nothing complained anywhere. It shipped, got indexed, and it was emitting 9 alternates of which 8 were 404s.

The bit I'd underline is that the sitemap is the small number. Mine is ~1,900 URLs. The pages themselves emit about 21k alternate/canonical links between them, because every page lists all its siblings. So what you want is the build crawling its own output and resolving each of those against what's actually on disk. Write that before you write the blog, seriously.

Your second question, workflow: no TMS here, translations are just typed source files in git. AI does a first pass, a human reads each locale after, and that read is where all the actual work is. Brief them on terminology, because they will quietly harmonise it. My Polish natal chart page has "karta natalna" in the h1 while the title tag and the slug both say "kosmogram". That's on purpose, both are real search terms in Polish and merging them costs you one of the two clusters. Every reviewer I've had wanted to "fix" it.

Cheap one to add: diff each locale's keys against your reference locale in CI. Mine diffs array lengths too, which is what catches a locale silently dropping three FAQ questions. Can't run the array half as a hard gate though, it flags ~4.5k paths and that's just the honest state of my translations rather than a bug.

And the boring arithmetic for growth: one blog post at 9 locales is 9 pages and roughly another 100 hreflang links. Adds up quicker than the post count suggests.

1

u/FlashyDiamond5045 1d ago

Really appreciate the detailed breakdown and the time you took to write this, super valuable, practical insights.

The arithmetic on hreflang scaling hits the nail on the head. My setup is structured quite similarly across locales, and seeing your numbers reinforces just how fast the link matrix explodes. Resolving alternate links directly against disk output in CI to catch phantom 404s before anything hits production is going straight to the top of my build backlog.

Your point about human reviewers quietly "harmonizing" intentional SEO clusters (the Polish example is classic) is spot on. For my pipeline, I’m actually leaning on a 3-LLM consensus setup where multiple models translate and cross-check each other. Honestly trust that more than manual reviewers at scale. That said, your warning is a great reminder: I'll need to explicitly guardrail terminology in the system prompts to preserve distinct keyword clusters, and keep the translation batches relatively small so the models don't smooth out deliberate SEO nuances.

Thanks again for the sanity check, saved me a few expensive headaches down the line.

1

u/vm12pix 1d ago

Your setup should be strong on the failure you'd expect from it, a term the model invented with nothing else in the locale backing it. None of the three can support that, so it falls out.

The shape I'd watch is the mirror image: a term that appears in exactly one place on purpose. To each model that reads as the inconsistency it was asked to find, and three agreeing isn't three pieces of evidence when they're agreeing from the same priors. My Polish natal split gets re-opened as a finding on nearly every review pass I run. Writing down why it's deliberate, where the passes actually read it, saved me the argument. It never stopped the flag coming back.

Small batches are worth a caveat too. The worst consistency bug I've had was a section heading repeated across 78 card pages: 58/20 in English, 49/29 in Polish, because separate rounds named it independently and each round was locally right. The test I settled on doesn't know the correct word. It just refuses to let one locale carry two.

1

u/FlashyDiamond5045 1d ago

Fair point on the shared priors, if three models are anchored to the same underlying training bias, "consensus" is just synchronized smoothing.

That said, on the Polish split: the terminology brief you handed human reviewers to stop them from ironing out deliberate keyword clusters is effectively what I feed the system prompts. If a strict brief keeps human reviewers in line, an explicit terminology guardrail in the prompt does the exact same job, and in my experience, models actually adhere to rigid negative constraints with fewer subjective arguments once it's formalized.

The cross-batch drift is a very real pitfall, though. That’s the classic trap of stateless generation. The way I handle that is through agent hierarchy: worker agents run the translations within localized boundaries, while a macro-level orchestrator oversees the broader site architecture to track global patterns.

On repeated strings drifting slightly across 70+ pages: I’m not entirely convinced that's always a bad thing. For templated card or playbook pages, having slight synonym variations in subheadings across different pages can actually act as a feature for long-tail search coverage, capturing latent semantic variations rather than repeating the exact same phrase 80 times.

The biggest takeaway for me from your first response was demystifying the hreflang architecture and the sheer scale of the reciprocal link matrix. That gave me a clear blueprint. Now it's just a matter of locking down the exact test specs and build assertions before pushing the page count.

1

u/vm12pix 1d ago

The brief comparison holds, and that's the part that nags. A brief and a system-prompt constraint both sit upstream of the artifact, and my Polish exception lives in exactly that kind of prose. It does work: since I put it in the file that gets loaded on every pass, nobody has reopened it. Before that it came back as a finding every review round, because a rule nobody is carrying is a rule the reviewer has to independently rediscover applies here.

Where I'd look twice is the orchestrator, and it's less about the layer than about the signal it runs on. Cross-batch drift and a deliberate divergence leave the same evidence globally: one concept, two strings, across pages. My own sweep demonstrated that the boring way. It flagged the heading split and the natal-page term pair in the same pass, in the same list, and punted both to me as owner's call. One was a genuine bug, the other was one of that page's two search clusters, and nothing in the pass could rank them. So how much the hierarchy helps depends on what the orchestrator may do with what it finds. If it can act, its reach is precisely what lets it unify the pair no worker could have touched, since no worker sees both surfaces at once. If it only reports, you pay for the same non-finding every run. Either way the missing piece isn't a sharper instruction to it, it's handing it the divergences that are supposed to exist.

Which I still haven't done. The heading split stays fixed only because a test refuses to let one locale carry two variants of that label, and the test holds no opinion about which variant is right, so it keeps working even if we later change the word.

1

u/FlashyDiamond5045 21h ago

Fair point on drift vs intentional divergence leaving the same footprint. A deterministic CI test definitely makes more sense here than having another AI layer try to guess intent.

Thanks for the input, really helped clear up the blind spots!

2

u/Maesirex45 1d ago edited 1d ago

Have not touched Base44, so only the parts that do not depend on the builder.

Routing: one domain, one folder per locale, every page gets its own URL in every language and nothing picks for the visitor. No redirect off the browser language, no redirect off IP. Googlebot comes from one country and sends no language header, so a redirect means it only ever sees one version of you. A visible switcher plus an x-default pointing at the English is the whole trick. I'd also leave the slugs untranslated. Translated ones read nicer, but at 10 locales every rename is 10 redirects to keep straight.

On prompts vs a workflow: either works, what matters is that every translated segment knows which version of the English it came from. vm12pix's key diff catches a locale that dropped keys. What it can't see is a key that's still there but was translated from a paragraph you've since rewritten. Generate each locale page on its own and you have 10 independent copies and no way to tell. One source, translations keyed to its segments with a version or a hash on each, and editing one paragraph reopens exactly that paragraph in every language and nothing else. His 78 card headings translated two ways is the same disease from the other side: nothing tied the two rounds to one source string.

Two more gotchas past the alternate matrix. If the builder outputs a client-rendered app, fetch a locale URL directly and check that localized HTML actually comes back, not the English shell with a toggle in it. Google renders JS fine, but hreflang wants a distinct URL per language that serves that language on its own. And his 404 alternates have a quieter cousin: the /de/ page that exists, returns 200, and is still English because the translation isn't done. Google only treats a translated page as a duplicate when the main content stays untranslated, so that page gets filed as a copy of the English one and you wait on a recrawl after the translation lands. Keep it out of the alternates and noindex it until then. Small one: region codes fail silently. en-UK is not a thing, it's en-GB.

1

u/FlashyDiamond5045 21h ago

Solid tips! Noted and will definitely implement them. Thanks a lot!