Passer au contenu
DedicatedPHP Contact

Internationaliser une application PHP sans dupliquer le domaine

Guide pour séparer la langue, les formats et le contenu des règles métier lors de la préparation d’une application PHP pour différents marchés.

Diagramme éditorial d’une application PHP séparant la langue, les formats régionaux, le fuseau horaire et les règles métier

Internationaliser une application PHP ne consiste pas seulement à traduire des boutons et des messages. Une application préparée pour plusieurs langues ou marchés doit séparer la langue, les formats régionaux, le fuseau horaire, la devise, le contenu et les politiques métier. Si ces couches sont mélangées, chaque expansion peut devenir une bifurcation fonctionnelle difficile à tester et à maintenir.

Ce qui change lorsque l’on opère dans plusieurs langues et marchés

Ce qui change lorsque l’on opère dans plusieurs langues et marchés — guía visual de DedicatedPHP

La langue détermine la manière dont un texte est exprimé. La configuration régionale, ou locale, définit des conventions de présentation telles que le séparateur décimal, l’ordre des dates ou le groupement des milliers. Un locale peut inclure une région, comme es-ES ou fr-CA, mais cette région ne doit pas se substituer au marché, à l’entité juridique, à la résidence, à la politique commerciale ni au pays d’exploitation.

Il convient également de distinguer des contextes qui sont souvent associés, mais qui n’ont pas la même signification :

  • Fuseau horaire : interprète les horaires, délais, agendas et clôtures opérationnelles.
  • Devise : identifie le montant d’une transaction ou d’une liste de prix ; elle ne se déduit pas de la langue.
  • Organisation ou entité juridique : peut déterminer les taxes, autorisations, la facturation ou la conservation des données.
  • Marché : peut conditionner le catalogue, la logistique, les moyens de paiement ou les canaux disponibles.
  • Préférences utilisateur : langue, locale et fuseau horaire choisis, qui peuvent différer de la configuration d’entreprise.

Une personne peut utiliser l’interface en anglais, travailler dans un fuseau horaire européen et gérer une organisation qui facture dans une autre devise. Réduire cette réalité à une unique variable locale crée des décisions implicites.

L’erreur coûteuse : faire de la langue une règle métier

Un mauvais signal apparaît lorsque le code prend des décisions de domaine à partir de la langue de l’interface : if ($locale === 'es'). Cette condition peut commencer par afficher un libellé différent et finir par appliquer des taxes, masquer un moyen de paiement ou modifier une approbation.

La bonne question est : « quelle donnée ou politique explique cette variation ? ». Si elle dépend d’une entité juridique, cette entité doit être consultée. Si elle répond à une politique commerciale, une politique identifiable et versionnable doit exister. Si elle n’affecte que la représentation, elle relève de la frontière d’entrée ou de sortie.

La langue traduit l’expérience ; elle n’autorise pas, ne calcule pas et ne définit pas à elle seule le comportement du domaine.

Ce qui doit rester dans le domaine

Le domaine doit travailler avec des concepts stables et des valeurs canoniques. Une commande a besoin de quantités, de montants, de lignes, d’états et de règles de calcul ; elle n’a pas besoin de savoir si un montant sera affiché sous la forme 1,234.50 ou 1.234,50. Une politique d’éligibilité doit recevoir des attributs explicites, et non lire la présentation de l’utilisateur.

  • Domaine : invariants, états, calculs, autorisations métier, politiques et événements.
  • Application : cas d’utilisation, chargement du contexte, coordination et sélection des politiques.
  • Adaptateurs d’entrée : formulaires, en-têtes, API ou fichiers ; validation et normalisation.
  • Adaptateurs de sortie : traduction, sérialisation et formatage des dates, montants et unités.

En PHP, évitez que les entités et services centraux consultent directement la session, les en-têtes HTTP, les variables d’environnement ou le locale global du processus. Ces dépendances font que le même cas d’utilisation se comporte différemment selon le canal ou le moment de l’exécution.

Concevoir un contexte explicite et limité

Un ExecutionContext peut inclure l’identifiant de l’organisation, l’acteur, le locale de présentation et le fuseau horaire préféré. Chaque champ doit avoir une sémantique claire. La devise d’une opération, en revanche, doit faire partie du montant ou de la politique tarifaire appliquée, et non d’une préférence globale mutable.

Résolvez le contexte à la frontière de chaque canal. Une requête web peut utiliser une préférence enregistrée ou une négociation contrôlée ; une API doit recevoir des champs explicites et documentés ; un processus en file d’attente doit persister les identifiants requis lors de sa création. Une tâche asynchrone ne doit pas supposer qu’elle héritera de la session, de l’utilisateur ou du fuseau horaire.

Contenu traduisible et données opérationnelles

Les textes d’interface, les templates de communication et le contenu éditorial ont un cycle de vie distinct de celui des données opérationnelles. Utilisez des clés stables et sémantiques, par exemple billing.invoice.overdue, plutôt que le texte d’origine. Il est ainsi possible de modifier la rédaction sans casser le code, les tests ou les intégrations.

Le contenu administrable nécessite une publication : une traduction peut exister à l’état de brouillon, être approuvée ou publiée. Définissez le fallback : langue demandée, langue de base de l’organisation et, le cas échéant, une absence visible et contrôlée. Une traduction alternative peut être acceptable pour une note interne, mais pas nécessairement pour une communication contractuelle.

