Quand « Completed » ne veut pas dire terminé
#18 - Ou comment j'ai refondu mon usine de traduction pour qu'elle tienne en prod
Buenos dias !
Bienvenue dans cette 18ᵉ édition de Founder Naked.
Cette semaine, je te raconte une galère que j’ai bien méritée : comment j’ai dû refondre entièrement mon usine de traduction — celle dont je te parlais dans l’édition #5 — parce qu’elle marchait à merveille… chez moi. Et nettement moins bien une fois lâchée en production.
Si ce n’est pas déjà le cas, tu peux aussi :
Me suivre sur Linkedin, où je publie des posts complémentaires,
Découvrir les services que je propose, notamment dans le secteur de la finance…
… mais pas encore découvrir Licorn, ma nouvelle boîte.
Allez, c’est parti !
Au programme
Petit rappel : mon usine à 210 langues
Le bug qui ne se voyait pas
« Ça marche en local » ne veut rien dire
Mon nouveau découpage, étape par étape
Comment t’en inspirer
Petit rappel : mon usine à 210 langues
Si tu m’as lu dans l’édition #5, tu sais que j’ai fait un choix un peu fou : traduire tout le contenu de Licorn dans 210 locales (un couple langue-pays, genre fr-FR ou ar-SA), pour adresser environ 95 % de la population mondiale dès le lancement.
Pour ça, j’ai deux briques bien séparées :
Un package « translator » : c’est lui qui sait comment traduire (quel fournisseur, quel prompt, quelles adaptations culturelles). Il est volontairement agnostique : il ne sait rien d’Inngest, rien de mon CMS. Il traduit, point.
Un orchestrateur (Inngest) : c’est lui qui déclenche et coordonne les traductions, encaisse la charge, et peut « scaler » indépendamment de mes apps.
En gros : je modifie une page, j’envoie un événement, et tout mon contenu part se faire traduire en 210 langues, sans bloquer le reste.
Sur le papier, c’est beau. Et chez moi, ça tournait nickel.
Le bug qui ne se voyait pas
Le pire genre de bug, c’est celui qui ne se voit pas.
Un jour, je lance la traduction d’une page complète de mon site marketing. L’interface d’Inngest m’affiche fièrement un beau « Completed » tout vert. Tout va bien.
Sauf que non.
Quand je vais vérifier en base, sur mes 210 langues, j’en ai à peu près… 42 réellement enregistrées. Les autres ? Évaporées. Pas d’erreur, pas d’alerte. Juste un « succès » qui n’en était pas un.
Et ça, c’est le genre de truc qui me rend dingue : un système qui te dit « tout va bien » alors que les deux tiers du boulot sont passés à la trappe.
Les deux principaux coupables :
Des étapes trop grosses : je traduisais et j’écrivais tout d’un seul bloc. Au moindre dépassement de temps, l’étape mourait — et avec elle, une partie du travail.
Des écritures en parallèle sur le même document : tout partait écrire dans mon CMS en même temps, et certaines écritures s’écrasaient les unes les autres (le fameux race condition : deux opérations qui se marchent dessus).
« Ça marche en local » ne veut rien dire
J’arrive là au cœur du vrai sujet, celui que je voulais partager avec toi 👇
La vraie leçon, ce n’est pas « j’ai écrit un bug ». C’est : tout fonctionne en local, et c’est exactement ça, le piège.
En local, sur mon gros PC, avec les outils qui tournent sur ma propre machine, je peux balancer une énorme page, lancer 210 traductions, attendre 1 h 15 sans broncher. Ça passe.
En production, c’est une autre histoire. Et surtout quand, comme moi, tu lances ta boîte et que tu dois tenir tes coûts.
Quand tu démarres, tu jongles avec les plans gratuits (« Hobby ») un peu partout, et tu fais avec leurs limites :
Côté hébergeur (Vercel), une fonction a une durée maximale d’exécution (300 sec = 5 min). Tu la dépasses, elle est coupée net.
Côté orchestrateur (Inngest), tu as des quotas à respecter.
Du coup tu te retrouves avec un truc qui tourne tranquillement chez toi, et qui se prend des timeouts dès qu’il passe en prod. Et j’ai aggravé mon cas tout seul : en prod, j’avais testé sur des petits bouts — quelques blocs de contenu par-ci par-là. Jamais une page marketing complète.
Or traduire une page entière en 210 langues, ce n’est ABSOLUMENT pas la même chose que traduire trois blocs. Le volume change tout.
Bref : j’avais validé les gros volumes en local, et les petits volumes en prod. Le pire des deux mondes pour passer à côté du problème.
Mon nouveau découpage, étape par étape
J’ai donc tout repensé. Et l’idée maîtresse, c’est de découper en petits bouts (encore et toujours).
Plutôt qu’un gros bloc qui fait tout et qui meurt en entier au moindre souci, j’ai maintenant un pipeline avec des étapes distinctes. Chacune est un point de reprise : si une étape a réussi, elle n’est pas refaite ; si une échoue, on ne relance qu’elle.
Voici les étapes (tu les retrouves telles quelles dans mon interface Inngest 👇) :
plan— Je classe mes 210 locales par familles, j’estime le coût et le temps. Aucune traduction encore, juste le plan de bataille.translate-primary— Je traduis mes langues « primaires » (celles que je traduis de zéro), par paquets bornés (10 langues max à la fois, jamais les 210 d’un coup).adapt— J’adapte les langues « secondaires » à partir d’un pivot (l’espagnol argentin à partir de l’espagnol mexicain, par exemple), toujours par paquets.write-back— J’écris les traductions dans mon CMS, par lots et séquentiellement cette fois, pour ne plus jamais écraser une écriture par une autre.finalize— Je fais les comptes : combien de langues écrites, combien échouées, combien bloquées. Une sorte de compte-rendu pour moi,

