Перейти к содержимому
DedicatedPHP Контакт

Интернационализация PHP-приложения без дублирования домена

Руководство по отделению языка, форматов и контента от бизнес-правил при подготовке PHP-приложения для разных рынков.

Редакционная схема PHP-приложения, разделяющего язык, региональные форматы, часовой пояс и бизнес-правила

Интернационализация PHP-приложения — это не только перевод кнопок и сообщений. Приложение, подготовленное для нескольких языков или рынков, должно разделять язык, региональные форматы, часовой пояс, валюту, контент и бизнес-политики. Если эти слои смешаны, каждое расширение может превратиться в функциональное ответвление, которое трудно тестировать и поддерживать.

Что меняется при работе с несколькими языками и рынками

Что меняется при работе с несколькими языками и рынками — guía visual de DedicatedPHP

Язык определяет, как выражается текст. Региональная настройка, или locale, задаёт соглашения представления, такие как десятичный разделитель, порядок дат или группировка разрядов. Locale может включать регион, например es-ES или fr-CA, но этот регион не должен подменять рынок, юридическое лицо, резидентство, коммерческую политику или страну ведения деятельности.

Также следует различать контексты, которые часто передаются вместе, но означают разное:

  • Часовой пояс: интерпретирует расписания, сроки, календари и операционные закрытия.
  • Валюта: определяет сумму транзакции или прайс-листа; она не выводится из языка.
  • Организация или юридическое лицо: может определять налоги, разрешения, выставление счетов или хранение данных.
  • Рынок: может влиять на каталог, логистику, способы оплаты или доступные каналы.
  • Пользовательские предпочтения: выбранные язык, locale и часовой пояс, которые могут отличаться от корпоративной конфигурации.

Человек может использовать интерфейс на английском языке, работать в европейском часовом поясе и управлять организацией, которая выставляет счета в другой валюте. Сведение этой реальности к единственной переменной locale создаёт неявные решения.

Дорогостоящая ошибка: превращать язык в бизнес-правило

Плохой признак появляется, когда код принимает доменные решения на основе языка интерфейса: if ($locale === 'es'). Это условие может начаться с отображения другой метки и закончиться применением налогов, скрытием способа оплаты или изменением согласования.

Правильный вопрос: «какие данные или политика объясняют это различие?». Если оно зависит от юридического лица, необходимо обратиться к этому лицу. Если оно определяется коммерческой политикой, должна существовать идентифицируемая и версионируемая политика. Если оно влияет только на представление, ему место на границе ввода или вывода.

Язык переводит пользовательский опыт; он сам по себе не авторизует, не рассчитывает и не определяет поведение домена.

Что должно оставаться в домене

Домен должен работать со стабильными понятиями и каноническими значениями. Заказу нужны количества, суммы, позиции, статусы и правила расчёта; ему не нужно знать, будет ли сумма отображаться как 1,234.50 или 1.234,50. Политика соответствия условиям должна получать явные атрибуты, а не считывать пользовательское представление.

  • Домен: инварианты, состояния, расчёты, бизнес-авторизации, политики и события.
  • Приложение: сценарии использования, загрузка контекста, координация и выбор политик.
  • Входные адаптеры: формы, заголовки, API или файлы; валидация и нормализация.
  • Выходные адаптеры: перевод, сериализация и форматирование дат, сумм и единиц измерения.

В PHP не допускайте, чтобы сущности и центральные сервисы напрямую обращались к сессии, HTTP-заголовкам, переменным окружения или глобальному locale процесса. Такие зависимости приводят к тому, что один и тот же сценарий использования ведёт себя по-разному в зависимости от канала или времени выполнения.

Проектирование явного и ограниченного контекста

ExecutionContext может включать идентификатор организации, актора, locale представления и предпочитаемый часовой пояс. У каждого поля должна быть ясная семантика. Однако валюта операции должна быть частью суммы или применённой ценовой политики, а не глобальным изменяемым предпочтением.

Разрешайте контекст на границе каждого канала. Веб-запрос может использовать сохранённое предпочтение или контролируемое согласование; API должен получать явные и документированные поля; процесс в очереди должен сохранять требуемые идентификаторы при создании. Асинхронная задача не должна предполагать, что унаследует сессию, пользователя или часовой пояс.

Переводимый контент и операционные данные

Тексты интерфейса, шаблоны коммуникаций и редакционный контент имеют жизненный цикл, отличный от операционных данных. Используйте стабильные и семантические ключи, например billing.invoice.overdue, вместо исходного текста. Так можно изменить формулировку, не нарушая код, тесты или интеграции.

