Skip to content

fix(i18n): résolution de langue déterministe — le préfixe d'URL décide, plus le cookie#258

Merged
thierryvm merged 3 commits into
mainfrom
fix/i18n-locale-cookie-deterministic
Jul 25, 2026
Merged

fix(i18n): résolution de langue déterministe — le préfixe d'URL décide, plus le cookie#258
thierryvm merged 3 commits into
mainfrom
fix/i18n-locale-cookie-deterministic

Conversation

@thierryvm

@thierryvm thierryvm commented Jul 25, 2026

Copy link
Copy Markdown
Owner

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

// 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.
Original summary in English

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.

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.
Original summary in English

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.
Original summary in English

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.

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.
Original summary in English

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.
Original summary in English

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.

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.
Original summary in English

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.
Original summary in English

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.

The language switcher kept reverting to English on its own, roughly half the
time, and never the other way round. next-intl's syncCookie rewrites
NEXT_LOCALE whenever the locale resolved from the URL differs from the cookie,
and the URL prefix always wins — so any request to /en... while the cookie held
fr-BE reset the stored language for a year. It is a race: an in-flight /en...
request lands after the Server Action's Set-Cookie. The page keeps rendering in
French while the cookie flips, and the NEXT navigation 307s to /en. The FR->EN
direction is immune because its requests target unprefixed URLs, which is
exactly why it always came back to English.

The fix cannot live in the middleware. Three attempts were built and measured
against production builds first: Next canonises RSC requests before middleware
runs, stripping the rsc / next-router-prefetch / sec-purpose headers AND the
?_rsc query param, so a prefetch and a real navigation are indistinguishable
there. The write had to be removed rather than the request qualified.

localeDetection: false is not an extra — it is required by the line above. In
next-intl's resolveLocale, the cookie and Accept-Language are two branches of
the same localeDetection gate, so dropping the cookie alone would promote
Accept-Language to sole detector. Since French lives on unprefixed URLs under
as-needed, an English-browser visitor who picked French would then be 307'd
back to /en on every unprefixed URL — French unreachable, deterministically,
for a whole class of users; a Dutch-speaking visitor would be pushed to
/nl-BE, which is not translated. plan-reviewer caught this before any code was
written. Trade-off re-confirmed by @Thierry: / always renders French, whatever
the browser language. It also stops the app auto-serving locales that have no
validated translation.

The cookie is kept: setLocaleAction still writes it, and it still has two
readers — the post-OAuth redirect target and the root 404, both outside the
[locale] segment. It simply no longer takes part in middleware routing.

Tests. Every Playwright project pins locale: 'fr-BE', so the suite was
structurally blind to the regression class above; locale-detection-off.spec.ts
uses test.use({ locale: 'en-US' }) and asserts an English browser choosing
French keeps French, on soft and hard navigation. Switcher specs 2 and 3 relied
on the cookie-based resolution path this commit removes — they now navigate to
the prefixed paths the app's own <Link>s emit, and spec 2 asserts the
counterpart, that unprefixed URLs are French for everyone. routing.test.ts locks
both flags so a silent revert breaks the build.

Measured: the regression test was red on the previous config (Expected fr-BE,
Received en) and is green here; e2e/i18n 7 passed on a production build; 1646
unit tests pass.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ankora Ready Ready Preview, Comment Jul 25, 2026 6:35pm

@sourcery-ai

sourcery-ai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

🧙 Sourcery examine votre pull request !


Conseils et commandes

