Un Tolgee auto-hébergé donne aux traducteurs un éditeur web complet, sur votre propre machine, sans confier vos libellés à un service tiers. Il se déploie en deux conteneurs avec Docker Compose. Pour qu’il reste un outil et ne devienne pas une dépendance, les fichiers de traduction du dépôt doivent rester la source de vérité : Tolgee est alimenté à la main, jamais lu par l’application ni par la CI.

Qu’est-ce que Tolgee ?

Tolgee est une plateforme de gestion des traductions (TMS, Translation Management System) open source. Elle organise les libellés d’une application en clés et en langues, affiche l’état « traduit / à traduire », propose une traduction automatique (DeepL, entre autres) et expose une API et une interface en ligne de commande pour échanger des fichiers avec un dépôt de code. Une personne qui ne développe pas peut ainsi proposer et corriger des traductions sans ouvrir un fichier JSON.

Pourquoi auto-héberger Tolgee plutôt qu’utiliser un TMS en ligne ?

L’auto-hébergement garde les libellés et leur historique sur votre infrastructure. Pour un socle multi-tenant qui tient ses données chez lui, c’est l’argument structurant : l’ADR du kit, après un essai de Crowdin sur le même catalogue, constate un import et un contexte équivalents des deux côtés, et retient la souveraineté des libellés comme seule différence décisive.

Le coût est connu d’avance : un service de plus à faire tourner. Il se limite ici à un poste de travail, car Tolgee n’est pas déployé avec l’application.

Comment déployer Tolgee avec Docker Compose ?

Il suffit d’un fichier Compose dédié, séparé de celui de l’application, avec sa propre base PostgreSQL. L’extrait ci-dessous reprend la structure du fichier livré avec le kit ; les identifiants locaux, écrits en clair dans le dépôt pour un usage sur poste, sont remplacés ici par des variables.

name: saas-core-tolgee

services:
  tolgee-postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: tolgee
      POSTGRES_PASSWORD: ${TOLGEE_DB_PASSWORD}
      POSTGRES_DB: tolgee
    volumes:
      - tolgee_pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U tolgee -d tolgee"]
      interval: 5s
      timeout: 3s
      retries: 10

  tolgee:
    image: tolgee/tolgee:v3.230.0
    depends_on:
      tolgee-postgres:
        condition: service_healthy
    environment:
      TOLGEE_POSTGRES_AUTOSTART_ENABLED: "false"
      SPRING_DATASOURCE_URL: jdbc:postgresql://tolgee-postgres:5432/tolgee
      SPRING_DATASOURCE_USERNAME: tolgee
      SPRING_DATASOURCE_PASSWORD: ${TOLGEE_DB_PASSWORD}
      TOLGEE_FRONTEND_URL: http://localhost:8087
      TOLGEE_MACHINE_TRANSLATION_DEEPL_AUTH_KEY: ${DEEPL_AUTH_KEY:-}
    ports:
      - "127.0.0.1:8087:8080"
    volumes:
      - tolgee_data:/data

volumes:
  tolgee_pgdata:
  tolgee_data:

Quatre choix méritent d’être repris tels quels :

  • un projet Compose distinct (name:), pour démarrer et arrêter Tolgee sans toucher à la pile applicative ;
  • le PostgreSQL embarqué de Tolgee désactivé (TOLGEE_POSTGRES_AUTOSTART_ENABLED: "false") au profit d’une base externe, sans port publié sur l’hôte, qui n’est jamais la base des tenants ;
  • une image épinglée sur une version précise, jamais latest ;
  • l’interface web publiée sur la seule boucle locale du poste, et une clé DeepL lue depuis un .env non suivi : absente, la ligne est inerte.

Le démarrage tient en une commande :

docker compose -f docker-compose.tolgee.yml up -d

Un envoi massif de clés, d’étiquettes et de captures déclenche la limite de débit par IP de Tolgee (HTTP 429). Le fichier du kit la laisse active par défaut et documente comment la lever le temps d’une synchronisation, puis la rétablir.

Faut-il activer l’édition en contexte ?

L’édition en contexte de Tolgee permet de cliquer sur un texte de l’application en cours d’exécution pour le traduire sur place. Elle suppose d’intégrer le SDK Tolgee dans le front. C’est confortable pendant le développement, mais cela fait entrer Tolgee dans le code livré.

Le kit a fait le choix inverse : aucun SDK Tolgee dans l’application, et un contexte généré depuis le code source puis poussé dans les champs natifs de Tolgee (description de clé, étiquettes, captures d’écran). L’ADR correspondant garde une porte ouverte : un SDK limité au mode développement, retiré du build de production, si des non-développeurs en ont un jour besoin.

Pourquoi le dépôt doit-il rester la source de vérité ?

Parce qu’un outil d’atelier ne doit jamais pouvoir arrêter le produit. Si l’application lisait ses traductions dans Tolgee, sa panne ou son remplacement toucheraient le service en production. Avec des fichiers JSON embarqués dans le build, Tolgee éteint ne change rien à l’exécution : seule l’édition est suspendue.

