Skip to content

Commit 8f554c9

Browse files
authored
In-app Persian (Farsi) localization with runtime language switching (#43)
* fix: add missing language code persistence to settings store and models - Add WhiteDnsLanguage object with En/Fa constants to WhiteDnsModels.kt - Add languageCode field to WhiteDnsSettings data class with default En - Add normalizeLanguageCode function for language code validation - Call normalizeLanguageCode in syncSelectedConnectionProfileFields - Add KeyLanguageCode constant to WhiteDnsSettingsStore - Load languageCode from preferences in WhiteDnsSettingsStore.load() - Save languageCode to preferences in WhiteDnsSettingsStore.save() This fixes the issue where language changes weren't persisting across app restarts. * feat(i18n): add language switcher to App Settings dialog - Add LanguageModeSegmentedControl composable (English/فارسی toggle) - Update AppSettingsDialog to show language picker below theme picker - Update HeaderCard to accept and thread languageCode/onLanguageCodeChange - Update all 4 HeaderCard call sites to wire language setting - Update WhiteDnsTheme to accept languageCode and inject RTL layout direction - Update MainActivity to pass languageCode to WhiteDnsTheme * feat(i18n): implement CompositionLocal string localization for key UI elements - Create WhiteDnsStrings.kt interface with EnglishStrings and PersianStrings - Add LocalWhiteDnsStrings and WhiteDnsL10n to WhiteDnsTheme.kt - Inject correct strings based on languageCode into CompositionLocalProvider - Replace key hardcoded visible strings with WhiteDnsL10n references: - Bottom tab labels (Profiles/Connect/Scan/Logs) - Connect button (CONNECT/CONNECTING/STOP) - App Settings dialog title and CLOSE button - Theme/Language field labels and option labels - Battery background banner text and action button - Profile tab headers (Connection/Resolver/Setting) - Parallel Test toggle label * feat(i18n): localize connection mode, section headers, and server route strings - Localize Mode field label (Mode / حالت) - Localize connection mode buttons (Proxy Mode/Full VPN → حالت پروکسی/VPN کامل) - Localize Connection/Resolver section headers - Localize 'Server route missing' in all occurrences - Fix ConnectionModeSegmentedControl to use localized option labels * feat(i18n): localize default profile names and remaining English strings - Add setupDefaultConnection/Resolver/Advanced strings to substitute localized labels when the user has not renamed the default profiles - Wire id-based label override into setup card, home selectors, connection info card, advanced profile controls, and the three profile list rows (connection, resolver, settings) - Localize the resolver row summary (count + SELECTED suffix) and the settings row MODIFIED/SELECTED status suffixes via new profileStatusModified and resolverProfileSummary entries * chore: drop local-only files from branch
1 parent c0c0fa2 commit 8f554c9

7 files changed

Lines changed: 2953 additions & 464 deletions

File tree

app/src/main/java/shop/whitedns/client/MainActivity.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ class MainActivity : ComponentActivity() {
4444
enableEdgeToEdge()
4545

4646
setContent {
47-
WhiteDnsTheme(themeMode = viewModel.uiState.settings.themeMode) {
47+
WhiteDnsTheme(
48+
themeMode = viewModel.uiState.settings.themeMode,
49+
languageCode = viewModel.uiState.settings.languageCode,
50+
) {
4851
val context = LocalContext.current
4952
var shouldConnectAfterNotificationPermission by rememberSaveable { mutableStateOf(false) }
5053
val vpnPermissionLauncher = rememberLauncherForActivityResult(

app/src/main/java/shop/whitedns/client/model/WhiteDnsModels.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ data class WhiteDnsSettings(
238238
val connectionMode: String = "proxy",
239239
val protocolType: String = "SOCKS5",
240240
val themeMode: String = WhiteDnsThemeMode.System,
241+
val languageCode: String = WhiteDnsLanguage.En,
241242
val resolverText: String = "",
242243
val listenIp: String = "127.0.0.1",
243244
val listenPort: String = "10886",
@@ -454,6 +455,11 @@ object WhiteDnsThemeMode {
454455
const val Dark = "dark"
455456
}
456457

458+
object WhiteDnsLanguage {
459+
const val En = "en"
460+
const val Fa = "fa"
461+
}
462+
457463
data class WhiteDnsScanState(
458464
val sessionId: String = "",
459465
val status: String = WhiteDnsScanStatus.Idle,
@@ -696,6 +702,7 @@ fun WhiteDnsSettings.syncSelectedConnectionProfileFields(): WhiteDnsSettings {
696702
val selected = profiles.firstOrNull { it.id == selectedConnectionProfileId } ?: profiles.first()
697703
val selectedConnectionMode = normalizeConnectionMode(connectionMode)
698704
val selectedThemeMode = normalizeThemeMode(themeMode)
705+
val selectedLanguageCode = normalizeLanguageCode(languageCode)
699706
val modeSyncedProfiles = profiles.map { profile ->
700707
if (profile.id == selected.id) {
701708
profile.copy(connectionMode = selectedConnectionMode)
@@ -725,6 +732,7 @@ fun WhiteDnsSettings.syncSelectedConnectionProfileFields(): WhiteDnsSettings {
725732
customServerEncryptionMethod = selected.customServerEncryptionMethod,
726733
connectionMode = selectedConnectionMode,
727734
themeMode = selectedThemeMode,
735+
languageCode = selectedLanguageCode,
728736
splitTunnelMode = normalizeSplitTunnelMode(splitTunnelMode),
729737
splitTunnelPackages = normalizePackageNames(splitTunnelPackages),
730738
)
@@ -1487,6 +1495,13 @@ private fun normalizeThemeMode(raw: String): String {
14871495
}
14881496
}
14891497

1498+
private fun normalizeLanguageCode(raw: String): String {
1499+
return when (raw) {
1500+
WhiteDnsLanguage.Fa -> WhiteDnsLanguage.Fa
1501+
else -> WhiteDnsLanguage.En
1502+
}
1503+
}
1504+
14901505
private fun normalizePackageNames(raw: List<String>): List<String> {
14911506
return raw
14921507
.asSequence()

app/src/main/java/shop/whitedns/client/model/WhiteDnsSettingsStore.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class WhiteDnsSettingsStore(
8383
connectionMode = legacyConnectionMode,
8484
protocolType = preferences.getString(KeyProtocolType, defaults.protocolType) ?: defaults.protocolType,
8585
themeMode = preferences.getString(KeyThemeMode, defaults.themeMode) ?: defaults.themeMode,
86+
languageCode = preferences.getString(KeyLanguageCode, defaults.languageCode) ?: defaults.languageCode,
8687
resolverText = if (resolverText == LegacyDefaultResolverText) defaults.resolverText else resolverText,
8788
listenIp = preferences.getString(KeyListenIp, defaults.listenIp) ?: defaults.listenIp,
8889
listenPort = preferences.getString(KeyListenPort, defaults.listenPort) ?: defaults.listenPort,
@@ -230,6 +231,7 @@ class WhiteDnsSettingsStore(
230231
.putString(KeyConnectionMode, normalizedSettings.connectionMode)
231232
.putString(KeyProtocolType, normalizedSettings.protocolType)
232233
.putString(KeyThemeMode, normalizedSettings.themeMode)
234+
.putString(KeyLanguageCode, normalizedSettings.languageCode)
233235
.putString(KeyResolverText, normalizedSettings.resolverText)
234236
.putString(KeyListenIp, normalizedSettings.listenIp)
235237
.putString(KeyListenPort, normalizedSettings.listenPort)
@@ -692,6 +694,7 @@ class WhiteDnsSettingsStore(
692694
const val KeyConnectionMode = "connection_mode"
693695
const val KeyProtocolType = "protocol_type"
694696
const val KeyThemeMode = "theme_mode"
697+
const val KeyLanguageCode = "language_code"
695698
const val KeyResolverText = "resolver_text"
696699
const val KeyListenIp = "listen_ip"
697700
const val KeyListenPort = "listen_port"

0 commit comments

Comments
 (0)