Interagir avec Sourcery

  • Lancer une nouvelle revue : Commentez @sourcery-ai review sur la pull request.
  • Poursuivre les discussions : Répondez directement aux commentaires de revue de Sourcery.
  • Générer un ticket GitHub à partir d’un commentaire de revue : Demandez à Sourcery de créer un
    ticket à partir d’un commentaire de revue en répondant à celui-ci. Vous pouvez aussi répondre à un
    commentaire de revue avec @sourcery-ai issue pour créer un ticket à partir de ce commentaire.
  • Générer un titre de pull request : Écrivez @sourcery-ai n’importe où dans le titre de la pull
    request pour générer un titre à tout moment. Vous pouvez aussi commenter
    @sourcery-ai title sur la pull request pour (re)générer le titre à tout moment.
  • Générer un résumé de pull request : Écrivez @sourcery-ai summary n’importe où dans
    le corps de la pull request pour générer un résumé de PR à tout moment, exactement là où vous
    le souhaitez. Vous pouvez aussi commenter @sourcery-ai summary sur la pull request pour
    (re)générer le résumé à tout moment.
  • Générer le guide du relecteur : Commentez @sourcery-ai guide sur la pull
    request pour (re)générer le guide du relecteur à tout moment.
  • Résoudre tous les commentaires Sourcery : Commentez @sourcery-ai resolve sur la pull
    request pour résoudre tous les commentaires Sourcery. Utile si vous avez déjà
    pris en compte tous les commentaires et ne souhaitez plus les voir.
  • Ignorer toutes les revues Sourcery : Commentez @sourcery-ai dismiss sur la pull
    request pour ignorer toutes les revues Sourcery existantes. Particulièrement utile si vous
    voulez repartir de zéro avec une nouvelle revue – n’oubliez pas de commenter
    @sourcery-ai review pour lancer une nouvelle revue !

Personnaliser votre expérience

Accédez à votre dashboard pour :

  • Activer ou désactiver des fonctionnalités de revue comme le résumé de pull request
    généré par Sourcery, le guide du relecteur, et d’autres.
  • Changer la langue de la revue.
  • Ajouter, supprimer ou modifier des instructions de revue personnalisées.
  • Ajuster d’autres paramètres de revue.

Obtenir de l’aide

Original review guide in English

🧙 Sourcery is reviewing your pull request!


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

…ile projects

The new locale-detection spec clicked the switcher directly, which times out
on the mobile projects: there the switcher lives inside the nav drawer and is
not visible. That is the same reason locale-switcher.spec.ts is testIgnore'd
on mobile-safari and mobile-chrome.

The contract under test — which locale an en-US browser resolves to — is
viewport-independent, so a desktop viewport in that one test is enough, and
the two navigation-only tests keep covering the contract on every project.

Verified locally against a production build: 13 passed across all five
projects, including mobile-safari and mobile-chrome.
…o audit verdicts

i18n-auditor returned NO-GO on a real P0, confirmed by measurement rather than
taken at face value: /app with a NEXT_LOCALE=en cookie 307s to /en/app on the
current production config, and to an unprefixed /login here — French. The
middleware was an implicit safety net for the ~9 bare next/navigation
redirect() call sites in the auth guards and Server Actions.

Not fixed here: those are auth guards and Server Actions, which is the heavy
lane, and extending a PR's scope mid-flight is a banned action. @Thierry
arbitrated on 2026-07-25 to merge this and chain a dedicated follow-up: the bug
this closes is daily and in production, while the regression only affects
authenticated English-speaking users, of which there are none before launch.

