i18nlanguage switcherbilingual SEOCMS
Simple Internationalization: Language Management That Works
I simplified multilanguage for two realities: German readers in Germany and everyone else. The first version shipped but was overengineered. I cut it down to clear locale paths, a one-time detector that respects preference and browser settings, a switcher that keeps context, and a clean split between interface strings and long-form content. Predictable for people. Indexable for search.
Summary
I set multilanguage in my website for two realities: German readers in Germany and everyone else. The first version shipped but was overengineered. I cut it down to clear locale paths, a one-time detector that respects preference and browser settings, a switcher that keeps context, and a clean split between interface strings and long-form content. Predictable for people. Indexable for search.
Context
Audience split: a growing base in Germany and a steady flow of English readers. The site had to feel local in Germany without trapping English speakers. Search engines needed clean alternates, not duplicates.
Problems
Problem A — Multilanguage and “place”
Support at least DE and EN. Make “place” practical: default to German in Germany, keep English close, and never surprise the user.
Problem B — Overengineered first build
It worked, but it was too clever: hidden fallbacks, too many flags, behavior I had to re-read. Complexity leaked into UX, SEO, and editorial flow.
Constraints and goals
- Clear URLs for trust and bilingual SEO.
- One polite decision on first visit, then remember it.
- Switching language must keep the current page.
- Editors must publish translations without a code deploy.
- Architecture small enough to explain in one minute.
Approach
I paused and applied basics from Clean Code (Robert C. Martin):
- “Functions should do one thing.”
- “Meaningful names.”
- “Leave the code better than you found it.”
No rewrite. Subtraction. I removed hidden rules, named decisions plainly, and wrote the behavior down.
Solution overview
- Locale paths: every page lives at
/de/...and/en/.... - One-time detector: saved preference → browser
Accept-Language→ default = DE. Decide once, stop guessing. - Context-preserving switcher: change language and stay on the same route; preference is sticky.
- Two translation layers: interface strings in small JSON files; articles and rich content in a CMS with EN/DE counterparts.
- Search alignment:
hreflangbetween versions and a sitemap that lists both locales.
How it works (user flow)
Arrive at / → if a language cookie exists, use it → else read Accept-Language and pick the first supported locale → else serve DE → user can switch any time → choice is saved for next visits.
Implementation notes (non-technical)
- The URL tells the truth about language.
- Detection is polite and one-time.
- The switcher maps equivalent pages across languages.
- Editors translate and publish in the CMS; interface text stays versioned in JSON.
- Search engines see two clean versions with correct alternates.
Impact
- Fewer surprises for users.
- Faster editorial work; no deploy cycle for translations.
- Cleaner indexing with obvious alternates.
- A system simple enough to maintain and extend.
Challenges and fixes
- Helpful fallbacks that weren’t. Removed silent rules; kept one detector and one recorded preference.
- Context loss on switch. Ensured
/de/x↔/en/xmapping. - SEO uncertainty. Locale in the path,
hreflanglinks, bilingual sitemap. - Editorial mismatches. Paired posts in the CMS so mid-article switching lands on the correct counterpart.
Scalability plan (adding new languages)
When Spanish (es) and Italian (it) join:
- Add locale codes and confirm the default.
- Create UI files with existing keys; translate values.
- Extend the switcher options; keep the same preference behavior.
- Update CMS schema and link translated posts to avoid orphans.
- Publish routes and sitemap for new locales; add
hreflang. - Smoke-test first-visit choice, switching, and indexing.
- Document supported locales.
Small, known steps. Repeatable.
What I’ll improve next
- Preserve
?queryand#anchoron language switch. Useful for analytics and deep links. - Explicit redirect types. Mark permanent routes as 301 instead of relying on defaults.
Lessons
Complexity arrives disguised as “helpful.” The fix is discipline: visible URLs, a one-time detector, a trustworthy switcher, and a clean split between interface strings and content. If you can explain the behavior in a minute, it’s ready.
References
- Clean Code: A Handbook of Agile Software Craftsmanship — Robert C. Martin (2008).
- MDN Web Docs —
Accept-Languageheader. - Google Search Central —
hreflangfor multilingual sites.
Is recurring work behind the question?
Tell us what keeps happening in the free fit check. We will review whether Groundlens is the right next step.