Localização Java - Formatando Mensagens

Localização Java - Formatando Mensagens

1. Introdução

Neste tutorial, vamos considerar como podemoslocalize and format messages com base emLocale.

UsaremosMessageFormat do Java e a biblioteca de terceiros,ICU.

2. Caso de Uso de Localização

Quando nosso aplicativo atinge um grande público de usuários de todo o mundo, podemos naturalmente desejarshow different messages based on the user’s preferences.

O primeiro e mais importante aspecto é o idioma que o usuário fala. Outros podem incluir moeda, número e formatos de data. Por último, mas não menos importante, as preferências culturais: o que é aceitável para usuários de um país pode ser intolerável para outros.

Suponha que tenhamos um cliente de email e queremos mostrar notificações quando uma nova mensagem chegar.

Um exemplo simples dessa mensagem pode ser este:

Alice has sent you a message.

É bom para usuários que falam inglês, mas aqueles que não falam inglês podem não ficar muito satisfeitos. Por exemplo, usuários de língua francesa preferem ver esta mensagem:

Alice vous a envoyé un message.

Enquanto o povo polonês ficaria satisfeito ao ver este:

Alice wysłała ci wiadomość.

E se quisermos receber uma notificação formatada corretamente, mesmo quando Alice enviar não apenas uma mensagem, mas poucas mensagens?

Podemos ficar tentados a resolver o problema concatenando várias peças em uma única sequência, assim:

String message = "Alice has sent " + quantity + " messages";

A situação pode facilmente ficar fora de controle quando precisamos de notificações, caso não apenas Alice, mas também Bob possam enviar as mensagens:

Bob has sent two messages.
Bob a envoyé deux messages.
Bob wysłał dwie wiadomości.

Observe como o verbo muda no caso do idioma polonês (wysłała vswysłał). Isso ilustra o fato de quebanal string concatenation is rarely acceptable for localizing messages.

Como podemos ver, temos dois tipos de problemas:one is related to translations and the other is related to formats. Vamos abordá-los nas seções a seguir.

3. Localização de Mensagens

Podemos definir olocalization, or l10n, of an application as the process of adapting the application to the user’s comfort. Às vezes, o termointernalization, oui18n também é usado.

Para localizar o aplicativo, em primeiro lugar, vamos eliminar todas as mensagens codificadas, movendo-as para nossa pastaresources:

image

Cada arquivo deve conter pares de valores-chave com as mensagens no idioma correspondente. Por exemplo, o arquivomessages_en.properties deve conter o seguinte par:

label=Alice has sent you a message.

messages_pl.properties deve conter o seguinte par:

label=Alice wysłała ci wiadomość.

Da mesma forma, outros arquivos atribuem valores apropriados à chavelabel. Agora, para obter a versão em inglês da notificação, podemos usarResourceBundle:

ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.UK);
String message = bundle.getString("label");

O valor da variávelmessage será“Alice has sent you a message.”

A classeLocale do Java contém atalhos para idiomas e países usados ​​com frequência.

No caso do idioma polonês, podemos escrever o seguinte:

ResourceBundle bundle
  = ResourceBundle.getBundle("messages", Locale.forLanguageTag("pl-PL"));
String message = bundle.getString("label");

Vamos apenas mencionar que, se não fornecermos nenhuma localidade, o sistema usará uma localidade padrão. Podemos obter mais detalhes sobre esse problema em nosso artigo “https://www.example.com/java-8-localization[Internationalization and Localization in Java 8]“. Em seguida, entre as traduções disponíveis, o sistema escolherá a que for mais semelhante à localidade atualmente ativa.

A colocação das mensagens nos arquivos de recursos é um bom passo para tornar o aplicativo mais amigável. Facilita a tradução de todo o aplicativo pelos seguintes motivos:

  1. um tradutor não precisa procurar no aplicativo em busca das mensagens

  2. um tradutor pode ver a frase inteira, o que ajuda a entender o contexto e, portanto, facilita uma tradução melhor

  3. não precisamos recompilar todo o aplicativo quando uma tradução para um novo idioma estiver pronta

4. Formato da mensagem

Embora tenhamos movido as mensagens do código para um local separado, elas ainda contêm algumas informações codificadas. Seria bom poder personalizar os nomes e números nas mensagensin such a way that they remain grammatically correct.

Podemos definir a formatação como um processo de renderização do modelo de string substituindo os marcadores de posição por seus valores.

Nas seções a seguir, consideraremos duas soluções que nos permitem formatar as mensagens.

4.1. MessageFormat de Java

Para formatar strings, Java definenumerous format methods in java.lang.String. Mas podemos obter ainda mais suporte por meio dejava.text.format.MessageFormat.

Para ilustrar, vamos criar um padrão e alimentá-lo para uma instânciaMessageFormat:

String pattern = "On {0, date}, {1} sent you "
  + "{2, choice, 0#no messages|1#a message|2#two messages|2<{2, number, integer} messages}.";
MessageFormat formatter = new MessageFormat(pattern, Locale.UK);

A sequência padrão possui slots para três espaços reservados.

Se fornecermos cada valor:

String message = formatter.format(new Object[] {date, "Alice", 2});

Em seguida,MessageFormat preencherá o modelo e renderizará nossa mensagem:

On 27-Apr-2019, Alice sent you two messages.

4.2. MessageFormat Sintaxe

No exemplo acima, vemos que o padrão da mensagem:

pattern = "On {...}, {..} sent you {...}.";

contém marcadores de posição que são as chaves\{…} com um argumento obrigatórioindex e dois argumentos opcionais,typeestyle:

{index}
{index, type}
{index, type, style}