Ce modèle a un nom dans la documentation du kit, le « modèle A » : les traductions sont livrées avec le code. Une correction de libellé suit le même chemin qu’une correction de code (commit, CI, nouvelle version), et git trace chaque libellé publié. L’alternative, charger les traductions à chaud depuis un CDN ou une table en base, est documentée comme porte de sortie si le rythme des corrections le rendait nécessaire.

Le pont entre les deux mondes est un fichier .tolgeerc.json et deux gestes manuels :

{
  "$schema": "https://tolgee.io/tolgeerc-schema.json",
  "apiUrl": "http://localhost:8087",
  "projectId": 2,
  "format": "JSON_ICU",
  "push": {
    "files": [
      { "path": "packages/i18n/src/locales/en.json", "language": "en" },
      { "path": "packages/i18n/src/locales/fr.json", "language": "fr" }
    ],
    "forceMode": "KEEP"
  },
  "pull": { "path": "packages/i18n/src/locales" }
}
  • pnpm i18n:push envoie le dépôt vers Tolgee. forceMode: "KEEP" n’écrase jamais une traduction déjà faite dans l’outil.
  • pnpm i18n:pull rapatrie les traductions dans les JSON. Le résultat passe ensuite par une revue git et par les tests du catalogue, comme n’importe quel changement.

Le format JSON_ICU conserve la syntaxe ICU des messages, pluriels compris. La clé d’API Tolgee vit dans la variable TOLGEE_API_KEY, jamais dans le dépôt.

Comment donner du contexte aux traducteurs ?

Un traducteur qui voit une clé et une valeur sans savoir où elles s’affichent traduit à l’aveugle. Le kit fournit quatre niveaux de contexte, tous lancés hors bande :

NiveauCommandeCe que voit le traducteur
Description déterministepnpm i18n:contextl’écran et le type d’élément déduits des appels t("clé")
Note rédigéecollect-context.mjs puis push-notes.mjsune phrase sur le rôle du libellé et ses contraintes
Étiquettespnpm i18n:tagsdomain:<namespace> pour filtrer par domaine, inherited:<locale> pour les clés volontairement identiques à l’anglais
Captures d’écranpnpm i18n:screenshots:uploadl’écran réel, lié à chaque clé qui y apparaît

pnpm i18n:sync enchaîne l’envoi, les étiquettes, les captures et leurs liaisons. Comme forceMode: "KEEP" ne supprime jamais rien, pnpm i18n:reconcile liste les dérives (clés présentes dans Tolgee mais absentes du code, traductions posées sur des clés héritées, étiquettes périmées) et ne les corrige qu’avec --apply.

Comment ajouter une langue ?

Ajouter une langue est un événement de code, suivi d’un déploiement :

pnpm i18n:add-locale es "Español" --dry-run
pnpm i18n:add-locale es "Español"

La commande crée le fichier JSON de la langue, vide au départ, donc entièrement hérité de l’anglais, et l’enregistre dans le registre des locales. Si TOLGEE_API_KEY est présente, elle crée aussi la langue dans le projet Tolgee ; --no-tolgee s’en abstient. On traduit ensuite dans Tolgee, puis pnpm i18n:pull remplit le fichier.

Comment saas-core l’intègre

  • docker-compose.tolgee.yml : projet saas-core-tolgee, base PostgreSQL dédiée sans port hôte, PostgreSQL embarqué désactivé, image épinglée, interface sur le port local 8087, DeepL optionnel.
  • .tolgeerc.json : format JSON_ICU, forceMode: "KEEP", fichiers en.json et fr.json.
  • package.json : i18n:push et i18n:pull passent par le CLI Tolgee appelé via npx, sans dépendance ajoutée à l’arbre ; les autres scripts i18n:* pointent vers scripts/i18n/.
  • scripts/i18n/README.md : le flux complet, la cadence (« rien de tout ceci n’est dans la CI, le build ou le déploiement ») et la convention d’étiquettes.
  • packages/i18n/src/index.ts : les catalogues sont importés statiquement, donc embarqués dans le build.
  • packages/i18n/test/i18n.spec.ts : refuse une clé orpheline et une surcharge identique à la source, ce qui filtre un pull mal maîtrisé.
  • L’ADR 0026-tolgee-auto-heberge et l’ADR 0027-i18n-modele-a-et-contexte-auto dans saas-core-doc/decisions consignent le choix et ses conditions de réévaluation.

Une limite assumée : l’instance est mono-utilisateur et sa base n’est pas sauvegardée. Une traduction faite dans Tolgee et jamais rapatriée par pnpm i18n:pull peut se perdre. Une instance partagée entre plusieurs traducteurs exigerait une authentification sérieuse et une sauvegarde.

Les messages que Tolgee manipule sont au format ICU ; leur syntaxe (pluriels, arguments, dates) est détaillée dans ICU MessageFormat et pluriels dans un SaaS. Pour voir pourquoi aucun outil d’atelier n’apparaît dans la chaîne de livraison, consultez le pipeline CI/CD GitHub Actions du kit.

Le fichier Compose de Tolgee, la configuration du CLI et les scripts d’outillage i18n font partie du kit ; le détail de ce qui est inclus figure sur la page tarifs.