Alternar idioma no aplicativo Android


Existe uma maneira fácil de implementar a alternância de idiomas em um aplicativo de atividade única. A pilha de telas nessa abordagem não é redefinida, o usuário permanece onde ele mudou o idioma. Quando o usuário acessa as telas anteriores, eles são exibidos imediatamente traduzidos. E o resultado da localização de números, somas de dinheiro e juros pode surpreender os designers.


Hebrew, motherfucker! Do you speak it?!


O que será discutido e o que não será discutido?


Além disso, não haverá nada sobre:


  • A teoria subjacente à saída formatada de strings e os detalhes de implementação das bibliotecas que lidam com isso. Ou seja, algo que ajudaria você a escrever sua biblioteca.
  • Recursos string, vetor e outros. Sobre quais qualificadores de recursos usar, quais figuras em árabe devem ser exibidas da direita para a esquerda, quais não e outras sutilezas.
  • O processo de tradução centralizada de recursos para todas as plataformas. Como organizá-lo para que todos vivam bem, até apelidos para iOS.

E falaremos sobre:


  • Prática. Considere o problema, suas limitações e solução com diagramas, exemplos e fragmentos de código.
  • A API do SDK que foi usada para esta solução.
  • Recursos da formatação de valores numéricos para diferentes padrões regionais, sobre os quais os designers devem conhecer.

O que queremos fazer?


Que haja uma tela com configurações em nosso aplicativo, e queremos adicionar alguns novos itens, um dos quais permitiria alterar o idioma do aplicativo e o outro para alterar a moeda na qual os valores monetários são exibidos. Aqui estão alguns exemplos de como isso pode parecer.


Telegram.  .


Telegram.  .


Além de traduzir o texto e exibir o layout da direita para a esquerda, essas configurações devem afetar o formato de exibição dos valores numéricos. É necessário que tudo seja exibido de acordo com o código do idioma selecionado.


.


Solução arquitetônica


Imagine que nosso aplicativo seja escrito de acordo com a abordagem de atividade única . Em seguida, o mecanismo de troca de idioma pode ser implementado da seguinte maneira.


.


SettingsInteractor é a fonte do valor atual do idioma. Ele permite que você assine esse valor, receba-o de forma síncrona e assine apenas atualizações. Se necessário, você pode introduzir uma abstração adicional no SettingsInteractor acordo com o princípio de separação de interface . No diagrama, detalhes irrelevantes são omitidos.


AppActivity na criação substitui o contexto por um novo, para que o aplicativo use recursos para o idioma selecionado.


 override fun attachBaseContext(base: Context) { super.attachBaseContext(applySelectedAppLanguage(base)) } private fun applySelectedAppLanguage(context: Context): Context { val locale = settingsInteractor.getUserSelectedLanguageBlocking() val newConfig = Configuration(context.resources.configuration) Locale.setDefault(locale) newConfig.setLocale(locale) return context.createConfigurationContext(newConfig) } 

AppPresenter por sua vez, assina atualizações de idioma e notifica a Visualização das alterações.


 override fun onFirstViewAttach() { super.onFirstViewAttach() subscribeToLanguageUpdates() } private fun subscribeToLanguageUpdates() { settingsInteractor .getUserSelectedLanguageUpdates() .subscribe( { newLang -> viewState.applyNewAppLanguage(newLang) }, { error -> errorHandler.handle(error) } ) .disposeOnDestroy() } 

AppActivity recriado quando uma notificação de uma alteração de idioma é recebida.


 override fun applyNewAppLanguage(lang: Locale) = recreate() 

.


AppActivity é o único no aplicativo. Todas as outras telas são implementadas em fragmentos. Portanto, ao recriar a atividade, a pilha de telas é salva pelo sistema. Se você retornar às telas anteriores, elas serão reinicializadas e exibidas traduzidas. O usuário permanecerá na lista de seleção de idioma e verá o resultado de sua escolha instantaneamente.


Formatação de números, dinheiro e juros


Além de substituir o contexto, é necessário formatar os dados - números, dinheiro, juros. Permita que cada View delegue essa tarefa a um componente separado, vamos chamá-lo de UiLocalizer .


UiLocalizer.


UiLocalizer usa as instâncias NumberFormat apropriadas para converter um número em uma string.


 private var numberFormat = NumberFormat.getNumberInstance(lang) private var percentFormat = NumberFormat.getPercentInstance(lang) private fun getNumberFormatForCurrency(currency: Currency) = NumberFormat .getCurrencyInstance(lang) .also { it.currency = currency } 

Observe que a moeda deve ser definida separadamente.


Se você salvar ciclos da CPU e bits de memória, e a troca de moeda e idioma for a função principal e frequentemente usada do seu aplicativo, é claro que você precisará de um cache.


Representação de idiomas e Moedas


