Um guia para o ResourceBundle

Um guia para o ResourceBundle

*1. Visão geral *

Muitos desenvolvedores de software, durante suas carreiras profissionais, enfrentam a oportunidade de desenvolver sistemas ou aplicativos multilíngues. Geralmente, eles são destinados a usuários finais de diferentes regiões ou diferentes idiomas.

É sempre um desafio manter e estender esses aplicativos. A capacidade de operar com vários dados específicos da localização ao mesmo tempo é geralmente crucial. A modificação dos dados do aplicativo deve ser o mais simples possível, sem a necessidade de recompilação. É por isso que geralmente evitamos nomes de etiquetas ou botões codificados.

Felizmente, podemos usar o Java que nos fornece essa classe, o que nos ajuda a resolver todos os problemas mencionados acima.

Simplificando, o ResourceBundle permite que nosso aplicativo carregue dados de arquivos distintos que contêm dados específicos de localidade.

====* 1.1 ResourceBundles *

A primeira coisa que devemos saber é que todos os arquivos dentro de* um pacote de recursos devem estar no mesmo pacote/diretório e ter um nome base * comum. Eles podem ter sufixos específicos do código de idioma, indicando idioma, país ou plataforma separados pelo símbolo de sublinhado.

É importante anexar o código do país, se já houver código de idioma, ou plataforma, se houver códigos de idioma e país.

Vejamos exemplos de nomes de arquivos:

  • ExampleResource

  • ExampleResource_en

  • ExampleResource_en_US *ExampleResource_en_US_UNIX

O arquivo padrão para cada pacote de dados é sempre um sem sufixos - ExampleResource. Como existem duas subclasses de ResourceBundle:* PropertyResourceBundle e ListResourceBundle *, podemos intercambiar os dados nos arquivos de propriedades e nos arquivos java.

Cada arquivo deve ter um nome específico do código do idioma e uma extensão de arquivo adequada , por exemplo, ExampleResource_en_US.properties ou Example_en.java.

1.2 Arquivos de propriedades - PropertyResourceBundle

Os arquivos de propriedades são representados por PropertyResourceBundle. Eles armazenam dados na forma de pares de valor-chave com distinção entre maiúsculas e minúsculas.

Vamos analisar um arquivo de propriedades de amostra:

# Buttons
continueButton continue
cancelButton=cancel

! Labels
helloLabel:hello

Como podemos ver, existem três estilos diferentes de definição de pares de valores-chave.

Todos eles são equivalentes, mas o primeiro é provavelmente o mais popular entre os programadores de Java. Vale a pena saber que também podemos colocar comentários nos arquivos de propriedades. Os comentários sempre começam com _ # _ ou _! _.

1.3 Arquivos Java - ListResourceBundle

Primeiro, para armazenar nossos dados específicos do idioma, precisamos criar uma classe que estenda ListResourceBundle e substitua o método _getContents () _. A convenção de nome da classe é a mesma dos arquivos de propriedades.

Para cada _Locale, _ precisamos criar uma classe Java separada.

Aqui está uma classe de amostra:

public class ExampleResource_pl_PL extends ListResourceBundle {

    @Override
    protected Object[][] getContents() {
        return new Object[][] {
          {"currency", "polish zloty"},
          {"toUsdRate", new BigDecimal("3.401")},
          {"cities", new String[] { "Warsaw", "Cracow" }}
        };
    }
}

Os arquivos Java têm uma grande vantagem sobre os arquivos de propriedades, que é a possibilidade de armazenar qualquer objeto que desejamos - não apenas Strings.

Por outro lado, cada modificação ou introdução de uma nova classe java específica do código do idioma requer a recompilação de um aplicativo, enquanto os arquivos de propriedades podem ser estendidos sem nenhum esforço adicional.

*2. Use pacotes de recursos *

Já sabemos como definir pacotes de recursos, por isso estamos prontos para usá-lo.

Vamos considerar o snippet de código curto:

Locale locale = new Locale("pl", "PL");
ResourceBundle exampleBundle = ResourceBundle.getBundle("package.ExampleResource", locale);

assertEquals(exampleBundle.getString("currency"), "polish zloty");
assertEquals(exampleBundle.getObject("toUsdRate"), new BigDecimal("3.401"));
assertArrayEquals(exampleBundle.getStringArray("cities"), new String[]{"Warsaw", "Cracow"});

Primeiramente, podemos definir nosso Locale, a menos que não desejemos usar o padrão.

Depois disso, vamos chamar um método estático de fábrica de ResourceBundle. Precisamos passar* o nome do pacote configurável com seu pacote/diretório *e o código do idioma como parâmetros.

Há também um método de fábrica que requer apenas um nome de pacote se o código do idioma padrão estiver correto. Assim que tivermos o objeto, podemos recuperar valores por suas chaves.