Управляемому контенту нужна публикация: перевод может существовать в черновике, быть утверждённым или опубликованным. Определите fallback: запрошенный язык, базовый язык организации и, при необходимости, видимое и контролируемое отсутствие. Альтернативный перевод может быть приемлем для внутренней заметки, но не обязательно для договорной коммуникации.

Не дублируйте полную операционную запись для каждого языка, если только данные не локализованы. У продукта могут быть переводимые название и описание, тогда как идентификатор, вес, статус и правила доступности остаются общими. Если существует реальное коммерческое различие, моделируйте его как вариант или политику, а не как перевод.

Даты, суммы, единицы измерения и округления

Храните моменты времени однозначно и сохраняйте часовой пояс, когда значение является локальным. «Встреча начинается в 09:00» требует знать пояс, в котором она была определена; «событие произошло в это время» требует абсолютного момента. Сезонные переходы создают несуществующие или повторяющиеся часы, поэтому ввод должен валидироваться, а принятое решение по разрешению неоднозначности должно фиксироваться, когда это уместно.

Для денег храните целое количество в младших единицах вместе с кодом валюты, но не предполагайте два десятичных знака. Масштаб или показатель определяется метаданными валюты, применимыми к операции. Некоторые валюты используют масштаб, отличный от двух, а исторические требования или требования платёжной сети могут потребовать сохранения фактического масштаба или версии использованного правила.

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

Масштаб позволяет правильно интерпретировать младшие единицы, но не заменяет политику округления. Определите момент округления, применяемый режим и правило наличных расчётов, если оно существует, поскольку кассовое округление может отличаться от бухгалтерского. Избегайте float, локализованных форматов во время расчётов и неявных преобразований.

Тот же подход применим к измерениям: сохраняйте исходную единицу, когда она имеет операционное значение, нормализуйте, когда этого требует расчёт, и конвертируйте только при вводе или представлении. Форма должна указывать допустимые единицу и формат; она не должна угадывать, означает ли 1,500 полтора или тысячу пятьсот.

Архитектура потоков ввода и вывода

Разделение слоёв должно быть видно в полном и повторяемом потоке:

  1. Захватить контекст и входные данные: получить организацию, актора, канал, locale, часовой пояс и полученное значение.
  2. Проверить допустимый формат: проверить обязательные поля, синтаксис, единицу, валюту, часовой пояс и ограничения канала.
  3. Нормализовать в канонические значения: преобразовать локализованный текст в однозначные суммы, даты, единицы и идентификаторы.
  4. Выполнить доменный сценарий использования: применить явные правила и политики к каноническим значениям.
  5. Перевести и отформатировать вывод: выбрать опубликованные сообщения и представить значения для получателя или контракта API.

API может решить принимать только канонические форматы, такие как даты с явно указанным поясом и структурированные суммы. Форма для человека может принимать локализованные форматы, если её парсер является явным. В обоих случаях домен получает одно и то же стабильное представление.

Настраиваемая политика или отдельное бизнес-правило

Вариация обычно настраиваема, если она использует общий процесс и меняет декларируемые параметры, например лимит, список праздничных дней или определённый конфигурацией метод расчёта. Такой конфигурации нужны схема, версия, ответственный и тесты.

Различие указывает на отдельное правило, когда оно изменяет инварианты, состояния, ответственность, источники данных или юридические последствия. В таком случае скрытие его в опциях создаёт непрозрачную конфигурацию. Смоделируйте политику через явный интерфейс или выделите отдельный поток, если процесс действительно отличается. Цель не в том, чтобы навязать единую абстракцию, а в том, чтобы не дублировать всё приложение из-за локализованного различия.

Тесты и контрольный список

Тесты и контрольный список — guía visual de DedicatedPHP

Тесты должны проверять расчёт и представление. Изменение языка или locale не должно менять итоговую сумму, авторизацию или коммерческую политику, если только это не установлено явным требованием.

  • Тестируйте даты при переходах времени, с неоднозначными и несуществующими часами.
  • Покрывайте нулевые и отрицательные суммы, разные масштабы, бухгалтерское и наличное округление.
  • Проверяйте разрешённый локализованный ввод и отклонение неоднозначных форматов.
  • Проверяйте fallback, отсутствие опубликованного контента и интерполированные переменные.
  • Запускайте асинхронные задачи без сессии, используя только сохранённый контекст.
  • Тестируйте контракты API с каноническими значениями и метаданными формата, когда они необходимы.

Перед открытием нового языка или рынка определите, что действительно меняется, разделите их источники истины, проверьте денежные и временные правила и вводите доступность постепенно, когда операции требуется контролируемая валидация. Эта дисциплина позволяет расширять приложение, не превращая каждый рынок в параллельную версию продукта.

Хотите применить эти идеи в своем проекте?Давайте обсудим вашу PHP-платформу.
Посмотреть связанные услуги