Cambiar idioma en la aplicación de Android


Hay una manera fácil de implementar el cambio de idioma en una aplicación de actividad única. La pila de pantallas en este enfoque no se restablece, el usuario permanece donde cambió el idioma. Cuando el usuario va a las pantallas anteriores, se muestran inmediatamente traducidas. Y el resultado de la localización de números, sumas de dinero e intereses puede sorprender a los diseñadores.


Hebrew, motherfucker! Do you speak it?!


¿Qué se discutirá y qué no se discutirá?


Además no habrá nada sobre:


  • La teoría que subyace en la salida formateada de las cadenas y los detalles de implementación de las bibliotecas que se ocupan de esto. Es decir, algo que te ayudaría a escribir tu biblioteca.
  • Recursos de cadena, vector y otros. Sobre qué calificadores de recursos usar, qué imágenes en árabe se deben mostrar de derecha a izquierda y cuáles no, y otras sutilezas.
  • El proceso de traducción centralizada de recursos para todas las plataformas. Cómo organizarlo para que todos vivan bien, incluso los apodos de iOS.

Y hablaremos de:


  • Practica Considere el problema, sus limitaciones y su solución con diagramas, ejemplos y fragmentos de código.
  • La API del SDK que se usó para esta solución.
  • Características del formato de valores numéricos para diferentes estándares regionales, que los diseñadores deben conocer.

Que queremos hacer


Deje que haya una pantalla con configuraciones en nuestra aplicación, y queremos agregarle un par de elementos nuevos, uno de los cuales le permitiría cambiar el idioma de la aplicación y el otro para cambiar la moneda en la que se muestran las cantidades de dinero. Aquí hay algunos ejemplos de cómo se vería esto.


Telegram.  .


Telegram.  .


Además de traducir el texto y mostrar el diseño de derecha a izquierda, esta configuración debería afectar el formato para mostrar valores numéricos. Es necesario que todo se muestre de acuerdo con la configuración regional seleccionada.


.


Solución arquitectónica


Imagine que nuestra aplicación está escrita de acuerdo con el enfoque de actividad única . Entonces el mecanismo de cambio de idioma se puede implementar de la siguiente manera.


.


SettingsInteractor es la fuente del valor de idioma actual. Le permite suscribirse a este valor, recibirlo sincrónicamente y solo suscribirse a las actualizaciones. Si es necesario, puede introducir una abstracción adicional sobre SettingsInteractor acuerdo con el principio de separación de la interfaz . En el diagrama, se omiten detalles irrelevantes.


AppActivity en la creación reemplaza el contexto con uno nuevo para que la aplicación use recursos para el idioma seleccionado.


 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 a su vez, se suscribe a las actualizaciones de idioma y notifica la Vista de los cambios.


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

AppActivity vuelve a crear cuando se recibe una notificación de un cambio de idioma.


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

.


AppActivity es el único en la aplicación. Todas las demás pantallas se implementan en fragmentos. Por lo tanto, al recrear la actividad, el sistema guarda la pila de pantalla. Si regresa a las pantallas anteriores, se reiniciarán y se mostrarán traducidas. El usuario permanecerá en la lista de selección de idioma y verá el resultado de su elección al instante.


Números de formato, efectivo e intereses


Además de reemplazar el contexto, es necesario formatear los datos: números, dinero, intereses. Deje que cada Vista delegue esta tarea a un componente separado, llamémoslo UiLocalizer .


UiLocalizer.


UiLocalizer usa las instancias adecuadas de NumberFormat para convertir un número en una cadena.


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

Tenga en cuenta que la moneda debe establecerse por separado.


Si guarda ciclos de CPU y bits de memoria, y cambiar la moneda y el idioma es la función principal y más utilizada de su aplicación, entonces, por supuesto, necesita un caché.


Representación de idiomas y monedas.


Las instancias de la clase Locale se crean mediante una etiqueta de idioma , que consta de un código de idioma de dos letras y un código de región de dos letras. Y las instancias de la clase Currency se basan en un código ISO de tres letras . De esta forma, el idioma y la moneda se deben serializar para guardar en el disco o transferir a través de la red, y luego será bueno. Aquí hay algunos ejemplos.


 // 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") ) 

