Files
chanora/apps/chanora_flutter/lib/l10n/app_zh.arb
T
Edison Jwa 5f1423c349 feat(voice): unified mobile voice bar with gesture-isolated PTT row (#22)
* feat(voice): unified mobile voice bar with gesture-isolated PTT row

Replace separate VoiceStatusChip + VoicePttButton with a single
CompactVoiceBar widget that combines both into a two-row layout:

- Control row (tap): status text, mute, deafen, settings chevron
- PTT row (hold): full-width hold-to-talk, shown only in PTT mode

Gesture isolation prevents mis-touch between rows: the control row
uses tap-only InkWell/IconButton while the PTT row uses a raw
Listener for pointer-down/up events.

Key changes:
- Add CompactVoiceBar widget with state-colored container (normal,
  muted, talk-power-blocked)
- Remove mute/deafen IconButtons from AppBar headerActions
- Restructure voice details sheet into primary section + collapsible
  ExpansionTiles (audio processing, PTT capability, debug)
- Optimistic state updates for mute/deafen to eliminate tap delay
- Instant PTT visual feedback (no AnimatedContainer fade)
- Constant geometry across all states (no layout shift on toggle)

* fix(voice): preserve current PTT button format

* feat(voice): move mute/deafen controls into VoiceStatusChip

* fix(voice): ensure consistent chip height across mute states

Remove isSelected/selectedIcon from IconButtons inside VoiceStatusChip.
Material 3 toggle IconButtons (_SelectableIconButton) can vary in height
when the selected state changes due to tap target sizing. Use simple
conditional icons instead and set shrinkWrap tap target size with tight
constraints for stable 40x40 buttons regardless of state.

* fix(voice): remove leftover duplicate mute/deafen buttons in VoiceStatusChip

* fix(voice): replace unsafe stereo cast with bytemuck and localise talk-power tooltip

Replace the raw-pointer `&mut [(f32, f32)]` to `&mut [f32]` cast in
the oboe output callback with `bytemuck::cast_slice_mut`, eliminating
the unsafe block and relying on bytemuck compile-time NoUninit
verification instead.

Add voiceTalkPowerBlocked l10n key (en + zh) and replace the only
remaining hard-coded English tooltip in VoiceStatusChip with it.
2026-06-05 20:58:16 +09:00

247 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"@@locale": "zh",
"@@x-source-of-truth": "DEC-015 (register v0.9.5). Simplified Chinese for the Beta key set.",
"appTitle": "Chanora",
"homeNotProductionReadyBanner": "Beta 版本——已接通语音收发;尚未达到生产环境质量。",
"fieldServerHost": "服务器地址",
"fieldNickname": "昵称",
"fieldServerPassword": "服务器密码",
"fieldServerPasswordHelp": "如果服务器无需密码,留空即可。",
"fieldPassword": "密码",
"fieldDisplayName": "显示名称",
"connectAction": "连接",
"disconnectAction": "离开服务器",
"disconnectConfirmTitle": "离开服务器?",
"disconnectConfirmBody": "你将从当前服务器断开连接。",
"disconnectConfirmCancel": "留下",
"disconnectConfirmAction": "离开",
"refreshAction": "刷新",
"diagnosticsAction": "诊断信息",
"diagnosticsSaveAction": "保存导出",
"diagnosticsLiveUpdating": "实时更新中",
"shareAction": "分享",
"diagnosticsSaved": "诊断导出已保存到 {path}",
"aboutAction": "关于",
"aboutVersion": "版本 {version}",
"aboutAuthor": "作者: Edison Jwa",
"aboutNonAffiliation": "Chanora 是独立项目,与 TeamSpeak 之间不存在任何附属、认可、赞助或官方关联关系。",
"aboutLicenseHeading": "许可协议",
"aboutLicenseBody": "Chanora 采用 Apache License 2.0 或 MIT License 双协议授权,使用者可任选其一。完整协议文本以 LICENSE-APACHE 与 LICENSE-MIT 形式随仓库一同分发。",
"aboutThirdPartyHeading": "第三方组件",
"aboutThirdPartyBody": "Chanora 基于 tsclientlib、flutter_rust_bridge、平台原生音频后端、rusqlite、Flutter 框架等开源组件构建。完整归属信息请参阅仓库根目录的 NOTICE 文件。",
"copyAction": "复制",
"closeAction": "关闭",
"openAction": "打开",
"cancelAction": "取消",
"retryAction": "重试",
"chatAction": "聊天",
"chatCloseAction": "关闭聊天",
"chatNewPrivateAction": "新建私聊",
"chatSearchClientsHint": "搜索用户...",
"chatDirectMessageAction": "私聊",
"chatPokeAction": "戳一下",
"clientInfoAction": "信息",
"startAudioAction": "启动语音",
"pttHoldToTalk": "按住说话",
"pttTransmitting": "正在发送…",
"pttHoldToTalkSemanticsHint": "按住进行语音发送,松开停止。",
"pttCapabilityBadge": "对讲能力:{level}{backend}",
"pttCapabilityBoundKey": "按键:{key}",
"pttCapabilityExplainTitle": "对讲能力说明",
"pttCapabilityExplainFocusedHeading": "聚焦对讲",
"pttCapabilityExplainFocusedBody": "Chanora 当前使用聚焦对讲:按键只在 Chanora 窗口处于聚焦时生效。这是所有平台在无法启用全局采集时的通用回退方案。",
"pttCapabilityExplainGoGlobalWindows": "在 Windows 上,绑定按键后会自动启用全局对讲,无需额外权限。",
"pttCapabilityExplainGoGlobalMacos": "在 macOS 上,启用全局对讲需要「输入监视」权限。请打开「系统设置 → 隐私与安全 → 输入监视」,授权 Chanora 后重新绑定按键。",
"pttCapabilityExplainGoGlobalLinux": "在 Linux 上,启用全局对讲需要带 GlobalShortcuts 门户的 GNOME-Wayland 桌面。请重新绑定按键,并在桌面弹出快捷键对话框时接受。",
"pttCapabilityExplainGoGlobalIos": "在 iOS 上,Apple 不向应用开放全局热键。Chanora 使用屏幕上的按住说话按钮,并且仅在应用位于前台时响应。",
"pttCapabilityExplainGoGlobalGeneric": "当前环境暂不支持全局对讲。聚焦对讲在 Chanora 窗口获得焦点时仍可正常使用。",
"pttConfigureAction": "配置",
"pttConfigureTitle": "配置对讲按键",
"pttConfigurePrompt": "按下您希望用于对讲的按键或鼠标侧键。",
"pttConfigureWaiting": "(等待输入…)",
"pttConfigureCaptured": "已捕获",
"pttConfigurePrivacyNote": "Chanora 不会记录具体的按键值。本对话框只会向应用提交输入类别(键盘 / 鼠标侧键)以及一个跨平台标签。",
"pttConfigureSaveAction": "保存",
"pttConfigurePortalRedirect": "您的桌面环境将打开自带的快捷键对话框,请在其中选择用于对讲的按键。",
"inputMuteAction": "静音麦克风",
"inputUnmuteAction": "取消麦克风静音",
"outputMuteAction": "静音扬声器",
"outputUnmuteAction": "取消扬声器静音",
"joinChannelAction": "加入频道",
"leaveChannelAction": "离开频道",
"channelPasswordTitle": "频道密码",
"bookmarksHeading": "书签",
"bookmarksEmpty": "尚无书签。先在上方填写服务器,然后点击“保存书签”。",
"bookmarkAddAction": "保存书签",
"bookmarkAddTitle": "保存书签",
"bookmarkDeleteAction": "删除书签",
"statusIdle": "未连接",
"statusConnecting": "正在连接…",
"statusConnected": "已连接到 {server}",
"statusError": "错误:{message}",
"statusReconnecting": "正在重新连接……第 {attempt} 次尝试,{delay} 秒后",
"statusConnectionLost": "连接已断开:{reason}",
"audioStatsLine": "发送 {sent} 帧 • 接收 {received} 帧 • 麦克风 {ptt}",
"channelsHeading": "频道",
"clientsHeading": "在线用户",
"serverWelcomeHeading": "服务器欢迎信息",
"countChannelsAndClients": "{channels} 个频道 • {clients} 在线",
"voiceModePtt": "按键说话",
"voiceModeContinuous": "持续发送",
"voiceModeVoiceActivity": "语音激活",
"voiceModeComingSoon": "即将推出",
"voiceModeLabel": "发送模式",
"voiceReleaseTailLabel": "释放延迟",
"voiceReleaseTailHint": "毫秒",
"voiceLeaveAction": "离开语音",
"voiceHardMuteLabel": "麦克风静音",
"voiceOutputMuteLabel": "扬声器静音",
"voiceSettingsTitle": "语音设置",
"voiceBindKeyAction": "绑定 PTT 按键",
"voiceBoundKeyLabel": "已绑定按键",
"voicePttHoldHint": "按住按钮",
"voiceMicOn": "开启",
"voiceMicOff": "关闭",
"voiceSheetTitle": "语音",
"audioOutputLabel": "音频输出",
"audioRouteSpeaker": "扬声器",
"audioRouteReceiver": "听筒",
"audioRouteBluetooth": "蓝牙",
"audioRouteWiredHeadset": "有线耳机",
"audioRouteCarAudio": "车载音频",
"audioRouteAirplay": "AirPlay",
"audioRouteUnknown": "未知",
"channelJoinAlreadyIn": "您已在此频道中。",
"channelJoinFailedPermission": "权限不足,无法加入此频道。",
"channelJoinFailedPassword": "频道密码错误。",
"channelJoinFailedFull": "频道已满。",
"channelJoinFailedFamilyFull": "频道家族人数已达上限。",
"channelJoinFailedPrivate": "此频道为私有频道。",
"channelJoinFailedTimeout": "无法加入频道:服务器响应超时。",
"channelJoinFailedFlooding": "切换频道过于频繁,请稍后再试。",
"channelJoinFailedGeneric": "无法加入频道:{message}",
"@channelJoinFailedGeneric": {
"placeholders": {
"message": { "type": "String" }
}
},
"networkPermissionTitle": "需要网络权限",
"networkPermissionBody": "Chanora 需要网络访问权限。请前往系统设置 → 隐私与安全性 → 本地网络,启用 Chanora,然后重试。",
"networkPermissionOpenSettings": "打开系统设置",
"microphonePermissionTitle": "需要麦克风权限",
"microphonePermissionBody": "Chanora 需要麦克风访问权限。请前往系统设置 → 隐私与安全性 → 麦克风,启用 Chanora。",
"microphonePermissionRequiredForVoice": "语音发送需要麦克风权限。",
"permissionGrantAction": "授权",
"startupPermissionsTitle": "权限",
"startupPermissionsBody": "Chanora 会在启动时请求麦克风、蓝牙耳机和通知权限,以确保语音、耳机路由和前台会话正常工作。",
"startupPermissionsNotNow": "暂不",
"startupPermissionsAllow": "允许",
"audioRouteSystemDefault": "系统默认",
"audioRouteEarpiece": "听筒",
"audioRouteUsbHeadset": "USB 耳机",
"audioRouteOtherDevice": "其他设备",
"audioRouteRefreshDevices": "刷新音频设备",
"audioRouteCannotSelect": "无法选择此输出设备。",
"audioRouteChangeFailed": "无法切换音频输出。",
"iosAudioInterrupted": "系统已中断音频(电话通话)",
"iosAudioResuming": "音频正在恢复",
"linkTrustTitle": "打开外部链接?",
"linkTrustBody": "你将打开指向以下域名的链接:\n\n{domain}",
"linkTrustRememberDomain": "信任来自此域名的所有链接",
"clientInfoFetchingProfile": "正在获取 TeamSpeak 资料",
"clientInfoLoadingProfile": "正在加载资料…",
"clientInfoProfileUnavailable": "资料不可用",
"clientInfoProfileUnavailableBody": "服务器没有返回此用户的资料详情。",
"clientInfoIdentitySection": "身份",
"clientInfoMembershipSection": "成员关系",
"clientInfoConnectionSection": "连接",
"clientInfoHistorySection": "历史",
"clientInfoTransferSection": "传输",
"clientInfoClientId": "用户 ID",
"clientInfoDatabaseId": "数据库 ID",
"clientInfoUniqueId": "唯一 ID",
"clientInfoDescription": "描述",
"clientInfoAvatar": "头像",
"clientInfoServerGroups": "服务器组",
"clientInfoChannelGroup": "频道组",
"clientInfoChannelId": "频道 ID",
"clientInfoOnline": "在线时长",
"clientInfoIdle": "空闲",
"clientInfoPing": "延迟",
"clientInfoPingDeviation": "延迟偏差",
"clientInfoAddress": "地址",
"clientInfoPacketLossClientToServer": "丢包 C->S",
"clientInfoPacketLossServerToClient": "丢包 S->C",
"clientInfoFirstConnected": "首次连接",
"clientInfoLastConnected": "上次连接",
"clientInfoConnections": "连接次数",
"clientInfoDownloadedMonth": "本月下载",
"clientInfoUploadedMonth": "本月上传",
"clientInfoDownloadedTotal": "总下载",
"clientInfoUploadedTotal": "总上传",
"clientInfoUnknown": "未知",
"clientInfoHidden": "隐藏",
"clientInfoNone": "无",
"pokeSnackBarClearAction": "清除",
"pokeSnackBarMoreIndicator": "...",
"pokeSnackBarIncomingNoMessage": "{sender} 戳了你一下",
"@pokeSnackBarIncomingNoMessage": {
"placeholders": {
"sender": { "type": "String" }
}
},
"pokeSnackBarIncomingWithMessage": "{sender} 戳了你一下:{message}",
"@pokeSnackBarIncomingWithMessage": {
"placeholders": {
"sender": { "type": "String" },
"message": { "type": "String" }
}
},
"pokeHistorySelfNoMessage": "<{time}> 你戳了“{target}”一下。",
"@pokeHistorySelfNoMessage": {
"placeholders": {
"time": { "type": "String" },
"target": { "type": "String" }
}
},
"pokeHistorySelfWithMessage": "<{time}> 你戳了“{target}”一下,消息:{message}",
"@pokeHistorySelfWithMessage": {
"placeholders": {
"time": { "type": "String" },
"target": { "type": "String" },
"message": { "type": "String" }
}
},
"pokeHistoryIncomingNoMessage": "<{time}> “{sender}”戳了你一下",
"@pokeHistoryIncomingNoMessage": {
"placeholders": {
"time": { "type": "String" },
"sender": { "type": "String" }
}
},
"pokeHistoryIncomingWithMessage": "<{time}> “{sender}”戳了你一下:{message}",
"@pokeHistoryIncomingWithMessage": {
"placeholders": {
"time": { "type": "String" },
"sender": { "type": "String" },
"message": { "type": "String" }
}
},
"clientVolumeAction": "用户音量",
"clientVolumeTitle": "{name} — 音量",
"clientVolumeLabel": "{percent}%",
"clientVolumeMuteAction": "静音该用户",
"clientVolumeUnmuteAction": "取消静音",
"clientVolumeResetAction": "恢复默认",
"permissionDenied": "权限被拒绝",
"voiceTalkPowerBlocked": "发言权限不足,无法在此频道发言"
}