Além disso, o exemplo mostra que podemos usar _getString (String string) _, _getObject (String string), _ e _getStringArray (String string) _ para obter os valores desejados.

===* 3. Selecionando o recurso de pacote adequado *

*Se queremos usar um recurso de pacote configurável, é importante saber como _Java_ seleciona arquivos de pacote configurável.*

Vamos imaginar que trabalhamos com um aplicativo que precisa de rótulos em polonês, mas seu local padrão JVM é Locale.US.

No início, o aplicativo procurará os arquivos no caminho de classe adequado ao local que você solicitar. Começa com o nome mais específico, ou seja, um que contém uma plataforma, um país e um idioma.

Então, vai para mais geral. Se não houver correspondência, ele retornará ao código de idioma padrão sem verificação de plataforma neste momento.

*Caso não haja correspondência, ele tentará ler o pacote padrão.* Tudo deve ficar claro quando observarmos a ordem dos nomes de arquivos selecionados:
  • Label_pl_PL_UNIX

  • Label_pl_PL

  • Label_pl

  • Label_en_US

  • Label_en *Rótulo

Devemos ter em mente que cada nome representa os arquivos .java e .properties, mas o primeiro tem precedência sobre o último. Quando não há arquivo adequado, uma MissingResourceException é lançada.

===* 4. Herança *

Outra vantagem do conceito de pacote configurável de recursos é a herança de propriedades. Isso significa que os pares de valores-chave incluídos em arquivos menos específicos são herdados por aqueles que são mais altos na árvore de herança.

Vamos supor que temos três arquivos de propriedades:

#resource.properties
cancelButton = cancel

#resource_pl.properties
continueButton = dalej

#resource_pl_PL.properties
backButton = cofnij

O pacote de recursos recuperado para _Locale (“pl”, “PL”) _ retornaria todas as três chaves/valores no resultado. Vale ressaltar,* não há retorno ao pacote de códigos de idioma padrão *, na medida em que a herança de propriedade é considerada.

Além disso, ListResourceBundles e PropertyResourceBundles não estão na mesma hierarquia.

Portanto, se um arquivo de propriedades for encontrado no caminho de classe, os pares de valores-chave serão herdados apenas dos arquivos de propriedades. A mesma regra se aplica aos arquivos Java.

*5. Costumização *

Tudo o que aprendemos acima foi sobre a implementação padrão do ResourceBundle. No entanto, existe uma maneira de modificar seu comportamento.

Fazemos isso estendendo ResourceBoundle.Control e substituindo seus métodos.

Por exemplo, podemos alterar o tempo de manutenção de valores no cache ou determinar a condição em que o cache deve ser recarregado.

Para uma melhor compreensão, vamos preparar um método breve como exemplo:

public class ExampleControl extends ResourceBundle.Control {

    @Override
    public List<Locale> getCandidateLocales(String s, Locale locale) {
        return Arrays.asList(new Locale("pl", "PL"));
    }
}

O objetivo deste método é alterar uma maneira de selecionar arquivos no caminho de classe. Como podemos ver, ExampleControl retornará apenas Locale polonês, não importa qual seja o Locale padrão ou definido.

===* 6. UTF-8 *

Como ainda existem muitos aplicativos usando o JDK 8 ou versões mais antigas, vale a pena saber que* antes de Java_9 *_ListResourceBundles tinha mais uma vantagem sobre PropertyResourceBundles. Como os arquivos Java podem armazenar objetos String, eles podem armazenar qualquer caractere suportado pela codificação UTF-16.

Pelo contrário, PropertyResourceBundle carrega arquivos por padrão usando a codificação ISO 8859-1, que possui menos caracteres que UTF-8 (causando problemas nos nossos exemplos de idioma polonês).

Para salvar caracteres que estão além do UTF-8, podemos usar o conversor Native-To-ASCII - native2ascii. Ele converte todos os caracteres que não são compatíveis com a ISO 8859-1, codificando-os para a notação _ \ uxxxx_.

Aqui está um exemplo de comando:

native2ascii -encoding UTF-8 utf8.properties nonUtf8.properties

E vamos ver como as propriedades se parecem antes e depois de uma alteração na codificação:

#Before
polishHello=cześć

#After
polishHello=cze\u015b\u0107

Felizmente, esse inconveniente não existe mais no Java 9. JVM lê arquivos de propriedades na codificação UTF-8, e não há problema em usar caracteres não latinos.

===* 7. Conclusão*

BundleResource contém muito do que precisamos para desenvolver um aplicativo multilíngue. Os recursos que abordamos tornam bastante simples a manipulação de diferentes localidades.

Também evitamos valores codificados, o que nos permite expandir os Locales suportados simplesmente adicionando novos arquivos Locale, permitindo que nosso aplicativo seja modificado e mantido sem problemas.

Como sempre, o código de exemplo está disponível em over no GitHub.