Internacionalização no código Java
Por Nico Steppat em 02/10/07Já falamos neste blog sobre i18n, o Fabio postou um artigo como internacionalizar as suas aplicações web usando JSTL.
fmt dá todo suporte, mas fora do ambiente web precisamos procurar outra solução. Por exemplo, numa aplicação swing, ou mesmo web tem casos que precisamos mostrar uma mensagem já internacionalizada. Temos que saber com i18n / L10n também programaticamente fora do contêiner web. Isto é útil no dia-a-dia e também faz parte da certificação SCJP
java.util.Locale
Esta classe influencia fortemente o trabalho de outras classes referente à formatação e internacionalização. Com ela será definida uma região, por exemplo:
Locale ptBr = new Locale("pt", "BR"); //Locale para o Brasil
Definimos um Locale com o idioma português (iso-code pt) e o país Brasil (iso-code BR). Vocês poderiam reclamar que está óbvio que o Brasil fala português, mas isso não aplica para todos os países, Veja o exemplo:
Locale enCA = new Locale("en","CA");
Locale frCA = new Locale("fr","CA");
Definimos o mesmo pais Canada mas com dois idiomas diferentes (uma região para francês outra para inglês).
Também podemos pegar o Locale da maquina virtual:
Locale vmLocale = Locale.getDefault(); //pergunta de certificação
java.text.DateFormat e java.text.NumberFormat
A formatação das duas classes é baseada em um Locale. Elas servem para formatar Data ou Números/Moeda respectivamente. Para receber um instância das classes é preciso usar um dos métodos getInstance() delas:
DateFormat dateFormat = DateFormat.getInstance();
NumberFormat numberFormat = NumberFormat.getInstance();
Mas aonde está o Locale? Nesse caso DateFormat/NumberFormat são baseado no Locale padrão, ou seja o da máquina virtual. Você pode passar o Locale no método, por exemplo:
DateFormat dateFormat =
DateFormat.getDateInstance(DateFormat.FULL, ptBR);
NumberFormat numberFormat =
NumberFormat.getNumberInstance(ptBR);
Agora você recebe uma instância para formatar datas ou numeros referente do Brasil. Existem outros métodos para receber uma instância, aqui algumas variações:
Locale ptBR = new Locale("pt", "BR");
DateFormat dateFormat =
DateFormat.getDateInstance(DateFormat.FULL, ptBR);
System.out.println(dateFormat.format(new Date()));
DateFormat timeFormat =
DateFormat.getTimeInstance(DateFormat.MEDIUM, ptBR);
System.out.println(timeFormat.format(new Date()));
NumberFormat numberFormat =
NumberFormat.getNumberInstance(ptBR); //para números
System.out.println(numberFormat.format(13.23));
NumberFormat moedaFormat =
NumberFormat.getCurrencyInstance(ptBR); //para moedas
System.out.println(moedaFormat.format(13.23));
Dependo do dia, o resultado seria:
Sexta-feira, 21 de Setembro de 2007
00:51:05
13,23
R$ 13,23
As duas classes, junto com o Locale dão muito poder e não servem somente para formatação, mas também para fazer parsing de uma String para um Date/Number, veja só:
System.out.println(dateFormat.parse(
"Sexta-feira, 21 de Setembro de 2007"));
System.out.println(timeFormat.parse("00:58:16"));
System.out.println(numberFormat.parse("13,23"));
System.out.println(moedaFormat.parse("R$ 13,23"));
Imprime (Dependo da data):
Fri Sep 21 00:00:00 BRT 2007
Thu Jan 01 00:58:16 BRT 1970
13.23
13.23
ResourceBundle
A classe ResourceBundle é aquela que acessa seu arquivo de internacionalização. Supondo que temos dois arquivos de propriedades no classpath, um para as mensagens em português, outro para as em alemão e um padrão, com o conteúdo seguinte:
messages_pt_BR.properties
welcome=Bem vindo no Brasil
messages_de_DE.properties
welcome=Hallo in Deutschland
messages.properties
welcome=Welcome in default
logout=Leaving default
A nomenclatura segue o padrão, messages_’iso-code-language’_'iso-code-country’. Parece familiar? Para carregar o arquivo “messages” baseada na Locale pt-BR basta usar:
Locale ptBR = new Locale("pt","BR");
ResourceBundle bundle = ResourceBundle.getBundle("messages", ptBR);
System.out.println(bundle.getString("welcome"));
O resultado será:
Bem vindo no Brasil
Para carregar a mensagem em alemão, só precisamos mudar o Locale. Mas se eu chamo uma mensagem com a chave “logout”? Por exemplo:
System.out.println(bundle.getString("logout"));
O bundle vai procurar de maneira seguinte:
messages_pt_BR.properties //não acha
messages_pt.properties //não acha
messages.properties
e finalmente acha o valor da chave ‘logout’ no arquivo messages.properties e imprime na tela:
Leaving default
MessageFormat
Falta mencionar a classe que ajuda substituir parâmetros na mensagem.
String mensagemParametrizada = "Isto foi um blog sobre {0} e {1}.";
String mensagem =
MessageFormat.format(mensagemParametrizada, "i18n", "formatação");
System.out.println(mensagem);
Imprime:
Isto foi um blog sobre i18n e formatação.
Muito bom, porém sempre tive uma dúvida em relação sobre como ficaria a arquitetura de um DB de um sistema multilingüe e como seria a comunicação de um framework de persistência, como o Hibernate, como o banco.
Comment by David — October 3, 2007 @ 3:05 pm
Legal o texto,mas é bom lembrar aos incautos que o locale para o Brasil não serve para tudo.Se alguem lida com cálculos de IPTU e algumas transações bancárias específicas, que lidam com 4 casas decimais, melhor fazer na mão mesmo, pois o number instance só vai até a 3ªcasa, e o Currency, até a segunda.
Comment by Ironlynx — October 5, 2007 @ 11:38 pm
David, qual é a sua dúvida referente do banco e internacionalização?
Ironlynx, vlw pela dica!
Comment by Nico Steppat — October 9, 2007 @ 1:37 pm
Nico, erro meu:
É o que dá falar as coisas sem ler a API(Tava sem acesso á ela aqui).É só usar o .setMinimumFractionDigits(4); que funciona ok, SEM perdas(e eu reescrevendo a roda com BDecimal).Ou seja:
Locale ptBR = new Locale(”pt”, “BR”);
NumberFormat numberFormat =
NumberFormat.getNumberInstance(ptBR); //para números
numberFormat.setMinimumFractionDigits(4);
System.out.println(numberFormat.format(13.231212));
NumberFormat moedaFormat =
NumberFormat.getCurrencyInstance(ptBR); //para moedas
moedaFormat.setMinimumFractionDigits(4);
System.out.println(moedaFormat.format(1312.231212));
No códifo acima, será impresso:
13,2312
R$ 1.312,2312
Um []´ção no Paulo.
Comment by Ironlynx — October 16, 2007 @ 1:16 pm
[...] No blog da Caelum foi apresentado um artigo interessante sobre internacionalização de programas escrito em java, recurso pouco explorado, recomendo sua leitura. Veja mais aqui. [...]
Pingback by Anibal Mantovani Diniz, MSc » Internacionalização no código Java — October 28, 2007 @ 7:26 pm
Algo que eu esqueci de comentar, foi que apartir do java5, pode se usar o método setParseBigDecimal(boolean), para converter um DecimalFormat para BigDecimal.É muito tranquilo e não trunca!(Muito útil quando vc lida com TextFields personalizados, mas tem que fazer operações precisas com BigDecimal)
Exemplo:
public BigDecimal convertToBigDecimal(String value){
Locale.setDefault(new Locale(”pt”,”BR”));
DecimalFormat df = new DecimalFormat(”#,##0.00″);
try{
df.setParseBigDecimal(true);
BigDecimal bd =(BigDecimal)df.parse(value);
return bd;
}catch(ParseException pe){
pe.printStackTrace();
}
return BigDecimal.ZERO;
}
Comment by Ironlynx — October 29, 2007 @ 2:23 pm
[...] Para conhecer mais sobre internacionalização e como implementá-la em Java, indico este excelente artigo da [...]
Pingback by Internacionalização de Mensagens no JavaScript « Marcelo Madeira — November 5, 2007 @ 3:44 am
adoraria saber como faço para internacionalizar exceções?
Comment by Tonelli — February 8, 2008 @ 12:32 pm
Gostaria de saber se você poderia disponibilizar o projeto.
Comment by Marco — June 10, 2008 @ 5:42 pm