Características para formatear valores numéricos


El resultado de formatear números de acuerdo con los estándares regionales puede diferir de lo esperado. El símbolo de moneda o su código de tres letras en diferentes idiomas se mostrará de diferentes maneras. Aparecerán signos negativos para valores monetarios negativos en lugares inesperados, y en algunos lugares se mostrarán paréntesis. El signo de porcentaje puede no ser exactamente el signo al que estamos acostumbrados.


El hecho es que, desde el punto de vista de los patrones regionales, la línea final consiste en un prefijo y sufijo para números positivos y negativos, un separador de miles y un separador decimal, y son diferentes para diferentes lugares.


Los numeros


IdiomaPrefijo negativoSufijo NegativoPrefijo PositivoSufijo positivoSeparador de agrupamientoSeparador decimal
ru-RU"-"""","
en-US"-"",""."
iw-IL"-"",""."
ar-AE"-""٬""٫"
fr-FR"-"""","
de-de"-""."","
de-CH"-""'""."
da-dk"-""."","

Monedas


IdiomaPrefijo negativoSufijo NegativoPrefijo PositivoSufijo positivoSeparador de agrupamientoSeparador decimal
ru-RU"-""₽""₽"""","
en-US"- $""$"",""."
iw-IL"-""₪""₪"",""."
ar-AE"-""د.إ.""د.إ.""٬""٫"
fr-FR"-""€""€"""","
de-de"-""€""€""."","
de-CH"CHF-"CHF"'""."
da-dk"-""kr.""kr.""."","

Interés


IdiomaPrefijo negativoSufijo NegativoPrefijo PositivoSufijo positivoSeparador de agrupamientoSeparador decimal
ru-RU"-""%""%"""","
en-US"-""%""%"",""."
iw-IL"-""%""%"",""."
ar-AE"-""٪""٪""٬""٫"
fr-FR"-""%""%"""","
de-de"-""%""%""."","
de-CH"-""%""%""'""."
da-dk"-""%""%""."","

Además, los resultados de formato para Android SDK y JDK pueden ser diferentes. Además, todas las opciones son correctas, cada una de ellas se usa en ciertos contextos.


Android  JDK.


Formato decimal


Cuando creamos NumberFormat para formatear ciertos valores, obtenemos objetos de la clase DecimalFormat que simplemente están configurados por diferentes plantillas. Al DecimalFormat objeto al tipo DecimalFormat y usar su interfaz, puede cambiar partes de la plantilla para romper todo. Pero es mejor adorar la entrega.


DecimalFormat.


También puedes escribir una prueba para disfrutar de la variedad. No para todas las configuraciones regionales, la misma moneda se muestra con un símbolo.


.


Al final


El esquema general de la solución es el siguiente.


.


AppActivity Life Cycle es el ciclo de vida de toda la aplicación. Por lo tanto, es suficiente recrearlo para reiniciar toda la aplicación y aplicar el idioma seleccionado. Y dado que solo hay una actividad, es suficiente mantener la suscripción para cambiar el idioma en un solo lugar: en AppPresenter .


Como vimos, los formatos regionales para generar números no son triviales. No debe establecer rígidamente una plantilla única para todas las ocasiones. Es mejor confiar el formato del SDK y aceptar que los números se mostrarán de acuerdo con el estándar, y no como se dibuja en los diseños.


¿Qué es más fácil de probar? (bonificación)


Para ahorrar tiempo, puede usar la siguiente bandera.


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

Seleccione el pseudo-locale deseado en la configuración del teléfono.


.


Y observe cómo va el diseño debido al texto largo, y algunos elementos de la IU no quieren mostrarse obstinadamente de derecha a izquierda.


.


Se puede encontrar más información en la documentación .


Vale la pena señalar que las pseudo-configuraciones regionales no funcionarán si cambia el contexto, como en la solución anterior. Estás cambiando el contexto. Por lo tanto, debe agregar en-XA y ar-XB a la lista de selección de idioma dentro de la aplicación.


Eso es todo. ¡Que tengas una buena localización y buen humor!


Thanks

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


All Articles