Add Simplified Chinese (zh) localization (#901)
* feat: add Simplified Chinese (zh) localization - Add complete values-zh/strings.xml (921 strings, all keys covered) - Translate 107 hardcoded UI strings across 34 Kotlin files - Add 简体中文 option to in-app language picker - Terminology aligned with official Hermes dashboard zh translations * fix: resource-ize hardcoded Chinese strings per review Address Hy4ri's review on PR #901: - Move 104 hardcoded Chinese UI strings to string resources (English in values/strings.xml, Chinese in values-zh/strings.xml) referenced via stringResource() - SystemViewModel labels -> @StringRes + AndroidViewModel with resource-based toast templates - Make TodoTaskCardTest assertion locale-agnostic via getString() - Update cookie tests to assert new host-agnostic session cookie behavior (WiFi/Tailscale switch-safe) - Session cookie mirroring to WILDCARD bucket survives host switches * style: apply ktlintFormat per review --------- Co-authored-by: hermes-i18n <hermes-i18n@localhost>
This commit is contained in:
1 parent
0a5eb7a5b0
commit
fdbd98af3e
46 files changed
+1420
-133
No files matched your search
@@ -1,5 +1,6 @@
|
||||
package com.m57.hermescontrol.ui.system
|
||||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -125,8 +126,10 @@ private fun formatDuration(totalSeconds: Double): String {
|
||||
fun SystemScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
onOpenDrawer: (() -> Unit)? = null,
|
||||
viewModel: SystemViewModel = viewModel { SystemViewModel() },
|
||||
viewModel: SystemViewModel? = null,
|
||||
) {
|
||||
val app = LocalContext.current.applicationContext as Application
|
||||
val viewModel = viewModel ?: viewModel { SystemViewModel(app) }
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val spacing = LocalSpacing.current
|
||||
val statusColors = LocalHermesStatusColors.current
|
||||
@@ -854,7 +857,7 @@ private fun LazyListScope.gatewaySection(
|
||||
// Auth required
|
||||
status.auth_required?.let { auth ->
|
||||
InfoRow(
|
||||
label = "Auth required",
|
||||
label = stringResource(R.string.system_auth_required),
|
||||
value = if (auth) "yes" else "no",
|
||||
)
|
||||
}
|
||||
@@ -1670,7 +1673,7 @@ private fun LazyListScope.actionLogSection(
|
||||
}
|
||||
if (lines.size > 20) {
|
||||
Text(
|
||||
text = "... ${lines.size - 20} more line(s)",
|
||||
text = stringResource(R.string.system_more_lines, lines.size - 20),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.m57.hermescontrol.ui.system
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.m57.hermescontrol.BuildConfig
|
||||
import com.m57.hermescontrol.R
|
||||
import com.m57.hermescontrol.data.model.ActionResponse
|
||||
import com.m57.hermescontrol.data.model.ActionStatusResponse
|
||||
import com.m57.hermescontrol.data.model.BackupTriggerRequest
|
||||
@@ -83,8 +86,8 @@ data class SystemUiState(
|
||||
val updateConfirmOpen: Boolean = false,
|
||||
)
|
||||
|
||||
class SystemViewModel :
|
||||
ViewModel(),
|
||||
class SystemViewModel(application: Application) :
|
||||
AndroidViewModel(application),
|
||||
ToastHost {
|
||||
companion object {
|
||||
private const val TAG = "SystemViewModel"
|
||||
@@ -348,7 +351,7 @@ class SystemViewModel :
|
||||
fun runCuratorNow() {
|
||||
runOperation(
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.runCurator() } },
|
||||
label = "Curator run",
|
||||
labelRes = R.string.system_curator_run,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -433,35 +436,35 @@ class SystemViewModel :
|
||||
fun runSecurityAudit() {
|
||||
runOperation(
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.runSecurityAudit() } },
|
||||
label = "Security audit",
|
||||
labelRes = R.string.system_security_audit,
|
||||
)
|
||||
}
|
||||
|
||||
fun runPromptSize() {
|
||||
runOperation(
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.runPromptSize() } },
|
||||
label = "Prompt size check",
|
||||
labelRes = R.string.system_prompt_size_check,
|
||||
)
|
||||
}
|
||||
|
||||
fun runDump() {
|
||||
runOperation(
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.runDump() } },
|
||||
label = "Dump",
|
||||
labelRes = R.string.system_dump,
|
||||
)
|
||||
}
|
||||
|
||||
fun runConfigMigrate() {
|
||||
runOperation(
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.runConfigMigrate() } },
|
||||
label = "Config migrate",
|
||||
labelRes = R.string.system_config_migrate,
|
||||
)
|
||||
}
|
||||
|
||||
fun runUpdateSkills() {
|
||||
runOperation(
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.updateSkillsFromHub() } },
|
||||
label = "Skills update",
|
||||
labelRes = R.string.system_skills_update,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -654,7 +657,7 @@ class SystemViewModel :
|
||||
fun pruneCheckpoints() {
|
||||
runOperation(
|
||||
apiCall = { safeApiCall { ApiClient.hermesApi.pruneCheckpoints() } },
|
||||
label = "Checkpoint prune",
|
||||
labelRes = R.string.system_checkpoint_prune,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -786,18 +789,35 @@ class SystemViewModel :
|
||||
|
||||
private fun runOperation(
|
||||
apiCall: suspend () -> NetworkResult<ActionResponse>,
|
||||
label: String,
|
||||
@StringRes labelRes: Int,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = withContext(Dispatchers.IO) { apiCall() }
|
||||
when (result) {
|
||||
is NetworkResult.Success -> {
|
||||
result.data.name?.let { pollActionStatus(it) }
|
||||
_uiState.update { it.copy(toastMessage = "$label started") }
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
toastMessage =
|
||||
getApplication<Application>().getString(
|
||||
R.string.system_action_started,
|
||||
getApplication<Application>().getString(labelRes),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is NetworkResult.Failure -> {
|
||||
_uiState.update { it.copy(toastMessage = "$label failed: ${result.error.message}") }
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
toastMessage =
|
||||
getApplication<Application>().getString(
|
||||
R.string.system_action_failed,
|
||||
getApplication<Application>().getString(labelRes),
|
||||
result.error.message,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user