Ne dupliquez pas un enregistrement opérationnel complet par langue, sauf si la donnée est localisée. Un produit peut avoir un nom et une description traduisibles, tandis que l’identifiant, le poids, l’état et les règles de disponibilité restent communs. S’il existe une véritable différence commerciale, modélisez-la comme une variante ou une politique, et non comme une traduction.

Dates, montants, unités et arrondis

Stockez les instants temporels de manière non ambiguë et conservez le fuseau horaire lorsque la signification est locale. « La réunion commence à 09:00 » exige de connaître le fuseau dans lequel elle a été définie ; « l’événement s’est produit à cette heure » requiert un instant absolu. Les changements saisonniers produisent des heures inexistantes ou répétées ; l’entrée doit donc être validée et la résolution adoptée doit être enregistrée lorsque cela est nécessaire.

Pour l’argent, stockez une quantité entière en unités mineures avec le code devise, mais ne supposez pas deux décimales. L’échelle ou l’exposant provient des métadonnées de devise applicables à l’opération. Certaines devises utilisent une échelle différente de deux, et les exigences historiques ou celles d’un réseau de paiement peuvent imposer de conserver l’échelle effective ou une version de la règle utilisée.

final class Money {
    public function __construct(
        public readonly int $minorUnits,
        public readonly string $currency,
        public readonly int $scale
    ) {}
}

L’échelle permet d’interpréter correctement les unités mineures, mais elle ne remplace pas une politique d’arrondi. Définissez le point d’arrondi, le mode appliqué et la règle d’arrondi en espèces lorsqu’elle existe, car l’arrondi de caisse peut différer de l’arrondi comptable. Évitez les float, les formats localisés pendant le calcul et les conversions implicites.

La même pratique s’applique aux mesures : conservez l’unité d’origine lorsqu’elle a une signification opérationnelle, normalisez lorsque le calcul l’exige et ne convertissez qu’à la saisie ou à la présentation. Un formulaire doit indiquer l’unité et le format autorisés ; il ne doit pas deviner si 1,500 signifie un et demi ou mille cinq cents.

Architecture des flux d’entrée et de sortie

La séparation des couches doit être visible dans un flux complet et reproductible :

  1. Capturer le contexte et la donnée d’entrée : obtenir l’organisation, l’acteur, le canal, le locale, le fuseau horaire et la valeur reçue.
  2. Valider le format autorisé : vérifier les champs obligatoires, la syntaxe, l’unité, la devise, le fuseau horaire et les restrictions du canal.
  3. Normaliser en valeurs canoniques : convertir le texte localisé en montants, dates, unités et identifiants non ambigus.
  4. Exécuter le cas d’utilisation du domaine : appliquer des règles et politiques explicites sur des valeurs canoniques.
  5. Traduire et formater la sortie : choisir les messages publiés et représenter les valeurs pour le destinataire ou le contrat d’API.

Une API peut décider de n’accepter que des formats canoniques, tels que des dates avec un fuseau explicite et des montants structurés. Un formulaire destiné à des humains peut accepter des formats localisés, à condition que son parseur soit explicite. Dans les deux cas, le domaine reçoit la même représentation stable.

Politique configurable ou règle métier distincte

Une variation est généralement configurable si elle partage le processus et modifie des paramètres déclarables, comme une limite, une liste de jours fériés ou une méthode de calcul définie par configuration. Cette configuration nécessite un schéma, une version, un responsable et des tests.

La différence révèle une règle distincte lorsqu’elle modifie les invariants, les états, les responsabilités, les sources de données ou les conséquences juridiques. Dans ce cas, la masquer dans des options crée une configuration opaque. Modélisez une politique au moyen d’une interface explicite, ou d’un flux séparé si le processus est réellement différent. L’objectif n’est pas de forcer une abstraction unique, mais d’éviter de dupliquer toute l’application pour une différence localisée.

Tests et liste de vérification

Tests et liste de vérification — guía visual de DedicatedPHP

Les tests doivent vérifier le calcul et la représentation. Changer de langue ou de locale ne doit pas modifier un total, une autorisation ou une politique commerciale, sauf si une exigence explicite l’établit.

  • Testez les dates lors des changements d’heure, avec des heures ambiguës et inexistantes.
  • Couvrez les montants nuls, négatifs, les différentes échelles, l’arrondi comptable et de caisse.
  • Vérifiez les entrées localisées autorisées et le rejet des formats ambigus.
  • Vérifiez le fallback, l’absence de contenu publié et les variables interpolées.
  • Exécutez les tâches asynchrones sans session, en utilisant uniquement le contexte persisté.
  • Testez les contrats d’API avec des valeurs canoniques et des métadonnées de format lorsqu’elles sont nécessaires.

Avant d’ouvrir une langue ou un marché, identifiez ce qui change réellement, séparez ses sources de vérité, examinez les règles monétaires et temporelles, et activez la disponibilité de manière progressive lorsque l’exploitation nécessite une validation contrôlée. Cette discipline permet d’étendre l’application sans transformer chaque marché en une version parallèle du produit.

Vous souhaitez appliquer ces idées à votre projet ?Parlons de votre plateforme PHP.
Afficher les services associés