Ir para o conteúdo
DedicatedPHP Contato

Internacionalizar uma aplicação PHP sem duplicar o domínio

Guia para separar idioma, formatos e conteúdo das regras de negócio ao preparar uma aplicação PHP para diferentes mercados.

Diagrama editorial de uma aplicação PHP que separa idioma, formatos regionais, fuso horário e regras de negócio

Internacionalizar uma aplicação PHP não consiste apenas em traduzir botões e mensagens. Uma aplicação preparada para vários idiomas ou mercados deve separar idioma, formatos regionais, fuso horário, moeda, conteúdo e políticas de negócio. Se essas camadas se misturarem, cada expansão pode se tornar uma bifurcação funcional difícil de testar e manter.

O que muda ao operar em vários idiomas e mercados

O que muda ao operar em vários idiomas e mercados — guía visual de DedicatedPHP

O idioma determina como um texto é expresso. A configuração regional, ou locale, define convenções de apresentação, como o separador decimal, a ordem das datas ou o agrupamento de milhares. Um locale pode incluir uma região, como es-ES ou fr-CA, mas essa região não deve substituir o mercado, a entidade legal, a residência, a política comercial nem o país de operação.

Também é conveniente distinguir contextos que costumam vir juntos, mas não significam a mesma coisa:

  • Fuso horário: interpreta horários, prazos, agendas e fechamentos operacionais.
  • Moeda: identifica o valor de uma transação ou lista de preços; não é inferida a partir do idioma.
  • Organização ou entidade legal: pode determinar impostos, permissões, faturamento ou retenção de dados.
  • Mercado: pode condicionar catálogo, logística, métodos de pagamento ou canais disponíveis.
  • Preferências do usuário: idioma, locale e fuso horário escolhidos, que podem diferir da configuração corporativa.

Uma pessoa pode usar a interface em inglês, trabalhar em um fuso horário europeu e administrar uma organização que fatura em outra moeda. Reduzir essa realidade a uma única variável locale cria decisões implícitas.

O erro caro: transformar o idioma em regra de negócio

Um mau sinal aparece quando o código toma decisões de domínio a partir do idioma da interface: if ($locale === 'es'). Esse condicional pode começar exibindo um rótulo diferente e terminar aplicando impostos, ocultando um método de pagamento ou alterando uma aprovação.

A pergunta correta é: “que dado ou política explica essa variação?”. Se ela depende de uma entidade legal, essa entidade deve ser consultada. Se responde a uma política comercial, deve existir uma política identificável e versionável. Se afeta apenas a representação, pertence à fronteira de entrada ou saída.

O idioma traduz a experiência; não autoriza, calcula nem define por si só o comportamento do domínio.

O que deve permanecer no domínio

O domínio deve trabalhar com conceitos estáveis e valores canônicos. Um pedido precisa de quantidades, valores, linhas, estados e regras de cálculo; não precisa saber se um valor será exibido como 1,234.50 ou 1.234,50. Uma política de elegibilidade deve receber atributos explícitos, não ler a apresentação do usuário.

  • Domínio: invariantes, estados, cálculos, autorizações de negócio, políticas e eventos.
  • Aplicação: casos de uso, carregamento de contexto, coordenação e seleção de políticas.
  • Adaptadores de entrada: formulários, cabeçalhos, APIs ou arquivos; validação e normalização.
  • Adaptadores de saída: tradução, serialização e formatação de datas, valores e unidades.

Em PHP, evite que entidades e serviços centrais consultem diretamente sessão, cabeçalhos HTTP, variáveis de ambiente ou o locale global do processo. Essas dependências fazem com que o mesmo caso de uso se comporte de forma diferente conforme o canal ou o momento da execução.

Projetar um contexto explícito e limitado

Um ExecutionContext pode incluir identificador da organização, ator, locale de apresentação e fuso horário preferido. Cada campo deve ter semântica clara. A moeda de uma operação, porém, deve fazer parte do valor ou da política de preços aplicada, não de uma preferência global mutável.

Resolva o contexto na borda de cada canal. Uma requisição web pode usar uma preferência salva ou negociação controlada; uma API deve receber campos explícitos e documentados; um processo em fila deve persistir os identificadores necessários ao ser criado. Um trabalho assíncrono não deve supor que herdará sessão, usuário nem fuso horário.

Conteúdo traduzível e dados operacionais

Os textos de interface, templates de comunicação e conteúdo editorial têm um ciclo de vida diferente dos dados operacionais. Use chaves estáveis e semânticas, por exemplo billing.invoice.overdue, em vez do texto original. Assim, é possível alterar a redação sem quebrar código, testes ou integrações.

