Ajouter next-intl (voir l'article
précédent) a résolu le routage
multilingue. Cela a aussi ajouté une dépendance à l'analyseur de messages
ICU de @formatjs, qui par défaut s'exécute dans le navigateur — le client
de chaque visiteur télécharge et exécute la mécanique qui analyse une
syntaxe de message de type {minutes, plural, one {...} other {...}}, alors
même que chaque message réel de ce site est connu au moment du build et ne
change jamais selon la requête.
Mesurer avant de supposer
Plutôt que d'estimer le coût, j'ai utilisé l'analyseur de bundle natif
Turbopack intégré à Next.js 16.1+ (npx next experimental-analyze) pour
obtenir une vraie carte des modules. Il a révélé un bloc dédié @formatjs/
de 22,88 Ko en tête du bundle client — pas une erreur d'arrondi, et pas
quelque chose que les audits Lighthouse
bootup-time/mainthread-work-breakdown (tous deux signalés à un score de
0 sur chaque test de ce site jusqu'ici) allaient expliquer seuls sans ouvrir
directement la carte des modules.
Note
J'avais d'abord utilisé @next/bundle-analyzer par habitude, puisque c'est
l'outil le plus souvent référencé. Le build a immédiatement signalé qu'il
n'est pas compatible avec les builds Turbopack — une vraie limitation
documentée, pas une erreur de configuration de ma part. Je l'ai retiré et
suis passé à l'analyseur natif de Next plutôt que de contourner
l'incompatibilité.
Le correctif
next-intl propose une option experimental.messages.precompile qui déplace
l'analyse ICU vers le moment du build plutôt que le client. Les
dictionnaires de messages sont compilés dans un format que le runtime peut
consommer directement, sans aucun analyseur nécessaire dans le navigateur.
const withNextIntl = createNextIntlPlugin({
requestConfig: "./i18n/request.ts",
experimental: {
messages: {
path: "./messages",
format: "json",
locales: "infer",
precompile: true,
},
},
});
Activer cette option a immédiatement provoqué une vraie erreur TypeScript —
le type de la version installée pour experimental.messages exige un champ
locales que l'exemple minimal de la documentation officielle de cette
option précise omet. Ajouter locales: "infer" (détection automatique
depuis le dossier messages/) a corrigé le problème.
Vérifier le gain réel
Decision
Je n'ai pas pris l'existence de la fonctionnalité comme preuve qu'elle fonctionnait — j'ai relancé l'analyseur de bundle avant et après, puis comparé les chiffres réels, avec la même rigueur que pour l'investigation Lighthouse de l'article ci-dessus.
Le total « All Route Modules » est passé de 386,56 Ko à 359,76 Ko
compressés — une réduction de 26,8 Ko, correspondant à peu près au
bloc @formatjs/ identifié plus tôt (désormais entièrement absent de la
carte des modules, à l'exception d'un résidu marginal). Le nombre de modules
est passé de 227 à 224. npm run build et npm test sont restés propres
ensuite — 24/24 pages, suite de tests complète passante.
Ce que je ferais différemment
Rien dans la séquence — mesurer, formuler une hypothèse sur la cause,
appliquer un correctif ciblé, remesurer pour confirmer le gain réel plutôt
que de faire confiance à la description de la fonctionnalité. La seule chose
que je changerais à l'avenir : vérifier toute la surface de type d'une
option de configuration par rapport à la version installée avant d'écrire
l'exemple minimal de la documentation, puisque c'est la deuxième fois le
même jour que cet écart m'a piégé (voir l'incident hasLocale de l'article
précédent).