TheCodeNaked

TLocalizer4D (Copy)

Introdução

Uma situação real para mim aqui no Japão desenvolvendo aplicativos é não ter domínio do idioma japonês. Dessa forma ao, por exemplo, enviar mensagens ao usuário em japonês, quando eu mesmo estou realizando testes, debug, muitas vezes não sei do que se trata a mensagem. É isso é vida real de desenvolvedor. Poderia utilizar ferramentas para tradução e traduzir o aplicativo inteiro, mas não preciso disso. Ao contrário do que muitos podem pensar, aqui no Japão só começa agora aparecerem aplicativos multiidioma. Já encontramos em hospitais e algumas lojas. Mas no meu caso desenvolvendo aplicativos administrativos para empresas japonesas essas empresas só querem em japonês mesmo.

Então resolvi criar um mecanismo que pudesse resolver o meu problema específico, ser leve eficiente e que não demandasse muito tempo para criá-lo. Resolvi utilizar o mesmo que muitos aplicativos web utilizam, ter um arquivo de idiomas e consumi-los.

Título (SEO)

Tradução FMX sem drama: Singleton de instância + “EN|JA|PT” em resourcestring (e por que não quebrei meu código)

Subtítulo

Uma abordagem pragmática para i18n em Delphi/FMX: helpers mínimos, portável para Windows/Android/iOS/macOS, e zero “mágica”.

TL;DR

  • Usei resourcestring contendo "EN|JA|PT" e um TranslationManager que seleciona o trecho certo conforme idioma.
  • Tornei o manager um singleton de instância sem class var (armazeno a instância em variáveis de módulo), para evitar o infame E2356.
  • Ergonomia com helper _() (ou String Helper .T/.TF).
  • Portável (FMX): funciona em Windows + mobile; Resource DLLs ficam como alternativa somente Windows.

Motivação

  • Queria i18n rápidoportável e sem vendor lock.
  • resourcestring + Resource DLL é oficial, mas Windows-only.
  • TLang é legal, mas precisei de menor atrito e placeholders simples (%s).
  • Solução: "EN|JA|PT" em resourcestring + TranslationManager.

O design (em 60 segundos)

  • Cada resourcestring guarda as 3 versões: EN|JA|PT.
  • TranslationManager.GetLocalizedString faz Split('|'), escolhe pelo idioma, remove pontuação final (opcional) e aplica Format.

Uso:

GlobalTranslationManager.SetLanguage(langPortuguese);
ShowMessage(_(rsMSG_APT_FOUND));

Helpers globais para ergonomia:

function _(const rs: string): string;
begin
  Result := TTranslationManager.Instance.GetLocalizedString(rs);
end;

Singleton de instância com variáveis de módulo:

// implementation
var
  GInstance: TTranslationManager;
  GLock: TObject;

class function TTranslationManager.Instance: TTranslationManager;
begin
  if GInstance = nil then
  begin
    TMonitor.Enter(GLock);
    try
      if GInstance = nil then
        GInstance := TTranslationManager.Create(langJapanese);
    finally
      TMonitor.Exit(GLock);
    end;
  end;
  Result := GInstance;
end;
Por que variáveis de módulo? Porque misturar class var com accessors de properties de instânciacostuma levar ao E2356. Mantendo  membros de instância dentro da classe, o compilador não confunde nada.

Ponto chave: evitar o E2356

O erro “Property accessor must be an instance field or method” aparece quando você:

  • Declara class var FCurrentLanguage e tenta usá-lo como read FCurrentLanguage numa property de instância, ou
  • Usa class procedure SetLanguage como write SetLanguage numa property de instância.

Fix: mantenha FCurrentLanguage e SetCurrentLanguage de instância; leve o estado do singleton (GInstanceGLock) para o escopo da unit (implementation). Pronto.


Ergonomia: _() vs String Helper

String Helper (opcional):

rsMSG_APT_FOUND.T
rsMSG_APT_FLOOR_INFO.TF(['101','3','A'])

_(): curtos e claros.

ShowMessage(_(rsMSG_APT_FLOOR_INFO, ['101','3','A']));

Traduzindo a UI inteira

Use TagString dos controles com “EN|JA|PT” e percorra a árvore:

procedure TranslateControls(const Root: TFmxObject);
begin
  // se houver 'Text' e TagString, aplica _(TagString) em Text
end;

Chame ao trocar o idioma (e/ou amarre no OnLanguageChanged do manager).


Portabilidade e alternativas

  • Esta solução: funciona em Windows, Android, iOS, macOS (FMX).
  • Resource DLLs (oficial Delphi): ótimo para Windows puro, elimina _(), mas não cobre mobile; carrega via LoadNewResourceModule.
  • TLang: boa integração FMX, arquivos de tradução, mas muda sua rotina de strings; escolha se quiser alinhar ao ecossistema FMX.

Armadilhas & Dicas

  • Placeholders: mantenha a mesma ordem nas 3 línguas (%s/%d).
  • Pipes literais: se precisar de | no texto, defina um escape (\|) e trate antes do Split.
  • Pontuação final: eu removo (.   ). Ajuste conforme sua UI.
  • Ordem de finalização: ok deixar o SO limpar (apps), mas liberar em finalization também funciona — evite acessar o manager depois disso.
  • Cache: para UIs grandes, cacheie rs → tradução por idioma.

Quando não usar este padrão

  • Se você precisa de tradução por arquivos/DLL gerenciados por tradutores externos e quer dispensar qualquer Split.
  • Se sua equipe quer padronizar em TLang e arquivos *.strings.

Checklist (para copiar e colar no projeto)

  •  resourcestring com "EN|JA|PT"
  •  TranslationManager (singleton de instância via GInstance/GLock)
  •  Helpers _() ou String Helper .T/.TF
  •  TranslateControls(Self) quando trocar idioma
  •  Tests: placeholders, fallback, pontuação, \|

Conclusão

i18n não precisa virar um projeto. Com resourcestring + TranslationManager você obtém uma solução simplesportável e sob controle — e ainda pode migrar para Resource DLLs depois se (e só se) fizer sentido.


Tags

delphi · fmx · i18n · localization · design-patterns · singleton · thecodenaked

Quer que eu já gere o post completo com trechos de código prontos (incluindo o TranslateControls), num arquivo .md?

Sobre o autor

TheCodeNaked

No TheCodeNaked, programar é consequência, não ponto de partida. Antes do código, vem a dúvida, a análise, o contexto. Não seguimos fórmulas — questionamos. Criar software é pensar com clareza. O resto é só digitação.

TheCodeNaked

Criar com clareza. Codificar com intenção.

TheCodeNaked

Ótimo! Você se inscreveu com sucesso.

Bem-vindo de volta! Você acessou com sucesso.

Você se inscreveu com sucesso o TheCodeNaked.

Sucesso! Verifique seu e-mail para acessar com o link mágico.

As suas informações de faturamento foram atualizadas.

Seu pagamento não foi atualizado