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
@@ -1182,7 +1182,7 @@ class E2eIntegrationTest {
|
||||
Response.success(UpdateCheckResponse())
|
||||
coEvery { mockApiService.triggerBackup(BackupTriggerRequest()) } returns Response.success(ActionResponse())
|
||||
|
||||
val viewModel = SystemViewModel()
|
||||
val viewModel = SystemViewModel(mockApp)
|
||||
viewModel.loadAll()
|
||||
advanceUntilIdle()
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ class CookieManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setSessionCookie_hostScoped_onlyMatchesThatHost() =
|
||||
fun setSessionCookie_isUsableAcrossHosts() =
|
||||
runTest {
|
||||
val jar = fakeJar()
|
||||
CookieManager.setSessionCookie("sess-xyz", ep("dashboard.local"))
|
||||
@@ -115,8 +115,11 @@ class CookieManagerTest {
|
||||
assertEquals(1, onHost.size)
|
||||
assertEquals("sess-xyz", onHost[0].value)
|
||||
|
||||
// Session cookie mirrors to the WILDCARD bucket — usable on any
|
||||
// host within the same server scope (WiFi/Tailscale switch-safe).
|
||||
val offHost = jar.loadForRequest("http://other.local/".toHttpUrl())
|
||||
assertEquals(0, offHost.size)
|
||||
assertEquals(1, offHost.size)
|
||||
assertEquals("sess-xyz", offHost[0].value)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,15 +1,35 @@
|
||||
package com.m57.hermescontrol.data.remote
|
||||
|
||||
import com.m57.hermescontrol.data.config.ServerStore
|
||||
import com.m57.hermescontrol.data.config.ServerStoreState
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class GatewayFileClientTest {
|
||||
private val base = "https://gw.example.com:9119"
|
||||
private val tok = "s e/cret"
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// AuthManager.isGatedMode() reads serverStore — inject a mock whose
|
||||
// wsAuthParam is NOT "ticket" so the tests exercise the loopback
|
||||
// branch (token stamped into the URL). Keeps this builder test
|
||||
// independent of real AuthManager initialization.
|
||||
val mockStore = mockk<ServerStore>(relaxed = true)
|
||||
every { mockStore.getLatestState() } returns
|
||||
ServerStoreState(wsAuthParam = "bearer")
|
||||
val field = AuthManager::class.java.getDeclaredField("_serverStore")
|
||||
field.isAccessible = true
|
||||
field.set(AuthManager, mockStore)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `buildDownloadUrl encodes path and token`() {
|
||||
val url = GatewayFileClient.buildDownloadUrl(base, tok, "/tmp/foo.png")!!
|
||||
|
||||
@@ -54,7 +54,7 @@ class PersistentCookieJarTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cookie_doesNotLeakToOtherHost() =
|
||||
fun sessionCookie_isUsableAcrossHostsWithinServer() =
|
||||
runTest {
|
||||
val jar = makeJar()
|
||||
jar.useStore("server-a")
|
||||
@@ -63,8 +63,12 @@ class PersistentCookieJarTest {
|
||||
listOf(sessionCookie("dashboard.local", "abc123")),
|
||||
)
|
||||
|
||||
// Session cookies mirror to the WILDCARD bucket (host-agnostic)
|
||||
// so the authenticated session survives WiFi/Tailscale host
|
||||
// switches — but only within the same server scope.
|
||||
val loaded = jar.loadForRequest("http://evil.local/".toHttpUrl())
|
||||
assertTrue(loaded.isEmpty())
|
||||
assertEquals(1, loaded.size)
|
||||
assertEquals("abc123", loaded[0].value)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.m57.hermescontrol.ui.system
|
||||
|
||||
import android.app.Application
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
@@ -14,6 +16,7 @@ import org.junit.Test
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class SystemViewModelTest {
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
private val mockApp = mockk<Application>(relaxed = true)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@@ -27,7 +30,7 @@ class SystemViewModelTest {
|
||||
|
||||
@Test
|
||||
fun `openUpdateConfirm sets updateConfirmOpen to true`() {
|
||||
val viewModel = SystemViewModel()
|
||||
val viewModel = SystemViewModel(mockApp)
|
||||
|
||||
assertFalse(viewModel.uiState.value.updateConfirmOpen)
|
||||
|
||||
@@ -38,7 +41,7 @@ class SystemViewModelTest {
|
||||
|
||||
@Test
|
||||
fun `closeUpdateConfirm sets updateConfirmOpen to false`() {
|
||||
val viewModel = SystemViewModel()
|
||||
val viewModel = SystemViewModel(mockApp)
|
||||
|
||||
// First open it
|
||||
viewModel.openUpdateConfirm()
|
||||
|
||||
Reference in new issue
Block a user