Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1eea700
intial functionality checked
Mandvii Feb 9, 2026
a2f29eb
Enable automatic per-app language support
Mandvii Feb 9, 2026
829dcd8
This commit introduces an in-app language selection feature, allowing…
Mandvii Feb 10, 2026
1d7b237
This commit refactors the application's language selection functional…
Mandvii Feb 10, 2026
39eee9f
This commit removes an unused `systemImpl` parameter and addresses va…
Mandvii Feb 10, 2026
680b332
Simplify language and system resource management
Mandvii Feb 11, 2026
158ef35
Simplify language and system resource management
Mandvii Feb 11, 2026
889c9c9
Add language selection to onboarding screen
Mandvii Feb 11, 2026
c53314d
feat: Add Hindi translation for "Get Started" button
Mandvii Feb 11, 2026
808fd50
Clean up language selection and related code
Mandvii Feb 11, 2026
097b55a
Extract LanguageDropdown into a separate component
Mandvii Feb 11, 2026
d03dc41
Merge branch 'main' into dev-language
Mandvii Feb 11, 2026
1980c98
Centralize UI language selection logic
Mandvii Feb 11, 2026
c19a4a8
Refactor: Centralize app language logic into `SupportedLanguagesConfig`
Mandvii Feb 12, 2026
b3306e7
feat: Add Nepali language support and refactor language settings
Mandvii Feb 12, 2026
97934f2
Refactor: Centralize app language logic into `SupportedLanguagesConfig`
Mandvii Feb 12, 2026
3ef117e
Merge remote-tracking branch 'origin/dev-language' into dev-language
Mandvii Feb 12, 2026
17ba425
Refactor: Improve system language handling and UI
Mandvii Feb 12, 2026
396d7f0
Merge branch 'main' into dev-language
Mandvii Feb 12, 2026
3433512
Refactor: Improve system language handling and UI
Mandvii Feb 12, 2026
6b21dc2
Refactor: Centralize language loading logic in `SupportedLanguagesCon…
Mandvii Feb 12, 2026
523798d
Simplify language loading logic in `SettingsViewModel`
Mandvii Feb 12, 2026
a0a0f2e
Simplify language loading logic in `SettingsViewModel`
Mandvii Feb 12, 2026
c5ae787
Simplify language loading logic in `SettingsViewModel`
Mandvii Feb 13, 2026
0e4f3b6
added language test
poojaustad Feb 16, 2026
6841501
Merge branch 'main' into dev-language
Mandvii Feb 17, 2026
65542d7
added id
poojaustad Feb 17, 2026
7e3e893
Merge remote-tracking branch 'origin/dev-language' into dev-language
Mandvii Feb 17, 2026
66c5153
Refactor: Improve language dropdown UI and testability
Mandvii Feb 17, 2026
d8dd575
updated id
poojaustad Feb 17, 2026
5297a47
Merge remote-tracking branch 'origin/dev-language' into dev-language
poojaustad Feb 17, 2026
6350335
updated test -
poojaustad Feb 17, 2026
31d20eb
updated test - password
poojaustad Feb 18, 2026
879a2f3
The settings icon in the app launcher is now always visible for all u…
Mandvii Feb 18, 2026
e41963c
Merge remote-tracking branch 'origin/dev-language' into dev-language
Mandvii Feb 18, 2026
79539a1
updated description
poojaustad Feb 19, 2026
fd63350
updated Readme
poojaustad Feb 19, 2026
eec2d2f
Merge branch 'refs/heads/main' into dev-language
Mandvii Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ onFlowStart:
SCHOOL_NAME: ${SCHOOL_NAME}
USERNAME: ${USERNAME}
URL_SUBSTITUTION: ${URL_SUBSTITUTION}
NAME: "006_user_signup_using_passkey_test.yaml"
NAME: "007_user_signup_using_passkey_test.yaml"

onFlowComplete:
- runScript:
Expand Down
63 changes: 63 additions & 0 deletions .maestro/flows/006_001_user_set_language_on_app_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
appId: world.respect.app
onFlowStart:
- clearState: world.respect.app
- runScript:
file: "scripts/school_init.js"
env:
TESTCONTROLLER_URL: ${TESTCONTROLLER_URL}
SCHOOL_ADMIN_PASSWORD: ${SCHOOL_ADMIN_PASSWORD}
DIR_ADMIN_AUTH_HEADER: ${DIR_ADMIN_AUTH_HEADER}
SCHOOL_URL: ${SCHOOL_URL}
SCHOOL_NAME: ${SCHOOL_NAME}
URL_SUBSTITUTION: ${URL_SUBSTITUTION}
NAME: "006_001_user_set_language_on_app_test"

onFlowComplete:
- runScript:
file: "scripts/teardown.js"
---
- clearState: world.respect.app
- launchApp:
arguments:
respect_directory: ${output.SCHOOL_URL}
- tapOn:
id: "choose_language"
- assertVisible: "Choose your language"
- assertVisible: "Use system default (English)"
- assertVisible: "हिन्दी"
- tapOn: "English"
- assertVisible: "English"
- tapOn: "Get Started"
- runFlow:
file: "subflows/get_started_select_school_by_name.yaml"
env:
SCHOOL_NAME: ${SCHOOL_NAME}
- tapOn:
id: "username"
- inputText: "admin"
- tapOn:
id : "password"
- inputText: ${SCHOOL_ADMIN_PASSWORD}
- tapOn: "Login"
- assertVisible:
id: "app_title"
text: "Apps"
- tapOn:
id: "Settings"
- assertVisible:
id: "app_title"
text: "Settings"
- assertVisible: "English"
- tapOn: "Language"
- assertVisible: "Use system default (English)"
- assertVisible: "English"
- tapOn: "हिन्दी"
- assertVisible:
id: "app_title"
text: "सेटिंग्स"
- tapOn: "भाषा"
- tapOn: "English"
- assertVisible:
id: "app_title"
text: "Settings"

7 changes: 7 additions & 0 deletions respect-app-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ kotlin {
implementation(libs.reorderable)
implementation(libs.kscan)
implementation(libs.qrose)
implementation(compose.components.resources)
}

desktopMain.dependencies {
Expand Down Expand Up @@ -177,13 +178,19 @@ android {
namespace = "world.respect.app"
compileSdk = libs.versions.android.compileSdk.get().toInt()

androidResources{
generateLocaleConfig = true
localeFilters.addAll(listOf("en", "hi", "ne"))
}

defaultConfig {
applicationId = "world.respect.app"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 121
versionName = "1.0.21"


for(propName in ACRA_PROP_NAMES) {
buildConfigField(
type = "String",
Expand Down
9 changes: 9 additions & 0 deletions respect-app-compose/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@
</intent-filter>
</activity>

<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package world.respect

import android.content.Context
import androidx.core.os.LocaleListCompat
import androidx.room.Room
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import com.russhwolf.settings.Settings
Expand Down Expand Up @@ -97,6 +98,7 @@ import world.respect.libxxhash.XXHasher64Factory
import world.respect.libxxhash.XXStringHasher
import world.respect.libxxhash.jvmimpl.XXHasher64FactoryCommonJvm
import world.respect.libxxhash.jvmimpl.XXStringHasherCommonJvm
import world.respect.shared.domain.applanguage.SupportedLanguagesConfig
import world.respect.shared.domain.account.RespectAccount
import world.respect.shared.domain.account.RespectAccountManager
import world.respect.shared.domain.account.RespectAccountSchoolScopeLink
Expand Down Expand Up @@ -129,6 +131,9 @@ import world.respect.shared.domain.account.username.filterusername.FilterUsernam
import world.respect.shared.domain.account.username.validateusername.ValidateUsernameUseCase
import world.respect.shared.domain.account.validatepassword.ValidatePasswordUseCase
import world.respect.shared.domain.account.validateqrbadge.ValidateQrCodeUseCase
import world.respect.shared.domain.applanguage.LocaleSettingDelegateAndroid
import world.respect.shared.domain.applanguage.SetLanguageUseCase
import world.respect.shared.domain.applanguage.SetLanguageUseCaseAndroid
import world.respect.shared.domain.appversioninfo.GetAppVersionInfoUseCase
import world.respect.shared.domain.appversioninfo.GetAppVersionInfoUseCaseAndroid
import world.respect.shared.domain.clipboard.SetClipboardStringUseCase
Expand Down Expand Up @@ -277,6 +282,21 @@ val appKoinModule = module {
LaunchSendEmailAndroid(androidContext())
}

single {
SupportedLanguagesConfig(
systemLocales = LocaleListCompat.getAdjustedDefault().let { localeList ->
(0 .. localeList.size()).mapNotNull { localeList[it]?.language }
},
localeSettingDelegate = LocaleSettingDelegateAndroid()
)
}

single<SetLanguageUseCase> {
SetLanguageUseCaseAndroid(
languagesConfig = get()
)
}

single<GetDeferredDeepLinkUseCase>(createdAtStart = true) {
GetDeferredDeepLinkUseCaseAndroid(
context = androidContext(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unqualifiedResLocale=en-US
3 changes: 3 additions & 0 deletions respect-app-compose/src/androidMain/res/values-hi/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
2 changes: 2 additions & 0 deletions respect-app-compose/src/androidMain/res/values-ne/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import world.respect.app.view.report.list.ReportTemplateListScreen
import world.respect.app.view.scanqrcode.ScanQRCodeScreen
import world.respect.app.view.schooldirectory.edit.SchoolDirectoryEditScreen
import world.respect.app.view.schooldirectory.list.SchoolDirectoryListScreen
import world.respect.app.view.settings.SettingsScreenForViewModel
import world.respect.app.view.settings.SettingsScreen
import world.respect.app.viewmodel.respectViewModel
import world.respect.shared.navigation.AccountList
import world.respect.shared.navigation.Acknowledgement
Expand Down Expand Up @@ -154,8 +154,6 @@ import world.respect.shared.viewmodel.report.list.ReportListViewModel
import world.respect.shared.viewmodel.report.list.ReportTemplateListViewModel
import world.respect.shared.viewmodel.schooldirectory.edit.SchoolDirectoryEditViewModel
import world.respect.shared.viewmodel.schooldirectory.list.SchoolDirectoryListViewModel
import world.respect.shared.viewmodel.settings.SettingsViewModel


@Composable
fun AppNavHost(
Expand Down Expand Up @@ -539,12 +537,11 @@ fun AppNavHost(
)
}
composable<Settings> {
val viewModel: SettingsViewModel = respectViewModel(
onSetAppUiState = onSetAppUiState,
navController = respectNavController
)
SettingsScreenForViewModel(
viewModel = viewModel
SettingsScreen(
viewModel = respectViewModel(
onSetAppUiState = onSetAppUiState,
navController = respectNavController
)
)
}
composable<ScanQRCode> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package world.respect.app.components


import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import org.jetbrains.compose.resources.stringResource
import world.respect.shared.domain.applanguage.SupportedLanguagesConfig
import world.respect.shared.generated.resources.Res
import world.respect.shared.generated.resources.choose_language
import world.respect.shared.generated.resources.default_language


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LanguageDropdown(
selected: SupportedLanguagesConfig.UiLanguage?,
languages: List<SupportedLanguagesConfig.UiLanguage>,
onSelected: (SupportedLanguagesConfig.UiLanguage) -> Unit,
enabled: Boolean
) {
var expanded by remember { mutableStateOf(false) }

ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { if (enabled) expanded = !expanded },
modifier = Modifier.testTag("choose_language")
) {
OutlinedTextField(
value = getLanguageDisplayText(selected),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(Res.string.choose_language)) },
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded)
},
modifier = Modifier
.menuAnchor()
.fillMaxWidth()
.defaultItemPadding()
)

ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
languages.forEach { lang ->
DropdownMenuItem(
text = { Text(getLanguageDisplayText(lang) ) },
onClick = {
onSelected(lang)
expanded = false
}
)
}
}
}
}

@Composable
fun getLanguageDisplayText(lang: SupportedLanguagesConfig.UiLanguage?): String {
return if(lang?.langCode?.isEmpty() == true)
stringResource(Res.string.default_language, lang.langDisplay)
else
lang?.langDisplay ?: ""
}


Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import world.respect.images.respectImagePainter
import world.respect.shared.viewmodel.onboarding.OnboardingUiState
import world.respect.shared.viewmodel.onboarding.OnboardingViewModel
import org.jetbrains.compose.resources.stringResource
import world.respect.app.components.LanguageDropdown
import world.respect.app.components.defaultItemPadding
import world.respect.shared.domain.applanguage.SupportedLanguagesConfig
import world.respect.shared.generated.resources.Res
import world.respect.shared.generated.resources.get_started
import world.respect.shared.generated.resources.onboardingDescription1
Expand All @@ -47,6 +49,7 @@ import world.respect.shared.generated.resources.onboardingTitle4
import world.respect.shared.generated.resources.send_usage_stats_and_crash_reports



data class OnboardingItem(
val onboardingImage: RespectImage,
val onboardingTitle: String,
Expand All @@ -62,6 +65,7 @@ fun OnboardingScreen(
uiState = uiState,
onClickGetStartedButton = viewModel::onClickGetStartedButton,
onToggleUsageStatsOptIn = viewModel::onToggleUsageStatsOptIn,
onLanguageSelected = viewModel::onLanguageSelected
)
}

Expand All @@ -70,6 +74,7 @@ fun OnboardingScreen(
uiState: OnboardingUiState,
onClickGetStartedButton: () -> Unit,
onToggleUsageStatsOptIn: () -> Unit,
onLanguageSelected: (SupportedLanguagesConfig.UiLanguage) -> Unit
) {

val onboardingItem = listOf(
Expand Down Expand Up @@ -104,6 +109,14 @@ fun OnboardingScreen(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {

LanguageDropdown(
selected = uiState.selectedLanguage,
languages = uiState.availableLanguages,
onSelected = onLanguageSelected,
enabled = !uiState.isLoading
)

HorizontalPager(
state = pagerState,
modifier = Modifier
Expand Down Expand Up @@ -204,5 +217,3 @@ fun OnboardingScreen(
}




Loading