Instâncias da classe Locale são criadas por uma marca de idioma , que consiste em um código de idioma de duas letras e um código de região de duas letras. E as instâncias da classe Currency são baseadas em um código ISO de três letras . Nesse formulário, o idioma e a moeda devem ser serializados para salvar em disco ou transferir pela rede e, em seguida, será bom. Aqui estão alguns exemplos.


 // IETF BCP 47 language tag string. private val langs = arrayOf( Locale.forLanguageTag("ru-RU"), Locale.forLanguageTag("en-US"), Locale.forLanguageTag("en-GB"), Locale.forLanguageTag("he-IL"), Locale.forLanguageTag("ar-SA"), Locale.forLanguageTag("ar-AE"), Locale.forLanguageTag("fr-FR"), Locale.forLanguageTag("fr-CH"), Locale.forLanguageTag("de-DE"), Locale.forLanguageTag("de-CH"), Locale.forLanguageTag("da-DK") ) // ISO 4217 code of the currency. private val currencies = arrayListOf( Currency.getInstance("RUB"), Currency.getInstance("USD"), Currency.getInstance("GBP"), Currency.getInstance("ILS"), Currency.getInstance("SAR"), Currency.getInstance("AED"), Currency.getInstance("EUR"), Currency.getInstance("CHF"), Currency.getInstance("DKK") ) 

Recursos para formatar valores numéricos


O resultado da formatação de números de acordo com os padrões regionais pode diferir do esperado. O símbolo da moeda ou seu código de três letras em diferentes idiomas será exibido de diferentes maneiras. Os sinais de menos para valores monetários negativos aparecerão em locais inesperados e, em alguns lugares, os colchetes serão exibidos. O sinal de porcentagem pode não ser exatamente o sinal com o qual estamos acostumados.


O fato é que, do ponto de vista dos padrões regionais, a linha final consiste em um prefixo e sufixo para números positivos e negativos, um separador de mil e um separador decimal, e eles são diferentes para diferentes localidades.


Os números


LinguagemPrefixo negativoSufixo negativoPrefixo positivoSufixo positivoSepaarator de agrupamentoSeparador decimal
ru-RU"-"""","
en-US"-"",""."
iw-IL"-"",""."
ar-AE"-""٬""٫"
fr-FR"-"""","
de-de"-""."","
de-CH"-""'""."
da-dk"-""."","

Moedas


LinguagemPrefixo negativoSufixo negativoPrefixo positivoSufixo positivoSepaarator de agrupamentoSeparador decimal
ru-RU"-""₽""₽"""","
en-US"- $""$"",""."
iw-IL"-""₪""₪"",""."
ar-AE"-""د.إ.""د.إ.""٬""٫"
fr-FR"-""€""€"""","
de-de"-""€""€""."","
de-CH"CHF-"CHF"'""."
da-dk"-""kr""kr""."","

Juros


LinguagemPrefixo negativoSufixo negativoPrefixo positivoSufixo positivoSepaarator de agrupamentoSeparador decimal
ru-RU"-""%""%"""","
en-US"-""%""%"",""."
iw-IL"-""%""%"",""."
ar-AE"-""٪""٪""٬""٫"
fr-FR"-""%""%"""","
de-de"-""%""%""."","
de-CH"-""%""%""'""."
da-dk"-""%""%""."","

Além disso, os resultados da formatação do Android SDK e JDK podem ser diferentes. Além disso, todas as opções estão corretas, cada uma delas é usada em determinados contextos.


Android  JDK.


Formato decimal


Quando criamos um NumberFormat para formatar determinados valores, obtemos objetos da classe DecimalFormat que são simplesmente configurados por modelos diferentes. Ao DecimalFormat objeto no tipo DecimalFormat e usar sua interface, você pode alterar partes do modelo para quebrar tudo. Mas é melhor adorar a doação.


DecimalFormat.


Você também pode escrever um teste para apreciar a variedade. Nem para todas as localidades, a mesma moeda é exibida com um símbolo.


.


No final


O esquema geral da solução é o seguinte.


.


O ciclo de vida do AppActivity é o ciclo de vida de todo o aplicativo. Portanto, basta recriá-lo para reiniciar o aplicativo inteiro e aplicar o idioma selecionado. E como existe apenas uma atividade, é suficiente manter a assinatura para alterar o idioma em um só lugar - no AppPresenter .


Como vimos, os formatos regionais para gerar números não são triviais. Você não deve definir rigidamente um único modelo para todas as ocasiões. É melhor confiar a formatação do SDK e concordar que os números serão exibidos de acordo com o padrão, e não como desenhados nos layouts.


O que é mais fácil de testar? (bônus)


Para economizar tempo, você pode usar o seguinte sinalizador.


 android { ... buildTypes { debug { pseudoLocalesEnabled true } } ... } 

Selecione a pseudo-localidade desejada nas configurações do telefone.


.


Observe como o layout decorre do texto longo, e alguns elementos da interface do usuário teimosamente não querem ser exibidos da direita para a esquerda.


.


Mais informações podem ser encontradas na documentação .


É importante notar que os pseudo-locais não funcionarão se você alterar o contexto, como na solução acima. Você está mudando o contexto. Portanto, você deve adicionar en-XA e ar-XB à lista de seleção de idioma dentro do aplicativo.


Só isso. Tenha uma boa localização e bom humor!


Thanks

Source: https://habr.com/ru/post/pt461085/


All Articles