Saltar al contenido
DedicatedPHP Contactar

Internacionalizar una aplicación PHP sin duplicar el dominio

Guía para separar idioma, formatos y contenido de las reglas de negocio al preparar una aplicación PHP para distintos mercados.

Diagrama editorial de una aplicación PHP que separa idioma, formatos regionales, zona horaria y reglas de negocio

Internacionalizar una aplicación PHP no consiste sólo en traducir botones y mensajes. Una aplicación preparada para varios idiomas o mercados debe separar idioma, formatos regionales, zona horaria, moneda, contenido y políticas de negocio. Si estas capas se mezclan, cada expansión puede convertirse en una bifurcación funcional difícil de probar y mantener.

Qué cambia al operar en varios idiomas y mercados

Qué cambia al operar en varios idiomas y mercados — guía visual de DedicatedPHP

El idioma determina cómo se expresa un texto. La configuración regional, o locale, define convenciones de presentación como el separador decimal, el orden de las fechas o la agrupación de miles. Un locale puede incluir una región, como es-ES o fr-CA, pero esa región no debe sustituir al mercado, la entidad legal, la residencia, la política comercial ni el país operativo.

También conviene distinguir contextos que suelen viajar juntos, pero no significan lo mismo:

  • Zona horaria: interpreta horarios, plazos, agendas y cierres operativos.
  • Moneda: identifica el importe de una transacción o lista de precios; no se infiere del idioma.
  • Organización o entidad legal: puede determinar impuestos, permisos, facturación o retención de datos.
  • Mercado: puede condicionar catálogo, logística, métodos de pago o canales disponibles.
  • Preferencias de usuario: idioma, locale y zona horaria elegidos, que pueden diferir de la configuración corporativa.

Una persona puede usar la interfaz en inglés, trabajar en una zona horaria europea y gestionar una organización que factura en otra moneda. Reducir esa realidad a una única variable locale crea decisiones implícitas.

El error costoso: convertir el idioma en regla de negocio

Una mala señal aparece cuando el código toma decisiones de dominio a partir del idioma de interfaz: if ($locale === 'es'). Ese condicional puede empezar mostrando una etiqueta distinta y terminar aplicando impuestos, ocultando un método de pago o cambiando una aprobación.

La pregunta correcta es: “¿qué dato o política explica esta variación?”. Si depende de una entidad legal, debe consultarse esa entidad. Si responde a una política comercial, debe existir una política identificable y versionable. Si sólo afecta a la representación, pertenece al límite de entrada o salida.

El idioma traduce la experiencia; no autoriza, calcula ni define por sí mismo el comportamiento del dominio.

Qué debe permanecer en el dominio

El dominio debe trabajar con conceptos estables y valores canónicos. Un pedido necesita cantidades, importes, líneas, estados y reglas de cálculo; no necesita saber si un importe se mostrará como 1,234.50 o 1.234,50. Una política de elegibilidad debe recibir atributos explícitos, no leer la presentación del usuario.

  • Dominio: invariantes, estados, cálculos, autorizaciones de negocio, políticas y eventos.
  • Aplicación: casos de uso, carga de contexto, coordinación y selección de políticas.
  • Adaptadores de entrada: formularios, cabeceras, APIs o archivos; validación y normalización.
  • Adaptadores de salida: traducción, serialización y formato de fechas, importes y unidades.

En PHP, evite que entidades y servicios centrales consulten directamente sesión, cabeceras HTTP, variables de entorno o el locale global del proceso. Esas dependencias hacen que el mismo caso de uso se comporte de forma distinta según el canal o el momento de ejecución.

Diseñar un contexto explícito y limitado

Un ExecutionContext puede incluir identificador de organización, actor, locale de presentación y zona horaria preferida. Cada campo debe tener semántica clara. La moneda de una operación, sin embargo, debe formar parte del importe o de la política de precios aplicada, no de una preferencia global mutable.

Resuelva el contexto en el borde de cada canal. Una petición web puede usar una preferencia guardada o negociación controlada; una API debe recibir campos explícitos y documentados; un proceso en cola debe persistir los identificadores requeridos al crearse. Un trabajo asíncrono no debe asumir que heredará sesión, usuario ni zona horaria.

Contenido traducible y datos operativos

Los textos de interfaz, plantillas de comunicación y contenido editorial tienen un ciclo de vida distinto de los datos operativos. Use claves estables y semánticas, por ejemplo billing.invoice.overdue, en lugar del texto original. Así se puede cambiar la redacción sin romper código, pruebas o integraciones.