Deux principes que j’ai ajoutés et qui changent tout :
Fail-closed : avant d’écrire une traduction, je la valide. Si elle est vide, malformée ou douteuse, je la marque « bloquée » et je ne l’écris pas. Mieux vaut une langue manquante qu’une langue pourrie qui écrase du bon contenu. (Dans mon interface, chaque langue a son petit rapport
valid: true / blocked: false.)Preuve d’écriture : après avoir écrit, je relis pour vérifier que c’est vraiment en base. Si ça ne l’est pas, l’étape se relance toute seule. Fini les faux positifs.
Comment t’en inspirer
Tu n’es peut-être pas en train de traduire 210 langues. Mais ces principes, tu peux les reprendre dès que tu construis un truc qui doit tourner ailleurs que sur ta machine :
Teste en conditions réelles, et au pire volume. « Ça marche en local » est le mensonge le plus courant en dev. Le local ment trop souvent.
Découpes en petits bouts reprenables. Un gros traitement monolithique, c’est un château de cartes : il tombe en entier.
Plafonne ce qui part en parallèle. Le parallélisme c’est puissant, mais sans garde-fou ça te pète à la figure.
Sépare le « quoi » du « comment ». Ma logique de traduction ne sait rien de mon orchestrateur ; le jour où Inngest ne me convient plus, je change sans tout casser.
Ne crois jamais un « succès » sur parole. Vérifie que le travail est réellement fait. Un statut vert n’est pas une preuve.
Et tout ça prend encore plus de sens quand tu démarres avec des bouts de ficelle et des plans gratuits : tes contraintes de coûts ne sont pas un détail, elles font partie de l’architecture.
Le mot de la fin
Voilà, encore une galère traitée, et partagée ici, sans filtre.
Au final, ma page d’accueil se traduit aujourd’hui proprement en 210 langues, et ça c’est cool ! 👇
Si tu ne me connais pas : je m'appelle Yoann, je construis Licorn, l'assistant culinaire dont on rêve avec ma femme et mes deux filles, et je documente tout ici, sans bullshit.
Bonne semaine.
Yoann





