Upgrade i18next v26 + react-i18next v17 with SSR-safe init

Split initI18n() into separate server/client initialization paths
inspired by remix-i18next's architecture:

- initI18nServer(lng): no LanguageDetector, per-request language from
  Accept-Language header
- initI18nClient(): LanguageDetector reads <html lang> first to match
  server, then localStorage and navigator
- detectLanguage(request): parses Accept-Language for best match

Fixes rehydration errors caused by v26 removing initImmediate option
(replaced with initAsync) and LanguageDetector running on the server
where browser APIs don't exist.

Also makes <html lang> dynamic instead of hardcoded "en".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-03-29 11:38:36 +02:00
parent 4be716687a
commit 79f000fcc5
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
10 changed files with 166 additions and 40 deletions

View file

@ -49,7 +49,7 @@
"drizzle-postgis": "^1.1.1",
"eslint": "^10.1.0",
"eslint-config-prettier": "^10.1.8",
"i18next": "^25.10.4",
"i18next": "^26.0.1",
"i18next-browser-languagedetector": "^8.2.1",
"jsdom": "^29.0.1",
"leaflet": "^1.9.4",
@ -59,7 +59,7 @@
"prettier": "^3.8.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-i18next": "^16.6.1",
"react-i18next": "^17.0.1",
"react-leaflet": "^5.0.0",
"react-router": "^7.13.2",
"tailwindcss": "^4.2.2",