diff --git a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt index aac72a4..c191e39 100644 --- a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt +++ b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatScreen.kt @@ -109,7 +109,7 @@ fun OnlineMsgApp(viewModel: ChatViewModel = viewModel()) { var tab by rememberSaveable { mutableStateOf(MainTab.CHAT) } // 定义翻译函数 t - fun t(key: String) = LanguageManager.getString(key, state.language) + fun language(key: String) = LanguageManager.getString(key, state.language) // 监听 ViewModel 发送的 UI 事件(如 Snackbar 消息) LaunchedEffect(Unit) { @@ -146,7 +146,7 @@ fun OnlineMsgApp(viewModel: ChatViewModel = viewModel()) { NavigationBarItem( selected = tab == MainTab.CHAT, onClick = { tab = MainTab.CHAT }, - label = { Text(t(MainTab.CHAT.labelKey), style = MaterialTheme.typography.labelSmall) }, + label = { Text(language(MainTab.CHAT.labelKey), style = MaterialTheme.typography.labelSmall) }, icon = { Icon( imageVector = Icons.Rounded.Forum, @@ -158,7 +158,7 @@ fun OnlineMsgApp(viewModel: ChatViewModel = viewModel()) { NavigationBarItem( selected = tab == MainTab.SETTINGS, onClick = { tab = MainTab.SETTINGS }, - label = { Text(t(MainTab.SETTINGS.labelKey), style = MaterialTheme.typography.labelSmall) }, + label = { Text(language(MainTab.SETTINGS.labelKey), style = MaterialTheme.typography.labelSmall) }, icon = { Icon( imageVector = Icons.Rounded.Settings, @@ -287,8 +287,8 @@ private fun ChatTab( ) { val listState = rememberLazyListState() - // 定义翻译函数 t - fun t(key: String) = LanguageManager.getString(key, state.language) + // 定义语言函数 language + fun language(key: String) = LanguageManager.getString(key, state.language) // 当消息列表新增消息时,自动滚动到底部 LaunchedEffect(state.visibleMessages.size) { @@ -312,12 +312,12 @@ private fun ChatTab( FilterChip( selected = !state.directMode, onClick = { onToggleDirectMode(false) }, - label = { Text(t("chat.broadcast")) } + label = { Text(language("chat.broadcast")) } ) FilterChip( selected = state.directMode, onClick = { onToggleDirectMode(true) }, - label = { Text(t("chat.private")) } + label = { Text(language("chat.private")) } ) // 在这一行腾出的空间可以放置其他快捷操作,或者保持简洁 @@ -336,8 +336,8 @@ private fun ChatTab( value = state.targetKey, onValueChange = onTargetKeyChange, modifier = Modifier.fillMaxWidth(), - label = { Text(t("chat.target_key")) }, - placeholder = { Text(t("chat.target_key")) }, + label = { Text(language("chat.target_key")) }, + placeholder = { Text(language("chat.target_key")) }, maxLines = 3 ) } @@ -362,7 +362,7 @@ private fun ChatTab( ) ) { Text( - text = t("chat.empty_hint"), + text = language("chat.empty_hint"), modifier = Modifier.padding(12.dp), style = MaterialTheme.typography.bodyMedium ) @@ -390,7 +390,7 @@ private fun ChatTab( value = state.draft, onValueChange = onDraftChange, modifier = Modifier.weight(1f), - label = { Text(t("chat.input_placeholder")) }, + label = { Text(language("chat.input_placeholder")) }, maxLines = 4, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send), keyboardActions = KeyboardActions( @@ -405,7 +405,7 @@ private fun ChatTab( ) { Icon(Icons.AutoMirrored.Rounded.Send, contentDescription = null) Spacer(Modifier.width(6.dp)) - Text(if (state.sending) "..." else t("chat.send")) + Text(if (state.sending) "..." else language("chat.send")) } } } @@ -593,7 +593,7 @@ private fun SettingsTab( onUseDynamicColorChange: (Boolean) -> Unit, onLanguageChange: (String) -> Unit ) { - fun t(key: String) = LanguageManager.getString(key, state.language) + fun language(key: String) = LanguageManager.getString(key, state.language) val settingsCardModifier = Modifier.fillMaxWidth() val settingsCardContentModifier = Modifier @@ -614,12 +614,12 @@ private fun SettingsTab( modifier = settingsCardContentModifier, verticalArrangement = settingsCardContentSpacing ) { - Text(t("settings.personal"), style = MaterialTheme.typography.titleMedium) + Text(language("settings.personal"), style = MaterialTheme.typography.titleMedium) OutlinedTextField( value = state.displayName, onValueChange = onDisplayNameChange, modifier = Modifier.fillMaxWidth(), - label = { Text(t("settings.display_name")) }, + label = { Text(language("settings.display_name")) }, maxLines = 1 ) } @@ -632,9 +632,9 @@ private fun SettingsTab( modifier = settingsCardContentModifier, verticalArrangement = settingsCardContentSpacing ) { - Text("聊天数据", style = MaterialTheme.typography.titleMedium) + Text(language("settings.chat_data"), style = MaterialTheme.typography.titleMedium) OutlinedButton(onClick = onClearMessages) { - Text(t("settings.clear_msg")) + Text(language("settings.clear_msg")) } } } @@ -646,21 +646,21 @@ private fun SettingsTab( modifier = settingsCardContentModifier, verticalArrangement = settingsCardContentSpacing ) { - Text(t("settings.server"), style = MaterialTheme.typography.titleMedium) + Text(language("settings.server"), style = MaterialTheme.typography.titleMedium) OutlinedTextField( value = state.serverUrl, onValueChange = onServerUrlChange, modifier = Modifier.fillMaxWidth(), - label = { Text(t("settings.server_url")) }, + label = { Text(language("settings.server_url")) }, maxLines = 1 ) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - Button(onClick = onSaveServer) { Text(t("settings.save_server")) } - OutlinedButton(onClick = onRemoveServer) { Text(t("settings.remove_current")) } + Button(onClick = onSaveServer) { Text(language("settings.save_server")) } + OutlinedButton(onClick = onRemoveServer) { Text(language("settings.remove_current")) } } if (state.serverUrls.isNotEmpty()) { HorizontalDivider() - Text(t("settings.saved_servers"), style = MaterialTheme.typography.labelLarge) + Text(language("settings.saved_servers"), style = MaterialTheme.typography.labelLarge) LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { items(state.serverUrls) { url -> AssistChip( @@ -680,16 +680,16 @@ private fun SettingsTab( modifier = settingsCardContentModifier, verticalArrangement = settingsCardContentSpacing ) { - Text(t("settings.identity"), style = MaterialTheme.typography.titleMedium) + Text(language("settings.identity"), style = MaterialTheme.typography.titleMedium) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Button(onClick = onRevealPublicKey, enabled = !state.loadingPublicKey) { - Text(if (state.loadingPublicKey) "..." else t("settings.reveal_key")) + Text(if (state.loadingPublicKey) "..." else language("settings.reveal_key")) } OutlinedButton( onClick = onCopyPublicKey, enabled = state.myPublicKey.isNotBlank() ) { - Text(t("settings.copy_key")) + Text(language("settings.copy_key")) } } OutlinedTextField( @@ -697,7 +697,7 @@ private fun SettingsTab( onValueChange = {}, modifier = Modifier.fillMaxWidth(), readOnly = true, - label = { Text(t("settings.my_key")) }, + label = { Text(language("settings.my_key")) }, maxLines = 4 ) } @@ -710,7 +710,7 @@ private fun SettingsTab( modifier = settingsCardContentModifier, verticalArrangement = settingsCardContentSpacing ) { - Text(t("settings.language"), style = MaterialTheme.typography.titleMedium) + Text(language("settings.language"), style = MaterialTheme.typography.titleMedium) LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { items(LanguageManager.supportedLanguages) { lang -> FilterChip( @@ -737,7 +737,7 @@ private fun SettingsTab( modifier = settingsCardContentModifier, verticalArrangement = settingsCardContentSpacing ) { - Text(t("settings.theme"), style = MaterialTheme.typography.titleMedium) + Text(language("settings.theme"), style = MaterialTheme.typography.titleMedium) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { Row( verticalAlignment = Alignment.CenterVertically, @@ -747,18 +747,18 @@ private fun SettingsTab( checked = state.useDynamicColor, onCheckedChange = onUseDynamicColorChange ) - Text(t("settings.dynamic_color")) + Text(language("settings.dynamic_color")) } } if (!state.useDynamicColor || Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { - Text(t("settings.preset_themes"), style = MaterialTheme.typography.labelLarge) + Text(language("settings.preset_themes"), style = MaterialTheme.typography.labelLarge) LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { items(themeOptions) { option -> val themeName = when (option.id) { - "blue" -> t("theme.blue") - "gray" -> t("theme.gray") - "green" -> t("theme.green") - "red" -> t("theme.red") + "blue" -> language("theme.blue") + "gray" -> language("theme.gray") + "green" -> language("theme.green") + "red" -> language("theme.red") else -> option.name } FilterChip( @@ -786,16 +786,16 @@ private fun SettingsTab( modifier = settingsCardContentModifier, verticalArrangement = settingsCardContentSpacing ) { - Text(t("settings.diagnostics"), style = MaterialTheme.typography.titleMedium) - Text("${t("settings.status_hint")}:${state.statusHint}") - Text("${t("settings.current_status")}:${state.statusText}") - Text("${t("settings.cert_fingerprint")}:${state.certFingerprint.ifBlank { "N/A" }}") + Text(language("settings.diagnostics"), style = MaterialTheme.typography.titleMedium) + Text("${language("settings.status_hint")}:${state.statusHint}") + Text("${language("settings.current_status")}:${state.statusText}") + Text("${language("settings.cert_fingerprint")}:${state.certFingerprint.ifBlank { "N/A" }}") Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Switch(checked = state.showSystemMessages, onCheckedChange = onToggleShowSystem) - Text(t("settings.show_system")) + Text(language("settings.show_system")) } } } diff --git a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatSessionManager.kt b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatSessionManager.kt index a9750ee..9bdec04 100644 --- a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatSessionManager.kt +++ b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatSessionManager.kt @@ -48,6 +48,7 @@ import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.encodeToJsonElement +import com.onlinemsg.client.util.LanguageManager /** * 单例管理类,负责整个聊天会话的生命周期、网络连接、消息收发、状态维护和持久化。 @@ -98,10 +99,11 @@ object ChatSessionManager { private val socketListener = object : OnlineMsgSocketClient.Listener { override fun onOpen() { scope.launch { + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.HANDSHAKING, - statusHint = "已连接,正在准备聊天..." + statusHint = LanguageManager.getString("status_hint.handshaking", lang) ) } addSystemMessage("连接已建立") @@ -122,7 +124,8 @@ object ChatSessionManager { override fun onBinaryMessage(payload: ByteArray) { scope.launch { if (_uiState.value.status == ConnectionStatus.HANDSHAKING) { - _uiState.update { it.copy(statusHint = "收到二进制握手帧,正在尝试解析...") } + val lang = _uiState.value.language + _uiState.update { it.copy(statusHint = LanguageManager.getString("status_hint.received_binary_handshake", lang)) } } val utf8 = runCatching { String(payload, StandardCharsets.UTF_8) }.getOrNull().orEmpty() @@ -152,10 +155,11 @@ object ChatSessionManager { if (manualClose) return@launch val message = throwable.message?.takeIf { it.isNotBlank() } ?: "unknown" addSystemMessage("连接异常:$message") + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "连接异常,正在重试" + statusHint = LanguageManager.getString("status_hint.connection_error_retrying", lang) ) } scheduleReconnect("连接异常") @@ -193,7 +197,8 @@ object ChatSessionManager { themeId = pref.themeId, useDynamicColor = pref.useDynamicColor, language = pref.language, - messages = historyMessages + messages = historyMessages, + statusHint = LanguageManager.getString("status_hint.click_to_connect", pref.language) ) } // 如果上次会话启用了自动重连,则自动恢复连接 @@ -322,11 +327,12 @@ object ChatSessionManager { } val nextUrls = ServerUrlFormatter.append(_uiState.value.serverUrls, normalized) + val lang = _uiState.value.language _uiState.update { it.copy( serverUrl = normalized, serverUrls = nextUrls, - statusHint = "服务器地址已保存" + statusHint = LanguageManager.getString("status_hint.server_saved", lang) ) } @@ -351,11 +357,16 @@ object ChatSessionManager { filtered } + val lang = _uiState.value.language _uiState.update { it.copy( serverUrls = nextUrls, serverUrl = nextUrls.first(), - statusHint = if (filtered.isEmpty()) "已恢复默认服务器地址" else "已移除当前服务器地址" + statusHint = if (filtered.isEmpty()) { + LanguageManager.getString("status_hint.server_removed_default", lang) + } else { + LanguageManager.getString("status_hint.server_removed", lang) + } ) } @@ -405,10 +416,11 @@ object ChatSessionManager { val normalized = ServerUrlFormatter.normalize(state.serverUrl) if (normalized.isBlank()) { + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "请填写有效服务器地址" + statusHint = LanguageManager.getString("status_hint.invalid_server_url", lang) ) } return @@ -424,10 +436,11 @@ object ChatSessionManager { cancelHelloTimeout() cancelAuthTimeout() + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.CONNECTING, - statusHint = "正在连接服务器...", + statusHint = LanguageManager.getString("status_hint.connecting", lang), serverUrl = normalized, certFingerprint = "" ) @@ -456,10 +469,11 @@ object ChatSessionManager { cancelHelloTimeout() cancelAuthTimeout() socketClient.close(1000, "manual_close") + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.IDLE, - statusHint = "连接已关闭" + statusHint = LanguageManager.getString("status_hint.disconnected", lang) ) } autoReconnectTriggered = false @@ -487,7 +501,8 @@ object ChatSessionManager { val key = if (_uiState.value.directMode) _uiState.value.targetKey.trim() else "" if (_uiState.value.directMode && key.isBlank()) { - _uiState.update { it.copy(statusHint = "请先填写目标公钥,再发送私聊消息") } + val lang = _uiState.value.language + _uiState.update { it.copy(statusHint = LanguageManager.getString("status_hint.target_key_required", lang)) } return@launch } @@ -563,7 +578,8 @@ object ChatSessionManager { */ private suspend fun handleIncomingMessage(rawText: String) { if (_uiState.value.status == ConnectionStatus.HANDSHAKING) { - _uiState.update { it.copy(statusHint = "已收到握手数据,正在解析...") } + val lang = _uiState.value.language + _uiState.update { it.copy(statusHint = LanguageManager.getString("status_hint.hello_received", lang)) } } val normalizedText = extractJsonCandidate(rawText) @@ -595,10 +611,11 @@ object ChatSessionManager { runCatching { json.decodeFromJsonElement(it) }.getOrNull() } if (hello == null || hello.publicKey.isBlank() || hello.authChallenge.isBlank()) { + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "握手失败:服务端响应不完整" + statusHint = LanguageManager.getString("status_hint.handshake_failed_incomplete", lang) ) } return @@ -609,14 +626,13 @@ object ChatSessionManager { // 握手阶段收到非预期消息则报错 if (_uiState.value.status == ConnectionStatus.HANDSHAKING && plain != null) { - _uiState.update { it.copy(statusHint = "握手失败:收到非预期消息") } + val lang = _uiState.value.language + _uiState.update { it.copy(statusHint = LanguageManager.getString("status_hint.handshake_failed_unexpected", lang)) } addSystemMessage("握手阶段收到非预期消息类型:${plain.type}") } else if (_uiState.value.status == ConnectionStatus.HANDSHAKING && plain == null) { - val preview = rawText - .replace("\n", " ") - .replace("\r", " ") - .take(80) - _uiState.update { it.copy(statusHint = "握手失败:首包解析失败") } + val lang = _uiState.value.language + val preview = rawText.replace("\n", " ").replace("\r", " ").take(80) + _uiState.update { it.copy(statusHint = LanguageManager.getString("status_hint.handshake_failed_parse", lang)) } addSystemMessage("握手包解析失败:$preview") } @@ -645,10 +661,11 @@ object ChatSessionManager { private suspend fun handleServerHello(hello: HelloDataDto) { cancelHelloTimeout() serverPublicKey = hello.publicKey + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.AUTHENTICATING, - statusHint = "正在完成身份验证...", + statusHint = LanguageManager.getString("status_hint.authenticating", lang), certFingerprint = hello.certFingerprintSha256.orEmpty() ) } @@ -657,10 +674,11 @@ object ChatSessionManager { authTimeoutJob = scope.launch { delay(AUTH_TIMEOUT_MS) if (_uiState.value.status == ConnectionStatus.AUTHENTICATING) { + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "连接超时,请重试" + statusHint = LanguageManager.getString("status_hint.auth_timeout", lang) ) } addSystemMessage("认证超时,请检查网络后重试") @@ -674,10 +692,11 @@ object ChatSessionManager { addSystemMessage("已发送认证请求") }.onFailure { error -> cancelAuthTimeout() + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "认证失败" + statusHint = LanguageManager.getString("status_hint.auth_failed", lang) ) } addSystemMessage("认证发送失败:${error.message ?: "unknown"}") @@ -742,10 +761,11 @@ object ChatSessionManager { cancelAuthTimeout() cancelReconnect() reconnectAttempt = 0 + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.READY, - statusHint = "已连接,可以开始聊天" + statusHint = LanguageManager.getString("status_hint.ready", lang) ) } addSystemMessage("连接准备完成") @@ -804,10 +824,11 @@ object ChatSessionManager { if (fallbackUrl.isNotBlank()) { fallbackTried = true connectedUrl = fallbackUrl + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.CONNECTING, - statusHint = "正在自动重试连接...", + statusHint = LanguageManager.getString("status_hint.reconnecting", lang), serverUrl = fallbackUrl ) } @@ -817,14 +838,16 @@ object ChatSessionManager { } } + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "连接已中断,正在重试" + statusHint = LanguageManager.getString("status_hint.connection_interrupted_retrying", lang) ) } addSystemMessage("连接关闭 ($code):${reason.ifBlank { "连接中断" }}") scheduleReconnect("连接已中断") + } /** @@ -935,10 +958,16 @@ object ChatSessionManager { val exponential = 1 shl minOf(reconnectAttempt - 1, 5) val delaySeconds = minOf(MAX_RECONNECT_DELAY_SECONDS, exponential) addSystemMessage("$reason,${delaySeconds}s 后自动重连(第 $reconnectAttempt 次)") + val lang = _uiState.value.language + val hint = String.format( + LanguageManager.getString("status_hint.reconnect_countdown", lang), + delaySeconds, + reconnectAttempt + ) _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "${delaySeconds}s 后自动重连(第 $reconnectAttempt 次)" + statusHint = hint ) } @@ -950,10 +979,11 @@ object ChatSessionManager { ServerUrlFormatter.normalize(_uiState.value.serverUrl) } if (target.isBlank()) { + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "重连失败:服务器地址无效" + statusHint = LanguageManager.getString("status_hint.reconnect_failed_invalid_url", lang) ) } return@launch @@ -1015,10 +1045,11 @@ object ChatSessionManager { delay(HELLO_TIMEOUT_MS) if (_uiState.value.status == ConnectionStatus.HANDSHAKING) { val currentUrl = connectedUrl.ifBlank { "unknown" } + val lang = _uiState.value.language _uiState.update { it.copy( status = ConnectionStatus.ERROR, - statusHint = "握手超时,请检查地址路径与反向代理" + statusHint = LanguageManager.getString("status_hint.hello_timeout", lang) ) } addSystemMessage("握手超时:未收到服务端 publickey 首包(当前地址:$currentUrl)") diff --git a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatUiState.kt b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatUiState.kt index c7cc58b..e0438ac 100644 --- a/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatUiState.kt +++ b/android-client/app/src/main/java/com/onlinemsg/client/ui/ChatUiState.kt @@ -1,6 +1,9 @@ package com.onlinemsg.client.ui +import androidx.compose.runtime.getValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle import java.util.UUID +import com.onlinemsg.client.util.LanguageManager /** * 连接状态枚举。 @@ -109,17 +112,18 @@ data class ChatUiState( val canSend: Boolean get() = status == ConnectionStatus.READY && draft.trim().isNotEmpty() && !sending + /** * 连接状态的简短文本描述。 */ val statusText: String get() = when (status) { - ConnectionStatus.IDLE -> "未连接" + ConnectionStatus.IDLE -> LanguageManager.getString("status.idle", language) ConnectionStatus.CONNECTING, ConnectionStatus.HANDSHAKING, - ConnectionStatus.AUTHENTICATING -> "连接中" - ConnectionStatus.READY -> "已连接" - ConnectionStatus.ERROR -> "异常断开" + ConnectionStatus.AUTHENTICATING -> LanguageManager.getString("status.connecting", language) + ConnectionStatus.READY -> LanguageManager.getString("status.ready", language) + ConnectionStatus.ERROR -> LanguageManager.getString("status.error", language) } /** diff --git a/android-client/app/src/main/java/com/onlinemsg/client/util/LanguageManager.kt b/android-client/app/src/main/java/com/onlinemsg/client/util/LanguageManager.kt index 355c591..3ebc08b 100644 --- a/android-client/app/src/main/java/com/onlinemsg/client/util/LanguageManager.kt +++ b/android-client/app/src/main/java/com/onlinemsg/client/util/LanguageManager.kt @@ -12,6 +12,7 @@ object LanguageManager { "tab.settings" to "设置", "settings.personal" to "个人设置", "settings.display_name" to "显示名称", + "settings.chat_data" to "聊天数据", "settings.server" to "服务器", "settings.server_url" to "服务器地址", "settings.save_server" to "保存地址", @@ -29,8 +30,6 @@ object LanguageManager { "settings.current_status" to "当前状态", "settings.cert_fingerprint" to "证书指纹", "settings.show_system" to "显示系统消息", - "settings.connect" to "连接", - "settings.disconnect" to "断开", "settings.clear_msg" to "清空消息", "settings.dynamic_color" to "使用动态颜色", "chat.broadcast" to "广播", @@ -46,13 +45,44 @@ object LanguageManager { "theme.gray" to "商务灰", "theme.green" to "翠绿", "theme.red" to "绯红", - "theme.warm" to "温暖" + "theme.warm" to "温暖", + "top_bar.link" to "已连接", + "top_bar.dislink" to "未连接", + "top_bar.link_start" to "连接中", + "top_bar.error_dislink" to "异常断开", + + "status_hint.ready" to "已连接,可以开始聊天", + "status_hint.click_to_connect" to "点击连接开始聊天", + "status_hint.handshaking" to "已连接,正在准备聊天...", + "status_hint.received_binary_handshake" to "收到二进制握手帧,正在尝试解析...", + "status_hint.connection_error_retrying" to "连接异常,正在重试", + "status_hint.invalid_server_url" to "请填写有效服务器地址", + "status_hint.connecting" to "正在连接服务器...", + "status_hint.disconnected" to "连接已关闭", + "status_hint.server_saved" to "服务器地址已保存", + "status_hint.server_removed_default" to "已恢复默认服务器地址", + "status_hint.server_removed" to "已移除当前服务器地址", + "status_hint.target_key_required" to "请先填写目标公钥,再发送私聊消息", + "status_hint.hello_received" to "已收到握手数据,正在解析...", + "status_hint.handshake_failed_incomplete" to "握手失败:服务端响应不完整", + "status_hint.handshake_failed_unexpected" to "握手失败:收到非预期消息", + "status_hint.handshake_failed_parse" to "握手失败:首包解析失败", + "status_hint.authenticating" to "正在完成身份验证...", + "status_hint.auth_timeout" to "连接超时,请重试", + "status_hint.auth_failed" to "认证失败", + "status_hint.ready" to "已连接,可以开始聊天", + "status_hint.reconnecting" to "正在自动重试连接...", + "status_hint.connection_interrupted_retrying" to "连接已中断,正在重试", + "status_hint.reconnect_countdown" to "%d秒后自动重连(第 %d 次)", + "status_hint.reconnect_failed_invalid_url" to "重连失败:服务器地址无效", + "status_hint.hello_timeout" to "握手超时,请检查地址路径与反向代理" ), "en" to mapOf( "tab.chat" to "Chat", "tab.settings" to "Settings", "settings.personal" to "Personal", "settings.display_name" to "Display Name", + "settings.chat_data" to "Chat Data", "settings.server" to "Server", "settings.server_url" to "Server Address", "settings.save_server" to "Save", @@ -70,8 +100,6 @@ object LanguageManager { "settings.current_status" to "Status", "settings.cert_fingerprint" to "Fingerprint", "settings.show_system" to "Show System Messages", - "settings.connect" to "Link", - "settings.disconnect" to "Dislink", "settings.clear_msg" to "ClearMsg", "settings.dynamic_color" to "Use dynamic color", "chat.broadcast" to "Broadcast", @@ -87,13 +115,44 @@ object LanguageManager { "theme.gray" to "Business Gray", "theme.green" to "Green", "theme.red" to "Red", - "theme.warm" to "Warm" + "theme.warm" to "Warm", + "status.idle" to "Idle", + "status.connecting" to "Connecting", + "status.ready" to "Connected", + "status.error" to "Error", + + "status_hint.ready" to "Ready, you can start chatting", + "status_hint.click_to_connect" to "Click connect to start chatting", + "status_hint.handshaking" to "Connected, preparing chat...", + "status_hint.received_binary_handshake" to "Received binary handshake frame, parsing...", + "status_hint.connection_error_retrying" to "Connection error, retrying", + "status_hint.invalid_server_url" to "Please enter a valid server address", + "status_hint.connecting" to "Connecting to server...", + "status_hint.disconnected" to "Connection closed", + "status_hint.server_saved" to "Server address saved", + "status_hint.server_removed_default" to "Restored default server address", + "status_hint.server_removed" to "Removed current server address", + "status_hint.target_key_required" to "Please enter target public key first", + "status_hint.hello_received" to "Handshake data received, parsing...", + "status_hint.handshake_failed_incomplete" to "Handshake failed: incomplete server response", + "status_hint.handshake_failed_unexpected" to "Handshake failed: unexpected message", + "status_hint.handshake_failed_parse" to "Handshake failed: first packet parse error", + "status_hint.authenticating" to "Authenticating...", + "status_hint.auth_timeout" to "Connection timeout, please retry", + "status_hint.auth_failed" to "Authentication failed", + "status_hint.ready" to "Connected, ready to chat", + "status_hint.reconnecting" to "Reconnecting...", + "status_hint.connection_interrupted_retrying" to "Connection interrupted, retrying", + "status_hint.reconnect_countdown" to "Reconnecting in %d seconds (attempt %d)", + "status_hint.reconnect_failed_invalid_url" to "Reconnect failed: invalid server address", + "status_hint.hello_timeout" to "Handshake timeout, check server path and reverse proxy" ), "ja" to mapOf( "tab.chat" to "チャット", "tab.settings" to "設定", "settings.personal" to "個人設定", "settings.display_name" to "表示名", + "settings.chat_data" to "チャットデータ", "settings.server" to "サーバー", "settings.server_url" to "アドレス", "settings.save_server" to "保存", @@ -111,8 +170,6 @@ object LanguageManager { "settings.current_status" to "ステータス", "settings.cert_fingerprint" to "証明書指紋", "settings.show_system" to "システムメッセージを表示", - "settings.connect" to "接続", - "settings.disconnect" to "切断", "settings.clear_msg" to "履歴を消去", "settings.dynamic_color" to "動的カラーを使用", "chat.broadcast" to "全体", @@ -128,13 +185,44 @@ object LanguageManager { "theme.gray" to "ビジネスグレー", "theme.green" to "グリーン", "theme.red" to "レッド", - "theme.warm" to "ウォーム" + "theme.warm" to "ウォーム", + "status.idle" to "未接続", + "status.connecting" to "接続中", + "status.ready" to "接続済み", + "status.error" to "エラー", + + "status_hint.ready" to "接続済み、チャットを開始できます", + "status_hint.click_to_connect" to "接続してチャットを開始", + "status_hint.handshaking" to "接続しました、準備中...", + "status_hint.received_binary_handshake" to "バイナリハンドシェイクを受信、解析中...", + "status_hint.connection_error_retrying" to "接続エラー、再試行中", + "status_hint.invalid_server_url" to "有効なサーバーアドレスを入力してください", + "status_hint.connecting" to "サーバーに接続中...", + "status_hint.disconnected" to "接続が切断されました", + "status_hint.server_saved" to "サーバーアドレスを保存しました", + "status_hint.server_removed_default" to "デフォルトサーバーに戻しました", + "status_hint.server_removed" to "現在のサーバーを削除しました", + "status_hint.target_key_required" to "相手の公開鍵を入力してください", + "status_hint.hello_received" to "ハンドシェイクデータを受信、解析中...", + "status_hint.handshake_failed_incomplete" to "ハンドシェイク失敗:サーバー応答が不完全", + "status_hint.handshake_failed_unexpected" to "ハンドシェイク失敗:予期しないメッセージ", + "status_hint.handshake_failed_parse" to "ハンドシェイク失敗:最初のパケット解析エラー", + "status_hint.authenticating" to "認証中...", + "status_hint.auth_timeout" to "接続タイムアウト、再試行してください", + "status_hint.auth_failed" to "認証に失敗しました", + "status_hint.ready" to "接続完了、チャットを開始できます", + "status_hint.reconnecting" to "自動再接続中...", + "status_hint.connection_interrupted_retrying" to "接続が切断されました、再試行中", + "status_hint.reconnect_countdown" to "%d秒後に再接続(%d回目)", + "status_hint.reconnect_failed_invalid_url" to "再接続失敗:サーバーアドレスが無効", + "status_hint.hello_timeout" to "ハンドシェイクタイムアウト、サーバーパスを確認してください" ), "ko" to mapOf( "tab.chat" to "채팅", "tab.settings" to "설정", "settings.personal" to "개인 설정", "settings.display_name" to "표시 이름", + "settings.chat_data" to "채팅 데이터", "settings.server" to "서버", "settings.server_url" to "서버 주소", "settings.save_server" to "주소 저장", @@ -152,8 +240,6 @@ object LanguageManager { "settings.current_status" to "현재 상태", "settings.cert_fingerprint" to "인증서 지문", "settings.show_system" to "시스템 메시지 표시", - "settings.connect" to "연결", - "settings.disconnect" to "연결 끊기", "settings.clear_msg" to "정보 삭제", "settings.dynamic_color" to "동적 색상 사용", "chat.broadcast" to "브로드캐스트", @@ -169,7 +255,37 @@ object LanguageManager { "theme.gray" to "비즈니스 그레이", "theme.green" to "초록", "theme.red" to "빨강", - "theme.warm" to "따뜻함" + "theme.warm" to "따뜻함", + "status.idle" to "연결 안 됨", + "status.connecting" to "연결 중", + "status.ready" to "연결됨", + "status.error" to "오류", + + "status_hint.ready" to "연결됨, 채팅을 시작할 수 있습니다", + "status_hint.click_to_connect" to "연결하여 채팅 시작", + "status_hint.handshaking" to "연결됨, 채팅 준비 중...", + "status_hint.received_binary_handshake" to "바이너리 핸드셰이크 수신, 분석 중...", + "status_hint.connection_error_retrying" to "연결 오류, 재시도 중", + "status_hint.invalid_server_url" to "유효한 서버 주소를 입력하세요", + "status_hint.connecting" to "서버에 연결 중...", + "status_hint.disconnected" to "연결이 종료됨", + "status_hint.server_saved" to "서버 주소가 저장됨", + "status_hint.server_removed_default" to "기본 서버 주소로 복원됨", + "status_hint.server_removed" to "현재 서버 주소가 제거됨", + "status_hint.target_key_required" to "대상 공개키를 먼저 입력하세요", + "status_hint.hello_received" to "핸드셰이크 데이터 수신, 분석 중...", + "status_hint.handshake_failed_incomplete" to "핸드셰이크 실패: 서버 응답 불완전", + "status_hint.handshake_failed_unexpected" to "핸드셰이크 실패: 예상치 못한 메시지", + "status_hint.handshake_failed_parse" to "핸드셰이크 실패: 첫 패킷 구문 분석 오류", + "status_hint.authenticating" to "인증 중...", + "status_hint.auth_timeout" to "연결 시간 초과, 다시 시도하세요", + "status_hint.auth_failed" to "인증 실패", + "status_hint.ready" to "연결됨, 채팅 가능", + "status_hint.reconnecting" to "자동 재연결 중...", + "status_hint.connection_interrupted_retrying" to "연결이 끊어짐, 재시도 중", + "status_hint.reconnect_countdown" to "%d초 후 자동 재연결 (시도 %d회)", + "status_hint.reconnect_failed_invalid_url" to "재연결 실패: 서버 주소가 유효하지 않음", + "status_hint.hello_timeout" to "핸드셰이크 시간 초과, 서버 경로와 리버스 프록시를 확인하세요" ) ) diff --git a/android-client/build.gradle.kts b/android-client/build.gradle.kts index 8c29eb8..916c95d 100644 --- a/android-client/build.gradle.kts +++ b/android-client/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.application") version "8.5.2" apply false + id("com.android.application") version "8.13.2" apply false id("org.jetbrains.kotlin.android") version "1.9.24" apply false id("org.jetbrains.kotlin.plugin.serialization") version "1.9.24" apply false id("com.google.devtools.ksp") version "1.9.24-1.0.20" apply false diff --git a/android-client/gradle/wrapper/gradle-wrapper.properties b/android-client/gradle/wrapper/gradle-wrapper.properties index b82aa23..37f853b 100644 --- a/android-client/gradle/wrapper/gradle-wrapper.properties +++ b/android-client/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME