docs(i18n): diagnostic de la course sur le cookie de langue — la couche middleware est un cul-de-sac prouvé - #255
Merged
Conversation
… non-regression Le cookie NEXT_LOCALE est reecrit en `en` par une requete /en... qui arrive apres le Set-Cookie de la Server Action de changement de langue. La page reste affichee en francais, seul le cookie bascule, et c'est la navigation suivante qui redirige en 307 vers /en. Trois correctifs middleware ont ete construits et mesures sur un build de production ; les trois sont structurellement impossibles. Next canonise les requetes RSC avant l'execution du middleware : les en-tetes `rsc` / `next-router-prefetch` / `sec-purpose` sont retires, un vrai prefetch n'envoie aucun `accept`, et le parametre `?_rsc=<hash>` est retire lui aussi. Un prefetch et une navigation document sont litteralement indiscernables depuis proxy.ts. Rien de speculatif n'est livre : le code middleware est reverte. Deux correctifs viables restent, tous deux hors du middleware et porteurs d'un arbitrage produit, detailles dans le diagnostic avec une recommandation. Le test de non-regression reste dans la suite en `test.fixme` : il decrit le defaut tant qu'il n'est pas clos, plutot que de disparaitre ou de passer au vert contre une forme de requete inventee. Piege d'ordonnancement documente au passage : toute manipulation brute de l'en-tete set-cookie dans proxy.ts doit etre la derniere operation, apres updateSession, qui re-serialise le jar entier via ResponseCookies.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Contributor
There was a problem hiding this comment.
Sorry @thierryvm, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
thierryvm
added a commit
that referenced
this pull request
Jul 25, 2026
…e, plus le cookie (#258) Ferme le bug « le sélecteur de langue repasse tout seul en anglais ». Exécute l'option A du diagnostic mergé en #255, **amendée** après revue. ## Le correctif ```ts // src/i18n/routing.ts localeCookie: false, localeDetection: false, ``` La résolution se réduit à un seul intrant déterministe : le préfixe d'URL, avec repli sur `defaultLocale`. `syncCookie` retourne immédiatement — la course disparaît **par construction**, pas par contournement. Rappel de la cause : `syncCookie` réécrit `NEXT_LOCALE` dès que le locale résolu par l'URL diffère du cookie, et l'URL gagne toujours. Une requête `/en…` encore en vol se termine après le `Set-Cookie` de la Server Action → la langue bascule pour un an. Le sens FR→EN est immunisé (URLs non préfixées), d'où « ça revient **toujours** à l'anglais ». Le correctif ne pouvait pas vivre dans le middleware : Next canonise les requêtes RSC avant son exécution (en-têtes **et** `?_rsc` retirés), donc un prefetch et une navigation y sont indiscernables. Trois tentatives mesurées avant d'en conclure. ## Ce que `plan-reviewer` a rattrapé Ma première version ne mettait que `localeCookie: false`. Elle aurait transformé un bug intermittent en bug **déterministe** : dans `resolveLocale`, le cookie et `Accept-Language` sont deux branches de la **même** garde `localeDetection`. Retirer le cookie seul promeut `Accept-Language` en détecteur unique — et comme le français vit sur les URLs non préfixées en `as-needed` : - un visiteur au navigateur anglais qui choisit le français serait renvoyé en 307 vers `/en` sur **toute** URL non préfixée → **français inatteignable**, 100 % du temps ; - un visiteur néerlandophone serait poussé vers `/nl-BE`, qui n'est pas traduit. @Thierry avait validé l'option A sur un énoncé de coût incomplet. L'énoncé corrigé — « `/` affiche **toujours** le français, quelle que soit la langue du navigateur » — lui a été re-soumis et **confirmé**. ## Le cookie est conservé `setLocaleAction` reste son écrivain, et il garde **deux lecteurs** vérifiés : la cible du retour OAuth Google (`auth/callback/route.ts`) et la 404 racine (`not-found.tsx`), toutes deux hors du segment `[locale]`. Le supprimer casserait la connexion Google. Il ne participe simplement plus au routage. ## Tests Les 5 projets Playwright épinglent `locale: 'fr-BE'` — **aucune spec existante ne pouvait voir** la régression ci-dessus. `e2e/i18n/locale-detection-off.spec.ts` (nouveau) utilise `test.use({ locale: 'en-US' })` et vérifie qu'un navigateur anglais qui choisit le français **garde** le français, en navigation douce comme dure. Les specs 2 et 3 du switcher s'appuyaient explicitement sur « the cookie-based locale resolution path » — le contrat que cette PR supprime. Réécrites vers les chemins préfixés que les `<Link>` de l'app émettent réellement, avec la contrepartie assertée : l'URL non préfixée est française pour tout le monde. `tests/i18n/routing.test.ts` verrouille les deux flags : un revert silencieux casse le build. ## Preuve | Config | Test de non-régression | |---|---| | `localeCookie: { … }` (avant) | ❌ `Expected "fr-BE"` / `Received "en"` — c'est ce qui l'avait fait passer en `test.fixme` en #255 | | `localeCookie: false` (cette PR) | ✅ | - `e2e/i18n/` sur build de production : **7 passés**, 0 échec - `npm run test` : **1646 / 1646** · `typecheck` 0 erreur · `lint` 0 erreur Rapport complet : `docs/prs/PR-i18n-locale-deterministic-report.md` ## Summary by Sourcery Rendre la résolution de la locale déterministe en s’appuyant uniquement sur le préfixe d’URL, en supprimant la détection basée sur les cookies et le navigateur afin d’éliminer les conditions de course et les états de langue inaccessibles. Bug fixes : - Éliminer la condition de course du cookie `NEXT_LOCALE` qui réinitialisait de manière intermittente la langue sélectionnée vers l’anglais après des préfetches ou des navigations sur `/en`. - Empêcher les navigateurs non francophones d’être redirigés hors du français ou vers des locales non traduites en désactivant la détection basée sur l’en-tête `Accept-Language`. Enhancements : - Clarifier le routage i18n et le comportement du proxy pour refléter une résolution de la locale basée sur le préfixe d’URL, avec `NEXT_LOCALE` qui n’influence plus le routage. - Introduire des garde-fous de configuration qui imposent le nouveau contrat de routage et font échouer la build en cas de réversions silencieuses des paramètres de cookie ou de détection de locale. Documentation : - Ajouter un rapport de PR détaillé documentant la stratégie de locale déterministe, le bug précédent, les limites du middleware et les étapes de validation. Tests : - Ajouter une couverture Playwright pour les locales de navigateur non francophones afin de valider une résolution de locale uniquement basée sur le préfixe d’URL et l’accessibilité du français. - Mettre à jour les tests de bout en bout du sélecteur de langue pour couvrir les routes préfixées et non préfixées dans le cadre du nouveau contrat de routage, y compris la non-régression du prefetch qui passe désormais. - Ajouter un test unitaire de routage qui verrouille `localeCookie` et `localeDetection` à `false` afin que les régressions de configuration soient détectées en CI. <details> <summary>Original summary in English</summary> ## Summary by Sourcery Make locale resolution deterministic by relying solely on the URL prefix, removing cookie- and browser-based detection to eliminate race conditions and unreachable-language states. Bug Fixes: - Eliminate the NEXT_LOCALE cookie race that intermittently reset the selected language to English after /en prefetches or navigations. - Prevent non-French browsers from being redirected away from French or into untranslated locales by disabling Accept-Language–driven detection. Enhancements: - Clarify i18n routing and proxy behaviour to reflect URL-prefix–driven locale resolution with NEXT_LOCALE no longer influencing routing. - Introduce configuration guards that enforce the new routing contract and make silent reverts of locale cookie or detection settings fail the build. Documentation: - Add a detailed PR report documenting the deterministic locale strategy, prior bug, middleware limitations, and validation steps. Tests: - Add Playwright coverage for non-French browser locales to assert URL-prefix–only locale resolution and French reachability. - Update locale switcher end-to-end tests to cover prefixed and unprefixed routes under the new routing contract, including the prefetch non-regression now passing. - Add a routing unit test that locks `localeCookie` and `localeDetection` to `false` so configuration regressions are caught in CI. </details> Bug Fixes : - Empêcher la condition de course du cookie `NEXT_LOCALE` qui rétablissait de façon intermittente la langue sélectionnée à l’anglais après des prefetch ou des navigations vers `/en`. - Garantir que les navigateurs non configurés en français peuvent toujours atteindre et conserver la langue française, en évitant les redirections vers des locales non traduites ou indésirables pilotées par `Accept-Language`. Enhancements : - Clarifier le routage i18n et le comportement du proxy pour refléter une résolution de locale pilotée par l’URL et le fait que `NEXT_LOCALE` n’est plus utilisé pour le routage. - Documenter le diagnostic, les compromis et la logique de configuration justifiant la désactivation de la gestion du cookie de locale et de la détection automatique de la locale dans un rapport de PR dédié. Documentation : - Ajouter un rapport de PR détaillé décrivant la stratégie de locale déterministe, le bug précédent, les limitations du middleware et les étapes de validation. Tests : - Ajouter une suite Playwright exécutée avec une locale de navigateur anglaise pour vérifier que la locale provient désormais uniquement du préfixe d’URL et que le français reste accessible et stable. - Mettre à jour les tests end-to-end existants du sélecteur de locale pour couvrir les routes préfixées et non préfixées dans le cadre du nouveau contrat de routage, y compris la non-régression sur le prefetch qui passe désormais. - Ajouter un test unitaire de routage qui fixe `localeCookie` et `localeDetection` à `false` afin que tout revert silencieux de configuration fasse échouer la build. <details> <summary>Original summary in English</summary> ## Summary by Sourcery Rendre la résolution de la locale déterministe en s’appuyant uniquement sur le préfixe d’URL, en supprimant la détection basée sur les cookies et le navigateur afin d’éliminer les conditions de course et les états de langue inaccessibles. Bug fixes : - Éliminer la condition de course du cookie `NEXT_LOCALE` qui réinitialisait de manière intermittente la langue sélectionnée vers l’anglais après des préfetches ou des navigations sur `/en`. - Empêcher les navigateurs non francophones d’être redirigés hors du français ou vers des locales non traduites en désactivant la détection basée sur l’en-tête `Accept-Language`. Enhancements : - Clarifier le routage i18n et le comportement du proxy pour refléter une résolution de la locale basée sur le préfixe d’URL, avec `NEXT_LOCALE` qui n’influence plus le routage. - Introduire des garde-fous de configuration qui imposent le nouveau contrat de routage et font échouer la build en cas de réversions silencieuses des paramètres de cookie ou de détection de locale. Documentation : - Ajouter un rapport de PR détaillé documentant la stratégie de locale déterministe, le bug précédent, les limites du middleware et les étapes de validation. Tests : - Ajouter une couverture Playwright pour les locales de navigateur non francophones afin de valider une résolution de locale uniquement basée sur le préfixe d’URL et l’accessibilité du français. - Mettre à jour les tests de bout en bout du sélecteur de langue pour couvrir les routes préfixées et non préfixées dans le cadre du nouveau contrat de routage, y compris la non-régression du prefetch qui passe désormais. - Ajouter un test unitaire de routage qui verrouille `localeCookie` et `localeDetection` à `false` afin que les régressions de configuration soient détectées en CI. <details> <summary>Original summary in English</summary> ## Summary by Sourcery Make locale resolution deterministic by relying solely on the URL prefix, removing cookie- and browser-based detection to eliminate race conditions and unreachable-language states. Bug Fixes: - Eliminate the NEXT_LOCALE cookie race that intermittently reset the selected language to English after /en prefetches or navigations. - Prevent non-French browsers from being redirected away from French or into untranslated locales by disabling Accept-Language–driven detection. Enhancements: - Clarify i18n routing and proxy behaviour to reflect URL-prefix–driven locale resolution with NEXT_LOCALE no longer influencing routing. - Introduce configuration guards that enforce the new routing contract and make silent reverts of locale cookie or detection settings fail the build. Documentation: - Add a detailed PR report documenting the deterministic locale strategy, prior bug, middleware limitations, and validation steps. Tests: - Add Playwright coverage for non-French browser locales to assert URL-prefix–only locale resolution and French reachability. - Update locale switcher end-to-end tests to cover prefixed and unprefixed routes under the new routing contract, including the prefetch non-regression now passing. - Add a routing unit test that locks `localeCookie` and `localeDetection` to `false` so configuration regressions are caught in CI. </details> </details> Bug Fixes : - Stopper les conflits autour du cookie `NEXT_LOCALE` qui réinitialisaient de façon intermittente la langue sélectionnée vers l’anglais après des préchargements ou navigations sur `/en`. - Empêcher les navigateurs dont la locale n’est pas le français d’être redirigés hors du français vers des locales non traduisibles ou non souhaitées, en garantissant que le français reste accessible. Enhancements : - Clarifier la documentation sur le routage i18n et le proxy afin de refléter une résolution de locale pilotée par l’URL et le fait que `NEXT_LOCALE` ne participe plus au routage. - Documenter l’ensemble du diagnostic et les compromis produit liés à la désactivation du cookie et de la détection automatique de locale dans un nouveau rapport de PR. Documentation : - Ajouter un rapport de PR détaillé décrivant la stratégie de résolution de locale déterministe, le bug précédent, les limites du middleware et les étapes de validation. Tests : - Ajouter une suite Playwright qui simule un utilisateur avec un navigateur en anglais pour vérifier que la locale provient désormais uniquement du préfixe d’URL et que le français reste accessible. - Mettre à jour les tests de bout en bout du sélecteur de locale pour valider le comportement sur les routes préfixées et non préfixées dans le cadre du nouveau contrat de routage. - Ajouter un test unitaire de routage qui fixe `localeCookie` et `localeDetection` à `false` afin que toute réversion silencieuse de la configuration fasse échouer la build. <details> <summary>Original summary in English</summary> ## Summary by Sourcery Rendre la résolution de la locale déterministe en s’appuyant uniquement sur le préfixe d’URL, en supprimant la détection basée sur les cookies et le navigateur afin d’éliminer les conditions de course et les états de langue inaccessibles. Bug fixes : - Éliminer la condition de course du cookie `NEXT_LOCALE` qui réinitialisait de manière intermittente la langue sélectionnée vers l’anglais après des préfetches ou des navigations sur `/en`. - Empêcher les navigateurs non francophones d’être redirigés hors du français ou vers des locales non traduites en désactivant la détection basée sur l’en-tête `Accept-Language`. Enhancements : - Clarifier le routage i18n et le comportement du proxy pour refléter une résolution de la locale basée sur le préfixe d’URL, avec `NEXT_LOCALE` qui n’influence plus le routage. - Introduire des garde-fous de configuration qui imposent le nouveau contrat de routage et font échouer la build en cas de réversions silencieuses des paramètres de cookie ou de détection de locale. Documentation : - Ajouter un rapport de PR détaillé documentant la stratégie de locale déterministe, le bug précédent, les limites du middleware et les étapes de validation. Tests : - Ajouter une couverture Playwright pour les locales de navigateur non francophones afin de valider une résolution de locale uniquement basée sur le préfixe d’URL et l’accessibilité du français. - Mettre à jour les tests de bout en bout du sélecteur de langue pour couvrir les routes préfixées et non préfixées dans le cadre du nouveau contrat de routage, y compris la non-régression du prefetch qui passe désormais. - Ajouter un test unitaire de routage qui verrouille `localeCookie` et `localeDetection` à `false` afin que les régressions de configuration soient détectées en CI. <details> <summary>Original summary in English</summary> ## Summary by Sourcery Make locale resolution deterministic by relying solely on the URL prefix, removing cookie- and browser-based detection to eliminate race conditions and unreachable-language states. Bug Fixes: - Eliminate the NEXT_LOCALE cookie race that intermittently reset the selected language to English after /en prefetches or navigations. - Prevent non-French browsers from being redirected away from French or into untranslated locales by disabling Accept-Language–driven detection. Enhancements: - Clarify i18n routing and proxy behaviour to reflect URL-prefix–driven locale resolution with NEXT_LOCALE no longer influencing routing. - Introduce configuration guards that enforce the new routing contract and make silent reverts of locale cookie or detection settings fail the build. Documentation: - Add a detailed PR report documenting the deterministic locale strategy, prior bug, middleware limitations, and validation steps. Tests: - Add Playwright coverage for non-French browser locales to assert URL-prefix–only locale resolution and French reachability. - Update locale switcher end-to-end tests to cover prefixed and unprefixed routes under the new routing contract, including the prefetch non-regression now passing. - Add a routing unit test that locks `localeCookie` and `localeDetection` to `false` so configuration regressions are caught in CI. </details> Bug Fixes : - Empêcher la condition de course du cookie `NEXT_LOCALE` qui rétablissait de façon intermittente la langue sélectionnée à l’anglais après des prefetch ou des navigations vers `/en`. - Garantir que les navigateurs non configurés en français peuvent toujours atteindre et conserver la langue française, en évitant les redirections vers des locales non traduites ou indésirables pilotées par `Accept-Language`. Enhancements : - Clarifier le routage i18n et le comportement du proxy pour refléter une résolution de locale pilotée par l’URL et le fait que `NEXT_LOCALE` n’est plus utilisé pour le routage. - Documenter le diagnostic, les compromis et la logique de configuration justifiant la désactivation de la gestion du cookie de locale et de la détection automatique de la locale dans un rapport de PR dédié. Documentation : - Ajouter un rapport de PR détaillé décrivant la stratégie de locale déterministe, le bug précédent, les limitations du middleware et les étapes de validation. Tests : - Ajouter une suite Playwright exécutée avec une locale de navigateur anglaise pour vérifier que la locale provient désormais uniquement du préfixe d’URL et que le français reste accessible et stable. - Mettre à jour les tests end-to-end existants du sélecteur de locale pour couvrir les routes préfixées et non préfixées dans le cadre du nouveau contrat de routage, y compris la non-régression sur le prefetch qui passe désormais. - Ajouter un test unitaire de routage qui fixe `localeCookie` et `localeDetection` à `false` afin que tout revert silencieux de configuration fasse échouer la build. <details> <summary>Original summary in English</summary> ## Summary by Sourcery Rendre la résolution de la locale déterministe en s’appuyant uniquement sur le préfixe d’URL, en supprimant la détection basée sur les cookies et le navigateur afin d’éliminer les conditions de course et les états de langue inaccessibles. Bug fixes : - Éliminer la condition de course du cookie `NEXT_LOCALE` qui réinitialisait de manière intermittente la langue sélectionnée vers l’anglais après des préfetches ou des navigations sur `/en`. - Empêcher les navigateurs non francophones d’être redirigés hors du français ou vers des locales non traduites en désactivant la détection basée sur l’en-tête `Accept-Language`. Enhancements : - Clarifier le routage i18n et le comportement du proxy pour refléter une résolution de la locale basée sur le préfixe d’URL, avec `NEXT_LOCALE` qui n’influence plus le routage. - Introduire des garde-fous de configuration qui imposent le nouveau contrat de routage et font échouer la build en cas de réversions silencieuses des paramètres de cookie ou de détection de locale. Documentation : - Ajouter un rapport de PR détaillé documentant la stratégie de locale déterministe, le bug précédent, les limites du middleware et les étapes de validation. Tests : - Ajouter une couverture Playwright pour les locales de navigateur non francophones afin de valider une résolution de locale uniquement basée sur le préfixe d’URL et l’accessibilité du français. - Mettre à jour les tests de bout en bout du sélecteur de langue pour couvrir les routes préfixées et non préfixées dans le cadre du nouveau contrat de routage, y compris la non-régression du prefetch qui passe désormais. - Ajouter un test unitaire de routage qui verrouille `localeCookie` et `localeDetection` à `false` afin que les régressions de configuration soient détectées en CI. <details> <summary>Original summary in English</summary> ## Summary by Sourcery Make locale resolution deterministic by relying solely on the URL prefix, removing cookie- and browser-based detection to eliminate race conditions and unreachable-language states. Bug Fixes: - Eliminate the NEXT_LOCALE cookie race that intermittently reset the selected language to English after /en prefetches or navigations. - Prevent non-French browsers from being redirected away from French or into untranslated locales by disabling Accept-Language–driven detection. Enhancements: - Clarify i18n routing and proxy behaviour to reflect URL-prefix–driven locale resolution with NEXT_LOCALE no longer influencing routing. - Introduce configuration guards that enforce the new routing contract and make silent reverts of locale cookie or detection settings fail the build. Documentation: - Add a detailed PR report documenting the deterministic locale strategy, prior bug, middleware limitations, and validation steps. Tests: - Add Playwright coverage for non-French browser locales to assert URL-prefix–only locale resolution and French reachability. - Update locale switcher end-to-end tests to cover prefixed and unprefixed routes under the new routing contract, including the prefetch non-regression now passing. - Add a routing unit test that locks `localeCookie` and `localeDetection` to `false` so configuration regressions are caught in CI. </details> </details> </details>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contexte
@Thierry : « on a encore ce bug du sélecteur de langue en-fr qui repasse automatiquement à l'anglais ».
Deux correctifs avaient déjà été livrés sur ce symptôme sans le résoudre (#252 cache SW, puis une détection de prefetch par en-tête). Cette PR ne livre pas un troisième correctif — elle livre la preuve que la couche visée est la mauvaise, et l'arbitrage qui reste à trancher.
Cause racine (établie)
next-intl@4.11.0réécritNEXT_LOCALEdès que le locale résolu par l'URL diffère du cookie. Le préfixe d'URL gagne toujours. Donc toute requête/en…alors que le cookie vautfr-BErebascule le cookie enen, pour un an.Le bug est une course : la requête
/en…encore en vol se termine après leSet-Cookiede la Server Action. La page reste en français, seul le cookie bascule, et c'est la navigation suivante qui redirige en 307. Asymétrique par construction — le sens FR→EN est immunisé (URLs non préfixées), d'où « ça revient toujours à l'anglais ».Ce qui a été mesuré (build de production, pas dev)
Trois correctifs middleware construits, trois échecs structurels :
rsc/next-router-prefetch/sec-purposeaccept: text/x-componentaccept=(none)fetch()fabriqués?_rsc=<hash>curl "/en?_rsc=probe"→nextUrl.search = "(empty)"Next canonise les requêtes RSC avant le middleware. Un prefetch et une navigation document sont indiscernables depuis
proxy.ts. Aucun correctif à cette couche ne peut fonctionner.Contenu de la PR
docs/audits/2026-07-25-locale-cookie-race-diagnostic.md— mesures, hypothèses écartées avec preuve, piège d'ordonnancementResponseCookies/updateSessiondécouvert au passage, et les deux correctifs viables avec recommandationtest.fixme— il décrit le défaut tant qu'il n'est pas clos, plutôt que de disparaître ou de passer au vert contre une forme de requête inventéesrc/proxy.ts: inchangé (revert complet du hack)Décision attendue de @Thierry
Option A (recommandée) —
localeCookie: false:syncCookiedevient inerte, la course disparaît par construction. Coût : une arrivée sur/nu ne mémorise plus une langue qui contreditAccept-Language. PR dédiée +plan-reviewer.Option B — navigation dure sur changement de langue : atténue sans supprimer la cause, non prouvable par un test. Jamais seule.
Quality gates
npm run lint: 0 erreurnpm run typecheck: 0 erreurnpm run test: 1645/1645