Skip to content

Commit db1975b

Browse files
anonfadedCopilot
andauthored
Added 5 new languages with a new dropdown menu, extracted hardcoded texts and added icons to sorting menu (#112)
* Add localization support for multiple languages including German, Spanish, French, Hindi, and Urdu - Created new strings.xml files for German (values-de), Spanish (values-es), French (values-fr), Hindi (values-hi), and Urdu (values-ur) with translations for all app strings. - Extracted the remaining hardcoded texts into Strings * chore: extracted hardcoded texts form TagSelectionBottomSheet into strings * fix: update LanguageSelectionDialog to use ListItemDefaults for transparent bg langauge items * fix: correct formatting issues in string resources for German, Spanish, Hindi, and Urdu * Update app/src/main/res/values-ur/strings.xml Co-authored-by: Copilot <[email protected]> * fix: update language display handling in LanguageSelectionDialog and Settings screen * fix: update icons in FilterMenu for sorting options * fix: reorder and update tag management strings in Hindi localization * fix: reorganize and restore tag management strings in German localization --------- Co-authored-by: Copilot <[email protected]>
1 parent f8df059 commit db1975b

File tree

20 files changed

+1222
-60
lines changed

20 files changed

+1222
-60
lines changed

app/src/main/java/com/yogeshpaliyal/deepr/MainActivity.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.yogeshpaliyal.deepr
22

3+
import android.content.Context
34
import android.content.Intent
45
import android.os.Bundle
56
import androidx.activity.ComponentActivity
@@ -19,14 +20,20 @@ import androidx.navigation3.runtime.NavEntry
1920
import androidx.navigation3.runtime.rememberSavedStateNavEntryDecorator
2021
import androidx.navigation3.ui.NavDisplay
2122
import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator
23+
import com.yogeshpaliyal.deepr.preference.AppPreferenceDataStore
2224
import com.yogeshpaliyal.deepr.ui.screens.AboutUs
2325
import com.yogeshpaliyal.deepr.ui.screens.AboutUsScreen
2426
import com.yogeshpaliyal.deepr.ui.screens.Settings
2527
import com.yogeshpaliyal.deepr.ui.screens.SettingsScreen
2628
import com.yogeshpaliyal.deepr.ui.screens.home.Home
2729
import com.yogeshpaliyal.deepr.ui.screens.home.HomeScreen
2830
import com.yogeshpaliyal.deepr.ui.theme.DeeprTheme
31+
import com.yogeshpaliyal.deepr.util.LanguageUtil
32+
import com.yogeshpaliyal.deepr.viewmodel.AccountViewModel
2933
import kotlinx.coroutines.flow.MutableStateFlow
34+
import kotlinx.coroutines.flow.first
35+
import kotlinx.coroutines.runBlocking
36+
import org.koin.androidx.viewmodel.ext.android.viewModel
3037

3138
data class SharedLink(
3239
val url: String,
@@ -35,6 +42,28 @@ data class SharedLink(
3542

3643
class MainActivity : ComponentActivity() {
3744
val sharingLink = MutableStateFlow<SharedLink?>(null)
45+
private val accountViewModel: AccountViewModel by viewModel()
46+
47+
override fun attachBaseContext(newBase: Context?) {
48+
super.attachBaseContext(
49+
newBase?.let { context ->
50+
try {
51+
val preferenceDataStore = AppPreferenceDataStore(context)
52+
val languageCode =
53+
runBlocking {
54+
preferenceDataStore.getLanguageCode.first()
55+
}
56+
if (languageCode.isNotEmpty()) {
57+
LanguageUtil.updateLocale(context, languageCode)
58+
} else {
59+
context
60+
}
61+
} catch (e: Exception) {
62+
context
63+
}
64+
},
65+
)
66+
}
3867

3968
@OptIn(ExperimentalMaterial3Api::class)
4069
override fun onCreate(savedInstanceState: Bundle?) {

app/src/main/java/com/yogeshpaliyal/deepr/preference/AppPreferenceDataStore.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class AppPreferenceDataStore(
2323
private val SYNC_ENABLED = booleanPreferencesKey("sync_enabled")
2424
private val SYNC_FILE_PATH = stringPreferencesKey("sync_file_path")
2525
private val LAST_SYNC_TIME = longPreferencesKey("last_sync_time")
26+
private val LANGUAGE_CODE = stringPreferencesKey("language_code")
2627
}
2728

2829
val getSortingOrder: Flow<@SortType String> =
@@ -50,6 +51,11 @@ class AppPreferenceDataStore(
5051
preferences[LAST_SYNC_TIME] ?: 0L // Default to 0 (never synced)
5152
}
5253

54+
val getLanguageCode: Flow<String> =
55+
context.appDataStore.data.map { preferences ->
56+
preferences[LANGUAGE_CODE] ?: "" // Default to system language
57+
}
58+
5359
suspend fun setSortingOrder(order: @SortType String) {
5460
context.appDataStore.edit { prefs ->
5561
prefs[SORTING_ORDER] = order
@@ -79,4 +85,10 @@ class AppPreferenceDataStore(
7985
prefs[LAST_SYNC_TIME] = timestamp
8086
}
8187
}
88+
89+
suspend fun setLanguageCode(code: String) {
90+
context.appDataStore.edit { prefs ->
91+
prefs[LANGUAGE_CODE] = code
92+
}
93+
}
8294
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.yogeshpaliyal.deepr.ui.components
2+
3+
import androidx.compose.foundation.clickable
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.shape.RoundedCornerShape
8+
import androidx.compose.material3.AlertDialog
9+
import androidx.compose.material3.ListItem
10+
import androidx.compose.material3.ListItemDefaults
11+
import androidx.compose.material3.MaterialTheme
12+
import androidx.compose.material3.RadioButton
13+
import androidx.compose.material3.Text
14+
import androidx.compose.material3.TextButton
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.draw.clip
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.res.stringResource
20+
import androidx.compose.ui.unit.dp
21+
import com.yogeshpaliyal.deepr.R
22+
import com.yogeshpaliyal.deepr.util.LanguageUtil
23+
24+
@Composable
25+
fun LanguageSelectionDialog(
26+
currentLanguageCode: String,
27+
onLanguageSelect: (String) -> Unit,
28+
onDismiss: () -> Unit,
29+
modifier: Modifier = Modifier,
30+
) {
31+
AlertDialog(
32+
modifier = modifier,
33+
onDismissRequest = onDismiss,
34+
title = {
35+
Text(
36+
text = stringResource(R.string.language_dialog_title),
37+
style = MaterialTheme.typography.headlineSmall,
38+
)
39+
},
40+
text = {
41+
Column {
42+
LanguageUtil.availableLanguages.forEach { language ->
43+
val displayName =
44+
if (language.code == LanguageUtil.SYSTEM_DEFAULT) {
45+
stringResource(R.string.system_default)
46+
} else {
47+
language.nativeName
48+
}
49+
50+
val supportingText =
51+
if (language.code == LanguageUtil.SYSTEM_DEFAULT) {
52+
null // No supporting text for system default
53+
} else if (language.name != language.nativeName) {
54+
language.name
55+
} else {
56+
null
57+
}
58+
59+
ListItem(
60+
modifier =
61+
Modifier
62+
.fillMaxWidth()
63+
.clip(RoundedCornerShape(8.dp))
64+
.clickable {
65+
onLanguageSelect(language.code)
66+
onDismiss()
67+
}.padding(vertical = 4.dp),
68+
headlineContent = {
69+
Text(
70+
text = displayName,
71+
style = MaterialTheme.typography.bodyLarge,
72+
)
73+
},
74+
supportingContent =
75+
supportingText?.let { text ->
76+
{
77+
Text(
78+
text = text,
79+
style = MaterialTheme.typography.bodyMedium,
80+
color = MaterialTheme.colorScheme.onSurfaceVariant,
81+
)
82+
}
83+
},
84+
trailingContent = {
85+
RadioButton(
86+
selected = currentLanguageCode == language.code,
87+
onClick = {
88+
onLanguageSelect(language.code)
89+
onDismiss()
90+
},
91+
)
92+
},
93+
colors =
94+
ListItemDefaults.colors(
95+
containerColor = Color.Transparent,
96+
),
97+
)
98+
}
99+
}
100+
},
101+
confirmButton = {
102+
TextButton(onClick = onDismiss) {
103+
Text(stringResource(R.string.cancel))
104+
}
105+
},
106+
)
107+
}

app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/AboutUs.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
2727
import androidx.compose.ui.Alignment
2828
import androidx.compose.ui.Modifier
2929
import androidx.compose.ui.draw.clip
30+
import androidx.compose.ui.draw.scale
31+
import androidx.compose.ui.platform.LocalLayoutDirection
3032
import androidx.compose.ui.platform.LocalUriHandler
3133
import androidx.compose.ui.res.painterResource
3234
import androidx.compose.ui.res.stringResource
3335
import androidx.compose.ui.text.style.TextAlign
36+
import androidx.compose.ui.unit.LayoutDirection
3437
import androidx.compose.ui.unit.dp
3538
import com.yogeshpaliyal.deepr.BuildConfig
3639
import com.yogeshpaliyal.deepr.R
@@ -48,6 +51,8 @@ fun AboutUsScreen(
4851
backStack: SnapshotStateList<Any>,
4952
modifier: Modifier = Modifier,
5053
) {
54+
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
55+
5156
Scaffold(modifier = modifier.fillMaxSize(), topBar = {
5257
Column {
5358
TopAppBar(
@@ -61,6 +66,7 @@ fun AboutUsScreen(
6166
Icon(
6267
TablerIcons.ArrowLeft,
6368
contentDescription = stringResource(R.string.back),
69+
modifier = if (isRtl) Modifier.scale(-1f, 1f) else Modifier,
6470
)
6571
}
6672
},

app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/Settings.kt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,35 @@ import androidx.compose.material3.TopAppBar
3131
import androidx.compose.runtime.Composable
3232
import androidx.compose.runtime.LaunchedEffect
3333
import androidx.compose.runtime.getValue
34+
import androidx.compose.runtime.mutableStateOf
35+
import androidx.compose.runtime.remember
36+
import androidx.compose.runtime.setValue
3437
import androidx.compose.runtime.snapshots.SnapshotStateList
3538
import androidx.compose.ui.Alignment
3639
import androidx.compose.ui.Modifier
40+
import androidx.compose.ui.graphics.graphicsLayer
3741
import androidx.compose.ui.platform.LocalContext
42+
import androidx.compose.ui.platform.LocalLayoutDirection
3843
import androidx.compose.ui.res.stringResource
3944
import androidx.compose.ui.text.style.TextAlign
45+
import androidx.compose.ui.unit.LayoutDirection
4046
import androidx.compose.ui.unit.dp
4147
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4248
import com.google.accompanist.permissions.ExperimentalPermissionsApi
4349
import com.google.accompanist.permissions.isGranted
4450
import com.google.accompanist.permissions.rememberPermissionState
4551
import com.yogeshpaliyal.deepr.BuildConfig
52+
import com.yogeshpaliyal.deepr.MainActivity
4653
import com.yogeshpaliyal.deepr.R
54+
import com.yogeshpaliyal.deepr.ui.components.LanguageSelectionDialog
55+
import com.yogeshpaliyal.deepr.util.LanguageUtil
4756
import com.yogeshpaliyal.deepr.viewmodel.AccountViewModel
4857
import compose.icons.TablerIcons
4958
import compose.icons.tablericons.ArrowLeft
5059
import compose.icons.tablericons.Download
5160
import compose.icons.tablericons.FileText
5261
import compose.icons.tablericons.InfoCircle
62+
import compose.icons.tablericons.Language
5363
import compose.icons.tablericons.Refresh
5464
import compose.icons.tablericons.Settings
5565
import compose.icons.tablericons.Upload
@@ -82,6 +92,10 @@ fun SettingsScreen(
8292
// Collect the shortcut icon preference state
8393
val useLinkBasedIcons by viewModel.useLinkBasedIcons.collectAsStateWithLifecycle()
8494

95+
// Collect language preference state
96+
val languageCode by viewModel.languageCode.collectAsStateWithLifecycle()
97+
var showLanguageDialog by remember { mutableStateOf(false) }
98+
8599
// Collect sync preference states
86100
val syncEnabled by viewModel.syncEnabled.collectAsStateWithLifecycle()
87101
val syncFilePath by viewModel.syncFilePath.collectAsStateWithLifecycle()
@@ -139,12 +153,20 @@ fun SettingsScreen(
139153
Text(stringResource(R.string.settings))
140154
},
141155
navigationIcon = {
156+
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
157+
142158
IconButton(onClick = {
143159
backStack.removeLastOrNull()
144160
}) {
145161
Icon(
146162
TablerIcons.ArrowLeft,
147163
contentDescription = stringResource(R.string.back),
164+
modifier =
165+
if (isRtl) {
166+
Modifier.graphicsLayer(scaleX = -1f)
167+
} else {
168+
Modifier
169+
},
148170
)
149171
}
150172
},
@@ -332,6 +354,33 @@ fun SettingsScreen(
332354
)
333355
HorizontalDivider()
334356

357+
// Language Selection Setting
358+
ListItem(
359+
modifier =
360+
Modifier.clickable {
361+
showLanguageDialog = true
362+
},
363+
headlineContent = { Text(stringResource(R.string.language)) },
364+
supportingContent = {
365+
Text(
366+
if (languageCode.isEmpty()) {
367+
stringResource(R.string.system_default)
368+
} else {
369+
LanguageUtil.getLanguageNativeName(languageCode).ifEmpty {
370+
stringResource(R.string.system_default)
371+
}
372+
},
373+
)
374+
},
375+
leadingContent = {
376+
Icon(
377+
TablerIcons.Language,
378+
contentDescription = stringResource(R.string.language),
379+
)
380+
},
381+
)
382+
HorizontalDivider()
383+
335384
ListItem(
336385
modifier =
337386
Modifier.clickable(true) {
@@ -368,5 +417,21 @@ fun SettingsScreen(
368417
Spacer(modifier = Modifier.height(16.dp))
369418
}
370419
}
420+
421+
// Language Selection Dialog
422+
if (showLanguageDialog) {
423+
LanguageSelectionDialog(
424+
currentLanguageCode = languageCode,
425+
onLanguageSelect = { selectedLanguageCode ->
426+
viewModel.setLanguageCode(selectedLanguageCode)
427+
showLanguageDialog = false
428+
// Recreate activity to apply language change
429+
(context as? MainActivity)?.let { activity ->
430+
activity.recreate()
431+
}
432+
},
433+
onDismiss = { showLanguageDialog = false },
434+
)
435+
}
371436
}
372437
}

0 commit comments

Comments
 (0)