El contenido administrable requiere publicación: una traducción puede existir en borrador, estar aprobada o publicada. Defina el fallback: idioma solicitado, idioma base de la organización y, si procede, una ausencia visible y controlada. Una traducción alternativa puede ser aceptable para una nota interna, pero no necesariamente para una comunicación contractual.

No duplique un registro operativo completo por idioma salvo que el dato sea localizado. Un producto puede tener nombre y descripción traducibles, mientras identificador, peso, estado y reglas de disponibilidad siguen siendo comunes. Si existe una diferencia comercial real, modélela como variante o política, no como traducción.

Fechas, importes, unidades y redondeos

Almacene instantes temporales de forma inequívoca y conserve la zona horaria cuando el significado sea local. “La reunión empieza a las 09:00” exige conocer la zona donde se definió; “el evento ocurrió a esta hora” requiere un instante absoluto. Los cambios estacionales producen horas inexistentes o repetidas, por lo que la entrada debe validarse y la resolución adoptada debe quedar registrada cuando corresponda.

Para dinero, almacene una cantidad entera en unidades menores junto con el código de moneda, pero no suponga dos decimales. La escala o exponente procede de los metadatos de la moneda aplicables a la operación. Algunas monedas usan una escala distinta de dos, y los requisitos históricos o de una red de pago pueden requerir conservar la escala efectiva o una versión de la regla utilizada.

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

La escala permite interpretar correctamente las unidades menores, pero no reemplaza una política de redondeo. Defina el punto de redondeo, el modo aplicado y la regla de efectivo cuando exista, ya que el redondeo de caja puede diferir del redondeo contable. Evite float, formatos localizados durante el cálculo y conversiones implícitas.

La misma pauta sirve para medidas: guarde la unidad original cuando tenga significado operativo, normalice cuando el cálculo lo requiera y convierta sólo al capturar o presentar. Un formulario debe indicar la unidad y el formato permitidos; no debe adivinar si 1,500 significa uno y medio o mil quinientos.

Arquitectura de flujos de entrada y salida

La separación de capas debe verse en un flujo completo y repetible:

  1. Capturar contexto y dato de entrada: obtener organización, actor, canal, locale, zona horaria y el valor recibido.
  2. Validar el formato permitido: comprobar campos obligatorios, sintaxis, unidad, moneda, zona horaria y restricciones del canal.
  3. Normalizar a valores canónicos: convertir texto localizado en importes, fechas, unidades e identificadores inequívocos.
  4. Ejecutar el caso de uso de dominio: aplicar reglas y políticas explícitas sobre valores canónicos.
  5. Traducir y formatear la salida: elegir mensajes publicados y representar valores para el destinatario o contrato de API.

Una API puede decidir aceptar únicamente formatos canónicos, como fechas con zona explícita e importes estructurados. Un formulario humano puede aceptar formatos localizados, siempre que su parser sea explícito. En ambos casos, el dominio recibe la misma representación estable.

Política configurable o regla de negocio distinta

Una variación suele ser configurable si comparte proceso y cambia parámetros declarables, como un límite, una lista de festivos o un método de cálculo definido por configuración. Esa configuración necesita esquema, versión, responsable y pruebas.

La diferencia revela una regla distinta cuando modifica invariantes, estados, responsabilidades, fuentes de datos o consecuencias legales. En ese caso, esconderla en opciones crea una configuración opaca. Modele una política mediante una interfaz explícita, o un flujo separado si el proceso es realmente diferente. El objetivo no es forzar una abstracción única, sino evitar duplicar toda la aplicación por una diferencia localizada.

Pruebas y lista de comprobación

Pruebas y lista de comprobación — guía visual de DedicatedPHP

Las pruebas deben verificar cálculo y representación. Cambiar idioma o locale no debe alterar un total, una autorización o una política comercial, salvo que un requisito explícito lo establezca.

  • Pruebe fechas en cambios de horario, con horas ambiguas e inexistentes.
  • Cubra importes cero, negativos, escalas distintas, redondeo contable y de efectivo.
  • Compruebe entradas localizadas permitidas y rechazo de formatos ambiguos.
  • Verifique fallback, ausencia de contenido publicado y variables interpoladas.
  • Ejecute trabajos asíncronos sin sesión, usando sólo contexto persistido.
  • Pruebe contratos de API con valores canónicos y metadatos de formato cuando sean necesarios.

Antes de abrir un idioma o mercado, identifique qué cambia realmente, separe sus fuentes de verdad, revise reglas monetarias y temporales, y active la disponibilidad de forma gradual cuando la operación requiera validación controlada. Esta disciplina permite ampliar la aplicación sin convertir cada mercado en una versión paralela del producto.

¿Quieres aplicar estas ideas a tu proyecto?Hablemos de tu plataforma PHP.
Ver servicio relacionado