Jetpack Compose で複数の言語をサポート
アプリケーションの開発中に 1 つの言語オプションだけを使用するだけでは十分ではありません。 私たちがアプリケーションを公開する場合、別の言語で使用したいユーザーや、開発した言語を知らないユーザーがいる可能性があります。 したがって、アプリケーションが複数の言語をサポートしているという事実により、アプリケーションは常にさらに便利になります。
設定:
プロジェクトに使うデペンデンシーライブラリー:
build.gradle.kts
dependencies {
//...Jetpack composeの必要なデペンデンシーライブラリー
//Navigation
implementation("androidx.navigation:navigation-compose:2.7.5")
//Hilt Navigation
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
// Use Dagger Hilt
implementation ("com.google.dagger:hilt-android:2.48.1")
kapt ("com.google.dagger:hilt-android-compiler:2.48")
// ViewModel
implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
// LiveData
implementation ("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
implementation("androidx.compose.runtime:runtime:1.6.0-beta01")
implementation("androidx.compose.runtime:runtime-livedata:1.6.0-beta01")
implementation("androidx.compose.runtime:runtime-rxjava2:1.6.0-beta01")
//kotlin Coroutines
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
// Preferences DataStore
implementation ("androidx.datastore:datastore-preferences:1.0.0")
}
build.gradle.kts(Project)
build.gradle.kts
plugins {
id("com.android.application") version "8.1.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
id("com.google.devtools.ksp") version "1.9.10-1.0.13" apply false
id ("com.google.dagger.hilt.android") version "2.48" apply false
}
build.gradle.kts(Module)
build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id ("kotlin-kapt")
id ("com.google.dagger.hilt.android")
}
Dependency Injection And Compose View:
Step 1: BaseApp.kt
BaseApp.kt
@HiltAndroidApp
class BaseApp : Application()
Step 2: AndroidManifest.ktにandroid:name
を追加
AndroidManifest.kt
<application
android:name=".BaseApp"
.
.
.
</application>
Step 3: PrefDataStore.ktにsetLanguage/getLanguage
をkey追加
https://gist.github.com/ihridoydas/0aae959fc4bdd3a45f07255cdfddfc13
PrefDataStore.kt
/**
* Preference data store class
* @param context get activity context
*/
class PrefDataStore(private val context: Context) {
private val defaultLanguage = 1
// to make sure there's only one instance
companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("appPref")
//For MultiLanguage
val PREF_LANGUAGE = intPreferencesKey("language")
}
//set and get value
//For MultiLanguage
suspend fun setLanguage(language: Int) {
context.dataStore.edit { preferences ->
preferences[PREF_LANGUAGE] = language
}
}
val getLanguage: Flow<Int> = context.dataStore.data
.map { preferences ->
preferences[PREF_LANGUAGE] ?: defaultLanguage
}
}
Step 4: AppModule.ktに@Module
をdiに提供する(providePreferencesDatastore)
https://gist.github.com/ihridoydas/1099c9b5d3d8de7889e0e2bc4506f991
AppModule.kt
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton
@Provides
fun providePreferencesDatastore(@ApplicationContext appContext: Context): PrefDataStore = PrefDataStore(appContext)
}
Step 5: viewmodel
LanguageViewModel.kt
https://gist.github.com/ihridoydas/95e3545d6178306b8198efa2c93b370d
LanguageViewModel.kt
@HiltViewModel
class LanguageViewModel @Inject constructor(
private val prefDataStore: PrefDataStore
):ViewModel(){
private val _language = MutableLiveData(1)
var language: LiveData<Int> = _language
init {
viewModelScope.launch {
prefDataStore.getLanguage.collect {
_language.value = it
}
}
}
suspend fun saveLanguage(language: Int) {
prefDataStore.setLanguage(language)
}
}
Step 6: values/strings.xml
string resourcesを追加
こちらは、以下のような3言語のresourcesを追加します。["English","Japanese","Bangla"]
English: strings.xml
strings.xml
<resources>
<string name="app_name">AndroidBlog</string>
<!-- For MultiLanguage-->
<string name="hello">Hello</string>
<string name="love">Love</string>
<string name="android">Android</string>
<string name="content">Jetpack Compose is Android’s modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.</string>
</resources>
```strings.xml
#### <ins>Japanese:</ins> strings.xml
```strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Androidブログ</string>
<!-- For MultiLanguage-->
<string name="hello">こんにちは</string>
<string name="love">愛する</string>
<string name="android">アンドロイド</string>
<string name="content">Jetpack Compose は、ネイティブ UI を構築するための Android の最新ツールキットです。 Android での UI 開発を簡素化し、加速します。 少ないコード、強力なツール、直感的な Kotlin API を使用して、アプリをすばやく実現します。</string>
</resources>
Bangla: strings.xml
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">অ্যান্ড্রয়েডব্লগ</string>
<!-- For MultiLanguage-->
<string name="hello">হ্যালো</string>
<string name="love">ভালবাসা</string>
<string name="android">অ্যান্ড্রয়েড</string>
<string name="content">জেটপ্যাক কম্পোজ হল নেটিভ UI তৈরির জন্য অ্যান্ড্রয়েডের আধুনিক টুলকিট। এটি অ্যান্ড্রয়েডে UI ডেভেলপমেন্টকে সহজ করে এবং ত্বরান্বিত করে। কম কোড, শক্তিশালী টুল এবং স্বজ্ঞাত Kotlin API-এর সাহায্যে আপনার অ্যাপকে দ্রুত প্রাণবন্ত করুন।</string>
</resources>
Step 7: Compose View
MultiLanguageScreen.kt
https://gist.github.com/ihridoydas/e9078b49c6373edb447db75968edc92f
MultiLanguageScreen.kt
private val language = listOf("English", "Japanese","Bangla")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MultiLanguage(
viewModel: LanguageViewModel = hiltViewModel(),
//navController: NavController,
//onBackPress : ()->Unit,
) {
val scope = rememberCoroutineScope()
val currentLanguage = viewModel.language.observeAsState().value
val menuExpanded = remember { mutableStateOf(false) }
SetLanguage(position = currentLanguage!!)
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = {
Text(
text = "Multi Language",
fontSize = 20.sp,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
},
navigationIcon = {
IconButton(
onClick = {
//onBackPress()
},
modifier = Modifier
) {
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
}
},
actions = {
IconButton(onClick = { menuExpanded.value = true }) {
Icon(
Icons.Filled.MoreVert,
contentDescription = "Menu",
tint = Color.Black
)
}
Column(
modifier = Modifier.wrapContentSize(Alignment.TopStart)
) {
DropdownMenu(
expanded = menuExpanded.value,
onDismissRequest = {
menuExpanded.value = false
},
modifier = Modifier
.width(150.dp)
.wrapContentSize(Alignment.TopStart)
) {
DropdownMenuItem(
text = {Text(text = stringResource(id = R.string.hello))},
onClick = { /*TODO*/ })
DropdownMenuItem(
text = {Text(text = stringResource(id = R.string.love))},
onClick = { /*TODO*/ })
DropdownMenuItem(
text = {Text(text = stringResource(id = R.string.android))},
onClick = { /*TODO*/ })
}
}
}
)
}
) {
Column(
modifier = Modifier
.padding(it)
.verticalScroll(rememberScrollState(0))
.fillMaxSize()
) {
Spacer(modifier = Modifier.height(8.dp))
LanguagePicker(currentLanguage) { selected ->
scope.launch {
viewModel.saveLanguage(selected)
}
}
}
}
}
@Composable
fun LanguagePicker(
selectedPosition: Int,
onLanguageSelected: (Int) -> Unit
) {
Card(
modifier = Modifier.fillMaxSize(),
shape = RoundedCornerShape(16.dp),
) {
LanguageContentPortrait(selectedPosition, onLanguageSelected)
}
}
@Composable
fun LanguageContentPortrait(
selectedPosition: Int,
onLanguageSelected: (Int) -> Unit
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
ToggleGroup(selectedPosition = selectedPosition, onClick = onLanguageSelected)
Spacer(modifier = Modifier.height(10.dp))
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = stringResource(id = R.string.content),
modifier = Modifier.fillMaxSize(),
textAlign = TextAlign.Center
)
}
}
}
@Composable
private fun SetLanguage(position: Int) {
val locale = Locale(
when(position){
1-> "ja"
2-> "bn"
else -> {"en"}
}
)
val configuration = LocalConfiguration.current
configuration.setLocale(locale)
val resources = LocalContext.current.resources
resources.updateConfiguration(configuration, resources.displayMetrics)
}
@Composable
private fun ToggleGroup(
selectedPosition: Int,
onClick: (Int) -> Unit
) {
val shape = RoundedCornerShape(4.dp)
Row(
modifier = Modifier
.padding(vertical = 8.dp)
.clip(shape)
.border(1.dp, Color(0xFFAAAAAA), shape)
) {
language.forEachIndexed { index, element ->
val verticalPadding = if (index == selectedPosition) 8.dp else 0.dp
Text(
text = element,
color = if (index != selectedPosition) Color.Black else Color.White,
modifier = Modifier
.align(Alignment.CenterVertically)
.run {
if (index != selectedPosition) this
else background(MaterialTheme.colorScheme.primary).border(1.dp, Color.Gray)
}
.clickable(
onClick = { onClick(index) },
role = Role.RadioButton
)
.padding(horizontal = 16.dp, vertical = verticalPadding)
)
}
}
}
Step 7: Main View
MainActivity.kt
MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
AndroidBlogTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MultiLanguage()
}
}
}
}
}