O conteúdo gerenciável requer publicação: uma tradução pode existir como rascunho, estar aprovada ou publicada. Defina o fallback: idioma solicitado, idioma base da organização e, se aplicável, uma ausência visível e controlada. Uma tradução alternativa pode ser aceitável para uma nota interna, mas não necessariamente para uma comunicação contratual.

Não duplique um registro operacional completo por idioma, a menos que o dado seja localizado. Um produto pode ter nome e descrição traduzíveis, enquanto identificador, peso, estado e regras de disponibilidade permanecem comuns. Se houver uma diferença comercial real, modele-a como variante ou política, não como tradução.

Datas, valores, unidades e arredondamentos

Armazene instantes temporais de forma inequívoca e mantenha o fuso horário quando o significado for local. “A reunião começa às 09:00” exige conhecer o fuso em que foi definida; “o evento ocorreu neste horário” requer um instante absoluto. As mudanças sazonais produzem horas inexistentes ou repetidas; portanto, a entrada deve ser validada e a resolução adotada deve ser registrada quando aplicável.

Para dinheiro, armazene uma quantidade inteira em unidades menores junto com o código da moeda, mas não suponha duas casas decimais. A escala ou expoente provém dos metadados da moeda aplicáveis à operação. Algumas moedas usam uma escala diferente de duas, e requisitos históricos ou de uma rede de pagamento podem exigir a preservação da escala efetiva ou de uma versão da regra usada.

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

A escala permite interpretar corretamente as unidades menores, mas não substitui uma política de arredondamento. Defina o ponto de arredondamento, o modo aplicado e a regra de numerário quando existir, pois o arredondamento de caixa pode diferir do arredondamento contábil. Evite float, formatos localizados durante o cálculo e conversões implícitas.

A mesma diretriz serve para medidas: guarde a unidade original quando ela tiver significado operacional, normalize quando o cálculo exigir e converta apenas ao capturar ou apresentar. Um formulário deve indicar a unidade e os formatos permitidos; não deve adivinhar se 1,500 significa um e meio ou mil e quinhentos.

Arquitetura dos fluxos de entrada e saída

A separação de camadas deve ser visível em um fluxo completo e repetível:

  1. Capturar o contexto e o dado de entrada: obter organização, ator, canal, locale, fuso horário e o valor recebido.
  2. Validar o formato permitido: verificar campos obrigatórios, sintaxe, unidade, moeda, fuso horário e restrições do canal.
  3. Normalizar para valores canônicos: converter texto localizado em valores, datas, unidades e identificadores inequívocos.
  4. Executar o caso de uso de domínio: aplicar regras e políticas explícitas sobre valores canônicos.
  5. Traduzir e formatar a saída: escolher mensagens publicadas e representar valores para o destinatário ou contrato da API.

Uma API pode decidir aceitar apenas formatos canônicos, como datas com fuso explícito e valores estruturados. Um formulário destinado a pessoas pode aceitar formatos localizados, desde que seu parser seja explícito. Em ambos os casos, o domínio recebe a mesma representação estável.

Política configurável ou regra de negócio distinta

Uma variação costuma ser configurável se compartilha o processo e altera parâmetros declaráveis, como um limite, uma lista de feriados ou um método de cálculo definido por configuração. Essa configuração precisa de esquema, versão, responsável e testes.

A diferença revela uma regra distinta quando modifica invariantes, estados, responsabilidades, fontes de dados ou consequências legais. Nesse caso, escondê-la em opções cria uma configuração opaca. Modele uma política por meio de uma interface explícita ou de um fluxo separado se o processo for realmente diferente. O objetivo não é forçar uma abstração única, mas evitar duplicar toda a aplicação por uma diferença localizada.

Testes e lista de verificação

Testes e lista de verificação — guía visual de DedicatedPHP

Os testes devem verificar cálculo e representação. Alterar o idioma ou locale não deve modificar um total, uma autorização ou uma política comercial, a menos que um requisito explícito o determine.

  • Teste datas em mudanças de horário, com horas ambíguas e inexistentes.
  • Inclua valores zero, negativos, escalas diferentes, arredondamento contábil e de caixa.
  • Verifique entradas localizadas permitidas e a rejeição de formatos ambíguos.
  • Verifique fallback, ausência de conteúdo publicado e variáveis interpoladas.
  • Execute trabalhos assíncronos sem sessão, usando apenas o contexto persistido.
  • Teste contratos de API com valores canônicos e metadados de formato quando forem necessários.

Antes de disponibilizar um idioma ou mercado, identifique o que realmente muda, separe suas fontes de verdade, revise regras monetárias e temporais e ative a disponibilidade gradualmente quando a operação exigir validação controlada. Essa disciplina permite expandir a aplicação sem transformar cada mercado em uma versão paralela do produto.

Deseja aplicar essas ideias ao seu projeto?Vamos discutir sua plataforma PHP.
Veja os serviços relacionados