fix: add support for more keys for translations

This commit is contained in:
particle-box
2026-02-06 23:29:57 +05:30
parent 4692987baf
commit 3744c5b422
25 changed files with 156 additions and 129 deletions

View File

@@ -121,7 +121,7 @@ class BridgeService : Service() {
val pendingTask = remoteSideContext.taskManager.createPendingTask(
Task(
type = TaskType.DOWNLOAD,
title = "Media conversion",
title = remoteSideContext.translation["task_media_conversion_title"],
author = null,
hash = taskId
)

View File

@@ -120,7 +120,7 @@ class CallDownloadSessionImpl(
val pendingTask = context.taskManager.createPendingTask(
Task(
type = TaskType.DOWNLOAD,
title = "Call Recording $author",
title = context.translation.format("task_call_recording_title", "author" to author),
author = author,
hash = UUID.randomUUID().toString()
)
@@ -188,4 +188,4 @@ class CallDownloadSessionImpl(
context.log.verbose("ending call")
}
}
}
}

View File

@@ -132,16 +132,16 @@ class MainActivity : ComponentActivity() {
if (shouldShowAbiWarning) {
AestheticDialog(
onDismissRequest = {},
title = "Wrong APK installed",
title = managerContext.translation["wrong_apk_title"],
text = "",
icon = Icons.Filled.Warning,
confirmButtonText = "Close",
confirmButtonText = managerContext.translation["common.close"],
onConfirm = { (context as? Activity)?.finishAffinity() },
showCloseButton = false,
opaque = true,
customContent = {
Text(
text = "Your device is armv8, please download the armv8 apk, not armv7.",
text = managerContext.translation["wrong_apk_message"],
color = PurrfectPalette.textSecondary,
lineHeight = 18.sp
)

View File

@@ -143,7 +143,7 @@ class ManageReposSection: Routes.Route() {
addRepo(url)
}.onFailure {
context.log.error("Failed to add repository", it)
context.shortToast(translation.format("add_repo_failed", "message" to (it.message ?: "Unknown")))
context.shortToast(translation.format("add_repo_failed", "message" to (it.message ?: context.translation["common.unknown"])))
}
loading = false
}

View File

@@ -138,7 +138,7 @@ class ConfigExportSummaryScreen : Routes.Route() {
override val content: @Composable (androidx.navigation.NavBackStackEntry) -> Unit = {
val exportSensitiveData = it.arguments?.getString("exportSensitiveData")?.toBoolean() ?: false
val includeSavedLocations = it.arguments?.getString("includeSavedLocations")?.toBoolean() ?: false
val exportLabel = context.translation["manager.sections.features.export_option"] ?: "Export"
val exportLabel = context.translation["manager.sections.features.export_option"]
val parser = remember { ConfigParser() }
val savedLocations = remember {
if (includeSavedLocations) context.database.getLocationCoordinates() else null
@@ -196,14 +196,14 @@ class ConfigExportSummaryScreen : Routes.Route() {
tint = Color.White,
modifier = Modifier.padding(end = 6.dp)
)
Text(context.translation["common.back"] ?: "Back")
Text(context.translation["common.back"])
}
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.Center
) {
Text(
text = "Summary",
text = translation["title"],
color = Color.White,
fontWeight = FontWeight.ExtraBold,
fontSize = 18.sp

View File

@@ -232,7 +232,7 @@ class ConfigImportConfirmationScreen : Routes.Route() {
routes.configJsonForImport?.let { parser.parse(it) } ?: emptyMap()
}
val expandedState = remember { mutableStateMapOf<String, Boolean>() }
val importLabel = translation["confirm_button"] ?: "Import"
val importLabel = translation["confirm_button"]
Box(
modifier = Modifier
@@ -281,14 +281,14 @@ class ConfigImportConfirmationScreen : Routes.Route() {
tint = Color.White,
modifier = Modifier.padding(end = 6.dp)
)
Text(context.translation["common.back"] ?: "Back")
Text(context.translation["common.back"])
}
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.Center
) {
Text(
text = "Summary",
text = translation["title"],
color = Color.White,
fontWeight = FontWeight.ExtraBold,
fontSize = 18.sp
@@ -308,7 +308,7 @@ class ConfigImportConfirmationScreen : Routes.Route() {
context.longToast(
context.translation.format(
"config_import_failure_toast",
"error" to (err.message ?: "Unknown error")
"error" to (err.message ?: context.translation["common.unknown_error"])
)
)
}

View File

@@ -112,7 +112,7 @@ class FeaturesRootSection : Routes.Route() {
} ?: routeInfo.translatedKey?.value
}
SEARCH_FEATURE_ROUTE -> {
translation["search_button"] ?: "Search"
translation["search_button"]
}
else -> {
routeInfo.translatedKey?.value
@@ -668,7 +668,7 @@ class FeaturesRootSection : Routes.Route() {
border = BorderStroke(1.dp, Color.White.copy(alpha = 0.12f))
) {
Text(
text = "$messageCount messages",
text = translation.format("search_results_count", "count" to messageCount.toString()),
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp),
style = MaterialTheme.typography.bodyMedium,
color = Color.White
@@ -1014,7 +1014,7 @@ class FeaturesRootSection : Routes.Route() {
},
placeholder = {
Text(
text = translation["search_button"] ?: "Search",
text = translation["search_button"],
color = Color(0xFFE0DCFF)
)
},
@@ -1109,7 +1109,7 @@ class FeaturesRootSection : Routes.Route() {
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Include Saved Locations",
text = context.translation["include_saved_locations"],
style = MaterialTheme.typography.bodyMedium,
color = Color.White
)
@@ -1310,7 +1310,7 @@ class FeaturesRootSection : Routes.Route() {
val headerTitle = activeSectionTitle ?: translation["manager.routes.features"]
val subtitleText = when {
isSearchResults -> translation["search_button"] ?: "Search"
isSearchResults -> translation["search_button"]
!activeSectionSubtitle.isNullOrBlank() -> activeSectionSubtitle
else -> translation["manager.sections.features.subtitle"] ?: ""
}
@@ -1364,7 +1364,7 @@ class FeaturesRootSection : Routes.Route() {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = translation["button.back"] ?: "Back",
contentDescription = context.translation["common.back"],
tint = Color.White
)
}
@@ -1385,7 +1385,7 @@ class FeaturesRootSection : Routes.Route() {
.weight(1f)
.focusRequester(focusRequester),
singleLine = true,
placeholder = { Text(text = translation["search_button"] ?: "Search", color = Color(0xFFE0DCFF)) },
placeholder = { Text(text = translation["search_button"], color = Color(0xFFE0DCFF)) },
leadingIcon = {
Icon(
imageVector = Icons.Filled.Search,
@@ -1543,7 +1543,7 @@ class FeaturesRootSection : Routes.Route() {
) {
Icon(Icons.Filled.Delete, contentDescription = null, tint = Color.White.copy(alpha = 0.85f))
Spacer(Modifier.width(6.dp))
Text(text = translation["clear_history"] ?: "Clear history", color = Color.White)
Text(text = translation["clear_history"], color = Color.White)
}
}
}

View File

@@ -203,8 +203,8 @@ class ManageRuleFeature : Routes.Route() {
title = translation["clear_list_button"],
text = translation["dialog_clear_confirmation_text"],
icon = Icons.Default.DeleteSweep,
confirmButtonText = context.translation["clear"],
dismissButtonText = context.translation["button.cancel"],
confirmButtonText = translation["dialog_clear_confirm_button"],
dismissButtonText = translation["dialog_clear_cancel_button"],
onDismiss = { confirmationDialog = false },
onConfirm = {
context.database.clearRuleIds(currentRuleType.key)
@@ -390,7 +390,7 @@ class ManageRuleFeature : Routes.Route() {
contentColor = Color.White
)
) {
Text(text = context.translation["clear"])
Text(text = translation["dialog_clear_confirm_button"])
}
}
}

View File

@@ -315,7 +315,7 @@ class HomeLogs : Routes.Route() {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null, tint = Color.White)
}
Text(
text = routeInfo.translatedKey?.value ?: translation["manager.routes.home_logs"] ?: "Logs",
text = routeInfo.translatedKey?.value ?: translation["manager.routes.home_logs"],
color = PurrfectPalette.textPrimary,
fontSize = 18.sp,
fontWeight = FontWeight.ExtraBold
@@ -412,7 +412,7 @@ class HomeLogs : Routes.Route() {
textAlign = TextAlign.Center
)
Text(
text = "Pull to refresh or trigger an action to see new entries.",
text = translation["refresh_hint"],
color = PurrfectPalette.textSecondary,
fontSize = 13.sp,
textAlign = TextAlign.Center,

View File

@@ -154,8 +154,8 @@ class HomeSettings : Routes.Route() {
var value by remember { mutableStateOf(sharedPreferences.getBoolean(realKey, defaultValue)) }
var showDisableDialog by remember { mutableStateOf(false) }
val hapticFeedback = LocalHapticFeedback.current
val positiveLabel = context.translation["button.positive"] ?: "Yes"
val negativeLabel = context.translation["button.negative"] ?: "No"
val positiveLabel = context.translation["button.positive"]
val negativeLabel = context.translation["button.negative"]
LaunchedEffect(realKey) {
if (!sharedPreferences.contains(realKey)) {
@@ -167,7 +167,7 @@ class HomeSettings : Routes.Route() {
if (showDisableDialog) {
AestheticDialog(
onDismissRequest = { showDisableDialog = false },
title = confirmDisableTitle ?: "Are you sure?",
title = confirmDisableTitle ?: translation["reset_setup_dialog_title"],
text = confirmDisableText.orEmpty(),
icon = Icons.Filled.Warning,
confirmButtonText = positiveLabel,
@@ -313,9 +313,9 @@ class HomeSettings : Routes.Route() {
val contextC = LocalContext.current
val scope = rememberCoroutineScope()
val scrollState = rememberScrollState()
val positiveLabel = context.translation["button.positive"] ?: "Yes"
val negativeLabel = context.translation["button.negative"] ?: "No"
val importLabel = context.translation["button.import"] ?: "Import"
val positiveLabel = context.translation["button.positive"]
val negativeLabel = context.translation["button.negative"]
val importLabel = context.translation["button.import"]
val sharedButtonColors = ButtonDefaults.buttonColors(
containerColor = Color.White.copy(alpha = 0.12f),
contentColor = Color.White

View File

@@ -127,7 +127,11 @@ class BetterLocationRoot : Routes.Route() {
overflow = TextOverflow.Ellipsis
)
Text(
text = "Lat ${friendLocation.latitude.toFloat()}, Lng ${friendLocation.longitude.toFloat()}",
text = context.translation.format(
"spoofed_coordinates_title",
"latitude" to friendLocation.latitude.toFloat().toString(),
"longitude" to friendLocation.longitude.toFloat().toString()
),
fontSize = 11.sp,
fontWeight = FontWeight.Light,
color = Color.White.copy(alpha = 0.8f)

View File

@@ -255,7 +255,7 @@ class ManageScriptReposSection : Routes.Route() {
}
}.onFailure {
context.log.error("Failed to add repository", it)
context.shortToast(translation.format("add_repo_failed_toast", "message" to (it.message ?: "Unknown")))
context.shortToast(translation.format("add_repo_failed_toast", "message" to (it.message ?: context.translation["common.unknown"])))
}
loading = false
}
@@ -292,7 +292,7 @@ class ManageScriptReposSection : Routes.Route() {
.background(PurrfectPalette.backgroundGradient)
) {
FloatingTopBar(
title = routeInfo.translatedKey?.value ?: (translation["title"] ?: "Repositories"),
title = routeInfo.translatedKey?.value ?: translation["title"],
onBack = { routes.navController.popBackStack() },
modifier = Modifier
.zIndex(2f)

View File

@@ -162,7 +162,7 @@ fun ScriptCatalog(root: ScriptingRootSection) {
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
context.shortToast(translation.format("error", "message" to (e.localizedMessage ?: "Unknown")))
context.shortToast(translation.format("error", "message" to (e.localizedMessage ?: context.translation["common.unknown"])))
}
}
}
@@ -238,7 +238,7 @@ fun ScriptCatalog(root: ScriptingRootSection) {
color = Color.White
)
Text(
text = translation["loading"] ?: "Loading...",
text = translation["loading"],
color = Color.White,
fontWeight = FontWeight.SemiBold,
fontSize = 13.sp
@@ -348,7 +348,7 @@ fun ScriptCatalog(root: ScriptingRootSection) {
border = BorderStroke(1.dp, Color.White.copy(alpha = 0.12f))
) {
Text(
text = translation.format("version", "version" to (entry.version ?: "N/A")),
text = translation.format("version", "version" to (entry.version ?: context.translation["common.not_available"])),
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp),
fontWeight = FontWeight.Medium,
fontSize = 11.sp,

View File

@@ -140,7 +140,7 @@ class ScriptingRootSection : Routes.Route() {
return@launch
}.onFailure {
context.log.error("Failed to import script", it)
context.shortToast(translation.format("import_failed", "message" to (it.message ?: "Unknown")))
context.shortToast(translation.format("import_failed", "message" to (it.message ?: context.translation["common.unknown"])))
}
isLoading = false
}
@@ -431,7 +431,7 @@ class ScriptingRootSection : Routes.Route() {
@Composable
private fun SelectFolderButton(onClick: () -> Unit) {
val label = translation.getOrNull("select_folder_button") ?: "Select folder"
val label = translation["select_folder_button"]
Box(
modifier = Modifier
.fillMaxWidth()
@@ -439,13 +439,13 @@ class ScriptingRootSection : Routes.Route() {
contentAlignment = Alignment.Center
) {
Surface(
modifier = Modifier.size(68.dp),
modifier = Modifier.size(78.dp),
shape = CircleShape,
color = Color.White.copy(alpha = 0.1f),
color = PurrfectPalette.cardOverlayColor.copy(alpha = 0.9f),
tonalElevation = 0.dp,
shadowElevation = 10.dp,
shadowElevation = 14.dp,
border = BorderStroke(
1.dp,
1.5.dp,
Brush.linearGradient(
listOf(
PurrfectPalette.glowPrimary.copy(alpha = 0.7f),
@@ -456,17 +456,18 @@ class ScriptingRootSection : Routes.Route() {
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(6.dp)
.size(66.dp)
.clip(CircleShape)
.background(
Brush.radialGradient(
colors = listOf(
PurrfectPalette.glowPrimary.copy(alpha = 0.335f),
PurrfectPalette.glowSecondary.copy(alpha = 0.25f),
Color.Transparent
PurrfectPalette.glowPrimary.copy(alpha = 0.42f),
PurrfectPalette.glowSecondary.copy(alpha = 0.34f)
)
)
)
.border(1.dp, Color.White.copy(alpha = 0.14f), CircleShape)
.clickable(onClick = onClick),
contentAlignment = Alignment.Center
) {
@@ -474,7 +475,7 @@ class ScriptingRootSection : Routes.Route() {
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = label,
tint = Color.White,
modifier = Modifier.size(32.dp)
modifier = Modifier.size(34.dp)
)
}
}
@@ -627,7 +628,7 @@ class ScriptingRootSection : Routes.Route() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(260.dp),
.heightIn(min = 260.dp),
contentAlignment = Alignment.Center
) {
Surface(
@@ -743,7 +744,7 @@ class ScriptingRootSection : Routes.Route() {
title = context.translation["manager.dialogs.scripting_warning.title"],
text = context.translation["manager.dialogs.scripting_warning.content"],
icon = Icons.Default.Warning,
confirmButtonText = translation["button.ok"] ?: "OK",
confirmButtonText = translation["button.ok"],
onConfirm = { if (timeout == 0) scriptingWarning = false },
loading = timeout > 0,
showCloseButton = false,
@@ -875,7 +876,7 @@ class ScriptingRootSection : Routes.Route() {
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = translation["manager.routes.scripts"] ?: "Scripts",
text = translation["manager.routes.scripts"],
color = Color.White,
fontWeight = FontWeight.ExtraBold,
fontSize = 18.sp

View File

@@ -375,8 +375,7 @@ class AddFriendDialog(
enabled = !allFriendsSelected
) {
Text(
text = context.translation["manager.dialogs.messaging_action.select_all_button"]
?: "Select All",
text = context.translation["manager.dialogs.messaging_action.select_all_button"],
color = if (allFriendsSelected) {
Color.White.copy(alpha = 0.45f)
} else {

View File

@@ -91,7 +91,7 @@ class ManageScope: Routes.Route() {
title = translation.format("delete_scope_confirm_dialog_title", "scope" to context.translation["scopes.${scope.key}"]),
text = "",
icon = Icons.Rounded.DeleteForever,
confirmButtonText = translation["delete_button"] ?: "Delete",
confirmButtonText = translation["delete_button"],
dismissButtonText = context.translation["button.cancel"],
onDismiss = { deleteConfirmDialog = false },
onConfirm = {
@@ -109,7 +109,7 @@ class ManageScope: Routes.Route() {
.background(PurrfectPalette.backgroundGradient)
) {
FloatingTopBar(
title = titleText ?: "Manage",
title = titleText ?: translation["manage_scope_title"],
onBack = { routes.navController.popBackStack() },
actions = {
IconButton(onClick = { deleteConfirmDialog = true }) {

View File

@@ -63,7 +63,7 @@ import me.eternal.purrfectsnap.ui.util.Dialog
import me.eternal.purrfectsnap.ui.util.purrfectSwitchColors
class MessagingPreview: Routes.Route() {
override val translation by lazy { context.translation.getCategory("manager.sections.social.messaging_preview.messaging_preview") }
override val translation by lazy { context.translation.getCategory("manager.sections.social.messaging_preview") }
private lateinit var coroutineScope: CoroutineScope
private lateinit var previewScrollState: LazyListState
@@ -79,21 +79,6 @@ class MessagingPreview: Routes.Route() {
else selectedMessages.add(messageId)
}
private fun tr(key: String, fallback: String? = null): String {
fun normalizeCandidate(candidate: String?): String? {
if (candidate.isNullOrBlank()) return null
if (candidate == key) return null
if (candidate.endsWith(".$key")) return null
return candidate
}
return normalizeCandidate(translation.getOrNull(key))
?: normalizeCandidate(context.translation.getOrNull("manager.sections.social.messaging_preview.$key"))
?: normalizeCandidate(context.translation.getOrNull("manager.social.messaging_preview.$key"))
?: fallback
?: key
}
@Composable
private fun ActionsSheetItem(
title: String,
@@ -338,20 +323,20 @@ class MessagingPreview: Routes.Route() {
val senderDisplayName by rememberAsyncMutableState<String?>(null, keys = arrayOf(senderId, myUserId, scope.key, scopeId, friendDisplayName)) {
when {
senderId == null -> "Unknown"
senderId == myUserId -> "You"
senderId == null -> translation["sender_unknown"]
senderId == myUserId -> translation["sender_you"]
scope == SocialScope.FRIEND -> friendDisplayName
?: context.database.getFriendInfo(scopeId)?.displayName
?: context.database.getFriendInfo(scopeId)?.mutableUsername
?: "Friend"
?: translation["sender_friend"]
else -> context.database.getFriendInfo(senderId)?.displayName
?: context.database.getFriendInfo(senderId)?.mutableUsername
?: "Unknown"
?: translation["sender_unknown"]
}
}
val contentTypeLabel = remember(contentType) {
contentType?.let { contentTypeTranslation.getOrNull(it.name) ?: it.name } ?: "Unknown"
contentType?.let { contentTypeTranslation.getOrNull(it.name) ?: it.name } ?: translation["sender_unknown"]
}
val bodyText = remember(message.contentType) { messageReader.getString(2, 1)?.trim().orEmpty() }
@@ -408,7 +393,7 @@ class MessagingPreview: Routes.Route() {
horizontalAlignment = if (isMine) Alignment.End else Alignment.Start
) {
Text(
text = senderDisplayName ?: "Unknown",
text = senderDisplayName ?: translation["sender_unknown"],
color = Color.White.copy(alpha = 0.82f),
fontSize = 12.sp,
fontWeight = FontWeight.SemiBold,
@@ -487,7 +472,7 @@ class MessagingPreview: Routes.Route() {
.padding(40.dp),
horizontalArrangement = Arrangement.Center
) {
Text(tr("no_message_hint", "No messages"), color = PurrfectPalette.textSecondary)
Text(translation["no_message_hint"], color = PurrfectPalette.textSecondary)
}
}
Spacer(modifier = Modifier.height(20.dp))
@@ -564,7 +549,7 @@ class MessagingPreview: Routes.Route() {
translation.format("processed_message_toast", "count" to processMessageCount.intValue.toString())
} ?: translation.getOrNull("processed_messages_toast")?.let {
translation.format("processed_messages_toast", "count" to processMessageCount.intValue.toString())
} ?: "Processed ${processMessageCount.intValue} messages"
} ?: translation.format("processed_messages_toast", "count" to processMessageCount.intValue.toString())
context.longToast(toastText)
}
}
@@ -579,7 +564,7 @@ class MessagingPreview: Routes.Route() {
context.longToast(
translation.getOrNull("bridge_connection_error")
?: translation.getOrNull("bridge_connection_failed")
?: "Failed to connect to bridge"
?: translation["bridge_connection_error"]
)
return
}
@@ -640,7 +625,7 @@ class MessagingPreview: Routes.Route() {
) {
val processedText = translation.getOrNull("processed_messages_text")?.let {
translation.format("processed_messages_text", "count" to processMessageCount.intValue.toString())
} ?: "Processed ${processMessageCount.intValue}"
} ?: translation.format("processed_messages_text", "count" to processMessageCount.intValue.toString())
Text(processedText)
if (activeTask?.hasFixedGoal() == true) {
LinearProgressIndicator(
@@ -679,7 +664,7 @@ class MessagingPreview: Routes.Route() {
}.onFailure {
context.log.error("Failed to fetch messages", it)
context.shortToast(
translation.getOrNull("message_fetch_failed") ?: "Failed to fetch messages"
translation.getOrNull("message_fetch_failed") ?: translation["message_fetch_failed"]
)
}
}
@@ -710,7 +695,7 @@ class MessagingPreview: Routes.Route() {
fetchNewMessages()
}.onFailure {
context.longToast(
translation.getOrNull("bridge_init_failed") ?: "Failed to initialize messaging bridge"
translation.getOrNull("bridge_init_failed") ?: translation["bridge_init_failed"]
)
context.log.error("Failed to initialize messaging bridge", it)
}
@@ -751,11 +736,11 @@ class MessagingPreview: Routes.Route() {
.background(PurrfectPalette.backgroundGradient)
) {
FloatingTopBar(
title = titleText ?: translation["title"] ?: "Preview",
title = titleText ?: translation["title"],
subtitle = if (selectedMessages.isNotEmpty()) {
"${selectedMessages.size} selected"
} else {
tr("subtitle", "Hold to select")
translation["subtitle"]
.substringBefore("")
.substringBefore("·")
.substringBefore("|")
@@ -798,7 +783,7 @@ class MessagingPreview: Routes.Route() {
Text(
translation.getOrNull("bridge_connection_error")
?: translation.getOrNull("bridge_connection_failed")
?: "Failed to connect to bridge",
?: translation["bridge_connection_error"],
modifier = Modifier.padding(16.dp),
color = Color.White
)
@@ -831,7 +816,7 @@ class MessagingPreview: Routes.Route() {
val selectionSubtitle = if (hasSelection) {
"${selectedMessages.size} selected"
} else {
"Choose message types"
translation["choose_message_types_subtitle"]
}
Column(
@@ -862,7 +847,7 @@ class MessagingPreview: Routes.Route() {
)
Spacer(Modifier.height(14.dp))
Text(
text = tr("actions_title", "Conversation Actions"),
text = translation["actions_title"],
color = Color.White,
fontWeight = FontWeight.ExtraBold,
fontSize = 18.sp,
@@ -886,31 +871,31 @@ class MessagingPreview: Routes.Route() {
val deleteKey = if (hasSelection) "delete_selection_option" else "delete_all_option"
ActionsSheetItem(
title = tr(saveKey, if (hasSelection) "Save Selection" else "Save All"),
subtitle = if (hasSelection) "Save selected messages" else "Save by content type",
title = translation[saveKey],
subtitle = if (hasSelection) translation["save_selected_messages_subtitle"] else translation["save_by_content_type_subtitle"],
icon = Icons.Rounded.BookmarkAdded
) {
launchMessagingTask(MessagingTaskType.SAVE)
if (hasSelection) runCurrentTask() else selectConstraintsDialog = true
}
ActionsSheetItem(
title = tr(unsaveKey, if (hasSelection) "Unsave Selection" else "Unsave All"),
subtitle = if (hasSelection) "Unsave selected messages" else "Unsave by content type",
title = translation[unsaveKey],
subtitle = if (hasSelection) translation["unsave_selected_messages_subtitle"] else translation["unsave_by_content_type_subtitle"],
icon = Icons.Rounded.BookmarkBorder
) {
launchMessagingTask(MessagingTaskType.UNSAVE)
if (hasSelection) runCurrentTask() else selectConstraintsDialog = true
}
ActionsSheetItem(
title = tr(markKey, if (hasSelection) "Mark selected as seen" else "Mark all as seen"),
subtitle = "Marks snaps as seen",
title = translation[markKey],
subtitle = translation["mark_as_seen_subtitle"],
icon = Icons.Rounded.RemoveRedEye
) {
if (messagingBridge == null) {
context.longToast(
translation.getOrNull("bridge_connection_error")
?: translation.getOrNull("bridge_connection_failed")
?: "Failed to connect to bridge"
?: translation["bridge_connection_error"]
)
return@ActionsSheetItem
}
@@ -924,8 +909,8 @@ class MessagingPreview: Routes.Route() {
runCurrentTask()
}
ActionsSheetItem(
title = tr(deleteKey, if (hasSelection) "Delete Selection" else "Delete All"),
subtitle = if (hasSelection) "Delete selected messages" else "Delete by content type",
title = translation[deleteKey],
subtitle = if (hasSelection) translation["delete_selected_messages_subtitle"] else translation["delete_by_content_type_subtitle"],
icon = Icons.Rounded.DeleteForever,
danger = true
) {
@@ -933,7 +918,7 @@ class MessagingPreview: Routes.Route() {
context.longToast(
translation.getOrNull("bridge_connection_error")
?: translation.getOrNull("bridge_connection_failed")
?: "Failed to connect to bridge"
?: translation["bridge_connection_error"]
)
return@ActionsSheetItem
}

View File

@@ -237,7 +237,7 @@ class SocialRootSection : Routes.Route() {
}
)
if (searchActive) {
val searchHint = context.translation["manager.dialogs.add_friend.search_hint"] ?: "Search"
val searchHint = context.translation["manager.dialogs.add_friend.search_hint"]
val searchShape = RoundedCornerShape(18.dp)
val searchBorder = Brush.linearGradient(
listOf(
@@ -520,7 +520,7 @@ class SocialRootSection : Routes.Route() {
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = translation["manager.routes.social"] ?: "Social",
text = translation["manager.routes.social"],
color = Color.White,
fontWeight = FontWeight.ExtraBold,
fontSize = 18.sp
@@ -631,7 +631,7 @@ class SocialRootSection : Routes.Route() {
fontSize = 15.sp
)
Text(
text = translation["social_empty_hint"] ?: "Tap the + button to sync friends or groups.",
text = translation["social_empty_hint"],
color = PurrfectPalette.textSecondary,
fontSize = 12.sp
)

View File

@@ -148,7 +148,7 @@ class FriendTrackerCatalog : Routes.Route() {
contentColor = Color.White
)
) {
Text(translation["manage_repos_description"] ?: (context.translation["manager.routes.manage_friend_tracker_repos"] ?: "Manage repositories"))
Text(translation["manage_repos_description"] ?: context.translation["manager.routes.manage_friend_tracker_repos"])
}
}
)
@@ -176,7 +176,7 @@ class FriendTrackerCatalog : Routes.Route() {
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
context.shortToast(translation.format("error", "message" to (e.localizedMessage ?: "Unknown")))
context.shortToast(translation.format("error", "message" to (e.localizedMessage ?: context.translation["common.unknown"])))
}
}
}
@@ -341,7 +341,7 @@ class FriendTrackerCatalog : Routes.Route() {
.background(PurrfectPalette.backgroundGradient)
) {
FloatingTopBar(
title = translation["title"] ?: (routeInfo.translatedKey?.value ?: "Catalog"),
title = translation["title"],
onBack = { routes.navController.popBackStack() },
actions = {
IconButton(onClick = { routes.manageFriendTrackerRepos.navigate() }) {

View File

@@ -115,7 +115,7 @@ class FriendTrackerConfigExportScreen : Routes.Route() {
}
}
}.onFailure {
context.longToast(translation.format("export_failed_toast", "message" to (it.message ?: "Unknown")))
context.longToast(translation.format("export_failed_toast", "message" to (it.message ?: context.translation["common.unknown"])))
}
}
}) {

View File

@@ -78,7 +78,7 @@ class FriendTrackerConfigImportScreen : Routes.Route() {
routes.onRuleImported?.invoke()
routes.navController.popBackStack()
}.onFailure {
context.longToast(translation.format("import_failed_toast", "message" to (it.message ?: "Unknown")))
context.longToast(translation.format("import_failed_toast", "message" to (it.message ?: context.translation["common.unknown"])))
}
}) {
Text(translation["confirm_button"])

View File

@@ -245,7 +245,7 @@ class ManageFriendTrackerReposSection: Routes.Route() {
}
}.onFailure {
context.log.error("Failed to add repository", it)
context.shortToast(translation.format("add_repo_failed_toast", "message" to (it.message ?: "Unknown")))
context.shortToast(translation.format("add_repo_failed_toast", "message" to (it.message ?: context.translation["common.unknown"])))
}
loading = false
}
@@ -282,7 +282,7 @@ class ManageFriendTrackerReposSection: Routes.Route() {
.background(PurrfectPalette.backgroundGradient)
) {
FloatingTopBar(
title = routeInfo.translatedKey?.value ?: (translation["title"] ?: "Repositories"),
title = routeInfo.translatedKey?.value ?: translation["title"],
onBack = { routes.navController.popBackStack() },
modifier = Modifier
.zIndex(2f)

View File

@@ -16,10 +16,14 @@ class TrackerConfigParser(private val context: Routes.Route) {
fun parse(configJson: String): Map<String, List<ImportedFeature>> {
val featureMap = mutableMapOf<String, MutableList<ImportedFeature>>()
val exportedData = context.context.gson.fromJson(configJson, ExportedTrackerData::class.java)
val authorLabel = context.translation["tracker_author_label"]
val enabledLabel = context.translation["tracker_enabled_label"]
val enabledValue = context.translation["tracker_enabled_value"]
val disabledValue = context.translation["tracker_disabled_value"]
exportedData.rules.forEach { rule ->
val features = mutableListOf<ImportedFeature>()
features.add(ImportedFeature(rule.name, "Author", "author", rule.author ?: "Unknown", 0))
features.add(ImportedFeature(rule.name, "Enabled", "enabled", rule.enabled, 0))
features.add(ImportedFeature(rule.name, authorLabel, "author", rule.author ?: context.context.translation["common.unknown"], 0))
features.add(ImportedFeature(rule.name, enabledLabel, "enabled", if (rule.enabled) enabledValue else disabledValue, 0))
rule.events?.forEach { event ->
features.add(ImportedFeature(rule.name, context.context.translation["tracker_events.${event.eventType}"], event.eventType, event.actions.joinToString(", ") { context.context.translation["tracker_actions.${it.key}"] }, 1))
}
@@ -30,7 +34,7 @@ class TrackerConfigParser(private val context: Routes.Route) {
fun parseValue(featureKey: String, value: Any): Any {
return when (value) {
is Boolean -> if (value) "Enabled" else "Disabled"
is Boolean -> if (value) context.translation["tracker_enabled_value"] else context.translation["tracker_disabled_value"]
is JSONArray -> {
val list = mutableListOf<String>()
for (i in 0 until value.length()) {

View File

@@ -499,7 +499,7 @@ class AlertDialogs(
currentColor = null
}
},
label = { Text(text = "Hex Color") },
label = { Text(text = translation["dialogs.hex_color_label"]) },
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
@@ -816,7 +816,7 @@ class AlertDialogs(
color = Color.White
)
Text(
text = "Search or tap on the map",
text = betterLocationTranslation["search_or_tap_map_hint"],
fontSize = 12.sp,
color = PurrfectPalette.textSecondary
)
@@ -870,11 +870,11 @@ class AlertDialogs(
search()
}
},
placeholder = { Text(text = "Search location...") },
placeholder = { Text(text = betterLocationTranslation["search_location_placeholder"]) },
leadingIcon = {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = "Search",
contentDescription = betterLocationTranslation["search_icon_description"],
tint = Color.White
)
},
@@ -994,7 +994,7 @@ class AlertDialogs(
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = "Searching...",
text = betterLocationTranslation["searching_label"],
style = MaterialTheme.typography.bodyMedium,
color = Color.White
)
@@ -1142,7 +1142,7 @@ class AlertDialogs(
.padding(horizontal = 6.dp),
value = lat.value,
onValueChange = { lat.value = it },
label = { Text(text = "Latitude") },
label = { Text(text = translation["latitude_dialog_hint"]) },
leadingIcon = { Icon(Icons.Filled.MyLocation, contentDescription = null) },
singleLine = true,
shape = RoundedCornerShape(14.dp),
@@ -1162,7 +1162,7 @@ class AlertDialogs(
.padding(horizontal = 6.dp),
value = lon.value,
onValueChange = { lon.value = it },
label = { Text(text = "Longitude") },
label = { Text(text = translation["longitude_dialog_hint"]) },
leadingIcon = { Icon(Icons.Filled.Navigation, contentDescription = null) },
singleLine = true,
shape = RoundedCornerShape(14.dp),