O índice do placeholder corresponde à posição de um elemento da matriz de objetos que queremos inserir.

Quando presentes,type estyle podem assumir os seguintes valores:

type estilo

número

inteiro, moeda, porcentagem, formato personalizado

date

curto, médio, longo, completo, formato personalizado

time

curto, médio, longo, completo, formato personalizado

escolha

formato personalizado

Os nomes dos tipos e estilos falam por si próprios, mas podemos consultarofficial documentation para obter mais detalhes.

Vamos dar uma olhada mais de perto, porém, emcustom format. 

No exemplo acima, usamos a seguinte expressão de formato:

{2, choice, 0#no messages|1#a message|2#two messages|2<{2, number, integer} messages}

Em geral, o estilo de escolha tem a forma de opções separadas pela barra vertical (ou tubo):

image

Dentro das opções, o valor de correspondênciakie a stringvi são separados por # exceto para a última opção. Observe que podemos aninhar outros padrões na stringvi como fizemos para a última opção:

{2, choice, ...|2<{2, number, integer} messages}

The choice type is a numeric-based one, portanto, há uma ordem natural para os valores de correspondênciak que dividem uma linha numérica em intervalos:

image

Se dermos um valork que pertence ao intervalo[ki, ki+1) (a extremidade esquerda é incluída, a direita é excluída), então o valorvi é selecionado.

Vamos considerar com mais detalhes as gamas do estilo escolhido. Para esse fim, adotamos este padrão:

pattern = "You''ve got "
  + "{0, choice, 0#no messages|1#a message|2#two messages|2<{0, number, integer} messages}.";

e passar vários valores para seu espaço reservado exclusivo:

n mensagem

-1, 0, 0,5

Você não tem mensagens.

1, 1,5

Você tem uma mensagem.

2

Você tem duas mensagens.

2.5

Você tem 2 mensagens.

5

Você tem 5 mensagens.

4.3. Tornando as coisas melhores

Então, agora estamos formatando nossas mensagens. Mas, a mensagem em si permanece codificada.

Na seção anterior, sabemos que devemos extrair os padrões de strings para os recursos. Para separar nossas preocupações, vamos criar outro grupo de arquivos de recursos chamadosformats:

image

Neles, criaremos uma chave chamadalabel com conteúdo específico do idioma.

Por exemplo, na versão em inglês, colocaremos a seguinte string:

label=On {0, date, full} {1} has sent you
  + {2, choice, 0#nothing|1#a message|2#two messages|2<{2,number,integer} messages}.

Devemos modificar um pouco a versão em francês por causa do caso de mensagem zero:

label={0, date, short}, {1}{2, choice, 0# ne|0<} vous a envoyé
  + {2, choice, 0#aucun message|1#un message|2#deux messages|2<{2,number,integer} messages}.

E precisaríamos fazer modificações semelhantes também nas versões polonesa e italiana.

De fato, a versão polonesa apresenta outro problema. De acordo com a gramática da língua polonesa (e muitas outras), o verbo deve concordar em gênero com o sujeito. We could resolve this problem by using the choice type, but let’s consider another solution.

4.4. MessageFormat da UTI

Vamos usar a bibliotecaInternational Components for Unicode (ICU). Já mencionamos isso em nosso tutorialConvert a String to Title Case. É uma solução madura e amplamente utilizada que nos permite personalizar o aplicativo para vários idiomas.

Aqui, não vamos explorá-lo em todos os detalhes. Nós apenas nos limitaremos às necessidades de nosso aplicativo de brinquedos. Para obter as informações mais abrangentes e atualizadas, devemos verificar oICU’s official site.

No momento da escrita, a versão mais recente deICU for Java (ICU4J) é 64.2. Como de costume, para começar a usá-lo, devemos adicioná-lo como uma dependência ao nosso projeto:


    com.ibm.icu
    icu4j
    64.2

Suponha que desejemos ter uma notificação formada adequadamente em vários idiomas e para diferentes números de mensagens:

N Inglês polonês

0

Alice não enviou nenhuma mensagem para você. Bob não lhe enviou nenhuma mensagem.

Alice nie wysłała ci żadnej wiadomości. Bob nie wysłał ci żadnej wiadomości.

1

Alice enviou uma mensagem para você. Bob enviou uma mensagem para você.

Alice wysłała ci wiadomość. Bob wysłał ci wiadomość.

> 1

Alice enviou a você N mensagens. Bob enviou N mensagens para você.

Alice wysłała ci N wiadomości. Bob wysłał ci N wiadomości.

Antes de tudo, devemos criar um padrão nos arquivos de recursos específicos do código do idioma.

Vamos reutilizar o arquivoformats.propertiese adicionar a chavelabel-icu com o seguinte conteúdo:

label-icu={0} has sent you
  + {2, plural, =0 {no messages} =1 {a message}
  + other {{2, number, integer} messages}}.

Ele contém três espaços reservados que alimentamos passando para lá uma matriz de três elementos:

Object[] data = new Object[] { "Alice", "female", 0 }

Vemos que na versão em inglês, o espaço reservado com valor de gênero é inútil, enquanto na versão polonesa:

label-icu={0} {2, plural, =0 {nie} other {}}
+  {1, select, male {wysłał} female {wysłała} other {wysłało}}
+  ci {2, plural, =0 {żadnych wiadomości} =1 {wiadomość}
+  other {{2, number, integer} wiadomości}}.

nós o usamos para distinguir entrewysłał/wysłała/wysłało.

5. Conclusão

Neste tutorial, consideramos como localizar e formatar as mensagens que demonstramos para os usuários de nossos aplicativos.

Como sempre, os trechos de código para este tutorial estão em nossoGitHub repository.