Локализация относится к изменению текста приложения, форматов валют и других визуальных элементов для соответствия локали пользователя на основе его региональных предпочтений.
С введением языковых предпочтений для каждого приложения (Per-app Language Preference) в Android 13 (API уровня 33) управление языками, специфичными для приложений, стало намного проще.
Настройки языка для приложения
Настройки языка для каждого приложения позволяют пользователям менять язык приложения, не влияя на весь язык системы. Этот новый API упрощает процесс локализации, устраняя необходимость в шаблонном коде и предлагая удобный подход к управлению определенными настройками языка приложения.
Давайте начнем.
Шаг 1: Добавление поддержки нескольких языков
Давайте добавим новый язык в наш каталог ресурсов values-*
в папке res
.
res > New Resource File > Select Locale qualifier > Language > Select Country Language Options> Введите имя файла «strings», чтобы добавить файл strings.xml для нужного языка.
// English Default (values/strings.xml) <resources> <string name="app_name">Firefly</string> <string name="label_email">Email</string> <string name="label_password">Password</string> <string name="label_name">Name</string> </resources> // Nepali (values-ne/strings.xml) <resources> <string name="app_name">Firefly</string> <string name="label_email">ईमेल</string> <string name="label_password">पासवर्ड</string> <string name="label_name">नाम</string> </resources>
Шаг 2: Автоматическая генерация Locale Config
Используйте Gradle для автоматической генерации поддерживаемых локалей. Это позволяет нашему приложению получить ресурсы, необходимые для поддерживаемых локалей на основе strings.xml, которые мы предоставили.
android { ... androidResources{ generateLocaleConfig = true } ... }
В качестве альтернативы мы можем создать файл с именем res/xml/locales_config.xml
и вручную указать языки нашего приложения.
--- locales_config.xml--- <?xml version="1.0" encoding="utf-8"?> <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> <locale android:name="en-US"/> <locale android:name="en-GB"/> <locale android:name="fr"/> <locale android:name="ja"/> </locale-config>
И укажите ссылку на него в AndroidManifest.xml
:
<manifest> ... <application ... android:localeConfig="@xml/locales_config"> </application> </manifest>
Примечание: Google рекомендует автоматически генерировать конфигурацию локали. Это означает, что мы можем пропустить альтернативный шаг выше.
Шаг 3: Установка локали по умолчанию
Чтобы указать язык по умолчанию, создайте файл resources.properties
внутри папки res
:
unqualifiedResLocale = en
Добавив это, наше приложение не будет сходить с ума, если не выбран ни один язык.
Шаг 4: включите функцию Auto Store Locales
Когда этот флаг включен, приложение автоматически запоминает выбор языка пользователем, поэтому нет необходимости вручную сохранять его в Preferences или DataStore.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <application <service android:name="androidx.appcompat.app.AppLocalesMetadataHolderService" android:enabled="false" android:exported="false"> <meta-data android:name="autoStoreLocales" android:value="true" /> </service> </application> </manifest>
И готово, теперь мы можем легко изменить язык приложения из настроек.
Перейдите в Настройки > Система > Языки и ввод > Языки приложений > (выберите приложение) и измените нужный язык.
На этом этапе мы можем изменить язык нашего приложения напрямую из настроек, не создавая отдельный выбор языка внутри.
Кастомный выбор языка
Если мы хотим добавить выбор языка в приложение, чтобы пользователи могли легко переключать языки, не выходя из приложения или не переходя в настроqrb, нам нужно вручную это сделать и уведомить per-app language API о том, что выбранный язык теперь является языком по умолчанию.
Шаг 5: Менеджер локали приложения
Давайте создадим вспомогательный класс, который обрабатывает выбор языка и уведомляет о любых изменениях Per-App Language API.
import android.app.LocaleManager import android.content.Context import android.os.Build import android.os.LocaleList import androidx.appcompat.app.AppCompatDelegate import androidx.core.os.LocaleListCompat data class Language( val code: String, val displayLanguage: String ) val appLanguages = listOf( Language("en", "English"), // default language Language("ne", "नेपाली"), Language("hi", "हिन्दी") ) class AppLocaleManager { fun changeLanguage(context: Context, languageCode: String) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { context.getSystemService(LocaleManager::class.java).applicationLocales = LocaleList.forLanguageTags(languageCode) } else { AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(languageCode)) } } fun getLanguageCode(context: Context,): String { val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { context.getSystemService(LocaleManager::class.java) ?.applicationLocales ?.get(0) } else { AppCompatDelegate.getApplicationLocales().get(0) } return locale?.language ?: getDefaultLanguageCode() } private fun getDefaultLanguageCode(): String { return appLanguages.first().code } }
Теперь, когда у нас есть AppLocaleManager
, мы можем создать собственный пикер языка.
Шаг 6: Интеграция с ViewModel
Мы также можем напрямую инжектировать AppLocaleManager
с помощью Hilt, если это необходимо. А пока давайте рассмотрим самый простой способ использования AppLocaleManager
с ViewModel
.
@HiltViewModel class SettingViewModel @Inject constructor( @ApplicationContext private val context: Context ) : BaseViewModel() { private val appLocaleManager = AppLocaleManager() private val _settingState = MutableStateFlow(SettingState()) val settingState: StateFlow<SettingState> = _settingState init { loadInitialLanguage() } private fun loadInitialLanguage() { val currentLanguage = appLocaleManager.getLanguageCode(context) _settingState.value = _settingState.value.copy(selectedLanguage = currentLanguage) } fun changeLanguage(languageCode: String) { appLocaleManager.changeLanguage(context, languageCode) _settingState.value = _settingState.value.copy(selectedLanguage = languageCode) } } data class SettingState( ... val selectedLanguage: String = "" )
Шаг 7: Создание пользовательского интерфейса
Теперь давайте создадим пользовательский интерфейс для экрана настроек. Композабл SettingContent
отображает список доступных языков в LazyColumn
, выделяя выбранный.
@Composable fun SettingScreen( settingViewModel: SettingViewModel = hiltViewModel(), navigateBack: () -> Unit, ) { val settingState by settingViewModel.settingState.collectAsStateLifecycleAware() val onAppLanguageChanged: (String) -> Unit = { newLanguage -> settingViewModel.changeLanguage(newLanguage) } SettingContent( selectedLanguage = settingState.selectedLanguage, onAppLanguageChanged ) { navigateBack() } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingContent( selectedLanguage: String, onAppLanguageChanged: (String) -> Unit, onNavigateBack: () -> Unit ) { val barScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() Scaffold(topBar = { ChildAppTopBar( stringResource(R.string.settings), barScrollBehavior ) { onNavigateBack() } }) { paddingValues -> Box(modifier = Modifier.padding(paddingValues)) { LazyColumn { items(appLanguages.size) { index -> LanguageRow(appLanguages[index], appLanguages[index].code == selectedLanguage) { onAppLanguageChanged(it.code) } } } } } }
Спасибо, что прочитали! Я надеюсь, что это руководство поможет вам создавать лучшие приложения со многими языками.