LoginSignup
1
0

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: viewmodelLanguageViewModel.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.xmlstring resourcesを追加

こちらは、以下のような3言語のresourcesを追加します。["English","Japanese","Bangla"]
strings.gif

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 ViewMultiLanguageScreen.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 ViewMainActivity.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()
                }
            }
        }
    }
}

結果: GitHub サンプル

Screen_recording_20231123_132136.gif
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0