Imported from israelita-felipe/ia-core (
ia-core/.aiassistant/skills/implementing-internationalization-with-translator/SKILL.md). Install upstream withnpx skills add israelita-felipe/ia-core --skill implementing-internationalization-with-translator. Copyright stays with the author.
Implementando Internacionalização com Translator
Implementando i18n usando o padrão Translator com constantes tipadas e integração automática.
💡 Quando Usar Esta Skill
- Ao criar um novo domínio (entidade/DTO) que precisa de internacionalização
- Ao adicionar labels, validações ou mensagens a um domínio existente
- Ao criar DTOs com validações Jakarta precisando de mensagens customizadas
- Ao implementar regras de negócio com mensagens internacionalizadas
- Ao adicionar texto de ajuda ou tooltips à interface
- Ao criar filtros de busca com labels internacionalizados
- Ao implementar eventos de domínio com mensagens
- Ao refatorar código hardcoded para usar i18n
⚡ Referência Rápida
Estrutura do Translator: Classe final com construtor privado, classes internas (HELP, VALIDATION, RULE, MESSAGE, EVENT)
Padrão de Chave: DOMAIN.key.subkey (ex: pessoa.validation.nome.not.blank)
Arquivo de Propriedades: Organizado por domínio com seções, em src/main/resources/i18n/
Integração DTO: Use constantes Translator em anotações de validação Jakarta
Integração Service: Use constantes Translator em exceções de regras de negócio
Integração View: Use $(Translator.KEY) em views Vaadin
📝 Exemplos de Código
Exemplo 1: Criando Classe Translator
Antes (Strings hardcoded - ❌):
// In DTO
@NotNull(message = "O título é obrigatório")
private String titulo;
// In Service
throw new ValidationException("Email já cadastrado");
// In Controller
notification.show("Pessoa criada com sucesso");
Depois (Translator - ✅):
public final class PessoaTranslator {
private PessoaTranslator() {
// Utility class
}
public static final class HELP {
public static final String PESSOA = "pessoa.help";
public static final String NOME = "pessoa.help.nome";
public static final String EMAIL = "pessoa.help.email";
}
public static final class VALIDATION {
public static final String NOME_NOT_BLANK = "pessoa.validation.nome.not.blank";
public static final String EMAIL_NOT_NULL = "pessoa.validation.email.not.null";
}
public static final class RULE {
public static final String EMAIL_UNICO = "pessoa.rule.email.unico";
}
public static final class MESSAGE {
public static final String CRIADO_SUCESSO = "pessoa.message.criado.sucesso";
}
public static final String PESSOA_CLASS = PessoaDTO.class.getCanonicalName();
public static final String PESSOA = "pessoa";
public static final String NOME = "pessoa.nome";
}
Exemplo 2: Criando Arquivo de Propriedades
#####################################################
# PESSOA
#####################################################
com.ia.biblia.service.pessoa.dto.PessoaDTO=Pessoa
pessoa=Pessoa
pessoa.nome=Nome
pessoa.email=E-mail
# PESSOA HELP
pessoa.help=Pessoa física ou jurídica
pessoa.help.nome=Nome completo da pessoa
# PESSOA - VALIDATIONS
pessoa.validation.nome.not.blank=O nome não pode estar em branco
pessoa.validation.email.not.null=O e-mail é obrigatório
# PESSOA - BUSINESS RULES
pessoa.rule.email.unico=E-mail já cadastrado no sistema
# PESSOA - MESSAGES
pessoa.message.criado.sucesso=Pessoa criada com sucesso
Exemplo 3: Usando em DTO com Validações Jakarta
@Data
public class PessoaDTO {
@NotBlank(message = PessoaTranslator.VALIDATION.NOME_NOT_BLANK)
private String nome;
@NotNull(message = PessoaTranslator.VALIDATION.EMAIL_NOT_NULL)
private String email;
}
Exemplo 4: Usando no Serviço para Regras de Negócio
@Service
@Slf4j
public class PessoaService {
public PessoaDTO salvar(PessoaDTO dto) {
log.debug("Salvando pessoa: {}", dto.getNome());
validarEmailUnico(dto.getEmail());
Pessoa entity = mapper.toEntity(dto);
Pessoa saved = repository.save(entity);
eventPublisher.publishEvent(new PessoaCriadaEvent(saved.getId()));
return mapper.toDTO(saved);
}
private void validarEmailUnico(String email) {
if (repository.existsByEmail(email)) {
throw new BusinessException(PessoaTranslator.RULE.EMAIL_UNICO);
}
}
}
Exemplo 5: Usando no Controller para Mensagens
@RestController
@RequestMapping("/api/${api.version}/pessoas")
public class PessoaController {
@PostMapping
public ResponseEntity<PessoaDTO> criar(@Valid @RequestBody PessoaDTO dto) {
PessoaDTO criado = service.salvar(dto);
String mensagem = translator.translate(PessoaTranslator.MESSAGE.CRIADO_SUCESSO);
return ResponseEntity
.status(HttpStatus.CREATED)
.header("X-Message", mensagem)
.body(criado);
}
}
Exemplo 6: Usando em Filtros de Busca
public class PessoaSearchRequest extends SearchRequestDTO {
protected PessoaSearchRequest() {
createFilters(filterMap, PessoaTranslator.NOME,
PessoaDTO.CAMPOS.NOME, FieldType.STRING,
OperatorDTO.LIKE, OperatorDTO.EQUAL);
}
}
Exemplo 7: Usando em View Vaadin
@Route("pessoas")
public class PessoaView extends VerticalLayout implements HasTranslator {
private final TextField nomeField = new TextField();
public PessoaView(PessoaService service) {
configurarCampos();
}
private void configurarCampos() {
nomeField.setLabel($(PessoaTranslator.NOME));
nomeField.setHelperText($(PessoaTranslator.HELP.NOME));
}
private void salvar() {
try {
// Save...
Notification.show($(PessoaTranslator.MESSAGE.CRIADO_SUCESSO));
} catch (Exception ex) {
Notification.show($(PessoaTranslator.RULE.EMAIL_UNICO));
}
}
}
🔧 Referência de API
Classes Internas do Translator:
HELP: Texto de ajuda e tooltipsVALIDATION: Mensagens de validação JakartaRULE: Mensagens de violação de regra de negócioMESSAGE: Mensagens de sucesso/erroEVENT: Mensagens de eventos de domínio
Padrão de Chave: DOMAIN.section.key.subkey
Métodos de Tradução:
translator.translate(key): Tradução simplestranslator.translate(key, arg1, arg2): Tradução com parâmetros$(key): Atalho Vaadin para tradução
🏗️ Arquitetura
Fluxo de Tradução:
- Classe Translator define chaves constantes
- Arquivo de propriedades contém traduções para chaves
- DTO usa constantes em anotações de validação
- Service usa constantes em exceções de negócio
- View usa constantes para elementos UI
- Translator resolve chaves para strings localizadas
Estrutura de Arquivo:
src/main/resources/i18n/
├── translations_biblia_pt_BR.properties # Camada Service
└── translations_biblia_pt_BR.properties # Camada View
Padrões de Design:
- Padrão Objeto Constante: Constantes string type-safe
- Padrão Estratégia: Diferentes estratégias de tradução
- Padrão Registro: Registro de chaves de tradução
⚠️ Problemas Comuns
Problema: Strings hardcoded no código Solução: Substituir com constantes Translator
Problema: Traduções ausentes no arquivo de propriedades Solução: Adicionar todas as constantes Translator às propriedades
Problema: Nomenclatura de chave inconsistente
Solução: Seguir padrão DOMAIN.section.key.subkey
Problema: Translator não final com construtor privado Solução: Tornar classe final com construtor privado
Problema: Classes internas ausentes Solução: Incluir HELP, VALIDATION, RULE, MESSAGE, EVENT