From 69e2e5dc596555b1bb61897d58b2a252cc5cae46 Mon Sep 17 00:00:00 2001 From: 2570165831 <200382837+2570165831@users.noreply.github.com> Date: Sat, 24 Oct 2026 03:38:07 +1100 Subject: [PATCH 1/3] feat(i18n): translate source and speaker playback status Translate Cast and Connect speaker failures, native playback recovery and alternative-source status messages into both Chinese scripts. Raw provider details, product names, protocol fields, URIs and config keys keep their original wording; the network, history and DJ strings in the same table follow in the next change. --- gui/src/bindings/strings.json | 568 +++++++++++++++++----------------- src/core/i18n/status_infra.rs | 292 ++++++++++++++++- 2 files changed, 574 insertions(+), 286 deletions(-) diff --git a/gui/src/bindings/strings.json b/gui/src/bindings/strings.json index 41dde5db..fffb7806 100644 --- a/gui/src/bindings/strings.json +++ b/gui/src/bindings/strings.json @@ -3653,17 +3653,17 @@ "youtube_playlist": "YouTube 播放列表" }, "infra": { - "audio_disconnected": "Audio output device disconnected.", - "audio_init_failed": "Audio output init failed: {error}", - "cast_access_token_missing": "no accessToken in the device-auth reply", - "cast_client_id_missing": "no clientID in getInfoResponse", - "cast_login_refused": "the speaker refused the login: {reply}", - "cast_malformed": "a malformed Cast message", - "cast_message_size": "a {size}-byte Cast message", - "cast_no_answer": "no answer from {address}", - "cast_no_reply": "no reply to {kind}", - "cast_no_tls": "no TLS handshake with {address}", - "cast_receiver_failed": "the Spotify receiver did not start: {reason}", + "audio_disconnected": "音频输出设备已断开。", + "audio_init_failed": "音频输出初始化失败:{error}", + "cast_access_token_missing": "device-auth 回复中缺少 accessToken", + "cast_client_id_missing": "getInfoResponse 中缺少 clientID", + "cast_login_refused": "音箱拒绝登录:{reply}", + "cast_malformed": "Cast 消息格式错误", + "cast_message_size": "Cast 消息大小为 {size} 字节", + "cast_no_answer": "{address} 没有响应", + "cast_no_reply": "{kind} 未收到回复", + "cast_no_tls": "与 {address} 的 TLS 握手未完成", + "cast_receiver_failed": "Spotify 接收器未启动:{reason}", "dj_duplicate": "{count} already queued", "dj_error": "DJ error ({backend}): {error}", "dj_index_capped": " (stopped at {count}; the rest is not filtered)", @@ -3676,7 +3676,7 @@ "dj_status": "DJ: {summary}", "dj_steps_exhausted": "Stopped after {count} steps. Ask again if that is not where you wanted to end up.", "dj_topped_up": "DJ: topped up the queue", - "fetching": "Fetching {name}…", + "fetching": "正在获取 {name}…", "friend_add_failed": "Could not add friend: {error}", "friend_added": "Added friend", "friend_added_code": "Added friend (code: {code})", @@ -3695,61 +3695,61 @@ "history_track_hits": "{count} track hits · {duration}", "history_week": "Last 7 Days", "liked_check_failed": "Could not check liked track state: {error}", - "local_audio_unavailable": "No audio output for local playback: {error}", - "local_exhausted": "{message} — no playable tracks left, stopping", - "local_no_folder": "No local music folder configured (set behavior.local_music_path)", - "local_no_tracks": "No local tracks to play", - "local_play_failed": "Cannot play local file: {error}", - "local_read_failed": "Cannot read folder: {error}", - "local_replay_failed": "Cannot replay local file", - "local_root_folder": "(root)", - "local_scan_failed": "Cannot scan music folder: {error}", - "local_scan_task_failed": "Local folder scan failed: {error}", - "local_task_failed": "Local playback task failed: {error}", - "local_tracks_scan_failed": "Local track scan failed: {error}", - "local_unknown_track": "Unknown Track", - "local_uri_invalid": "Invalid local file URI: {error}", + "local_audio_unavailable": "本地播放无音频输出:{error}", + "local_exhausted": "{message} — 已无可播放曲目,停止播放", + "local_no_folder": "尚未设置本地音乐文件夹(请设置 behavior.local_music_path)", + "local_no_tracks": "没有可播放的本地曲目", + "local_play_failed": "无法播放本地文件:{error}", + "local_read_failed": "无法读取文件夹:{error}", + "local_replay_failed": "无法重新播放本地文件", + "local_root_folder": "(根目录)", + "local_scan_failed": "无法扫描音乐文件夹:{error}", + "local_scan_task_failed": "本地文件夹扫描失败:{error}", + "local_task_failed": "本地播放任务失败:{error}", + "local_tracks_scan_failed": "本地曲目扫描失败:{error}", + "local_unknown_track": "未知曲目", + "local_uri_invalid": "本地文件 URI 无效:{error}", "made_for_you_missing": "Spotify did not send the Made For You page", "made_for_you_unavailable": "Made For You is unavailable: every known browsePage hash is retired", "mcp_status": "MCP: {message}", "monthly_recap_ready": "Monthly listening recap ready at {path} ({count} listens)", - "music_busy": "Music is busy; retry the request", - "music_busy_playback": "Music is busy; retry the playback request", - "music_loaded": "Apple Music: loaded {count} items", - "music_loading": "Loading Apple Music library: {end}/{total}", - "music_not_running": "Music is not running; choose a Music track to start it", - "music_queue_unsupported": "Apple Music tracks can't be queued", - "music_requires_macos": "Apple Music requires macOS and the apple-music feature", - "music_shuffle_repeat": "Use Music to change shuffle or repeat", - "music_status_unavailable": "Music status unavailable: {error}. Retrying in the background.", - "music_switch_spotify": "Start a Spotify track to switch playback from Music", - "music_wait_pause": "Waiting for Music to pause before switching source", - "native_connection_restored": "Native connection restored; restoring playback.", - "native_disconnected": "Native streaming disconnected; attempting recovery.", - "native_lost_reconnecting": "Native streaming connection lost; reconnecting.", - "native_lost_recovering": "Native streaming connection lost; attempting recovery.", - "native_many_unavailable": "Several tracks in a row couldn't be played natively, so playback was stopped. They may be unavailable on your account or region. Press 'd' to switch to an official Spotify Connect device.", - "native_moved": "Playback moved to another Spotify device.", - "native_no_state": "Native connection recovered, but there was no playback state to restore.", - "native_nothing_to_resume": "No playback to resume on {device}.", - "native_reconnect_resume": "Reconnecting native streaming; playback will resume.", - "native_reconnected": "Native streaming connection recovered.", - "native_reconnecting": "Reconnecting native streaming…", - "native_recovered": "Native streaming recovered.", - "native_recovery_dropped": "Native recovery failed; playback request dropped: {error}", - "native_recovery_failed": "Native recovery failed: {error}", - "native_restore_retry": "Native playback restore failed; reconnecting again.", - "native_restored": "Native playback restored.", - "native_restored_paused": "Native playback restored in paused state.", - "native_resume_failed": "Failed to resume playback: {error}", - "native_resuming": "Resuming the last playback here…", - "native_settling": "Native streaming is still settling…", - "native_shuffle_failed": "Failed to apply shuffle: {error}", - "native_skip_unavailable": "Couldn't play this track natively (unavailable or blocked); skipping.", - "native_stalled": "Native streaming stalled; attempting recovery.", - "native_start_failed": "Failed to start native playback: {error}", - "native_stopped": "Native streaming stopped.", - "native_stopped_recovering": "Native streaming stopped; attempting recovery.", + "music_busy": "Music 正忙,请重试请求", + "music_busy_playback": "Music 正忙,请重试播放请求", + "music_loaded": "Apple Music:已加载 {count} 项", + "music_loading": "正在加载 Apple Music 音乐库:{end}/{total}", + "music_not_running": "Music 未运行,请选择 Music 曲目启动", + "music_queue_unsupported": "Apple Music 曲目无法加入队列", + "music_requires_macos": "Apple Music 需要 macOS 和 apple-music 功能", + "music_shuffle_repeat": "请在 Music 中更改随机播放或循环播放", + "music_status_unavailable": "无法获取 Music 状态:{error}。正在后台重试。", + "music_switch_spotify": "播放 Spotify 曲目即可从 Music 切换", + "music_wait_pause": "正在等待 Music 暂停,再切换音源", + "native_connection_restored": "内置播放连接已恢复,正在恢复播放。", + "native_disconnected": "内置播放已断开,正在尝试恢复。", + "native_lost_reconnecting": "内置播放连接已断开,正在重新连接。", + "native_lost_recovering": "内置播放连接已断开,正在尝试恢复。", + "native_many_unavailable": "多首曲目连续无法使用内置播放,已停止播放。你的账号或所在地区可能无法使用这些曲目。请按 'd' 切换到官方 Spotify Connect 设备。", + "native_moved": "播放已转移到另一台 Spotify 设备。", + "native_no_state": "内置播放连接已恢复,但没有可恢复的播放状态。", + "native_nothing_to_resume": "{device} 上没有可恢复的播放。", + "native_reconnect_resume": "正在重新连接内置播放,随后将恢复播放。", + "native_reconnected": "内置播放连接已恢复。", + "native_reconnecting": "正在重新连接内置播放…", + "native_recovered": "内置播放已恢复。", + "native_recovery_dropped": "内置播放恢复失败,播放请求已放弃:{error}", + "native_recovery_failed": "内置播放恢复失败:{error}", + "native_restore_retry": "内置播放恢复失败,正在再次连接。", + "native_restored": "内置播放已恢复。", + "native_restored_paused": "内置播放已恢复,保持暂停。", + "native_resume_failed": "恢复播放失败:{error}", + "native_resuming": "正在此设备恢复上次播放…", + "native_settling": "内置播放仍在准备中…", + "native_shuffle_failed": "启用随机播放失败:{error}", + "native_skip_unavailable": "此曲目无法使用内置播放(不可用或被限制),正在跳过。", + "native_stalled": "内置播放已停滞,正在尝试恢复。", + "native_start_failed": "启动内置播放失败:{error}", + "native_stopped": "内置播放已停止。", + "native_stopped_recovering": "内置播放已停止,正在尝试恢复。", "network_error": "{error}", "network_rate_error": "{error}", "other_source_playing": "Another source owns playback", @@ -3771,46 +3771,46 @@ "playlist_removed": "Removed from playlist", "playlist_search_matches": "{count} playlist tracks match \"{query}\"{scope}", "playlist_search_scope": " in the first {count} tracks", - "qobuz_audio_unavailable": "No audio output for Qobuz playback: {error}", - "qobuz_browser_login": "Qobuz: open the browser window to log in", - "qobuz_logged_in": "Qobuz: logged in", - "qobuz_login_busy": "Qobuz login already in progress...", - "qobuz_login_expired": "Qobuz: login expired, press `d` and pick Qobuz to log in again", - "qobuz_login_failed": "Qobuz login failed: {error}", - "qobuz_login_url": "Open this URL in your browser to log in to Qobuz: {url}", - "qobuz_no_tracks": "No Qobuz tracks to play", - "qobuz_operation_failed": "Qobuz: {step}: {error}", - "qobuz_replay_failed": "Cannot replay Qobuz track", - "qobuz_step_constants": "web player constants", - "qobuz_step_library": "library", - "qobuz_step_login": "login", - "qobuz_step_search": "search", - "qobuz_step_stream": "stream", - "qobuz_step_tracks": "tracks", - "qobuz_uri_invalid": "Invalid Qobuz track URI", - "queue_added": "Added to queue", - "queue_audio_unavailable": "No audio output for queue playback: {error}", - "queue_finished": "Queue finished", - "queue_load_failed": "Could not load queue (no active device?)", - "queue_local_source": "Local", - "queue_no_uri": "Skipped a queued track with no URI", - "queue_play_failed": "Cannot play {name}: {error}", - "queue_playing": "♪ {name} (queue)", - "queue_repeat_unsupported": "Repeat does not apply to this source", - "queue_restore_failed": "Cannot restore {name}: {error}", - "queue_shuffle_unsupported": "Shuffle does not apply to this source", - "queue_skip_disconnected": "Native streaming isn't connected; skipped \"{name}\"", - "radio_audio_unavailable": "No audio output for radio playback: {error}", - "radio_empty": "No radio stations configured or saved; search to add some", - "radio_no_skip": "Live radio: no track skipping", - "radio_not_resumed": "Playback is paused - radio not resumed.", - "radio_open_failed": "Cannot open radio stream: {error}", - "radio_play_failed": "Cannot play radio stream: {error}", - "radio_playing": "♪ {name} (live)", - "radio_probe_timeout": "Cannot play radio stream: no recognisable audio format in time.", - "radio_search_failed": "Radio search failed: {error}", - "radio_task_failed": "Radio playback task failed: {error}", - "radio_uri_invalid": "Invalid radio URI", + "qobuz_audio_unavailable": "Qobuz 播放无音频输出:{error}", + "qobuz_browser_login": "Qobuz:请打开浏览器窗口登录", + "qobuz_logged_in": "Qobuz:已登录", + "qobuz_login_busy": "Qobuz 正在登录…", + "qobuz_login_expired": "Qobuz:登录已过期,请按 `d` 并选择 Qobuz 重新登录", + "qobuz_login_failed": "Qobuz 登录失败:{error}", + "qobuz_login_url": "请在浏览器中打开此 URL 登录 Qobuz:{url}", + "qobuz_no_tracks": "没有可播放的 Qobuz 曲目", + "qobuz_operation_failed": "Qobuz:{step}失败:{error}", + "qobuz_replay_failed": "无法重新播放 Qobuz 曲目", + "qobuz_step_constants": "网页播放器参数", + "qobuz_step_library": "音乐库", + "qobuz_step_login": "登录", + "qobuz_step_search": "搜索", + "qobuz_step_stream": "音频流", + "qobuz_step_tracks": "曲目", + "qobuz_uri_invalid": "Qobuz 曲目 URI 无效", + "queue_added": "已加入队列", + "queue_audio_unavailable": "队列播放无音频输出:{error}", + "queue_finished": "队列播放完毕", + "queue_load_failed": "无法加载队列(没有活动设备?)", + "queue_local_source": "本地", + "queue_no_uri": "已跳过队列中缺少 URI 的曲目", + "queue_play_failed": "无法播放 {name}:{error}", + "queue_playing": "♪ {name}(队列)", + "queue_repeat_unsupported": "此音源不支持循环播放", + "queue_restore_failed": "无法恢复 {name}:{error}", + "queue_shuffle_unsupported": "此音源不支持随机播放", + "queue_skip_disconnected": "内置播放未连接,已跳过“{name}”", + "radio_audio_unavailable": "电台播放无音频输出:{error}", + "radio_empty": "尚未设置或保存电台;请搜索并添加", + "radio_no_skip": "电台直播无法切换曲目", + "radio_not_resumed": "播放已暂停,未恢复电台播放。", + "radio_open_failed": "无法打开电台流:{error}", + "radio_play_failed": "无法播放电台流:{error}", + "radio_playing": "♪ {name}(直播)", + "radio_probe_timeout": "无法播放电台流:未能及时识别音频格式。", + "radio_search_failed": "电台搜索失败:{error}", + "radio_task_failed": "电台播放任务失败:{error}", + "radio_uri_invalid": "电台 URI 无效", "recap_created": "Listening recap generated at {path} ({count} listens)", "recap_failed": "Failed to generate recap: {error}", "search_query_unavailable": "Search is unavailable: every known {operation} hash is retired", @@ -3853,17 +3853,17 @@ "shuffle_capped": "Large context: shuffling the first {count} tracks", "shuffle_context_failed": "Shuffle: failed to load the context's track list", "shuffle_not_applied": "Playback started; couldn't apply shuffle.", - "source_playback_unavailable": "{source} playback isn't available in this build", - "speaker_client_id_missing": "no clientID in getInfo", - "speaker_login_refused": "the speaker refused the login: {message} ({error})", - "speaker_not_joined": "{name} did not join after its login", - "speaker_token_failed": "the token exchange failed ({status}): {error}", - "speaker_token_unsupported": "the speaker asks for token type {kind}, which is not supported yet", + "source_playback_unavailable": "此版本不支持 {source} 播放", + "speaker_client_id_missing": "getInfo 中缺少 clientID", + "speaker_login_refused": "音箱拒绝登录:{message}({error})", + "speaker_not_joined": "{name} 登录后未连接", + "speaker_token_failed": "令牌交换失败({status}):{error}", + "speaker_token_unsupported": "音箱请求的令牌类型 {kind} 尚不支持", "spotify_api_error": "Spotify API {status} failed: {body}", "spotify_connected": "Spotify connected. Restart spotatui to enable native playback.", "spotify_login_failed": "Spotify login failed: {error}", "spotify_login_timeout": "Spotify login timed out. Press `d` and pick Spotify to try again.", - "spotify_not_compiled": "This build of spotatui has no Spotify support", + "spotify_not_compiled": "此版本不支持 Spotify", "spotify_not_connected": "Spotify not connected. Press `d` and pick Spotify to log in.", "spotify_pair": "Log in to Spotify: open {url} and confirm the code {code}", "spotify_playing_elsewhere": "Playback remains on another Spotify device.", @@ -3873,17 +3873,17 @@ "spotify_rate_retry": "Spotify rate limit hit. Retrying automatically; please wait a few seconds.", "spotify_rate_wait": "Spotify rate limit: waiting {seconds}s", "spotify_server_retry": "Spotify server temporarily unavailable (5xx); retrying automatically.", - "subsonic_audio_unavailable": "No audio output for Subsonic playback: {error}", - "subsonic_download_failed": "Cannot download Subsonic track: {error}", - "subsonic_no_server": "No Subsonic server configured (set behavior.subsonic_url)", - "subsonic_no_tracks": "No Subsonic tracks to play", - "subsonic_play_failed": "Cannot play Subsonic track: {error}", - "subsonic_playlist_failed": "Cannot load Subsonic playlist: {error}", - "subsonic_playlists_failed": "Cannot load Subsonic playlists: {error}", - "subsonic_replay_failed": "Cannot replay Subsonic track", - "subsonic_search_failed": "Subsonic search failed: {error}", - "subsonic_task_failed": "Subsonic playback task failed: {error}", - "subsonic_uri_invalid": "Invalid Subsonic track URI", + "subsonic_audio_unavailable": "Subsonic 播放无音频输出:{error}", + "subsonic_download_failed": "无法下载 Subsonic 曲目:{error}", + "subsonic_no_server": "尚未设置 Subsonic 服务器(请设置 behavior.subsonic_url)", + "subsonic_no_tracks": "没有可播放的 Subsonic 曲目", + "subsonic_play_failed": "无法播放 Subsonic 曲目:{error}", + "subsonic_playlist_failed": "无法加载 Subsonic 播放列表:{error}", + "subsonic_playlists_failed": "无法加载 Subsonic 播放列表:{error}", + "subsonic_replay_failed": "无法重新播放 Subsonic 曲目", + "subsonic_search_failed": "Subsonic 搜索失败:{error}", + "subsonic_task_failed": "Subsonic 播放任务失败:{error}", + "subsonic_uri_invalid": "Subsonic 曲目 URI 无效", "sync_busy": "Playlist sync already running", "sync_dry_run": " (dry run)", "sync_failed": "Playlist sync failed: {error}", @@ -3901,27 +3901,27 @@ "sync_summary": "Playlist sync{dry}: {added} added, {removed} removed, {unmatched} unmatched", "sync_using_playlist": "Using the existing {source} playlist \"{name}\"", "unnamed_folder": "Unnamed Folder", - "youtube_add_failed": "Cannot add video to playlist: {error}", - "youtube_added": "Added \"{name}\" to {playlist}", - "youtube_already_added": "\"{name}\" is already in {playlist}", - "youtube_audio_unavailable": "No audio output for YouTube playback: {error}", - "youtube_create_failed": "Cannot create YouTube playlist: {error}", - "youtube_created": "Created YouTube playlist \"{name}\"", - "youtube_decode_failed": "Cannot play YouTube audio: {error} (installing ffmpeg fixes most container issues)", - "youtube_delete_failed": "Cannot delete YouTube playlist: {error}", - "youtube_deleted": "Deleted YouTube playlist \"{name}\"", - "youtube_download_failed": "Cannot download YouTube audio: {error}", - "youtube_metadata_missing": "Cannot resolve that video's metadata", - "youtube_no_live": "Live streams aren't supported on the YouTube source yet", - "youtube_no_tracks": "No YouTube videos to play", - "youtube_playlist_missing": "That YouTube playlist no longer exists", - "youtube_playlists_failed": "Cannot load YouTube playlists: {error}", - "youtube_remove_failed": "Cannot remove video from playlist: {error}", - "youtube_removed": "Removed \"{name}\" from playlist", - "youtube_replay_failed": "Cannot replay YouTube track", - "youtube_search_failed": "YouTube search failed: {error}", - "youtube_task_failed": "YouTube playback task failed: {error}", - "youtube_uri_invalid": "Invalid YouTube URI" + "youtube_add_failed": "无法将视频添加到播放列表:{error}", + "youtube_added": "已将“{name}”添加到 {playlist}", + "youtube_already_added": "“{name}”已在 {playlist} 中", + "youtube_audio_unavailable": "YouTube 播放无音频输出:{error}", + "youtube_create_failed": "无法创建 YouTube 播放列表:{error}", + "youtube_created": "已创建 YouTube 播放列表“{name}”", + "youtube_decode_failed": "无法播放 YouTube 音频:{error}(安装 ffmpeg 可解决大多数容器格式问题)", + "youtube_delete_failed": "无法删除 YouTube 播放列表:{error}", + "youtube_deleted": "已删除 YouTube 播放列表“{name}”", + "youtube_download_failed": "无法下载 YouTube 音频:{error}", + "youtube_metadata_missing": "无法获取该视频的元数据", + "youtube_no_live": "YouTube 音源暂不支持直播", + "youtube_no_tracks": "没有可播放的 YouTube 视频", + "youtube_playlist_missing": "该 YouTube 播放列表已不存在", + "youtube_playlists_failed": "无法加载 YouTube 播放列表:{error}", + "youtube_remove_failed": "无法从播放列表移除视频:{error}", + "youtube_removed": "已从播放列表移除“{name}”", + "youtube_replay_failed": "无法重新播放 YouTube 曲目", + "youtube_search_failed": "YouTube 搜索失败:{error}", + "youtube_task_failed": "YouTube 播放任务失败:{error}", + "youtube_uri_invalid": "YouTube URI 无效" }, "runtime": { "apple_music_needs_macos": "Apple Music 播放需要 macOS", @@ -5658,17 +5658,17 @@ "youtube_playlist": "YouTube 播放清單" }, "infra": { - "audio_disconnected": "Audio output device disconnected.", - "audio_init_failed": "Audio output init failed: {error}", - "cast_access_token_missing": "no accessToken in the device-auth reply", - "cast_client_id_missing": "no clientID in getInfoResponse", - "cast_login_refused": "the speaker refused the login: {reply}", - "cast_malformed": "a malformed Cast message", - "cast_message_size": "a {size}-byte Cast message", - "cast_no_answer": "no answer from {address}", - "cast_no_reply": "no reply to {kind}", - "cast_no_tls": "no TLS handshake with {address}", - "cast_receiver_failed": "the Spotify receiver did not start: {reason}", + "audio_disconnected": "音訊輸出裝置已中斷連線。", + "audio_init_failed": "音訊輸出初始化失敗:{error}", + "cast_access_token_missing": "device-auth 回覆中缺少 accessToken", + "cast_client_id_missing": "getInfoResponse 中缺少 clientID", + "cast_login_refused": "喇叭拒絕登入:{reply}", + "cast_malformed": "Cast 訊息格式錯誤", + "cast_message_size": "Cast 訊息大小為 {size} 位元組", + "cast_no_answer": "{address} 沒有回應", + "cast_no_reply": "{kind} 未收到回覆", + "cast_no_tls": "與 {address} 的 TLS 交握未完成", + "cast_receiver_failed": "Spotify 接收器未啟動:{reason}", "dj_duplicate": "{count} already queued", "dj_error": "DJ error ({backend}): {error}", "dj_index_capped": " (stopped at {count}; the rest is not filtered)", @@ -5681,7 +5681,7 @@ "dj_status": "DJ: {summary}", "dj_steps_exhausted": "Stopped after {count} steps. Ask again if that is not where you wanted to end up.", "dj_topped_up": "DJ: topped up the queue", - "fetching": "Fetching {name}…", + "fetching": "正在取得 {name}…", "friend_add_failed": "Could not add friend: {error}", "friend_added": "Added friend", "friend_added_code": "Added friend (code: {code})", @@ -5700,61 +5700,61 @@ "history_track_hits": "{count} track hits · {duration}", "history_week": "Last 7 Days", "liked_check_failed": "Could not check liked track state: {error}", - "local_audio_unavailable": "No audio output for local playback: {error}", - "local_exhausted": "{message} — no playable tracks left, stopping", - "local_no_folder": "No local music folder configured (set behavior.local_music_path)", - "local_no_tracks": "No local tracks to play", - "local_play_failed": "Cannot play local file: {error}", - "local_read_failed": "Cannot read folder: {error}", - "local_replay_failed": "Cannot replay local file", - "local_root_folder": "(root)", - "local_scan_failed": "Cannot scan music folder: {error}", - "local_scan_task_failed": "Local folder scan failed: {error}", - "local_task_failed": "Local playback task failed: {error}", - "local_tracks_scan_failed": "Local track scan failed: {error}", - "local_unknown_track": "Unknown Track", - "local_uri_invalid": "Invalid local file URI: {error}", + "local_audio_unavailable": "本機播放無音訊輸出:{error}", + "local_exhausted": "{message} — 已無可播放曲目,停止播放", + "local_no_folder": "尚未設定本機音樂資料夾(請設定 behavior.local_music_path)", + "local_no_tracks": "沒有可播放的本機曲目", + "local_play_failed": "無法播放本機檔案:{error}", + "local_read_failed": "無法讀取資料夾:{error}", + "local_replay_failed": "無法重新播放本機檔案", + "local_root_folder": "(根目錄)", + "local_scan_failed": "無法掃描音樂資料夾:{error}", + "local_scan_task_failed": "本機資料夾掃描失敗:{error}", + "local_task_failed": "本機播放工作失敗:{error}", + "local_tracks_scan_failed": "本機曲目掃描失敗:{error}", + "local_unknown_track": "未知曲目", + "local_uri_invalid": "本機檔案 URI 無效:{error}", "made_for_you_missing": "Spotify did not send the Made For You page", "made_for_you_unavailable": "Made For You is unavailable: every known browsePage hash is retired", "mcp_status": "MCP: {message}", "monthly_recap_ready": "Monthly listening recap ready at {path} ({count} listens)", - "music_busy": "Music is busy; retry the request", - "music_busy_playback": "Music is busy; retry the playback request", - "music_loaded": "Apple Music: loaded {count} items", - "music_loading": "Loading Apple Music library: {end}/{total}", - "music_not_running": "Music is not running; choose a Music track to start it", - "music_queue_unsupported": "Apple Music tracks can't be queued", - "music_requires_macos": "Apple Music requires macOS and the apple-music feature", - "music_shuffle_repeat": "Use Music to change shuffle or repeat", - "music_status_unavailable": "Music status unavailable: {error}. Retrying in the background.", - "music_switch_spotify": "Start a Spotify track to switch playback from Music", - "music_wait_pause": "Waiting for Music to pause before switching source", - "native_connection_restored": "Native connection restored; restoring playback.", - "native_disconnected": "Native streaming disconnected; attempting recovery.", - "native_lost_reconnecting": "Native streaming connection lost; reconnecting.", - "native_lost_recovering": "Native streaming connection lost; attempting recovery.", - "native_many_unavailable": "Several tracks in a row couldn't be played natively, so playback was stopped. They may be unavailable on your account or region. Press 'd' to switch to an official Spotify Connect device.", - "native_moved": "Playback moved to another Spotify device.", - "native_no_state": "Native connection recovered, but there was no playback state to restore.", - "native_nothing_to_resume": "No playback to resume on {device}.", - "native_reconnect_resume": "Reconnecting native streaming; playback will resume.", - "native_reconnected": "Native streaming connection recovered.", - "native_reconnecting": "Reconnecting native streaming…", - "native_recovered": "Native streaming recovered.", - "native_recovery_dropped": "Native recovery failed; playback request dropped: {error}", - "native_recovery_failed": "Native recovery failed: {error}", - "native_restore_retry": "Native playback restore failed; reconnecting again.", - "native_restored": "Native playback restored.", - "native_restored_paused": "Native playback restored in paused state.", - "native_resume_failed": "Failed to resume playback: {error}", - "native_resuming": "Resuming the last playback here…", - "native_settling": "Native streaming is still settling…", - "native_shuffle_failed": "Failed to apply shuffle: {error}", - "native_skip_unavailable": "Couldn't play this track natively (unavailable or blocked); skipping.", - "native_stalled": "Native streaming stalled; attempting recovery.", - "native_start_failed": "Failed to start native playback: {error}", - "native_stopped": "Native streaming stopped.", - "native_stopped_recovering": "Native streaming stopped; attempting recovery.", + "music_busy": "Music 忙碌中,請重試請求", + "music_busy_playback": "Music 忙碌中,請重試播放請求", + "music_loaded": "Apple Music:已載入 {count} 項", + "music_loading": "正在載入 Apple Music 音樂庫:{end}/{total}", + "music_not_running": "Music 未執行,請選擇 Music 曲目啟動", + "music_queue_unsupported": "Apple Music 曲目無法加入佇列", + "music_requires_macos": "Apple Music 需要 macOS 和 apple-music 功能", + "music_shuffle_repeat": "請在 Music 中更改隨機播放或循環播放", + "music_status_unavailable": "無法取得 Music 狀態:{error}。正在背景重試。", + "music_switch_spotify": "播放 Spotify 曲目即可從 Music 切換", + "music_wait_pause": "正在等待 Music 暫停,再切換來源", + "native_connection_restored": "內建串流連線已恢復,正在恢復播放。", + "native_disconnected": "內建串流已中斷,正在嘗試恢復。", + "native_lost_reconnecting": "內建串流連線已中斷,正在重新連線。", + "native_lost_recovering": "內建串流連線已中斷,正在嘗試恢復。", + "native_many_unavailable": "多首曲目連續無法透過內建串流播放,已停止播放。你的帳號或所在地區可能無法使用這些曲目。請按 'd' 切換到官方 Spotify Connect 裝置。", + "native_moved": "播放已轉移到另一台 Spotify 裝置。", + "native_no_state": "內建串流連線已恢復,但沒有可恢復的播放狀態。", + "native_nothing_to_resume": "{device} 上沒有可恢復的播放。", + "native_reconnect_resume": "正在重新連線內建串流,隨後將恢復播放。", + "native_reconnected": "內建串流連線已恢復。", + "native_reconnecting": "正在重新連線內建串流…", + "native_recovered": "內建串流已恢復。", + "native_recovery_dropped": "內建串流恢復失敗,播放請求已放棄:{error}", + "native_recovery_failed": "內建串流恢復失敗:{error}", + "native_restore_retry": "內建串流恢復失敗,正在再次連線。", + "native_restored": "內建串流已恢復。", + "native_restored_paused": "內建串流已恢復,保持暫停。", + "native_resume_failed": "恢復播放失敗:{error}", + "native_resuming": "正在此裝置恢復上次播放…", + "native_settling": "內建串流仍在準備中…", + "native_shuffle_failed": "啟用隨機播放失敗:{error}", + "native_skip_unavailable": "無法透過內建串流播放此曲目(無法使用或受限制),正在略過。", + "native_stalled": "內建串流已停滯,正在嘗試恢復。", + "native_start_failed": "啟動內建串流失敗:{error}", + "native_stopped": "內建串流已停止。", + "native_stopped_recovering": "內建串流已停止,正在嘗試恢復。", "network_error": "{error}", "network_rate_error": "{error}", "other_source_playing": "Another source owns playback", @@ -5776,46 +5776,46 @@ "playlist_removed": "Removed from playlist", "playlist_search_matches": "{count} playlist tracks match \"{query}\"{scope}", "playlist_search_scope": " in the first {count} tracks", - "qobuz_audio_unavailable": "No audio output for Qobuz playback: {error}", - "qobuz_browser_login": "Qobuz: open the browser window to log in", - "qobuz_logged_in": "Qobuz: logged in", - "qobuz_login_busy": "Qobuz login already in progress...", - "qobuz_login_expired": "Qobuz: login expired, press `d` and pick Qobuz to log in again", - "qobuz_login_failed": "Qobuz login failed: {error}", - "qobuz_login_url": "Open this URL in your browser to log in to Qobuz: {url}", - "qobuz_no_tracks": "No Qobuz tracks to play", - "qobuz_operation_failed": "Qobuz: {step}: {error}", - "qobuz_replay_failed": "Cannot replay Qobuz track", - "qobuz_step_constants": "web player constants", - "qobuz_step_library": "library", - "qobuz_step_login": "login", - "qobuz_step_search": "search", - "qobuz_step_stream": "stream", - "qobuz_step_tracks": "tracks", - "qobuz_uri_invalid": "Invalid Qobuz track URI", - "queue_added": "Added to queue", - "queue_audio_unavailable": "No audio output for queue playback: {error}", - "queue_finished": "Queue finished", - "queue_load_failed": "Could not load queue (no active device?)", - "queue_local_source": "Local", - "queue_no_uri": "Skipped a queued track with no URI", - "queue_play_failed": "Cannot play {name}: {error}", - "queue_playing": "♪ {name} (queue)", - "queue_repeat_unsupported": "Repeat does not apply to this source", - "queue_restore_failed": "Cannot restore {name}: {error}", - "queue_shuffle_unsupported": "Shuffle does not apply to this source", - "queue_skip_disconnected": "Native streaming isn't connected; skipped \"{name}\"", - "radio_audio_unavailable": "No audio output for radio playback: {error}", - "radio_empty": "No radio stations configured or saved; search to add some", - "radio_no_skip": "Live radio: no track skipping", - "radio_not_resumed": "Playback is paused - radio not resumed.", - "radio_open_failed": "Cannot open radio stream: {error}", - "radio_play_failed": "Cannot play radio stream: {error}", - "radio_playing": "♪ {name} (live)", - "radio_probe_timeout": "Cannot play radio stream: no recognisable audio format in time.", - "radio_search_failed": "Radio search failed: {error}", - "radio_task_failed": "Radio playback task failed: {error}", - "radio_uri_invalid": "Invalid radio URI", + "qobuz_audio_unavailable": "Qobuz 播放無音訊輸出:{error}", + "qobuz_browser_login": "Qobuz:請開啟瀏覽器視窗登入", + "qobuz_logged_in": "Qobuz:已登入", + "qobuz_login_busy": "Qobuz 正在登入…", + "qobuz_login_expired": "Qobuz:登入已過期,請按 `d` 並選擇 Qobuz 重新登入", + "qobuz_login_failed": "Qobuz 登入失敗:{error}", + "qobuz_login_url": "請在瀏覽器中開啟此 URL 登入 Qobuz:{url}", + "qobuz_no_tracks": "沒有可播放的 Qobuz 曲目", + "qobuz_operation_failed": "Qobuz:{step}失敗:{error}", + "qobuz_replay_failed": "無法重新播放 Qobuz 曲目", + "qobuz_step_constants": "網頁播放器參數", + "qobuz_step_library": "音樂庫", + "qobuz_step_login": "登入", + "qobuz_step_search": "搜尋", + "qobuz_step_stream": "音訊串流", + "qobuz_step_tracks": "曲目", + "qobuz_uri_invalid": "Qobuz 曲目 URI 無效", + "queue_added": "已加入佇列", + "queue_audio_unavailable": "佇列播放無音訊輸出:{error}", + "queue_finished": "佇列播放完畢", + "queue_load_failed": "無法載入佇列(沒有使用中的裝置?)", + "queue_local_source": "本機", + "queue_no_uri": "已略過佇列中缺少 URI 的曲目", + "queue_play_failed": "無法播放 {name}:{error}", + "queue_playing": "♪ {name}(佇列)", + "queue_repeat_unsupported": "此來源不支援循環播放", + "queue_restore_failed": "無法恢復 {name}:{error}", + "queue_shuffle_unsupported": "此來源不支援隨機播放", + "queue_skip_disconnected": "內建串流未連線,已略過「{name}」", + "radio_audio_unavailable": "電台播放無音訊輸出:{error}", + "radio_empty": "尚未設定或儲存電台;請搜尋並新增", + "radio_no_skip": "電台直播無法切換曲目", + "radio_not_resumed": "播放已暫停,未恢復電台播放。", + "radio_open_failed": "無法開啟電台串流:{error}", + "radio_play_failed": "無法播放電台串流:{error}", + "radio_playing": "♪ {name}(直播)", + "radio_probe_timeout": "無法播放電台串流:未能及時辨識音訊格式。", + "radio_search_failed": "電台搜尋失敗:{error}", + "radio_task_failed": "電台播放工作失敗:{error}", + "radio_uri_invalid": "電台 URI 無效", "recap_created": "Listening recap generated at {path} ({count} listens)", "recap_failed": "Failed to generate recap: {error}", "search_query_unavailable": "Search is unavailable: every known {operation} hash is retired", @@ -5858,17 +5858,17 @@ "shuffle_capped": "Large context: shuffling the first {count} tracks", "shuffle_context_failed": "Shuffle: failed to load the context's track list", "shuffle_not_applied": "Playback started; couldn't apply shuffle.", - "source_playback_unavailable": "{source} playback isn't available in this build", - "speaker_client_id_missing": "no clientID in getInfo", - "speaker_login_refused": "the speaker refused the login: {message} ({error})", - "speaker_not_joined": "{name} did not join after its login", - "speaker_token_failed": "the token exchange failed ({status}): {error}", - "speaker_token_unsupported": "the speaker asks for token type {kind}, which is not supported yet", + "source_playback_unavailable": "此版本不支援 {source} 播放", + "speaker_client_id_missing": "getInfo 中缺少 clientID", + "speaker_login_refused": "喇叭拒絕登入:{message}({error})", + "speaker_not_joined": "{name} 登入後未連線", + "speaker_token_failed": "權杖交換失敗({status}):{error}", + "speaker_token_unsupported": "喇叭要求的權杖類型 {kind} 尚未支援", "spotify_api_error": "Spotify API {status} failed: {body}", "spotify_connected": "Spotify connected. Restart spotatui to enable native playback.", "spotify_login_failed": "Spotify login failed: {error}", "spotify_login_timeout": "Spotify login timed out. Press `d` and pick Spotify to try again.", - "spotify_not_compiled": "This build of spotatui has no Spotify support", + "spotify_not_compiled": "此版本不支援 Spotify", "spotify_not_connected": "Spotify not connected. Press `d` and pick Spotify to log in.", "spotify_pair": "Log in to Spotify: open {url} and confirm the code {code}", "spotify_playing_elsewhere": "Playback remains on another Spotify device.", @@ -5878,17 +5878,17 @@ "spotify_rate_retry": "Spotify rate limit hit. Retrying automatically; please wait a few seconds.", "spotify_rate_wait": "Spotify rate limit: waiting {seconds}s", "spotify_server_retry": "Spotify server temporarily unavailable (5xx); retrying automatically.", - "subsonic_audio_unavailable": "No audio output for Subsonic playback: {error}", - "subsonic_download_failed": "Cannot download Subsonic track: {error}", - "subsonic_no_server": "No Subsonic server configured (set behavior.subsonic_url)", - "subsonic_no_tracks": "No Subsonic tracks to play", - "subsonic_play_failed": "Cannot play Subsonic track: {error}", - "subsonic_playlist_failed": "Cannot load Subsonic playlist: {error}", - "subsonic_playlists_failed": "Cannot load Subsonic playlists: {error}", - "subsonic_replay_failed": "Cannot replay Subsonic track", - "subsonic_search_failed": "Subsonic search failed: {error}", - "subsonic_task_failed": "Subsonic playback task failed: {error}", - "subsonic_uri_invalid": "Invalid Subsonic track URI", + "subsonic_audio_unavailable": "Subsonic 播放無音訊輸出:{error}", + "subsonic_download_failed": "無法下載 Subsonic 曲目:{error}", + "subsonic_no_server": "尚未設定 Subsonic 伺服器(請設定 behavior.subsonic_url)", + "subsonic_no_tracks": "沒有可播放的 Subsonic 曲目", + "subsonic_play_failed": "無法播放 Subsonic 曲目:{error}", + "subsonic_playlist_failed": "無法載入 Subsonic 播放清單:{error}", + "subsonic_playlists_failed": "無法載入 Subsonic 播放清單:{error}", + "subsonic_replay_failed": "無法重新播放 Subsonic 曲目", + "subsonic_search_failed": "Subsonic 搜尋失敗:{error}", + "subsonic_task_failed": "Subsonic 播放工作失敗:{error}", + "subsonic_uri_invalid": "Subsonic 曲目 URI 無效", "sync_busy": "Playlist sync already running", "sync_dry_run": " (dry run)", "sync_failed": "Playlist sync failed: {error}", @@ -5906,27 +5906,27 @@ "sync_summary": "Playlist sync{dry}: {added} added, {removed} removed, {unmatched} unmatched", "sync_using_playlist": "Using the existing {source} playlist \"{name}\"", "unnamed_folder": "Unnamed Folder", - "youtube_add_failed": "Cannot add video to playlist: {error}", - "youtube_added": "Added \"{name}\" to {playlist}", - "youtube_already_added": "\"{name}\" is already in {playlist}", - "youtube_audio_unavailable": "No audio output for YouTube playback: {error}", - "youtube_create_failed": "Cannot create YouTube playlist: {error}", - "youtube_created": "Created YouTube playlist \"{name}\"", - "youtube_decode_failed": "Cannot play YouTube audio: {error} (installing ffmpeg fixes most container issues)", - "youtube_delete_failed": "Cannot delete YouTube playlist: {error}", - "youtube_deleted": "Deleted YouTube playlist \"{name}\"", - "youtube_download_failed": "Cannot download YouTube audio: {error}", - "youtube_metadata_missing": "Cannot resolve that video's metadata", - "youtube_no_live": "Live streams aren't supported on the YouTube source yet", - "youtube_no_tracks": "No YouTube videos to play", - "youtube_playlist_missing": "That YouTube playlist no longer exists", - "youtube_playlists_failed": "Cannot load YouTube playlists: {error}", - "youtube_remove_failed": "Cannot remove video from playlist: {error}", - "youtube_removed": "Removed \"{name}\" from playlist", - "youtube_replay_failed": "Cannot replay YouTube track", - "youtube_search_failed": "YouTube search failed: {error}", - "youtube_task_failed": "YouTube playback task failed: {error}", - "youtube_uri_invalid": "Invalid YouTube URI" + "youtube_add_failed": "無法將影片加入播放清單:{error}", + "youtube_added": "已將「{name}」加入 {playlist}", + "youtube_already_added": "「{name}」已在 {playlist} 中", + "youtube_audio_unavailable": "YouTube 播放無音訊輸出:{error}", + "youtube_create_failed": "無法建立 YouTube 播放清單:{error}", + "youtube_created": "已建立 YouTube 播放清單「{name}」", + "youtube_decode_failed": "無法播放 YouTube 音訊:{error}(安裝 ffmpeg 可解決大多數容器格式問題)", + "youtube_delete_failed": "無法刪除 YouTube 播放清單:{error}", + "youtube_deleted": "已刪除 YouTube 播放清單「{name}」", + "youtube_download_failed": "無法下載 YouTube 音訊:{error}", + "youtube_metadata_missing": "無法取得該影片的中繼資料", + "youtube_no_live": "YouTube 來源暫不支援直播", + "youtube_no_tracks": "沒有可播放的 YouTube 影片", + "youtube_playlist_missing": "該 YouTube 播放清單已不存在", + "youtube_playlists_failed": "無法載入 YouTube 播放清單:{error}", + "youtube_remove_failed": "無法從播放清單移除影片:{error}", + "youtube_removed": "已從播放清單移除「{name}」", + "youtube_replay_failed": "無法重新播放 YouTube 曲目", + "youtube_search_failed": "YouTube 搜尋失敗:{error}", + "youtube_task_failed": "YouTube 播放工作失敗:{error}", + "youtube_uri_invalid": "YouTube URI 無效" }, "runtime": { "apple_music_needs_macos": "Apple Music 播放需要 macOS", diff --git a/src/core/i18n/status_infra.rs b/src/core/i18n/status_infra.rs index a38678a4..e928ff0e 100644 --- a/src/core/i18n/status_infra.rs +++ b/src/core/i18n/status_infra.rs @@ -1034,8 +1034,296 @@ pub const EN: InfraStatusStrings = InfraStatusStrings { // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] -pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { ..EN }; +pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { + cast_no_answer: "{address} 没有响应", + cast_no_tls: "与 {address} 的 TLS 握手未完成", + cast_message_size: "Cast 消息大小为 {size} 字节", + cast_malformed: "Cast 消息格式错误", + cast_no_reply: "{kind} 未收到回复", + cast_receiver_failed: "Spotify 接收器未启动:{reason}", + cast_client_id_missing: "getInfoResponse 中缺少 clientID", + cast_login_refused: "音箱拒绝登录:{reply}", + cast_access_token_missing: "device-auth 回复中缺少 accessToken", + speaker_token_failed: "令牌交换失败({status}):{error}", + speaker_token_unsupported: "音箱请求的令牌类型 {kind} 尚不支持", + speaker_client_id_missing: "getInfo 中缺少 clientID", + speaker_login_refused: "音箱拒绝登录:{message}({error})", + speaker_not_joined: "{name} 登录后未连接", + local_replay_failed: "无法重新播放本地文件", + local_no_tracks: "没有可播放的本地曲目", + local_no_folder: "尚未设置本地音乐文件夹(请设置 behavior.local_music_path)", + local_uri_invalid: "本地文件 URI 无效:{error}", + local_play_failed: "无法播放本地文件:{error}", + local_task_failed: "本地播放任务失败:{error}", + local_audio_unavailable: "本地播放无音频输出:{error}", + audio_init_failed: "音频输出初始化失败:{error}", + local_scan_failed: "无法扫描音乐文件夹:{error}", + local_scan_task_failed: "本地文件夹扫描失败:{error}", + local_read_failed: "无法读取文件夹:{error}", + local_tracks_scan_failed: "本地曲目扫描失败:{error}", + local_exhausted: "{message} — 已无可播放曲目,停止播放", + subsonic_audio_unavailable: "Subsonic 播放无音频输出:{error}", + youtube_audio_unavailable: "YouTube 播放无音频输出:{error}", + radio_audio_unavailable: "电台播放无音频输出:{error}", + qobuz_audio_unavailable: "Qobuz 播放无音频输出:{error}", + subsonic_no_tracks: "没有可播放的 Subsonic 曲目", + subsonic_uri_invalid: "Subsonic 曲目 URI 无效", + subsonic_replay_failed: "无法重新播放 Subsonic 曲目", + subsonic_playlists_failed: "无法加载 Subsonic 播放列表:{error}", + subsonic_playlist_failed: "无法加载 Subsonic 播放列表:{error}", + subsonic_search_failed: "Subsonic 搜索失败:{error}", + subsonic_download_failed: "无法下载 Subsonic 曲目:{error}", + subsonic_play_failed: "无法播放 Subsonic 曲目:{error}", + subsonic_task_failed: "Subsonic 播放任务失败:{error}", + subsonic_no_server: "尚未设置 Subsonic 服务器(请设置 behavior.subsonic_url)", + radio_no_skip: "电台直播无法切换曲目", + radio_empty: "尚未设置或保存电台;请搜索并添加", + radio_uri_invalid: "电台 URI 无效", + radio_probe_timeout: "无法播放电台流:未能及时识别音频格式。", + radio_search_failed: "电台搜索失败:{error}", + radio_open_failed: "无法打开电台流:{error}", + radio_play_failed: "无法播放电台流:{error}", + radio_task_failed: "电台播放任务失败:{error}", + radio_playing: "♪ {name}(直播)", + youtube_playlist_missing: "该 YouTube 播放列表已不存在", + youtube_metadata_missing: "无法获取该视频的元数据", + youtube_no_tracks: "没有可播放的 YouTube 视频", + youtube_no_live: "YouTube 音源暂不支持直播", + youtube_uri_invalid: "YouTube URI 无效", + youtube_replay_failed: "无法重新播放 YouTube 曲目", + youtube_search_failed: "YouTube 搜索失败:{error}", + youtube_playlists_failed: "无法加载 YouTube 播放列表:{error}", + youtube_create_failed: "无法创建 YouTube 播放列表:{error}", + youtube_delete_failed: "无法删除 YouTube 播放列表:{error}", + youtube_add_failed: "无法将视频添加到播放列表:{error}", + youtube_remove_failed: "无法从播放列表移除视频:{error}", + youtube_download_failed: "无法下载 YouTube 音频:{error}", + youtube_task_failed: "YouTube 播放任务失败:{error}", + youtube_created: "已创建 YouTube 播放列表“{name}”", + youtube_deleted: "已删除 YouTube 播放列表“{name}”", + youtube_removed: "已从播放列表移除“{name}”", + fetching: "正在获取 {name}…", + youtube_added: "已将“{name}”添加到 {playlist}", + youtube_already_added: "“{name}”已在 {playlist} 中", + youtube_decode_failed: "无法播放 YouTube 音频:{error}(安装 ffmpeg 可解决大多数容器格式问题)", + qobuz_login_expired: "Qobuz:登录已过期,请按 `d` 并选择 Qobuz 重新登录", + qobuz_login_busy: "Qobuz 正在登录…", + qobuz_browser_login: "Qobuz:请打开浏览器窗口登录", + qobuz_logged_in: "Qobuz:已登录", + qobuz_no_tracks: "没有可播放的 Qobuz 曲目", + qobuz_uri_invalid: "Qobuz 曲目 URI 无效", + qobuz_replay_failed: "无法重新播放 Qobuz 曲目", + qobuz_login_url: "请在浏览器中打开此 URL 登录 Qobuz:{url}", + qobuz_login_failed: "Qobuz 登录失败:{error}", + qobuz_step_constants: "网页播放器参数", + qobuz_step_login: "登录", + qobuz_step_library: "音乐库", + qobuz_step_tracks: "曲目", + qobuz_step_search: "搜索", + qobuz_step_stream: "音频流", + qobuz_operation_failed: "Qobuz:{step}失败:{error}", + music_busy_playback: "Music 正忙,请重试播放请求", + music_wait_pause: "正在等待 Music 暂停,再切换音源", + music_not_running: "Music 未运行,请选择 Music 曲目启动", + music_shuffle_repeat: "请在 Music 中更改随机播放或循环播放", + music_switch_spotify: "播放 Spotify 曲目即可从 Music 切换", + music_busy: "Music 正忙,请重试请求", + music_queue_unsupported: "Apple Music 曲目无法加入队列", + music_loading: "正在加载 Apple Music 音乐库:{end}/{total}", + music_loaded: "Apple Music:已加载 {count} 项", + music_status_unavailable: "无法获取 Music 状态:{error}。正在后台重试。", + queue_repeat_unsupported: "此音源不支持循环播放", + queue_shuffle_unsupported: "此音源不支持随机播放", + queue_no_uri: "已跳过队列中缺少 URI 的曲目", + queue_finished: "队列播放完毕", + audio_disconnected: "音频输出设备已断开。", + radio_not_resumed: "播放已暂停,未恢复电台播放。", + source_playback_unavailable: "此版本不支持 {source} 播放", + queue_skip_disconnected: "内置播放未连接,已跳过“{name}”", + queue_play_failed: "无法播放 {name}:{error}", + queue_restore_failed: "无法恢复 {name}:{error}", + queue_playing: "♪ {name}(队列)", + queue_audio_unavailable: "队列播放无音频输出:{error}", + music_requires_macos: "Apple Music 需要 macOS 和 apple-music 功能", + spotify_not_compiled: "此版本不支持 Spotify", + native_reconnecting: "正在重新连接内置播放…", + native_settling: "内置播放仍在准备中…", + native_resuming: "正在此设备恢复上次播放…", + native_reconnect_resume: "正在重新连接内置播放,随后将恢复播放。", + native_connection_restored: "内置播放连接已恢复,正在恢复播放。", + native_no_state: "内置播放连接已恢复,但没有可恢复的播放状态。", + native_restore_retry: "内置播放恢复失败,正在再次连接。", + queue_added: "已加入队列", + queue_load_failed: "无法加载队列(没有活动设备?)", + native_nothing_to_resume: "{device} 上没有可恢复的播放。", + native_start_failed: "启动内置播放失败:{error}", + native_shuffle_failed: "启用随机播放失败:{error}", + native_resume_failed: "恢复播放失败:{error}", + native_recovered: "内置播放已恢复。", + native_lost_reconnecting: "内置播放连接已断开,正在重新连接。", + native_reconnected: "内置播放连接已恢复。", + native_restored: "内置播放已恢复。", + native_restored_paused: "内置播放已恢复,保持暂停。", + native_skip_unavailable: "此曲目无法使用内置播放(不可用或被限制),正在跳过。", + native_many_unavailable: "多首曲目连续无法使用内置播放,已停止播放。你的账号或所在地区可能无法使用这些曲目。请按 'd' 切换到官方 Spotify Connect 设备。", + native_recovery_dropped: "内置播放恢复失败,播放请求已放弃:{error}", + native_recovery_failed: "内置播放恢复失败:{error}", + native_lost_recovering: "内置播放连接已断开,正在尝试恢复。", + native_stalled: "内置播放已停滞,正在尝试恢复。", + native_stopped: "内置播放已停止。", + native_moved: "播放已转移到另一台 Spotify 设备。", + native_disconnected: "内置播放已断开,正在尝试恢复。", + native_stopped_recovering: "内置播放已停止,正在尝试恢复。", + queue_local_source: "本地", + local_root_folder: "(根目录)", + local_unknown_track: "未知曲目", + ..EN +}; // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] -pub const ZH_HANT: InfraStatusStrings = InfraStatusStrings { ..EN }; +pub const ZH_HANT: InfraStatusStrings = InfraStatusStrings { + cast_no_answer: "{address} 沒有回應", + cast_no_tls: "與 {address} 的 TLS 交握未完成", + cast_message_size: "Cast 訊息大小為 {size} 位元組", + cast_malformed: "Cast 訊息格式錯誤", + cast_no_reply: "{kind} 未收到回覆", + cast_receiver_failed: "Spotify 接收器未啟動:{reason}", + cast_client_id_missing: "getInfoResponse 中缺少 clientID", + cast_login_refused: "喇叭拒絕登入:{reply}", + cast_access_token_missing: "device-auth 回覆中缺少 accessToken", + speaker_token_failed: "權杖交換失敗({status}):{error}", + speaker_token_unsupported: "喇叭要求的權杖類型 {kind} 尚未支援", + speaker_client_id_missing: "getInfo 中缺少 clientID", + speaker_login_refused: "喇叭拒絕登入:{message}({error})", + speaker_not_joined: "{name} 登入後未連線", + local_replay_failed: "無法重新播放本機檔案", + local_no_tracks: "沒有可播放的本機曲目", + local_no_folder: "尚未設定本機音樂資料夾(請設定 behavior.local_music_path)", + local_uri_invalid: "本機檔案 URI 無效:{error}", + local_play_failed: "無法播放本機檔案:{error}", + local_task_failed: "本機播放工作失敗:{error}", + local_audio_unavailable: "本機播放無音訊輸出:{error}", + audio_init_failed: "音訊輸出初始化失敗:{error}", + local_scan_failed: "無法掃描音樂資料夾:{error}", + local_scan_task_failed: "本機資料夾掃描失敗:{error}", + local_read_failed: "無法讀取資料夾:{error}", + local_tracks_scan_failed: "本機曲目掃描失敗:{error}", + local_exhausted: "{message} — 已無可播放曲目,停止播放", + subsonic_audio_unavailable: "Subsonic 播放無音訊輸出:{error}", + youtube_audio_unavailable: "YouTube 播放無音訊輸出:{error}", + radio_audio_unavailable: "電台播放無音訊輸出:{error}", + qobuz_audio_unavailable: "Qobuz 播放無音訊輸出:{error}", + subsonic_no_tracks: "沒有可播放的 Subsonic 曲目", + subsonic_uri_invalid: "Subsonic 曲目 URI 無效", + subsonic_replay_failed: "無法重新播放 Subsonic 曲目", + subsonic_playlists_failed: "無法載入 Subsonic 播放清單:{error}", + subsonic_playlist_failed: "無法載入 Subsonic 播放清單:{error}", + subsonic_search_failed: "Subsonic 搜尋失敗:{error}", + subsonic_download_failed: "無法下載 Subsonic 曲目:{error}", + subsonic_play_failed: "無法播放 Subsonic 曲目:{error}", + subsonic_task_failed: "Subsonic 播放工作失敗:{error}", + subsonic_no_server: "尚未設定 Subsonic 伺服器(請設定 behavior.subsonic_url)", + radio_no_skip: "電台直播無法切換曲目", + radio_empty: "尚未設定或儲存電台;請搜尋並新增", + radio_uri_invalid: "電台 URI 無效", + radio_probe_timeout: "無法播放電台串流:未能及時辨識音訊格式。", + radio_search_failed: "電台搜尋失敗:{error}", + radio_open_failed: "無法開啟電台串流:{error}", + radio_play_failed: "無法播放電台串流:{error}", + radio_task_failed: "電台播放工作失敗:{error}", + radio_playing: "♪ {name}(直播)", + youtube_playlist_missing: "該 YouTube 播放清單已不存在", + youtube_metadata_missing: "無法取得該影片的中繼資料", + youtube_no_tracks: "沒有可播放的 YouTube 影片", + youtube_no_live: "YouTube 來源暫不支援直播", + youtube_uri_invalid: "YouTube URI 無效", + youtube_replay_failed: "無法重新播放 YouTube 曲目", + youtube_search_failed: "YouTube 搜尋失敗:{error}", + youtube_playlists_failed: "無法載入 YouTube 播放清單:{error}", + youtube_create_failed: "無法建立 YouTube 播放清單:{error}", + youtube_delete_failed: "無法刪除 YouTube 播放清單:{error}", + youtube_add_failed: "無法將影片加入播放清單:{error}", + youtube_remove_failed: "無法從播放清單移除影片:{error}", + youtube_download_failed: "無法下載 YouTube 音訊:{error}", + youtube_task_failed: "YouTube 播放工作失敗:{error}", + youtube_created: "已建立 YouTube 播放清單「{name}」", + youtube_deleted: "已刪除 YouTube 播放清單「{name}」", + youtube_removed: "已從播放清單移除「{name}」", + fetching: "正在取得 {name}…", + youtube_added: "已將「{name}」加入 {playlist}", + youtube_already_added: "「{name}」已在 {playlist} 中", + youtube_decode_failed: "無法播放 YouTube 音訊:{error}(安裝 ffmpeg 可解決大多數容器格式問題)", + qobuz_login_expired: "Qobuz:登入已過期,請按 `d` 並選擇 Qobuz 重新登入", + qobuz_login_busy: "Qobuz 正在登入…", + qobuz_browser_login: "Qobuz:請開啟瀏覽器視窗登入", + qobuz_logged_in: "Qobuz:已登入", + qobuz_no_tracks: "沒有可播放的 Qobuz 曲目", + qobuz_uri_invalid: "Qobuz 曲目 URI 無效", + qobuz_replay_failed: "無法重新播放 Qobuz 曲目", + qobuz_login_url: "請在瀏覽器中開啟此 URL 登入 Qobuz:{url}", + qobuz_login_failed: "Qobuz 登入失敗:{error}", + qobuz_step_constants: "網頁播放器參數", + qobuz_step_login: "登入", + qobuz_step_library: "音樂庫", + qobuz_step_tracks: "曲目", + qobuz_step_search: "搜尋", + qobuz_step_stream: "音訊串流", + qobuz_operation_failed: "Qobuz:{step}失敗:{error}", + music_busy_playback: "Music 忙碌中,請重試播放請求", + music_wait_pause: "正在等待 Music 暫停,再切換來源", + music_not_running: "Music 未執行,請選擇 Music 曲目啟動", + music_shuffle_repeat: "請在 Music 中更改隨機播放或循環播放", + music_switch_spotify: "播放 Spotify 曲目即可從 Music 切換", + music_busy: "Music 忙碌中,請重試請求", + music_queue_unsupported: "Apple Music 曲目無法加入佇列", + music_loading: "正在載入 Apple Music 音樂庫:{end}/{total}", + music_loaded: "Apple Music:已載入 {count} 項", + music_status_unavailable: "無法取得 Music 狀態:{error}。正在背景重試。", + queue_repeat_unsupported: "此來源不支援循環播放", + queue_shuffle_unsupported: "此來源不支援隨機播放", + queue_no_uri: "已略過佇列中缺少 URI 的曲目", + queue_finished: "佇列播放完畢", + audio_disconnected: "音訊輸出裝置已中斷連線。", + radio_not_resumed: "播放已暫停,未恢復電台播放。", + source_playback_unavailable: "此版本不支援 {source} 播放", + queue_skip_disconnected: "內建串流未連線,已略過「{name}」", + queue_play_failed: "無法播放 {name}:{error}", + queue_restore_failed: "無法恢復 {name}:{error}", + queue_playing: "♪ {name}(佇列)", + queue_audio_unavailable: "佇列播放無音訊輸出:{error}", + music_requires_macos: "Apple Music 需要 macOS 和 apple-music 功能", + spotify_not_compiled: "此版本不支援 Spotify", + native_reconnecting: "正在重新連線內建串流…", + native_settling: "內建串流仍在準備中…", + native_resuming: "正在此裝置恢復上次播放…", + native_reconnect_resume: "正在重新連線內建串流,隨後將恢復播放。", + native_connection_restored: "內建串流連線已恢復,正在恢復播放。", + native_no_state: "內建串流連線已恢復,但沒有可恢復的播放狀態。", + native_restore_retry: "內建串流恢復失敗,正在再次連線。", + queue_added: "已加入佇列", + queue_load_failed: "無法載入佇列(沒有使用中的裝置?)", + native_nothing_to_resume: "{device} 上沒有可恢復的播放。", + native_start_failed: "啟動內建串流失敗:{error}", + native_shuffle_failed: "啟用隨機播放失敗:{error}", + native_resume_failed: "恢復播放失敗:{error}", + native_recovered: "內建串流已恢復。", + native_lost_reconnecting: "內建串流連線已中斷,正在重新連線。", + native_reconnected: "內建串流連線已恢復。", + native_restored: "內建串流已恢復。", + native_restored_paused: "內建串流已恢復,保持暫停。", + native_skip_unavailable: "無法透過內建串流播放此曲目(無法使用或受限制),正在略過。", + native_many_unavailable: "多首曲目連續無法透過內建串流播放,已停止播放。你的帳號或所在地區可能無法使用這些曲目。請按 'd' 切換到官方 Spotify Connect 裝置。", + native_recovery_dropped: "內建串流恢復失敗,播放請求已放棄:{error}", + native_recovery_failed: "內建串流恢復失敗:{error}", + native_lost_recovering: "內建串流連線已中斷,正在嘗試恢復。", + native_stalled: "內建串流已停滯,正在嘗試恢復。", + native_stopped: "內建串流已停止。", + native_moved: "播放已轉移到另一台 Spotify 裝置。", + native_disconnected: "內建串流已中斷,正在嘗試恢復。", + native_stopped_recovering: "內建串流已停止,正在嘗試恢復。", + queue_local_source: "本機", + local_root_folder: "(根目錄)", + local_unknown_track: "未知曲目", + ..EN +}; From f9bbc6ed4184eb23ed5ac63d414baf68d1dd860d Mon Sep 17 00:00:00 2001 From: 2570165831 <200382837+2570165831@users.noreply.github.com> Date: Sat, 24 Oct 2026 16:48:05 +1100 Subject: [PATCH 2/3] fix(i18n): keep the spotatui name in the no-Spotify build message --- gui/src/bindings/strings.json | 4 ++-- src/core/i18n/status_infra.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/src/bindings/strings.json b/gui/src/bindings/strings.json index 68869a68..7b681633 100644 --- a/gui/src/bindings/strings.json +++ b/gui/src/bindings/strings.json @@ -3875,7 +3875,7 @@ "spotify_connected": "Spotify connected. Restart spotatui to enable native playback.", "spotify_login_failed": "Spotify login failed: {error}", "spotify_login_timeout": "Spotify login timed out. Press `d` and pick Spotify to try again.", - "spotify_not_compiled": "此版本不支持 Spotify", + "spotify_not_compiled": "此版本的 spotatui 不支持 Spotify", "spotify_not_connected": "Spotify not connected. Press `d` and pick Spotify to log in.", "spotify_pair": "Log in to Spotify: open {url} and confirm the code {code}", "spotify_playing_elsewhere": "Playback remains on another Spotify device.", @@ -5886,7 +5886,7 @@ "spotify_connected": "Spotify connected. Restart spotatui to enable native playback.", "spotify_login_failed": "Spotify login failed: {error}", "spotify_login_timeout": "Spotify login timed out. Press `d` and pick Spotify to try again.", - "spotify_not_compiled": "此版本不支援 Spotify", + "spotify_not_compiled": "此版本的 spotatui 不支援 Spotify", "spotify_not_connected": "Spotify not connected. Press `d` and pick Spotify to log in.", "spotify_pair": "Log in to Spotify: open {url} and confirm the code {code}", "spotify_playing_elsewhere": "Playback remains on another Spotify device.", diff --git a/src/core/i18n/status_infra.rs b/src/core/i18n/status_infra.rs index e928ff0e..6557789b 100644 --- a/src/core/i18n/status_infra.rs +++ b/src/core/i18n/status_infra.rs @@ -1145,7 +1145,7 @@ pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { queue_playing: "♪ {name}(队列)", queue_audio_unavailable: "队列播放无音频输出:{error}", music_requires_macos: "Apple Music 需要 macOS 和 apple-music 功能", - spotify_not_compiled: "此版本不支持 Spotify", + spotify_not_compiled: "此版本的 spotatui 不支持 Spotify", native_reconnecting: "正在重新连接内置播放…", native_settling: "内置播放仍在准备中…", native_resuming: "正在此设备恢复上次播放…", @@ -1293,7 +1293,7 @@ pub const ZH_HANT: InfraStatusStrings = InfraStatusStrings { queue_playing: "♪ {name}(佇列)", queue_audio_unavailable: "佇列播放無音訊輸出:{error}", music_requires_macos: "Apple Music 需要 macOS 和 apple-music 功能", - spotify_not_compiled: "此版本不支援 Spotify", + spotify_not_compiled: "此版本的 spotatui 不支援 Spotify", native_reconnecting: "正在重新連線內建串流…", native_settling: "內建串流仍在準備中…", native_resuming: "正在此裝置恢復上次播放…", From ea1de5bb35498b1b76229839dcfd1006357a683c Mon Sep 17 00:00:00 2001 From: 2570165831 <200382837+2570165831@users.noreply.github.com> Date: Sat, 24 Oct 2026 16:56:21 +1100 Subject: [PATCH 3/3] feat(i18n): translate network, history and DJ feedback Complete the infrastructure status table and translate the exported HTML recap and the user-visible DJ and MCP strings into both Chinese scripts. Model-facing tool schemas and replies stay in English. Tests that compared a translation with its own table field now assert the expected Chinese text, and the Traditional sync summary uses the same word for unmatched tracks as the screen table. --- gui/src/bindings/strings.json | 884 +++++++++++++++++----------------- src/core/i18n/recap.rs | 72 ++- src/core/i18n/status_infra.rs | 254 ++++++++++ src/core/i18n/tools.rs | 134 +++++- src/gui/protocol.rs | 30 +- src/infra/dj/exec.rs | 9 +- src/infra/dj/setup.rs | 5 + src/infra/history.rs | 10 +- src/infra/local/mod.rs | 12 +- src/infra/network/library.rs | 4 +- src/infra/network/mod.rs | 14 +- src/infra/network/requests.rs | 6 +- 12 files changed, 945 insertions(+), 489 deletions(-) diff --git a/gui/src/bindings/strings.json b/gui/src/bindings/strings.json index 7b681633..815f70e9 100644 --- a/gui/src/bindings/strings.json +++ b/gui/src/bindings/strings.json @@ -2844,38 +2844,38 @@ "waiting_guests": "等待听众加入…" }, "recap": { - "analytics": "Detailed Analytics", - "by_day": "Listening by Day", - "by_hour": "Listening by Hour", - "cover_art": "Cover Art", - "download": "Download Share Card", - "empty_summary": "No qualified local listening history was recorded for this window yet.", - "first_track": "NO. 1 TRACK", - "footer": "History is generated from spotatui local listening records and starts when the feature is enabled. Short or skipped plays are stored but excluded from headline recap totals.", - "generated_by": "generated by ", - "generated_by_product": "generated by spotatui", - "generating": "Generating...", - "language": "en", - "listened_at_template": "listened at {time}", - "listening_time": "Listening Time", - "no_albums": "No albums yet.", - "no_artists": "No artists yet.", - "no_data": "No data", - "no_data_yet": "No data yet.", - "no_recent": "No recent plays.", - "no_tracks": "No tracks yet.", - "page_title": "spotatui recap", - "qualified_plays": "Qualified Plays", - "recent_date_format": "%b %d, %H:%M", - "recent_plays": "Recent Plays", - "render_failed": "Could not render image. Please try again!", - "summary": "A shareable HTML snapshot of your qualified local listening history, generated directly from spotatui.", - "title": "Listening History Recap", - "top_album": "Top Album", - "top_albums": "Top Albums", - "top_artist": "Top Artist", - "top_artists": "Top Artists", - "top_tracks": "Top Tracks" + "analytics": "详细统计", + "by_day": "每日听歌时长", + "by_hour": "各时段听歌时长", + "cover_art": "封面", + "download": "下载分享卡片", + "empty_summary": "此时间段还没有有效的本地听歌记录。", + "first_track": "最常听歌曲", + "footer": "回顾根据 spotatui 本地听歌记录生成,从启用此功能时开始统计。短时播放或跳过的歌曲会被记录,但不计入回顾的主要统计。", + "generated_by": "生成自 ", + "generated_by_product": "由 spotatui 生成", + "generating": "正在生成…", + "language": "zh-Hans", + "listened_at_template": "播放于 {time}", + "listening_time": "听歌时长", + "no_albums": "暂无专辑。", + "no_artists": "暂无艺人。", + "no_data": "暂无数据", + "no_data_yet": "暂无数据。", + "no_recent": "暂无最近播放记录。", + "no_tracks": "暂无歌曲。", + "page_title": "spotatui 听歌回顾", + "qualified_plays": "有效播放次数", + "recent_date_format": "%m月%d日 %H:%M", + "recent_plays": "最近播放", + "render_failed": "无法生成图片,请重试!", + "summary": "由 spotatui 直接生成的本地听歌回顾,可作为 HTML 页面分享。", + "title": "听歌记录回顾", + "top_album": "最常听专辑", + "top_albums": "最常听专辑", + "top_artist": "最常听艺人", + "top_artists": "最常听艺人", + "top_tracks": "最常听歌曲" }, "screens": { "account_required": "需要账号", @@ -3676,37 +3676,37 @@ "cast_no_reply": "{kind} 未收到回复", "cast_no_tls": "与 {address} 的 TLS 握手未完成", "cast_receiver_failed": "Spotify 接收器未启动:{reason}", - "dj_duplicate": "{count} already queued", - "dj_error": "DJ error ({backend}): {error}", - "dj_index_capped": " (stopped at {count}; the rest is not filtered)", - "dj_indexed": "Indexed {tracks} track(s) across {playlists} playlist(s)", - "dj_library_failed": "DJ: could not read your playlists ({error}); recommending unfiltered", - "dj_not_found": "{count} not found", - "dj_nothing_queued": "DJ: nothing was queued that time. Ask again, or try a different direction.", - "dj_owned": "{count} already yours", - "dj_queued": "queued {count}", - "dj_status": "DJ: {summary}", - "dj_steps_exhausted": "Stopped after {count} steps. Ask again if that is not where you wanted to end up.", - "dj_topped_up": "DJ: topped up the queue", + "dj_duplicate": "已在队列中 {count} 首", + "dj_error": "DJ 错误({backend}):{error}", + "dj_index_capped": "(已在 {count} 首处停止,其余曲目未过滤)", + "dj_indexed": "已索引 {playlists} 个播放列表中的 {tracks} 首曲目", + "dj_library_failed": "DJ:无法读取你的播放列表({error}),将不加过滤地推荐", + "dj_not_found": "未找到 {count} 首", + "dj_nothing_queued": "DJ:这次未加入任何曲目。请再试一次,或换个方向。", + "dj_owned": "已拥有 {count} 首", + "dj_queued": "已加入队列 {count} 首", + "dj_status": "DJ:{summary}", + "dj_steps_exhausted": "已在 {count} 步后停止。如果结果不符合预期,请再次提问。", + "dj_topped_up": "DJ:已补充队列", "fetching": "正在获取 {name}…", - "friend_add_failed": "Could not add friend: {error}", - "friend_added": "Added friend", - "friend_added_code": "Added friend (code: {code})", - "friend_unfollow_failed": "Failed to unfollow: {error}", - "friend_unfollowed": "Unfollowed", - "history_all": "All Time", - "history_failed": "failed to load listening history: {error}", - "history_hours_minutes": "{hours}h {minutes}m", - "history_minutes": "{minutes}m", - "history_month": "Last 30 Days", - "history_play": "{count} play · {duration}", - "history_plays": "{count} plays · {duration}", - "history_this_month": "This Month", - "history_this_year": "This Year", - "history_track_hit": "{count} track hit · {duration}", - "history_track_hits": "{count} track hits · {duration}", - "history_week": "Last 7 Days", - "liked_check_failed": "Could not check liked track state: {error}", + "friend_add_failed": "无法添加好友:{error}", + "friend_added": "已添加好友", + "friend_added_code": "已添加好友(代码:{code})", + "friend_unfollow_failed": "取消关注失败:{error}", + "friend_unfollowed": "已取消关注", + "history_all": "全部时间", + "history_failed": "无法加载收听历史:{error}", + "history_hours_minutes": "{hours} 小时 {minutes} 分钟", + "history_minutes": "{minutes} 分钟", + "history_month": "最近 30 天", + "history_play": "{count} 次播放 · {duration}", + "history_plays": "{count} 次播放 · {duration}", + "history_this_month": "本月", + "history_this_year": "今年", + "history_track_hit": "曲目播放 {count} 次 · {duration}", + "history_track_hits": "曲目播放 {count} 次 · {duration}", + "history_week": "最近 7 天", + "liked_check_failed": "无法检查曲目的喜欢状态:{error}", "local_audio_unavailable": "本地播放无音频输出:{error}", "local_exhausted": "{message} — 已无可播放曲目,停止播放", "local_no_folder": "尚未设置本地音乐文件夹(请设置 behavior.local_music_path)", @@ -3721,10 +3721,10 @@ "local_tracks_scan_failed": "本地曲目扫描失败:{error}", "local_unknown_track": "未知曲目", "local_uri_invalid": "本地文件 URI 无效:{error}", - "made_for_you_missing": "Spotify did not send the Made For You page", - "made_for_you_unavailable": "Made For You is unavailable: every known browsePage hash is retired", - "mcp_status": "MCP: {message}", - "monthly_recap_ready": "Monthly listening recap ready at {path} ({count} listens)", + "made_for_you_missing": "Spotify 未返回为你精选页面", + "made_for_you_unavailable": "为你精选不可用:所有已知的 browsePage 查询标识均已失效", + "mcp_status": "MCP:{message}", + "monthly_recap_ready": "月度收听回顾已生成于 {path}({count} 次收听)", "music_busy": "Music 正忙,请重试请求", "music_busy_playback": "Music 正忙,请重试播放请求", "music_loaded": "Apple Music:已加载 {count} 项", @@ -3762,27 +3762,27 @@ "native_start_failed": "启动内置播放失败:{error}", "native_stopped": "内置播放已停止。", "native_stopped_recovering": "内置播放已停止,正在尝试恢复。", - "network_error": "{error}", - "network_rate_error": "{error}", - "other_source_playing": "Another source owns playback", - "party_disconnected": "Listening Party ended: Spotify is not connected.", - "party_ended": "Party ended", - "party_error": "Party: {error}", - "party_join_failed": "Failed to join party: {error}", - "party_joined": "{name} joined the party", - "party_left": "{name} left the party", - "party_start_failed": "Failed to start party: {error}", - "playlist_added": "Added to playlist", - "playlist_added_named": "Added to {name}", - "playlist_changed": "The playlist changed since it loaded; open it again and retry", - "playlist_created": "Playlist \"{name}\" created!", - "playlist_created_no_id": "Playlist created but could not get its ID", - "playlist_external_unavailable": "Spotify Development Mode blocks playlist contents owned by another user. Only playlists you own or collaborate on are available.", - "playlist_id_invalid": "Invalid playlist identifier.", - "playlist_refresh_partial": "Playlist refresh incomplete; kept the previous list.", - "playlist_removed": "Removed from playlist", - "playlist_search_matches": "{count} playlist tracks match \"{query}\"{scope}", - "playlist_search_scope": " in the first {count} tracks", + "network_error": "Spotify 请求失败:{error}", + "network_rate_error": "Spotify 请求受限:{error}", + "other_source_playing": "其他音源正在播放", + "party_disconnected": "一起听已结束:Spotify 未连接。", + "party_ended": "一起听已结束", + "party_error": "一起听:{error}", + "party_join_failed": "加入一起听失败:{error}", + "party_joined": "{name} 加入了一起听", + "party_left": "{name} 离开了一起听", + "party_start_failed": "发起一起听失败:{error}", + "playlist_added": "已添加到播放列表", + "playlist_added_named": "已添加到 {name}", + "playlist_changed": "播放列表在加载后发生了变化,请重新打开后再试", + "playlist_created": "播放列表“{name}”已创建!", + "playlist_created_no_id": "播放列表已创建,但无法获取其 ID", + "playlist_external_unavailable": "Spotify Development Mode 禁止读取其他用户拥有的播放列表内容。只能访问你拥有或参与协作的播放列表。", + "playlist_id_invalid": "播放列表标识无效。", + "playlist_refresh_partial": "播放列表刷新不完整,已保留原列表。", + "playlist_removed": "已从播放列表移除", + "playlist_search_matches": "播放列表中有 {count} 首曲目匹配“{query}”{scope}", + "playlist_search_scope": "(仅搜索前 {count} 首曲目)", "qobuz_audio_unavailable": "Qobuz 播放无音频输出:{error}", "qobuz_browser_login": "Qobuz:请打开浏览器窗口登录", "qobuz_logged_in": "Qobuz:已登录", @@ -3823,68 +3823,68 @@ "radio_search_failed": "电台搜索失败:{error}", "radio_task_failed": "电台播放任务失败:{error}", "radio_uri_invalid": "电台 URI 无效", - "recap_created": "Listening recap generated at {path} ({count} listens)", - "recap_failed": "Failed to generate recap: {error}", - "search_query_unavailable": "Search is unavailable: every known {operation} hash is retired", - "setup_agent_subscription": "uses that CLI's own subscription", - "setup_agy_plan": "uses your Antigravity plan", - "setup_anthropic": "anthropic (API key)", - "setup_anthropic_billing": "Anthropic API, pay per token", - "setup_builtin_default": "Use the built-in default ({model})", - "setup_cheapest": "cheapest", - "setup_claude_plan": "uses your Claude Pro/Max plan", - "setup_cli_default": "Use the CLI's default", - "setup_codex_plan": "uses your ChatGPT plan", - "setup_copilot_plan": "uses your GitHub Copilot plan", - "setup_current": "currently configured", - "setup_custom": "Custom…", - "setup_fable_note": "heaviest, hits it faster still", - "setup_gemini_legacy": "legacy Gemini CLI, superseded by agy", - "setup_haiku_note": "cheapest, easiest on a Pro plan", - "setup_heaviest_all": "heaviest of all", - "setup_heaviest_gemini": "heaviest Gemini", - "setup_keep_as_is": "Keep this command as it is", - "setup_keep_command": "{command} (keep)", - "setup_needs_key": "needs {variable} (or behavior.dj_api_key)", - "setup_no_api_key": "{agent} (no API key)", - "setup_no_model_flag": "no model flag", - "setup_not_installed": " · not on PATH", - "setup_openai": "openai_compat (local or self-hosted)", - "setup_openai_endpoint": "any OpenAI-compatible endpoint, e.g. Ollama on localhost", - "setup_opencode_plan": "uses whatever provider opencode is logged into", - "setup_opus_note": "heavy, a Pro plan hits its limit fast", - "setup_own_command": "your own command, run exactly as written", - "setup_own_model_note": "spotatui adds no model flag to a command it does not own", - "setup_price_fable": "$10/$50 per MTok, 1M ctx", - "setup_price_haiku": "$1/$5 per MTok, 200K ctx", - "setup_price_opus": "$5/$25 per MTok, 1M ctx", - "setup_price_sonnet": "$3/$15 per MTok, 1M ctx", - "setup_sonnet_note": "balanced", - "setup_type_model": "type a model name", - "setup_type_provider_model": "type a model name, as provider/model", - "shuffle_capped": "Large context: shuffling the first {count} tracks", - "shuffle_context_failed": "Shuffle: failed to load the context's track list", - "shuffle_not_applied": "Playback started; couldn't apply shuffle.", + "recap_created": "收听回顾已生成于 {path}({count} 次收听)", + "recap_failed": "生成收听回顾失败:{error}", + "search_query_unavailable": "搜索不可用:所有已知的 {operation} 查询标识均已失效", + "setup_agent_subscription": "使用该 CLI 自己的订阅", + "setup_agy_plan": "使用你的 Antigravity 套餐", + "setup_anthropic": "anthropic(API key)", + "setup_anthropic_billing": "Anthropic API,按 token 计费", + "setup_builtin_default": "使用内置默认模型({model})", + "setup_cheapest": "最省费用", + "setup_claude_plan": "使用你的 Claude Pro/Max 套餐", + "setup_cli_default": "使用 CLI 的默认模型", + "setup_codex_plan": "使用你的 ChatGPT 套餐", + "setup_copilot_plan": "使用你的 GitHub Copilot 套餐", + "setup_current": "当前配置", + "setup_custom": "自定义…", + "setup_fable_note": "用量最大,更容易用满额度", + "setup_gemini_legacy": "旧版 Gemini CLI,已由 agy 取代", + "setup_haiku_note": "最省费用,对 Pro 套餐额度压力最小", + "setup_heaviest_all": "用量最大", + "setup_heaviest_gemini": "Gemini 中用量最大", + "setup_keep_as_is": "保留此命令不变", + "setup_keep_command": "{command}(保留)", + "setup_needs_key": "需要 {variable}(或 behavior.dj_api_key)", + "setup_no_api_key": "{agent}(无需 API key)", + "setup_no_model_flag": "不传入模型参数", + "setup_not_installed": " · 不在 PATH 中", + "setup_openai": "openai_compat(本地或自托管)", + "setup_openai_endpoint": "任意兼容 OpenAI 的端点,如 localhost 上的 Ollama", + "setup_opencode_plan": "使用 opencode 当前登录的服务提供商", + "setup_opus_note": "用量较大,Pro 套餐容易用满额度", + "setup_own_command": "使用你自己的命令,按原样运行", + "setup_own_model_note": "spotatui 不会向自定义命令添加模型参数", + "setup_price_fable": "每百万 token $10/$50,1M 上下文", + "setup_price_haiku": "每百万 token $1/$5,200K 上下文", + "setup_price_opus": "每百万 token $5/$25,1M 上下文", + "setup_price_sonnet": "每百万 token $3/$15,1M 上下文", + "setup_sonnet_note": "均衡", + "setup_type_model": "输入模型名称", + "setup_type_provider_model": "输入模型名称,格式为 provider/model", + "shuffle_capped": "列表较大:随机播放前 {count} 首曲目", + "shuffle_context_failed": "随机播放:无法加载所属列表的曲目", + "shuffle_not_applied": "已开始播放,但无法启用随机播放。", "source_playback_unavailable": "此版本不支持 {source} 播放", "speaker_client_id_missing": "getInfo 中缺少 clientID", "speaker_login_refused": "音箱拒绝登录:{message}({error})", "speaker_not_joined": "{name} 登录后未连接", "speaker_token_failed": "令牌交换失败({status}):{error}", "speaker_token_unsupported": "音箱请求的令牌类型 {kind} 尚不支持", - "spotify_api_error": "Spotify API {status} failed: {body}", - "spotify_connected": "Spotify connected. Restart spotatui to enable native playback.", - "spotify_login_failed": "Spotify login failed: {error}", - "spotify_login_timeout": "Spotify login timed out. Press `d` and pick Spotify to try again.", + "spotify_api_error": "Spotify API {status} 请求失败:{body}", + "spotify_connected": "Spotify 已连接。重启 spotatui 以启用内置播放。", + "spotify_login_failed": "Spotify 登录失败:{error}", + "spotify_login_timeout": "Spotify 登录超时。请按 `d` 并选择 Spotify 重试。", "spotify_not_compiled": "此版本的 spotatui 不支持 Spotify", - "spotify_not_connected": "Spotify not connected. Press `d` and pick Spotify to log in.", - "spotify_pair": "Log in to Spotify: open {url} and confirm the code {code}", - "spotify_playing_elsewhere": "Playback remains on another Spotify device.", - "spotify_poll_rejected": "Spotify keeps rejecting the playback poll; retrying automatically.", - "spotify_poll_retry": "Temporary Spotify network error while polling playback; retrying automatically.", - "spotify_profile_retry": "Spotify rate limit hit while loading profile. Retrying automatically.", - "spotify_rate_retry": "Spotify rate limit hit. Retrying automatically; please wait a few seconds.", - "spotify_rate_wait": "Spotify rate limit: waiting {seconds}s", - "spotify_server_retry": "Spotify server temporarily unavailable (5xx); retrying automatically.", + "spotify_not_connected": "Spotify 未连接。请按 `d` 并选择 Spotify 登录。", + "spotify_pair": "登录 Spotify:打开 {url} 并确认配对码 {code}", + "spotify_playing_elsewhere": "仍在另一台 Spotify 设备上播放。", + "spotify_poll_rejected": "Spotify 持续拒绝播放状态请求,正在自动重试。", + "spotify_poll_retry": "获取播放状态时发生临时 Spotify 网络错误,正在自动重试。", + "spotify_profile_retry": "加载个人资料时 Spotify 请求受限,正在自动重试。", + "spotify_rate_retry": "Spotify 请求受限。正在自动重试,请稍候。", + "spotify_rate_wait": "Spotify 请求受限:等待 {seconds} 秒", + "spotify_server_retry": "Spotify 服务器暂时不可用(5xx),正在自动重试。", "subsonic_audio_unavailable": "Subsonic 播放无音频输出:{error}", "subsonic_download_failed": "无法下载 Subsonic 曲目:{error}", "subsonic_no_server": "尚未设置 Subsonic 服务器(请设置 behavior.subsonic_url)", @@ -3896,23 +3896,23 @@ "subsonic_search_failed": "Subsonic 搜索失败:{error}", "subsonic_task_failed": "Subsonic 播放任务失败:{error}", "subsonic_uri_invalid": "Subsonic 曲目 URI 无效", - "sync_busy": "Playlist sync already running", - "sync_dry_run": " (dry run)", - "sync_failed": "Playlist sync failed: {error}", - "sync_link_failed": "Playlist link failed: {error}", - "sync_no_links": "Playlist sync: no links", - "sync_progress": "Playlist sync: {name} {done}/{total}", - "sync_qobuz_not_logged_in": "Qobuz is not logged in", - "sync_remove_failed": "Removing the link failed: {error}", - "sync_removed": "Playlist link removed; the mirror playlists stay", - "sync_source_not_compiled": "{source} is not compiled into this build (feature `{feature}`)", - "sync_source_unreachable": "{source} unreachable: {error}", - "sync_source_unsupported": "{source} playlists cannot be synced", - "sync_spotify_not_connected": "Spotify is not connected", - "sync_subsonic_not_configured": "No Subsonic server configured (set behavior.subsonic_url)", - "sync_summary": "Playlist sync{dry}: {added} added, {removed} removed, {unmatched} unmatched", - "sync_using_playlist": "Using the existing {source} playlist \"{name}\"", - "unnamed_folder": "Unnamed Folder", + "sync_busy": "播放列表同步已在运行", + "sync_dry_run": "(模拟运行)", + "sync_failed": "播放列表同步失败:{error}", + "sync_link_failed": "播放列表关联失败:{error}", + "sync_no_links": "播放列表同步:没有关联", + "sync_progress": "播放列表同步:{name} {done}/{total}", + "sync_qobuz_not_logged_in": "Qobuz 未登录", + "sync_remove_failed": "移除关联失败:{error}", + "sync_removed": "播放列表关联已移除,镜像播放列表保留", + "sync_source_not_compiled": "此版本未编译 {source}(功能 `{feature}`)", + "sync_source_unreachable": "无法连接 {source}:{error}", + "sync_source_unsupported": "无法同步 {source} 播放列表", + "sync_spotify_not_connected": "Spotify 未连接", + "sync_subsonic_not_configured": "尚未配置 Subsonic 服务器(请设置 behavior.subsonic_url)", + "sync_summary": "播放列表同步{dry}:添加 {added} 首,移除 {removed} 首,未匹配 {unmatched} 首", + "sync_using_playlist": "使用现有 {source} 播放列表“{name}”", + "unnamed_folder": "未命名文件夹", "youtube_add_failed": "无法将视频添加到播放列表:{error}", "youtube_added": "已将“{name}”添加到 {playlist}", "youtube_already_added": "“{name}”已在 {playlist} 中", @@ -3955,69 +3955,69 @@ "spotify_login_failed": "Spotify 登录失败:{error}。按 `d` 并选择 Spotify 重试。" }, "tools": { - "adding": "Adding {count} track(s) to {playlist}", - "brief_albums": "Top albums", - "brief_artists": "Top artists", - "brief_now": "\nCurrently playing: {name}\n", - "brief_recent": "Recently played (do not repeat these)", - "brief_tracks": "Top tracks", - "brief_vibe": "\nThe listener asked for: {name}\n", - "catalogue_unavailable": "spotatui has no Spotify session, so the catalogue is unavailable. Log in from the spotatui UI, then try again.", - "creating": "Creating the playlist \"{name}\" with {count} track(s). It is in list_playlists once Spotify has made it.", - "dropped": "spotatui dropped the request before answering", - "following": "Following spotify:artist:{artist}", + "adding": "正在向 {playlist} 添加 {count} 首歌曲", + "brief_albums": "最常听专辑", + "brief_artists": "最常听艺人", + "brief_now": "\n正在播放:{name}\n", + "brief_recent": "最近播放(不要重复)", + "brief_tracks": "最常听歌曲", + "brief_vibe": "\n听众想听:{name}\n", + "catalogue_unavailable": "spotatui 尚未登录 Spotify,无法访问曲库。请在 spotatui 界面中登录后重试。", + "creating": "正在创建播放列表“{name}”,包含 {count} 首歌曲。Spotify 创建后即可由 list_playlists 查看。", + "dropped": "spotatui 在回应前取消了请求", + "following": "正在关注 spotify:artist:{artist}", "found_track": "{name} — {artists} [{uri}] [{owned}]", - "history_failed": "Could not read listening history: {error}", - "history_sparse": "Very little listening history so far ({count} qualifying plays in {period}). Ask the user what they feel like instead of inferring it.", - "history_summary": "Listening history ({period}, {count} qualifying plays):", - "history_task_failed": "Listening history task failed: {error}", - "invalid_arguments": "Invalid arguments: {detail}", - "liking": "Liking {changed} track(s); {unchanged} already liked", - "missing_feature": "Nothing is playing that: this spotatui was built without the `{feature}` feature, so it has no source that can play {uri}. Playback was left alone.", - "missing_track": "Nothing is playing that: {uri} is not in the catalogue, so playback was left alone. Use search_tracks to get a URI that exists.", - "new_track": "new", - "no_tracks": "No tracks found for \"{query}\"", - "none_duplicate": "Already queued, playing, or recently played: {names}", - "none_found": "Could not find: {names}", - "none_new": "Every track was already queued or playing.", - "none_owned": "Already in your library: {names}", - "nothing_playing": "Nothing is playing ({count} track(s) queued)", - "nothing_queued": "Nothing was queued.\n{detail}", - "now_playing": "{state}: {label} ({count} track(s) queued)", - "owned": "owned", - "ownership_incomplete": "\n\nNote: the playlist index is still being built, so [owned] reflects Liked Songs only for this search. Search again for the complete answer.", - "paused": "paused", - "period_all": "all time", - "period_month": "the last 30 days", - "period_this_month": "this month", - "period_week": "the last 7 days", - "period_year": "this year", - "play_started": "Playing {label}", - "playing": "playing", - "queue_duplicate": "\nAlready queued, playing, or recently played (skipped): {names}", - "queue_empty": "The queue is empty", - "queue_filter_failed": "Could not read your playlists, so exclude_owned cannot be honoured and nothing was queued. Try again in a moment, or call queue_tracks without exclude_owned to queue these anyway.", - "queue_filter_spotify_only": "\nexclude_owned only checks Spotify tracks, so it skipped nothing here.", - "queue_not_found": "\n\nNot found (skipped): {names}", - "queue_owned": "\nAlready in your library (skipped by exclude_owned): {names}", - "queued": "Queued {count} track(s):", - "removing": "Removing {uri} at position {position} from {playlist}, if that is the track there", - "search_failed": "Search failed: {error}", - "sent": "Sent {uri} to the player", - "session_unavailable": "spotatui has no Spotify session. Log in from the spotatui UI, then try again.", - "shutting_down": "spotatui is shutting down; the request was not run", - "skipped": "Skipped to the next track", - "timed_out": "spotatui did not answer in time; it may be busy. Try again in a moment.", - "transfer": "Moving playback to {name}", - "uneditable": "\"{name}\" is not a playlist the user owns or collaborates on, so it cannot be edited", - "unfollowing": "Unfollowing spotify:artist:{artist}", - "unknown_tool": "There is no tool called {name}. Use one of the tools listed above.", - "unliking": "Unliking {changed} track(s); {unchanged} already not liked", - "vibe_cleared": "DJ vibe cleared", - "vibe_off": "The built-in DJ's auto-queue is off, so nothing will act on it until the user turns it on; queue tracks yourself in the meantime. get_listening_history returns it either way.", - "vibe_on": "The built-in DJ will follow it from its next refill.", - "vibe_set": "DJ vibe set to: {vibe}. {effect}", - "vibe_unavailable": "This build has no built-in auto-queue DJ, so nothing will act on it automatically. It is stored and returned by get_listening_history, so you can follow it yourself when you queue." + "history_failed": "无法读取收听历史:{error}", + "history_sparse": "收听历史较少({period}有 {count} 次有效播放)。请先询问用户想听什么。", + "history_summary": "收听历史({period},{count} 次有效播放):", + "history_task_failed": "收听历史任务失败:{error}", + "invalid_arguments": "参数无效:{detail}", + "liking": "正在喜欢 {changed} 首歌曲;{unchanged} 首已喜欢", + "missing_feature": "无法播放:此 spotatui 未编译 `{feature}` 功能,没有可播放 {uri} 的音源。当前播放未更改。", + "missing_track": "无法播放:曲库中没有 {uri},当前播放未更改。请用 search_tracks 获取有效 URI。", + "new_track": "未收藏", + "no_tracks": "未找到与“{query}”相关的歌曲", + "none_duplicate": "已在队列中、正在播放或最近播放过:{names}", + "none_found": "未找到:{names}", + "none_new": "所有歌曲都已在队列中或正在播放。", + "none_owned": "已在音乐库中:{names}", + "nothing_playing": "没有正在播放的内容(队列中有 {count} 首歌曲)", + "nothing_queued": "未加入任何歌曲。\n{detail}", + "now_playing": "{state}:{label}(队列中有 {count} 首歌曲)", + "owned": "已收藏", + "ownership_incomplete": "\n\n提示:播放列表索引仍在建立,本次搜索的 [已收藏] 仅表示喜欢的歌曲。稍后重新搜索可获得完整结果。", + "paused": "已暂停", + "period_all": "全部时间", + "period_month": "最近 30 天", + "period_this_month": "本月", + "period_week": "最近 7 天", + "period_year": "今年", + "play_started": "正在播放 {label}", + "playing": "正在播放", + "queue_duplicate": "\n已在队列中、正在播放或最近播放过(已跳过):{names}", + "queue_empty": "队列为空", + "queue_filter_failed": "无法读取播放列表以执行 exclude_owned,未加入任何歌曲。请稍后重试,或在 queue_tracks 中关闭 exclude_owned 后加入。", + "queue_filter_spotify_only": "\nexclude_owned 仅检查 Spotify 歌曲,本次未跳过任何歌曲。", + "queue_not_found": "\n\n未找到(已跳过):{names}", + "queue_owned": "\n已在音乐库中(因 exclude_owned 跳过):{names}", + "queued": "已加入 {count} 首歌曲:", + "removing": "正在从 {playlist} 的位置 {position} 移除 {uri}(仅在曲目相符时移除)", + "search_failed": "搜索失败:{error}", + "sent": "已将 {uri} 交给播放器", + "session_unavailable": "spotatui 尚未登录 Spotify。请在 spotatui 界面中登录后重试。", + "shutting_down": "spotatui 正在关闭,未执行此请求", + "skipped": "已跳至下一首", + "timed_out": "spotatui 未及时回应,可能正在忙碌。请稍后重试。", + "transfer": "正在将播放切换至 {name}", + "uneditable": "“{name}”不属于用户,也不是协作播放列表,无法编辑", + "unfollowing": "正在取消关注 spotify:artist:{artist}", + "unknown_tool": "没有名为 {name} 的工具。请使用上方列出的工具。", + "unliking": "正在取消喜欢 {changed} 首歌曲;{unchanged} 首原本未喜欢", + "vibe_cleared": "已清除 DJ 氛围", + "vibe_off": "内置 DJ 的自动补充队列已关闭,开启后才会生效;目前请手动加入歌曲。get_listening_history 仍会返回此氛围。", + "vibe_on": "内置 DJ 将从下次补充队列起遵循此氛围。", + "vibe_set": "DJ 氛围已设为:{vibe}。{effect}", + "vibe_unavailable": "此版本没有内置自动补充队列 DJ,不会自动应用此氛围。氛围已保存,可由 get_listening_history 读取后用于加入歌曲。" } } }, @@ -4855,38 +4855,38 @@ "waiting_guests": "等待聽眾加入…" }, "recap": { - "analytics": "Detailed Analytics", - "by_day": "Listening by Day", - "by_hour": "Listening by Hour", - "cover_art": "Cover Art", - "download": "Download Share Card", - "empty_summary": "No qualified local listening history was recorded for this window yet.", - "first_track": "NO. 1 TRACK", - "footer": "History is generated from spotatui local listening records and starts when the feature is enabled. Short or skipped plays are stored but excluded from headline recap totals.", - "generated_by": "generated by ", - "generated_by_product": "generated by spotatui", - "generating": "Generating...", - "language": "en", - "listened_at_template": "listened at {time}", - "listening_time": "Listening Time", - "no_albums": "No albums yet.", - "no_artists": "No artists yet.", - "no_data": "No data", - "no_data_yet": "No data yet.", - "no_recent": "No recent plays.", - "no_tracks": "No tracks yet.", - "page_title": "spotatui recap", - "qualified_plays": "Qualified Plays", - "recent_date_format": "%b %d, %H:%M", - "recent_plays": "Recent Plays", - "render_failed": "Could not render image. Please try again!", - "summary": "A shareable HTML snapshot of your qualified local listening history, generated directly from spotatui.", - "title": "Listening History Recap", - "top_album": "Top Album", - "top_albums": "Top Albums", - "top_artist": "Top Artist", - "top_artists": "Top Artists", - "top_tracks": "Top Tracks" + "analytics": "詳細統計", + "by_day": "每日聆聽時間", + "by_hour": "各時段聆聽時間", + "cover_art": "封面", + "download": "下載分享卡片", + "empty_summary": "此時間範圍還沒有有效的本機聆聽紀錄。", + "first_track": "最常聽歌曲", + "footer": "回顧根據 spotatui 本機聆聽紀錄產生,從啟用此功能時開始統計。短暫播放或跳過的歌曲會被記錄,但不計入回顧的主要統計。", + "generated_by": "產生自 ", + "generated_by_product": "由 spotatui 產生", + "generating": "正在產生…", + "language": "zh-Hant", + "listened_at_template": "播放於 {time}", + "listening_time": "聆聽時間", + "no_albums": "尚無專輯。", + "no_artists": "尚無藝人。", + "no_data": "尚無資料", + "no_data_yet": "尚無資料。", + "no_recent": "尚無最近播放紀錄。", + "no_tracks": "尚無歌曲。", + "page_title": "spotatui 聆聽回顧", + "qualified_plays": "有效播放次數", + "recent_date_format": "%m月%d日 %H:%M", + "recent_plays": "最近播放", + "render_failed": "無法產生圖片,請重試!", + "summary": "由 spotatui 直接產生的本機聆聽回顧,可作為 HTML 頁面分享。", + "title": "聆聽紀錄回顧", + "top_album": "最常聽專輯", + "top_albums": "最常聽專輯", + "top_artist": "最常聽藝人", + "top_artists": "最常聽藝人", + "top_tracks": "最常聽歌曲" }, "screens": { "account_required": "需要帳號", @@ -5687,37 +5687,37 @@ "cast_no_reply": "{kind} 未收到回覆", "cast_no_tls": "與 {address} 的 TLS 交握未完成", "cast_receiver_failed": "Spotify 接收器未啟動:{reason}", - "dj_duplicate": "{count} already queued", - "dj_error": "DJ error ({backend}): {error}", - "dj_index_capped": " (stopped at {count}; the rest is not filtered)", - "dj_indexed": "Indexed {tracks} track(s) across {playlists} playlist(s)", - "dj_library_failed": "DJ: could not read your playlists ({error}); recommending unfiltered", - "dj_not_found": "{count} not found", - "dj_nothing_queued": "DJ: nothing was queued that time. Ask again, or try a different direction.", - "dj_owned": "{count} already yours", - "dj_queued": "queued {count}", - "dj_status": "DJ: {summary}", - "dj_steps_exhausted": "Stopped after {count} steps. Ask again if that is not where you wanted to end up.", - "dj_topped_up": "DJ: topped up the queue", + "dj_duplicate": "已在佇列中 {count} 首", + "dj_error": "DJ 錯誤({backend}):{error}", + "dj_index_capped": "(已在 {count} 首處停止,其餘曲目未篩選)", + "dj_indexed": "已索引 {playlists} 個播放清單中的 {tracks} 首曲目", + "dj_library_failed": "DJ:無法讀取你的播放清單({error}),將不加篩選地推薦", + "dj_not_found": "找不到 {count} 首", + "dj_nothing_queued": "DJ:這次未加入任何曲目。請再試一次,或換個方向。", + "dj_owned": "已擁有 {count} 首", + "dj_queued": "已加入佇列 {count} 首", + "dj_status": "DJ:{summary}", + "dj_steps_exhausted": "已在 {count} 步後停止。如果結果不符合預期,請再次提問。", + "dj_topped_up": "DJ:已補充佇列", "fetching": "正在取得 {name}…", - "friend_add_failed": "Could not add friend: {error}", - "friend_added": "Added friend", - "friend_added_code": "Added friend (code: {code})", - "friend_unfollow_failed": "Failed to unfollow: {error}", - "friend_unfollowed": "Unfollowed", - "history_all": "All Time", - "history_failed": "failed to load listening history: {error}", - "history_hours_minutes": "{hours}h {minutes}m", - "history_minutes": "{minutes}m", - "history_month": "Last 30 Days", - "history_play": "{count} play · {duration}", - "history_plays": "{count} plays · {duration}", - "history_this_month": "This Month", - "history_this_year": "This Year", - "history_track_hit": "{count} track hit · {duration}", - "history_track_hits": "{count} track hits · {duration}", - "history_week": "Last 7 Days", - "liked_check_failed": "Could not check liked track state: {error}", + "friend_add_failed": "無法新增好友:{error}", + "friend_added": "已新增好友", + "friend_added_code": "已新增好友(代碼:{code})", + "friend_unfollow_failed": "取消追蹤失敗:{error}", + "friend_unfollowed": "已取消追蹤", + "history_all": "全部時間", + "history_failed": "無法載入聆聽紀錄:{error}", + "history_hours_minutes": "{hours} 小時 {minutes} 分鐘", + "history_minutes": "{minutes} 分鐘", + "history_month": "最近 30 天", + "history_play": "{count} 次播放 · {duration}", + "history_plays": "{count} 次播放 · {duration}", + "history_this_month": "本月", + "history_this_year": "今年", + "history_track_hit": "曲目播放 {count} 次 · {duration}", + "history_track_hits": "曲目播放 {count} 次 · {duration}", + "history_week": "最近 7 天", + "liked_check_failed": "無法檢查曲目的喜歡狀態:{error}", "local_audio_unavailable": "本機播放無音訊輸出:{error}", "local_exhausted": "{message} — 已無可播放曲目,停止播放", "local_no_folder": "尚未設定本機音樂資料夾(請設定 behavior.local_music_path)", @@ -5732,10 +5732,10 @@ "local_tracks_scan_failed": "本機曲目掃描失敗:{error}", "local_unknown_track": "未知曲目", "local_uri_invalid": "本機檔案 URI 無效:{error}", - "made_for_you_missing": "Spotify did not send the Made For You page", - "made_for_you_unavailable": "Made For You is unavailable: every known browsePage hash is retired", - "mcp_status": "MCP: {message}", - "monthly_recap_ready": "Monthly listening recap ready at {path} ({count} listens)", + "made_for_you_missing": "Spotify 未傳回為你精選頁面", + "made_for_you_unavailable": "為你精選無法使用:所有已知的 browsePage 查詢識別碼均已失效", + "mcp_status": "MCP:{message}", + "monthly_recap_ready": "每月聆聽回顧已產生於 {path}({count} 次聆聽)", "music_busy": "Music 忙碌中,請重試請求", "music_busy_playback": "Music 忙碌中,請重試播放請求", "music_loaded": "Apple Music:已載入 {count} 項", @@ -5773,27 +5773,27 @@ "native_start_failed": "啟動內建串流失敗:{error}", "native_stopped": "內建串流已停止。", "native_stopped_recovering": "內建串流已停止,正在嘗試恢復。", - "network_error": "{error}", - "network_rate_error": "{error}", - "other_source_playing": "Another source owns playback", - "party_disconnected": "Listening Party ended: Spotify is not connected.", - "party_ended": "Party ended", - "party_error": "Party: {error}", - "party_join_failed": "Failed to join party: {error}", - "party_joined": "{name} joined the party", - "party_left": "{name} left the party", - "party_start_failed": "Failed to start party: {error}", - "playlist_added": "Added to playlist", - "playlist_added_named": "Added to {name}", - "playlist_changed": "The playlist changed since it loaded; open it again and retry", - "playlist_created": "Playlist \"{name}\" created!", - "playlist_created_no_id": "Playlist created but could not get its ID", - "playlist_external_unavailable": "Spotify Development Mode blocks playlist contents owned by another user. Only playlists you own or collaborate on are available.", - "playlist_id_invalid": "Invalid playlist identifier.", - "playlist_refresh_partial": "Playlist refresh incomplete; kept the previous list.", - "playlist_removed": "Removed from playlist", - "playlist_search_matches": "{count} playlist tracks match \"{query}\"{scope}", - "playlist_search_scope": " in the first {count} tracks", + "network_error": "Spotify 請求失敗:{error}", + "network_rate_error": "Spotify 請求受限:{error}", + "other_source_playing": "其他來源正在播放", + "party_disconnected": "一起聽已結束:Spotify 未連線。", + "party_ended": "一起聽已結束", + "party_error": "一起聽:{error}", + "party_join_failed": "加入一起聽失敗:{error}", + "party_joined": "{name} 加入了一起聽", + "party_left": "{name} 離開了一起聽", + "party_start_failed": "發起一起聽失敗:{error}", + "playlist_added": "已加入播放清單", + "playlist_added_named": "已加入 {name}", + "playlist_changed": "播放清單在載入後發生了變更,請重新開啟後再試", + "playlist_created": "播放清單「{name}」已建立!", + "playlist_created_no_id": "播放清單已建立,但無法取得其 ID", + "playlist_external_unavailable": "Spotify Development Mode 禁止讀取其他使用者擁有的播放清單內容。只能存取你擁有或參與協作的播放清單。", + "playlist_id_invalid": "播放清單識別碼無效。", + "playlist_refresh_partial": "播放清單重新整理不完整,已保留原清單。", + "playlist_removed": "已從播放清單移除", + "playlist_search_matches": "播放清單中有 {count} 首曲目符合「{query}」{scope}", + "playlist_search_scope": "(僅搜尋前 {count} 首曲目)", "qobuz_audio_unavailable": "Qobuz 播放無音訊輸出:{error}", "qobuz_browser_login": "Qobuz:請開啟瀏覽器視窗登入", "qobuz_logged_in": "Qobuz:已登入", @@ -5834,68 +5834,68 @@ "radio_search_failed": "電台搜尋失敗:{error}", "radio_task_failed": "電台播放工作失敗:{error}", "radio_uri_invalid": "電台 URI 無效", - "recap_created": "Listening recap generated at {path} ({count} listens)", - "recap_failed": "Failed to generate recap: {error}", - "search_query_unavailable": "Search is unavailable: every known {operation} hash is retired", - "setup_agent_subscription": "uses that CLI's own subscription", - "setup_agy_plan": "uses your Antigravity plan", - "setup_anthropic": "anthropic (API key)", - "setup_anthropic_billing": "Anthropic API, pay per token", - "setup_builtin_default": "Use the built-in default ({model})", - "setup_cheapest": "cheapest", - "setup_claude_plan": "uses your Claude Pro/Max plan", - "setup_cli_default": "Use the CLI's default", - "setup_codex_plan": "uses your ChatGPT plan", - "setup_copilot_plan": "uses your GitHub Copilot plan", - "setup_current": "currently configured", - "setup_custom": "Custom…", - "setup_fable_note": "heaviest, hits it faster still", - "setup_gemini_legacy": "legacy Gemini CLI, superseded by agy", - "setup_haiku_note": "cheapest, easiest on a Pro plan", - "setup_heaviest_all": "heaviest of all", - "setup_heaviest_gemini": "heaviest Gemini", - "setup_keep_as_is": "Keep this command as it is", - "setup_keep_command": "{command} (keep)", - "setup_needs_key": "needs {variable} (or behavior.dj_api_key)", - "setup_no_api_key": "{agent} (no API key)", - "setup_no_model_flag": "no model flag", - "setup_not_installed": " · not on PATH", - "setup_openai": "openai_compat (local or self-hosted)", - "setup_openai_endpoint": "any OpenAI-compatible endpoint, e.g. Ollama on localhost", - "setup_opencode_plan": "uses whatever provider opencode is logged into", - "setup_opus_note": "heavy, a Pro plan hits its limit fast", - "setup_own_command": "your own command, run exactly as written", - "setup_own_model_note": "spotatui adds no model flag to a command it does not own", - "setup_price_fable": "$10/$50 per MTok, 1M ctx", - "setup_price_haiku": "$1/$5 per MTok, 200K ctx", - "setup_price_opus": "$5/$25 per MTok, 1M ctx", - "setup_price_sonnet": "$3/$15 per MTok, 1M ctx", - "setup_sonnet_note": "balanced", - "setup_type_model": "type a model name", - "setup_type_provider_model": "type a model name, as provider/model", - "shuffle_capped": "Large context: shuffling the first {count} tracks", - "shuffle_context_failed": "Shuffle: failed to load the context's track list", - "shuffle_not_applied": "Playback started; couldn't apply shuffle.", + "recap_created": "聆聽回顧已產生於 {path}({count} 次聆聽)", + "recap_failed": "產生聆聽回顧失敗:{error}", + "search_query_unavailable": "搜尋無法使用:所有已知的 {operation} 查詢識別碼均已失效", + "setup_agent_subscription": "使用該 CLI 自己的訂閱", + "setup_agy_plan": "使用你的 Antigravity 方案", + "setup_anthropic": "anthropic(API key)", + "setup_anthropic_billing": "Anthropic API,依 token 計費", + "setup_builtin_default": "使用內建預設模型({model})", + "setup_cheapest": "最省費用", + "setup_claude_plan": "使用你的 Claude Pro/Max 方案", + "setup_cli_default": "使用 CLI 的預設模型", + "setup_codex_plan": "使用你的 ChatGPT 方案", + "setup_copilot_plan": "使用你的 GitHub Copilot 方案", + "setup_current": "目前設定", + "setup_custom": "自訂…", + "setup_fable_note": "用量最大,更容易用滿額度", + "setup_gemini_legacy": "舊版 Gemini CLI,已由 agy 取代", + "setup_haiku_note": "最省費用,對 Pro 方案額度負擔最小", + "setup_heaviest_all": "用量最大", + "setup_heaviest_gemini": "Gemini 中用量最大", + "setup_keep_as_is": "保留此指令不變", + "setup_keep_command": "{command}(保留)", + "setup_needs_key": "需要 {variable}(或 behavior.dj_api_key)", + "setup_no_api_key": "{agent}(不需 API key)", + "setup_no_model_flag": "不傳入模型參數", + "setup_not_installed": " · 不在 PATH 中", + "setup_openai": "openai_compat(本機或自行架設)", + "setup_openai_endpoint": "任何相容 OpenAI 的端點,例如 localhost 上的 Ollama", + "setup_opencode_plan": "使用 opencode 目前登入的服務供應商", + "setup_opus_note": "用量較大,Pro 方案容易用滿額度", + "setup_own_command": "使用你自己的指令,依原樣執行", + "setup_own_model_note": "spotatui 不會向自訂指令新增模型參數", + "setup_price_fable": "每百萬 token $10/$50,1M 上下文", + "setup_price_haiku": "每百萬 token $1/$5,200K 上下文", + "setup_price_opus": "每百萬 token $5/$25,1M 上下文", + "setup_price_sonnet": "每百萬 token $3/$15,1M 上下文", + "setup_sonnet_note": "均衡", + "setup_type_model": "輸入模型名稱", + "setup_type_provider_model": "輸入模型名稱,格式為 provider/model", + "shuffle_capped": "清單較大:隨機播放前 {count} 首曲目", + "shuffle_context_failed": "隨機播放:無法載入所屬清單的曲目", + "shuffle_not_applied": "已開始播放,但無法啟用隨機播放。", "source_playback_unavailable": "此版本不支援 {source} 播放", "speaker_client_id_missing": "getInfo 中缺少 clientID", "speaker_login_refused": "喇叭拒絕登入:{message}({error})", "speaker_not_joined": "{name} 登入後未連線", "speaker_token_failed": "權杖交換失敗({status}):{error}", "speaker_token_unsupported": "喇叭要求的權杖類型 {kind} 尚未支援", - "spotify_api_error": "Spotify API {status} failed: {body}", - "spotify_connected": "Spotify connected. Restart spotatui to enable native playback.", - "spotify_login_failed": "Spotify login failed: {error}", - "spotify_login_timeout": "Spotify login timed out. Press `d` and pick Spotify to try again.", + "spotify_api_error": "Spotify API {status} 請求失敗:{body}", + "spotify_connected": "Spotify 已連線。重新啟動 spotatui 以啟用內建串流。", + "spotify_login_failed": "Spotify 登入失敗:{error}", + "spotify_login_timeout": "Spotify 登入逾時。請按 `d` 並選擇 Spotify 重試。", "spotify_not_compiled": "此版本的 spotatui 不支援 Spotify", - "spotify_not_connected": "Spotify not connected. Press `d` and pick Spotify to log in.", - "spotify_pair": "Log in to Spotify: open {url} and confirm the code {code}", - "spotify_playing_elsewhere": "Playback remains on another Spotify device.", - "spotify_poll_rejected": "Spotify keeps rejecting the playback poll; retrying automatically.", - "spotify_poll_retry": "Temporary Spotify network error while polling playback; retrying automatically.", - "spotify_profile_retry": "Spotify rate limit hit while loading profile. Retrying automatically.", - "spotify_rate_retry": "Spotify rate limit hit. Retrying automatically; please wait a few seconds.", - "spotify_rate_wait": "Spotify rate limit: waiting {seconds}s", - "spotify_server_retry": "Spotify server temporarily unavailable (5xx); retrying automatically.", + "spotify_not_connected": "Spotify 未連線。請按 `d` 並選擇 Spotify 登入。", + "spotify_pair": "登入 Spotify:開啟 {url} 並確認配對碼 {code}", + "spotify_playing_elsewhere": "仍在另一台 Spotify 裝置上播放。", + "spotify_poll_rejected": "Spotify 持續拒絕播放狀態請求,正在自動重試。", + "spotify_poll_retry": "取得播放狀態時發生暫時性 Spotify 網路錯誤,正在自動重試。", + "spotify_profile_retry": "載入個人資料時 Spotify 請求受限,正在自動重試。", + "spotify_rate_retry": "Spotify 請求受限。正在自動重試,請稍候。", + "spotify_rate_wait": "Spotify 請求受限:等待 {seconds} 秒", + "spotify_server_retry": "Spotify 伺服器暫時無法使用(5xx),正在自動重試。", "subsonic_audio_unavailable": "Subsonic 播放無音訊輸出:{error}", "subsonic_download_failed": "無法下載 Subsonic 曲目:{error}", "subsonic_no_server": "尚未設定 Subsonic 伺服器(請設定 behavior.subsonic_url)", @@ -5907,23 +5907,23 @@ "subsonic_search_failed": "Subsonic 搜尋失敗:{error}", "subsonic_task_failed": "Subsonic 播放工作失敗:{error}", "subsonic_uri_invalid": "Subsonic 曲目 URI 無效", - "sync_busy": "Playlist sync already running", - "sync_dry_run": " (dry run)", - "sync_failed": "Playlist sync failed: {error}", - "sync_link_failed": "Playlist link failed: {error}", - "sync_no_links": "Playlist sync: no links", - "sync_progress": "Playlist sync: {name} {done}/{total}", - "sync_qobuz_not_logged_in": "Qobuz is not logged in", - "sync_remove_failed": "Removing the link failed: {error}", - "sync_removed": "Playlist link removed; the mirror playlists stay", - "sync_source_not_compiled": "{source} is not compiled into this build (feature `{feature}`)", - "sync_source_unreachable": "{source} unreachable: {error}", - "sync_source_unsupported": "{source} playlists cannot be synced", - "sync_spotify_not_connected": "Spotify is not connected", - "sync_subsonic_not_configured": "No Subsonic server configured (set behavior.subsonic_url)", - "sync_summary": "Playlist sync{dry}: {added} added, {removed} removed, {unmatched} unmatched", - "sync_using_playlist": "Using the existing {source} playlist \"{name}\"", - "unnamed_folder": "Unnamed Folder", + "sync_busy": "播放清單同步已在執行", + "sync_dry_run": "(模擬執行)", + "sync_failed": "播放清單同步失敗:{error}", + "sync_link_failed": "播放清單連結失敗:{error}", + "sync_no_links": "播放清單同步:沒有連結", + "sync_progress": "播放清單同步:{name} {done}/{total}", + "sync_qobuz_not_logged_in": "Qobuz 尚未登入", + "sync_remove_failed": "移除連結失敗:{error}", + "sync_removed": "播放清單連結已移除,鏡像播放清單保留", + "sync_source_not_compiled": "此版本未編譯 {source}(功能 `{feature}`)", + "sync_source_unreachable": "無法連線至 {source}:{error}", + "sync_source_unsupported": "無法同步 {source} 播放清單", + "sync_spotify_not_connected": "Spotify 未連線", + "sync_subsonic_not_configured": "尚未設定 Subsonic 伺服器(請設定 behavior.subsonic_url)", + "sync_summary": "播放清單同步{dry}:新增 {added} 首,移除 {removed} 首,未對應 {unmatched} 首", + "sync_using_playlist": "使用現有 {source} 播放清單「{name}」", + "unnamed_folder": "未命名資料夾", "youtube_add_failed": "無法將影片加入播放清單:{error}", "youtube_added": "已將「{name}」加入 {playlist}", "youtube_already_added": "「{name}」已在 {playlist} 中", @@ -5966,69 +5966,69 @@ "spotify_login_failed": "Spotify 登入失敗:{error}。按 `d` 並選擇 Spotify 重試。" }, "tools": { - "adding": "Adding {count} track(s) to {playlist}", - "brief_albums": "Top albums", - "brief_artists": "Top artists", - "brief_now": "\nCurrently playing: {name}\n", - "brief_recent": "Recently played (do not repeat these)", - "brief_tracks": "Top tracks", - "brief_vibe": "\nThe listener asked for: {name}\n", - "catalogue_unavailable": "spotatui has no Spotify session, so the catalogue is unavailable. Log in from the spotatui UI, then try again.", - "creating": "Creating the playlist \"{name}\" with {count} track(s). It is in list_playlists once Spotify has made it.", - "dropped": "spotatui dropped the request before answering", - "following": "Following spotify:artist:{artist}", + "adding": "正在向 {playlist} 新增 {count} 首歌曲", + "brief_albums": "最常聽專輯", + "brief_artists": "最常聽藝人", + "brief_now": "\n正在播放:{name}\n", + "brief_recent": "最近播放(不要重複)", + "brief_tracks": "最常聽歌曲", + "brief_vibe": "\n聽眾想聽:{name}\n", + "catalogue_unavailable": "spotatui 尚未登入 Spotify,無法存取曲庫。請在 spotatui 介面中登入後重試。", + "creating": "正在建立播放清單「{name}」,包含 {count} 首歌曲。Spotify 建立後即可由 list_playlists 查看。", + "dropped": "spotatui 在回應前取消了請求", + "following": "正在追蹤 spotify:artist:{artist}", "found_track": "{name} — {artists} [{uri}] [{owned}]", - "history_failed": "Could not read listening history: {error}", - "history_sparse": "Very little listening history so far ({count} qualifying plays in {period}). Ask the user what they feel like instead of inferring it.", - "history_summary": "Listening history ({period}, {count} qualifying plays):", - "history_task_failed": "Listening history task failed: {error}", - "invalid_arguments": "Invalid arguments: {detail}", - "liking": "Liking {changed} track(s); {unchanged} already liked", - "missing_feature": "Nothing is playing that: this spotatui was built without the `{feature}` feature, so it has no source that can play {uri}. Playback was left alone.", - "missing_track": "Nothing is playing that: {uri} is not in the catalogue, so playback was left alone. Use search_tracks to get a URI that exists.", - "new_track": "new", - "no_tracks": "No tracks found for \"{query}\"", - "none_duplicate": "Already queued, playing, or recently played: {names}", - "none_found": "Could not find: {names}", - "none_new": "Every track was already queued or playing.", - "none_owned": "Already in your library: {names}", - "nothing_playing": "Nothing is playing ({count} track(s) queued)", - "nothing_queued": "Nothing was queued.\n{detail}", - "now_playing": "{state}: {label} ({count} track(s) queued)", - "owned": "owned", - "ownership_incomplete": "\n\nNote: the playlist index is still being built, so [owned] reflects Liked Songs only for this search. Search again for the complete answer.", - "paused": "paused", - "period_all": "all time", - "period_month": "the last 30 days", - "period_this_month": "this month", - "period_week": "the last 7 days", - "period_year": "this year", - "play_started": "Playing {label}", - "playing": "playing", - "queue_duplicate": "\nAlready queued, playing, or recently played (skipped): {names}", - "queue_empty": "The queue is empty", - "queue_filter_failed": "Could not read your playlists, so exclude_owned cannot be honoured and nothing was queued. Try again in a moment, or call queue_tracks without exclude_owned to queue these anyway.", - "queue_filter_spotify_only": "\nexclude_owned only checks Spotify tracks, so it skipped nothing here.", - "queue_not_found": "\n\nNot found (skipped): {names}", - "queue_owned": "\nAlready in your library (skipped by exclude_owned): {names}", - "queued": "Queued {count} track(s):", - "removing": "Removing {uri} at position {position} from {playlist}, if that is the track there", - "search_failed": "Search failed: {error}", - "sent": "Sent {uri} to the player", - "session_unavailable": "spotatui has no Spotify session. Log in from the spotatui UI, then try again.", - "shutting_down": "spotatui is shutting down; the request was not run", - "skipped": "Skipped to the next track", - "timed_out": "spotatui did not answer in time; it may be busy. Try again in a moment.", - "transfer": "Moving playback to {name}", - "uneditable": "\"{name}\" is not a playlist the user owns or collaborates on, so it cannot be edited", - "unfollowing": "Unfollowing spotify:artist:{artist}", - "unknown_tool": "There is no tool called {name}. Use one of the tools listed above.", - "unliking": "Unliking {changed} track(s); {unchanged} already not liked", - "vibe_cleared": "DJ vibe cleared", - "vibe_off": "The built-in DJ's auto-queue is off, so nothing will act on it until the user turns it on; queue tracks yourself in the meantime. get_listening_history returns it either way.", - "vibe_on": "The built-in DJ will follow it from its next refill.", - "vibe_set": "DJ vibe set to: {vibe}. {effect}", - "vibe_unavailable": "This build has no built-in auto-queue DJ, so nothing will act on it automatically. It is stored and returned by get_listening_history, so you can follow it yourself when you queue." + "history_failed": "無法讀取聆聽紀錄:{error}", + "history_sparse": "聆聽紀錄較少({period}有 {count} 次有效播放)。請先詢問使用者想聽什麼。", + "history_summary": "聆聽紀錄({period},{count} 次有效播放):", + "history_task_failed": "聆聽紀錄工作失敗:{error}", + "invalid_arguments": "參數無效:{detail}", + "liking": "正在喜歡 {changed} 首歌曲;{unchanged} 首已喜歡", + "missing_feature": "無法播放:此 spotatui 未編譯 `{feature}` 功能,沒有可播放 {uri} 的來源。目前播放未變更。", + "missing_track": "無法播放:曲庫中沒有 {uri},目前播放未變更。請用 search_tracks 取得有效 URI。", + "new_track": "未收藏", + "no_tracks": "未找到與「{query}」相關的歌曲", + "none_duplicate": "已在佇列中、正在播放或最近播放過:{names}", + "none_found": "未找到:{names}", + "none_new": "所有歌曲都已在佇列中或正在播放。", + "none_owned": "已在音樂庫中:{names}", + "nothing_playing": "沒有正在播放的內容(佇列中有 {count} 首歌曲)", + "nothing_queued": "未加入任何歌曲。\n{detail}", + "now_playing": "{state}:{label}(佇列中有 {count} 首歌曲)", + "owned": "已收藏", + "ownership_incomplete": "\n\n提示:播放清單索引仍在建立,本次搜尋的 [已收藏] 僅表示喜歡的歌曲。稍後重新搜尋可取得完整結果。", + "paused": "已暫停", + "period_all": "全部時間", + "period_month": "最近 30 天", + "period_this_month": "本月", + "period_week": "最近 7 天", + "period_year": "今年", + "play_started": "正在播放 {label}", + "playing": "正在播放", + "queue_duplicate": "\n已在佇列中、正在播放或最近播放過(已略過):{names}", + "queue_empty": "佇列為空", + "queue_filter_failed": "無法讀取播放清單以執行 exclude_owned,未加入任何歌曲。請稍後重試,或在 queue_tracks 中關閉 exclude_owned 後加入。", + "queue_filter_spotify_only": "\nexclude_owned 僅檢查 Spotify 歌曲,本次未略過任何歌曲。", + "queue_not_found": "\n\n未找到(已略過):{names}", + "queue_owned": "\n已在音樂庫中(因 exclude_owned 略過):{names}", + "queued": "已加入 {count} 首歌曲:", + "removing": "正在從 {playlist} 的位置 {position} 移除 {uri}(僅在曲目相符時移除)", + "search_failed": "搜尋失敗:{error}", + "sent": "已將 {uri} 交給播放器", + "session_unavailable": "spotatui 尚未登入 Spotify。請在 spotatui 介面中登入後重試。", + "shutting_down": "spotatui 正在關閉,未執行此請求", + "skipped": "已跳至下一首", + "timed_out": "spotatui 未及時回應,可能正在忙碌。請稍後重試。", + "transfer": "正在將播放切換至 {name}", + "uneditable": "「{name}」不屬於使用者,也不是協作播放清單,無法編輯", + "unfollowing": "正在取消追蹤 spotify:artist:{artist}", + "unknown_tool": "沒有名為 {name} 的工具。請使用上方列出的工具。", + "unliking": "正在取消喜歡 {changed} 首歌曲;{unchanged} 首原本未喜歡", + "vibe_cleared": "已清除 DJ 氛圍", + "vibe_off": "內建 DJ 的自動補充佇列已關閉,開啟後才會生效;目前請手動加入歌曲。get_listening_history 仍會傳回此氛圍。", + "vibe_on": "內建 DJ 將從下次補充佇列起遵循此氛圍。", + "vibe_set": "DJ 氛圍已設為:{vibe}。{effect}", + "vibe_unavailable": "此版本沒有內建自動補充佇列 DJ,不會自動套用此氛圍。氛圍已儲存,可由 get_listening_history 讀取後用於加入歌曲。" } } } diff --git a/src/core/i18n/recap.rs b/src/core/i18n/recap.rs index de189adb..296097a7 100644 --- a/src/core/i18n/recap.rs +++ b/src/core/i18n/recap.rs @@ -74,11 +74,79 @@ pub const EN: RecapStrings = RecapStrings { // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] -pub const ZH_HANS: RecapStrings = RecapStrings { ..EN }; +pub const ZH_HANS: RecapStrings = RecapStrings { + language: "zh-Hans", + page_title: "spotatui 听歌回顾", + title: "听歌记录回顾", + cover_art: "封面", + first_track: "最常听歌曲", + qualified_plays: "有效播放次数", + listening_time: "听歌时长", + top_artist: "最常听艺人", + top_album: "最常听专辑", + generated_by: "生成自 ", + generated_by_product: "由 spotatui 生成", + download: "下载分享卡片", + analytics: "详细统计", + top_tracks: "最常听歌曲", + top_artists: "最常听艺人", + top_albums: "最常听专辑", + recent_plays: "最近播放", + by_day: "每日听歌时长", + by_hour: "各时段听歌时长", + footer: "回顾根据 spotatui 本地听歌记录生成,从启用此功能时开始统计。短时播放或跳过的歌曲会被记录,但不计入回顾的主要统计。", + generating: "正在生成…", + render_failed: "无法生成图片,请重试!", + empty_summary: "此时间段还没有有效的本地听歌记录。", + summary: "由 spotatui 直接生成的本地听歌回顾,可作为 HTML 页面分享。", + no_tracks: "暂无歌曲。", + no_artists: "暂无艺人。", + no_albums: "暂无专辑。", + no_recent: "暂无最近播放记录。", + no_data_yet: "暂无数据。", + no_data: "暂无数据", + listened_at_template: "播放于 {time}", + recent_date_format: "%m月%d日 %H:%M", + ..EN +}; // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] -pub const ZH_HANT: RecapStrings = RecapStrings { ..EN }; +pub const ZH_HANT: RecapStrings = RecapStrings { + language: "zh-Hant", + page_title: "spotatui 聆聽回顧", + title: "聆聽紀錄回顧", + cover_art: "封面", + first_track: "最常聽歌曲", + qualified_plays: "有效播放次數", + listening_time: "聆聽時間", + top_artist: "最常聽藝人", + top_album: "最常聽專輯", + generated_by: "產生自 ", + generated_by_product: "由 spotatui 產生", + download: "下載分享卡片", + analytics: "詳細統計", + top_tracks: "最常聽歌曲", + top_artists: "最常聽藝人", + top_albums: "最常聽專輯", + recent_plays: "最近播放", + by_day: "每日聆聽時間", + by_hour: "各時段聆聽時間", + footer: "回顧根據 spotatui 本機聆聽紀錄產生,從啟用此功能時開始統計。短暫播放或跳過的歌曲會被記錄,但不計入回顧的主要統計。", + generating: "正在產生…", + render_failed: "無法產生圖片,請重試!", + empty_summary: "此時間範圍還沒有有效的本機聆聽紀錄。", + summary: "由 spotatui 直接產生的本機聆聽回顧,可作為 HTML 頁面分享。", + no_tracks: "尚無歌曲。", + no_artists: "尚無藝人。", + no_albums: "尚無專輯。", + no_recent: "尚無最近播放紀錄。", + no_data_yet: "尚無資料。", + no_data: "尚無資料", + listened_at_template: "播放於 {time}", + recent_date_format: "%m月%d日 %H:%M", + ..EN +}; impl RecapStrings { pub fn listened_at(&self, time: &str) -> String { diff --git a/src/core/i18n/status_infra.rs b/src/core/i18n/status_infra.rs index 6557789b..eb5851e0 100644 --- a/src/core/i18n/status_infra.rs +++ b/src/core/i18n/status_infra.rs @@ -1035,6 +1035,17 @@ pub const EN: InfraStatusStrings = InfraStatusStrings { // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { + sync_spotify_not_connected: "Spotify 未连接", + sync_qobuz_not_logged_in: "Qobuz 未登录", + sync_subsonic_not_configured: "尚未配置 Subsonic 服务器(请设置 behavior.subsonic_url)", + sync_source_not_compiled: "此版本未编译 {source}(功能 `{feature}`)", + sync_source_unsupported: "无法同步 {source} 播放列表", + sync_source_unreachable: "无法连接 {source}:{error}", + search_query_unavailable: "搜索不可用:所有已知的 {operation} 查询标识均已失效", + made_for_you_unavailable: "为你精选不可用:所有已知的 browsePage 查询标识均已失效", + made_for_you_missing: "Spotify 未返回为你精选页面", + history_track_hit: "曲目播放 {count} 次 · {duration}", + history_play: "{count} 次播放 · {duration}", cast_no_answer: "{address} 没有响应", cast_no_tls: "与 {address} 的 TLS 握手未完成", cast_message_size: "Cast 消息大小为 {size} 字节", @@ -1049,6 +1060,8 @@ pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { speaker_client_id_missing: "getInfo 中缺少 clientID", speaker_login_refused: "音箱拒绝登录:{message}({error})", speaker_not_joined: "{name} 登录后未连接", + playlist_changed: "播放列表在加载后发生了变化,请重新打开后再试", + spotify_api_error: "Spotify API {status} 请求失败:{body}", local_replay_failed: "无法重新播放本地文件", local_no_tracks: "没有可播放的本地曲目", local_no_folder: "尚未设置本地音乐文件夹(请设置 behavior.local_music_path)", @@ -1144,21 +1157,63 @@ pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { queue_restore_failed: "无法恢复 {name}:{error}", queue_playing: "♪ {name}(队列)", queue_audio_unavailable: "队列播放无音频输出:{error}", + spotify_not_connected: "Spotify 未连接。请按 `d` 并选择 Spotify 登录。", + playlist_id_invalid: "播放列表标识无效。", music_requires_macos: "Apple Music 需要 macOS 和 apple-music 功能", spotify_not_compiled: "此版本的 spotatui 不支持 Spotify", + spotify_connected: "Spotify 已连接。重启 spotatui 以启用内置播放。", + party_disconnected: "一起听已结束:Spotify 未连接。", + party_ended: "一起听已结束", + spotify_login_timeout: "Spotify 登录超时。请按 `d` 并选择 Spotify 重试。", + spotify_login_failed: "Spotify 登录失败:{error}", + spotify_rate_wait: "Spotify 请求受限:等待 {seconds} 秒", + spotify_pair: "登录 Spotify:打开 {url} 并确认配对码 {code}", + recap_created: "收听回顾已生成于 {path}({count} 次收听)", + history_failed: "无法加载收听历史:{error}", + recap_failed: "生成收听回顾失败:{error}", + party_start_failed: "发起一起听失败:{error}", + party_join_failed: "加入一起听失败:{error}", + party_error: "一起听:{error}", + party_joined: "{name} 加入了一起听", + party_left: "{name} 离开了一起听", native_reconnecting: "正在重新连接内置播放…", native_settling: "内置播放仍在准备中…", + shuffle_not_applied: "已开始播放,但无法启用随机播放。", + spotify_rate_retry: "Spotify 请求受限。正在自动重试,请稍候。", + spotify_poll_retry: "获取播放状态时发生临时 Spotify 网络错误,正在自动重试。", + spotify_server_retry: "Spotify 服务器暂时不可用(5xx),正在自动重试。", + spotify_poll_rejected: "Spotify 持续拒绝播放状态请求,正在自动重试。", native_resuming: "正在此设备恢复上次播放…", native_reconnect_resume: "正在重新连接内置播放,随后将恢复播放。", native_connection_restored: "内置播放连接已恢复,正在恢复播放。", native_no_state: "内置播放连接已恢复,但没有可恢复的播放状态。", native_restore_retry: "内置播放恢复失败,正在再次连接。", + other_source_playing: "其他音源正在播放", + spotify_playing_elsewhere: "仍在另一台 Spotify 设备上播放。", queue_added: "已加入队列", queue_load_failed: "无法加载队列(没有活动设备?)", native_nothing_to_resume: "{device} 上没有可恢复的播放。", native_start_failed: "启动内置播放失败:{error}", native_shuffle_failed: "启用随机播放失败:{error}", native_resume_failed: "恢复播放失败:{error}", + shuffle_context_failed: "随机播放:无法加载所属列表的曲目", + shuffle_capped: "列表较大:随机播放前 {count} 首曲目", + spotify_profile_retry: "加载个人资料时 Spotify 请求受限,正在自动重试。", + friend_added: "已添加好友", + friend_unfollowed: "已取消关注", + friend_added_code: "已添加好友(代码:{code})", + friend_add_failed: "无法添加好友:{error}", + friend_unfollow_failed: "取消关注失败:{error}", + playlist_external_unavailable: "Spotify Development Mode 禁止读取其他用户拥有的播放列表内容。只能访问你拥有或参与协作的播放列表。", + liked_check_failed: "无法检查曲目的喜欢状态:{error}", + playlist_search_scope: "(仅搜索前 {count} 首曲目)", + playlist_search_matches: "播放列表中有 {count} 首曲目匹配“{query}”{scope}", + playlist_refresh_partial: "播放列表刷新不完整,已保留原列表。", + playlist_added: "已添加到播放列表", + playlist_removed: "已从播放列表移除", + playlist_created_no_id: "播放列表已创建,但无法获取其 ID", + playlist_added_named: "已添加到 {name}", + playlist_created: "播放列表“{name}”已创建!", native_recovered: "内置播放已恢复。", native_lost_reconnecting: "内置播放连接已断开,正在重新连接。", native_reconnected: "内置播放连接已恢复。", @@ -1174,7 +1229,79 @@ pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { native_moved: "播放已转移到另一台 Spotify 设备。", native_disconnected: "内置播放已断开,正在尝试恢复。", native_stopped_recovering: "内置播放已停止,正在尝试恢复。", + sync_busy: "播放列表同步已在运行", + sync_no_links: "播放列表同步:没有关联", + sync_dry_run: "(模拟运行)", + sync_removed: "播放列表关联已移除,镜像播放列表保留", + sync_failed: "播放列表同步失败:{error}", + sync_link_failed: "播放列表关联失败:{error}", + sync_remove_failed: "移除关联失败:{error}", + sync_summary: "播放列表同步{dry}:添加 {added} 首,移除 {removed} 首,未匹配 {unmatched} 首", + sync_using_playlist: "使用现有 {source} 播放列表“{name}”", + sync_progress: "播放列表同步:{name} {done}/{total}", + dj_indexed: "已索引 {playlists} 个播放列表中的 {tracks} 首曲目", + dj_index_capped: "(已在 {count} 首处停止,其余曲目未过滤)", + dj_queued: "已加入队列 {count} 首", + dj_not_found: "未找到 {count} 首", + dj_owned: "已拥有 {count} 首", + dj_duplicate: "已在队列中 {count} 首", + dj_status: "DJ:{summary}", + dj_library_failed: "DJ:无法读取你的播放列表({error}),将不加过滤地推荐", + dj_topped_up: "DJ:已补充队列", + dj_nothing_queued: "DJ:这次未加入任何曲目。请再试一次,或换个方向。", + dj_error: "DJ 错误({backend}):{error}", + dj_steps_exhausted: "已在 {count} 步后停止。如果结果不符合预期,请再次提问。", + history_plays: "{count} 次播放 · {duration}", + history_track_hits: "曲目播放 {count} 次 · {duration}", + history_minutes: "{minutes} 分钟", + history_hours_minutes: "{hours} 小时 {minutes} 分钟", + setup_no_api_key: "{agent}(无需 API key)", + setup_keep_command: "{command}(保留)", + setup_anthropic: "anthropic(API key)", + setup_openai: "openai_compat(本地或自托管)", + setup_own_command: "使用你自己的命令,按原样运行", + setup_not_installed: " · 不在 PATH 中", + setup_anthropic_billing: "Anthropic API,按 token 计费", + setup_needs_key: "需要 {variable}(或 behavior.dj_api_key)", + setup_openai_endpoint: "任意兼容 OpenAI 的端点,如 localhost 上的 Ollama", + setup_claude_plan: "使用你的 Claude Pro/Max 套餐", + setup_codex_plan: "使用你的 ChatGPT 套餐", + setup_agy_plan: "使用你的 Antigravity 套餐", + setup_copilot_plan: "使用你的 GitHub Copilot 套餐", + setup_opencode_plan: "使用 opencode 当前登录的服务提供商", + setup_gemini_legacy: "旧版 Gemini CLI,已由 agy 取代", + setup_agent_subscription: "使用该 CLI 自己的订阅", + setup_keep_as_is: "保留此命令不变", + setup_own_model_note: "spotatui 不会向自定义命令添加模型参数", + setup_current: "当前配置", + setup_cli_default: "使用 CLI 的默认模型", + setup_no_model_flag: "不传入模型参数", + setup_builtin_default: "使用内置默认模型({model})", + setup_custom: "自定义…", + setup_type_provider_model: "输入模型名称,格式为 provider/model", + setup_type_model: "输入模型名称", + setup_haiku_note: "最省费用,对 Pro 套餐额度压力最小", + setup_sonnet_note: "均衡", + setup_opus_note: "用量较大,Pro 套餐容易用满额度", + setup_fable_note: "用量最大,更容易用满额度", + setup_cheapest: "最省费用", + setup_heaviest_gemini: "Gemini 中用量最大", + setup_heaviest_all: "用量最大", + setup_price_haiku: "每百万 token $1/$5,200K 上下文", + setup_price_sonnet: "每百万 token $3/$15,1M 上下文", + setup_price_opus: "每百万 token $5/$25,1M 上下文", + setup_price_fable: "每百万 token $10/$50,1M 上下文", + history_week: "最近 7 天", + history_month: "最近 30 天", + history_all: "全部时间", + history_this_month: "本月", + history_this_year: "今年", + mcp_status: "MCP:{message}", + network_error: "Spotify 请求失败:{error}", + network_rate_error: "Spotify 请求受限:{error}", + monthly_recap_ready: "月度收听回顾已生成于 {path}({count} 次收听)", queue_local_source: "本地", + unnamed_folder: "未命名文件夹", local_root_folder: "(根目录)", local_unknown_track: "未知曲目", ..EN @@ -1183,6 +1310,17 @@ pub const ZH_HANS: InfraStatusStrings = InfraStatusStrings { // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] pub const ZH_HANT: InfraStatusStrings = InfraStatusStrings { + sync_spotify_not_connected: "Spotify 未連線", + sync_qobuz_not_logged_in: "Qobuz 尚未登入", + sync_subsonic_not_configured: "尚未設定 Subsonic 伺服器(請設定 behavior.subsonic_url)", + sync_source_not_compiled: "此版本未編譯 {source}(功能 `{feature}`)", + sync_source_unsupported: "無法同步 {source} 播放清單", + sync_source_unreachable: "無法連線至 {source}:{error}", + search_query_unavailable: "搜尋無法使用:所有已知的 {operation} 查詢識別碼均已失效", + made_for_you_unavailable: "為你精選無法使用:所有已知的 browsePage 查詢識別碼均已失效", + made_for_you_missing: "Spotify 未傳回為你精選頁面", + history_track_hit: "曲目播放 {count} 次 · {duration}", + history_play: "{count} 次播放 · {duration}", cast_no_answer: "{address} 沒有回應", cast_no_tls: "與 {address} 的 TLS 交握未完成", cast_message_size: "Cast 訊息大小為 {size} 位元組", @@ -1197,6 +1335,8 @@ pub const ZH_HANT: InfraStatusStrings = InfraStatusStrings { speaker_client_id_missing: "getInfo 中缺少 clientID", speaker_login_refused: "喇叭拒絕登入:{message}({error})", speaker_not_joined: "{name} 登入後未連線", + playlist_changed: "播放清單在載入後發生了變更,請重新開啟後再試", + spotify_api_error: "Spotify API {status} 請求失敗:{body}", local_replay_failed: "無法重新播放本機檔案", local_no_tracks: "沒有可播放的本機曲目", local_no_folder: "尚未設定本機音樂資料夾(請設定 behavior.local_music_path)", @@ -1292,21 +1432,63 @@ pub const ZH_HANT: InfraStatusStrings = InfraStatusStrings { queue_restore_failed: "無法恢復 {name}:{error}", queue_playing: "♪ {name}(佇列)", queue_audio_unavailable: "佇列播放無音訊輸出:{error}", + spotify_not_connected: "Spotify 未連線。請按 `d` 並選擇 Spotify 登入。", + playlist_id_invalid: "播放清單識別碼無效。", music_requires_macos: "Apple Music 需要 macOS 和 apple-music 功能", spotify_not_compiled: "此版本的 spotatui 不支援 Spotify", + spotify_connected: "Spotify 已連線。重新啟動 spotatui 以啟用內建串流。", + party_disconnected: "一起聽已結束:Spotify 未連線。", + party_ended: "一起聽已結束", + spotify_login_timeout: "Spotify 登入逾時。請按 `d` 並選擇 Spotify 重試。", + spotify_login_failed: "Spotify 登入失敗:{error}", + spotify_rate_wait: "Spotify 請求受限:等待 {seconds} 秒", + spotify_pair: "登入 Spotify:開啟 {url} 並確認配對碼 {code}", + recap_created: "聆聽回顧已產生於 {path}({count} 次聆聽)", + history_failed: "無法載入聆聽紀錄:{error}", + recap_failed: "產生聆聽回顧失敗:{error}", + party_start_failed: "發起一起聽失敗:{error}", + party_join_failed: "加入一起聽失敗:{error}", + party_error: "一起聽:{error}", + party_joined: "{name} 加入了一起聽", + party_left: "{name} 離開了一起聽", native_reconnecting: "正在重新連線內建串流…", native_settling: "內建串流仍在準備中…", + shuffle_not_applied: "已開始播放,但無法啟用隨機播放。", + spotify_rate_retry: "Spotify 請求受限。正在自動重試,請稍候。", + spotify_poll_retry: "取得播放狀態時發生暫時性 Spotify 網路錯誤,正在自動重試。", + spotify_server_retry: "Spotify 伺服器暫時無法使用(5xx),正在自動重試。", + spotify_poll_rejected: "Spotify 持續拒絕播放狀態請求,正在自動重試。", native_resuming: "正在此裝置恢復上次播放…", native_reconnect_resume: "正在重新連線內建串流,隨後將恢復播放。", native_connection_restored: "內建串流連線已恢復,正在恢復播放。", native_no_state: "內建串流連線已恢復,但沒有可恢復的播放狀態。", native_restore_retry: "內建串流恢復失敗,正在再次連線。", + other_source_playing: "其他來源正在播放", + spotify_playing_elsewhere: "仍在另一台 Spotify 裝置上播放。", queue_added: "已加入佇列", queue_load_failed: "無法載入佇列(沒有使用中的裝置?)", native_nothing_to_resume: "{device} 上沒有可恢復的播放。", native_start_failed: "啟動內建串流失敗:{error}", native_shuffle_failed: "啟用隨機播放失敗:{error}", native_resume_failed: "恢復播放失敗:{error}", + shuffle_context_failed: "隨機播放:無法載入所屬清單的曲目", + shuffle_capped: "清單較大:隨機播放前 {count} 首曲目", + spotify_profile_retry: "載入個人資料時 Spotify 請求受限,正在自動重試。", + friend_added: "已新增好友", + friend_unfollowed: "已取消追蹤", + friend_added_code: "已新增好友(代碼:{code})", + friend_add_failed: "無法新增好友:{error}", + friend_unfollow_failed: "取消追蹤失敗:{error}", + playlist_external_unavailable: "Spotify Development Mode 禁止讀取其他使用者擁有的播放清單內容。只能存取你擁有或參與協作的播放清單。", + liked_check_failed: "無法檢查曲目的喜歡狀態:{error}", + playlist_search_scope: "(僅搜尋前 {count} 首曲目)", + playlist_search_matches: "播放清單中有 {count} 首曲目符合「{query}」{scope}", + playlist_refresh_partial: "播放清單重新整理不完整,已保留原清單。", + playlist_added: "已加入播放清單", + playlist_removed: "已從播放清單移除", + playlist_created_no_id: "播放清單已建立,但無法取得其 ID", + playlist_added_named: "已加入 {name}", + playlist_created: "播放清單「{name}」已建立!", native_recovered: "內建串流已恢復。", native_lost_reconnecting: "內建串流連線已中斷,正在重新連線。", native_reconnected: "內建串流連線已恢復。", @@ -1322,7 +1504,79 @@ pub const ZH_HANT: InfraStatusStrings = InfraStatusStrings { native_moved: "播放已轉移到另一台 Spotify 裝置。", native_disconnected: "內建串流已中斷,正在嘗試恢復。", native_stopped_recovering: "內建串流已停止,正在嘗試恢復。", + sync_busy: "播放清單同步已在執行", + sync_no_links: "播放清單同步:沒有連結", + sync_dry_run: "(模擬執行)", + sync_removed: "播放清單連結已移除,鏡像播放清單保留", + sync_failed: "播放清單同步失敗:{error}", + sync_link_failed: "播放清單連結失敗:{error}", + sync_remove_failed: "移除連結失敗:{error}", + sync_summary: "播放清單同步{dry}:新增 {added} 首,移除 {removed} 首,未對應 {unmatched} 首", + sync_using_playlist: "使用現有 {source} 播放清單「{name}」", + sync_progress: "播放清單同步:{name} {done}/{total}", + dj_indexed: "已索引 {playlists} 個播放清單中的 {tracks} 首曲目", + dj_index_capped: "(已在 {count} 首處停止,其餘曲目未篩選)", + dj_queued: "已加入佇列 {count} 首", + dj_not_found: "找不到 {count} 首", + dj_owned: "已擁有 {count} 首", + dj_duplicate: "已在佇列中 {count} 首", + dj_status: "DJ:{summary}", + dj_library_failed: "DJ:無法讀取你的播放清單({error}),將不加篩選地推薦", + dj_topped_up: "DJ:已補充佇列", + dj_nothing_queued: "DJ:這次未加入任何曲目。請再試一次,或換個方向。", + dj_error: "DJ 錯誤({backend}):{error}", + dj_steps_exhausted: "已在 {count} 步後停止。如果結果不符合預期,請再次提問。", + history_plays: "{count} 次播放 · {duration}", + history_track_hits: "曲目播放 {count} 次 · {duration}", + history_minutes: "{minutes} 分鐘", + history_hours_minutes: "{hours} 小時 {minutes} 分鐘", + setup_no_api_key: "{agent}(不需 API key)", + setup_keep_command: "{command}(保留)", + setup_anthropic: "anthropic(API key)", + setup_openai: "openai_compat(本機或自行架設)", + setup_own_command: "使用你自己的指令,依原樣執行", + setup_not_installed: " · 不在 PATH 中", + setup_anthropic_billing: "Anthropic API,依 token 計費", + setup_needs_key: "需要 {variable}(或 behavior.dj_api_key)", + setup_openai_endpoint: "任何相容 OpenAI 的端點,例如 localhost 上的 Ollama", + setup_claude_plan: "使用你的 Claude Pro/Max 方案", + setup_codex_plan: "使用你的 ChatGPT 方案", + setup_agy_plan: "使用你的 Antigravity 方案", + setup_copilot_plan: "使用你的 GitHub Copilot 方案", + setup_opencode_plan: "使用 opencode 目前登入的服務供應商", + setup_gemini_legacy: "舊版 Gemini CLI,已由 agy 取代", + setup_agent_subscription: "使用該 CLI 自己的訂閱", + setup_keep_as_is: "保留此指令不變", + setup_own_model_note: "spotatui 不會向自訂指令新增模型參數", + setup_current: "目前設定", + setup_cli_default: "使用 CLI 的預設模型", + setup_no_model_flag: "不傳入模型參數", + setup_builtin_default: "使用內建預設模型({model})", + setup_custom: "自訂…", + setup_type_provider_model: "輸入模型名稱,格式為 provider/model", + setup_type_model: "輸入模型名稱", + setup_haiku_note: "最省費用,對 Pro 方案額度負擔最小", + setup_sonnet_note: "均衡", + setup_opus_note: "用量較大,Pro 方案容易用滿額度", + setup_fable_note: "用量最大,更容易用滿額度", + setup_cheapest: "最省費用", + setup_heaviest_gemini: "Gemini 中用量最大", + setup_heaviest_all: "用量最大", + setup_price_haiku: "每百萬 token $1/$5,200K 上下文", + setup_price_sonnet: "每百萬 token $3/$15,1M 上下文", + setup_price_opus: "每百萬 token $5/$25,1M 上下文", + setup_price_fable: "每百萬 token $10/$50,1M 上下文", + history_week: "最近 7 天", + history_month: "最近 30 天", + history_all: "全部時間", + history_this_month: "本月", + history_this_year: "今年", + mcp_status: "MCP:{message}", + network_error: "Spotify 請求失敗:{error}", + network_rate_error: "Spotify 請求受限:{error}", + monthly_recap_ready: "每月聆聽回顧已產生於 {path}({count} 次聆聽)", queue_local_source: "本機", + unnamed_folder: "未命名資料夾", local_root_folder: "(根目錄)", local_unknown_track: "未知曲目", ..EN diff --git a/src/core/i18n/tools.rs b/src/core/i18n/tools.rs index efba3708..f7d16466 100644 --- a/src/core/i18n/tools.rs +++ b/src/core/i18n/tools.rs @@ -133,10 +133,140 @@ pub const EN: ToolStrings = ToolStrings { }; // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] -pub const ZH_HANS: ToolStrings = ToolStrings { ..EN }; +pub const ZH_HANS: ToolStrings = ToolStrings { + brief_artists: "最常听艺人", + brief_tracks: "最常听歌曲", + brief_albums: "最常听专辑", + brief_recent: "最近播放(不要重复)", + brief_now: "\n正在播放:{name}\n", + brief_vibe: "\n听众想听:{name}\n", + period_week: "最近 7 天", + period_month: "最近 30 天", + period_this_month: "本月", + period_year: "今年", + period_all: "全部时间", + ownership_incomplete: "\n\n提示:播放列表索引仍在建立,本次搜索的 [已收藏] 仅表示喜欢的歌曲。稍后重新搜索可获得完整结果。", + queue_not_found: "\n\n未找到(已跳过):{names}", + queue_owned: "\n已在音乐库中(因 exclude_owned 跳过):{names}", + queue_duplicate: "\n已在队列中、正在播放或最近播放过(已跳过):{names}", + queue_filter_spotify_only: "\nexclude_owned 仅检查 Spotify 歌曲,本次未跳过任何歌曲。", + none_found: "未找到:{names}", + none_owned: "已在音乐库中:{names}", + none_duplicate: "已在队列中、正在播放或最近播放过:{names}", + none_new: "所有歌曲都已在队列中或正在播放。", + skipped: "已跳至下一首", + vibe_set: "DJ 氛围已设为:{vibe}。{effect}", + vibe_cleared: "已清除 DJ 氛围", + vibe_on: "内置 DJ 将从下次补充队列起遵循此氛围。", + vibe_off: "内置 DJ 的自动补充队列已关闭,开启后才会生效;目前请手动加入歌曲。get_listening_history 仍会返回此氛围。", + vibe_unavailable: "此版本没有内置自动补充队列 DJ,不会自动应用此氛围。氛围已保存,可由 get_listening_history 读取后用于加入歌曲。", + creating: "正在创建播放列表“{name}”,包含 {count} 首歌曲。Spotify 创建后即可由 list_playlists 查看。", + adding: "正在向 {playlist} 添加 {count} 首歌曲", + removing: "正在从 {playlist} 的位置 {position} 移除 {uri}(仅在曲目相符时移除)", + following: "正在关注 spotify:artist:{artist}", + unfollowing: "正在取消关注 spotify:artist:{artist}", + transfer: "正在将播放切换至 {name}", + liking: "正在喜欢 {changed} 首歌曲;{unchanged} 首已喜欢", + unliking: "正在取消喜欢 {changed} 首歌曲;{unchanged} 首原本未喜欢", + search_failed: "搜索失败:{error}", + no_tracks: "未找到与“{query}”相关的歌曲", + found_track: "{name} — {artists} [{uri}] [{owned}]", + owned: "已收藏", + new_track: "未收藏", + queued: "已加入 {count} 首歌曲:", + queue_empty: "队列为空", + nothing_playing: "没有正在播放的内容(队列中有 {count} 首歌曲)", + now_playing: "{state}:{label}(队列中有 {count} 首歌曲)", + playing: "正在播放", + paused: "已暂停", + sent: "已将 {uri} 交给播放器", + play_started: "正在播放 {label}", + missing_feature: "无法播放:此 spotatui 未编译 `{feature}` 功能,没有可播放 {uri} 的音源。当前播放未更改。", + missing_track: "无法播放:曲库中没有 {uri},当前播放未更改。请用 search_tracks 获取有效 URI。", + history_failed: "无法读取收听历史:{error}", + history_task_failed: "收听历史任务失败:{error}", + history_sparse: "收听历史较少({period}有 {count} 次有效播放)。请先询问用户想听什么。", + history_summary: "收听历史({period},{count} 次有效播放):", + catalogue_unavailable: "spotatui 尚未登录 Spotify,无法访问曲库。请在 spotatui 界面中登录后重试。", + session_unavailable: "spotatui 尚未登录 Spotify。请在 spotatui 界面中登录后重试。", + uneditable: "“{name}”不属于用户,也不是协作播放列表,无法编辑", + queue_filter_failed: "无法读取播放列表以执行 exclude_owned,未加入任何歌曲。请稍后重试,或在 queue_tracks 中关闭 exclude_owned 后加入。", + nothing_queued: "未加入任何歌曲。\n{detail}", + unknown_tool: "没有名为 {name} 的工具。请使用上方列出的工具。", + invalid_arguments: "参数无效:{detail}", + shutting_down: "spotatui 正在关闭,未执行此请求", + dropped: "spotatui 在回应前取消了请求", + timed_out: "spotatui 未及时回应,可能正在忙碌。请稍后重试。", + ..EN +}; // Keep ..EN so new English fields compile before their translations are added. #[allow(clippy::needless_update)] -pub const ZH_HANT: ToolStrings = ToolStrings { ..EN }; +pub const ZH_HANT: ToolStrings = ToolStrings { + brief_artists: "最常聽藝人", + brief_tracks: "最常聽歌曲", + brief_albums: "最常聽專輯", + brief_recent: "最近播放(不要重複)", + brief_now: "\n正在播放:{name}\n", + brief_vibe: "\n聽眾想聽:{name}\n", + period_week: "最近 7 天", + period_month: "最近 30 天", + period_this_month: "本月", + period_year: "今年", + period_all: "全部時間", + ownership_incomplete: "\n\n提示:播放清單索引仍在建立,本次搜尋的 [已收藏] 僅表示喜歡的歌曲。稍後重新搜尋可取得完整結果。", + queue_not_found: "\n\n未找到(已略過):{names}", + queue_owned: "\n已在音樂庫中(因 exclude_owned 略過):{names}", + queue_duplicate: "\n已在佇列中、正在播放或最近播放過(已略過):{names}", + queue_filter_spotify_only: "\nexclude_owned 僅檢查 Spotify 歌曲,本次未略過任何歌曲。", + none_found: "未找到:{names}", + none_owned: "已在音樂庫中:{names}", + none_duplicate: "已在佇列中、正在播放或最近播放過:{names}", + none_new: "所有歌曲都已在佇列中或正在播放。", + skipped: "已跳至下一首", + vibe_set: "DJ 氛圍已設為:{vibe}。{effect}", + vibe_cleared: "已清除 DJ 氛圍", + vibe_on: "內建 DJ 將從下次補充佇列起遵循此氛圍。", + vibe_off: "內建 DJ 的自動補充佇列已關閉,開啟後才會生效;目前請手動加入歌曲。get_listening_history 仍會傳回此氛圍。", + vibe_unavailable: "此版本沒有內建自動補充佇列 DJ,不會自動套用此氛圍。氛圍已儲存,可由 get_listening_history 讀取後用於加入歌曲。", + creating: "正在建立播放清單「{name}」,包含 {count} 首歌曲。Spotify 建立後即可由 list_playlists 查看。", + adding: "正在向 {playlist} 新增 {count} 首歌曲", + removing: "正在從 {playlist} 的位置 {position} 移除 {uri}(僅在曲目相符時移除)", + following: "正在追蹤 spotify:artist:{artist}", + unfollowing: "正在取消追蹤 spotify:artist:{artist}", + transfer: "正在將播放切換至 {name}", + liking: "正在喜歡 {changed} 首歌曲;{unchanged} 首已喜歡", + unliking: "正在取消喜歡 {changed} 首歌曲;{unchanged} 首原本未喜歡", + search_failed: "搜尋失敗:{error}", + no_tracks: "未找到與「{query}」相關的歌曲", + found_track: "{name} — {artists} [{uri}] [{owned}]", + owned: "已收藏", + new_track: "未收藏", + queued: "已加入 {count} 首歌曲:", + queue_empty: "佇列為空", + nothing_playing: "沒有正在播放的內容(佇列中有 {count} 首歌曲)", + now_playing: "{state}:{label}(佇列中有 {count} 首歌曲)", + playing: "正在播放", + paused: "已暫停", + sent: "已將 {uri} 交給播放器", + play_started: "正在播放 {label}", + missing_feature: "無法播放:此 spotatui 未編譯 `{feature}` 功能,沒有可播放 {uri} 的來源。目前播放未變更。", + missing_track: "無法播放:曲庫中沒有 {uri},目前播放未變更。請用 search_tracks 取得有效 URI。", + history_failed: "無法讀取聆聽紀錄:{error}", + history_task_failed: "聆聽紀錄工作失敗:{error}", + history_sparse: "聆聽紀錄較少({period}有 {count} 次有效播放)。請先詢問使用者想聽什麼。", + history_summary: "聆聽紀錄({period},{count} 次有效播放):", + catalogue_unavailable: "spotatui 尚未登入 Spotify,無法存取曲庫。請在 spotatui 介面中登入後重試。", + session_unavailable: "spotatui 尚未登入 Spotify。請在 spotatui 介面中登入後重試。", + uneditable: "「{name}」不屬於使用者,也不是協作播放清單,無法編輯", + queue_filter_failed: "無法讀取播放清單以執行 exclude_owned,未加入任何歌曲。請稍後重試,或在 queue_tracks 中關閉 exclude_owned 後加入。", + nothing_queued: "未加入任何歌曲。\n{detail}", + unknown_tool: "沒有名為 {name} 的工具。請使用上方列出的工具。", + invalid_arguments: "參數無效:{detail}", + shutting_down: "spotatui 正在關閉,未執行此請求", + dropped: "spotatui 在回應前取消了請求", + timed_out: "spotatui 未及時回應,可能正在忙碌。請稍後重試。", + ..EN +}; #[cfg(any(feature = "dj-core", test))] impl ToolStrings { pub fn brief_now(&self, name: &str) -> String { diff --git a/src/gui/protocol.rs b/src/gui/protocol.rs index cf49aa1f..c93eefe3 100644 --- a/src/gui/protocol.rs +++ b/src/gui/protocol.rs @@ -2988,26 +2988,28 @@ mod tests { app.user_config.behavior.language = Language::ZhHant; app.refresh_language(); let changed = pushed(&diff(&before, &app, Language::En), "playlist_sync"); - assert!(changed["payload"]["links"][0]["last_line"] - .as_str() - .unwrap() - .contains("{added} 五月天")); + assert_eq!( + changed["payload"]["links"][0]["last_line"], + "{added} 五月天:已新增 2 首,已移除 1 首,3 首未對應" + ); + assert_eq!( + changed["payload"]["last_summary"], + "播放清單同步(模擬執行):新增 2 首,移除 1 首,未對應 3 首" + ); let mut failed = report; failed.error = Some("{added} HTTP 503".to_string()); failed.links[0].outcome = LinkOutcome::Failed("{name} HTTP 503".into()); app.finish_playlist_sync(failed); let localized = playlist_sync(&app); - assert!(localized - .last_summary - .as_deref() - .unwrap() - .contains("{added} HTTP 503")); - assert!(localized.links[0] - .last_line - .as_deref() - .unwrap() - .contains("{name} HTTP 503")); + assert_eq!( + localized.last_summary.as_deref(), + Some("播放清單同步失敗:{added} HTTP 503") + ); + assert_eq!( + localized.links[0].last_line.as_deref(), + Some("{added} 五月天:失敗({name} HTTP 503)") + ); } #[test] diff --git a/src/infra/dj/exec.rs b/src/infra/dj/exec.rs index 82461271..fc02d5ef 100644 --- a/src/infra/dj/exec.rs +++ b/src/infra/dj/exec.rs @@ -138,6 +138,13 @@ mod tests { if language == Language::En { assert_eq!(result.display_text(language), result.text); assert_eq!(status, format!("MCP: {}", result.text)); + } else { + assert!(!status.contains("DJ vibe set to")); + assert!(status.contains(if language == Language::ZhHans { + "DJ 氛围已设为" + } else { + "DJ 氛圍已設為" + })); } } } @@ -164,7 +171,7 @@ mod tests { ); assert_eq!( result.display_text(Language::ZhHant), - crate::core::i18n::ZH_HANT.status.tools.shutting_down + "spotatui 正在關閉,未執行此請求" ); } diff --git a/src/infra/dj/setup.rs b/src/infra/dj/setup.rs index 5dfd6fb5..95c5a25f 100644 --- a/src/infra/dj/setup.rs +++ b/src/infra/dj/setup.rs @@ -1657,5 +1657,10 @@ mod tests { assert!(claude.label_with(strings).contains("claude")); assert!(claude.note_with(strings).contains("PATH")); } + assert_eq!(claude.label_with(&ZH_HANS), "claude(无需 API key)"); + assert_eq!( + claude.note_with(&ZH_HANT), + "使用你的 Claude Pro/Max 方案 · 不在 PATH 中" + ); } } diff --git a/src/infra/history.rs b/src/infra/history.rs index 6741ff64..e47e74cb 100644 --- a/src/infra/history.rs +++ b/src/infra/history.rs @@ -3754,7 +3754,15 @@ mod tests { assert_eq!(tracks[0].uri, english[0].uri); assert_eq!(tracks[0].value, 3_720_000); } - + assert_eq!( + aggregate_top_tracks_with(&records, 10, &ZH_HANS)[0].detail, + "2 次播放 · 1 小时 2 分钟" + ); + assert_eq!( + aggregate_top_artists_with(&records, 10, &ZH_HANT)[0].detail, + "曲目播放 2 次 · 1 小時 2 分鐘" + ); assert_eq!(period_label(RecapPeriod::Year), "This Year"); + assert_eq!(period_label_with(RecapPeriod::Year, &ZH_HANT), "今年"); } } diff --git a/src/infra/local/mod.rs b/src/infra/local/mod.rs index 81baf6b4..e6c6e0ec 100644 --- a/src/infra/local/mod.rs +++ b/src/infra/local/mod.rs @@ -723,16 +723,8 @@ mod tests { let cached = futures::executor::block_on(LocalSource::new(root).playlists()).unwrap(); for (strings, root_label, unknown_track) in [ (&EN, "(root)", "Unknown Track"), - ( - &ZH_HANS, - ZH_HANS.status.infra.local_root_folder, - ZH_HANS.status.infra.local_unknown_track, - ), - ( - &ZH_HANT, - ZH_HANT.status.infra.local_root_folder, - ZH_HANT.status.infra.local_unknown_track, - ), + (&ZH_HANS, "(根目录)", "未知曲目"), + (&ZH_HANT, "(根目錄)", "未知曲目"), ] { let source = LocalSource::new(root); let playlists = futures::executor::block_on(source.playlists()).unwrap(); diff --git a/src/infra/network/library.rs b/src/infra/network/library.rs index 0b3138a1..8c22c70c 100644 --- a/src/infra/network/library.rs +++ b/src/infra/network/library.rs @@ -3260,8 +3260,8 @@ mod folder_label_tests { let cached = structurize_playlist_folders(&nodes, &[]); for (strings, fallback) in [ (&EN, "Unnamed Folder"), - (&ZH_HANS, ZH_HANS.status.infra.unnamed_folder), - (&ZH_HANT, ZH_HANT.status.infra.unnamed_folder), + (&ZH_HANS, "未命名文件夹"), + (&ZH_HANT, "未命名資料夾"), ] { assert_eq!( names(&cached, strings), diff --git a/src/infra/network/mod.rs b/src/infra/network/mod.rs index 62d8ade9..9bccc40b 100644 --- a/src/infra/network/mod.rs +++ b/src/infra/network/mod.rs @@ -2816,12 +2816,7 @@ mod tests { .expect("reading the table must release the lock before writing status"); assert_eq!( app.lock().await.status_message(), - Some( - crate::core::i18n::ZH_HANT - .status - .infra - .spotify_not_connected - ) + Some("Spotify 未連線。請按 `d` 並選擇 Spotify 登入。") ); { let mut app = app.lock().await; @@ -2831,12 +2826,7 @@ mod tests { network.start_party(sync::ControlMode::HostOnly).await; assert_eq!( app.lock().await.status_message(), - Some( - crate::core::i18n::ZH_HANS - .status - .infra - .spotify_not_connected - ) + Some("Spotify 未连接。请按 `d` 并选择 Spotify 登录。") ); app.lock().await.use_english_messages(); network.start_party(sync::ControlMode::HostOnly).await; diff --git a/src/infra/network/requests.rs b/src/infra/network/requests.rs index 4c802694..713a0057 100644 --- a/src/infra/network/requests.rs +++ b/src/infra/network/requests.rs @@ -802,7 +802,7 @@ mod tests { assert!(text.contains("客廳 {body}")); assert!(text.contains("raw {status}")); assert!(text.ends_with("[PUT /v1/me/player?device_id=客廳]")); - + assert!(!text.contains("did not join after its login")); assert_eq!(error.to_string(), english); } } @@ -819,11 +819,11 @@ mod tests { ); assert_eq!( error_text(&error, Language::ZhHans), - crate::core::i18n::ZH_HANS.status.infra.playlist_changed + "播放列表在加载后发生了变化,请重新打开后再试" ); assert_eq!( error_text(&error, Language::ZhHant), - crate::core::i18n::ZH_HANT.status.infra.playlist_changed + "播放清單在載入後發生了變更,請重新開啟後再試" ); let raw = anyhow!("unknown speaker reply {name}", name = "{type}"); assert_eq!(