Also records what both audits found outside this scope: a broken locale select
in Settings, a third undocumented cookie reader, and four pre-existing SEO
canonical/sitemap bugs.
@thierryvm
thierryvm merged commit 68ea981 into main Jul 25, 2026
9 checks passed
@thierryvm
thierryvm deleted the fix/i18n-locale-cookie-deterministic branch July 25, 2026 18:44
thierryvm added a commit that referenced this pull request Jul 25, 2026
…ées par les audits (#259)

## Pourquoi

@Thierry a constaté que `/app/commitments` (« Engagements ») est
inatteignable depuis la nav mobile. Il a choisi de **ne pas corriger à
chaud** : la nav mobile est refondue d'un bloc en Phase 1 du programme
UX, un patch isolé serait jeté. Cette PR ne contient donc **aucun
changement de code** — uniquement la trace, pour que le sujet entre dans
le périmètre de la Phase 1 au lieu d'être redécouvert.

## Le trou, confirmé

La route existe et fonctionne, mais aucune surface de navigation mobile
ne la référence :

| Surface | Engagements ? |
|---|---|
| `Header.tsx` (7 destinations) | oui, mais `hidden … lg:flex` → desktop
≥ 1024 px |
| `BottomTabBar` (4 onglets) | non |
| `MoreSheet` | **non** |
| `EngagementsCard` (cockpit) | oui — seul point d'entrée mobile |

Hors du cockpit, Engagements n'existe plus sur mobile.

## Le constat qui compte

C'est structurel : les destinations sont **dupliquées dans trois
composants** sans contrat commun. Rien n'empêche d'ajouter une route et
d'oublier une ou deux surfaces — c'est exactement ce qui s'est produit.
La Phase 1 reçoit donc une attente explicite : un registre unique de
destinations, plus un test qui échoue si une route de `app/**` n'y
figure pas.

## Également tracé

`docs/audits/2026-07-25-dettes-tracees-audits-i18n-seo.md` recense ce
que `i18n-auditor` et `seo-geo-auditor` ont remonté pendant #258, hors
de son périmètre :

- **P0** — les redirections serveur non préfixées perdent la langue
(introduit par #258, arbitré par @Thierry, PR de suivi à faire). Mesuré
: `/app` avec cookie `en` faisait `307 → /en/app`, fait désormais `307 →
/login` (français). ~9 sites dans les gardes auth et Server Actions.
- **P1** — le sélecteur de langue des Réglages ne peut rien enregistrer
d'autre que `fr-BE` (le `<Select>` propose `fr-FR`/`en-GB`, absents de
l'enum `LOCALES`), et son action n'écrit ni le cookie ni ne revalide.
Préexistant.
- **P1** — 4 bugs SEO préexistants : pages `noindex` soumises au
sitemap, canoniques cross-locale sur la FAQ et les pages légales,
glossaire qui se canonicalise vers l'accueil, locales non traduites
indexables.
- **P2** — 3ᵉ lecteur du cookie non documenté, deep-links des locales
cachées non testés, `llms.txt` qui surestime le périmètre livré.

Aucun n'est corrigé ici : étendre le scope d'une PR en vol est une
action bannie par la doctrine.

## Périmètre

Documentation uniquement — 2 fichiers, 0 ligne de code applicatif.

## Summary by Sourcery

Documenter les problèmes connus d’UX et de SEO/i18n sans modifier le
code de l’application afin de garantir qu’ils soient suivis et intégrés
dans les travaux à venir.

Documentation :
- Étendre la spécification de conception du programme UX avec la
documentation des lacunes de navigation sur mobile et des problèmes de
routage structurel liés aux Engagements.
- Ajouter un document d’audit recensant les dettes i18n, de gestion des
locales et de SEO en suspens, identifiées lors des audits récents.

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Document known UX and SEO/i18n issues without changing application code
to ensure they are tracked and included in upcoming work.

Documentation:
- Extend the UX program design spec with documented mobile navigation
gaps and structural routing issues around Engagements.
- Add an audit document capturing outstanding i18n, locale handling, and
SEO debts identified during recent audits.

</details>
thierryvm added a commit that referenced this pull request Jul 25, 2026
… et mergé (#260)

Le handoff décrivait encore #258 comme en vol. Mis à jour sur l'état
clos : 7 PR mergées, working tree propre, aucune PR ouverte.

**Bug 1 fermé.** Le correctif livré est consigné, mais surtout la raison
pour laquelle il ne pouvait pas vivre dans le middleware — pour que
personne ne retente cette couche : Next canonise les requêtes RSC avant
l'exécution du middleware, en retirant à la fois les en-têtes de
prefetch et le paramètre `?_rsc`, donc un prefetch et une navigation
réelle y sont indiscernables. Le piège d'ordonnancement
`ResponseCookies` est conservé également.

Le reste à faire est désormais explicite et ordonné : le P0 remonté par
l'audit i18n (les redirections serveur non préfixées perdent la langue —
arbitré, PR de suivi), le trou de nav mobile que @Thierry a choisi de
reporter en Phase 1 plutôt que de patcher, et les défauts préexistants
du sélecteur de langue des Réglages et des canoniques SEO.

Miroir dans le vault Obsidian, conformément à la règle de double
redondance.

Documentation uniquement — 1 fichier, 0 ligne de code.

## Summary by Sourcery

Documentation :
- Réviser la remise locale/CSP afin de lister l’ensemble final des PR
fusionnées, documenter la correction livrée et les limitations du
middleware pour la gestion des paramètres régionaux, et énumérer les
tâches en suspens concernant l’i18n, la navigation mobile, les
paramètres et le SEO en tant qu’éléments suivis.

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Documentation:
- Revise the locale/CSP handoff to list the final merged PR set,
document the delivered fix and middleware limitations for locale
handling, and enumerate outstanding i18n, mobile navigation, settings,
and SEO tasks as tracked items.

</details>
- Réviser le document de transfert de session locale et CSP pour
indiquer que tous les PR pertinents ont été fusionnés, décrire la
correction apportée à la gestion des locales et les limitations du
middleware, et énumérer les problèmes en suspens liés à l’i18n, à la
navigation mobile, aux paramètres et au SEO en tant qu’éléments de
travail suivis.

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Documentation :
- Réviser la remise locale/CSP afin de lister l’ensemble final des PR
fusionnées, documenter la correction livrée et les limitations du
middleware pour la gestion des paramètres régionaux, et énumérer les
tâches en suspens concernant l’i18n, la navigation mobile, les
paramètres et le SEO en tant qu’éléments suivis.

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Documentation:
- Revise the locale/CSP handoff to list the final merged PR set,
document the delivered fix and middleware limitations for locale
handling, and enumerate outstanding i18n, mobile navigation, settings,
and SEO tasks as tracked items.

</details>

</details>
thierryvm added a commit that referenced this pull request Jul 25, 2026
#261)

Ferme le P0 laissé ouvert par #258, que tu avais arbitré.

## Le problème

`localePrefix: 'as-needed'` met le français sur les URLs **non
préfixées** : le préfixe du français est `/fr-BE`, jamais la chaîne
vide. Un chemin comme `/login` ne matche donc aucun préfixe.

Jusqu'à #258, next-intl rattrapait le coup via la branche cookie de
`localeDetection`. #258 a désactivé cette détection — c'était la **même
garde** qui laissait un prefetch réécrire silencieusement ta langue. Le
filet est parti avec, et chaque `redirect()` nu renvoyait un utilisateur
anglophone sur une page française : session expirée, connexion,
déconnexion, fin d'onboarding, refus admin.

## Le correctif

Le helper que je comptais écrire **existait déjà** — `plan-reviewer` l'a
relevé, et c'est meilleur que ma proposition :

```ts
redirect({ href: '/login', locale: await getLocale() })
```

Ce `redirect` de `@/i18n/navigation` retourne `never`, lève
immédiatement, et **son type impose la locale**.

### Le piège évité

J'avais envisagé un helper `async` (`await
redirectLocalised('/login')`). Vérifié : `no-floating-promises` n'est
pas activé (pas de lint typé). Un `await` oublié sur une garde d'auth
n'aurait levé **ni erreur TypeScript ni erreur de lint** — la garde ne
lèverait plus et l'utilisateur passerait **non authentifié**. Le
redirect synchrone rend cette classe d'erreur impossible.

### Terminaison explicite

TypeScript ne propage pas le narrowing `never` depuis un destructuring,
contrairement au `redirect` de `next/navigation` : 8 erreurs `possibly
null` sont apparues. Corrigé par un `return redirect(...)` à chaque
site.

## Ce qui n'est délibérément PAS localisé

- **L'URL OAuth Google** — absolue et externe, la préfixer la casserait.
Elle garde le `redirect` de `next/navigation` sous l'alias explicite
`redirectToExternalUrl`.
- **`auth/callback`** garde sa résolution par cookie : la route est
exclue du matcher du proxy, donc `getLocale()` y retomberait sur un
chemin coûtant un `auth.getUser()` + un select `users` sur le chemin
chaud de l'OAuth.
- **`proxy.ts` et `routing.ts`** ne sont pas touchés — un 307 par
lecture de cookie dans le proxy est l'alternative tentante en une ligne,
interdite par la note « do not retry that layer ».

Le paramètre `redirectTo` de `requireUser` est **supprimé** : aucun de
ses 6 appelants ne le passe, le localiser aurait inventé une surface
d'open redirect.

## Preuve

| Requête | Production (avant) | Cette branche |
|---|---|---|
| `/en/app` — visiteur anglais | `307 → /login` ❌ français | `307 →
/en/login` ✅ |
| `/app` — visiteur français | `307 → /login` | `307 → /login` ✅
inchangé |

**Ces branches avaient zéro couverture** : la suite voisine n'exerce que
`getOptionalUser`. Le nouveau test mocke un `redirect` qui **lève** — un
mock inerte laisserait `requireUser` retourner `undefined as User` et le
test resterait vert au-dessus d'une garde qui ne garde plus.

**Angle mort assumé** : les specs Playwright authentifiées sont skippées
en CI. Ces tests unitaires sont le **seul** filet automatisé pour cette
régression, pas un complément.

`typecheck` 0 · `lint` 0 · `test` **1652/1652**

Rapport : `docs/prs/PR-i18n-localised-server-redirects-report.md`

## Summary by Sourcery

Garantir que les redirections d’authentification et d’onboarding côté
serveur préservent la locale de l’utilisateur au lieu de cibler
systématiquement les pages en français.

Bug Fixes :
- Corriger les redirections liées à l’auth (login, logout, signup,
onboarding, accès admin, snapshot de workspace, statut de suppression)
afin que les utilisateurs anglophones soient redirigés vers les pages en
anglais sous des URL préfixées par la locale.

Enhancements :
- Adopter le helper de redirection compatible i18n (locale-aware) dans
les actions et pages côté serveur, avec une terminaison explicite via
`return` pour satisfaire l’analyse de contrôle de flux de TypeScript.
- Supprimer le paramètre inutilisé de cible de redirection dans
`requireUser` afin d’éviter d’introduire une surface de redirection
ouverte inutile.

Documentation :
- Ajouter un rapport de PR détaillé documentant la régression des
redirections i18n, la logique de la correction locale-aware, ainsi que
les flux OAuth et callback non localisés.

Tests :
- Ajouter des tests unitaires pour couvrir les redirections qui
transportent la locale dans `requireUser` et `requireUserWithWorkspace`,
et garantir que ces gardes ne peuvent pas être contournés
silencieusement.
- Mettre à jour les tests existants liés à l’auth afin de mocker la
redirection de navigation i18n et la résolution de locale de `next-intl`
au lieu de `next/navigation`.

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Ensure server-side auth and onboarding redirects preserve the user’s
locale instead of always targeting the French pages.

Bug Fixes:
- Fix auth-related redirects (login, logout, signup, onboarding, admin
access, workspace snapshot, deletion status) so English users are
redirected to English pages under locale-prefixed URLs.

Enhancements:
- Adopt the locale-aware redirect helper across server actions and
pages, with explicit termination via return to satisfy TypeScript’s
control-flow analysis.
- Remove the unused redirect target parameter from requireUser to avoid
introducing an unnecessary open-redirect surface.

Documentation:
- Add a detailed PR report documenting the i18n redirect regression,
rationale for the locale-aware fix, and the non-localised OAuth and
callback flows.

Tests:
- Add unit tests to cover locale-carrying redirects in requireUser and
requireUserWithWorkspace and to ensure these guards cannot be silently
bypassed.
- Update existing auth-related tests to mock the i18n navigation
redirect and next-intl locale resolution instead of next/navigation.

</details>
thierryvm added a commit that referenced this pull request Jul 25, 2026
Ferme un défaut **préexistant** remonté par `i18n-auditor` pendant #258,
avec la simplification que tu as demandée : **FR - EN, sans précision
régionale**.

## Le défaut

Le sélecteur de langue des Réglages ne pouvait **rien enregistrer
d'autre que le français**, et même ça ne changeait rien à l'écran.

- Il proposait `fr-BE`, `fr-FR`, `en-GB`, alors que le serveur valide
`z.enum(LOCALES)` qui ne contient ni `fr-FR` ni `en-GB` → toute autre
sélection échouait avec un toast d'erreur générique.
- Et `updateProfileAction` écrivait `users.locale` sans toucher au
cookie ni revalider : depuis #258 la langue affichée vient **du préfixe
d'URL**, donc la colonne bougeait et l'app restait dans la langue
précédente.

Une préférence, **deux écrivains divergents** : `setLocaleAction`
faisait tout le travail, `updateProfileAction` en faisait un tiers, mal.

## Le correctif

La carte Profil rend désormais le **`LocaleSwitcher` qui existe déjà** —
segmented control FR | EN, déjà audité a11y, déjà branché sur le bon
chemin. `setLocaleAction` devient le **seul** écrivain.

Réparer le `<Select>` maison revenait à écrire une **troisième**
implémentation du même contrôle. Réutiliser l'existant supprime d'un
coup le double submit, les clés dupliquées et la question de la valeur
initiale.

Le contrôle **sort du formulaire** : il persiste immédiatement et
navigue, ce qui remonte la carte. À l'intérieur, il laissait croire que
« Enregistrer » s'y appliquait — et un nom saisi non sauvegardé
disparaissait au changement de langue.

## Ce que l'audit UI a rattrapé

`ui-auditor` a relevé une **régression que j'introduisais** : à ≥1024px
la page monte aussi le switcher du header, donc **deux `radiogroup`
annoncés « Changer de langue »**, indiscernables dans la liste des
éléments d'un lecteur d'écran.

Corrigé plutôt que laissé en arbitrage : le champ des Réglages nomme son
groupe par son libellé visible « Langue ». Header et MoreSheet
inchangés. Bénéfice au passage — nom accessible et texte visible sont
désormais **identiques** (WCAG 2.5.3) au lieu de simplement se recouper.

## Le piège des deux clés d'erreur

`settings.locale.invalid` perdait son émetteur → supprimée des 5
fichiers. `errors.locale.invalid` est émise par `setLocaleAction` →
**préservée**. Les confondre cassait le chemin d'erreur du switcher.
`i18n-auditor` a vérifié les deux : ✅ **GO**, parité intacte.

## Preuve

| | |
|---|---|
| `npm run test` | **1669 / 1669** |
| `typecheck` · `lint` | 0 erreur (8 warnings, niveau préexistant) |
| e2e `settings-locale-field` iPhone 14 | **3 / 3** en local |
| Smoke connecté | libellé « Langue », options `["FR","EN"]`, ancien
`<Select>` absent |

**Falsifiabilité vérifiée** : en réintroduisant l'écriture de `locale`,
deux specs passent au rouge.

⚠️ **Une CI verte ne prouve pas ce correctif** : aucun spec Playwright
ne couvrait ce contrôle et les parcours authentifiés s'auto-skippent en
CI. D'où le smoke seedé local, exigé par la revue.

## Restent en P2, non traités

Pas d'`aria-describedby` « s'applique immédiatement » ; relation
libellé/nom accessible non verrouillée par un test pour les autres
instances ; `CardTitle` rend un `<div>` et jamais un vrai titre
(concerne toute l'app, à tracer séparément).

Rapport : `docs/prs/PR-settings-locale-select-report.md`

## Summary by Sourcery

Utiliser le `LocaleSwitcher` partagé comme unique contrôle de préférence
de langue et supprimer la gestion de la locale du flux de mise à jour du
profil.

New Features:
- Exposer un `LocaleSwitcher` configurable qui peut être étiqueté par un
élément visible externe.
- Ajouter une section dédiée au champ de langue sur la carte de profil
des paramètres en utilisant le `LocaleSwitcher` partagé.

Bug Fixes:
- Corriger le sélecteur de langue des paramètres pour que le changement
de langue utilise la bonne action de locale et mette effectivement à
jour la langue de l’application.
- Empêcher les valeurs de locale invalides ou héritées de casser les
mises à jour de profil en ignorant le champ `locale` dans la payload du
profil.

Enhancements:
- Simplifier le schéma et l’action de mise à jour de profil pour ne
gérer que le nom d’affichage, en laissant la langue à l’action de locale
dédiée.
- Améliorer l’accessibilité du contrôle de langue des paramètres en
donnant à son `radiogroup` un nom accessible distinct basé sur une
étiquette visible.

Documentation:
- Ajouter un rapport détaillé documentant le défaut du champ de locale
dans les paramètres, sa correction, les audits, et les suivis
d’accessibilité restants.

Tests:
- Ajouter des tests unitaires pour le schéma de mise à jour de profil
afin de vérifier que la locale est ignorée et qu’aucune valeur par
défaut n’est réécrite.
- Ajouter des tests unitaires pour `updateProfileAction` afin de
s’assurer qu’il n’écrit jamais la locale et qu’il valide la gestion du
nom d’affichage.
- Introduire une spécification Playwright mobile iOS pour le champ de
langue des paramètres couvrant les options, la dénomination accessible
et la séparation du formulaire de profil.

Chores:
- Supprimer les traductions obsolètes liées à la locale ainsi que les
clés d’erreur de schéma associées à l’ancien sélecteur de langue des
paramètres.

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Use the shared LocaleSwitcher as the single language preference control
and remove locale handling from the profile update flow.

New Features:
- Expose a configurable LocaleSwitcher that can be labeled by an
external visible element.
- Add a dedicated language field section on the Settings profile card
using the shared LocaleSwitcher.

Bug Fixes:
- Fix the Settings language selector so changing language uses the
proper locale action and actually updates the app language.
- Prevent invalid or legacy locale values from breaking profile updates
by ignoring the locale field in the profile payload.

Enhancements:
- Simplify the profile update schema and action to manage display name
only, leaving language to the dedicated locale action.
- Improve accessibility of the Settings language control by giving its
radiogroup a distinct visible label-based accessible name.

Documentation:
- Add a detailed report documenting the Settings locale field defect,
its fix, audits, and remaining accessibility follow-ups.

Tests:
- Add unit tests for the profile update schema to assert locale is
ignored and no default is written back.
- Add unit tests for updateProfileAction to ensure it never writes
locale and validates display name handling.
- Introduce a Playwright mobile-iOS spec for the Settings language field
covering options, accessibility naming, and separation from the profile
form.

Chores:
- Remove obsolete locale-related translations and schema error keys tied
to the old Settings language select.

</details>
thierryvm added a commit that referenced this pull request Jul 26, 2026
Corrige les 4 défauts SEO **préexistants** que `seo-geo-auditor` avait
relevés pendant #258. Tous silencieux : rien ne cassait, Search Console
aurait juste remonté des erreurs des semaines plus tard.

## Ce qui était cassé

| Défaut | Effet |
|---|---|
| Canoniques codées en dur (`/faq`, `/legal/*`) | `/en/faq` déclarait la
page **française** comme canonique — soit « la version anglaise est un
doublon », en contradiction avec le hreflang de la même URL |
| Index du glossaire sans `alternates` | héritait de la canonique du
layout → `/glossaire`, `/en/glossaire` et `/nl-BE/glossaire` pointaient
chacun vers **leur accueil** |
| 3 pages légales `noindex` au sitemap | 15 URLs (3 × 5 locales)
garanties « Submitted URL marked noindex » |
| nl-BE / de-DE / es-ES au sitemap | on demandait à Google d'indexer des
pages dont le contenu est français verbatim |

## Mesuré sur build de production

| | avant | après |
|---|---|---|
| URLs `/legal/*` au sitemap | 15 | **0** |
| Canonique de `/en/faq` | `/faq` | `/en/faq` |
| Canonique de `/en/glossaire` | l'accueil | `/en/glossaire` |
| hreflang de l'index glossaire | aucun | 3 (fr-BE, nl-BE, en) |

## Ce que l'audit a rattrapé sur mon propre correctif

L'index du glossaire n'émettait que la canonique, sans le bloc
`languages` que sa page sœur `[slug]` produit déjà. J'aurais donc
reproduit **sur le correctif lui-même** le défaut que la PR ferme : une
canonique sans hreflang correspondant. Corrigé.

Second point pris : la locale vient désormais de `params` et non de
`getLocale()`. Ce dernier ne fonctionnait que parce que le layout
appelle `cookies()` pour le thème, ce qui force le rendu dynamique — un
couplage implicite à un détail sans rapport.

## Tests

`tests/seo/sitemap.test.ts` (5 specs) verrouille : aucune route noindex,
aucune locale non traduite hors glossaire, pas de doublon d'URL, et
aucun hreflang pointant vers une URL absente du sitemap.
**Falsifiabilité vérifiée** : en réintroduisant l'une ou l'autre
régression, 2 specs passent au rouge.

`npm run test` **1674/1674** · `typecheck` 0 · `lint` 0 (7 warnings
préexistants)

## Volontairement hors périmètre

- Le header `Link` du middleware next-intl annonce toujours les 5
locales, plus large que ce que le sitemap soumet désormais. Le corriger
implique `proxy.ts`, que la note de `routing.ts` réserve à une PR
dédiée.
- `robots.txt` **ne doit pas** bloquer `/legal/` : Google ne pourrait
plus lire le `noindex` qui les retire réellement.
- `public/llms.txt` annonce toujours le néerlandais comme langue
opérationnelle — correctif d'une ligne, tracé à part.
- Asymétrie assumée : le glossaire reste indexé en nl-BE (ses termes
sont réellement traduits) alors que la landing ne l'est plus.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n Internationalization status:review-needed Ready for review type:fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant