From a4d3a33b197f51cb4d8d14272883749fce8b2ddd Mon Sep 17 00:00:00 2001 From: DCHA <426225+daocha@users.noreply.github.com> Date: Fri, 3 Apr 2026 05:49:23 +0800 Subject: [PATCH 1/5] Add /diff /pull commands, and show diff buttons, fix queued questions after image text , change /switch pagination behavior (#42) * Add /pull command * Add /diff command * Fix bug for queued questions right after the image message * Add AI-assisted /commit generation flow; prompt to execute generated commit commands -- Fix prompt of commit command generation -- Fix generated git commit command to bind with session id insetad of chat id * Add show diff buttons with pagination for tracked changed files including staged files * Change /switch session listing to inline pagination; add Prev/Next callback navigation; stop showing /switch page commands in the listing; cover inline page editing and button rendering in router tests --------- Co-authored-by: DCHA Agent <259406208+dcha-agent@users.noreply.github.com> --- src/coding_agent_telegram/bot.py | 9 + src/coding_agent_telegram/diff_utils.py | 58 +- .../resources/locales/de.json | 27 + .../resources/locales/en.json | 27 + .../resources/locales/fr.json | 27 + .../resources/locales/ja.json | 27 + .../resources/locales/ko.json | 27 + .../resources/locales/nl.json | 27 + .../resources/locales/th.json | 27 + .../resources/locales/vi.json | 27 + .../resources/locales/zh-CN.json | 27 + .../resources/locales/zh-HK.json | 27 + .../resources/locales/zh-TW.json | 27 + .../router/git_commands.py | 523 +++++++++++++- .../router/message_commands.py | 21 +- .../router/queue_processing.py | 5 +- .../router/session_common.py | 1 + .../router/switch_commands.py | 71 +- tests/test_bot.py | 4 +- tests/test_command_router.py | 636 +++++++++++++++++- tests/test_diff_chunking.py | 36 + 21 files changed, 1639 insertions(+), 22 deletions(-) diff --git a/src/coding_agent_telegram/bot.py b/src/coding_agent_telegram/bot.py index 0cad645..cb0ba4d 100644 --- a/src/coding_agent_telegram/bot.py +++ b/src/coding_agent_telegram/bot.py @@ -48,9 +48,11 @@ def default_bot_commands(*, enable_commit_command: bool, locale: str = DEFAULT_L BotCommand("new", translate(locale, "bot.command.new")), BotCommand("switch", translate(locale, "bot.command.switch")), BotCommand("compact", translate(locale, "bot.command.compact")), + BotCommand("diff", translate(locale, "bot.command.diff")), ] if enable_commit_command: commands.append(BotCommand("commit", translate(locale, "bot.command.commit"))) + commands.append(BotCommand("pull", translate(locale, "bot.command.pull"))) commands.append(BotCommand("push", translate(locale, "bot.command.push"))) commands.append(BotCommand("abort", translate(locale, "bot.command.abort"))) return commands @@ -148,7 +150,9 @@ async def log_incoming_private_message(update, _context) -> None: app.add_handler(CommandHandler("new", router.handle_new, filters=allowed_private, block=False)) app.add_handler(CommandHandler("switch", router.handle_switch, filters=allowed_private)) app.add_handler(CommandHandler("compact", router.handle_compact, filters=allowed_private)) + app.add_handler(CommandHandler("diff", router.handle_diff, filters=allowed_private)) app.add_handler(CommandHandler("commit", router.handle_commit, filters=allowed_private)) + app.add_handler(CommandHandler("pull", router.handle_pull, filters=allowed_private)) app.add_handler(CommandHandler("push", router.handle_push, filters=allowed_private)) app.add_handler(CommandHandler("abort", router.handle_abort, filters=allowed_private)) app.add_handler(CallbackQueryHandler(router.handle_provider_callback, pattern=r"^provider:set:(codex|copilot)$", block=False)) @@ -156,6 +160,11 @@ async def log_incoming_private_message(update, _context) -> None: app.add_handler(CallbackQueryHandler(router.handle_queue_continue_callback, pattern=r"^queuecontinue:(yes|no)$", block=False)) app.add_handler(CallbackQueryHandler(router.handle_branch_source_callback, pattern=r"^branchsource:[0-9a-f]{12}$", block=False)) app.add_handler(CallbackQueryHandler(router.handle_branch_discrepancy_callback, pattern=r"^branchdiscrepancy:(stored|current)$", block=False)) + app.add_handler(CallbackQueryHandler(router.handle_commit_generate_callback, pattern=r"^commitgen:(confirm|cancel)$")) + app.add_handler(CallbackQueryHandler(router.handle_commit_execute_callback, pattern=r"^commitexec:(confirm|cancel)$")) + app.add_handler(CallbackQueryHandler(router.handle_diff_callback, pattern=r"^diff(?:show|page):\d+$")) + app.add_handler(CallbackQueryHandler(router.handle_switch_page_callback, pattern=r"^switchpage:\d+$")) + app.add_handler(CallbackQueryHandler(router.handle_pull_callback, pattern=r"^pull:(confirm|cancel)$")) app.add_handler(CallbackQueryHandler(router.handle_push_callback, pattern=r"^push:(confirm|cancel)$")) app.add_handler(CallbackQueryHandler(router.handle_trust_project_callback, pattern=r"^trustproject:(yes|no):")) app.add_handler(MessageHandler(allowed_private & tg_filters.PHOTO, router.handle_photo, block=False)) diff --git a/src/coding_agent_telegram/diff_utils.py b/src/coding_agent_telegram/diff_utils.py index 62c0e40..d8a1113 100644 --- a/src/coding_agent_telegram/diff_utils.py +++ b/src/coding_agent_telegram/diff_utils.py @@ -233,16 +233,66 @@ def changed_files(project_path: Path) -> list[str]: ] -def _collect_diff_for_file(project_path: Path, path: str) -> str: - return _git(project_path, ["diff", "--", path]).strip() +def split_changed_files(project_path: Path) -> tuple[list[str], list[str]]: + output = _git(project_path, ["status", "--short", "--untracked-files=all"]) + tracked: list[str] = [] + untracked: list[str] = [] + for line in output.splitlines(): + if len(line) < 4: + continue + status = line[:2] + path = line[3:].strip() + if " -> " in path: + path = path.split(" -> ", 1)[1].strip() + if ( + not path + or path.startswith(f"{INTERNAL_APP_DIR}/") + or is_snapshot_excluded_path(path) + ): + continue + if status == "??": + untracked.append(path) + else: + tracked.append(path) + return tracked, untracked -def collect_diffs(project_path: Path, files: list[str]) -> list[FileDiff]: +def _collect_diff_for_file( + project_path: Path, + path: str, + *, + against_ref: str | None = None, + cached: bool = False, +) -> str: + args = ["diff"] + if cached: + args.append("--cached") + if against_ref: + args.append(against_ref) + args.extend(["--", path]) + return _git(project_path, args).strip() + + +def collect_diffs( + project_path: Path, + files: list[str], + *, + against_ref: str | None = None, + include_cached: bool = False, +) -> list[FileDiff]: results: list[FileDiff] = [] for path in files: if is_snapshot_excluded_path(path): continue - diff = _collect_diff_for_file(project_path, path) + parts: list[str] = [] + diff = _collect_diff_for_file(project_path, path, against_ref=against_ref) + if diff: + parts.append(diff) + if include_cached: + cached_diff = _collect_diff_for_file(project_path, path, cached=True) + if cached_diff and cached_diff not in parts: + parts.append(cached_diff) + diff = "\n\n".join(parts).strip() results.append(FileDiff(path=path, diff=diff.strip())) return results diff --git a/src/coding_agent_telegram/resources/locales/de.json b/src/coding_agent_telegram/resources/locales/de.json index 1d48a75..97e12c9 100644 --- a/src/coding_agent_telegram/resources/locales/de.json +++ b/src/coding_agent_telegram/resources/locales/de.json @@ -3,7 +3,9 @@ "bot.command.branch": "Git branch erstellen und wechseln", "bot.command.commit": "Geprüfte Git commit-Befehle ausführen", "bot.command.current": "Aktive Sitzung anzeigen", + "bot.command.diff": "Geänderte Dateinamen gegenüber HEAD anzeigen", "bot.command.new": "Neue Sitzung erstellen", + "bot.command.pull": "Die aktuelle Sitzungs-branch pullen", "bot.command.project": "Aktuellen Projektordner festlegen", "bot.command.provider": "Provider für neue Sitzungen wählen", "bot.command.push": "Die aktuelle Sitzungs-branch pushen", @@ -18,6 +20,15 @@ "common.project_folder_missing": "⚠️ Der Projektordner für diese Sitzung existiert nicht mehr: {project_folder}", "git.branch_unknown": "⚠️ Die branch der aktuellen Sitzung konnte nicht ermittelt werden.", "git.cancel_button": "Abbrechen", + "git.usage_diff": "Verwendung: /diff", + "git.usage_pull": "Verwendung: /pull", + "git.pull_cancelled": "Pull abgebrochen.", + "git.pull_completed": "Pull abgeschlossen.", + "git.pull_confirm_button": "Pull bestätigen", + "git.pull_confirm_prompt": "Branch `{branch_name}` von `origin` pullen?", + "git.pull_confirm_prompt_with_default": "Branch `{branch_name}` von `origin` pullen und zusätzlich die Standard-branch `{default_branch}` aktualisieren?", + "git.pull_in_progress": "Branch `{branch_name}` wird von `origin` gepullt...", + "git.pull_in_progress_with_default": "Branch `{branch_name}` wird von `origin` gepullt und die Standard-branch `{default_branch}` wird aktualisiert...", "git.push_cancelled": "Push abgebrochen.", "git.push_cancelled_checkout_failed": "Push abgebrochen. Wechsel zu `{branch_name}` ist zuerst fehlgeschlagen.", "git.push_confirm_button": "Push bestätigen", @@ -163,6 +174,16 @@ "git.commit_disabled": "/commit ist deaktiviert.\nSetze ENABLE_COMMIT_COMMAND=true in der Bot-Umgebung, um ihn zu aktivieren.", "git.usage_commit": "Verwendung: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Es wurden keine gültigen git commit-Befehle gefunden.", + "git.commit_generate_prompt": "Einen git commit-Befehl für die aktuell geänderten Dateien erzeugen?", + "git.commit_generate_button": "Befehl erzeugen", + "git.commit_generate_cancelled": "Die Generierung des Commit-Befehls wurde abgebrochen.", + "git.commit_generate_no_changes": "Es gibt keine geänderten Dateien, für die ein Commit-Befehl erzeugt werden könnte.", + "git.commit_generated_below": "Der erzeugte Commit-Befehl steht unten.", + "git.commit_generated_command": "Erzeugter Commit-Befehl", + "git.commit_execute_prompt": "Möchtest du den Commit ausführen?", + "git.commit_execute_button": "Commit ausführen", + "git.commit_execute_confirmed": "Der erzeugte Commit-Befehl wird ausgeführt...", + "git.commit_execute_context_changed": "Die aktive Sitzung oder das Projekt wurde geändert. Bitte den Commit-Befehl erneut erzeugen.", "git.project_not_trusted_for_mutation": "Dieses Projekt ist nicht für verändernde Git-Operationen freigegeben. Verwende ein mit /project angelegtes Projekt oder markiere es zuerst als vertrauenswürdig.", "git.unsafe_path_arguments": "Unsichere Pfadargumente sind nicht erlaubt. Es dürfen nur Dateien innerhalb des aktuellen Projekts verwendet werden.", "diff.task_completed": "Aufgabe abgeschlossen.", @@ -170,6 +191,12 @@ "diff.project_label": "Projekt: {project_folder}", "diff.changed_files": "Geänderte Dateien:", "diff.none": "(keine)", + "diff.tracked_files": "Geänderte versionierte Dateien:", + "diff.tracked_files_page_info": "Zeige {start}-{end} von {total}.", + "diff.untracked_files": "Unversionierte Dateien:", + "diff.click_button_to_see_file_diff": "Klicke unten auf eine Schaltfläche, um den Diff einer Datei anzuzeigen.", + "diff.button_prev_page": "Zurück", + "diff.button_next_page": "Weiter", "diff.new_file_header": "{file_path} (neue Datei) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", "cli.created_env_if_missing": "{env_path} wurde erstellt, falls die Datei noch nicht existierte.", diff --git a/src/coding_agent_telegram/resources/locales/en.json b/src/coding_agent_telegram/resources/locales/en.json index 9115cea..4d0678f 100644 --- a/src/coding_agent_telegram/resources/locales/en.json +++ b/src/coding_agent_telegram/resources/locales/en.json @@ -3,7 +3,9 @@ "bot.command.branch": "Create and switch to a Git branch", "bot.command.commit": "Run validated Git commit commands", "bot.command.current": "Show the active session", + "bot.command.diff": "Show changed filenames vs HEAD", "bot.command.new": "Create a new session", + "bot.command.pull": "Pull the current session branch", "bot.command.project": "Set the current project folder", "bot.command.provider": "Choose the provider for new sessions", "bot.command.push": "Push the current session branch", @@ -19,11 +21,20 @@ "common.project_folder_missing": "⚠️ Project folder no longer exists for this session: {project_folder}", "git.branch_unknown": "⚠️ Could not determine the branch for the current session.", "git.cancel_button": "Cancel", + "git.usage_diff": "Usage: /diff", + "git.pull_cancelled": "Pull cancelled.", + "git.pull_completed": "Pull completed.", + "git.pull_confirm_button": "Confirm pull", + "git.pull_confirm_prompt": "Pull branch `{branch_name}` from `origin`?", + "git.pull_confirm_prompt_with_default": "Pull branch `{branch_name}` from `origin` and also refresh default branch `{default_branch}`?", + "git.pull_in_progress": "Pulling branch `{branch_name}` from `origin`...", + "git.pull_in_progress_with_default": "Pulling branch `{branch_name}` from `origin` and refreshing default branch `{default_branch}`...", "git.push_cancelled": "Push cancelled.", "git.push_cancelled_checkout_failed": "Push cancelled. Failed to switch to `{branch_name}` first.", "git.push_confirm_button": "Confirm push", "git.push_confirm_prompt": "Push branch `{branch_name}` to `origin`?", "git.push_in_progress": "Pushing branch `{branch_name}` to `origin`...", + "git.usage_pull": "Usage: /pull", "git.usage_push": "Usage: /push", "message.photo_only_codex": "Photo attachments are currently supported only for codex sessions.", "message.question_queued": "Question queued as Q{question_number}. It will run after the current agent task finishes.", @@ -165,6 +176,16 @@ "git.commit_disabled": "/commit is disabled.\nSet ENABLE_COMMIT_COMMAND=true in the bot environment to enable it.", "git.usage_commit": "Usage: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "No valid git commit commands were found.", + "git.commit_generate_prompt": "Generate a git commit command for the current changed files?", + "git.commit_generate_button": "Generate command", + "git.commit_generate_cancelled": "Commit command generation cancelled.", + "git.commit_generate_no_changes": "There are no changed files to generate a commit command for.", + "git.commit_generated_below": "Generated commit command below.", + "git.commit_generated_command": "Generated commit command", + "git.commit_execute_prompt": "Do you want to execute the commit?", + "git.commit_execute_button": "Execute commit", + "git.commit_execute_confirmed": "Executing generated commit command...", + "git.commit_execute_context_changed": "The active session or project changed. Please generate the commit command again.", "git.project_not_trusted_for_mutation": "This project is not trusted for mutating git operations. Use a project created by /project or mark it trusted first.", "git.unsafe_path_arguments": "Unsafe path arguments are not allowed. Only files inside the current project may be used.", "diff.task_completed": "Task completed.", @@ -172,6 +193,12 @@ "diff.project_label": "Project: {project_folder}", "diff.changed_files": "Changed files:", "diff.none": "(none)", + "diff.tracked_files": "Tracked changed files:", + "diff.tracked_files_page_info": "Showing {start}-{end} of {total}.", + "diff.untracked_files": "Untracked files:", + "diff.click_button_to_see_file_diff": "Click a button below to see a file diff.", + "diff.button_prev_page": "Prev", + "diff.button_next_page": "Next", "diff.new_file_header": "{file_path} (new file) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", "cli.created_env_if_missing": "Created {env_path} if it did not already exist.", diff --git a/src/coding_agent_telegram/resources/locales/fr.json b/src/coding_agent_telegram/resources/locales/fr.json index 9700b88..83870dd 100644 --- a/src/coding_agent_telegram/resources/locales/fr.json +++ b/src/coding_agent_telegram/resources/locales/fr.json @@ -3,7 +3,9 @@ "bot.command.branch": "Créer et changer de Git branch", "bot.command.commit": "Exécuter des commandes Git commit validées", "bot.command.current": "Afficher la session active", + "bot.command.diff": "Afficher les noms de fichiers modifiés par rapport à HEAD", "bot.command.new": "Créer une nouvelle session", + "bot.command.pull": "Pull la branch de la session actuelle", "bot.command.project": "Définir le dossier de projet actuel", "bot.command.provider": "Choisir le fournisseur pour les nouvelles sessions", "bot.command.push": "Push la branch de la session actuelle", @@ -18,6 +20,15 @@ "common.project_folder_missing": "⚠️ Le dossier du projet n’existe plus pour cette session : {project_folder}", "git.branch_unknown": "⚠️ Impossible de déterminer la branch de la session actuelle.", "git.cancel_button": "Annuler", + "git.usage_diff": "Utilisation : /diff", + "git.usage_pull": "Utilisation : /pull", + "git.pull_cancelled": "Pull annulé.", + "git.pull_completed": "Pull terminé.", + "git.pull_confirm_button": "Confirmer pull", + "git.pull_confirm_prompt": "Pull la branch `{branch_name}` depuis `origin` ?", + "git.pull_confirm_prompt_with_default": "Pull la branch `{branch_name}` depuis `origin` et rafraîchir aussi la branch par défaut `{default_branch}` ?", + "git.pull_in_progress": "Pull de la branch `{branch_name}` depuis `origin`...", + "git.pull_in_progress_with_default": "Pull de la branch `{branch_name}` depuis `origin` et rafraîchissement de la branch par défaut `{default_branch}`...", "git.push_cancelled": "Push annulé.", "git.push_cancelled_checkout_failed": "Push annulé. Échec du basculement vers `{branch_name}`.", "git.push_confirm_button": "Confirmer push", @@ -163,6 +174,16 @@ "git.commit_disabled": "/commit est désactivé.\nDéfinissez ENABLE_COMMIT_COMMAND=true dans l'environnement du bot pour l'activer.", "git.usage_commit": "Utilisation : /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Aucune commande git commit valide n'a été trouvée.", + "git.commit_generate_prompt": "Générer une commande git commit pour les fichiers actuellement modifiés ?", + "git.commit_generate_button": "Générer la commande", + "git.commit_generate_cancelled": "La génération de la commande de commit a été annulée.", + "git.commit_generate_no_changes": "Aucun fichier modifié n'est disponible pour générer une commande de commit.", + "git.commit_generated_below": "La commande de commit générée se trouve ci-dessous.", + "git.commit_generated_command": "Commande de commit générée", + "git.commit_execute_prompt": "Voulez-vous exécuter le commit ?", + "git.commit_execute_button": "Exécuter le commit", + "git.commit_execute_confirmed": "Exécution de la commande de commit générée...", + "git.commit_execute_context_changed": "La session active ou le projet a changé. Veuillez régénérer la commande de commit.", "git.project_not_trusted_for_mutation": "Ce projet n'est pas approuvé pour les opérations Git qui modifient l'état. Utilisez un projet créé via /project ou marquez-le d'abord comme approuvé.", "git.unsafe_path_arguments": "Les arguments de chemin non sûrs ne sont pas autorisés. Seuls les fichiers à l'intérieur du projet actuel peuvent être utilisés.", "diff.task_completed": "Tâche terminée.", @@ -170,6 +191,12 @@ "diff.project_label": "Projet : {project_folder}", "diff.changed_files": "Fichiers modifiés :", "diff.none": "(aucun)", + "diff.tracked_files": "Fichiers suivis modifiés :", + "diff.tracked_files_page_info": "Affichage de {start}-{end} sur {total}.", + "diff.untracked_files": "Fichiers non suivis :", + "diff.click_button_to_see_file_diff": "Cliquez sur un bouton ci-dessous pour voir le diff d’un fichier.", + "diff.button_prev_page": "Préc.", + "diff.button_next_page": "Suiv.", "diff.new_file_header": "{file_path} (nouveau fichier) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", "cli.created_env_if_missing": "{env_path} a été créé s'il n'existait pas déjà.", diff --git a/src/coding_agent_telegram/resources/locales/ja.json b/src/coding_agent_telegram/resources/locales/ja.json index 6524730..7bd9977 100644 --- a/src/coding_agent_telegram/resources/locales/ja.json +++ b/src/coding_agent_telegram/resources/locales/ja.json @@ -3,7 +3,9 @@ "bot.command.branch": "Git branch を作成して切り替え", "bot.command.commit": "検証済みの Git commit コマンドを実行", "bot.command.current": "現在のアクティブセッションを表示", + "bot.command.diff": "HEAD との差分があるファイル名を表示", "bot.command.new": "新しいセッションを作成", + "bot.command.pull": "現在のセッション branch を pull", "bot.command.project": "現在のプロジェクトフォルダーを設定", "bot.command.provider": "新しいセッションのプロバイダーを選択", "bot.command.push": "現在のセッション branch を push", @@ -18,6 +20,15 @@ "common.project_folder_missing": "⚠️ このセッションのプロジェクトフォルダーは存在しなくなりました: {project_folder}", "git.branch_unknown": "⚠️ 現在のセッションの branch を特定できませんでした。", "git.cancel_button": "キャンセル", + "git.usage_diff": "使い方: /diff", + "git.usage_pull": "使い方: /pull", + "git.pull_cancelled": "pull をキャンセルしました。", + "git.pull_completed": "pull が完了しました。", + "git.pull_confirm_button": "pull を確認", + "git.pull_confirm_prompt": "branch `{branch_name}` を `origin` から pull しますか?", + "git.pull_confirm_prompt_with_default": "branch `{branch_name}` を `origin` から pull し、あわせてデフォルト branch `{default_branch}` も更新しますか?", + "git.pull_in_progress": "branch `{branch_name}` を `origin` から pull 中...", + "git.pull_in_progress_with_default": "branch `{branch_name}` を `origin` から pull し、デフォルト branch `{default_branch}` を更新中...", "git.push_cancelled": "push をキャンセルしました。", "git.push_cancelled_checkout_failed": "push をキャンセルしました。まず `{branch_name}` への切り替えに失敗しました。", "git.push_confirm_button": "push を確認", @@ -163,6 +174,16 @@ "git.commit_disabled": "/commit は無効です。\n有効にするには bot の環境で ENABLE_COMMIT_COMMAND=true を設定してください。", "git.usage_commit": "使い方: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "有効な git commit コマンドが見つかりませんでした。", + "git.commit_generate_prompt": "現在変更されているファイル向けの git commit コマンドを生成しますか?", + "git.commit_generate_button": "コマンドを生成", + "git.commit_generate_cancelled": "Commit コマンドの生成をキャンセルしました。", + "git.commit_generate_no_changes": "Commit コマンドを生成できる変更ファイルがありません。", + "git.commit_generated_below": "生成された commit コマンドを下に表示します。", + "git.commit_generated_command": "生成された commit コマンド", + "git.commit_execute_prompt": "この commit を実行しますか?", + "git.commit_execute_button": "Commit を実行", + "git.commit_execute_confirmed": "生成された commit コマンドを実行しています...", + "git.commit_execute_context_changed": "アクティブなセッションまたはプロジェクトが変更されました。commit コマンドをもう一度生成してください。", "git.project_not_trusted_for_mutation": "このプロジェクトは Git を変更する操作用に信頼されていません。/project で作成したプロジェクトを使うか、先に信頼済みに設定してください。", "git.unsafe_path_arguments": "安全でないパス引数は使用できません。現在のプロジェクト内のファイルだけが使用できます。", "diff.task_completed": "タスクが完了しました。", @@ -170,6 +191,12 @@ "diff.project_label": "プロジェクト: {project_folder}", "diff.changed_files": "変更されたファイル:", "diff.none": "(なし)", + "diff.tracked_files": "追跡中で変更されたファイル:", + "diff.tracked_files_page_info": "{total} 件中 {start}-{end} 件を表示中。", + "diff.untracked_files": "未追跡ファイル:", + "diff.click_button_to_see_file_diff": "下のボタンを押すと、そのファイルの diff を表示します。", + "diff.button_prev_page": "前へ", + "diff.button_next_page": "次へ", "diff.new_file_header": "{file_path} (新規ファイル) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", "cli.created_env_if_missing": "{env_path} が存在しない場合は作成しました。", diff --git a/src/coding_agent_telegram/resources/locales/ko.json b/src/coding_agent_telegram/resources/locales/ko.json index 2680cd3..b018a94 100644 --- a/src/coding_agent_telegram/resources/locales/ko.json +++ b/src/coding_agent_telegram/resources/locales/ko.json @@ -3,7 +3,9 @@ "bot.command.branch": "Git branch 생성 및 전환", "bot.command.commit": "검증된 Git commit 명령 실행", "bot.command.current": "현재 활성 세션 표시", + "bot.command.diff": "HEAD 대비 변경된 파일 이름 표시", "bot.command.new": "새 세션 생성", + "bot.command.pull": "현재 세션 branch pull", "bot.command.project": "현재 프로젝트 폴더 설정", "bot.command.provider": "새 세션용 제공자 선택", "bot.command.push": "현재 세션 branch push", @@ -18,6 +20,15 @@ "common.project_folder_missing": "⚠️ 이 세션의 프로젝트 폴더가 더 이상 존재하지 않습니다: {project_folder}", "git.branch_unknown": "⚠️ 현재 세션의 branch 를 확인할 수 없습니다.", "git.cancel_button": "취소", + "git.usage_diff": "사용법: /diff", + "git.usage_pull": "사용법: /pull", + "git.pull_cancelled": "pull 이 취소되었습니다.", + "git.pull_completed": "pull 이 완료되었습니다.", + "git.pull_confirm_button": "pull 확인", + "git.pull_confirm_prompt": "branch `{branch_name}` 를 `origin` 에서 pull 할까요?", + "git.pull_confirm_prompt_with_default": "branch `{branch_name}` 를 `origin` 에서 pull 하고 기본 branch `{default_branch}` 도 함께 새로고침할까요?", + "git.pull_in_progress": "branch `{branch_name}` 를 `origin` 에서 pull 하는 중...", + "git.pull_in_progress_with_default": "branch `{branch_name}` 를 `origin` 에서 pull 하고 기본 branch `{default_branch}` 를 새로고침하는 중...", "git.push_cancelled": "push 가 취소되었습니다.", "git.push_cancelled_checkout_failed": "push 가 취소되었습니다. 먼저 `{branch_name}` 로 전환하지 못했습니다.", "git.push_confirm_button": "push 확인", @@ -163,6 +174,16 @@ "git.commit_disabled": "/commit 이 비활성화되어 있습니다.\n활성화하려면 bot 환경에서 ENABLE_COMMIT_COMMAND=true 를 설정하세요.", "git.usage_commit": "사용법: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "유효한 git commit 명령을 찾지 못했습니다.", + "git.commit_generate_prompt": "현재 변경된 파일에 대한 git commit 명령을 생성할까요?", + "git.commit_generate_button": "명령 생성", + "git.commit_generate_cancelled": "Commit 명령 생성이 취소되었습니다.", + "git.commit_generate_no_changes": "Commit 명령을 생성할 변경 파일이 없습니다.", + "git.commit_generated_below": "생성된 commit 명령은 아래에 있습니다.", + "git.commit_generated_command": "생성된 commit 명령", + "git.commit_execute_prompt": "이 commit을 실행할까요?", + "git.commit_execute_button": "Commit 실행", + "git.commit_execute_confirmed": "생성된 commit 명령을 실행하는 중입니다...", + "git.commit_execute_context_changed": "활성 세션 또는 프로젝트가 변경되었습니다. commit 명령을 다시 생성해 주세요.", "git.project_not_trusted_for_mutation": "이 프로젝트는 변경이 발생하는 Git 작업에 대해 신뢰되지 않았습니다. /project 로 만든 프로젝트를 사용하거나 먼저 신뢰 상태로 표시하세요.", "git.unsafe_path_arguments": "안전하지 않은 경로 인수는 허용되지 않습니다. 현재 프로젝트 안의 파일만 사용할 수 있습니다.", "diff.task_completed": "작업이 완료되었습니다.", @@ -170,6 +191,12 @@ "diff.project_label": "프로젝트: {project_folder}", "diff.changed_files": "변경된 파일:", "diff.none": "(없음)", + "diff.tracked_files": "추적 중인 변경 파일:", + "diff.tracked_files_page_info": "전체 {total}개 중 {start}-{end}개를 표시합니다.", + "diff.untracked_files": "추적되지 않는 파일:", + "diff.click_button_to_see_file_diff": "아래 버튼을 눌러 파일 diff를 볼 수 있습니다.", + "diff.button_prev_page": "이전", + "diff.button_next_page": "다음", "diff.new_file_header": "{file_path} (새 파일) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", "cli.created_env_if_missing": "{env_path} 가 없으면 생성했습니다.", diff --git a/src/coding_agent_telegram/resources/locales/nl.json b/src/coding_agent_telegram/resources/locales/nl.json index 401388b..948c8cb 100644 --- a/src/coding_agent_telegram/resources/locales/nl.json +++ b/src/coding_agent_telegram/resources/locales/nl.json @@ -3,7 +3,9 @@ "bot.command.branch": "Git branch maken en wisselen", "bot.command.commit": "Gevalideerde Git commit-opdrachten uitvoeren", "bot.command.current": "Actieve sessie tonen", + "bot.command.diff": "Gewijzigde bestandsnamen ten opzichte van HEAD tonen", "bot.command.new": "Nieuwe sessie maken", + "bot.command.pull": "De huidige sessiebranch pullen", "bot.command.project": "Huidige projectmap instellen", "bot.command.provider": "Provider voor nieuwe sessies kiezen", "bot.command.push": "De huidige sessiebranch pushen", @@ -18,6 +20,15 @@ "common.project_folder_missing": "⚠️ De projectmap voor deze sessie bestaat niet meer: {project_folder}", "git.branch_unknown": "⚠️ De branch voor de huidige sessie kon niet worden bepaald.", "git.cancel_button": "Annuleren", + "git.usage_diff": "Gebruik: /diff", + "git.usage_pull": "Gebruik: /pull", + "git.pull_cancelled": "Pull geannuleerd.", + "git.pull_completed": "Pull voltooid.", + "git.pull_confirm_button": "Pull bevestigen", + "git.pull_confirm_prompt": "Branch `{branch_name}` van `origin` pullen?", + "git.pull_confirm_prompt_with_default": "Branch `{branch_name}` van `origin` pullen en ook de standaardbranch `{default_branch}` verversen?", + "git.pull_in_progress": "Branch `{branch_name}` wordt van `origin` gepulld...", + "git.pull_in_progress_with_default": "Branch `{branch_name}` wordt van `origin` gepulld en de standaardbranch `{default_branch}` wordt ververst...", "git.push_cancelled": "Push geannuleerd.", "git.push_cancelled_checkout_failed": "Push geannuleerd. Wisselen naar `{branch_name}` is eerst mislukt.", "git.push_confirm_button": "Push bevestigen", @@ -163,6 +174,16 @@ "git.commit_disabled": "/commit is uitgeschakeld.\nStel ENABLE_COMMIT_COMMAND=true in in de botomgeving om het in te schakelen.", "git.usage_commit": "Gebruik: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Er zijn geen geldige git commit-opdrachten gevonden.", + "git.commit_generate_prompt": "Een git commit-opdracht genereren voor de momenteel gewijzigde bestanden?", + "git.commit_generate_button": "Opdracht genereren", + "git.commit_generate_cancelled": "Het genereren van de commit-opdracht is geannuleerd.", + "git.commit_generate_no_changes": "Er zijn geen gewijzigde bestanden om een commit-opdracht voor te genereren.", + "git.commit_generated_below": "De gegenereerde commit-opdracht staat hieronder.", + "git.commit_generated_command": "Gegenereerde commit-opdracht", + "git.commit_execute_prompt": "Wil je de commit uitvoeren?", + "git.commit_execute_button": "Commit uitvoeren", + "git.commit_execute_confirmed": "De gegenereerde commit-opdracht wordt uitgevoerd...", + "git.commit_execute_context_changed": "De actieve sessie of het project is gewijzigd. Genereer de commit-opdracht opnieuw.", "git.project_not_trusted_for_mutation": "Dit project is niet vertrouwd voor wijzigende Git-bewerkingen. Gebruik een project dat via /project is gemaakt of markeer het eerst als vertrouwd.", "git.unsafe_path_arguments": "Onveilige padargumenten zijn niet toegestaan. Alleen bestanden binnen het huidige project mogen worden gebruikt.", "diff.task_completed": "Taak voltooid.", @@ -170,6 +191,12 @@ "diff.project_label": "Project: {project_folder}", "diff.changed_files": "Gewijzigde bestanden:", "diff.none": "(geen)", + "diff.tracked_files": "Gewijzigde gevolgde bestanden:", + "diff.tracked_files_page_info": "Toont {start}-{end} van {total}.", + "diff.untracked_files": "Niet-gevolgde bestanden:", + "diff.click_button_to_see_file_diff": "Klik hieronder op een knop om de diff van een bestand te bekijken.", + "diff.button_prev_page": "Vorige", + "diff.button_next_page": "Volgende", "diff.new_file_header": "{file_path} (nieuw bestand) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", "cli.created_env_if_missing": "{env_path} is aangemaakt als het nog niet bestond.", diff --git a/src/coding_agent_telegram/resources/locales/th.json b/src/coding_agent_telegram/resources/locales/th.json index 449a3e0..95713c0 100644 --- a/src/coding_agent_telegram/resources/locales/th.json +++ b/src/coding_agent_telegram/resources/locales/th.json @@ -3,7 +3,9 @@ "bot.command.branch": "สร้างและสลับไปยัง Git branch", "bot.command.commit": "รันคำสั่ง Git commit ที่ผ่านการตรวจสอบ", "bot.command.current": "แสดงเซสชันที่กำลังใช้งาน", + "bot.command.diff": "แสดงชื่อไฟล์ที่เปลี่ยนไปเมื่อเทียบกับ HEAD", "bot.command.new": "สร้างเซสชันใหม่", + "bot.command.pull": "pull session branch ปัจจุบัน", "bot.command.project": "ตั้งค่าโฟลเดอร์โปรเจ็กต์ปัจจุบัน", "bot.command.provider": "เลือกผู้ให้บริการสำหรับเซสชันใหม่", "bot.command.push": "push session branch ปัจจุบัน", @@ -18,11 +20,20 @@ "common.project_folder_missing": "⚠️ โฟลเดอร์โปรเจ็กต์สำหรับเซสชันนี้ไม่มีอยู่แล้ว: {project_folder}", "git.branch_unknown": "⚠️ ไม่สามารถระบุ branch ของ session ปัจจุบันได้", "git.cancel_button": "ยกเลิก", + "git.pull_cancelled": "ยกเลิก pull แล้ว", + "git.pull_completed": "pull เสร็จแล้ว", + "git.pull_confirm_button": "ยืนยัน pull", + "git.pull_confirm_prompt": "ต้องการ pull branch `{branch_name}` จาก `origin` หรือไม่?", + "git.pull_confirm_prompt_with_default": "ต้องการ pull default branch `{default_branch}` และ branch `{branch_name}` จาก `origin` หรือไม่?", + "git.pull_in_progress": "กำลัง pull branch `{branch_name}` จาก `origin`...", + "git.pull_in_progress_with_default": "กำลัง pull default branch `{default_branch}` และ branch `{branch_name}` จาก `origin`...", "git.push_cancelled": "ยกเลิก push แล้ว", "git.push_cancelled_checkout_failed": "ยกเลิก push แล้ว เนื่องจากสลับไป `{branch_name}` ไม่สำเร็จก่อน", "git.push_confirm_button": "ยืนยัน push", "git.push_confirm_prompt": "ต้องการ push branch `{branch_name}` ไปที่ `origin` หรือไม่?", "git.push_in_progress": "กำลัง push branch `{branch_name}` ไปที่ `origin`...", + "git.usage_diff": "วิธีใช้: /diff", + "git.usage_pull": "วิธีใช้: /pull", "git.usage_push": "วิธีใช้: /push", "message.photo_only_codex": "ขณะนี้รองรับไฟล์แนบรูปภาพเฉพาะสำหรับเซสชัน Codex เท่านั้น", "message.question_queued": "จัดคิวคำถามเป็น Q{question_number} แล้ว จะประมวลผลหลังจากงานเอเจนต์ปัจจุบันเสร็จสิ้น", @@ -163,12 +174,28 @@ "git.commit_disabled": "/commit ถูกปิดใช้งานอยู่\nให้ตั้งค่า ENABLE_COMMIT_COMMAND=true ใน environment ของ bot เพื่อเปิดใช้งาน", "git.usage_commit": "วิธีใช้: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "ไม่พบคำสั่ง git commit ที่ถูกต้อง", + "git.commit_generate_prompt": "ต้องการสร้างคำสั่ง git commit สำหรับไฟล์ที่เปลี่ยนอยู่ตอนนี้หรือไม่?", + "git.commit_generate_button": "สร้างคำสั่ง", + "git.commit_generate_cancelled": "ยกเลิกการสร้างคำสั่ง commit แล้ว", + "git.commit_generate_no_changes": "ไม่มีไฟล์ที่เปลี่ยนแปลงสำหรับสร้างคำสั่ง commit", + "git.commit_generated_below": "คำสั่ง commit ที่สร้างไว้แสดงอยู่ด้านล่าง", + "git.commit_generated_command": "คำสั่ง commit ที่สร้างไว้", + "git.commit_execute_prompt": "ต้องการรัน commit นี้หรือไม่?", + "git.commit_execute_button": "รัน commit", + "git.commit_execute_confirmed": "กำลังรันคำสั่ง commit ที่สร้างไว้...", + "git.commit_execute_context_changed": "เซสชันหรือโปรเจกต์ที่ใช้งานอยู่เปลี่ยนไปแล้ว โปรดสร้างคำสั่ง commit ใหม่อีกครั้ง", "git.project_not_trusted_for_mutation": "โปรเจ็กต์นี้ยังไม่ได้รับความเชื่อถือสำหรับการทำงาน Git ที่มีการแก้ไขข้อมูล ให้ใช้โปรเจ็กต์ที่สร้างผ่าน /project หรือทำเครื่องหมายว่าเชื่อถือได้ก่อน", "git.unsafe_path_arguments": "ไม่อนุญาตให้ใช้ path argument ที่ไม่ปลอดภัย ใช้ได้เฉพาะไฟล์ภายในโปรเจ็กต์ปัจจุบันเท่านั้น", "diff.task_completed": "งานเสร็จสิ้นแล้ว", "diff.session_label": "เซสชัน: {session_name}", "diff.project_label": "โปรเจ็กต์: {project_folder}", "diff.changed_files": "ไฟล์ที่เปลี่ยนแปลง:", + "diff.tracked_files": "ไฟล์ที่ติดตามซึ่งมีการเปลี่ยนแปลง:", + "diff.tracked_files_page_info": "กำลังแสดง {start}-{end} จากทั้งหมด {total}", + "diff.untracked_files": "ไฟล์ที่ยังไม่ได้ติดตาม:", + "diff.click_button_to_see_file_diff": "แตะปุ่มด้านล่างเพื่อดู diff ของไฟล์", + "diff.button_prev_page": "ก่อนหน้า", + "diff.button_next_page": "ถัดไป", "diff.none": "(ไม่มี)", "diff.new_file_header": "{file_path} (ไฟล์ใหม่) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", diff --git a/src/coding_agent_telegram/resources/locales/vi.json b/src/coding_agent_telegram/resources/locales/vi.json index 80e7fd9..c1b45b0 100644 --- a/src/coding_agent_telegram/resources/locales/vi.json +++ b/src/coding_agent_telegram/resources/locales/vi.json @@ -3,7 +3,9 @@ "bot.command.branch": "Tạo và chuyển sang Git branch", "bot.command.commit": "Chạy các lệnh Git commit đã được kiểm tra", "bot.command.current": "Hiển thị phiên đang hoạt động", + "bot.command.diff": "Hiển thị tên file đã thay đổi so với HEAD", "bot.command.new": "Tạo phiên mới", + "bot.command.pull": "Pull branch của phiên hiện tại", "bot.command.project": "Đặt thư mục dự án hiện tại", "bot.command.provider": "Chọn nhà cung cấp cho phiên mới", "bot.command.push": "Push branch của phiên hiện tại", @@ -18,11 +20,20 @@ "common.project_folder_missing": "⚠️ Thư mục dự án của phiên này không còn tồn tại: {project_folder}", "git.branch_unknown": "⚠️ Không thể xác định branch của phiên hiện tại.", "git.cancel_button": "Hủy", + "git.pull_cancelled": "Đã hủy pull.", + "git.pull_completed": "Đã pull xong.", + "git.pull_confirm_button": "Xác nhận pull", + "git.pull_confirm_prompt": "Pull branch `{branch_name}` từ `origin`?", + "git.pull_confirm_prompt_with_default": "Pull branch mặc định `{default_branch}` và branch `{branch_name}` từ `origin`?", + "git.pull_in_progress": "Đang pull branch `{branch_name}` từ `origin`...", + "git.pull_in_progress_with_default": "Đang pull branch mặc định `{default_branch}` và branch `{branch_name}` từ `origin`...", "git.push_cancelled": "Đã hủy push.", "git.push_cancelled_checkout_failed": "Đã hủy push. Không thể chuyển sang `{branch_name}` trước.", "git.push_confirm_button": "Xác nhận push", "git.push_confirm_prompt": "Push branch `{branch_name}` lên `origin`?", "git.push_in_progress": "Đang push branch `{branch_name}` lên `origin`...", + "git.usage_diff": "Cách dùng: /diff", + "git.usage_pull": "Cách dùng: /pull", "git.usage_push": "Cách dùng: /push", "message.photo_only_codex": "Hiện tại tệp đính kèm ảnh chỉ được hỗ trợ cho các phiên Codex.", "message.question_queued": "Câu hỏi đã được xếp hàng dưới dạng Q{question_number}. Nó sẽ được xử lý sau khi tác vụ hiện tại của tác nhân hoàn tất.", @@ -163,12 +174,28 @@ "git.commit_disabled": "/commit hiện đang bị tắt.\nHãy đặt ENABLE_COMMIT_COMMAND=true trong môi trường của bot để bật nó.", "git.usage_commit": "Cách dùng: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Không tìm thấy lệnh git commit hợp lệ.", + "git.commit_generate_prompt": "Tạo lệnh git commit cho các tệp hiện đang thay đổi?", + "git.commit_generate_button": "Tạo lệnh", + "git.commit_generate_cancelled": "Đã hủy tạo lệnh commit.", + "git.commit_generate_no_changes": "Không có tệp thay đổi nào để tạo lệnh commit.", + "git.commit_generated_below": "Lệnh commit đã tạo nằm bên dưới.", + "git.commit_generated_command": "Lệnh commit đã tạo", + "git.commit_execute_prompt": "Bạn có muốn thực thi commit này không?", + "git.commit_execute_button": "Thực thi commit", + "git.commit_execute_confirmed": "Đang thực thi lệnh commit đã tạo...", + "git.commit_execute_context_changed": "Phiên hoặc dự án đang hoạt động đã thay đổi. Hãy tạo lại lệnh commit.", "git.project_not_trusted_for_mutation": "Project này chưa được tin cậy cho các thao tác Git có thay đổi dữ liệu. Hãy dùng project được tạo bằng /project hoặc đánh dấu tin cậy trước.", "git.unsafe_path_arguments": "Không cho phép đối số đường dẫn không an toàn. Chỉ được dùng các tệp bên trong project hiện tại.", "diff.task_completed": "Tác vụ đã hoàn tất.", "diff.session_label": "Phiên: {session_name}", "diff.project_label": "Dự án: {project_folder}", "diff.changed_files": "Các tệp đã thay đổi:", + "diff.tracked_files": "Các tệp đã theo dõi có thay đổi:", + "diff.tracked_files_page_info": "Đang hiển thị {start}-{end} trên tổng số {total}.", + "diff.untracked_files": "Các tệp chưa theo dõi:", + "diff.click_button_to_see_file_diff": "Nhấn nút bên dưới để xem diff của tệp.", + "diff.button_prev_page": "Trước", + "diff.button_next_page": "Tiếp", "diff.none": "(không có)", "diff.new_file_header": "{file_path} (tệp mới) ({index}/{total})", "diff.changed_file_header": "{file_path} (+{additions} -{deletions}) ({index}/{total})", diff --git a/src/coding_agent_telegram/resources/locales/zh-CN.json b/src/coding_agent_telegram/resources/locales/zh-CN.json index 65c2959..f470cf2 100644 --- a/src/coding_agent_telegram/resources/locales/zh-CN.json +++ b/src/coding_agent_telegram/resources/locales/zh-CN.json @@ -3,7 +3,9 @@ "bot.command.branch": "创建并切换到 Git branch", "bot.command.commit": "执行已校验的 Git commit 命令", "bot.command.current": "显示当前活动会话", + "bot.command.diff": "显示相对 HEAD 已变更的文件名", "bot.command.new": "创建新会话", + "bot.command.pull": "pull 当前会话 branch", "bot.command.project": "设置当前项目目录", "bot.command.provider": "为新会话选择提供方", "bot.command.push": "push 当前会话 branch", @@ -18,11 +20,20 @@ "common.project_folder_missing": "⚠️ 此会话对应的项目目录已不存在:{project_folder}", "git.branch_unknown": "⚠️ 无法确定当前会话的 branch。", "git.cancel_button": "取消", + "git.pull_cancelled": "已取消 pull。", + "git.pull_completed": "已完成 pull。", + "git.pull_confirm_button": "确认 pull", + "git.pull_confirm_prompt": "要从 `origin` pull branch `{branch_name}` 吗?", + "git.pull_confirm_prompt_with_default": "要从 `origin` pull 默认 branch `{default_branch}` 和 branch `{branch_name}` 吗?", + "git.pull_in_progress": "正在从 `origin` pull branch `{branch_name}`...", + "git.pull_in_progress_with_default": "正在从 `origin` pull 默认 branch `{default_branch}` 和 branch `{branch_name}`...", "git.push_cancelled": "已取消 push。", "git.push_cancelled_checkout_failed": "已取消 push。先切换到 `{branch_name}` 失败。", "git.push_confirm_button": "确认 push", "git.push_confirm_prompt": "要将 branch `{branch_name}` push 到 `origin` 吗?", "git.push_in_progress": "正在将 branch `{branch_name}` push 到 `origin`...", + "git.usage_diff": "用法:/diff", + "git.usage_pull": "用法:/pull", "git.usage_push": "用法:/push", "message.photo_only_codex": "当前仅 Codex 会话支持图片附件。", "message.question_queued": "问题已加入队列,编号为 Q{question_number}。当前代理任务完成后将开始处理。", @@ -163,12 +174,28 @@ "git.commit_disabled": "/commit 已禁用。\n请在 bot 环境中将 ENABLE_COMMIT_COMMAND 设为 true 以启用它。", "git.usage_commit": "用法:/commit git add ... && git commit ...", "git.no_valid_commit_commands": "未找到有效的 git commit 命令。", + "git.commit_generate_prompt": "要为当前已变更的文件生成 git commit 命令吗?", + "git.commit_generate_button": "生成命令", + "git.commit_generate_cancelled": "已取消生成 commit 命令。", + "git.commit_generate_no_changes": "当前没有可用于生成 commit 命令的已变更文件。", + "git.commit_generated_below": "生成的 commit 命令如下。", + "git.commit_generated_command": "生成的 commit 命令", + "git.commit_execute_prompt": "要执行这个 commit 吗?", + "git.commit_execute_button": "执行 commit", + "git.commit_execute_confirmed": "正在执行生成的 commit 命令...", + "git.commit_execute_context_changed": "当前活动会话或项目已更改。请重新生成 commit 命令。", "git.project_not_trusted_for_mutation": "此项目尚未被信任用于会修改 Git 的操作。请使用通过 /project 创建的项目,或先将其标记为受信任。", "git.unsafe_path_arguments": "不允许不安全的路径参数。只能使用当前项目内的文件。", "diff.task_completed": "任务已完成。", "diff.session_label": "Session:{session_name}", "diff.project_label": "项目:{project_folder}", "diff.changed_files": "已更改文件:", + "diff.tracked_files": "已跟踪且有变更的文件:", + "diff.tracked_files_page_info": "当前显示第 {start}-{end} 个,共 {total} 个。", + "diff.untracked_files": "未跟踪文件:", + "diff.click_button_to_see_file_diff": "点击下方按钮查看文件 diff。", + "diff.button_prev_page": "上一页", + "diff.button_next_page": "下一页", "diff.none": "(无)", "diff.new_file_header": "{file_path}(新文件)({index}/{total})", "diff.changed_file_header": "{file_path}(+{additions} -{deletions})({index}/{total})", diff --git a/src/coding_agent_telegram/resources/locales/zh-HK.json b/src/coding_agent_telegram/resources/locales/zh-HK.json index 8d67c56..a7a4e38 100644 --- a/src/coding_agent_telegram/resources/locales/zh-HK.json +++ b/src/coding_agent_telegram/resources/locales/zh-HK.json @@ -3,7 +3,9 @@ "bot.command.branch": "建立並切換到 Git branch", "bot.command.commit": "執行已驗證的 Git commit 指令", "bot.command.current": "顯示目前使用中的工作階段", + "bot.command.diff": "顯示相對 HEAD 已變更的檔案名稱", "bot.command.new": "建立新工作階段", + "bot.command.pull": "pull 目前工作階段 branch", "bot.command.project": "設定目前專案資料夾", "bot.command.provider": "為新工作階段選擇供應方", "bot.command.push": "push 目前工作階段 branch", @@ -18,11 +20,20 @@ "common.project_folder_missing": "⚠️ 此工作階段的專案資料夾已不存在:{project_folder}", "git.branch_unknown": "⚠️ 無法判斷目前工作階段的 branch。", "git.cancel_button": "取消", + "git.pull_cancelled": "已取消 pull。", + "git.pull_completed": "已完成 pull。", + "git.pull_confirm_button": "確認 pull", + "git.pull_confirm_prompt": "要從 `origin` pull branch `{branch_name}` 嗎?", + "git.pull_confirm_prompt_with_default": "要從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}` 嗎?", + "git.pull_in_progress": "正在從 `origin` pull branch `{branch_name}`...", + "git.pull_in_progress_with_default": "正在從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}`...", "git.push_cancelled": "已取消 push。", "git.push_cancelled_checkout_failed": "已取消 push。先切換到 `{branch_name}` 失敗。", "git.push_confirm_button": "確認 push", "git.push_confirm_prompt": "要將 branch `{branch_name}` push 到 `origin` 嗎?", "git.push_in_progress": "正在將 branch `{branch_name}` push 到 `origin`...", + "git.usage_diff": "用法:/diff", + "git.usage_pull": "用法:/pull", "git.usage_push": "用法:/push", "message.photo_only_codex": "目前只有 Codex 工作階段支援圖片附件。", "message.question_queued": "問題已加入佇列,編號為 Q{question_number}。目前代理工作完成後會開始處理。", @@ -163,12 +174,28 @@ "git.commit_disabled": "/commit 已停用。\n請在 bot 環境中把 ENABLE_COMMIT_COMMAND 設為 true 以啟用它。", "git.usage_commit": "用法:/commit git add ... && git commit ...", "git.no_valid_commit_commands": "找不到有效的 git commit 指令。", + "git.commit_generate_prompt": "要為目前已變更的檔案產生 git commit 指令嗎?", + "git.commit_generate_button": "產生指令", + "git.commit_generate_cancelled": "已取消產生 commit 指令。", + "git.commit_generate_no_changes": "目前沒有可用來產生 commit 指令的已變更檔案。", + "git.commit_generated_below": "產生的 commit 指令如下。", + "git.commit_generated_command": "產生的 commit 指令", + "git.commit_execute_prompt": "要執行這個 commit 嗎?", + "git.commit_execute_button": "執行 commit", + "git.commit_execute_confirmed": "正在執行產生的 commit 指令...", + "git.commit_execute_context_changed": "目前作用中的工作階段或專案已變更。請重新產生 commit 指令。", "git.project_not_trusted_for_mutation": "此專案尚未被信任可執行會修改 Git 的操作。請使用由 /project 建立的專案,或先將它標記為受信任。", "git.unsafe_path_arguments": "不允許不安全的路徑參數。只能使用目前專案內的檔案。", "diff.task_completed": "任務已完成。", "diff.session_label": "Session:{session_name}", "diff.project_label": "專案:{project_folder}", "diff.changed_files": "已變更檔案:", + "diff.tracked_files": "已追蹤且有變更的檔案:", + "diff.tracked_files_page_info": "目前顯示第 {start}-{end} 個,共 {total} 個。", + "diff.untracked_files": "未追蹤檔案:", + "diff.click_button_to_see_file_diff": "點擊下方按鈕查看檔案 diff。", + "diff.button_prev_page": "上一頁", + "diff.button_next_page": "下一頁", "diff.none": "(無)", "diff.new_file_header": "{file_path}(新檔案)({index}/{total})", "diff.changed_file_header": "{file_path}(+{additions} -{deletions})({index}/{total})", diff --git a/src/coding_agent_telegram/resources/locales/zh-TW.json b/src/coding_agent_telegram/resources/locales/zh-TW.json index 5055e6c..1655c78 100644 --- a/src/coding_agent_telegram/resources/locales/zh-TW.json +++ b/src/coding_agent_telegram/resources/locales/zh-TW.json @@ -3,7 +3,9 @@ "bot.command.branch": "建立並切換到 Git branch", "bot.command.commit": "執行已驗證的 Git commit 指令", "bot.command.current": "顯示目前使用中的工作階段", + "bot.command.diff": "顯示相對 HEAD 已變更的檔案名稱", "bot.command.new": "建立新工作階段", + "bot.command.pull": "pull 目前工作階段 branch", "bot.command.project": "設定目前專案資料夾", "bot.command.provider": "為新工作階段選擇提供者", "bot.command.push": "push 目前工作階段 branch", @@ -18,11 +20,20 @@ "common.project_folder_missing": "⚠️ 此工作階段的專案資料夾已不存在:{project_folder}", "git.branch_unknown": "⚠️ 無法判斷目前工作階段的 branch。", "git.cancel_button": "取消", + "git.pull_cancelled": "已取消 pull。", + "git.pull_completed": "已完成 pull。", + "git.pull_confirm_button": "確認 pull", + "git.pull_confirm_prompt": "要從 `origin` pull branch `{branch_name}` 嗎?", + "git.pull_confirm_prompt_with_default": "要從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}` 嗎?", + "git.pull_in_progress": "正在從 `origin` pull branch `{branch_name}`...", + "git.pull_in_progress_with_default": "正在從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}`...", "git.push_cancelled": "已取消 push。", "git.push_cancelled_checkout_failed": "已取消 push。先切換到 `{branch_name}` 失敗。", "git.push_confirm_button": "確認 push", "git.push_confirm_prompt": "要將 branch `{branch_name}` push 到 `origin` 嗎?", "git.push_in_progress": "正在將 branch `{branch_name}` push 到 `origin`...", + "git.usage_diff": "用法:/diff", + "git.usage_pull": "用法:/pull", "git.usage_push": "用法:/push", "message.photo_only_codex": "目前只有 Codex 工作階段支援圖片附件。", "message.question_queued": "問題已加入佇列,編號為 Q{question_number}。目前代理工作完成後會開始處理。", @@ -163,12 +174,28 @@ "git.commit_disabled": "/commit 已停用。\n請在 bot 環境中把 ENABLE_COMMIT_COMMAND 設為 true 以啟用它。", "git.usage_commit": "用法:/commit git add ... && git commit ...", "git.no_valid_commit_commands": "找不到有效的 git commit 指令。", + "git.commit_generate_prompt": "要為目前已變更的檔案產生 git commit 指令嗎?", + "git.commit_generate_button": "產生指令", + "git.commit_generate_cancelled": "已取消產生 commit 指令。", + "git.commit_generate_no_changes": "目前沒有可用來產生 commit 指令的已變更檔案。", + "git.commit_generated_below": "產生的 commit 指令如下。", + "git.commit_generated_command": "產生的 commit 指令", + "git.commit_execute_prompt": "要執行這個 commit 嗎?", + "git.commit_execute_button": "執行 commit", + "git.commit_execute_confirmed": "正在執行產生的 commit 指令...", + "git.commit_execute_context_changed": "目前作用中的工作階段或專案已變更。請重新產生 commit 指令。", "git.project_not_trusted_for_mutation": "此專案尚未被信任可執行會修改 Git 的操作。請使用由 /project 建立的專案,或先將它標記為受信任。", "git.unsafe_path_arguments": "不允許不安全的路徑參數。只能使用目前專案內的檔案。", "diff.task_completed": "任務已完成。", "diff.session_label": "Session:{session_name}", "diff.project_label": "專案:{project_folder}", "diff.changed_files": "已變更檔案:", + "diff.tracked_files": "已追蹤且有變更的檔案:", + "diff.tracked_files_page_info": "目前顯示第 {start}-{end} 個,共 {total} 個。", + "diff.untracked_files": "未追蹤檔案:", + "diff.click_button_to_see_file_diff": "點擊下方按鈕查看檔案 diff。", + "diff.button_prev_page": "上一頁", + "diff.button_next_page": "下一頁", "diff.none": "(無)", "diff.new_file_header": "{file_path}(新檔案)({index}/{total})", "diff.changed_file_header": "{file_path}(+{additions} -{deletions})({index}/{total})", diff --git a/src/coding_agent_telegram/router/git_commands.py b/src/coding_agent_telegram/router/git_commands.py index be4a41f..5009102 100644 --- a/src/coding_agent_telegram/router/git_commands.py +++ b/src/coding_agent_telegram/router/git_commands.py @@ -1,16 +1,152 @@ from __future__ import annotations import asyncio +import html +import os +from types import SimpleNamespace from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import ContextTypes -from coding_agent_telegram.telegram_sender import send_html_text, send_text +from coding_agent_telegram.diff_utils import chunk_fenced_diff, collect_diffs, split_changed_files +from coding_agent_telegram.telegram_sender import send_code_block, send_html_text, send_text, split_assistant_output from .base import require_allowed_chat class GitCommandMixin: + DIFF_BUTTON_PAGE_SIZE = 10 + COMMIT_GENERATION_PROMPT = ( + 'Execute: Analyze and compare to git HEAD, then Generate a git commit command for the files you changed in this task, with a detailed changelog-style commit message. ' + 'Only include files you intentionally modified for this task. ' + 'Do not include unrelated changed files. ' + 'Do not include untracked files unless they were created for this task and are clearly required. ' + 'Output only a single executable command in this format with \\ if there is line break: git add && git commit -m "".' + ) + + @staticmethod + def _diff_button_label(index: int, path: str, *, max_name_length: int = 20) -> str: + name = os.path.basename(path.rstrip("/")) or path + if len(name) > max_name_length: + name = f"{name[: max_name_length - 1]}…" + return f"{index}. {name}" + + def _build_diff_button_rows(self, update: Update, tracked_files: list[str], *, page: int) -> list[list[InlineKeyboardButton]]: + rows: list[list[InlineKeyboardButton]] = [] + total_pages = max(1, (len(tracked_files) + self.DIFF_BUTTON_PAGE_SIZE - 1) // self.DIFF_BUTTON_PAGE_SIZE) + page = min(max(page, 0), total_pages - 1) + start = page * self.DIFF_BUTTON_PAGE_SIZE + page_files = tracked_files[start : start + self.DIFF_BUTTON_PAGE_SIZE] + row: list[InlineKeyboardButton] = [] + for offset, path in enumerate(page_files, start=1): + absolute_index = start + offset + row.append( + InlineKeyboardButton( + self._diff_button_label(absolute_index, path), + callback_data=f"diffshow:{absolute_index - 1}", + ) + ) + if len(row) == 2: + rows.append(row) + row = [] + if row: + rows.append(row) + nav_row: list[InlineKeyboardButton] = [] + if page > 0: + nav_row.append(InlineKeyboardButton(self._t(update, "diff.button_prev_page"), callback_data=f"diffpage:{page - 1}")) + if page < total_pages - 1: + nav_row.append(InlineKeyboardButton(self._t(update, "diff.button_next_page"), callback_data=f"diffpage:{page + 1}")) + if nav_row: + rows.append(nav_row) + return rows + + def _build_diff_message( + self, + update: Update, + session: dict[str, object], + *, + branch_name: str, + tracked_files: list[str], + untracked_files: list[str], + page: int, + ) -> tuple[str, InlineKeyboardMarkup | None]: + total_pages = max(1, (len(tracked_files) + self.DIFF_BUTTON_PAGE_SIZE - 1) // self.DIFF_BUTTON_PAGE_SIZE) + page = min(max(page, 0), total_pages - 1) + start = page * self.DIFF_BUTTON_PAGE_SIZE + page_files = tracked_files[start : start + self.DIFF_BUTTON_PAGE_SIZE] + lines = [ + self._t(update, "diff.session_label", session_name=session["name"]), + f"{self._t(update, 'diff.project_label', project_folder=session['project_folder'])} <{branch_name}>", + "", + self._t(update, "diff.tracked_files"), + ] + if page_files: + if len(tracked_files) > len(page_files): + lines.append( + self._t( + update, + "diff.tracked_files_page_info", + start=start + 1, + end=start + len(page_files), + total=len(tracked_files), + ) + ) + lines.extend(f"{start + index}. {path}" for index, path in enumerate(page_files, start=1)) + else: + lines.append(f"- {self._t(update, 'diff.none')}") + lines.extend(["", self._t(update, "diff.untracked_files")]) + if untracked_files: + lines.extend(f"- {path}" for path in untracked_files) + else: + lines.append(f"- {self._t(update, 'diff.none')}") + if tracked_files: + lines.extend(["", self._t(update, "diff.click_button_to_see_file_diff")]) + reply_markup = InlineKeyboardMarkup(self._build_diff_button_rows(update, tracked_files, page=page)) if tracked_files else None + return "\n".join(lines), reply_markup + + async def _refresh_branch_with_checkout( + self, + update: Update, + context: ContextTypes.DEFAULT_TYPE, + *, + project_path, + branch_name: str, + ) -> tuple[bool, str | None, tuple[str, ...]]: + current_branch = self.git.current_branch(project_path) + if current_branch != branch_name: + checkout = await asyncio.to_thread(self.git.checkout_branch, project_path, branch_name) + if not checkout.success: + return False, checkout.message, () + + result = await asyncio.to_thread(self.git.refresh_current_branch, project_path) + if not result.success: + return False, result.message, () + return True, result.message, tuple(result.warnings) + + def _generated_commit_commands(self) -> dict[int, dict[str, str]]: + commands = getattr(self, "_chat_generated_commit_commands", None) + if not isinstance(commands, dict): + commands = {} + self._chat_generated_commit_commands = commands + return commands + + def _extract_generated_commit_command(self, assistant_text: str) -> str | None: + for segment in split_assistant_output(assistant_text or ""): + if segment.kind != "code": + continue + lines = [line.strip() for line in segment.text.splitlines() if line.strip()] + if not lines or not lines[0].startswith("git add "): + continue + command = " ".join(line.removesuffix("\\").strip() for line in lines) + if "git commit " in command: + return command + stripped_lines = [line.strip() for line in (assistant_text or "").splitlines() if line.strip()] + if stripped_lines and stripped_lines[0].startswith("git add "): + command = " ".join(line.removesuffix("\\").strip() for line in stripped_lines) + if "git commit " in command: + return command + return None + @require_allowed_chat() async def handle_commit(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: if await self._notify_if_current_project_busy(update, context): @@ -29,7 +165,34 @@ async def handle_commit(self, update: Update, context: ContextTypes.DEFAULT_TYPE raw = update.message.text.partition(" ")[2].strip() if not raw: - await send_text(update, context, self._t(update, "git.usage_commit")) + session, project_path = await self._active_session_project_or_notify( + update, + context, + require_git_repo=True, + ) + if session is None or project_path is None: + return + confirm_markup = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + self._t(update, "git.commit_generate_button"), + callback_data="commitgen:confirm", + **self._affirmative_inline_button_kwargs(), + ), + InlineKeyboardButton( + self._t(update, "git.cancel_button"), + callback_data="commitgen:cancel", + **self._negative_inline_button_kwargs(), + ), + ] + ] + ) + await context.bot.send_message( + chat_id=update.effective_chat.id, + text=f"{self._t(update, 'git.usage_commit')}\n\n{self._t(update, 'git.commit_generate_prompt')}", + reply_markup=confirm_markup, + ) return session, project_path = await self._active_session_project_or_notify( @@ -70,6 +233,236 @@ async def handle_commit(self, update: Update, context: ContextTypes.DEFAULT_TYPE await send_html_text(update, context, self._bash_block(self._format_git_response(command_results, ignored))) + @require_allowed_chat() + async def handle_diff(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + if context.args: + await send_text(update, context, self._t(update, "git.usage_diff")) + return + + session, project_path = await self._active_session_project_or_notify( + update, + context, + require_git_repo=True, + ) + if session is None or project_path is None: + return + + branch_name = session.get("branch_name") or self.git.current_branch(project_path) or self._t( + update, + "status.current_branch_placeholder", + ) + tracked_files, untracked_files = split_changed_files(project_path) + text, reply_markup = self._build_diff_message( + update, + session, + branch_name=branch_name, + tracked_files=tracked_files, + untracked_files=untracked_files, + page=0, + ) + await context.bot.send_message( + chat_id=update.effective_chat.id, + text=html.escape(text), + parse_mode="HTML", + reply_markup=reply_markup, + ) + + @require_allowed_chat(answer_callback=True) + async def handle_diff_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + if query is None: + return + + await query.answer() + data = (query.data or "").strip() + if data.startswith("diffpage:"): + try: + page = int(data.partition(":")[2]) + except ValueError: + return + session, project_path = await self._active_session_project_or_notify( + update, + context, + require_git_repo=True, + ) + if session is None or project_path is None: + return + branch_name = session.get("branch_name") or self.git.current_branch(project_path) or self._t( + update, + "status.current_branch_placeholder", + ) + tracked_files, untracked_files = split_changed_files(project_path) + text, reply_markup = self._build_diff_message( + update, + session, + branch_name=branch_name, + tracked_files=tracked_files, + untracked_files=untracked_files, + page=page, + ) + await query.edit_message_text( + text=html.escape(text), + parse_mode="HTML", + reply_markup=reply_markup, + ) + return + if not data.startswith("diffshow:"): + return + try: + file_index = int(data.partition(":")[2]) + except ValueError: + return + + session, project_path = await self._active_session_project_or_notify( + update, + context, + require_git_repo=True, + ) + if session is None or project_path is None: + return + + tracked_files, _ = split_changed_files(project_path) + if file_index < 0 or file_index >= len(tracked_files): + await send_text(update, context, self._t(update, "diff.none")) + return + + file_path = tracked_files[file_index] + diffs = collect_diffs(project_path, [file_path], include_cached=True) + if not diffs: + await send_text(update, context, self._t(update, "diff.none")) + return + + chunks = chunk_fenced_diff( + file_path, + diffs[0].diff, + self.deps.cfg.max_telegram_message_length, + locale=self._locale(update), + ) + if not chunks: + await send_text(update, context, self._t(update, "diff.none")) + return + for chunk in chunks: + await send_code_block( + update, + context, + chunk.header, + chunk.code, + language=chunk.language, + ) + + @require_allowed_chat(answer_callback=True) + async def handle_commit_generate_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + if query is None: + return + + await query.answer() + action = (query.data or "").strip() + if action == "commitgen:cancel": + await query.edit_message_text(self._t(update, "git.commit_generate_cancelled")) + return + if action != "commitgen:confirm": + return + + generated_command = await self._generate_commit_command_with_provider(update, context) + if generated_command is None: + await query.edit_message_text(self._t(update, "git.no_valid_commit_commands")) + return + + chat_state = self.deps.store.get_chat_state(self.deps.bot_id, update.effective_chat.id) + active_session_id = str(chat_state.get("active_session_id") or "").strip() + if not active_session_id: + await query.edit_message_text(self._t(update, "common.no_active_session")) + return + session = chat_state.get("sessions", {}).get(active_session_id) + if not isinstance(session, dict): + await query.edit_message_text(self._t(update, "common.no_active_session")) + return + + self._generated_commit_commands()[update.effective_chat.id] = { + "command": generated_command, + "session_id": active_session_id, + "project_folder": str(session.get("project_folder") or ""), + } + execute_markup = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + self._t(update, "git.commit_execute_button"), + callback_data="commitexec:confirm", + **self._affirmative_inline_button_kwargs(), + ), + InlineKeyboardButton( + self._t(update, "git.cancel_button"), + callback_data="commitexec:cancel", + **self._negative_inline_button_kwargs(), + ), + ] + ] + ) + await query.edit_message_text(self._t(update, "git.commit_generated_below")) + await context.bot.send_message(chat_id=update.effective_chat.id, text=self._t(update, "git.commit_execute_prompt"), reply_markup=execute_markup) + + async def _generate_commit_command_with_provider(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> str | None: + session, project_path = await self._active_session_project_or_notify( + update, + context, + require_git_repo=True, + ) + if session is None or project_path is None: + return + + result = await self.runtime.run_active_session(update, context, user_message=self.COMMIT_GENERATION_PROMPT) + if result is None or not result.success: + return None + return self._extract_generated_commit_command(result.assistant_text) + + @require_allowed_chat(answer_callback=True) + async def handle_commit_execute_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + if query is None: + return + + await query.answer() + action = (query.data or "").strip() + if action == "commitexec:cancel": + await query.edit_message_text(self._t(update, "git.commit_generate_cancelled")) + return + if action != "commitexec:confirm": + return + + payload = self._generated_commit_commands().get(update.effective_chat.id) + if payload is None: + await query.edit_message_text(self._t(update, "git.no_valid_commit_commands")) + return + chat_state = self.deps.store.get_chat_state(self.deps.bot_id, update.effective_chat.id) + active_session_id = str(chat_state.get("active_session_id") or "").strip() + active_session = chat_state.get("sessions", {}).get(active_session_id) if active_session_id else None + active_project_folder = str(active_session.get("project_folder") or "") if isinstance(active_session, dict) else "" + if ( + active_session_id != str(payload.get("session_id") or "") + or active_project_folder != str(payload.get("project_folder") or "") + ): + self._generated_commit_commands().pop(update.effective_chat.id, None) + await query.edit_message_text(self._t(update, "git.commit_execute_context_changed")) + return + command = str(payload.get("command") or "").strip() + if not command: + self._generated_commit_commands().pop(update.effective_chat.id, None) + await query.edit_message_text(self._t(update, "git.no_valid_commit_commands")) + return + + await query.edit_message_text(self._t(update, "git.commit_execute_confirmed")) + synthetic_update = SimpleNamespace( + effective_chat=update.effective_chat, + message=SimpleNamespace(text=f"/commit {command}"), + ) + synthetic_context = SimpleNamespace(args=[], bot=context.bot) + try: + await self.handle_commit(synthetic_update, synthetic_context) + finally: + self._generated_commit_commands().pop(update.effective_chat.id, None) + @require_allowed_chat() async def handle_push(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: if await self._notify_if_current_project_busy(update, context): @@ -114,6 +507,132 @@ async def handle_push(self, update: Update, context: ContextTypes.DEFAULT_TYPE) reply_markup=confirm_markup, ) + @require_allowed_chat() + async def handle_pull(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + if await self._notify_if_current_project_busy(update, context): + return + if context.args: + await send_text(update, context, self._t(update, "git.usage_pull")) + return + + session, project_path = await self._active_session_project_or_notify( + update, + context, + require_git_repo=True, + ) + if session is None or project_path is None: + return + + branch_name = session.get("branch_name") or self.git.current_branch(project_path) + if not branch_name: + await send_text(update, context, self._t(update, "git.branch_unknown")) + return + + default_branch = self.git.default_branch(project_path) or branch_name + prompt_key = "git.pull_confirm_prompt_with_default" if default_branch and default_branch != branch_name else "git.pull_confirm_prompt" + + confirm_markup = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + self._t(update, "git.pull_confirm_button"), + callback_data="pull:confirm", + **self._affirmative_inline_button_kwargs(), + ), + InlineKeyboardButton( + self._t(update, "git.cancel_button"), + callback_data="pull:cancel", + **self._negative_inline_button_kwargs(), + ), + ] + ] + ) + await context.bot.send_message( + chat_id=update.effective_chat.id, + text=self._t( + update, + prompt_key, + branch_name=branch_name, + default_branch=default_branch, + ), + parse_mode="Markdown", + reply_markup=confirm_markup, + ) + + @require_allowed_chat(answer_callback=True) + async def handle_pull_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + if query is None: + return + + action = (query.data or "").strip() + if action == "pull:cancel": + await query.edit_message_text(self._t(update, "git.pull_cancelled")) + return + if action != "pull:confirm": + return + + session, project_path = await self._active_session_project_or_notify( + update, + context, + require_git_repo=True, + ) + if session is None or project_path is None: + return + + branch_name = session.get("branch_name") or self.git.current_branch(project_path) + if not branch_name: + await query.edit_message_text(self._t(update, "git.branch_unknown")) + return + + default_branch = self.git.default_branch(project_path) or branch_name + prompt_key = "git.pull_in_progress_with_default" if default_branch and default_branch != branch_name else "git.pull_in_progress" + await query.edit_message_text( + self._t( + update, + prompt_key, + branch_name=branch_name, + default_branch=default_branch, + ), + parse_mode="Markdown", + ) + + completed_messages: list[str] = [] + warnings: list[str] = [] + + if default_branch and default_branch != branch_name: + ok, message, branch_warnings = await self._refresh_branch_with_checkout( + update, + context, + project_path=project_path, + branch_name=default_branch, + ) + if not ok: + await send_text(update, context, message or self._t(update, "bot.error.command_failed")) + return + if message: + completed_messages.append(message) + warnings.extend(branch_warnings) + + ok, message, branch_warnings = await self._refresh_branch_with_checkout( + update, + context, + project_path=project_path, + branch_name=branch_name, + ) + if not ok: + await send_text(update, context, message or self._t(update, "bot.error.command_failed")) + return + if message: + completed_messages.append(message) + warnings.extend(branch_warnings) + + lines = completed_messages or [self._t(update, "git.pull_completed")] + if warnings: + lines.extend(["", self._t(update, "project.refresh_warnings")]) + lines.extend(f"- {warning}" for warning in warnings) + await send_text(update, context, "\n".join(lines)) + @require_allowed_chat(answer_callback=True) async def handle_push_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: query = update.callback_query diff --git a/src/coding_agent_telegram/router/message_commands.py b/src/coding_agent_telegram/router/message_commands.py index aafad8c..a4a6769 100644 --- a/src/coding_agent_telegram/router/message_commands.py +++ b/src/coding_agent_telegram/router/message_commands.py @@ -29,11 +29,19 @@ async def _process_user_message( suppress_working_notice: bool = False, ) -> None: chat_id = update.effective_chat.id + pending_action = self._pending_action(chat_id) + should_prioritize_existing_queue = ( + self._has_pending_queue_files(chat_id) + and not self._is_project_busy(chat_id) + and not self._has_pending_queue_decision(chat_id) + and not isinstance(pending_action, dict) + ) if self._should_queue_incoming_message(chat_id): _queue_file, question_number = self._enqueue_chat_message( chat_id, user_message, reply_to_message_id=getattr(update.message, "message_id", None), + separate_batch=should_prioritize_existing_queue, ) logger.info( "Queued user message for chat %s as Q%s. Preview: %.120r", @@ -47,6 +55,8 @@ async def _process_user_message( self._t(update, "message.question_queued", question_number=question_number), reply_to_message_id=getattr(update.message, "message_id", None), ) + if should_prioritize_existing_queue: + await self._drain_chat_message_queue(chat_id, context) return logger.info("Processing user message immediately for chat %s. Preview: %.120r", chat_id, user_message) self._store_pending_action( @@ -97,7 +107,16 @@ async def handle_photo(self, update: Update, context: ContextTypes.DEFAULT_TYPE) await send_text(update, context, error_text) return prompt = self.photo_attachments.build_prompt(attachment_path, project_path, caption) - await self.runtime.run_active_session(update, context, user_message=prompt, image_paths=(attachment_path,)) + chat_id = update.effective_chat.id + try: + self._last_run_results[chat_id] = await self.runtime.run_active_session( + update, + context, + user_message=prompt, + image_paths=(attachment_path,), + ) + finally: + await self._drain_chat_message_queue(chat_id, context) async def _handle_audio_like( self, diff --git a/src/coding_agent_telegram/router/queue_processing.py b/src/coding_agent_telegram/router/queue_processing.py index 2386e7a..9707a12 100644 --- a/src/coding_agent_telegram/router/queue_processing.py +++ b/src/coding_agent_telegram/router/queue_processing.py @@ -120,10 +120,11 @@ def _enqueue_chat_message( user_message: str, *, reply_to_message_id: int | None = None, + separate_batch: bool = False, ) -> tuple[Path, int]: queue = self._chat_message_queue_files.setdefault(chat_id, deque()) - queue_file = queue[-1] if queue else self._next_queue_file_path(chat_id) - if not queue: + queue_file = self._next_queue_file_path(chat_id) if separate_batch or not queue else queue[-1] + if separate_batch or not queue: queue.append(queue_file) question_number = self._append_question_to_queue_file( queue_file, diff --git a/src/coding_agent_telegram/router/session_common.py b/src/coding_agent_telegram/router/session_common.py index 5381a3c..2ee0669 100644 --- a/src/coding_agent_telegram/router/session_common.py +++ b/src/coding_agent_telegram/router/session_common.py @@ -47,6 +47,7 @@ def _should_queue_incoming_message(self, chat_id: int) -> bool: pending_action = self._pending_action(chat_id) return ( self._is_project_busy(chat_id) + or self._has_pending_queue_files(chat_id) or self._has_pending_queue_decision(chat_id) or isinstance(pending_action, dict) ) diff --git a/src/coding_agent_telegram/router/switch_commands.py b/src/coding_agent_telegram/router/switch_commands.py index 80773a5..b396b9b 100644 --- a/src/coding_agent_telegram/router/switch_commands.py +++ b/src/coding_agent_telegram/router/switch_commands.py @@ -1,7 +1,7 @@ from __future__ import annotations import html -from telegram import Update +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import ContextTypes from coding_agent_telegram.filters import resolve_project_path @@ -92,7 +92,7 @@ def _build_switch_page_from_entries( entries: list[dict[str, str]], current_project_folder: str | None, page: int, - ) -> str: + ) -> tuple[str, InlineKeyboardMarkup | None]: locale = self._chat_locale(chat_id) total_sessions = len(entries) total_pages = max(1, (total_sessions + self.SWITCH_PAGE_SIZE - 1) // self.SWITCH_PAGE_SIZE) @@ -128,13 +128,29 @@ def _build_switch_page_from_entries( [ translate(locale, "switch.use_label"), "/switch <session_id>", - f"/switch page {page}", translate(locale, "switch.native_import_note"), ] ) + reply_markup = None if total_pages > 1: - lines.append(translate(locale, "switch.pages_label", total_pages=total_pages)) - return "\n".join(lines).strip() + nav_buttons: list[InlineKeyboardButton] = [] + if page > 1: + nav_buttons.append( + InlineKeyboardButton( + translate(locale, "diff.button_prev_page"), + callback_data=f"switchpage:{page - 1}", + ) + ) + if page < total_pages: + nav_buttons.append( + InlineKeyboardButton( + translate(locale, "diff.button_next_page"), + callback_data=f"switchpage:{page + 1}", + ) + ) + if nav_buttons: + reply_markup = InlineKeyboardMarkup([nav_buttons]) + return "\n".join(lines).strip(), reply_markup @require_allowed_chat() async def handle_switch(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: @@ -148,7 +164,13 @@ async def handle_switch(self, update: Update, context: ContextTypes.DEFAULT_TYPE await send_text(update, context, self._t(update, "switch.no_sessions_found")) return logger.info("Listed sessions page 1 for chat %s (%d sessions total).", chat_id, len(entries)) - await send_html_text(update, context, self._build_switch_page_from_entries(chat_id, entries, current_project_folder, 1)) + text, reply_markup = self._build_switch_page_from_entries(chat_id, entries, current_project_folder, 1) + await context.bot.send_message( + chat_id=chat_id, + text=text, + parse_mode="HTML", + reply_markup=reply_markup, + ) return if len(context.args) == 2 and context.args[0].lower() == "page": @@ -164,7 +186,13 @@ async def handle_switch(self, update: Update, context: ContextTypes.DEFAULT_TYPE await send_text(update, context, self._t(update, "switch.invalid_page_number")) return logger.info("Listed sessions page %d for chat %s (%d sessions total).", page, chat_id, len(entries)) - await send_html_text(update, context, self._build_switch_page_from_entries(chat_id, entries, current_project_folder, page)) + text, reply_markup = self._build_switch_page_from_entries(chat_id, entries, current_project_folder, page) + await context.bot.send_message( + chat_id=chat_id, + text=text, + parse_mode="HTML", + reply_markup=reply_markup, + ) return session_id = " ".join(context.args).strip() @@ -233,3 +261,32 @@ async def handle_switch(self, update: Update, context: ContextTypes.DEFAULT_TYPE ) ), ) + + @require_allowed_chat(answer_callback=True) + async def handle_switch_page_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + if query is None: + return + + await query.answer() + data = (query.data or "").strip() + if not data.startswith("switchpage:"): + return + try: + page = int(data.partition(":")[2]) + except ValueError: + return + if page < 1: + return + + chat_id = update.effective_chat.id + entries, current_project_folder = self._switch_listing_entries(chat_id) + if not entries: + await query.edit_message_text(self._t(update, "switch.no_sessions_found")) + return + text, reply_markup = self._build_switch_page_from_entries(chat_id, entries, current_project_folder, page) + await query.edit_message_text( + text=text, + parse_mode="HTML", + reply_markup=reply_markup, + ) diff --git a/tests/test_bot.py b/tests/test_bot.py index a37a25e..6348af3 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -5,7 +5,7 @@ def test_default_bot_commands_hide_commit_and_push_when_disabled(): commands = default_bot_commands(enable_commit_command=False) names = [command.command for command in commands] - assert names == ["provider", "project", "branch", "current", "new", "switch", "compact", "push", "abort"] + assert names == ["provider", "project", "branch", "current", "new", "switch", "compact", "diff", "pull", "push", "abort"] assert "commit" not in names @@ -13,7 +13,7 @@ def test_default_bot_commands_show_commit_and_push_when_enabled(): commands = default_bot_commands(enable_commit_command=True) names = [command.command for command in commands] - assert names == ["provider", "project", "branch", "current", "new", "switch", "compact", "commit", "push", "abort"] + assert names == ["provider", "project", "branch", "current", "new", "switch", "compact", "diff", "commit", "pull", "push", "abort"] # --------------------------------------------------------------------------- diff --git a/tests/test_command_router.py b/tests/test_command_router.py index d0f3de9..e2fc1ac 100644 --- a/tests/test_command_router.py +++ b/tests/test_command_router.py @@ -395,6 +395,7 @@ def __init__( self.git_commands = [] self.safe_git_commands = [] self.push_calls = [] + self.refresh_calls = [] self.prepare_from_source_calls = [] def is_git_repo(self, project_path): @@ -416,7 +417,19 @@ def remote_branch_exists(self, project_path, branch_name): return branch_name in self._local_branches or branch_name == self._default_branch def refresh_current_branch(self, project_path): - return self.refresh_result + self.refresh_calls.append((project_path, self._current_branch)) + if self.refresh_result is not None: + message = getattr(self.refresh_result, "message", None) or f"Updated branch '{self._current_branch}' from origin." + return SimpleNamespace( + success=getattr(self.refresh_result, "success", True), + message=message, + warnings=getattr(self.refresh_result, "warnings", ()), + ) + return SimpleNamespace( + success=True, + message=f"Updated branch '{self._current_branch}' from origin.", + warnings=(), + ) def prepare_branch(self, project_path, *, origin_branch, new_branch): return self.prepare_result @@ -1908,8 +1921,12 @@ def test_switch_lists_latest_10_sessions_by_default(tmp_path: Path): assert "session-7" in message assert "session-6" not in message assert "session-4" not in message - assert "Pages: /switch page 1 ... /switch page 3" in message assert "/switch <session_id>" in message + reply_markup = bot.messages[-1][3] + assert reply_markup is not None + buttons = reply_markup.inline_keyboard[0] + assert [button.text for button in buttons] == ["Next"] + assert [button.callback_data for button in buttons] == ["switchpage:2"] def test_switch_lists_requested_page(tmp_path: Path): @@ -1934,6 +1951,48 @@ def test_switch_lists_requested_page(tmp_path: Path): assert "session-9" not in message +def test_switch_page_callback_edits_listing_with_inline_pagination(tmp_path: Path): + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + + for idx in range(12): + store.create_session("bot-a", 123, f"sess_{idx}", f"session-{idx}", "backend", "codex") + + edited = [] + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="switchpage:2", + answer=None, + edit_message_text=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + async def fake_edit(text, parse_mode=None, reply_markup=None): + edited.append((text, parse_mode, reply_markup)) + + update.callback_query.answer = fake_answer + update.callback_query.edit_message_text = fake_edit + + asyncio.run(router.handle_switch_page_callback(update, context)) + + assert "Available sessions (page 2/3):" in edited[-1][0] + assert "session-4" in edited[-1][0] + assert "session-2" in edited[-1][0] + reply_markup = edited[-1][2] + assert reply_markup is not None + buttons = reply_markup.inline_keyboard[0] + assert [button.text for button in buttons] == ["Prev", "Next"] + assert [button.callback_data for button in buttons] == ["switchpage:1", "switchpage:3"] + + def test_switch_lists_mixed_bot_and_native_project_sessions_with_legend(tmp_path: Path, monkeypatch): home = tmp_path / "home" monkeypatch.setenv("HOME", str(home)) @@ -2545,6 +2604,45 @@ async def exercise(): asyncio.run(exercise()) +def test_text_message_is_processed_after_photo_triggered_run_finishes(tmp_path: Path): + backend = tmp_path / "backend" + backend.mkdir() + runner = BlockingRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_photo", "photo-session", "backend", "codex") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=False) + + async def exercise(): + bot = FakeBot() + photo = FakePhotoSize(FakeTelegramFile(b"fake-image-bytes", "photos/pic.png")) + photo_update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + message=SimpleNamespace(text=None, photo=[photo], caption="what is shown here?", message_id=101), + ) + text_update = make_update(text="follow-up text question", message_id=202) + + photo_task = asyncio.create_task(router.handle_photo(photo_update, SimpleNamespace(args=[], bot=bot))) + started = await asyncio.to_thread(runner.wait_started, 1, 1.0) + assert started is True + + await router.handle_message(text_update, SimpleNamespace(args=[], bot=bot)) + assert any("Question queued as Q1." in message for _, message, _, _ in bot.messages) + + runner.release_next() + started_second = await asyncio.to_thread(runner.wait_started, 2, 1.0) + assert started_second is True + runner.release_next() + await photo_task + + assert len(runner.resume_calls) == 2 + assert "Open and inspect that image before answering." in runner.resume_calls[0]["user_message"] + assert runner.resume_calls[1]["user_message"] == "follow-up text question" + + asyncio.run(exercise()) + + def test_busy_queue_and_final_output_reply_to_original_message(tmp_path: Path): backend = tmp_path / "backend" backend.mkdir() @@ -2857,6 +2955,37 @@ async def fake_edit(text, reply_markup=None): asyncio.run(exercise()) +def test_new_message_queues_behind_existing_backlog_and_triggers_drain(tmp_path: Path): + backend = tmp_path / "backend" + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_queue", "queue-session", "backend", "codex") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=False) + + queue_file, question_number = router._enqueue_chat_message(123, "older queued question", reply_to_message_id=101) + assert question_number == 1 + assert queue_file.exists() + + bot = FakeBot() + update = make_update(text="newer question", message_id=202) + context = SimpleNamespace(args=[], bot=bot) + + asyncio.run(router.handle_message(update, context)) + + assert any("Question queued as Q1." in entry["text"] for entry in bot.sent_messages) + assert runner.create_calls == [] + assert [call["user_message"] for call in runner.resume_calls] == [ + "older queued question", + "newer question", + ] + assert store.get_chat_state("bot-a", 123).get("pending_action") is None + assert not queue_file.exists() + assert not router._has_pending_queue_files(123) + + def test_message_prompts_for_branch_discrepancy_before_running_bot_managed_session(tmp_path: Path): backend = tmp_path / "backend" backend.mkdir() @@ -4184,6 +4313,22 @@ def _run_push_command(router: CommandRouter) -> FakeBot: return bot +def _run_pull_command(router: CommandRouter, *, args: list[str] | None = None) -> FakeBot: + update = make_update(text="/pull" if not args else "/pull " + " ".join(args)) + bot = FakeBot() + context = SimpleNamespace(args=args or [], bot=bot) + asyncio.run(router.handle_pull(update, context)) + return bot + + +def _run_diff_command(router: CommandRouter, *, args: list[str] | None = None) -> FakeBot: + update = make_update(text="/diff" if not args else "/diff " + " ".join(args)) + bot = FakeBot() + context = SimpleNamespace(args=args or [], bot=bot) + asyncio.run(router.handle_diff(update, context)) + return bot + + def test_commit_executes_only_valid_git_commands_and_ignores_non_git_segments(tmp_path: Path): router, backend = _make_commit_router( tmp_path, @@ -4616,9 +4761,323 @@ async def fake_edit(text): asyncio.run(router.handle_push_callback(update, context)) assert edited == ["Push cancelled."] + + +def test_pull_refreshes_active_session_branch(tmp_path: Path): + backend = (tmp_path / "backend").resolve() + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_pull", "pull-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager( + is_git_repo=True, + current_branch="feature-1", + ) + router.git.refresh_result = SimpleNamespace( + success=True, + message="Updated branch 'feature-1' from origin.", + warnings=(), + ) + router.runtime.git = router.git + + bot = _run_pull_command(router) + + assert router.git.refresh_calls == [] + assert bot.messages[-1][1] == "Pull branch `feature-1` from `origin`?" + buttons = bot.messages[-1][3].inline_keyboard[0] + assert buttons[0].callback_data == "pull:confirm" + assert buttons[1].callback_data == "pull:cancel" + assert buttons[0].text == "Confirm pull" + assert buttons[1].text == "Cancel" + + +def test_pull_sends_usage_when_extra_args_provided(tmp_path: Path): + backend = tmp_path / "backend" + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_pull", "pull-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=True, current_branch="feature-1") + router.runtime.git = router.git + + bot = _run_pull_command(router, args=["extra"]) + + assert router.git.refresh_calls == [] + assert bot.messages[-1][1] == "Usage: /pull" + + +def test_pull_confirmation_refreshes_default_and_session_branch(tmp_path: Path): + backend = (tmp_path / "backend").resolve() + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_pull", "pull-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager( + is_git_repo=True, + current_branch="main", + default_branch="develop", + checkout_result=SimpleNamespace(success=True, message="Checked out branch"), + ) + router.git.refresh_result = SimpleNamespace( + success=True, + warnings=("git fetch origin failed.",), + ) + router.runtime.git = router.git + + edited = [] + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="pull:confirm", + answer=None, + edit_message_text=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + async def fake_edit(text, parse_mode=None): + edited.append((text, parse_mode)) + + update.callback_query.answer = fake_answer + update.callback_query.edit_message_text = fake_edit + + asyncio.run(router.handle_pull_callback(update, context)) + + assert edited == [("Pulling branch `feature-1` from `origin` and refreshing default branch `develop`...", "Markdown")] + assert router.git.refresh_calls == [ + (backend, "develop"), + (backend, "feature-1"), + ] + assert "Updated branch 'develop' from origin." in bot.messages[-1][1] + assert "Updated branch 'feature-1' from origin." in bot.messages[-1][1] + assert "Refresh warnings:" in bot.messages[-1][1] + assert "- git fetch origin failed." in bot.messages[-1][1] assert router.git.push_calls == [] +def test_pull_confirmation_cancel_does_not_refresh(tmp_path: Path): + backend = (tmp_path / "backend").resolve() + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_pull", "pull-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=True, current_branch="feature-1") + router.runtime.git = router.git + edited = [] + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="pull:cancel", + answer=None, + edit_message_text=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + async def fake_edit(text): + edited.append(text) + + update.callback_query.answer = fake_answer + update.callback_query.edit_message_text = fake_edit + + asyncio.run(router.handle_pull_callback(update, context)) + + assert edited == ["Pull cancelled."] + assert router.git.refresh_calls == [] + + +def test_diff_lists_tracked_and_untracked_filenames(monkeypatch, tmp_path: Path): + backend = (tmp_path / "backend").resolve() + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_diff", "diff-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=True, current_branch="feature-1") + router.runtime.git = router.git + monkeypatch.setattr( + "coding_agent_telegram.router.git_commands.split_changed_files", + lambda _project_path: (["src/app.py"], ["notes/todo.md"]), + ) + + bot = _run_diff_command(router) + + assert "Session: diff-session" in bot.messages[-1][1] + assert "Project: backend <feature-1>" in bot.messages[-1][1] + assert "Tracked changed files:" in bot.messages[-1][1] + assert "Untracked files:" in bot.messages[-1][1] + assert "Click a button below to see a file diff." in bot.messages[-1][1] + assert "1. src/app.py" in bot.messages[-1][1] + assert "- notes/todo.md" in bot.messages[-1][1] + reply_markup = bot.messages[-1][3] + assert reply_markup is not None + labels = [button.text for row in reply_markup.inline_keyboard for button in row] + callback_data = [button.callback_data for row in reply_markup.inline_keyboard for button in row] + assert labels == ["1. app.py"] + assert callback_data == ["diffshow:0"] + + +def test_diff_callback_sends_selected_file_diff(monkeypatch, tmp_path: Path): + backend = (tmp_path / "backend").resolve() + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_diff", "diff-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=True, current_branch="feature-1") + router.runtime.git = router.git + monkeypatch.setattr( + "coding_agent_telegram.router.git_commands.split_changed_files", + lambda _project_path: (["src/app.py", "src/worker.py"], []), + ) + monkeypatch.setattr( + "coding_agent_telegram.router.git_commands.collect_diffs", + lambda _project_path, files, *, against_ref=None, include_cached=False: [ + SimpleNamespace(path=files[0], diff="--- a/src/worker.py\n+++ b/src/worker.py\n@@\n-old\n+new") + ] + if include_cached + else [], + ) + + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="diffshow:1", + answer=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + update.callback_query.answer = fake_answer + + asyncio.run(router.handle_diff_callback(update, context)) + + assert "src/worker.py (+1 -1) (1/1)" in bot.messages[-2][1] + assert "old" in bot.messages[-1][1] + assert "new" in bot.messages[-1][1] + + +def test_diff_limits_buttons_to_ten_per_page(monkeypatch, tmp_path: Path): + backend = (tmp_path / "backend").resolve() + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_diff", "diff-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=True, current_branch="feature-1") + router.runtime.git = router.git + tracked_files = [f"src/file_{index}.py" for index in range(1, 13)] + monkeypatch.setattr( + "coding_agent_telegram.router.git_commands.split_changed_files", + lambda _project_path: (tracked_files, []), + ) + + bot = _run_diff_command(router) + + reply_markup = bot.messages[-1][3] + assert reply_markup is not None + rows = reply_markup.inline_keyboard + file_buttons = [button for row in rows[:-1] for button in row] + nav_buttons = rows[-1] + assert len(file_buttons) == 10 + assert [button.text for button in file_buttons[:3]] == ["1. file_1.py", "2. file_2.py", "3. file_3.py"] + assert [button.callback_data for button in file_buttons[-2:]] == ["diffshow:8", "diffshow:9"] + assert [button.text for button in nav_buttons] == ["Next"] + assert [button.callback_data for button in nav_buttons] == ["diffpage:1"] + assert "Showing 1-10 of 12." in bot.messages[-1][1] + assert "10. src/file_10.py" in bot.messages[-1][1] + assert "11. src/file_11.py" not in bot.messages[-1][1] + + +def test_diff_pagination_edits_message_for_next_page(monkeypatch, tmp_path: Path): + backend = (tmp_path / "backend").resolve() + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_diff", "diff-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=True, current_branch="feature-1") + router.runtime.git = router.git + tracked_files = [f"src/file_{index}.py" for index in range(1, 13)] + monkeypatch.setattr( + "coding_agent_telegram.router.git_commands.split_changed_files", + lambda _project_path: (tracked_files, []), + ) + + edited = [] + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="diffpage:1", + answer=None, + edit_message_text=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + async def fake_edit(text, parse_mode=None, reply_markup=None): + edited.append((text, parse_mode, reply_markup)) + + update.callback_query.answer = fake_answer + update.callback_query.edit_message_text = fake_edit + + asyncio.run(router.handle_diff_callback(update, context)) + + assert "Showing 11-12 of 12." in edited[-1][0] + assert "11. src/file_11.py" in edited[-1][0] + assert "10. src/file_10.py" not in edited[-1][0] + reply_markup = edited[-1][2] + assert reply_markup is not None + labels = [button.text for row in reply_markup.inline_keyboard for button in row] + callback_data = [button.callback_data for row in reply_markup.inline_keyboard for button in row] + assert "Prev" in labels + assert callback_data[-1] == "diffpage:0" + + +def test_diff_sends_usage_when_extra_args_provided(tmp_path: Path): + backend = tmp_path / "backend" + backend.mkdir() + runner = DummyRunner() + cfg = make_config(tmp_path) + store = SessionStore(cfg.state_file, cfg.state_backup_file) + store.create_session("bot-a", 123, "sess_diff", "diff-session", "backend", "codex", branch_name="feature-1") + router = CommandRouter(RouterDeps(cfg=cfg, store=store, agent_runner=runner, bot_id="bot-a")) + router.git = FakeGitManager(is_git_repo=True, current_branch="feature-1") + router.runtime.git = router.git + + bot = _run_diff_command(router, args=["extra"]) + + assert bot.messages[-1][1] == "Usage: /diff" + + def test_push_reports_missing_project_folder_before_git_calls(tmp_path: Path): backend = (tmp_path / "backend").resolve() backend.mkdir() @@ -4946,6 +5405,7 @@ def test_switch_command_lists_sessions_when_no_arg(tmp_path: Path): asyncio.run(router.handle_switch(update, context)) assert "my-session" in bot.messages[-1][1] + assert bot.messages[-1][3] is None def test_switch_command_handles_page_arg(tmp_path: Path): @@ -5223,15 +5683,183 @@ def test_commit_disabled_sends_message(tmp_path: Path): assert "/commit is disabled" in bot.messages[-1][1] -def test_commit_no_args_sends_usage(tmp_path: Path): +def test_commit_no_args_shows_generate_prompt(monkeypatch, tmp_path: Path): router, _ = _make_commit_router(tmp_path, git_manager=FakeGitManager(is_git_repo=True)) + monkeypatch.setattr( + "coding_agent_telegram.router.git_commands.split_changed_files", + lambda _project_path: (["src/app.py"], ["notes/todo.md"]), + ) update = make_update(text="/commit") bot = FakeBot() context = SimpleNamespace(args=[], bot=bot) asyncio.run(router.handle_commit(update, context)) - assert "Usage: /commit" in bot.messages[-1][1] + assert "Usage: /commit git add ... && git commit ..." in bot.messages[-1][1] + assert "Generate a git commit command for the current changed files?" in bot.messages[-1][1] + reply_markup = bot.messages[-1][3] + assert reply_markup is not None + buttons = reply_markup.inline_keyboard[0] + assert buttons[0].text == "Generate command" + assert buttons[0].callback_data == "commitgen:confirm" + assert buttons[0].api_kwargs == {"style": "primary"} + assert buttons[1].text == "Cancel" + assert buttons[1].callback_data == "commitgen:cancel" + assert buttons[1].api_kwargs == {"style": "danger"} + + +def test_commit_generate_callback_sends_generated_command(monkeypatch, tmp_path: Path): + router, _ = _make_commit_router(tmp_path, git_manager=FakeGitManager(is_git_repo=True)) + monkeypatch.setattr("coding_agent_telegram.router.git_commands.split_changed_files", lambda _project_path: (["src/app.py"], ["notes/todo.md"])) + + async def fake_run_active_session(_update, _context, *, user_message, image_paths=(), suppress_working_notice=False): + assert "Generate a git commit command for the files you changed in this task" in user_message + return SimpleNamespace( + success=True, + assistant_text='git add src/app.py && git commit -m "Update app"', + ) + + router.runtime.run_active_session = fake_run_active_session + + edited = [] + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="commitgen:confirm", + answer=None, + edit_message_text=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + async def fake_edit(text): + edited.append(text) + + update.callback_query.answer = fake_answer + update.callback_query.edit_message_text = fake_edit + + asyncio.run(router.handle_commit_generate_callback(update, context)) + + assert edited == ["Generated commit command below."] + assert router._generated_commit_commands()[123] == { + "command": 'git add src/app.py && git commit -m "Update app"', + "session_id": "sess_commit", + "project_folder": "backend", + } + assert bot.messages[-1][1] == "Do you want to execute the commit?" + reply_markup = bot.messages[-1][3] + assert reply_markup is not None + buttons = reply_markup.inline_keyboard[0] + assert buttons[0].text == "Execute commit" + assert buttons[0].callback_data == "commitexec:confirm" + assert buttons[0].api_kwargs == {"style": "primary"} + assert buttons[1].text == "Cancel" + assert buttons[1].callback_data == "commitexec:cancel" + assert buttons[1].api_kwargs == {"style": "danger"} + + +def test_extract_generated_commit_command_accepts_multiline_shell_block(tmp_path: Path): + router, _ = _make_commit_router(tmp_path, git_manager=FakeGitManager(is_git_repo=True)) + + command = router._extract_generated_commit_command( + '```bash\n' + 'git add src/app.py \\\n' + ' && git commit -m "Update app"\n' + '```' + ) + + assert command == 'git add src/app.py && git commit -m "Update app"' + + +def test_commit_execute_callback_runs_generated_commit_command(monkeypatch, tmp_path: Path): + router, _ = _make_commit_router( + tmp_path, + git_manager=FakeGitManager( + is_git_repo=True, + git_command_results=[ + SimpleNamespace(success=True, message="git add completed."), + SimpleNamespace(success=True, message="git commit completed."), + ], + ), + ) + router._generated_commit_commands()[123] = { + "command": 'git add src/app.py && git commit -m "Update app"', + "session_id": "sess_commit", + "project_folder": "backend", + } + + edited = [] + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="commitexec:confirm", + answer=None, + edit_message_text=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + async def fake_edit(text): + edited.append(text) + + update.callback_query.answer = fake_answer + update.callback_query.edit_message_text = fake_edit + + asyncio.run(router.handle_commit_execute_callback(update, context)) + + assert edited == ["Executing generated commit command..."] + assert router.git.safe_git_commands[0][1] == ["add", "src/app.py"] + assert router.git.safe_git_commands[1][0] == (tmp_path / "backend").resolve() + assert router.git.safe_git_commands[1][1][0] == "commit" + assert any("$git add src/app.py" in message[1] for message in bot.messages) + assert any("$git commit -m 'Update app' --no-verify --no-post-rewrite" in message[1] for message in bot.messages) + assert any("[Completed]" in message[1] for message in bot.messages) + + +def test_commit_execute_callback_rejects_when_active_session_changes(tmp_path: Path): + router, _ = _make_commit_router(tmp_path, git_manager=FakeGitManager(is_git_repo=True)) + (tmp_path / "frontend").mkdir() + router.deps.store.create_session("bot-a", 123, "sess_other", "other-session", "frontend", "codex") + router._generated_commit_commands()[123] = { + "command": 'git add src/app.py && git commit -m "Update app"', + "session_id": "sess_commit", + "project_folder": "backend", + } + + edited = [] + update = SimpleNamespace( + effective_chat=SimpleNamespace(id=123, type="private"), + callback_query=SimpleNamespace( + data="commitexec:confirm", + answer=None, + edit_message_text=None, + ), + ) + bot = FakeBot() + context = SimpleNamespace(args=[], bot=bot) + + async def fake_answer(): + return None + + async def fake_edit(text): + edited.append(text) + + update.callback_query.answer = fake_answer + update.callback_query.edit_message_text = fake_edit + + asyncio.run(router.handle_commit_execute_callback(update, context)) + + assert edited == ["The active session or project changed. Please generate the commit command again."] + assert router.git.safe_git_commands == [] + assert 123 not in router._generated_commit_commands() def test_commit_no_valid_git_commands_found(tmp_path: Path): diff --git a/tests/test_diff_chunking.py b/tests/test_diff_chunking.py index 6bf6f2c..36099ea 100644 --- a/tests/test_diff_chunking.py +++ b/tests/test_diff_chunking.py @@ -14,6 +14,7 @@ collect_diffs, collect_snapshot_diffs, is_snapshot_excluded_path, + split_changed_files, snapshot_project_files, ) @@ -59,6 +60,19 @@ def test_parse_status_paths_includes_renames_and_untracked(): assert _parse_status_paths(output) == ["src/app.py", "src/new.py", "new.py"] +def test_split_changed_files_separates_tracked_and_untracked(monkeypatch, tmp_path: Path): + monkeypatch.setattr( + diff_utils_module, + "_git", + lambda _project_path, _args: " M src/app.py\n?? src/new.py\nR old.py -> new.py\n", + ) + + tracked, untracked = split_changed_files(tmp_path) + + assert tracked == ["src/app.py", "new.py"] + assert untracked == ["src/new.py"] + + def test_new_javascript_file_uses_language_code_block(): diff = "\n".join( [ @@ -220,6 +234,28 @@ def test_collect_diffs_excludes_snapshot_ignored_paths(tmp_path: Path): assert [diff.path for diff in diffs] == ["src/app.py"] +def test_collect_diffs_include_cached_includes_staged_only_changes(tmp_path: Path): + subprocess.run(["git", "init"], cwd=tmp_path, check=True, capture_output=True) + (tmp_path / "src").mkdir() + (tmp_path / "src" / "app.py").write_text("print('before')\n", encoding="utf-8") + subprocess.run(["git", "add", "src/app.py"], cwd=tmp_path, check=True, capture_output=True) + subprocess.run( + ["git", "-c", "user.name=Test User", "-c", "user.email=test@example.com", "commit", "-m", "init"], + cwd=tmp_path, + check=True, + capture_output=True, + ) + + (tmp_path / "src" / "app.py").write_text("print('after')\n", encoding="utf-8") + subprocess.run(["git", "add", "src/app.py"], cwd=tmp_path, check=True, capture_output=True) + + diffs = collect_diffs(tmp_path, ["src/app.py"], include_cached=True) + + assert len(diffs) == 1 + assert "-print('before')" in diffs[0].diff + assert "+print('after')" in diffs[0].diff + + def test_snapshot_override_globs_can_hide_hidden_paths(monkeypatch): monkeypatch.setenv("SNAPSHOT_EXCLUDE_PATH_GLOBS", ".*") diff_utils_module._snapshot_exclude_path_globs_from_env.cache_clear() From 17e6361e1ba40883ad50c5dc14e4b5d436200e37 Mon Sep 17 00:00:00 2001 From: DCHA <426225+daocha@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:32:41 +0800 Subject: [PATCH 2/5] Fix translations for labels (#43) Co-authored-by: DCHA Agent <259406208+dcha-agent@users.noreply.github.com> --- .../resources/locales/de.json | 124 +++++++------- .../resources/locales/fr.json | 116 ++++++------- .../resources/locales/ja.json | 126 ++++++++------- .../resources/locales/ko.json | 130 +++++++-------- .../resources/locales/nl.json | 124 +++++++------- .../resources/locales/th.json | 136 ++++++++-------- .../resources/locales/vi.json | 152 +++++++++--------- .../resources/locales/zh-CN.json | 146 ++++++++--------- .../resources/locales/zh-HK.json | 146 ++++++++--------- .../resources/locales/zh-TW.json | 146 ++++++++--------- 10 files changed, 683 insertions(+), 663 deletions(-) diff --git a/src/coding_agent_telegram/resources/locales/de.json b/src/coding_agent_telegram/resources/locales/de.json index 97e12c9..cf7ee86 100644 --- a/src/coding_agent_telegram/resources/locales/de.json +++ b/src/coding_agent_telegram/resources/locales/de.json @@ -1,14 +1,14 @@ { "bot.command.abort": "Aktuellen Agent-Lauf abbrechen", - "bot.command.branch": "Git branch erstellen und wechseln", + "bot.command.branch": "Git Zweig erstellen und wechseln", "bot.command.commit": "Geprüfte Git commit-Befehle ausführen", "bot.command.current": "Aktive Sitzung anzeigen", "bot.command.diff": "Geänderte Dateinamen gegenüber HEAD anzeigen", "bot.command.new": "Neue Sitzung erstellen", - "bot.command.pull": "Die aktuelle Sitzungs-branch pullen", + "bot.command.pull": "Die aktuelle Sitzungs-Zweig pullen", "bot.command.project": "Aktuellen Projektordner festlegen", - "bot.command.provider": "Provider für neue Sitzungen wählen", - "bot.command.push": "Die aktuelle Sitzungs-branch pushen", + "bot.command.provider": "Anbieter für neue Sitzungen wählen", + "bot.command.push": "Die aktuelle Sitzungs-Zweig pushen", "bot.command.switch": "Sitzungen auflisten oder wechseln", "bot.error.command_failed": "⚠️ Befehl fehlgeschlagen. Bitte das Server-Log prüfen.", "bot.error.session_store": "⚠️ {error}", @@ -18,22 +18,23 @@ "common.no_project_selected": "Kein Projekt ausgewählt.\nBitte zuerst /project ausführen.", "common.project_busy": "Auf Projekt '{project_folder}' läuft gerade ein Agent.\nBis zum Abschluss werden nur /current und /abort unterstützt.", "common.project_folder_missing": "⚠️ Der Projektordner für diese Sitzung existiert nicht mehr: {project_folder}", - "git.branch_unknown": "⚠️ Die branch der aktuellen Sitzung konnte nicht ermittelt werden.", + "common.button_expired": "⚠️ Diese Schaltfläche ist abgelaufen. Bitte führe den Befehl erneut aus.", + "git.branch_unknown": "⚠️ Die Zweig der aktuellen Sitzung konnte nicht ermittelt werden.", "git.cancel_button": "Abbrechen", "git.usage_diff": "Verwendung: /diff", "git.usage_pull": "Verwendung: /pull", "git.pull_cancelled": "Pull abgebrochen.", "git.pull_completed": "Pull abgeschlossen.", "git.pull_confirm_button": "Pull bestätigen", - "git.pull_confirm_prompt": "Branch `{branch_name}` von `origin` pullen?", - "git.pull_confirm_prompt_with_default": "Branch `{branch_name}` von `origin` pullen und zusätzlich die Standard-branch `{default_branch}` aktualisieren?", - "git.pull_in_progress": "Branch `{branch_name}` wird von `origin` gepullt...", - "git.pull_in_progress_with_default": "Branch `{branch_name}` wird von `origin` gepullt und die Standard-branch `{default_branch}` wird aktualisiert...", + "git.pull_confirm_prompt": "Zweig `{branch_name}` von `origin` pullen?", + "git.pull_confirm_prompt_with_default": "Zweig `{branch_name}` von `origin` pullen und zusätzlich die Standard-Zweig `{default_branch}` aktualisieren?", + "git.pull_in_progress": "Zweig `{branch_name}` wird von `origin` gepullt...", + "git.pull_in_progress_with_default": "Zweig `{branch_name}` wird von `origin` gepullt und die Standard-Zweig `{default_branch}` wird aktualisiert...", "git.push_cancelled": "Push abgebrochen.", "git.push_cancelled_checkout_failed": "Push abgebrochen. Wechsel zu `{branch_name}` ist zuerst fehlgeschlagen.", "git.push_confirm_button": "Push bestätigen", - "git.push_confirm_prompt": "Branch `{branch_name}` nach `origin` pushen?", - "git.push_in_progress": "Branch `{branch_name}` wird nach `origin` gepusht...", + "git.push_confirm_prompt": "Zweig `{branch_name}` nach `origin` pushen?", + "git.push_in_progress": "Zweig `{branch_name}` wird nach `origin` gepusht...", "git.usage_push": "Verwendung: /push", "message.photo_only_codex": "Fotoanhänge werden derzeit nur für Codex-Sitzungen unterstützt.", "message.question_queued": "Frage als Q{question_number} in die Warteschlange gestellt. Sie wird verarbeitet, sobald die aktuelle Agent-Aufgabe abgeschlossen ist.", @@ -60,6 +61,7 @@ "runtime.agent_run_failed": "Der Agent-Lauf ist fehlgeschlagen.", "runtime.live_agent_output": "Live-Ausgabe des Agenten", "runtime.photo_too_large": "Das Foto ist zu groß. Die maximal unterstützte Größe beträgt 5 MB.", + "runtime.voice_audio_too_large": "Die Audiodatei ist zu groß für die lokale Spracherkennung. Die maximal unterstützte Größe beträgt {max_size_mb} MB.", "runtime.provider_output_index": "{provider}-Ausgabe {index}/{total}", "runtime.provider_output_single": "{provider}-Ausgabe", "runtime.replacement_session_stall": "Die Erstellung der Ersatzsitzung scheint festzuhängen.\nDer lokale Agent-Prozess läuft noch, hat aber keine Ausgabe erzeugt.\nUnter macOS kann dies daran liegen, dass auf dem Rechner, auf dem der Bot läuft, ein versteckter Berechtigungsdialog auf eine Bestätigung wartet.", @@ -90,7 +92,7 @@ "switch.switched_to_session": "Zur Sitzung gewechselt", "switch.project_label": "Projekt", "switch.provider_label": "Anbieter", - "switch.branch_label": "Branch", + "switch.branch_label": "Zweig", "switch.source_label": "Quelle", "switch.imported_into_state_json": "In state.json importiert.", "switch.status_active": "aktiv", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "Wenn du später eine andere Sprache verwenden möchtest, ändere APP_LOCALE in {env_path}.", "queue.button_cancel": "Abbrechen", "queue.cancelled": "Die Warteschlangenfragen wurden verworfen.", - "project.branch_switch_requires_source": "Für den Wechsel zu branch {new_branch} muss zuerst eine Quelle gewählt werden.", - "project.branch_create_from_source": "Um die neue branch {new_branch} zu erstellen, wähle zuerst eine Quell-branch.", - "project.branch_source_missing": "Für das Projekt '{project_folder}' wurde keine branch-Quelle gefunden.\nEs fehlen local/{source_branch} und origin/{source_branch}.", + "project.branch_switch_requires_source": "Für den Wechsel zu Zweig {new_branch} muss zuerst eine Quelle gewählt werden.", + "project.branch_create_from_source": "Um die neue Zweig {new_branch} zu erstellen, wähle zuerst eine Quell-Zweig.", + "project.branch_source_missing": "Für das Projekt '{project_folder}' wurde keine Zweig-Quelle gefunden.\nEs fehlen local/{source_branch} und origin/{source_branch}.", "project.project_label": "Projekt: {project_folder}", - "project.branch_target_label": "Ziel-branch: {new_branch}", - "project.choose_branch_source": "Wähle die branch-Quelle:", - "project.current_branch_in_repo_label": "Aktuelle branch im Repository: {branch_name}", - "project.default_branch_label": "Standard-branch: {branch_name}", + "project.branch_target_label": "Ziel-Zweig: {new_branch}", + "project.choose_branch_source": "Wähle die Zweig-Quelle:", + "project.current_branch_in_repo_label": "Aktuelle Zweig im Repository: {branch_name}", + "project.default_branch_label": "Standard-Zweig: {branch_name}", "project.refresh_warnings": "Warnungen beim Aktualisieren:", - "project.local_branches": "Lokale branches:", + "project.local_branches": "Lokale Zweiges:", "project.annotation_default": "Standard", - "project.annotation_current_branch_in_repo": "aktuelle branch im Repository", + "project.annotation_current_branch_in_repo": "aktuelle Zweig im Repository", "project.none": "(keine)", - "project.select_branch_with": "Wähle eine branch mit:", + "project.select_branch_with": "Wähle eine Zweig mit:", "project.usage_project": "Verwendung: /project \nBeispiel: /project backend", "project.invalid_project_folder": "Ungültiger Projektordner. Es ist nur ein Ordnername erlaubt.", "project.path_not_directory": "Der Projektpfad existiert, ist aber kein Verzeichnis: {folder}", - "project.active_session_mismatch_title": "Aktive Session passt nicht zum Projekt", - "project.current_session_html": "Aktuelle Session: {session_name}", - "project.session_project_html": "Session-Projekt: {project_folder}", - "project.start_new_session_for_project_html": "Starte mit /new eine neue Session, wenn du in diesem neu gewählten Projekt arbeiten möchtest.", + "project.active_session_mismatch_title": "Aktive Sitzung passt nicht zum Projekt", + "project.current_session_html": "Aktuelle Sitzung: {session_name}", + "project.session_project_html": "Sitzung-Projekt: {project_folder}", + "project.start_new_session_for_project_html": "Starte mit /new eine neue Sitzung, wenn du in diesem neu gewählten Projekt arbeiten möchtest.", "project.project_changed_to": "Projekt gewechselt zu: {folder}", - "project.branch_selection_required": "Bevor in diesem Projekt eine Session erstellt oder fortgesetzt werden kann, muss eine branch gewählt werden.", - "project.active_session_label": "Aktive Session: {session_name}", - "project.session_project_label": "Session-Projekt: {project_folder}", + "project.branch_selection_required": "Bevor in diesem Projekt eine Sitzung erstellt oder fortgesetzt werden kann, muss eine Zweig gewählt werden.", + "project.active_session_label": "Aktive Sitzung: {session_name}", + "project.session_project_label": "Sitzung-Projekt: {project_folder}", "project.project_set_title": "Projekt gesetzt", "project.project_html": "Projekt: {project_folder}", - "project.current_branch_html": "Aktuelle branch: {branch_name}", - "project.branch_usage_html": "Verwende /branch <new_branch> oder /branch <origin_branch> <new_branch>, wenn du eine eigene Arbeits-branch möchtest.", - "project.default_branch_behavior_html": "Wenn <origin_branch> nicht angegeben ist, verwendet der Bot die Standard-branch: {branch_name}.", - "project.current_branch_behavior_html": "Wenn du keine setzt, arbeitet der Bot auf der aktuellen branch: {branch_name}", + "project.current_branch_html": "Aktuelle Zweig: {branch_name}", + "project.branch_usage_html": "Verwende /branch <new_branch> oder /branch <origin_branch> <new_branch>, wenn du eine eigene Arbeits-Zweig möchtest.", + "project.default_branch_behavior_html": "Wenn <origin_branch> nicht angegeben ist, verwendet der Bot die Standard-Zweig: {branch_name}.", + "project.current_branch_behavior_html": "Wenn du keine setzt, arbeitet der Bot auf der aktuellen Zweig: {branch_name}", "project.trust_prompt_html": "Vertraust du diesem Projekt?\nProjekt: {project_folder}", "project.invalid_trust_decision": "Ungültige Vertrauensentscheidung.", "project.project_folder_missing_only": "Projektordner existiert nicht: {project_folder}", @@ -137,40 +139,40 @@ "project.already_trusted": "Projekt ist bereits vertrauenswürdig: {folder}", "project.trusted": "Projekt als vertrauenswürdig markiert: {folder}", "project.usage_branch": "Verwendung: /branch \nOder: /branch ", - "project.default_branch_unknown": "Die Standard-branch für dieses Repository konnte nicht bestimmt werden.", + "project.default_branch_unknown": "Die Standard-Zweig für dieses Repository konnte nicht bestimmt werden.", "project.project_folder_missing_retry": "Projektordner existiert nicht: {project_folder}\nFühre /project {project_folder} erneut aus.", "branch_resolution.use_branch": "{branch_name} verwenden", - "branch_resolution.create_from_instead_of_origin": "Möchtest du branch {new_branch} von einer dieser branches statt von origin/{source_branch} erstellen?", - "branch_resolution.discrepancy_detected": "Vor dem Start der aktiven Session wurde ein branch-Konflikt erkannt.", - "branch_resolution.session_label": "Session: {session_name}", - "branch_resolution.stored_branch_label": "Gespeicherte branch: {branch_name}", - "branch_resolution.choose_branch_to_use": "Wähle, welche branch verwendet werden soll.", - "branch_resolution.no_pending_decision": "Keine ausstehende branch-Entscheidung gefunden.", - "branch_resolution.no_pending_discrepancy": "Kein ausstehender branch-Konflikt gefunden.", - "branch_resolution.no_active_session": "Keine aktive Session verfügbar.", - "branch_resolution.using_current_branch": "Aktuelle branch wird verwendet: {branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "Die gespeicherte branch ist nicht mehr verfügbar: {branch_name}\nEs ist keine Ersatz-Quell-branch verfügbar.", - "branch_resolution.stored_branch_unavailable": "Die gespeicherte branch ist nicht mehr verfügbar.", + "branch_resolution.create_from_instead_of_origin": "Möchtest du Zweig {new_branch} von einer dieser Zweiges statt von origin/{source_branch} erstellen?", + "branch_resolution.discrepancy_detected": "Vor dem Start der aktiven Sitzung wurde ein Zweig-Konflikt erkannt.", + "branch_resolution.session_label": "Sitzung: {session_name}", + "branch_resolution.stored_branch_label": "Gespeicherte Zweig: {branch_name}", + "branch_resolution.choose_branch_to_use": "Wähle, welche Zweig verwendet werden soll.", + "branch_resolution.no_pending_decision": "Keine ausstehende Zweig-Entscheidung gefunden.", + "branch_resolution.no_pending_discrepancy": "Kein ausstehender Zweig-Konflikt gefunden.", + "branch_resolution.no_active_session": "Keine aktive Sitzung verfügbar.", + "branch_resolution.using_current_branch": "Aktuelle Zweig wird verwendet: {branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "Die gespeicherte Zweig ist nicht mehr verfügbar: {branch_name}\nEs ist keine Ersatz-Quell-Zweig verfügbar.", + "branch_resolution.stored_branch_unavailable": "Die gespeicherte Zweig ist nicht mehr verfügbar.", "branch_resolution.missing_local_and_origin": "Es fehlen local/{branch_name} und origin/{branch_name}.", - "branch_resolution.create_branch_from_choices": "Möchtest du branch {branch_name} von einer dieser branches erstellen?", - "branch_resolution.choose_restore_method": "Wähle, wie die gespeicherte branch wiederhergestellt werden soll.", - "lifecycle.provider_selection_required": "Vor dem Erstellen oder Fortsetzen einer Session muss ein Provider gewählt werden.", + "branch_resolution.create_branch_from_choices": "Möchtest du Zweig {branch_name} von einer dieser Zweiges erstellen?", + "branch_resolution.choose_restore_method": "Wähle, wie die gespeicherte Zweig wiederhergestellt werden soll.", + "lifecycle.provider_selection_required": "Vor dem Erstellen oder Fortsetzen einer Sitzung muss ein Anbieter gewählt werden.", "lifecycle.no_project_selected_example": "Kein Projekt ausgewählt.\nBitte zuerst /project ausführen.\nBeispiel: /project backend", - "lifecycle.session_name_exists": "Der Session-Name existiert bereits: {session_name}\nBitte verwende einen anderen Session-Namen.", - "lifecycle.creating_session": "Session wird erstellt...", - "lifecycle.failed_create_session": "Session konnte nicht erstellt werden.", - "lifecycle.session_created_successfully": "Session erfolgreich erstellt: {session_name}\nSession-ID: {session_id}\nProjekt: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "provider.cli_not_found": "{provider_label} CLI nicht gefunden: {bin_name}\nFühre /provider aus, um einen verfügbaren Provider zu wählen oder die Bot-Konfiguration zu aktualisieren.", + "lifecycle.session_name_exists": "Der Sitzung-Name existiert bereits: {session_name}\nBitte verwende einen anderen Sitzung-Namen.", + "lifecycle.creating_session": "Sitzung wird erstellt...", + "lifecycle.failed_create_session": "Sitzung konnte nicht erstellt werden.", + "lifecycle.session_created_successfully": "Sitzung erfolgreich erstellt: {session_name}\nSitzung-ID: {session_id}\nProjekt: {project_folder}\nAnbieter: {provider}\nZweig: {branch_name}", + "provider.cli_not_found": "{provider_label} CLI nicht gefunden: {bin_name}\nFühre /provider aus, um einen verfügbaren Anbieter zu wählen oder die Bot-Konfiguration zu aktualisieren.", "provider.status_available": "verfügbar", "provider.status_missing": "fehlt", "provider.status_current": "aktuell", "provider.usage_provider": "Verwendung: /provider", - "provider.current_provider_prompt": "Aktueller Provider: {provider}\nWähle den Provider für neue Sessions.", + "provider.current_provider_prompt": "Aktueller Anbieter: {provider}\nWähle den Anbieter für neue Sitzungs.", "provider.not_selected": "(nicht ausgewählt)", "provider.cli_not_found_install_first": "{provider_label} CLI nicht gefunden: {bin_name}\nAktualisiere die Bot-Konfiguration oder installiere zuerst diese CLI.", - "provider.current_provider_set": "Aktueller Provider gesetzt auf: {provider}", - "status.current_session_details": "Aktuelle Session: {session_name}\nSession-ID: {session_id}\nProjekt: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "status.current_branch_placeholder": "(aktuelle branch)", + "provider.current_provider_set": "Aktueller Anbieter gesetzt auf: {provider}", + "status.current_session_details": "Aktuelle Sitzung: {session_name}\nSitzung-ID: {session_id}\nProjekt: {project_folder}\nAnbieter: {provider}\nZweig: {branch_name}", + "status.current_branch_placeholder": "(aktuelle Zweig)", "git.commit_disabled": "/commit ist deaktiviert.\nSetze ENABLE_COMMIT_COMMAND=true in der Bot-Umgebung, um ihn zu aktivieren.", "git.usage_commit": "Verwendung: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Es wurden keine gültigen git commit-Befehle gefunden.", @@ -187,7 +189,7 @@ "git.project_not_trusted_for_mutation": "Dieses Projekt ist nicht für verändernde Git-Operationen freigegeben. Verwende ein mit /project angelegtes Projekt oder markiere es zuerst als vertrauenswürdig.", "git.unsafe_path_arguments": "Unsichere Pfadargumente sind nicht erlaubt. Es dürfen nur Dateien innerhalb des aktuellen Projekts verwendet werden.", "diff.task_completed": "Aufgabe abgeschlossen.", - "diff.session_label": "Session: {session_name}", + "diff.session_label": "Sitzung: {session_name}", "diff.project_label": "Projekt: {project_folder}", "diff.changed_files": "Geänderte Dateien:", "diff.none": "(keine)", @@ -204,7 +206,7 @@ "cli.then_run": "Dann ausführen: coding-agent-telegram", "bot.command.compact": "Aktive Sitzung komprimieren", "status.usage_compact": "Verwendung: /compact", - "runtime.compacting_session": "Aktive Session wird komprimiert...", - "runtime.compact_summary_missing": "Der Provider hat keine verwendbare kompakte Übergabezusammenfassung zurückgegeben.", - "runtime.session_compacted": "Session erfolgreich komprimiert. Zu {session_name} ({session_id}) gewechselt." + "runtime.compacting_session": "Aktive Sitzung wird komprimiert...", + "runtime.compact_summary_missing": "Der Anbieter hat keine verwendbare kompakte Übergabezusammenfassung zurückgegeben.", + "runtime.session_compacted": "Sitzung erfolgreich komprimiert. Zu {session_name} ({session_id}) gewechselt." } diff --git a/src/coding_agent_telegram/resources/locales/fr.json b/src/coding_agent_telegram/resources/locales/fr.json index 83870dd..03b3ea6 100644 --- a/src/coding_agent_telegram/resources/locales/fr.json +++ b/src/coding_agent_telegram/resources/locales/fr.json @@ -1,14 +1,14 @@ { "bot.command.abort": "Interrompre l’exécution actuelle de l’agent", - "bot.command.branch": "Créer et changer de Git branch", + "bot.command.branch": "Créer et changer de Git branche", "bot.command.commit": "Exécuter des commandes Git commit validées", "bot.command.current": "Afficher la session active", "bot.command.diff": "Afficher les noms de fichiers modifiés par rapport à HEAD", "bot.command.new": "Créer une nouvelle session", - "bot.command.pull": "Pull la branch de la session actuelle", + "bot.command.pull": "Pull la branche de la session actuelle", "bot.command.project": "Définir le dossier de projet actuel", "bot.command.provider": "Choisir le fournisseur pour les nouvelles sessions", - "bot.command.push": "Push la branch de la session actuelle", + "bot.command.push": "Push la branche de la session actuelle", "bot.command.switch": "Lister les sessions ou basculer", "bot.error.command_failed": "⚠️ La commande a échoué. Vérifiez le journal du serveur.", "bot.error.session_store": "⚠️ {error}", @@ -18,22 +18,23 @@ "common.no_project_selected": "Aucun projet sélectionné.\nVeuillez d’abord exécuter /project .", "common.project_busy": "Un agent est actuellement en cours sur le projet '{project_folder}'.\nSeuls /current et /abort sont disponibles jusqu’à la fin.", "common.project_folder_missing": "⚠️ Le dossier du projet n’existe plus pour cette session : {project_folder}", - "git.branch_unknown": "⚠️ Impossible de déterminer la branch de la session actuelle.", + "common.button_expired": "⚠️ Ce bouton a expiré. Veuillez relancer la commande.", + "git.branch_unknown": "⚠️ Impossible de déterminer la branche de la session actuelle.", "git.cancel_button": "Annuler", "git.usage_diff": "Utilisation : /diff", "git.usage_pull": "Utilisation : /pull", "git.pull_cancelled": "Pull annulé.", "git.pull_completed": "Pull terminé.", "git.pull_confirm_button": "Confirmer pull", - "git.pull_confirm_prompt": "Pull la branch `{branch_name}` depuis `origin` ?", - "git.pull_confirm_prompt_with_default": "Pull la branch `{branch_name}` depuis `origin` et rafraîchir aussi la branch par défaut `{default_branch}` ?", - "git.pull_in_progress": "Pull de la branch `{branch_name}` depuis `origin`...", - "git.pull_in_progress_with_default": "Pull de la branch `{branch_name}` depuis `origin` et rafraîchissement de la branch par défaut `{default_branch}`...", + "git.pull_confirm_prompt": "Pull la branche `{branch_name}` depuis `origin` ?", + "git.pull_confirm_prompt_with_default": "Pull la branche `{branch_name}` depuis `origin` et rafraîchir aussi la branche par défaut `{default_branch}` ?", + "git.pull_in_progress": "Pull de la branche `{branch_name}` depuis `origin`...", + "git.pull_in_progress_with_default": "Pull de la branche `{branch_name}` depuis `origin` et rafraîchissement de la branche par défaut `{default_branch}`...", "git.push_cancelled": "Push annulé.", "git.push_cancelled_checkout_failed": "Push annulé. Échec du basculement vers `{branch_name}`.", "git.push_confirm_button": "Confirmer push", - "git.push_confirm_prompt": "Push la branch `{branch_name}` vers `origin` ?", - "git.push_in_progress": "Push de la branch `{branch_name}` vers `origin`...", + "git.push_confirm_prompt": "Push la branche `{branch_name}` vers `origin` ?", + "git.push_in_progress": "Push de la branche `{branch_name}` vers `origin`...", "git.usage_push": "Utilisation : /push", "message.photo_only_codex": "Les pièces jointes photo sont actuellement prises en charge uniquement pour les sessions Codex.", "message.question_queued": "Question mise en file d’attente sous Q{question_number}. Elle sera traitée une fois la tâche actuelle terminée.", @@ -60,6 +61,7 @@ "runtime.agent_run_failed": "L’exécution de l’agent a échoué.", "runtime.live_agent_output": "Sortie en direct de l’agent", "runtime.photo_too_large": "La photo est trop volumineuse. La taille maximale prise en charge est de 5 Mo.", + "runtime.voice_audio_too_large": "L’audio est trop volumineux pour la transcription vocale locale. La taille maximale prise en charge est de {max_size_mb} Mo.", "runtime.provider_output_index": "Sortie {provider} {index}/{total}", "runtime.provider_output_single": "Sortie {provider}", "runtime.replacement_session_stall": "La création de la session de remplacement semble bloquée.\nLe processus local de l’agent est toujours en cours, mais n’a produit aucune sortie.\nSous macOS, cela peut venir d’une boîte de dialogue d’autorisation cachée qui attend une confirmation sur la machine qui exécute le bot.", @@ -75,9 +77,9 @@ "status.abort_signal_sent": "Signal d’arrêt envoyé pour l’exécution actuelle du projet.", "status.no_running_agent": "Aucun processus d’agent en cours n’a été trouvé pour le projet actuel.", "status.usage_abort": "Utilisation : /abort", - "switch.available_sessions": "Sessions disponibles (page {page}/{total_pages}) :", - "switch.source_bot_managed": "Session gérée par le bot", - "switch.source_native_cli": "Session CLI native", + "switch.available_sessions": "sessions disponibles (page {page}/{total_pages}) :", + "switch.source_bot_managed": "session gérée par le bot", + "switch.source_native_cli": "session CLI native", "switch.current_project_filter": "Filtre de projet actuel pour les sessions CLI natives : {project_folder}", "switch.current_project_filter_none": "Filtre de projet actuel pour les sessions CLI natives : (aucun)", "switch.initialized_label": "initialisé", @@ -86,12 +88,12 @@ "switch.pages_label": "Pages : /switch page 1 ... /switch page {total_pages}", "switch.no_sessions_found": "Aucune session trouvée.", "switch.invalid_page_number": "Numéro de page invalide.\nUtilisation : /switch page ", - "switch.session_not_found": "⚠️ Session introuvable.\nExécutez /switch pour lister les sessions disponibles.", - "switch.switched_to_session": "Session activée", + "switch.session_not_found": "⚠️ session introuvable.\nExécutez /switch pour lister les sessions disponibles.", + "switch.switched_to_session": "session activée", "switch.project_label": "Projet", "switch.provider_label": "Fournisseur", - "switch.branch_label": "Branch", - "switch.source_label": "Source", + "switch.branch_label": "Branchee", + "switch.source_label": "Origine", "switch.imported_into_state_json": "Importé dans state.json.", "switch.status_active": "actif", "switch.status_idle": "inactive", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "Pour utiliser une autre langue plus tard, modifiez APP_LOCALE dans {env_path}.", "queue.button_cancel": "Annuler", "queue.cancelled": "Les questions en file d'attente ont été annulées.", - "project.branch_switch_requires_source": "Pour passer à la branch {new_branch}, choisissez d'abord une source.", - "project.branch_create_from_source": "Pour créer la nouvelle branch {new_branch}, choisissez d'abord une branch source.", - "project.branch_source_missing": "Aucune source de branch trouvée pour le projet '{project_folder}'.\nlocal/{source_branch} et origin/{source_branch} sont introuvables.", + "project.branch_switch_requires_source": "Pour passer à la branche {new_branch}, choisissez d'abord une origine.", + "project.branch_create_from_source": "Pour créer la nouvelle branche {new_branch}, choisissez d'abord une branche origine.", + "project.branch_source_missing": "Aucune origine de branche trouvée pour le projet '{project_folder}'.\nlocal/{source_branch} et origin/{source_branch} sont introuvables.", "project.project_label": "Projet : {project_folder}", - "project.branch_target_label": "Branch cible : {new_branch}", - "project.choose_branch_source": "Choisissez la source de la branch :", - "project.current_branch_in_repo_label": "Branch actuelle dans le dépôt : {branch_name}", - "project.default_branch_label": "Branch par défaut : {branch_name}", + "project.branch_target_label": "Branche cible : {new_branch}", + "project.choose_branch_source": "Choisissez la origine de la branche :", + "project.current_branch_in_repo_label": "Branche actuelle dans le dépôt : {branch_name}", + "project.default_branch_label": "Branche par défaut : {branch_name}", "project.refresh_warnings": "Avertissements d'actualisation :", - "project.local_branches": "Branches locales :", + "project.local_branches": "Branchees locales :", "project.annotation_default": "par défaut", - "project.annotation_current_branch_in_repo": "branch actuelle dans le dépôt", + "project.annotation_current_branch_in_repo": "branche actuelle dans le dépôt", "project.none": "(aucun)", - "project.select_branch_with": "Choisissez une branch avec :", + "project.select_branch_with": "Choisissez une branche avec :", "project.usage_project": "Utilisation : /project \nExemple : /project backend", "project.invalid_project_folder": "Dossier de projet invalide. Seul un nom de dossier est autorisé.", "project.path_not_directory": "Le chemin du projet existe mais ce n'est pas un dossier : {folder}", "project.active_session_mismatch_title": "La session active ne correspond pas au projet", - "project.current_session_html": "Session actuelle : {session_name}", + "project.current_session_html": "session actuelle : {session_name}", "project.session_project_html": "Projet de la session : {project_folder}", "project.start_new_session_for_project_html": "Démarrez une nouvelle session avec /new si vous voulez travailler dans ce projet nouvellement sélectionné.", "project.project_changed_to": "Projet changé vers : {folder}", - "project.branch_selection_required": "Vous devez choisir une branch avant de créer ou de reprendre une session dans ce projet.", - "project.active_session_label": "Session active : {session_name}", + "project.branch_selection_required": "Vous devez choisir une branche avant de créer ou de reprendre une session dans ce projet.", + "project.active_session_label": "session active : {session_name}", "project.session_project_label": "Projet de la session : {project_folder}", "project.project_set_title": "Projet défini", "project.project_html": "Projet : {project_folder}", - "project.current_branch_html": "Branch actuelle : {branch_name}", - "project.branch_usage_html": "Utilisez /branch <new_branch> ou /branch <origin_branch> <new_branch> si vous voulez une branch de travail dédiée.", - "project.default_branch_behavior_html": "Si <origin_branch> n'est pas précisée, le bot utilise la branch par défaut : {branch_name}.", - "project.current_branch_behavior_html": "Si vous n'en définissez pas, le bot travaillera sur la branch actuelle : {branch_name}", + "project.current_branch_html": "Branche actuelle : {branch_name}", + "project.branch_usage_html": "Utilisez /branch <new_branch> ou /branch <origin_branch> <new_branch> si vous voulez une branche de travail dédiée.", + "project.default_branch_behavior_html": "Si <origin_branch> n'est pas précisée, le bot utilise la branche par défaut : {branch_name}.", + "project.current_branch_behavior_html": "Si vous n'en définissez pas, le bot travaillera sur la branche actuelle : {branch_name}", "project.trust_prompt_html": "Faites-vous confiance à ce projet ?\nProjet : {project_folder}", "project.invalid_trust_decision": "Décision de confiance invalide.", "project.project_folder_missing_only": "Le dossier du projet n'existe pas : {project_folder}", @@ -137,40 +139,40 @@ "project.already_trusted": "Le projet est déjà approuvé : {folder}", "project.trusted": "Projet approuvé : {folder}", "project.usage_branch": "Utilisation : /branch \nOu : /branch ", - "project.default_branch_unknown": "Impossible de déterminer la branch par défaut pour ce dépôt.", + "project.default_branch_unknown": "Impossible de déterminer la branche par défaut pour ce dépôt.", "project.project_folder_missing_retry": "Le dossier du projet n'existe pas : {project_folder}\nExécutez de nouveau /project {project_folder}.", "branch_resolution.use_branch": "utiliser {branch_name}", - "branch_resolution.create_from_instead_of_origin": "Voulez-vous créer la branch {new_branch} à partir de l'une de ces branches au lieu de origin/{source_branch} ?", - "branch_resolution.discrepancy_detected": "Un écart de branch a été détecté avant l'exécution de la session active.", - "branch_resolution.session_label": "Session : {session_name}", - "branch_resolution.stored_branch_label": "Branch enregistrée : {branch_name}", - "branch_resolution.choose_branch_to_use": "Choisissez la branch à utiliser.", - "branch_resolution.no_pending_decision": "Aucune décision de branch en attente n'a été trouvée.", - "branch_resolution.no_pending_discrepancy": "Aucun écart de branch en attente n'a été trouvé.", + "branch_resolution.create_from_instead_of_origin": "Voulez-vous créer la branche {new_branch} à partir de l'une de ces branches au lieu de origin/{source_branch} ?", + "branch_resolution.discrepancy_detected": "Un écart de branche a été détecté avant l'exécution de la session active.", + "branch_resolution.session_label": "session : {session_name}", + "branch_resolution.stored_branch_label": "Branche enregistrée : {branch_name}", + "branch_resolution.choose_branch_to_use": "Choisissez la branche à utiliser.", + "branch_resolution.no_pending_decision": "Aucune décision de branche en attente n'a été trouvée.", + "branch_resolution.no_pending_discrepancy": "Aucun écart de branche en attente n'a été trouvé.", "branch_resolution.no_active_session": "Aucune session active n'est disponible.", - "branch_resolution.using_current_branch": "Utilisation de la branch actuelle : {branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "La branch enregistrée n'est plus disponible : {branch_name}\nAucune branch source de secours n'est disponible.", - "branch_resolution.stored_branch_unavailable": "La branch enregistrée n'est plus disponible.", + "branch_resolution.using_current_branch": "Utilisation de la branche actuelle : {branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "La branche enregistrée n'est plus disponible : {branch_name}\nAucune branche origine de secours n'est disponible.", + "branch_resolution.stored_branch_unavailable": "La branche enregistrée n'est plus disponible.", "branch_resolution.missing_local_and_origin": "local/{branch_name} et origin/{branch_name} sont introuvables.", - "branch_resolution.create_branch_from_choices": "Voulez-vous créer la branch {branch_name} à partir de l'une de ces branches ?", - "branch_resolution.choose_restore_method": "Choisissez comment restaurer la branch enregistrée.", - "lifecycle.provider_selection_required": "Vous devez choisir un provider avant de créer ou de reprendre une session.", + "branch_resolution.create_branch_from_choices": "Voulez-vous créer la branche {branch_name} à partir de l'une de ces branches ?", + "branch_resolution.choose_restore_method": "Choisissez comment restaurer la branche enregistrée.", + "lifecycle.provider_selection_required": "Vous devez choisir un fournisseur avant de créer ou de reprendre une session.", "lifecycle.no_project_selected_example": "Aucun projet sélectionné.\nVeuillez d'abord exécuter /project .\nExemple : /project backend", "lifecycle.session_name_exists": "Le nom de session existe déjà : {session_name}\nVeuillez utiliser un autre nom de session.", "lifecycle.creating_session": "Création de la session...", "lifecycle.failed_create_session": "Échec de la création de la session.", - "lifecycle.session_created_successfully": "Session créée avec succès : {session_name}\nID de session : {session_id}\nProjet : {project_folder}\nProvider : {provider}\nBranch : {branch_name}", - "provider.cli_not_found": "CLI {provider_label} introuvable : {bin_name}\nExécutez /provider pour choisir un provider disponible ou mettre à jour la configuration du bot.", + "lifecycle.session_created_successfully": "session créée avec succès : {session_name}\nID de session : {session_id}\nProjet : {project_folder}\nFournisseur : {provider}\nBranche : {branch_name}", + "provider.cli_not_found": "CLI {provider_label} introuvable : {bin_name}\nExécutez /provider pour choisir un fournisseur disponible ou mettre à jour la configuration du bot.", "provider.status_available": "disponible", "provider.status_missing": "manquant", "provider.status_current": "actuel", "provider.usage_provider": "Utilisation : /provider", - "provider.current_provider_prompt": "Provider actuel : {provider}\nChoisissez le provider pour les nouvelles sessions.", + "provider.current_provider_prompt": "Fournisseur actuel : {provider}\nChoisissez le fournisseur pour les nouvelles sessions.", "provider.not_selected": "(non sélectionné)", "provider.cli_not_found_install_first": "CLI {provider_label} introuvable : {bin_name}\nMettez à jour la configuration du bot ou installez d'abord cette CLI.", - "provider.current_provider_set": "Provider actuel défini sur : {provider}", - "status.current_session_details": "Session actuelle : {session_name}\nID de session : {session_id}\nProjet : {project_folder}\nProvider : {provider}\nBranch : {branch_name}", - "status.current_branch_placeholder": "(branch actuelle)", + "provider.current_provider_set": "Fournisseur actuel défini sur : {provider}", + "status.current_session_details": "session actuelle : {session_name}\nID de session : {session_id}\nProjet : {project_folder}\nFournisseur : {provider}\nBranche : {branch_name}", + "status.current_branch_placeholder": "(branche actuelle)", "git.commit_disabled": "/commit est désactivé.\nDéfinissez ENABLE_COMMIT_COMMAND=true dans l'environnement du bot pour l'activer.", "git.usage_commit": "Utilisation : /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Aucune commande git commit valide n'a été trouvée.", @@ -187,7 +189,7 @@ "git.project_not_trusted_for_mutation": "Ce projet n'est pas approuvé pour les opérations Git qui modifient l'état. Utilisez un projet créé via /project ou marquez-le d'abord comme approuvé.", "git.unsafe_path_arguments": "Les arguments de chemin non sûrs ne sont pas autorisés. Seuls les fichiers à l'intérieur du projet actuel peuvent être utilisés.", "diff.task_completed": "Tâche terminée.", - "diff.session_label": "Session : {session_name}", + "diff.session_label": "session : {session_name}", "diff.project_label": "Projet : {project_folder}", "diff.changed_files": "Fichiers modifiés :", "diff.none": "(aucun)", @@ -205,6 +207,6 @@ "bot.command.compact": "Compacter la session active", "status.usage_compact": "Utilisation : /compact", "runtime.compacting_session": "Compression de la session active en cours...", - "runtime.compact_summary_missing": "Le provider n'a pas renvoyé de résumé de relais compact exploitable.", - "runtime.session_compacted": "Session compactée avec succès. Bascule vers {session_name} ({session_id})." + "runtime.compact_summary_missing": "Le fournisseur n'a pas renvoyé de résumé de relais compact exploitable.", + "runtime.session_compacted": "session compactée avec succès. Bascule vers {session_name} ({session_id})." } diff --git a/src/coding_agent_telegram/resources/locales/ja.json b/src/coding_agent_telegram/resources/locales/ja.json index 7bd9977..bb074ec 100644 --- a/src/coding_agent_telegram/resources/locales/ja.json +++ b/src/coding_agent_telegram/resources/locales/ja.json @@ -1,14 +1,14 @@ { "bot.command.abort": "現在のエージェント実行を中止", - "bot.command.branch": "Git branch を作成して切り替え", + "bot.command.branch": "Git ブランチ を作成して切り替え", "bot.command.commit": "検証済みの Git commit コマンドを実行", "bot.command.current": "現在のアクティブセッションを表示", "bot.command.diff": "HEAD との差分があるファイル名を表示", "bot.command.new": "新しいセッションを作成", - "bot.command.pull": "現在のセッション branch を pull", + "bot.command.pull": "現在のセッション ブランチ を pull", "bot.command.project": "現在のプロジェクトフォルダーを設定", "bot.command.provider": "新しいセッションのプロバイダーを選択", - "bot.command.push": "現在のセッション branch を push", + "bot.command.push": "現在のセッション ブランチ を push", "bot.command.switch": "セッション一覧または切り替え", "bot.error.command_failed": "⚠️ コマンドが失敗しました。サーバーログを確認してください。", "bot.error.session_store": "⚠️ {error}", @@ -18,22 +18,23 @@ "common.no_project_selected": "プロジェクトが選択されていません。\nまず /project を実行してください。", "common.project_busy": "プロジェクト '{project_folder}' では現在エージェントが実行中です。\n完了するまでは /current と /abort のみ利用できます。", "common.project_folder_missing": "⚠️ このセッションのプロジェクトフォルダーは存在しなくなりました: {project_folder}", - "git.branch_unknown": "⚠️ 現在のセッションの branch を特定できませんでした。", + "common.button_expired": "⚠️ このボタンの有効期限が切れました。コマンドを再実行してください。", + "git.branch_unknown": "⚠️ 現在のセッションの ブランチ を特定できませんでした。", "git.cancel_button": "キャンセル", "git.usage_diff": "使い方: /diff", "git.usage_pull": "使い方: /pull", "git.pull_cancelled": "pull をキャンセルしました。", "git.pull_completed": "pull が完了しました。", "git.pull_confirm_button": "pull を確認", - "git.pull_confirm_prompt": "branch `{branch_name}` を `origin` から pull しますか?", - "git.pull_confirm_prompt_with_default": "branch `{branch_name}` を `origin` から pull し、あわせてデフォルト branch `{default_branch}` も更新しますか?", - "git.pull_in_progress": "branch `{branch_name}` を `origin` から pull 中...", - "git.pull_in_progress_with_default": "branch `{branch_name}` を `origin` から pull し、デフォルト branch `{default_branch}` を更新中...", + "git.pull_confirm_prompt": "ブランチ `{branch_name}` を `origin` から pull しますか?", + "git.pull_confirm_prompt_with_default": "ブランチ `{branch_name}` を `origin` から pull し、あわせてデフォルト ブランチ `{default_branch}` も更新しますか?", + "git.pull_in_progress": "ブランチ `{branch_name}` を `origin` から pull 中...", + "git.pull_in_progress_with_default": "ブランチ `{branch_name}` を `origin` から pull し、デフォルト ブランチ `{default_branch}` を更新中...", "git.push_cancelled": "push をキャンセルしました。", "git.push_cancelled_checkout_failed": "push をキャンセルしました。まず `{branch_name}` への切り替えに失敗しました。", "git.push_confirm_button": "push を確認", - "git.push_confirm_prompt": "branch `{branch_name}` を `origin` に push しますか?", - "git.push_in_progress": "branch `{branch_name}` を `origin` に push 中...", + "git.push_confirm_prompt": "ブランチ `{branch_name}` を `origin` に push しますか?", + "git.push_in_progress": "ブランチ `{branch_name}` を `origin` に push 中...", "git.usage_push": "使い方: /push", "message.photo_only_codex": "写真添付は現在 Codex セッションでのみサポートされています。", "message.question_queued": "質問は Q{question_number} としてキューに追加されました。現在のエージェント処理が終わった後に実行されます。", @@ -60,6 +61,7 @@ "runtime.agent_run_failed": "エージェント実行に失敗しました。", "runtime.live_agent_output": "エージェントのライブ出力", "runtime.photo_too_large": "写真が大きすぎます。対応する最大サイズは 5 MB です。", + "runtime.voice_audio_too_large": "音声がローカル音声文字起こしの上限を超えています。対応する最大サイズは {max_size_mb} MB です。", "runtime.provider_output_index": "{provider} 出力 {index}/{total}", "runtime.provider_output_single": "{provider} 出力", "runtime.replacement_session_stall": "代替セッションの作成が停止しているようです。\nローカルのエージェントプロセスはまだ動作していますが、出力がありません。\nmacOS では、ボットを実行しているマシンで非表示の権限ダイアログが確認待ちになっている可能性があります。", @@ -76,13 +78,13 @@ "status.no_running_agent": "現在のプロジェクトで実行中のエージェントプロセスは見つかりませんでした。", "status.usage_abort": "使い方: /abort", "switch.available_sessions": "利用可能なセッション (ページ {page}/{total_pages}):", - "switch.source_bot_managed": "Bot 管理セッション", + "switch.source_bot_managed": "ボット 管理セッション", "switch.source_native_cli": "ネイティブ CLI セッション", "switch.current_project_filter": "ネイティブセッション用の現在のプロジェクトフィルター: {project_folder}", "switch.current_project_filter_none": "ネイティブセッション用の現在のプロジェクトフィルター: (なし)", "switch.initialized_label": "初期化元", "switch.use_label": "使用方法:", - "switch.native_import_note": "ネイティブ CLI セッションを選択すると、state.json に取り込み、Bot がそのセッションに切り替わります。", + "switch.native_import_note": "ネイティブ CLI セッションを選択すると、state.json に取り込み、ボット がそのセッションに切り替わります。", "switch.pages_label": "ページ: /switch page 1 ... /switch page {total_pages}", "switch.no_sessions_found": "セッションが見つかりません。", "switch.invalid_page_number": "無効なページ番号です。\n使用方法: /switch page ", @@ -90,7 +92,7 @@ "switch.switched_to_session": "セッションを切り替えました", "switch.project_label": "プロジェクト", "switch.provider_label": "プロバイダー", - "switch.branch_label": "Branch", + "switch.branch_label": "ブランチ", "switch.source_label": "ソース", "switch.imported_into_state_json": "state.json に取り込みました。", "switch.status_active": "使用中", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "後で別の言語に変更する場合は、{env_path} の APP_LOCALE を編集してください。", "queue.button_cancel": "キャンセル", "queue.cancelled": "キューに入っていた質問をキャンセルしました。", - "project.branch_switch_requires_source": "branch {new_branch} に切り替える前に、まずソースを選択してください。", - "project.branch_create_from_source": "新しい branch {new_branch} を作成する前に、まずソース branch を選択してください。", - "project.branch_source_missing": "プロジェクト '{project_folder}' の branch ソースが見つかりません。\nlocal/{source_branch} と origin/{source_branch} がありません。", + "project.branch_switch_requires_source": "ブランチ {new_branch} に切り替える前に、まずソースを選択してください。", + "project.branch_create_from_source": "新しい ブランチ {new_branch} を作成する前に、まずソース ブランチ を選択してください。", + "project.branch_source_missing": "プロジェクト '{project_folder}' の ブランチ ソースが見つかりません。\nlocal/{source_branch} と origin/{source_branch} がありません。", "project.project_label": "プロジェクト: {project_folder}", - "project.branch_target_label": "対象 branch: {new_branch}", - "project.choose_branch_source": "branch ソースを選択してください:", - "project.current_branch_in_repo_label": "リポジトリの現在の branch: {branch_name}", - "project.default_branch_label": "デフォルト branch: {branch_name}", + "project.branch_target_label": "対象 ブランチ: {new_branch}", + "project.choose_branch_source": "ブランチ ソースを選択してください:", + "project.current_branch_in_repo_label": "リポジトリの現在の ブランチ: {branch_name}", + "project.default_branch_label": "デフォルト ブランチ: {branch_name}", "project.refresh_warnings": "更新時の警告:", - "project.local_branches": "ローカル branch:", + "project.local_branches": "ローカル ブランチ:", "project.annotation_default": "デフォルト", - "project.annotation_current_branch_in_repo": "現在の branch", + "project.annotation_current_branch_in_repo": "現在の ブランチ", "project.none": "(なし)", - "project.select_branch_with": "次のコマンドで branch を選択してください:", + "project.select_branch_with": "次のコマンドで ブランチ を選択してください:", "project.usage_project": "使い方: /project \n例: /project backend", "project.invalid_project_folder": "無効なプロジェクトフォルダです。フォルダ名のみ指定できます。", "project.path_not_directory": "プロジェクトのパスは存在しますが、ディレクトリではありません: {folder}", - "project.active_session_mismatch_title": "アクティブな session とプロジェクトが一致しません", - "project.current_session_html": "現在の session: {session_name}", - "project.session_project_html": "Session のプロジェクト: {project_folder}", - "project.start_new_session_for_project_html": "この新しく選択したプロジェクトで作業したい場合は、/new で新しい session を開始してください。", + "project.active_session_mismatch_title": "アクティブな セッション とプロジェクトが一致しません", + "project.current_session_html": "現在の セッション: {session_name}", + "project.session_project_html": "セッション のプロジェクト: {project_folder}", + "project.start_new_session_for_project_html": "この新しく選択したプロジェクトで作業したい場合は、/new で新しい セッション を開始してください。", "project.project_changed_to": "プロジェクトを切り替えました: {folder}", - "project.branch_selection_required": "このプロジェクトで session を作成または再開する前に、branch を選択する必要があります。", - "project.active_session_label": "アクティブな session: {session_name}", - "project.session_project_label": "Session のプロジェクト: {project_folder}", + "project.branch_selection_required": "このプロジェクトで セッション を作成または再開する前に、ブランチ を選択する必要があります。", + "project.active_session_label": "アクティブな セッション: {session_name}", + "project.session_project_label": "セッション のプロジェクト: {project_folder}", "project.project_set_title": "プロジェクトを設定しました", "project.project_html": "プロジェクト: {project_folder}", - "project.current_branch_html": "現在の branch: {branch_name}", - "project.branch_usage_html": "専用の作業 branch が必要な場合は、/branch <new_branch> または /branch <origin_branch> <new_branch> を使ってください。", - "project.default_branch_behavior_html": "<origin_branch> を指定しない場合、bot はデフォルト branch {branch_name} を使います。", - "project.current_branch_behavior_html": "設定しない場合、bot は現在の branch {branch_name} で作業します", + "project.current_branch_html": "現在の ブランチ: {branch_name}", + "project.branch_usage_html": "専用の作業 ブランチ が必要な場合は、/branch <new_branch> または /branch <origin_branch> <new_branch> を使ってください。", + "project.default_branch_behavior_html": "<origin_branch> を指定しない場合、ボット はデフォルト ブランチ {branch_name} を使います。", + "project.current_branch_behavior_html": "設定しない場合、ボット は現在の ブランチ {branch_name} で作業します", "project.trust_prompt_html": "このプロジェクトを信頼しますか?\nプロジェクト: {project_folder}", "project.invalid_trust_decision": "無効な信頼設定です。", "project.project_folder_missing_only": "プロジェクトフォルダが存在しません: {project_folder}", @@ -137,41 +139,41 @@ "project.already_trusted": "プロジェクトはすでに信頼されています: {folder}", "project.trusted": "プロジェクトを信頼済みにしました: {folder}", "project.usage_branch": "使い方: /branch \nまたは: /branch ", - "project.default_branch_unknown": "このリポジトリのデフォルト branch を判定できませんでした。", + "project.default_branch_unknown": "このリポジトリのデフォルト ブランチ を判定できませんでした。", "project.project_folder_missing_retry": "プロジェクトフォルダが存在しません: {project_folder}\nもう一度 /project {project_folder} を実行してください。", "branch_resolution.use_branch": "{branch_name} を使う", - "branch_resolution.create_from_instead_of_origin": "origin/{source_branch} の代わりに、これらの branch のいずれかから branch {new_branch} を作成しますか?", - "branch_resolution.discrepancy_detected": "アクティブな session を実行する前に branch の不一致が検出されました。", + "branch_resolution.create_from_instead_of_origin": "origin/{source_branch} の代わりに、これらの ブランチ のいずれかから ブランチ {new_branch} を作成しますか?", + "branch_resolution.discrepancy_detected": "アクティブな セッション を実行する前に ブランチ の不一致が検出されました。", "branch_resolution.session_label": "セッション: {session_name}", - "branch_resolution.stored_branch_label": "保存された branch: {branch_name}", - "branch_resolution.choose_branch_to_use": "使用する branch を選択してください。", - "branch_resolution.no_pending_decision": "保留中の branch 判断は見つかりませんでした。", - "branch_resolution.no_pending_discrepancy": "保留中の branch 不一致は見つかりませんでした。", - "branch_resolution.no_active_session": "アクティブな session がありません。", - "branch_resolution.using_current_branch": "現在の branch を使用します: {branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "保存された branch は利用できません: {branch_name}\n代替のソース branch もありません。", - "branch_resolution.stored_branch_unavailable": "保存された branch は利用できません。", + "branch_resolution.stored_branch_label": "保存された ブランチ: {branch_name}", + "branch_resolution.choose_branch_to_use": "使用する ブランチ を選択してください。", + "branch_resolution.no_pending_decision": "保留中の ブランチ 判断は見つかりませんでした。", + "branch_resolution.no_pending_discrepancy": "保留中の ブランチ 不一致は見つかりませんでした。", + "branch_resolution.no_active_session": "アクティブな セッション がありません。", + "branch_resolution.using_current_branch": "現在の ブランチ を使用します: {branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "保存された ブランチ は利用できません: {branch_name}\n代替のソース ブランチ もありません。", + "branch_resolution.stored_branch_unavailable": "保存された ブランチ は利用できません。", "branch_resolution.missing_local_and_origin": "local/{branch_name} と origin/{branch_name} がありません。", - "branch_resolution.create_branch_from_choices": "これらの branch のいずれかから branch {branch_name} を作成しますか?", - "branch_resolution.choose_restore_method": "保存された branch を復元する方法を選択してください。", - "lifecycle.provider_selection_required": "session を作成または再開する前に、provider を選択する必要があります。", + "branch_resolution.create_branch_from_choices": "これらの ブランチ のいずれかから ブランチ {branch_name} を作成しますか?", + "branch_resolution.choose_restore_method": "保存された ブランチ を復元する方法を選択してください。", + "lifecycle.provider_selection_required": "セッション を作成または再開する前に、プロバイダー を選択する必要があります。", "lifecycle.no_project_selected_example": "プロジェクトが選択されていません。\nまず /project を実行してください。\n例: /project backend", - "lifecycle.session_name_exists": "Session 名はすでに存在します: {session_name}\n別の session 名を使用してください。", - "lifecycle.creating_session": "session を作成しています...", - "lifecycle.failed_create_session": "session の作成に失敗しました。", - "lifecycle.session_created_successfully": "session を作成しました: {session_name}\nSession ID: {session_id}\nプロジェクト: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "provider.cli_not_found": "{provider_label} CLI が見つかりません: {bin_name}\n/provider を実行して利用可能な provider を選択するか、bot 設定を更新してください。", + "lifecycle.session_name_exists": "セッション 名はすでに存在します: {session_name}\n別の セッション 名を使用してください。", + "lifecycle.creating_session": "セッション を作成しています...", + "lifecycle.failed_create_session": "セッション の作成に失敗しました。", + "lifecycle.session_created_successfully": "セッション を作成しました: {session_name}\nセッション ID: {session_id}\nプロジェクト: {project_folder}\nプロバイダー: {provider}\nブランチ: {branch_name}", + "provider.cli_not_found": "{provider_label} CLI が見つかりません: {bin_name}\n/provider を実行して利用可能な プロバイダー を選択するか、ボット 設定を更新してください。", "provider.status_available": "利用可能", "provider.status_missing": "未検出", "provider.status_current": "現在", "provider.usage_provider": "使い方: /provider", - "provider.current_provider_prompt": "現在の provider: {provider}\n新しい session の provider を選択してください。", + "provider.current_provider_prompt": "現在の プロバイダー: {provider}\n新しい セッション の プロバイダー を選択してください。", "provider.not_selected": "(未選択)", - "provider.cli_not_found_install_first": "{provider_label} CLI が見つかりません: {bin_name}\nbot 設定を更新するか、この CLI を先にインストールしてください。", - "provider.current_provider_set": "現在の provider を {provider} に設定しました", - "status.current_session_details": "現在の session: {session_name}\nSession ID: {session_id}\nプロジェクト: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "status.current_branch_placeholder": "(現在の branch)", - "git.commit_disabled": "/commit は無効です。\n有効にするには bot の環境で ENABLE_COMMIT_COMMAND=true を設定してください。", + "provider.cli_not_found_install_first": "{provider_label} CLI が見つかりません: {bin_name}\nボット 設定を更新するか、この CLI を先にインストールしてください。", + "provider.current_provider_set": "現在の プロバイダー を {provider} に設定しました", + "status.current_session_details": "現在の セッション: {session_name}\nセッション ID: {session_id}\nプロジェクト: {project_folder}\nプロバイダー: {provider}\nブランチ: {branch_name}", + "status.current_branch_placeholder": "(現在の ブランチ)", + "git.commit_disabled": "/commit は無効です。\n有効にするには ボット の環境で ENABLE_COMMIT_COMMAND=true を設定してください。", "git.usage_commit": "使い方: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "有効な git commit コマンドが見つかりませんでした。", "git.commit_generate_prompt": "現在変更されているファイル向けの git commit コマンドを生成しますか?", @@ -204,7 +206,7 @@ "cli.then_run": "その後、次を実行してください: coding-agent-telegram", "bot.command.compact": "アクティブなセッションを compact", "status.usage_compact": "使い方: /compact", - "runtime.compacting_session": "アクティブな session を compact 中です...", - "runtime.compact_summary_missing": "provider から利用可能な compact handoff summary が返されませんでした。", - "runtime.session_compacted": "session の compact に成功しました。{session_name} ({session_id}) に切り替えました。" + "runtime.compacting_session": "アクティブな セッション を compact 中です...", + "runtime.compact_summary_missing": "プロバイダー から利用可能な compact handoff summary が返されませんでした。", + "runtime.session_compacted": "セッション の compact に成功しました。{session_name} ({session_id}) に切り替えました。" } diff --git a/src/coding_agent_telegram/resources/locales/ko.json b/src/coding_agent_telegram/resources/locales/ko.json index b018a94..41063ee 100644 --- a/src/coding_agent_telegram/resources/locales/ko.json +++ b/src/coding_agent_telegram/resources/locales/ko.json @@ -1,14 +1,14 @@ { "bot.command.abort": "현재 에이전트 실행 중단", - "bot.command.branch": "Git branch 생성 및 전환", + "bot.command.branch": "Git 브랜치 생성 및 전환", "bot.command.commit": "검증된 Git commit 명령 실행", "bot.command.current": "현재 활성 세션 표시", "bot.command.diff": "HEAD 대비 변경된 파일 이름 표시", "bot.command.new": "새 세션 생성", - "bot.command.pull": "현재 세션 branch pull", + "bot.command.pull": "현재 세션 브랜치 pull", "bot.command.project": "현재 프로젝트 폴더 설정", "bot.command.provider": "새 세션용 제공자 선택", - "bot.command.push": "현재 세션 branch push", + "bot.command.push": "현재 세션 브랜치 push", "bot.command.switch": "세션 목록 보기 또는 전환", "bot.error.command_failed": "⚠️ 명령이 실패했습니다. 서버 로그를 확인하세요.", "bot.error.session_store": "⚠️ {error}", @@ -18,22 +18,23 @@ "common.no_project_selected": "선택된 프로젝트가 없습니다.\n먼저 /project 를 실행하세요.", "common.project_busy": "프로젝트 '{project_folder}' 에서 현재 에이전트가 실행 중입니다.\n완료될 때까지 /current 와 /abort 만 사용할 수 있습니다.", "common.project_folder_missing": "⚠️ 이 세션의 프로젝트 폴더가 더 이상 존재하지 않습니다: {project_folder}", - "git.branch_unknown": "⚠️ 현재 세션의 branch 를 확인할 수 없습니다.", + "common.button_expired": "⚠️ 이 버튼은 만료되었습니다. 명령을 다시 실행해 주세요.", + "git.branch_unknown": "⚠️ 현재 세션의 브랜치 를 확인할 수 없습니다.", "git.cancel_button": "취소", "git.usage_diff": "사용법: /diff", "git.usage_pull": "사용법: /pull", "git.pull_cancelled": "pull 이 취소되었습니다.", "git.pull_completed": "pull 이 완료되었습니다.", "git.pull_confirm_button": "pull 확인", - "git.pull_confirm_prompt": "branch `{branch_name}` 를 `origin` 에서 pull 할까요?", - "git.pull_confirm_prompt_with_default": "branch `{branch_name}` 를 `origin` 에서 pull 하고 기본 branch `{default_branch}` 도 함께 새로고침할까요?", - "git.pull_in_progress": "branch `{branch_name}` 를 `origin` 에서 pull 하는 중...", - "git.pull_in_progress_with_default": "branch `{branch_name}` 를 `origin` 에서 pull 하고 기본 branch `{default_branch}` 를 새로고침하는 중...", + "git.pull_confirm_prompt": "브랜치 `{branch_name}` 를 `origin` 에서 pull 할까요?", + "git.pull_confirm_prompt_with_default": "브랜치 `{branch_name}` 를 `origin` 에서 pull 하고 기본 브랜치 `{default_branch}` 도 함께 새로고침할까요?", + "git.pull_in_progress": "브랜치 `{branch_name}` 를 `origin` 에서 pull 하는 중...", + "git.pull_in_progress_with_default": "브랜치 `{branch_name}` 를 `origin` 에서 pull 하고 기본 브랜치 `{default_branch}` 를 새로고침하는 중...", "git.push_cancelled": "push 가 취소되었습니다.", "git.push_cancelled_checkout_failed": "push 가 취소되었습니다. 먼저 `{branch_name}` 로 전환하지 못했습니다.", "git.push_confirm_button": "push 확인", - "git.push_confirm_prompt": "branch `{branch_name}` 를 `origin` 으로 push 할까요?", - "git.push_in_progress": "branch `{branch_name}` 를 `origin` 으로 push 하는 중...", + "git.push_confirm_prompt": "브랜치 `{branch_name}` 를 `origin` 으로 push 할까요?", + "git.push_in_progress": "브랜치 `{branch_name}` 를 `origin` 으로 push 하는 중...", "git.usage_push": "사용법: /push", "message.photo_only_codex": "사진 첨부는 현재 Codex 세션에서만 지원됩니다.", "message.question_queued": "질문이 Q{question_number} 로 대기열에 추가되었습니다. 현재 에이전트 작업이 끝난 뒤 처리됩니다.", @@ -55,14 +56,15 @@ "queue.processing_grouped": "대기 질문을 한 배치로 처리합니다.", "queue.processing_single": "대기 질문을 하나씩 처리합니다.", "queue.working_on_queued": "대기 중인 질문 처리 중:", - "runtime.active_run_stall": "현재 에이전트 실행이 멈춘 것 같습니다.\n로컬 에이전트 프로세스는 아직 실행 중이지만 출력이 없습니다.\nmacOS에서는 bot이 실행 중인 장비에서 숨겨진 권한 확인 대화상자가 승인을 기다리고 있어서 이런 현상이 생길 수 있습니다.", + "runtime.active_run_stall": "현재 에이전트 실행이 멈춘 것 같습니다.\n로컬 에이전트 프로세스는 아직 실행 중이지만 출력이 없습니다.\nmacOS에서는 봇이 실행 중인 장비에서 숨겨진 권한 확인 대화상자가 승인을 기다리고 있어서 이런 현상이 생길 수 있습니다.", "runtime.agent_run_aborted": "/abort 로 에이전트 실행이 중단되었습니다.", "runtime.agent_run_failed": "에이전트 실행에 실패했습니다.", "runtime.live_agent_output": "에이전트 실시간 출력", "runtime.photo_too_large": "사진이 너무 큽니다. 지원되는 최대 크기는 5MB입니다.", + "runtime.voice_audio_too_large": "오디오가 로컬 음성 텍스트 변환 한도를 초과했습니다. 지원되는 최대 크기는 {max_size_mb}MB입니다.", "runtime.provider_output_index": "{provider} 출력 {index}/{total}", "runtime.provider_output_single": "{provider} 출력", - "runtime.replacement_session_stall": "대체 세션 생성이 멈춘 것 같습니다.\n로컬 에이전트 프로세스는 아직 실행 중이지만 출력이 없습니다.\nmacOS에서는 bot이 실행 중인 장비에서 숨겨진 권한 확인 대화상자가 승인을 기다리고 있어서 이런 현상이 생길 수 있습니다.", + "runtime.replacement_session_stall": "대체 세션 생성이 멈춘 것 같습니다.\n로컬 에이전트 프로세스는 아직 실행 중이지만 출력이 없습니다.\nmacOS에서는 봇이 실행 중인 장비에서 숨겨진 권한 확인 대화상자가 승인을 기다리고 있어서 이런 현상이 생길 수 있습니다.", "runtime.resume_created_new": "재개에 실패하여 새 세션이 생성되었습니다.\n새 세션 ID: {session_id}\n새 세션 이름: {session_name}", "runtime.resume_id_changed": "재개에는 성공했지만 세션 ID가 변경되었습니다.\n새 세션 ID: {session_id}\n새 세션 이름: {session_name}", "runtime.sensitive_diff_omitted": "{path}\n이 파일에는 민감한 내용이 포함되어 있어 생략되었습니다.", @@ -76,13 +78,13 @@ "status.no_running_agent": "현재 프로젝트에서 실행 중인 에이전트 프로세스를 찾지 못했습니다.", "status.usage_abort": "사용법: /abort", "switch.available_sessions": "사용 가능한 세션 (페이지 {page}/{total_pages}):", - "switch.source_bot_managed": "Bot 관리 세션", + "switch.source_bot_managed": "봇 관리 세션", "switch.source_native_cli": "네이티브 CLI 세션", "switch.current_project_filter": "네이티브 세션용 현재 프로젝트 필터: {project_folder}", "switch.current_project_filter_none": "네이티브 세션용 현재 프로젝트 필터: (없음)", "switch.initialized_label": "초기화 정보", "switch.use_label": "사용:", - "switch.native_import_note": "네이티브 CLI 세션을 선택하면 state.json으로 가져오고 bot이 해당 세션으로 전환됩니다.", + "switch.native_import_note": "네이티브 CLI 세션을 선택하면 state.json으로 가져오고 봇이 해당 세션으로 전환됩니다.", "switch.pages_label": "페이지: /switch page 1 ... /switch page {total_pages}", "switch.no_sessions_found": "세션을 찾을 수 없습니다.", "switch.invalid_page_number": "잘못된 페이지 번호입니다.\n사용법: /switch page ", @@ -90,7 +92,7 @@ "switch.switched_to_session": "세션으로 전환했습니다", "switch.project_label": "프로젝트", "switch.provider_label": "프로바이더", - "switch.branch_label": "Branch", + "switch.branch_label": "브랜치", "switch.source_label": "출처", "switch.imported_into_state_json": "state.json으로 가져왔습니다.", "switch.status_active": "사용 중", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "나중에 다른 언어로 바꾸려면 {env_path} 에서 APP_LOCALE 값을 수정하세요.", "queue.button_cancel": "취소", "queue.cancelled": "대기열 질문이 취소되었습니다.", - "project.branch_switch_requires_source": "branch {new_branch}로 전환하려면 먼저 소스를 선택해야 합니다.", - "project.branch_create_from_source": "새 branch {new_branch}를 만들기 전에 먼저 소스 branch를 선택하세요.", - "project.branch_source_missing": "프로젝트 '{project_folder}'의 branch 소스를 찾을 수 없습니다.\nlocal/{source_branch} 및 origin/{source_branch}가 없습니다.", + "project.branch_switch_requires_source": "브랜치 {new_branch}로 전환하려면 먼저 소스를 선택해야 합니다.", + "project.branch_create_from_source": "새 브랜치 {new_branch}를 만들기 전에 먼저 소스 브랜치를 선택하세요.", + "project.branch_source_missing": "프로젝트 '{project_folder}'의 브랜치 소스를 찾을 수 없습니다.\nlocal/{source_branch} 및 origin/{source_branch}가 없습니다.", "project.project_label": "프로젝트: {project_folder}", - "project.branch_target_label": "대상 branch: {new_branch}", - "project.choose_branch_source": "branch 소스를 선택하세요:", - "project.current_branch_in_repo_label": "저장소의 현재 branch: {branch_name}", - "project.default_branch_label": "기본 branch: {branch_name}", + "project.branch_target_label": "대상 브랜치: {new_branch}", + "project.choose_branch_source": "브랜치 소스를 선택하세요:", + "project.current_branch_in_repo_label": "저장소의 현재 브랜치: {branch_name}", + "project.default_branch_label": "기본 브랜치: {branch_name}", "project.refresh_warnings": "새로고침 경고:", - "project.local_branches": "로컬 branch:", + "project.local_branches": "로컬 브랜치:", "project.annotation_default": "기본", - "project.annotation_current_branch_in_repo": "현재 branch", + "project.annotation_current_branch_in_repo": "현재 브랜치", "project.none": "(없음)", - "project.select_branch_with": "다음 명령으로 branch를 선택하세요:", + "project.select_branch_with": "다음 명령으로 브랜치를 선택하세요:", "project.usage_project": "사용법: /project \n예시: /project backend", "project.invalid_project_folder": "잘못된 프로젝트 폴더입니다. 폴더 이름만 사용할 수 있습니다.", "project.path_not_directory": "프로젝트 경로는 존재하지만 디렉터리가 아닙니다: {folder}", - "project.active_session_mismatch_title": "활성 session과 프로젝트가 일치하지 않습니다", - "project.current_session_html": "현재 session: {session_name}", - "project.session_project_html": "Session 프로젝트: {project_folder}", - "project.start_new_session_for_project_html": "새로 선택한 이 프로젝트에서 작업하려면 /new로 새 session을 시작하세요.", + "project.active_session_mismatch_title": "활성 세션과 프로젝트가 일치하지 않습니다", + "project.current_session_html": "현재 세션: {session_name}", + "project.session_project_html": "세션 프로젝트: {project_folder}", + "project.start_new_session_for_project_html": "새로 선택한 이 프로젝트에서 작업하려면 /new로 새 세션을 시작하세요.", "project.project_changed_to": "프로젝트를 다음으로 변경했습니다: {folder}", - "project.branch_selection_required": "이 프로젝트에서 session을 만들거나 이어서 작업하려면 먼저 branch를 선택해야 합니다.", - "project.active_session_label": "활성 session: {session_name}", - "project.session_project_label": "Session 프로젝트: {project_folder}", + "project.branch_selection_required": "이 프로젝트에서 세션을 만들거나 이어서 작업하려면 먼저 브랜치를 선택해야 합니다.", + "project.active_session_label": "활성 세션: {session_name}", + "project.session_project_label": "세션 프로젝트: {project_folder}", "project.project_set_title": "프로젝트 설정 완료", "project.project_html": "프로젝트: {project_folder}", - "project.current_branch_html": "현재 branch: {branch_name}", - "project.branch_usage_html": "전용 작업 branch가 필요하면 /branch <new_branch> 또는 /branch <origin_branch> <new_branch>를 사용하세요.", - "project.default_branch_behavior_html": "<origin_branch>를 지정하지 않으면 bot이 기본 branch {branch_name}를 사용합니다.", - "project.current_branch_behavior_html": "설정하지 않으면 bot이 현재 branch {branch_name}에서 작업합니다", + "project.current_branch_html": "현재 브랜치: {branch_name}", + "project.branch_usage_html": "전용 작업 브랜치가 필요하면 /branch <new_branch> 또는 /branch <origin_branch> <new_branch>를 사용하세요.", + "project.default_branch_behavior_html": "<origin_branch>를 지정하지 않으면 봇이 기본 브랜치 {branch_name}를 사용합니다.", + "project.current_branch_behavior_html": "설정하지 않으면 봇이 현재 브랜치 {branch_name}에서 작업합니다", "project.trust_prompt_html": "이 프로젝트를 신뢰하시겠습니까?\n프로젝트: {project_folder}", "project.invalid_trust_decision": "잘못된 신뢰 선택입니다.", "project.project_folder_missing_only": "프로젝트 폴더가 존재하지 않습니다: {project_folder}", @@ -137,41 +139,41 @@ "project.already_trusted": "프로젝트는 이미 신뢰 상태입니다: {folder}", "project.trusted": "프로젝트를 신뢰 상태로 설정했습니다: {folder}", "project.usage_branch": "사용법: /branch \n또는: /branch ", - "project.default_branch_unknown": "이 저장소의 기본 branch를 확인할 수 없습니다.", + "project.default_branch_unknown": "이 저장소의 기본 브랜치를 확인할 수 없습니다.", "project.project_folder_missing_retry": "프로젝트 폴더가 존재하지 않습니다: {project_folder}\n다시 /project {project_folder}를 실행하세요.", "branch_resolution.use_branch": "{branch_name} 사용", - "branch_resolution.create_from_instead_of_origin": "origin/{source_branch} 대신 이 branch들 중 하나에서 branch {new_branch}를 만들겠습니까?", - "branch_resolution.discrepancy_detected": "활성 session을 실행하기 전에 branch 불일치가 감지되었습니다.", + "branch_resolution.create_from_instead_of_origin": "origin/{source_branch} 대신 이 브랜치들 중 하나에서 브랜치 {new_branch}를 만들겠습니까?", + "branch_resolution.discrepancy_detected": "활성 세션을 실행하기 전에 브랜치 불일치가 감지되었습니다.", "branch_resolution.session_label": "세션: {session_name}", - "branch_resolution.stored_branch_label": "저장된 branch: {branch_name}", - "branch_resolution.choose_branch_to_use": "어떤 branch를 사용할지 선택하세요.", - "branch_resolution.no_pending_decision": "대기 중인 branch 결정이 없습니다.", - "branch_resolution.no_pending_discrepancy": "대기 중인 branch 불일치가 없습니다.", - "branch_resolution.no_active_session": "활성 session이 없습니다.", - "branch_resolution.using_current_branch": "현재 branch 사용: {branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "저장된 branch를 더 이상 사용할 수 없습니다: {branch_name}\n대체 소스 branch도 없습니다.", - "branch_resolution.stored_branch_unavailable": "저장된 branch를 더 이상 사용할 수 없습니다.", + "branch_resolution.stored_branch_label": "저장된 브랜치: {branch_name}", + "branch_resolution.choose_branch_to_use": "어떤 브랜치를 사용할지 선택하세요.", + "branch_resolution.no_pending_decision": "대기 중인 브랜치 결정이 없습니다.", + "branch_resolution.no_pending_discrepancy": "대기 중인 브랜치 불일치가 없습니다.", + "branch_resolution.no_active_session": "활성 세션이 없습니다.", + "branch_resolution.using_current_branch": "현재 브랜치 사용: {branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "저장된 브랜치를 더 이상 사용할 수 없습니다: {branch_name}\n대체 소스 브랜치도 없습니다.", + "branch_resolution.stored_branch_unavailable": "저장된 브랜치를 더 이상 사용할 수 없습니다.", "branch_resolution.missing_local_and_origin": "local/{branch_name} 및 origin/{branch_name}가 없습니다.", - "branch_resolution.create_branch_from_choices": "이 branch들 중 하나에서 branch {branch_name}를 만들겠습니까?", - "branch_resolution.choose_restore_method": "저장된 branch를 복구할 방법을 선택하세요.", - "lifecycle.provider_selection_required": "session을 만들거나 이어서 작업하기 전에 provider를 선택해야 합니다.", + "branch_resolution.create_branch_from_choices": "이 브랜치들 중 하나에서 브랜치 {branch_name}를 만들겠습니까?", + "branch_resolution.choose_restore_method": "저장된 브랜치를 복구할 방법을 선택하세요.", + "lifecycle.provider_selection_required": "세션을 만들거나 이어서 작업하기 전에 프로바이더를 선택해야 합니다.", "lifecycle.no_project_selected_example": "선택된 프로젝트가 없습니다.\n먼저 /project 를 실행하세요.\n예시: /project backend", - "lifecycle.session_name_exists": "Session 이름이 이미 존재합니다: {session_name}\n다른 session 이름을 사용하세요.", - "lifecycle.creating_session": "session을 생성하는 중...", - "lifecycle.failed_create_session": "session 생성에 실패했습니다.", - "lifecycle.session_created_successfully": "session을 생성했습니다: {session_name}\nSession ID: {session_id}\n프로젝트: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "provider.cli_not_found": "{provider_label} CLI를 찾을 수 없습니다: {bin_name}\n/provider 를 실행해서 사용 가능한 provider 를 선택하거나 bot 설정을 업데이트하세요.", + "lifecycle.session_name_exists": "세션 이름이 이미 존재합니다: {session_name}\n다른 세션 이름을 사용하세요.", + "lifecycle.creating_session": "세션을 생성하는 중...", + "lifecycle.failed_create_session": "세션 생성에 실패했습니다.", + "lifecycle.session_created_successfully": "세션을 생성했습니다: {session_name}\n세션 ID: {session_id}\n프로젝트: {project_folder}\n프로바이더: {provider}\n브랜치: {branch_name}", + "provider.cli_not_found": "{provider_label} CLI를 찾을 수 없습니다: {bin_name}\n/provider 를 실행해서 사용 가능한 프로바이더 를 선택하거나 봇 설정을 업데이트하세요.", "provider.status_available": "사용 가능", "provider.status_missing": "없음", "provider.status_current": "현재", "provider.usage_provider": "사용법: /provider", - "provider.current_provider_prompt": "현재 provider: {provider}\n새 session 에 사용할 provider 를 선택하세요.", + "provider.current_provider_prompt": "현재 프로바이더: {provider}\n새 세션 에 사용할 프로바이더 를 선택하세요.", "provider.not_selected": "(선택 안 됨)", - "provider.cli_not_found_install_first": "{provider_label} CLI를 찾을 수 없습니다: {bin_name}\nbot 설정을 업데이트하거나 먼저 이 CLI를 설치하세요.", - "provider.current_provider_set": "현재 provider 를 {provider}(으)로 설정했습니다", - "status.current_session_details": "현재 session: {session_name}\nSession ID: {session_id}\n프로젝트: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "status.current_branch_placeholder": "(현재 branch)", - "git.commit_disabled": "/commit 이 비활성화되어 있습니다.\n활성화하려면 bot 환경에서 ENABLE_COMMIT_COMMAND=true 를 설정하세요.", + "provider.cli_not_found_install_first": "{provider_label} CLI를 찾을 수 없습니다: {bin_name}\n봇 설정을 업데이트하거나 먼저 이 CLI를 설치하세요.", + "provider.current_provider_set": "현재 프로바이더 를 {provider}(으)로 설정했습니다", + "status.current_session_details": "현재 세션: {session_name}\n세션 ID: {session_id}\n프로젝트: {project_folder}\n프로바이더: {provider}\n브랜치: {branch_name}", + "status.current_branch_placeholder": "(현재 브랜치)", + "git.commit_disabled": "/commit 이 비활성화되어 있습니다.\n활성화하려면 봇 환경에서 ENABLE_COMMIT_COMMAND=true 를 설정하세요.", "git.usage_commit": "사용법: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "유효한 git commit 명령을 찾지 못했습니다.", "git.commit_generate_prompt": "현재 변경된 파일에 대한 git commit 명령을 생성할까요?", @@ -204,7 +206,7 @@ "cli.then_run": "그다음 실행: coding-agent-telegram", "bot.command.compact": "활성 세션 compact", "status.usage_compact": "사용법: /compact", - "runtime.compacting_session": "활성 session 을 compact 하는 중입니다...", - "runtime.compact_summary_missing": "provider 가 사용할 수 있는 compact handoff summary 를 반환하지 않았습니다.", - "runtime.session_compacted": "session compact 를 완료했습니다. {session_name} ({session_id}) 로 전환했습니다." + "runtime.compacting_session": "활성 세션 을 compact 하는 중입니다...", + "runtime.compact_summary_missing": "프로바이더 가 사용할 수 있는 compact handoff summary 를 반환하지 않았습니다.", + "runtime.session_compacted": "세션 compact 를 완료했습니다. {session_name} ({session_id}) 로 전환했습니다." } diff --git a/src/coding_agent_telegram/resources/locales/nl.json b/src/coding_agent_telegram/resources/locales/nl.json index 948c8cb..f4e3f52 100644 --- a/src/coding_agent_telegram/resources/locales/nl.json +++ b/src/coding_agent_telegram/resources/locales/nl.json @@ -1,14 +1,14 @@ { "bot.command.abort": "Huidige agent-run afbreken", - "bot.command.branch": "Git branch maken en wisselen", + "bot.command.branch": "Git tak maken en wisselen", "bot.command.commit": "Gevalideerde Git commit-opdrachten uitvoeren", "bot.command.current": "Actieve sessie tonen", "bot.command.diff": "Gewijzigde bestandsnamen ten opzichte van HEAD tonen", "bot.command.new": "Nieuwe sessie maken", - "bot.command.pull": "De huidige sessiebranch pullen", + "bot.command.pull": "De huidige sessietak pullen", "bot.command.project": "Huidige projectmap instellen", - "bot.command.provider": "Provider voor nieuwe sessies kiezen", - "bot.command.push": "De huidige sessiebranch pushen", + "bot.command.provider": "Aanbieder voor nieuwe sessies kiezen", + "bot.command.push": "De huidige sessietak pushen", "bot.command.switch": "Sessies tonen of wisselen", "bot.error.command_failed": "⚠️ Opdracht mislukt. Controleer de serverlog.", "bot.error.session_store": "⚠️ {error}", @@ -18,22 +18,23 @@ "common.no_project_selected": "Geen project geselecteerd.\nVoer eerst /project uit.", "common.project_busy": "Er draait momenteel een agent op project '{project_folder}'.\nAlleen /current en /abort zijn beschikbaar totdat die klaar is.", "common.project_folder_missing": "⚠️ De projectmap voor deze sessie bestaat niet meer: {project_folder}", - "git.branch_unknown": "⚠️ De branch voor de huidige sessie kon niet worden bepaald.", + "common.button_expired": "⚠️ Deze knop is verlopen. Voer de opdracht opnieuw uit.", + "git.branch_unknown": "⚠️ De tak voor de huidige sessie kon niet worden bepaald.", "git.cancel_button": "Annuleren", "git.usage_diff": "Gebruik: /diff", "git.usage_pull": "Gebruik: /pull", "git.pull_cancelled": "Pull geannuleerd.", "git.pull_completed": "Pull voltooid.", "git.pull_confirm_button": "Pull bevestigen", - "git.pull_confirm_prompt": "Branch `{branch_name}` van `origin` pullen?", - "git.pull_confirm_prompt_with_default": "Branch `{branch_name}` van `origin` pullen en ook de standaardbranch `{default_branch}` verversen?", - "git.pull_in_progress": "Branch `{branch_name}` wordt van `origin` gepulld...", - "git.pull_in_progress_with_default": "Branch `{branch_name}` wordt van `origin` gepulld en de standaardbranch `{default_branch}` wordt ververst...", + "git.pull_confirm_prompt": "Tak `{branch_name}` van `origin` pullen?", + "git.pull_confirm_prompt_with_default": "Tak `{branch_name}` van `origin` pullen en ook de standaardtak `{default_branch}` verversen?", + "git.pull_in_progress": "Tak `{branch_name}` wordt van `origin` gepulld...", + "git.pull_in_progress_with_default": "Tak `{branch_name}` wordt van `origin` gepulld en de standaardtak `{default_branch}` wordt ververst...", "git.push_cancelled": "Push geannuleerd.", "git.push_cancelled_checkout_failed": "Push geannuleerd. Wisselen naar `{branch_name}` is eerst mislukt.", "git.push_confirm_button": "Push bevestigen", - "git.push_confirm_prompt": "Branch `{branch_name}` naar `origin` pushen?", - "git.push_in_progress": "Branch `{branch_name}` wordt naar `origin` gepusht...", + "git.push_confirm_prompt": "Tak `{branch_name}` naar `origin` pushen?", + "git.push_in_progress": "Tak `{branch_name}` wordt naar `origin` gepusht...", "git.usage_push": "Gebruik: /push", "message.photo_only_codex": "Foto-bijlagen worden momenteel alleen ondersteund voor Codex-sessies.", "message.question_queued": "Vraag in de wachtrij geplaatst als Q{question_number}. Deze wordt verwerkt nadat de huidige agenttaak is voltooid.", @@ -60,6 +61,7 @@ "runtime.agent_run_failed": "De agent-run is mislukt.", "runtime.live_agent_output": "Live agentuitvoer", "runtime.photo_too_large": "De foto is te groot. De maximaal ondersteunde grootte is 5 MB.", + "runtime.voice_audio_too_large": "De audio is te groot voor lokale spraak-naar-tekst. De maximaal ondersteunde grootte is {max_size_mb} MB.", "runtime.provider_output_index": "{provider}-uitvoer {index}/{total}", "runtime.provider_output_single": "{provider}-uitvoer", "runtime.replacement_session_stall": "Het aanmaken van de vervangende sessie lijkt vast te lopen.\nHet lokale agentproces draait nog steeds maar heeft geen uitvoer gegeven.\nOp macOS kan dit komen doordat er op de machine waarop de bot draait een verborgen machtigingsdialoog op bevestiging wacht.", @@ -76,7 +78,7 @@ "status.no_running_agent": "Er is geen draaiend agentproces gevonden voor het huidige project.", "status.usage_abort": "Gebruik: /abort", "switch.available_sessions": "Beschikbare sessies (pagina {page}/{total_pages}):", - "switch.source_bot_managed": "Bot-beheerde sessie", + "switch.source_bot_managed": "bot-beheerde sessie", "switch.source_native_cli": "Native CLI-sessie", "switch.current_project_filter": "Huidige projectfilter voor native sessies: {project_folder}", "switch.current_project_filter_none": "Huidige projectfilter voor native sessies: (geen)", @@ -89,8 +91,8 @@ "switch.session_not_found": "⚠️ Sessie niet gevonden.\nVoer /switch uit om beschikbare sessies weer te geven.", "switch.switched_to_session": "Gewisseld naar sessie", "switch.project_label": "Project", - "switch.provider_label": "Provider", - "switch.branch_label": "Branch", + "switch.provider_label": "Aanbieder", + "switch.branch_label": "Tak", "switch.source_label": "Bron", "switch.imported_into_state_json": "Geïmporteerd in state.json.", "switch.status_active": "actief", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "Wil je later een andere taal gebruiken, wijzig dan APP_LOCALE in {env_path}.", "queue.button_cancel": "Annuleren", "queue.cancelled": "De vragen in de wachtrij zijn geannuleerd.", - "project.branch_switch_requires_source": "Om naar branch {new_branch} te wisselen, moet je eerst een bron kiezen.", - "project.branch_create_from_source": "Kies eerst een bron-branch om de nieuwe branch {new_branch} aan te maken.", - "project.branch_source_missing": "Geen branch-bron gevonden voor project '{project_folder}'.\nlocal/{source_branch} en origin/{source_branch} ontbreken.", + "project.branch_switch_requires_source": "Om naar tak {new_branch} te wisselen, moet je eerst een bron kiezen.", + "project.branch_create_from_source": "Kies eerst een bron-tak om de nieuwe tak {new_branch} aan te maken.", + "project.branch_source_missing": "Geen tak-bron gevonden voor project '{project_folder}'.\nlocal/{source_branch} en origin/{source_branch} ontbreken.", "project.project_label": "Project: {project_folder}", - "project.branch_target_label": "Doel-branch: {new_branch}", - "project.choose_branch_source": "Kies de branch-bron:", - "project.current_branch_in_repo_label": "Huidige branch in repo: {branch_name}", - "project.default_branch_label": "Standaard-branch: {branch_name}", + "project.branch_target_label": "Doel-tak: {new_branch}", + "project.choose_branch_source": "Kies de tak-bron:", + "project.current_branch_in_repo_label": "Huidige tak in repo: {branch_name}", + "project.default_branch_label": "Standaard-tak: {branch_name}", "project.refresh_warnings": "Vernieuwingswaarschuwingen:", - "project.local_branches": "Lokale branches:", + "project.local_branches": "Lokale takken:", "project.annotation_default": "standaard", - "project.annotation_current_branch_in_repo": "huidige branch in repo", + "project.annotation_current_branch_in_repo": "huidige tak in repo", "project.none": "(geen)", - "project.select_branch_with": "Kies een branch met:", + "project.select_branch_with": "Kies een tak met:", "project.usage_project": "Gebruik: /project \nVoorbeeld: /project backend", "project.invalid_project_folder": "Ongeldige projectmap. Alleen een mapnaam is toegestaan.", "project.path_not_directory": "Projectpad bestaat, maar is geen map: {folder}", - "project.active_session_mismatch_title": "Actieve session komt niet overeen met project", - "project.current_session_html": "Huidige session: {session_name}", - "project.session_project_html": "Session-project: {project_folder}", - "project.start_new_session_for_project_html": "Start een nieuwe session met /new als je in dit nieuw gekozen project wilt werken.", + "project.active_session_mismatch_title": "Actieve sessie komt niet overeen met project", + "project.current_session_html": "Huidige sessie: {session_name}", + "project.session_project_html": "Sessie-project: {project_folder}", + "project.start_new_session_for_project_html": "Start een nieuwe sessie met /new als je in dit nieuw gekozen project wilt werken.", "project.project_changed_to": "Project gewijzigd naar: {folder}", - "project.branch_selection_required": "Je moet eerst een branch kiezen voordat je in dit project een session maakt of hervat.", - "project.active_session_label": "Actieve session: {session_name}", - "project.session_project_label": "Session-project: {project_folder}", + "project.branch_selection_required": "Je moet eerst een tak kiezen voordat je in dit project een sessie maakt of hervat.", + "project.active_session_label": "Actieve sessie: {session_name}", + "project.session_project_label": "Sessie-project: {project_folder}", "project.project_set_title": "Project ingesteld", "project.project_html": "Project: {project_folder}", - "project.current_branch_html": "Huidige branch: {branch_name}", - "project.branch_usage_html": "Gebruik /branch <new_branch> of /branch <origin_branch> <new_branch> als je een aparte werk-branch wilt.", - "project.default_branch_behavior_html": "Als <origin_branch> niet is opgegeven, gebruikt de bot de standaard-branch: {branch_name}.", - "project.current_branch_behavior_html": "Als je er geen instelt, werkt de bot op de huidige branch: {branch_name}", + "project.current_branch_html": "Huidige tak: {branch_name}", + "project.branch_usage_html": "Gebruik /branch <new_branch> of /branch <origin_branch> <new_branch> als je een aparte werk-tak wilt.", + "project.default_branch_behavior_html": "Als <origin_branch> niet is opgegeven, gebruikt de bot de standaard-tak: {branch_name}.", + "project.current_branch_behavior_html": "Als je er geen instelt, werkt de bot op de huidige tak: {branch_name}", "project.trust_prompt_html": "Vertrouw je dit project?\nProject: {project_folder}", "project.invalid_trust_decision": "Ongeldige vertrouwenskeuze.", "project.project_folder_missing_only": "Projectmap bestaat niet: {project_folder}", @@ -137,40 +139,40 @@ "project.already_trusted": "Project is al vertrouwd: {folder}", "project.trusted": "Project vertrouwd: {folder}", "project.usage_branch": "Gebruik: /branch \nOf: /branch ", - "project.default_branch_unknown": "Kon de standaard-branch voor deze repository niet bepalen.", + "project.default_branch_unknown": "Kon de standaard-tak voor deze repository niet bepalen.", "project.project_folder_missing_retry": "Projectmap bestaat niet: {project_folder}\nVoer /project {project_folder} opnieuw uit.", "branch_resolution.use_branch": "{branch_name} gebruiken", - "branch_resolution.create_from_instead_of_origin": "Wil je branch {new_branch} aanmaken vanaf een van deze branches in plaats van origin/{source_branch}?", - "branch_resolution.discrepancy_detected": "Er is een branch-verschil gedetecteerd voordat de actieve session werd gestart.", + "branch_resolution.create_from_instead_of_origin": "Wil je tak {new_branch} aanmaken vanaf een van deze takken in plaats van origin/{source_branch}?", + "branch_resolution.discrepancy_detected": "Er is een tak-verschil gedetecteerd voordat de actieve sessie werd gestart.", "branch_resolution.session_label": "Sessie: {session_name}", - "branch_resolution.stored_branch_label": "Opgeslagen branch: {branch_name}", - "branch_resolution.choose_branch_to_use": "Kies welke branch je wilt gebruiken.", - "branch_resolution.no_pending_decision": "Geen openstaande branch-keuze gevonden.", - "branch_resolution.no_pending_discrepancy": "Geen openstaand branch-verschil gevonden.", - "branch_resolution.no_active_session": "Er is geen actieve session beschikbaar.", - "branch_resolution.using_current_branch": "Huidige branch gebruiken: {branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "Opgeslagen branch is niet meer beschikbaar: {branch_name}\nEr is geen alternatieve bron-branch beschikbaar.", - "branch_resolution.stored_branch_unavailable": "Opgeslagen branch is niet meer beschikbaar.", + "branch_resolution.stored_branch_label": "Opgeslagen tak: {branch_name}", + "branch_resolution.choose_branch_to_use": "Kies welke tak je wilt gebruiken.", + "branch_resolution.no_pending_decision": "Geen openstaande tak-keuze gevonden.", + "branch_resolution.no_pending_discrepancy": "Geen openstaand tak-verschil gevonden.", + "branch_resolution.no_active_session": "Er is geen actieve sessie beschikbaar.", + "branch_resolution.using_current_branch": "Huidige tak gebruiken: {branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "Opgeslagen tak is niet meer beschikbaar: {branch_name}\nEr is geen alternatieve bron-tak beschikbaar.", + "branch_resolution.stored_branch_unavailable": "Opgeslagen tak is niet meer beschikbaar.", "branch_resolution.missing_local_and_origin": "local/{branch_name} en origin/{branch_name} ontbreken.", - "branch_resolution.create_branch_from_choices": "Wil je branch {branch_name} aanmaken vanaf een van deze branches?", - "branch_resolution.choose_restore_method": "Kies hoe je de opgeslagen branch wilt herstellen.", - "lifecycle.provider_selection_required": "Je moet eerst een provider kiezen voordat je een session maakt of hervat.", + "branch_resolution.create_branch_from_choices": "Wil je tak {branch_name} aanmaken vanaf een van deze takken?", + "branch_resolution.choose_restore_method": "Kies hoe je de opgeslagen tak wilt herstellen.", + "lifecycle.provider_selection_required": "Je moet eerst een aanbieder kiezen voordat je een sessie maakt of hervat.", "lifecycle.no_project_selected_example": "Geen project geselecteerd.\nVoer eerst /project uit.\nVoorbeeld: /project backend", - "lifecycle.session_name_exists": "De sessionnaam bestaat al: {session_name}\nGebruik een andere sessionnaam.", - "lifecycle.creating_session": "Session wordt aangemaakt...", - "lifecycle.failed_create_session": "Maken van de session is mislukt.", - "lifecycle.session_created_successfully": "Session succesvol aangemaakt: {session_name}\nSession ID: {session_id}\nProject: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "provider.cli_not_found": "{provider_label} CLI niet gevonden: {bin_name}\nVoer /provider uit om een beschikbare provider te kiezen of de botconfiguratie bij te werken.", + "lifecycle.session_name_exists": "De sessienaam bestaat al: {session_name}\nGebruik een andere sessienaam.", + "lifecycle.creating_session": "Sessie wordt aangemaakt...", + "lifecycle.failed_create_session": "Maken van de sessie is mislukt.", + "lifecycle.session_created_successfully": "Sessie succesvol aangemaakt: {session_name}\nSessie ID: {session_id}\nProject: {project_folder}\nAanbieder: {provider}\nTak: {branch_name}", + "provider.cli_not_found": "{provider_label} CLI niet gevonden: {bin_name}\nVoer /provider uit om een beschikbare aanbieder te kiezen of de botconfiguratie bij te werken.", "provider.status_available": "beschikbaar", "provider.status_missing": "ontbreekt", "provider.status_current": "huidig", "provider.usage_provider": "Gebruik: /provider", - "provider.current_provider_prompt": "Huidige provider: {provider}\nKies de provider voor nieuwe sessions.", + "provider.current_provider_prompt": "Huidige aanbieder: {provider}\nKies de aanbieder voor nieuwe sessies.", "provider.not_selected": "(niet geselecteerd)", "provider.cli_not_found_install_first": "{provider_label} CLI niet gevonden: {bin_name}\nWerk de botconfiguratie bij of installeer eerst deze CLI.", - "provider.current_provider_set": "Huidige provider ingesteld op: {provider}", - "status.current_session_details": "Huidige session: {session_name}\nSession ID: {session_id}\nProject: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "status.current_branch_placeholder": "(huidige branch)", + "provider.current_provider_set": "Huidige aanbieder ingesteld op: {provider}", + "status.current_session_details": "Huidige sessie: {session_name}\nSessie ID: {session_id}\nProject: {project_folder}\nAanbieder: {provider}\nTak: {branch_name}", + "status.current_branch_placeholder": "(huidige tak)", "git.commit_disabled": "/commit is uitgeschakeld.\nStel ENABLE_COMMIT_COMMAND=true in in de botomgeving om het in te schakelen.", "git.usage_commit": "Gebruik: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Er zijn geen geldige git commit-opdrachten gevonden.", @@ -204,7 +206,7 @@ "cli.then_run": "Voer daarna uit: coding-agent-telegram", "bot.command.compact": "Actieve sessie comprimeren", "status.usage_compact": "Gebruik: /compact", - "runtime.compacting_session": "Actieve session wordt gecompact...", - "runtime.compact_summary_missing": "De provider gaf geen bruikbare compacte overdrachtssamenvatting terug.", - "runtime.session_compacted": "Session succesvol gecompact. Overgeschakeld naar {session_name} ({session_id})." + "runtime.compacting_session": "Actieve sessie wordt gecompact...", + "runtime.compact_summary_missing": "De aanbieder gaf geen bruikbare compacte overdrachtssamenvatting terug.", + "runtime.session_compacted": "Sessie succesvol gecompact. Overgeschakeld naar {session_name} ({session_id})." } diff --git a/src/coding_agent_telegram/resources/locales/th.json b/src/coding_agent_telegram/resources/locales/th.json index 95713c0..3ee0453 100644 --- a/src/coding_agent_telegram/resources/locales/th.json +++ b/src/coding_agent_telegram/resources/locales/th.json @@ -1,14 +1,14 @@ { "bot.command.abort": "ยกเลิกงานเอเจนต์ปัจจุบัน", - "bot.command.branch": "สร้างและสลับไปยัง Git branch", + "bot.command.branch": "สร้างและสลับไปยัง Git สาขา", "bot.command.commit": "รันคำสั่ง Git commit ที่ผ่านการตรวจสอบ", "bot.command.current": "แสดงเซสชันที่กำลังใช้งาน", "bot.command.diff": "แสดงชื่อไฟล์ที่เปลี่ยนไปเมื่อเทียบกับ HEAD", "bot.command.new": "สร้างเซสชันใหม่", - "bot.command.pull": "pull session branch ปัจจุบัน", + "bot.command.pull": "pull เซสชัน สาขา ปัจจุบัน", "bot.command.project": "ตั้งค่าโฟลเดอร์โปรเจ็กต์ปัจจุบัน", "bot.command.provider": "เลือกผู้ให้บริการสำหรับเซสชันใหม่", - "bot.command.push": "push session branch ปัจจุบัน", + "bot.command.push": "push เซสชัน สาขา ปัจจุบัน", "bot.command.switch": "แสดงรายการเซสชันหรือสลับเซสชัน", "bot.error.command_failed": "⚠️ คำสั่งล้มเหลว โปรดตรวจสอบบันทึกของเซิร์ฟเวอร์", "bot.error.session_store": "⚠️ {error}", @@ -18,20 +18,21 @@ "common.no_project_selected": "ยังไม่ได้เลือกโปรเจ็กต์\nโปรดรัน /project ก่อน", "common.project_busy": "ขณะนี้มีเอเจนต์กำลังทำงานอยู่บนโปรเจ็กต์ '{project_folder}'\nจนกว่าจะเสร็จ จะใช้ได้เฉพาะ /current และ /abort", "common.project_folder_missing": "⚠️ โฟลเดอร์โปรเจ็กต์สำหรับเซสชันนี้ไม่มีอยู่แล้ว: {project_folder}", - "git.branch_unknown": "⚠️ ไม่สามารถระบุ branch ของ session ปัจจุบันได้", + "common.button_expired": "⚠️ ปุ่มนี้หมดอายุแล้ว โปรดลองสั่งคำสั่งอีกครั้ง", + "git.branch_unknown": "⚠️ ไม่สามารถระบุ สาขา ของ เซสชัน ปัจจุบันได้", "git.cancel_button": "ยกเลิก", "git.pull_cancelled": "ยกเลิก pull แล้ว", "git.pull_completed": "pull เสร็จแล้ว", "git.pull_confirm_button": "ยืนยัน pull", - "git.pull_confirm_prompt": "ต้องการ pull branch `{branch_name}` จาก `origin` หรือไม่?", - "git.pull_confirm_prompt_with_default": "ต้องการ pull default branch `{default_branch}` และ branch `{branch_name}` จาก `origin` หรือไม่?", - "git.pull_in_progress": "กำลัง pull branch `{branch_name}` จาก `origin`...", - "git.pull_in_progress_with_default": "กำลัง pull default branch `{default_branch}` และ branch `{branch_name}` จาก `origin`...", + "git.pull_confirm_prompt": "ต้องการ pull สาขา `{branch_name}` จาก `origin` หรือไม่?", + "git.pull_confirm_prompt_with_default": "ต้องการ pull default สาขา `{default_branch}` และ สาขา `{branch_name}` จาก `origin` หรือไม่?", + "git.pull_in_progress": "กำลัง pull สาขา `{branch_name}` จาก `origin`...", + "git.pull_in_progress_with_default": "กำลัง pull default สาขา `{default_branch}` และ สาขา `{branch_name}` จาก `origin`...", "git.push_cancelled": "ยกเลิก push แล้ว", "git.push_cancelled_checkout_failed": "ยกเลิก push แล้ว เนื่องจากสลับไป `{branch_name}` ไม่สำเร็จก่อน", "git.push_confirm_button": "ยืนยัน push", - "git.push_confirm_prompt": "ต้องการ push branch `{branch_name}` ไปที่ `origin` หรือไม่?", - "git.push_in_progress": "กำลัง push branch `{branch_name}` ไปที่ `origin`...", + "git.push_confirm_prompt": "ต้องการ push สาขา `{branch_name}` ไปที่ `origin` หรือไม่?", + "git.push_in_progress": "กำลัง push สาขา `{branch_name}` ไปที่ `origin`...", "git.usage_diff": "วิธีใช้: /diff", "git.usage_pull": "วิธีใช้: /pull", "git.usage_push": "วิธีใช้: /push", @@ -55,16 +56,17 @@ "queue.processing_grouped": "กำลังประมวลผลคำถามในคิวเป็นชุดเดียว", "queue.processing_single": "กำลังประมวลผลคำถามในคิวทีละข้อ", "queue.working_on_queued": "กำลังประมวลผลคำถามในคิว:", - "runtime.active_run_stall": "ดูเหมือนว่างานของเอเจนต์ปัจจุบันค้างอยู่\nโปรเซสเอเจนต์ในเครื่องยังทำงานอยู่ แต่ยังไม่มีเอาต์พุต\nบน macOS อาจเกิดจากมีหน้าต่างขอสิทธิ์ที่ซ่อนอยู่กำลังรอการยืนยันบนเครื่องที่รัน bot อยู่", + "runtime.active_run_stall": "ดูเหมือนว่างานของเอเจนต์ปัจจุบันค้างอยู่\nโปรเซสเอเจนต์ในเครื่องยังทำงานอยู่ แต่ยังไม่มีเอาต์พุต\nบน macOS อาจเกิดจากมีหน้าต่างขอสิทธิ์ที่ซ่อนอยู่กำลังรอการยืนยันบนเครื่องที่รัน บอท อยู่", "runtime.agent_run_aborted": "การทำงานของเอเจนต์ถูกยกเลิกโดย /abort", "runtime.agent_run_failed": "การทำงานของเอเจนต์ล้มเหลว", "runtime.live_agent_output": "ผลลัพธ์สดของเอเจนต์", "runtime.photo_too_large": "รูปภาพมีขนาดใหญ่เกินไป ขนาดสูงสุดที่รองรับคือ 5 MB", + "runtime.voice_audio_too_large": "ไฟล์เสียงมีขนาดใหญ่เกินไปสำหรับการถอดเสียงในเครื่อง ขนาดสูงสุดที่รองรับคือ {max_size_mb} MB", "runtime.provider_output_index": "ผลลัพธ์ {provider} {index}/{total}", "runtime.provider_output_single": "ผลลัพธ์ {provider}", - "runtime.replacement_session_stall": "ดูเหมือนว่าการสร้างเซสชันทดแทนค้างอยู่\nโปรเซสเอเจนต์ในเครื่องยังทำงานอยู่ แต่ยังไม่มีเอาต์พุต\nบน macOS อาจเกิดจากมีหน้าต่างขอสิทธิ์ที่ซ่อนอยู่กำลังรอการยืนยันบนเครื่องที่รัน bot อยู่", - "runtime.resume_created_new": "กลับมาทำงานต่อไม่สำเร็จ จึงสร้างเซสชันใหม่แทน\nSession ID ใหม่: {session_id}\nชื่อเซสชันใหม่: {session_name}", - "runtime.resume_id_changed": "กลับมาทำงานต่อได้สำเร็จ แต่ session ID เปลี่ยนไป\nSession ID ใหม่: {session_id}\nชื่อเซสชันใหม่: {session_name}", + "runtime.replacement_session_stall": "ดูเหมือนว่าการสร้างเซสชันทดแทนค้างอยู่\nโปรเซสเอเจนต์ในเครื่องยังทำงานอยู่ แต่ยังไม่มีเอาต์พุต\nบน macOS อาจเกิดจากมีหน้าต่างขอสิทธิ์ที่ซ่อนอยู่กำลังรอการยืนยันบนเครื่องที่รัน บอท อยู่", + "runtime.resume_created_new": "กลับมาทำงานต่อไม่สำเร็จ จึงสร้างเซสชันใหม่แทน\nเซสชัน ID ใหม่: {session_id}\nชื่อเซสชันใหม่: {session_name}", + "runtime.resume_id_changed": "กลับมาทำงานต่อได้สำเร็จ แต่ เซสชัน ID เปลี่ยนไป\nเซสชัน ID ใหม่: {session_id}\nชื่อเซสชันใหม่: {session_name}", "runtime.sensitive_diff_omitted": "{path}\nไฟล์นี้มีข้อมูลสำคัญจึงถูกละไว้", "runtime.voice_conversion_failed": "แปลงเสียงเป็นข้อความไม่สำเร็จ", "runtime.voice_conversion_timed_out": "การแปลงเสียงเป็นข้อความหมดเวลา", @@ -76,13 +78,13 @@ "status.no_running_agent": "ไม่พบโปรเซสเอเจนต์ที่กำลังทำงานสำหรับโปรเจ็กต์ปัจจุบัน", "status.usage_abort": "วิธีใช้: /abort", "switch.available_sessions": "เซสชันที่พร้อมใช้งาน (หน้า {page}/{total_pages}):", - "switch.source_bot_managed": "เซสชันที่ bot จัดการ", + "switch.source_bot_managed": "เซสชันที่ บอท จัดการ", "switch.source_native_cli": "เซสชัน Native CLI", "switch.current_project_filter": "ตัวกรองโปรเจ็กต์ปัจจุบันสำหรับเซสชัน native: {project_folder}", "switch.current_project_filter_none": "ตัวกรองโปรเจ็กต์ปัจจุบันสำหรับเซสชัน native: (ไม่มี)", "switch.initialized_label": "แหล่งที่เริ่มต้น", "switch.use_label": "ใช้:", - "switch.native_import_note": "การเลือกเซสชัน Native CLI จะนำเข้าไปยัง state.json และสลับ bot ไปยังเซสชันนั้น", + "switch.native_import_note": "การเลือกเซสชัน Native CLI จะนำเข้าไปยัง state.json และสลับ บอท ไปยังเซสชันนั้น", "switch.pages_label": "หน้า: /switch page 1 ... /switch page {total_pages}", "switch.no_sessions_found": "ไม่พบเซสชัน", "switch.invalid_page_number": "หมายเลขหน้าไม่ถูกต้อง\nใช้: /switch page ", @@ -90,7 +92,7 @@ "switch.switched_to_session": "สลับไปยังเซสชันแล้ว", "switch.project_label": "โปรเจ็กต์", "switch.provider_label": "ผู้ให้บริการ", - "switch.branch_label": "Branch", + "switch.branch_label": "สาขา", "switch.source_label": "แหล่งที่มา", "switch.imported_into_state_json": "นำเข้าไปยัง state.json แล้ว", "switch.status_active": "กำลังใช้งาน", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "หากต้องการเปลี่ยนภาษาในภายหลัง ให้แก้ค่า APP_LOCALE ใน {env_path}", "queue.button_cancel": "ยกเลิก", "queue.cancelled": "ยกเลิกคำถามที่อยู่ในคิวแล้ว", - "project.branch_switch_requires_source": "ก่อนสลับไปยัง branch {new_branch} ต้องเลือก source ก่อน", - "project.branch_create_from_source": "ก่อนสร้าง branch ใหม่ {new_branch} ให้เลือก source branch ก่อน", - "project.branch_source_missing": "ไม่พบ source ของ branch สำหรับโปรเจ็กต์ '{project_folder}'\nไม่มี local/{source_branch} และ origin/{source_branch}", + "project.branch_switch_requires_source": "ก่อนสลับไปยัง สาขา {new_branch} ต้องเลือก แหล่งที่มา ก่อน", + "project.branch_create_from_source": "ก่อนสร้าง สาขา ใหม่ {new_branch} ให้เลือก แหล่งที่มา สาขา ก่อน", + "project.branch_source_missing": "ไม่พบ แหล่งที่มา ของ สาขา สำหรับโปรเจ็กต์ '{project_folder}'\nไม่มี local/{source_branch} และ origin/{source_branch}", "project.project_label": "โปรเจ็กต์: {project_folder}", - "project.branch_target_label": "branch เป้าหมาย: {new_branch}", - "project.choose_branch_source": "เลือก source ของ branch:", - "project.current_branch_in_repo_label": "branch ปัจจุบันใน repo: {branch_name}", - "project.default_branch_label": "branch เริ่มต้น: {branch_name}", + "project.branch_target_label": "สาขา เป้าหมาย: {new_branch}", + "project.choose_branch_source": "เลือก แหล่งที่มา ของ สาขา:", + "project.current_branch_in_repo_label": "สาขา ปัจจุบันใน repo: {branch_name}", + "project.default_branch_label": "สาขา เริ่มต้น: {branch_name}", "project.refresh_warnings": "คำเตือนระหว่างรีเฟรช:", - "project.local_branches": "local branch:", + "project.local_branches": "local สาขา:", "project.annotation_default": "ค่าเริ่มต้น", - "project.annotation_current_branch_in_repo": "branch ปัจจุบันใน repo", + "project.annotation_current_branch_in_repo": "สาขา ปัจจุบันใน repo", "project.none": "(ไม่มี)", - "project.select_branch_with": "เลือก branch ด้วยคำสั่ง:", + "project.select_branch_with": "เลือก สาขา ด้วยคำสั่ง:", "project.usage_project": "วิธีใช้: /project \nตัวอย่าง: /project backend", "project.invalid_project_folder": "โฟลเดอร์โปรเจ็กต์ไม่ถูกต้อง อนุญาตเฉพาะชื่อโฟลเดอร์เท่านั้น", "project.path_not_directory": "พาธโปรเจ็กต์มีอยู่แล้วแต่ไม่ใช่โฟลเดอร์: {folder}", - "project.active_session_mismatch_title": "session ที่ใช้งานอยู่ไม่ตรงกับโปรเจ็กต์", - "project.current_session_html": "session ปัจจุบัน: {session_name}", - "project.session_project_html": "โปรเจ็กต์ของ session: {project_folder}", - "project.start_new_session_for_project_html": "เริ่ม session ใหม่ด้วย /new หากต้องการทำงานในโปรเจ็กต์ที่เพิ่งเลือกนี้", + "project.active_session_mismatch_title": "เซสชัน ที่ใช้งานอยู่ไม่ตรงกับโปรเจ็กต์", + "project.current_session_html": "เซสชัน ปัจจุบัน: {session_name}", + "project.session_project_html": "โปรเจ็กต์ของ เซสชัน: {project_folder}", + "project.start_new_session_for_project_html": "เริ่ม เซสชัน ใหม่ด้วย /new หากต้องการทำงานในโปรเจ็กต์ที่เพิ่งเลือกนี้", "project.project_changed_to": "เปลี่ยนโปรเจ็กต์เป็น: {folder}", - "project.branch_selection_required": "ต้องเลือก branch ก่อนสร้างหรือทำงานต่อใน session ของโปรเจ็กต์นี้", - "project.active_session_label": "session ที่ใช้งานอยู่: {session_name}", - "project.session_project_label": "โปรเจ็กต์ของ session: {project_folder}", + "project.branch_selection_required": "ต้องเลือก สาขา ก่อนสร้างหรือทำงานต่อใน เซสชัน ของโปรเจ็กต์นี้", + "project.active_session_label": "เซสชัน ที่ใช้งานอยู่: {session_name}", + "project.session_project_label": "โปรเจ็กต์ของ เซสชัน: {project_folder}", "project.project_set_title": "ตั้งค่าโปรเจ็กต์แล้ว", "project.project_html": "โปรเจ็กต์: {project_folder}", - "project.current_branch_html": "branch ปัจจุบัน: {branch_name}", - "project.branch_usage_html": "ใช้ /branch <new_branch> หรือ /branch <origin_branch> <new_branch> หากต้องการ work branch แยก", - "project.default_branch_behavior_html": "ถ้าไม่ได้ระบุ <origin_branch> bot จะใช้ default branch: {branch_name}", - "project.current_branch_behavior_html": "ถ้าไม่ตั้งค่า bot จะทำงานบน branch ปัจจุบัน: {branch_name}", + "project.current_branch_html": "สาขา ปัจจุบัน: {branch_name}", + "project.branch_usage_html": "ใช้ /branch <new_branch> หรือ /branch <origin_branch> <new_branch> หากต้องการ work สาขา แยก", + "project.default_branch_behavior_html": "ถ้าไม่ได้ระบุ <origin_branch> บอท จะใช้ default สาขา: {branch_name}", + "project.current_branch_behavior_html": "ถ้าไม่ตั้งค่า บอท จะทำงานบน สาขา ปัจจุบัน: {branch_name}", "project.trust_prompt_html": "คุณเชื่อถือโปรเจ็กต์นี้หรือไม่?\nโปรเจ็กต์: {project_folder}", "project.invalid_trust_decision": "การตัดสินใจความเชื่อถือไม่ถูกต้อง", "project.project_folder_missing_only": "ไม่พบโฟลเดอร์โปรเจ็กต์: {project_folder}", @@ -137,41 +139,41 @@ "project.already_trusted": "โปรเจ็กต์นี้ถูกเชื่อถืออยู่แล้ว: {folder}", "project.trusted": "เชื่อถือโปรเจ็กต์แล้ว: {folder}", "project.usage_branch": "วิธีใช้: /branch \nหรือ: /branch ", - "project.default_branch_unknown": "ไม่สามารถระบุ default branch ของ repository นี้ได้", + "project.default_branch_unknown": "ไม่สามารถระบุ default สาขา ของ repository นี้ได้", "project.project_folder_missing_retry": "ไม่พบโฟลเดอร์โปรเจ็กต์: {project_folder}\nให้รัน /project {project_folder} อีกครั้ง", "branch_resolution.use_branch": "ใช้ {branch_name}", - "branch_resolution.create_from_instead_of_origin": "ต้องการสร้าง branch {new_branch} จากหนึ่งใน branch เหล่านี้แทน origin/{source_branch} หรือไม่?", - "branch_resolution.discrepancy_detected": "พบ branch ไม่ตรงกันก่อนรัน session ที่ใช้งานอยู่", + "branch_resolution.create_from_instead_of_origin": "ต้องการสร้าง สาขา {new_branch} จากหนึ่งใน สาขา เหล่านี้แทน origin/{source_branch} หรือไม่?", + "branch_resolution.discrepancy_detected": "พบ สาขา ไม่ตรงกันก่อนรัน เซสชัน ที่ใช้งานอยู่", "branch_resolution.session_label": "เซสชัน: {session_name}", - "branch_resolution.stored_branch_label": "branch ที่บันทึกไว้: {branch_name}", - "branch_resolution.choose_branch_to_use": "เลือกว่าจะใช้ branch ไหน", - "branch_resolution.no_pending_decision": "ไม่พบการตัดสินใจเรื่อง branch ที่ค้างอยู่", - "branch_resolution.no_pending_discrepancy": "ไม่พบปัญหา branch ไม่ตรงกันที่ค้างอยู่", - "branch_resolution.no_active_session": "ไม่มี session ที่ใช้งานอยู่", - "branch_resolution.using_current_branch": "ใช้ branch ปัจจุบัน: {branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "branch ที่บันทึกไว้ไม่สามารถใช้ได้แล้ว: {branch_name}\nและไม่มี source branch สำรอง", - "branch_resolution.stored_branch_unavailable": "branch ที่บันทึกไว้ไม่สามารถใช้ได้แล้ว", + "branch_resolution.stored_branch_label": "สาขา ที่บันทึกไว้: {branch_name}", + "branch_resolution.choose_branch_to_use": "เลือกว่าจะใช้ สาขา ไหน", + "branch_resolution.no_pending_decision": "ไม่พบการตัดสินใจเรื่อง สาขา ที่ค้างอยู่", + "branch_resolution.no_pending_discrepancy": "ไม่พบปัญหา สาขา ไม่ตรงกันที่ค้างอยู่", + "branch_resolution.no_active_session": "ไม่มี เซสชัน ที่ใช้งานอยู่", + "branch_resolution.using_current_branch": "ใช้ สาขา ปัจจุบัน: {branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "สาขา ที่บันทึกไว้ไม่สามารถใช้ได้แล้ว: {branch_name}\nและไม่มี แหล่งที่มา สาขา สำรอง", + "branch_resolution.stored_branch_unavailable": "สาขา ที่บันทึกไว้ไม่สามารถใช้ได้แล้ว", "branch_resolution.missing_local_and_origin": "ไม่มี local/{branch_name} และ origin/{branch_name}", - "branch_resolution.create_branch_from_choices": "ต้องการสร้าง branch {branch_name} จากหนึ่งใน branch เหล่านี้หรือไม่?", - "branch_resolution.choose_restore_method": "เลือกวิธีกู้คืน branch ที่บันทึกไว้", - "lifecycle.provider_selection_required": "ต้องเลือก provider ก่อนจึงจะสร้างหรือทำงานต่อใน session ได้", + "branch_resolution.create_branch_from_choices": "ต้องการสร้าง สาขา {branch_name} จากหนึ่งใน สาขา เหล่านี้หรือไม่?", + "branch_resolution.choose_restore_method": "เลือกวิธีกู้คืน สาขา ที่บันทึกไว้", + "lifecycle.provider_selection_required": "ต้องเลือก ผู้ให้บริการ ก่อนจึงจะสร้างหรือทำงานต่อใน เซสชัน ได้", "lifecycle.no_project_selected_example": "ยังไม่ได้เลือกโปรเจ็กต์\nกรุณารัน /project ก่อน\nตัวอย่าง: /project backend", - "lifecycle.session_name_exists": "มีชื่อ session นี้อยู่แล้ว: {session_name}\nกรุณาใช้ชื่อ session อื่น", - "lifecycle.creating_session": "กำลังสร้าง session...", - "lifecycle.failed_create_session": "สร้าง session ไม่สำเร็จ", - "lifecycle.session_created_successfully": "สร้าง session สำเร็จ: {session_name}\nSession ID: {session_id}\nโปรเจ็กต์: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "provider.cli_not_found": "ไม่พบ {provider_label} CLI: {bin_name}\nใช้ /provider เพื่อเลือก provider ที่พร้อมใช้งาน หรืออัปเดตการตั้งค่า bot", + "lifecycle.session_name_exists": "มีชื่อ เซสชัน นี้อยู่แล้ว: {session_name}\nกรุณาใช้ชื่อ เซสชัน อื่น", + "lifecycle.creating_session": "กำลังสร้าง เซสชัน...", + "lifecycle.failed_create_session": "สร้าง เซสชัน ไม่สำเร็จ", + "lifecycle.session_created_successfully": "สร้าง เซสชัน สำเร็จ: {session_name}\nเซสชัน ID: {session_id}\nโปรเจ็กต์: {project_folder}\nผู้ให้บริการ: {provider}\nสาขา: {branch_name}", + "provider.cli_not_found": "ไม่พบ {provider_label} CLI: {bin_name}\nใช้ /provider เพื่อเลือก ผู้ให้บริการ ที่พร้อมใช้งาน หรืออัปเดตการตั้งค่า บอท", "provider.status_available": "พร้อมใช้งาน", "provider.status_missing": "ไม่พบ", "provider.status_current": "ปัจจุบัน", "provider.usage_provider": "วิธีใช้: /provider", - "provider.current_provider_prompt": "provider ปัจจุบัน: {provider}\nเลือก provider สำหรับ session ใหม่", + "provider.current_provider_prompt": "ผู้ให้บริการ ปัจจุบัน: {provider}\nเลือก ผู้ให้บริการ สำหรับ เซสชัน ใหม่", "provider.not_selected": "(ยังไม่ได้เลือก)", - "provider.cli_not_found_install_first": "ไม่พบ {provider_label} CLI: {bin_name}\nอัปเดตการตั้งค่า bot หรือติดตั้ง CLI นี้ก่อน", - "provider.current_provider_set": "ตั้ง provider ปัจจุบันเป็น: {provider}", - "status.current_session_details": "session ปัจจุบัน: {session_name}\nSession ID: {session_id}\nโปรเจ็กต์: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "status.current_branch_placeholder": "(branch ปัจจุบัน)", - "git.commit_disabled": "/commit ถูกปิดใช้งานอยู่\nให้ตั้งค่า ENABLE_COMMIT_COMMAND=true ใน environment ของ bot เพื่อเปิดใช้งาน", + "provider.cli_not_found_install_first": "ไม่พบ {provider_label} CLI: {bin_name}\nอัปเดตการตั้งค่า บอท หรือติดตั้ง CLI นี้ก่อน", + "provider.current_provider_set": "ตั้ง ผู้ให้บริการ ปัจจุบันเป็น: {provider}", + "status.current_session_details": "เซสชัน ปัจจุบัน: {session_name}\nเซสชัน ID: {session_id}\nโปรเจ็กต์: {project_folder}\nผู้ให้บริการ: {provider}\nสาขา: {branch_name}", + "status.current_branch_placeholder": "(สาขา ปัจจุบัน)", + "git.commit_disabled": "/commit ถูกปิดใช้งานอยู่\nให้ตั้งค่า ENABLE_COMMIT_COMMAND=true ใน environment ของ บอท เพื่อเปิดใช้งาน", "git.usage_commit": "วิธีใช้: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "ไม่พบคำสั่ง git commit ที่ถูกต้อง", "git.commit_generate_prompt": "ต้องการสร้างคำสั่ง git commit สำหรับไฟล์ที่เปลี่ยนอยู่ตอนนี้หรือไม่?", @@ -202,9 +204,9 @@ "cli.created_env_if_missing": "ได้สร้าง {env_path} แล้วหากไฟล์นี้ยังไม่มีอยู่", "cli.update_fields_in_env": "อัปเดตค่าต่อไปนี้ใน {env_path}:", "cli.then_run": "จากนั้นรัน: coding-agent-telegram", - "bot.command.compact": "ย่อ session ที่กำลังใช้งาน", + "bot.command.compact": "ย่อ เซสชัน ที่กำลังใช้งาน", "status.usage_compact": "วิธีใช้: /compact", - "runtime.compacting_session": "กำลังย่อ session ที่กำลังใช้งาน...", - "runtime.compact_summary_missing": "provider ไม่ได้ส่ง compact handoff summary ที่ใช้งานได้กลับมา", - "runtime.session_compacted": "ย่อ session สำเร็จแล้ว และสลับไปที่ {session_name} ({session_id})" + "runtime.compacting_session": "กำลังย่อ เซสชัน ที่กำลังใช้งาน...", + "runtime.compact_summary_missing": "ผู้ให้บริการ ไม่ได้ส่ง compact handoff summary ที่ใช้งานได้กลับมา", + "runtime.session_compacted": "ย่อ เซสชัน สำเร็จแล้ว และสลับไปที่ {session_name} ({session_id})" } diff --git a/src/coding_agent_telegram/resources/locales/vi.json b/src/coding_agent_telegram/resources/locales/vi.json index c1b45b0..697dc02 100644 --- a/src/coding_agent_telegram/resources/locales/vi.json +++ b/src/coding_agent_telegram/resources/locales/vi.json @@ -1,14 +1,14 @@ { "bot.command.abort": "Hủy lần chạy tác nhân hiện tại", - "bot.command.branch": "Tạo và chuyển sang Git branch", + "bot.command.branch": "Tạo và chuyển sang Git nhánh", "bot.command.commit": "Chạy các lệnh Git commit đã được kiểm tra", "bot.command.current": "Hiển thị phiên đang hoạt động", "bot.command.diff": "Hiển thị tên file đã thay đổi so với HEAD", "bot.command.new": "Tạo phiên mới", - "bot.command.pull": "Pull branch của phiên hiện tại", + "bot.command.pull": "Pull nhánh của phiên hiện tại", "bot.command.project": "Đặt thư mục dự án hiện tại", "bot.command.provider": "Chọn nhà cung cấp cho phiên mới", - "bot.command.push": "Push branch của phiên hiện tại", + "bot.command.push": "Push nhánh của phiên hiện tại", "bot.command.switch": "Liệt kê hoặc chuyển phiên", "bot.error.command_failed": "⚠️ Lệnh thất bại. Hãy kiểm tra log của máy chủ.", "bot.error.session_store": "⚠️ {error}", @@ -18,27 +18,28 @@ "common.no_project_selected": "Chưa chọn dự án.\nVui lòng chạy /project trước.", "common.project_busy": "Hiện có một tác nhân đang chạy trên dự án '{project_folder}'.\nChỉ /current và /abort được hỗ trợ cho đến khi hoàn tất.", "common.project_folder_missing": "⚠️ Thư mục dự án của phiên này không còn tồn tại: {project_folder}", - "git.branch_unknown": "⚠️ Không thể xác định branch của phiên hiện tại.", + "common.button_expired": "⚠️ Nút này đã hết hạn. Vui lòng chạy lại lệnh.", + "git.branch_unknown": "⚠️ Không thể xác định nhánh của phiên hiện tại.", "git.cancel_button": "Hủy", "git.pull_cancelled": "Đã hủy pull.", "git.pull_completed": "Đã pull xong.", "git.pull_confirm_button": "Xác nhận pull", - "git.pull_confirm_prompt": "Pull branch `{branch_name}` từ `origin`?", - "git.pull_confirm_prompt_with_default": "Pull branch mặc định `{default_branch}` và branch `{branch_name}` từ `origin`?", - "git.pull_in_progress": "Đang pull branch `{branch_name}` từ `origin`...", - "git.pull_in_progress_with_default": "Đang pull branch mặc định `{default_branch}` và branch `{branch_name}` từ `origin`...", + "git.pull_confirm_prompt": "Pull nhánh `{branch_name}` từ `origin`?", + "git.pull_confirm_prompt_with_default": "Pull nhánh mặc định `{default_branch}` và nhánh `{branch_name}` từ `origin`?", + "git.pull_in_progress": "Đang pull nhánh `{branch_name}` từ `origin`...", + "git.pull_in_progress_with_default": "Đang pull nhánh mặc định `{default_branch}` và nhánh `{branch_name}` từ `origin`...", "git.push_cancelled": "Đã hủy push.", "git.push_cancelled_checkout_failed": "Đã hủy push. Không thể chuyển sang `{branch_name}` trước.", "git.push_confirm_button": "Xác nhận push", - "git.push_confirm_prompt": "Push branch `{branch_name}` lên `origin`?", - "git.push_in_progress": "Đang push branch `{branch_name}` lên `origin`...", + "git.push_confirm_prompt": "Push nhánh `{branch_name}` lên `origin`?", + "git.push_in_progress": "Đang push nhánh `{branch_name}` lên `origin`...", "git.usage_diff": "Cách dùng: /diff", "git.usage_pull": "Cách dùng: /pull", "git.usage_push": "Cách dùng: /push", "message.photo_only_codex": "Hiện tại tệp đính kèm ảnh chỉ được hỗ trợ cho các phiên Codex.", "message.question_queued": "Câu hỏi đã được xếp hàng dưới dạng Q{question_number}. Nó sẽ được xử lý sau khi tác vụ hiện tại của tác nhân hoàn tất.", "message.voice_speech_to_text_disabled": "Tin nhắn thoại chưa được bật.\nHãy đặt ENABLE_OPENAI_WHISPER_SPEECH_TO_TEXT=true và cài đặt trước các điều kiện cần cục bộ của Whisper.", - "message.unsupported_message_type": "Loại tin nhắn không được hỗ trợ.\nBot này hiện chấp nhận tin nhắn văn bản, ảnh, tin nhắn thoại và tệp âm thanh.", + "message.unsupported_message_type": "Loại tin nhắn không được hỗ trợ.\nbot này hiện chấp nhận tin nhắn văn bản, ảnh, tin nhắn thoại và tệp âm thanh.", "queue.button_group": "Gộp các câu hỏi", "queue.button_no": "Không", "queue.button_single": "Xử lý từng câu một", @@ -60,6 +61,7 @@ "runtime.agent_run_failed": "Lần chạy của tác nhân đã thất bại.", "runtime.live_agent_output": "Đầu ra trực tiếp của tác nhân", "runtime.photo_too_large": "Ảnh quá lớn. Kích thước tối đa được hỗ trợ là 5 MB.", + "runtime.voice_audio_too_large": "Tệp âm thanh quá lớn cho chuyển giọng nói thành văn bản cục bộ. Kích thước tối đa được hỗ trợ là {max_size_mb} MB.", "runtime.provider_output_index": "Đầu ra {provider} {index}/{total}", "runtime.provider_output_single": "Đầu ra {provider}", "runtime.replacement_session_stall": "Việc tạo phiên thay thế có vẻ bị treo.\nTiến trình tác nhân cục bộ vẫn đang chạy nhưng chưa tạo ra đầu ra nào.\nTrên macOS, điều này có thể do một hộp thoại cấp quyền ẩn đang chờ xác nhận trên máy đang chạy bot.", @@ -78,8 +80,8 @@ "switch.available_sessions": "Các phiên khả dụng (trang {page}/{total_pages}):", "switch.source_bot_managed": "Phiên do bot quản lý", "switch.source_native_cli": "Phiên CLI gốc", - "switch.current_project_filter": "Bộ lọc project hiện tại cho các phiên native: {project_folder}", - "switch.current_project_filter_none": "Bộ lọc project hiện tại cho các phiên native: (không có)", + "switch.current_project_filter": "Bộ lọc dự án hiện tại cho các phiên native: {project_folder}", + "switch.current_project_filter_none": "Bộ lọc dự án hiện tại cho các phiên native: (không có)", "switch.initialized_label": "khởi tạo từ", "switch.use_label": "Dùng:", "switch.native_import_note": "Chọn một phiên CLI gốc sẽ nhập nó vào state.json và chuyển bot sang phiên đó.", @@ -90,7 +92,7 @@ "switch.switched_to_session": "Đã chuyển sang phiên", "switch.project_label": "Dự án", "switch.provider_label": "Nhà cung cấp", - "switch.branch_label": "Branch", + "switch.branch_label": "Nhánh", "switch.source_label": "Nguồn", "switch.imported_into_state_json": "Đã nhập vào state.json.", "switch.status_active": "đang dùng", @@ -99,78 +101,78 @@ "bootstrap.env_created_change_line": "Nếu muốn đổi sang ngôn ngữ khác sau này, hãy sửa APP_LOCALE trong {env_path}.", "queue.button_cancel": "Hủy", "queue.cancelled": "Các câu hỏi trong hàng đợi đã bị hủy.", - "project.branch_switch_requires_source": "Để chuyển sang branch {new_branch}, trước tiên hãy chọn source.", - "project.branch_create_from_source": "Để tạo branch mới {new_branch}, trước tiên hãy chọn source branch.", - "project.branch_source_missing": "Không tìm thấy source branch cho project '{project_folder}'.\nThiếu local/{source_branch} và origin/{source_branch}.", + "project.branch_switch_requires_source": "Để chuyển sang nhánh {new_branch}, trước tiên hãy chọn nguồn.", + "project.branch_create_from_source": "Để tạo nhánh mới {new_branch}, trước tiên hãy chọn nguồn nhánh.", + "project.branch_source_missing": "Không tìm thấy nguồn nhánh cho dự án '{project_folder}'.\nThiếu local/{source_branch} và origin/{source_branch}.", "project.project_label": "Dự án: {project_folder}", - "project.branch_target_label": "Branch đích: {new_branch}", - "project.choose_branch_source": "Chọn source branch:", - "project.current_branch_in_repo_label": "Branch hiện tại trong repo: {branch_name}", - "project.default_branch_label": "Branch mặc định: {branch_name}", + "project.branch_target_label": "Nhánh đích: {new_branch}", + "project.choose_branch_source": "Chọn nguồn nhánh:", + "project.current_branch_in_repo_label": "Nhánh hiện tại trong repo: {branch_name}", + "project.default_branch_label": "Nhánh mặc định: {branch_name}", "project.refresh_warnings": "Cảnh báo khi làm mới:", - "project.local_branches": "Local branch:", + "project.local_branches": "Local nhánh:", "project.annotation_default": "mặc định", - "project.annotation_current_branch_in_repo": "branch hiện tại trong repo", + "project.annotation_current_branch_in_repo": "nhánh hiện tại trong repo", "project.none": "(không có)", - "project.select_branch_with": "Chọn branch bằng lệnh:", + "project.select_branch_with": "Chọn nhánh bằng lệnh:", "project.usage_project": "Cách dùng: /project \nVí dụ: /project backend", - "project.invalid_project_folder": "Thư mục project không hợp lệ. Chỉ cho phép tên thư mục.", - "project.path_not_directory": "Đường dẫn project đã tồn tại nhưng không phải thư mục: {folder}", - "project.active_session_mismatch_title": "Session đang hoạt động không khớp với project", - "project.current_session_html": "Session hiện tại: {session_name}", - "project.session_project_html": "Project của session: {project_folder}", - "project.start_new_session_for_project_html": "Hãy bắt đầu session mới bằng /new nếu bạn muốn làm việc trong project vừa chọn này.", - "project.project_changed_to": "Đã đổi project sang: {folder}", - "project.branch_selection_required": "Bạn phải chọn branch trước khi tạo hoặc tiếp tục session trong project này.", - "project.active_session_label": "Session đang hoạt động: {session_name}", - "project.session_project_label": "Project của session: {project_folder}", - "project.project_set_title": "Đã đặt project", + "project.invalid_project_folder": "Thư mục dự án không hợp lệ. Chỉ cho phép tên thư mục.", + "project.path_not_directory": "Đường dẫn dự án đã tồn tại nhưng không phải thư mục: {folder}", + "project.active_session_mismatch_title": "Phiên đang hoạt động không khớp với dự án", + "project.current_session_html": "Phiên hiện tại: {session_name}", + "project.session_project_html": "Dự án của phiên: {project_folder}", + "project.start_new_session_for_project_html": "Hãy bắt đầu phiên mới bằng /new nếu bạn muốn làm việc trong dự án vừa chọn này.", + "project.project_changed_to": "Đã đổi dự án sang: {folder}", + "project.branch_selection_required": "Bạn phải chọn nhánh trước khi tạo hoặc tiếp tục phiên trong dự án này.", + "project.active_session_label": "Phiên đang hoạt động: {session_name}", + "project.session_project_label": "Dự án của phiên: {project_folder}", + "project.project_set_title": "Đã đặt dự án", "project.project_html": "Dự án: {project_folder}", - "project.current_branch_html": "Branch hiện tại: {branch_name}", - "project.branch_usage_html": "Dùng /branch <new_branch> hoặc /branch <origin_branch> <new_branch> nếu bạn muốn có work branch riêng.", - "project.default_branch_behavior_html": "Nếu không chỉ định <origin_branch>, bot sẽ dùng branch mặc định: {branch_name}.", - "project.current_branch_behavior_html": "Nếu bạn không đặt, bot sẽ làm việc trên branch hiện tại: {branch_name}", - "project.trust_prompt_html": "Bạn có tin cậy project này không?\nProject: {project_folder}", + "project.current_branch_html": "Nhánh hiện tại: {branch_name}", + "project.branch_usage_html": "Dùng /branch <new_branch> hoặc /branch <origin_branch> <new_branch> nếu bạn muốn có work nhánh riêng.", + "project.default_branch_behavior_html": "Nếu không chỉ định <origin_branch>, bot sẽ dùng nhánh mặc định: {branch_name}.", + "project.current_branch_behavior_html": "Nếu bạn không đặt, bot sẽ làm việc trên nhánh hiện tại: {branch_name}", + "project.trust_prompt_html": "Bạn có tin cậy dự án này không?\nDự án: {project_folder}", "project.invalid_trust_decision": "Lựa chọn tin cậy không hợp lệ.", - "project.project_folder_missing_only": "Thư mục project không tồn tại: {project_folder}", - "project.left_untrusted": "Project vẫn ở trạng thái không tin cậy: {folder}", - "project.already_trusted": "Project đã được tin cậy: {folder}", - "project.trusted": "Đã tin cậy project: {folder}", + "project.project_folder_missing_only": "Thư mục dự án không tồn tại: {project_folder}", + "project.left_untrusted": "Dự án vẫn ở trạng thái không tin cậy: {folder}", + "project.already_trusted": "Dự án đã được tin cậy: {folder}", + "project.trusted": "Đã tin cậy dự án: {folder}", "project.usage_branch": "Cách dùng: /branch \nHoặc: /branch ", - "project.default_branch_unknown": "Không thể xác định branch mặc định cho repository này.", - "project.project_folder_missing_retry": "Thư mục project không tồn tại: {project_folder}\nHãy chạy lại /project {project_folder}.", + "project.default_branch_unknown": "Không thể xác định nhánh mặc định cho repository này.", + "project.project_folder_missing_retry": "Thư mục dự án không tồn tại: {project_folder}\nHãy chạy lại /project {project_folder}.", "branch_resolution.use_branch": "dùng {branch_name}", - "branch_resolution.create_from_instead_of_origin": "Bạn có muốn tạo branch {new_branch} từ một trong các branch này thay vì origin/{source_branch} không?", - "branch_resolution.discrepancy_detected": "Phát hiện branch không khớp trước khi chạy session đang hoạt động.", + "branch_resolution.create_from_instead_of_origin": "Bạn có muốn tạo nhánh {new_branch} từ một trong các nhánh này thay vì origin/{source_branch} không?", + "branch_resolution.discrepancy_detected": "Phát hiện nhánh không khớp trước khi chạy phiên đang hoạt động.", "branch_resolution.session_label": "Phiên: {session_name}", - "branch_resolution.stored_branch_label": "Branch đã lưu: {branch_name}", - "branch_resolution.choose_branch_to_use": "Chọn branch sẽ dùng.", - "branch_resolution.no_pending_decision": "Không tìm thấy quyết định branch đang chờ.", - "branch_resolution.no_pending_discrepancy": "Không tìm thấy trạng thái branch không khớp đang chờ.", - "branch_resolution.no_active_session": "Không có session đang hoạt động.", - "branch_resolution.using_current_branch": "Dùng branch hiện tại: {branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "Branch đã lưu không còn khả dụng: {branch_name}\nKhông có source branch thay thế nào.", - "branch_resolution.stored_branch_unavailable": "Branch đã lưu không còn khả dụng.", + "branch_resolution.stored_branch_label": "Nhánh đã lưu: {branch_name}", + "branch_resolution.choose_branch_to_use": "Chọn nhánh sẽ dùng.", + "branch_resolution.no_pending_decision": "Không tìm thấy quyết định nhánh đang chờ.", + "branch_resolution.no_pending_discrepancy": "Không tìm thấy trạng thái nhánh không khớp đang chờ.", + "branch_resolution.no_active_session": "Không có phiên đang hoạt động.", + "branch_resolution.using_current_branch": "Dùng nhánh hiện tại: {branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "Nhánh đã lưu không còn khả dụng: {branch_name}\nKhông có nguồn nhánh thay thế nào.", + "branch_resolution.stored_branch_unavailable": "Nhánh đã lưu không còn khả dụng.", "branch_resolution.missing_local_and_origin": "Thiếu local/{branch_name} và origin/{branch_name}.", - "branch_resolution.create_branch_from_choices": "Bạn có muốn tạo branch {branch_name} từ một trong các branch này không?", - "branch_resolution.choose_restore_method": "Chọn cách khôi phục branch đã lưu.", - "lifecycle.provider_selection_required": "Bạn phải chọn provider trước khi tạo hoặc tiếp tục session.", - "lifecycle.no_project_selected_example": "Chưa chọn project.\nVui lòng chạy /project trước.\nVí dụ: /project backend", - "lifecycle.session_name_exists": "Tên session đã tồn tại: {session_name}\nVui lòng dùng tên session khác.", - "lifecycle.creating_session": "Đang tạo session...", - "lifecycle.failed_create_session": "Tạo session thất bại.", - "lifecycle.session_created_successfully": "Đã tạo session thành công: {session_name}\nSession ID: {session_id}\nProject: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "provider.cli_not_found": "Không tìm thấy CLI {provider_label}: {bin_name}\nChạy /provider để chọn provider khả dụng hoặc cập nhật cấu hình bot.", + "branch_resolution.create_branch_from_choices": "Bạn có muốn tạo nhánh {branch_name} từ một trong các nhánh này không?", + "branch_resolution.choose_restore_method": "Chọn cách khôi phục nhánh đã lưu.", + "lifecycle.provider_selection_required": "Bạn phải chọn nhà cung cấp trước khi tạo hoặc tiếp tục phiên.", + "lifecycle.no_project_selected_example": "Chưa chọn dự án.\nVui lòng chạy /project trước.\nVí dụ: /project backend", + "lifecycle.session_name_exists": "Tên phiên đã tồn tại: {session_name}\nVui lòng dùng tên phiên khác.", + "lifecycle.creating_session": "Đang tạo phiên...", + "lifecycle.failed_create_session": "Tạo phiên thất bại.", + "lifecycle.session_created_successfully": "Đã tạo phiên thành công: {session_name}\nPhiên ID: {session_id}\nDự án: {project_folder}\nNhà cung cấp: {provider}\nNhánh: {branch_name}", + "provider.cli_not_found": "Không tìm thấy CLI {provider_label}: {bin_name}\nChạy /provider để chọn nhà cung cấp khả dụng hoặc cập nhật cấu hình bot.", "provider.status_available": "khả dụng", "provider.status_missing": "thiếu", "provider.status_current": "hiện tại", "provider.usage_provider": "Cách dùng: /provider", - "provider.current_provider_prompt": "Provider hiện tại: {provider}\nChọn provider cho các session mới.", + "provider.current_provider_prompt": "Nhà cung cấp hiện tại: {provider}\nChọn nhà cung cấp cho các phiên mới.", "provider.not_selected": "(chưa chọn)", "provider.cli_not_found_install_first": "Không tìm thấy CLI {provider_label}: {bin_name}\nCập nhật cấu hình bot hoặc cài CLI này trước.", - "provider.current_provider_set": "Đã đặt provider hiện tại thành: {provider}", - "status.current_session_details": "Session hiện tại: {session_name}\nSession ID: {session_id}\nProject: {project_folder}\nProvider: {provider}\nBranch: {branch_name}", - "status.current_branch_placeholder": "(branch hiện tại)", + "provider.current_provider_set": "Đã đặt nhà cung cấp hiện tại thành: {provider}", + "status.current_session_details": "Phiên hiện tại: {session_name}\nPhiên ID: {session_id}\nDự án: {project_folder}\nNhà cung cấp: {provider}\nNhánh: {branch_name}", + "status.current_branch_placeholder": "(nhánh hiện tại)", "git.commit_disabled": "/commit hiện đang bị tắt.\nHãy đặt ENABLE_COMMIT_COMMAND=true trong môi trường của bot để bật nó.", "git.usage_commit": "Cách dùng: /commit git add ... && git commit ...", "git.no_valid_commit_commands": "Không tìm thấy lệnh git commit hợp lệ.", @@ -184,8 +186,8 @@ "git.commit_execute_button": "Thực thi commit", "git.commit_execute_confirmed": "Đang thực thi lệnh commit đã tạo...", "git.commit_execute_context_changed": "Phiên hoặc dự án đang hoạt động đã thay đổi. Hãy tạo lại lệnh commit.", - "git.project_not_trusted_for_mutation": "Project này chưa được tin cậy cho các thao tác Git có thay đổi dữ liệu. Hãy dùng project được tạo bằng /project hoặc đánh dấu tin cậy trước.", - "git.unsafe_path_arguments": "Không cho phép đối số đường dẫn không an toàn. Chỉ được dùng các tệp bên trong project hiện tại.", + "git.project_not_trusted_for_mutation": "Dự án này chưa được tin cậy cho các thao tác Git có thay đổi dữ liệu. Hãy dùng dự án được tạo bằng /project hoặc đánh dấu tin cậy trước.", + "git.unsafe_path_arguments": "Không cho phép đối số đường dẫn không an toàn. Chỉ được dùng các tệp bên trong dự án hiện tại.", "diff.task_completed": "Tác vụ đã hoàn tất.", "diff.session_label": "Phiên: {session_name}", "diff.project_label": "Dự án: {project_folder}", @@ -202,9 +204,9 @@ "cli.created_env_if_missing": "Đã tạo {env_path} nếu tệp này chưa tồn tại.", "cli.update_fields_in_env": "Hãy cập nhật các trường sau trong {env_path}:", "cli.then_run": "Sau đó chạy: coding-agent-telegram", - "bot.command.compact": "Thu gọn session đang hoạt động", + "bot.command.compact": "Thu gọn phiên đang hoạt động", "status.usage_compact": "Cách dùng: /compact", - "runtime.compacting_session": "Đang thu gọn session đang hoạt động...", - "runtime.compact_summary_missing": "Provider không trả về bản tóm tắt bàn giao dạng rút gọn có thể sử dụng được.", - "runtime.session_compacted": "Đã thu gọn session thành công. Đã chuyển sang {session_name} ({session_id})." + "runtime.compacting_session": "Đang thu gọn phiên đang hoạt động...", + "runtime.compact_summary_missing": "Nhà cung cấp không trả về bản tóm tắt bàn giao dạng rút gọn có thể sử dụng được.", + "runtime.session_compacted": "Đã thu gọn phiên thành công. Đã chuyển sang {session_name} ({session_id})." } diff --git a/src/coding_agent_telegram/resources/locales/zh-CN.json b/src/coding_agent_telegram/resources/locales/zh-CN.json index f470cf2..cffafbc 100644 --- a/src/coding_agent_telegram/resources/locales/zh-CN.json +++ b/src/coding_agent_telegram/resources/locales/zh-CN.json @@ -1,14 +1,14 @@ { "bot.command.abort": "中止当前代理运行", - "bot.command.branch": "创建并切换到 Git branch", + "bot.command.branch": "创建并切换到 Git 分支", "bot.command.commit": "执行已校验的 Git commit 命令", "bot.command.current": "显示当前活动会话", "bot.command.diff": "显示相对 HEAD 已变更的文件名", "bot.command.new": "创建新会话", - "bot.command.pull": "pull 当前会话 branch", + "bot.command.pull": "拉取当前会话分支", "bot.command.project": "设置当前项目目录", "bot.command.provider": "为新会话选择提供方", - "bot.command.push": "push 当前会话 branch", + "bot.command.push": "推送当前会话分支", "bot.command.switch": "列出会话或切换会话", "bot.error.command_failed": "⚠️ 命令失败。请检查服务器日志。", "bot.error.session_store": "⚠️ {error}", @@ -18,27 +18,28 @@ "common.no_project_selected": "尚未选择项目。\n请先运行 /project 。", "common.project_busy": "项目 '{project_folder}' 当前有代理正在运行。\n在其完成之前,只支持 /current 和 /abort。", "common.project_folder_missing": "⚠️ 此会话对应的项目目录已不存在:{project_folder}", - "git.branch_unknown": "⚠️ 无法确定当前会话的 branch。", + "common.button_expired": "⚠️ 此按钮已过期。请重新执行命令。", + "git.branch_unknown": "⚠️ 无法确定当前会话的分支。", "git.cancel_button": "取消", - "git.pull_cancelled": "已取消 pull。", - "git.pull_completed": "已完成 pull。", - "git.pull_confirm_button": "确认 pull", - "git.pull_confirm_prompt": "要从 `origin` pull branch `{branch_name}` 吗?", - "git.pull_confirm_prompt_with_default": "要从 `origin` pull 默认 branch `{default_branch}` 和 branch `{branch_name}` 吗?", - "git.pull_in_progress": "正在从 `origin` pull branch `{branch_name}`...", - "git.pull_in_progress_with_default": "正在从 `origin` pull 默认 branch `{default_branch}` 和 branch `{branch_name}`...", - "git.push_cancelled": "已取消 push。", + "git.pull_cancelled": "已取消拉取。", + "git.pull_completed": "已完成拉取。", + "git.pull_confirm_button": "确认拉取", + "git.pull_confirm_prompt": "要从 `origin` 拉取分支 `{branch_name}` 吗?", + "git.pull_confirm_prompt_with_default": "要从 `origin` 拉取默认分支 `{default_branch}` 和分支 `{branch_name}` 吗?", + "git.pull_in_progress": "正在从 `origin` 拉取分支 `{branch_name}`...", + "git.pull_in_progress_with_default": "正在从 `origin` 拉取默认分支 `{default_branch}` 和分支 `{branch_name}`...", + "git.push_cancelled": "已取消推送。", "git.push_cancelled_checkout_failed": "已取消 push。先切换到 `{branch_name}` 失败。", - "git.push_confirm_button": "确认 push", - "git.push_confirm_prompt": "要将 branch `{branch_name}` push 到 `origin` 吗?", - "git.push_in_progress": "正在将 branch `{branch_name}` push 到 `origin`...", + "git.push_confirm_button": "确认推送", + "git.push_confirm_prompt": "要将分支 `{branch_name}` 推送到 `origin` 吗?", + "git.push_in_progress": "正在将分支 `{branch_name}` 推送到 `origin`...", "git.usage_diff": "用法:/diff", "git.usage_pull": "用法:/pull", "git.usage_push": "用法:/push", "message.photo_only_codex": "当前仅 Codex 会话支持图片附件。", "message.question_queued": "问题已加入队列,编号为 Q{question_number}。当前代理任务完成后将开始处理。", "message.voice_speech_to_text_disabled": "语音消息功能尚未启用。\n请先设置 ENABLE_OPENAI_WHISPER_SPEECH_TO_TEXT=true,并安装本地 Whisper 依赖。", - "message.unsupported_message_type": "不支持的消息类型。\n此 bot 当前接受文本消息、图片、语音消息和音频文件。", + "message.unsupported_message_type": "不支持的消息类型。\n此机器人当前接受文本消息、图片、语音消息和音频文件。", "queue.button_group": "合并问题", "queue.button_no": "否", "queue.button_single": "逐个处理", @@ -55,14 +56,15 @@ "queue.processing_grouped": "正在将队列问题作为一个批次处理。", "queue.processing_single": "正在逐个处理队列问题。", "queue.working_on_queued": "正在处理队列中的问题:", - "runtime.active_run_stall": "当前代理运行似乎已卡住。\n本地代理进程仍在运行,但没有产生输出。\n在 macOS 上,这可能是因为运行 bot 的机器上有一个隐藏的权限对话框正在等待确认。", + "runtime.active_run_stall": "当前代理运行似乎已卡住。\n本地代理进程仍在运行,但没有产生输出。\n在 macOS 上,这可能是因为运行机器人的机器上有一个隐藏的权限对话框正在等待确认。", "runtime.agent_run_aborted": "已通过 /abort 中止代理运行。", "runtime.agent_run_failed": "代理运行失败。", "runtime.live_agent_output": "代理实时输出", "runtime.photo_too_large": "图片过大。支持的最大大小为 5 MB。", + "runtime.voice_audio_too_large": "音频过大,无法使用本地语音转文字。支持的最大大小为 {max_size_mb} MB。", "runtime.provider_output_index": "{provider} 输出 {index}/{total}", "runtime.provider_output_single": "{provider} 输出", - "runtime.replacement_session_stall": "替代会话创建似乎已卡住。\n本地代理进程仍在运行,但没有产生输出。\n在 macOS 上,这可能是因为运行 bot 的机器上有一个隐藏的权限对话框正在等待确认。", + "runtime.replacement_session_stall": "替代会话创建似乎已卡住。\n本地代理进程仍在运行,但没有产生输出。\n在 macOS 上,这可能是因为运行机器人的机器上有一个隐藏的权限对话框正在等待确认。", "runtime.resume_created_new": "恢复失败,因此已创建一个新会话。\n新的会话 ID:{session_id}\n新的会话名称:{session_name}", "runtime.resume_id_changed": "恢复成功,但会话 ID 已更改。\n新的会话 ID:{session_id}\n新的会话名称:{session_name}", "runtime.sensitive_diff_omitted": "{path}\n此文件包含敏感内容,已省略。", @@ -76,13 +78,13 @@ "status.no_running_agent": "当前项目未找到正在运行的代理进程。", "status.usage_abort": "用法:/abort", "switch.available_sessions": "可用会话(第 {page}/{total_pages} 页):", - "switch.source_bot_managed": "Bot 管理的会话", + "switch.source_bot_managed": "机器人管理的会话", "switch.source_native_cli": "原生 CLI 会话", "switch.current_project_filter": "原生会话的当前项目筛选:{project_folder}", "switch.current_project_filter_none": "原生会话的当前项目筛选:(无)", "switch.initialized_label": "初始化来源", "switch.use_label": "使用:", - "switch.native_import_note": "选择原生 CLI 会话会将其导入 state.json,并让 bot 切换到该会话。", + "switch.native_import_note": "选择原生 CLI 会话会将其导入 state.json,并让机器人切换到该会话。", "switch.pages_label": "页码:/switch page 1 ... /switch page {total_pages}", "switch.no_sessions_found": "未找到会话。", "switch.invalid_page_number": "页码无效。\n用法:/switch page ", @@ -90,7 +92,7 @@ "switch.switched_to_session": "已切换到会话", "switch.project_label": "项目", "switch.provider_label": "提供方", - "switch.branch_label": "Branch", + "switch.branch_label": "分支", "switch.source_label": "来源", "switch.imported_into_state_json": "已导入 state.json。", "switch.status_active": "使用中", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "如果之后想切换语言,请修改 {env_path} 中的 APP_LOCALE。", "queue.button_cancel": "取消", "queue.cancelled": "已取消队列中的问题。", - "project.branch_switch_requires_source": "切换到 branch {new_branch} 前,请先选择来源。", - "project.branch_create_from_source": "要创建新 branch {new_branch},请先选择来源 branch。", - "project.branch_source_missing": "在项目 '{project_folder}' 中找不到 branch 来源。\n缺少 local/{source_branch} 和 origin/{source_branch}。", + "project.branch_switch_requires_source": "切换到分支 {new_branch} 前,请先选择来源。", + "project.branch_create_from_source": "要创建新分支 {new_branch},请先选择来源分支。", + "project.branch_source_missing": "在项目 '{project_folder}' 中找不到分支来源。\n缺少 local/{source_branch} 和 origin/{source_branch}。", "project.project_label": "项目:{project_folder}", - "project.branch_target_label": "目标 branch:{new_branch}", - "project.choose_branch_source": "请选择 branch 来源:", - "project.current_branch_in_repo_label": "仓库当前 branch:{branch_name}", - "project.default_branch_label": "默认 branch:{branch_name}", + "project.branch_target_label": "目标分支:{new_branch}", + "project.choose_branch_source": "请选择分支来源:", + "project.current_branch_in_repo_label": "仓库当前分支:{branch_name}", + "project.default_branch_label": "默认分支:{branch_name}", "project.refresh_warnings": "刷新警告:", - "project.local_branches": "本地 branch:", + "project.local_branches": "本地分支:", "project.annotation_default": "默认", - "project.annotation_current_branch_in_repo": "当前 branch", + "project.annotation_current_branch_in_repo": "当前分支", "project.none": "(无)", - "project.select_branch_with": "请使用以下命令选择 branch:", + "project.select_branch_with": "请使用以下命令选择分支:", "project.usage_project": "用法:/project \n示例:/project backend", "project.invalid_project_folder": "项目文件夹无效。只允许填写文件夹名称。", "project.path_not_directory": "项目路径存在,但不是文件夹:{folder}", - "project.active_session_mismatch_title": "当前 session 与项目不一致", - "project.current_session_html": "当前 session:{session_name}", - "project.session_project_html": "Session 项目:{project_folder}", - "project.start_new_session_for_project_html": "如果你想在新选择的项目中工作,请使用 /new 创建新的 session。", + "project.active_session_mismatch_title": "当前会话与项目不一致", + "project.current_session_html": "当前会话:{session_name}", + "project.session_project_html": "会话项目:{project_folder}", + "project.start_new_session_for_project_html": "如果你想在新选择的项目中工作,请使用 /new 创建新的会话。", "project.project_changed_to": "项目已切换为:{folder}", - "project.branch_selection_required": "在此项目中创建或继续 session 前,必须先选择 branch。", - "project.active_session_label": "当前 session:{session_name}", - "project.session_project_label": "Session 项目:{project_folder}", + "project.branch_selection_required": "在此项目中创建或继续会话前,必须先选择分支。", + "project.active_session_label": "当前会话:{session_name}", + "project.session_project_label": "会话项目:{project_folder}", "project.project_set_title": "项目已设置", "project.project_html": "项目:{project_folder}", - "project.current_branch_html": "当前 branch:{branch_name}", - "project.branch_usage_html": "如果你想使用专用工作 branch,请使用 /branch <new_branch>/branch <origin_branch> <new_branch>。", - "project.default_branch_behavior_html": "如果未指定 <origin_branch>,bot 会使用默认 branch:{branch_name}。", - "project.current_branch_behavior_html": "如果你不设置,bot 会在当前 branch 上工作:{branch_name}", + "project.current_branch_html": "当前分支:{branch_name}", + "project.branch_usage_html": "如果你想使用专用工作分支,请使用 /branch <new_branch>/branch <origin_branch> <new_branch>。", + "project.default_branch_behavior_html": "如果未指定 <origin_branch>,机器人会使用默认分支:{branch_name}。", + "project.current_branch_behavior_html": "如果你不设置,机器人会在当前分支上工作:{branch_name}", "project.trust_prompt_html": "你信任这个项目吗?\n项目:{project_folder}", "project.invalid_trust_decision": "无效的信任选项。", "project.project_folder_missing_only": "项目文件夹不存在:{project_folder}", @@ -137,41 +139,41 @@ "project.already_trusted": "项目已处于受信任状态:{folder}", "project.trusted": "项目已信任:{folder}", "project.usage_branch": "用法:/branch \n或:/branch ", - "project.default_branch_unknown": "无法确定此仓库的默认 branch。", + "project.default_branch_unknown": "无法确定此仓库的默认分支。", "project.project_folder_missing_retry": "项目文件夹不存在:{project_folder}\n请重新运行 /project {project_folder}。", "branch_resolution.use_branch": "使用 {branch_name}", - "branch_resolution.create_from_instead_of_origin": "是否改为从以下 branch 之一创建 {new_branch},而不是从 origin/{source_branch}?", - "branch_resolution.discrepancy_detected": "在运行当前 session 前检测到 branch 不一致。", - "branch_resolution.session_label": "Session:{session_name}", - "branch_resolution.stored_branch_label": "保存的 branch:{branch_name}", - "branch_resolution.choose_branch_to_use": "请选择要使用的 branch。", - "branch_resolution.no_pending_decision": "未找到待处理的 branch 决定。", - "branch_resolution.no_pending_discrepancy": "未找到待处理的 branch 不一致状态。", - "branch_resolution.no_active_session": "当前没有可用的活动 session。", - "branch_resolution.using_current_branch": "使用当前 branch:{branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "保存的 branch 已不可用:{branch_name}\n也没有可用的备用来源 branch。", - "branch_resolution.stored_branch_unavailable": "保存的 branch 已不可用。", + "branch_resolution.create_from_instead_of_origin": "是否改为从以下分支之一创建 {new_branch},而不是从 origin/{source_branch}?", + "branch_resolution.discrepancy_detected": "在运行当前会话前检测到分支不一致。", + "branch_resolution.session_label": "会话:{session_name}", + "branch_resolution.stored_branch_label": "保存的分支:{branch_name}", + "branch_resolution.choose_branch_to_use": "请选择要使用的分支。", + "branch_resolution.no_pending_decision": "未找到待处理的分支决定。", + "branch_resolution.no_pending_discrepancy": "未找到待处理的分支不一致状态。", + "branch_resolution.no_active_session": "当前没有可用的活动会话。", + "branch_resolution.using_current_branch": "使用当前分支:{branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "保存的分支已不可用:{branch_name}\n也没有可用的备用来源分支。", + "branch_resolution.stored_branch_unavailable": "保存的分支已不可用。", "branch_resolution.missing_local_and_origin": "缺少 local/{branch_name} 和 origin/{branch_name}。", - "branch_resolution.create_branch_from_choices": "是否从以下 branch 之一创建 {branch_name}?", - "branch_resolution.choose_restore_method": "请选择恢复保存 branch 的方式。", - "lifecycle.provider_selection_required": "创建或继续 session 前,必须先选择 provider。", + "branch_resolution.create_branch_from_choices": "是否从以下分支之一创建 {branch_name}?", + "branch_resolution.choose_restore_method": "请选择恢复保存分支的方式。", + "lifecycle.provider_selection_required": "创建或继续会话前,必须先选择提供方。", "lifecycle.no_project_selected_example": "尚未选择项目。\n请先运行 /project 。\n示例:/project backend", - "lifecycle.session_name_exists": "Session 名称已存在:{session_name}\n请使用其他 session 名称。", - "lifecycle.creating_session": "正在创建 session...", - "lifecycle.failed_create_session": "创建 session 失败。", - "lifecycle.session_created_successfully": "Session 创建成功:{session_name}\nSession ID:{session_id}\n项目:{project_folder}\nProvider:{provider}\nBranch:{branch_name}", - "provider.cli_not_found": "未找到 {provider_label} CLI:{bin_name}\n运行 /provider 选择可用的 provider,或更新 bot 配置。", + "lifecycle.session_name_exists": "会话名称已存在:{session_name}\n请使用其他会话名称。", + "lifecycle.creating_session": "正在创建会话...", + "lifecycle.failed_create_session": "创建会话失败。", + "lifecycle.session_created_successfully": "会话创建成功:{session_name}\n会话 ID:{session_id}\n项目:{project_folder}\n提供方:{provider}\n分支:{branch_name}", + "provider.cli_not_found": "未找到 {provider_label} CLI:{bin_name}\n运行 /provider 选择可用的提供方,或更新机器人配置。", "provider.status_available": "可用", "provider.status_missing": "缺失", "provider.status_current": "当前", "provider.usage_provider": "用法:/provider", - "provider.current_provider_prompt": "当前 provider:{provider}\n请选择新 session 使用的 provider。", + "provider.current_provider_prompt": "当前提供方:{provider}\n请选择新会话使用的提供方。", "provider.not_selected": "(未选择)", - "provider.cli_not_found_install_first": "未找到 {provider_label} CLI:{bin_name}\n请更新 bot 配置,或先安装该 CLI。", - "provider.current_provider_set": "当前 provider 已设置为:{provider}", - "status.current_session_details": "当前 session:{session_name}\nSession ID:{session_id}\n项目:{project_folder}\nProvider:{provider}\nBranch:{branch_name}", - "status.current_branch_placeholder": "(当前 branch)", - "git.commit_disabled": "/commit 已禁用。\n请在 bot 环境中将 ENABLE_COMMIT_COMMAND 设为 true 以启用它。", + "provider.cli_not_found_install_first": "未找到 {provider_label} CLI:{bin_name}\n请更新机器人配置,或先安装该 CLI。", + "provider.current_provider_set": "当前提供方已设置为:{provider}", + "status.current_session_details": "当前会话:{session_name}\n会话 ID:{session_id}\n项目:{project_folder}\n提供方:{provider}\n分支:{branch_name}", + "status.current_branch_placeholder": "(当前分支)", + "git.commit_disabled": "/commit 已禁用。\n请在 机器人 环境中将 ENABLE_COMMIT_COMMAND 设为 true 以启用它。", "git.usage_commit": "用法:/commit git add ... && git commit ...", "git.no_valid_commit_commands": "未找到有效的 git commit 命令。", "git.commit_generate_prompt": "要为当前已变更的文件生成 git commit 命令吗?", @@ -187,7 +189,7 @@ "git.project_not_trusted_for_mutation": "此项目尚未被信任用于会修改 Git 的操作。请使用通过 /project 创建的项目,或先将其标记为受信任。", "git.unsafe_path_arguments": "不允许不安全的路径参数。只能使用当前项目内的文件。", "diff.task_completed": "任务已完成。", - "diff.session_label": "Session:{session_name}", + "diff.session_label": "会话:{session_name}", "diff.project_label": "项目:{project_folder}", "diff.changed_files": "已更改文件:", "diff.tracked_files": "已跟踪且有变更的文件:", @@ -204,7 +206,7 @@ "cli.then_run": "然后运行:coding-agent-telegram", "bot.command.compact": "压缩当前活动会话", "status.usage_compact": "用法:/compact", - "runtime.compacting_session": "正在压缩当前活动 session...", - "runtime.compact_summary_missing": "provider 没有返回可用的压缩交接摘要。", - "runtime.session_compacted": "session 压缩成功。已切换到 {session_name} ({session_id})。" + "runtime.compacting_session": "正在压缩当前活动会话...", + "runtime.compact_summary_missing": "提供方没有返回可用的压缩交接摘要。", + "runtime.session_compacted": "会话压缩成功。已切换到 {session_name} ({session_id})。" } diff --git a/src/coding_agent_telegram/resources/locales/zh-HK.json b/src/coding_agent_telegram/resources/locales/zh-HK.json index a7a4e38..8b8fa4c 100644 --- a/src/coding_agent_telegram/resources/locales/zh-HK.json +++ b/src/coding_agent_telegram/resources/locales/zh-HK.json @@ -1,14 +1,14 @@ { "bot.command.abort": "中止目前代理執行", - "bot.command.branch": "建立並切換到 Git branch", + "bot.command.branch": "建立並切換到 Git 分支", "bot.command.commit": "執行已驗證的 Git commit 指令", "bot.command.current": "顯示目前使用中的工作階段", "bot.command.diff": "顯示相對 HEAD 已變更的檔案名稱", "bot.command.new": "建立新工作階段", - "bot.command.pull": "pull 目前工作階段 branch", + "bot.command.pull": "拉取目前工作階段分支", "bot.command.project": "設定目前專案資料夾", "bot.command.provider": "為新工作階段選擇供應方", - "bot.command.push": "push 目前工作階段 branch", + "bot.command.push": "推送目前工作階段分支", "bot.command.switch": "列出工作階段或切換", "bot.error.command_failed": "⚠️ 指令失敗。請檢查伺服器日誌。", "bot.error.session_store": "⚠️ {error}", @@ -18,27 +18,28 @@ "common.no_project_selected": "尚未選擇專案。\n請先執行 /project 。", "common.project_busy": "專案 '{project_folder}' 目前有代理正在執行。\n完成前只支援 /current 和 /abort。", "common.project_folder_missing": "⚠️ 此工作階段的專案資料夾已不存在:{project_folder}", - "git.branch_unknown": "⚠️ 無法判斷目前工作階段的 branch。", + "common.button_expired": "⚠️ 此按鈕已過期。請重新執行命令。", + "git.branch_unknown": "⚠️ 無法判斷目前工作階段的分支。", "git.cancel_button": "取消", - "git.pull_cancelled": "已取消 pull。", - "git.pull_completed": "已完成 pull。", - "git.pull_confirm_button": "確認 pull", - "git.pull_confirm_prompt": "要從 `origin` pull branch `{branch_name}` 嗎?", - "git.pull_confirm_prompt_with_default": "要從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}` 嗎?", - "git.pull_in_progress": "正在從 `origin` pull branch `{branch_name}`...", - "git.pull_in_progress_with_default": "正在從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}`...", - "git.push_cancelled": "已取消 push。", + "git.pull_cancelled": "已取消拉取。", + "git.pull_completed": "已完成拉取。", + "git.pull_confirm_button": "確認拉取", + "git.pull_confirm_prompt": "要從 `origin` 拉取分支 `{branch_name}` 嗎?", + "git.pull_confirm_prompt_with_default": "要從 `origin` 拉取預設分支 `{default_branch}` 與分支 `{branch_name}` 嗎?", + "git.pull_in_progress": "正在從 `origin` 拉取分支 `{branch_name}`...", + "git.pull_in_progress_with_default": "正在從 `origin` 拉取預設分支 `{default_branch}` 與分支 `{branch_name}`...", + "git.push_cancelled": "已取消推送。", "git.push_cancelled_checkout_failed": "已取消 push。先切換到 `{branch_name}` 失敗。", - "git.push_confirm_button": "確認 push", - "git.push_confirm_prompt": "要將 branch `{branch_name}` push 到 `origin` 嗎?", - "git.push_in_progress": "正在將 branch `{branch_name}` push 到 `origin`...", + "git.push_confirm_button": "確認推送", + "git.push_confirm_prompt": "要將分支 `{branch_name}` 推送到 `origin` 嗎?", + "git.push_in_progress": "正在將分支 `{branch_name}` 推送到 `origin`...", "git.usage_diff": "用法:/diff", "git.usage_pull": "用法:/pull", "git.usage_push": "用法:/push", "message.photo_only_codex": "目前只有 Codex 工作階段支援圖片附件。", "message.question_queued": "問題已加入佇列,編號為 Q{question_number}。目前代理工作完成後會開始處理。", "message.voice_speech_to_text_disabled": "語音訊息功能尚未啟用。\n請先設定 ENABLE_OPENAI_WHISPER_SPEECH_TO_TEXT=true,並安裝本機 Whisper 依賴。", - "message.unsupported_message_type": "不支援的訊息類型。\n此 bot 目前接受文字訊息、圖片、語音訊息與音訊檔案。", + "message.unsupported_message_type": "不支援的訊息類型。\n此機械人目前接受文字訊息、圖片、語音訊息與音訊檔案。", "queue.button_group": "合併問題", "queue.button_no": "否", "queue.button_single": "逐一處理", @@ -55,14 +56,15 @@ "queue.processing_grouped": "正在把佇列問題作為一個批次處理。", "queue.processing_single": "正在逐一處理佇列問題。", "queue.working_on_queued": "正在處理佇列中的問題:", - "runtime.active_run_stall": "目前代理執行似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行 bot 的機器上有隱藏的權限對話框正在等待確認。", + "runtime.active_run_stall": "目前代理執行似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行機械人的機器上有隱藏的權限對話框正在等待確認。", "runtime.agent_run_aborted": "已透過 /abort 中止代理執行。", "runtime.agent_run_failed": "代理執行失敗。", "runtime.live_agent_output": "代理即時輸出", "runtime.photo_too_large": "圖片太大。支援的最大大小為 5 MB。", + "runtime.voice_audio_too_large": "音訊太大,無法使用本機語音轉文字。支援的最大大小為 {max_size_mb} MB。", "runtime.provider_output_index": "{provider} 輸出 {index}/{total}", "runtime.provider_output_single": "{provider} 輸出", - "runtime.replacement_session_stall": "替代工作階段建立似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行 bot 的機器上有隱藏的權限對話框正在等待確認。", + "runtime.replacement_session_stall": "替代工作階段建立似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行機械人的機器上有隱藏的權限對話框正在等待確認。", "runtime.resume_created_new": "恢復失敗,因此已建立新的工作階段。\n新的工作階段 ID:{session_id}\n新的工作階段名稱:{session_name}", "runtime.resume_id_changed": "恢復成功,但工作階段 ID 已變更。\n新的工作階段 ID:{session_id}\n新的工作階段名稱:{session_name}", "runtime.sensitive_diff_omitted": "{path}\n此檔案包含敏感內容,已省略。", @@ -76,13 +78,13 @@ "status.no_running_agent": "目前專案找不到正在執行的代理程序。", "status.usage_abort": "用法:/abort", "switch.available_sessions": "可用工作階段(第 {page}/{total_pages} 頁):", - "switch.source_bot_managed": "Bot 管理工作階段", + "switch.source_bot_managed": "機械人管理工作階段", "switch.source_native_cli": "原生 CLI 工作階段", "switch.current_project_filter": "原生工作階段的目前專案篩選:{project_folder}", "switch.current_project_filter_none": "原生工作階段的目前專案篩選:(無)", "switch.initialized_label": "初始化來源", "switch.use_label": "使用:", - "switch.native_import_note": "選擇原生 CLI 工作階段會將其匯入 state.json,並讓 bot 切換到該工作階段。", + "switch.native_import_note": "選擇原生 CLI 工作階段會將其匯入 state.json,並讓機械人切換到該工作階段。", "switch.pages_label": "頁碼:/switch page 1 ... /switch page {total_pages}", "switch.no_sessions_found": "找不到工作階段。", "switch.invalid_page_number": "頁碼無效。\n用法:/switch page ", @@ -90,7 +92,7 @@ "switch.switched_to_session": "已切換到工作階段", "switch.project_label": "專案", "switch.provider_label": "提供者", - "switch.branch_label": "Branch", + "switch.branch_label": "分支", "switch.source_label": "來源", "switch.imported_into_state_json": "已匯入 state.json。", "switch.status_active": "使用中", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "如果之後想切換語言,請修改 {env_path} 內的 APP_LOCALE。", "queue.button_cancel": "取消", "queue.cancelled": "已取消佇列中的問題。", - "project.branch_switch_requires_source": "要切換到 branch {new_branch},請先選擇來源。", - "project.branch_create_from_source": "要建立新 branch {new_branch},請先選擇來源 branch。", - "project.branch_source_missing": "在專案 '{project_folder}' 找不到 branch 來源。\n缺少 local/{source_branch} 與 origin/{source_branch}。", + "project.branch_switch_requires_source": "要切換到分支 {new_branch},請先選擇來源。", + "project.branch_create_from_source": "要建立新分支 {new_branch},請先選擇來源分支。", + "project.branch_source_missing": "在專案 '{project_folder}' 找不到分支來源。\n缺少 local/{source_branch} 與 origin/{source_branch}。", "project.project_label": "專案:{project_folder}", - "project.branch_target_label": "目標 branch:{new_branch}", - "project.choose_branch_source": "請選擇 branch 來源:", - "project.current_branch_in_repo_label": "儲存庫目前 branch:{branch_name}", - "project.default_branch_label": "預設 branch:{branch_name}", + "project.branch_target_label": "目標分支:{new_branch}", + "project.choose_branch_source": "請選擇分支來源:", + "project.current_branch_in_repo_label": "儲存庫目前分支:{branch_name}", + "project.default_branch_label": "預設分支:{branch_name}", "project.refresh_warnings": "重新整理警告:", - "project.local_branches": "本機 branch:", + "project.local_branches": "本機分支:", "project.annotation_default": "預設", - "project.annotation_current_branch_in_repo": "目前 branch", + "project.annotation_current_branch_in_repo": "目前分支", "project.none": "(無)", - "project.select_branch_with": "請用以下指令選擇 branch:", + "project.select_branch_with": "請用以下指令選擇分支:", "project.usage_project": "用法:/project \n範例:/project backend", "project.invalid_project_folder": "專案資料夾無效。只允許輸入資料夾名稱。", "project.path_not_directory": "專案路徑存在,但不是資料夾:{folder}", - "project.active_session_mismatch_title": "使用中的 session 與專案不一致", - "project.current_session_html": "目前 session:{session_name}", - "project.session_project_html": "Session 專案:{project_folder}", - "project.start_new_session_for_project_html": "如果你想在新選取的專案中工作,請用 /new 建立新的 session。", + "project.active_session_mismatch_title": "使用中的工作階段與專案不一致", + "project.current_session_html": "目前工作階段:{session_name}", + "project.session_project_html": "工作階段專案:{project_folder}", + "project.start_new_session_for_project_html": "如果你想在新選取的專案中工作,請用 /new 建立新的工作階段。", "project.project_changed_to": "已切換專案至:{folder}", - "project.branch_selection_required": "在此專案中建立或延續 session 前,必須先選擇 branch。", - "project.active_session_label": "目前 session:{session_name}", - "project.session_project_label": "Session 專案:{project_folder}", + "project.branch_selection_required": "在此專案中建立或延續工作階段前,必須先選擇分支。", + "project.active_session_label": "目前工作階段:{session_name}", + "project.session_project_label": "工作階段專案:{project_folder}", "project.project_set_title": "專案已設定", "project.project_html": "專案:{project_folder}", - "project.current_branch_html": "目前 branch:{branch_name}", - "project.branch_usage_html": "如果你想使用專用工作 branch,請用 /branch <new_branch>/branch <origin_branch> <new_branch>。", - "project.default_branch_behavior_html": "如果沒有指定 <origin_branch>,bot 會使用預設 branch:{branch_name}。", - "project.current_branch_behavior_html": "如果你沒有設定,bot 會在目前 branch 上工作:{branch_name}", + "project.current_branch_html": "目前分支:{branch_name}", + "project.branch_usage_html": "如果你想使用專用工作分支,請用 /branch <new_branch>/branch <origin_branch> <new_branch>。", + "project.default_branch_behavior_html": "如果沒有指定 <origin_branch>,機械人會使用預設分支:{branch_name}。", + "project.current_branch_behavior_html": "如果你沒有設定,機械人會在目前分支上工作:{branch_name}", "project.trust_prompt_html": "你信任這個專案嗎?\n專案:{project_folder}", "project.invalid_trust_decision": "無效的信任選擇。", "project.project_folder_missing_only": "專案資料夾不存在:{project_folder}", @@ -137,41 +139,41 @@ "project.already_trusted": "專案已是受信任狀態:{folder}", "project.trusted": "已信任專案:{folder}", "project.usage_branch": "用法:/branch \n或:/branch ", - "project.default_branch_unknown": "無法判斷此儲存庫的預設 branch。", + "project.default_branch_unknown": "無法判斷此儲存庫的預設分支。", "project.project_folder_missing_retry": "專案資料夾不存在:{project_folder}\n請重新執行 /project {project_folder}。", "branch_resolution.use_branch": "使用 {branch_name}", - "branch_resolution.create_from_instead_of_origin": "要不要改為從以下 branch 之一建立 {new_branch},而不是從 origin/{source_branch}?", - "branch_resolution.discrepancy_detected": "執行目前 session 前偵測到 branch 不一致。", - "branch_resolution.session_label": "Session:{session_name}", - "branch_resolution.stored_branch_label": "儲存的 branch:{branch_name}", - "branch_resolution.choose_branch_to_use": "請選擇要使用的 branch。", - "branch_resolution.no_pending_decision": "找不到待處理的 branch 決定。", - "branch_resolution.no_pending_discrepancy": "找不到待處理的 branch 不一致狀態。", - "branch_resolution.no_active_session": "目前沒有可用的使用中 session。", - "branch_resolution.using_current_branch": "使用目前 branch:{branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "儲存的 branch 已無法使用:{branch_name}\n亦沒有可用的替代來源 branch。", - "branch_resolution.stored_branch_unavailable": "儲存的 branch 已無法使用。", + "branch_resolution.create_from_instead_of_origin": "要不要改為從以下分支之一建立 {new_branch},而不是從 origin/{source_branch}?", + "branch_resolution.discrepancy_detected": "執行目前工作階段前偵測到分支不一致。", + "branch_resolution.session_label": "工作階段:{session_name}", + "branch_resolution.stored_branch_label": "儲存的分支:{branch_name}", + "branch_resolution.choose_branch_to_use": "請選擇要使用的分支。", + "branch_resolution.no_pending_decision": "找不到待處理的分支決定。", + "branch_resolution.no_pending_discrepancy": "找不到待處理的分支不一致狀態。", + "branch_resolution.no_active_session": "目前沒有可用的使用中工作階段。", + "branch_resolution.using_current_branch": "使用目前分支:{branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "儲存的分支已無法使用:{branch_name}\n亦沒有可用的替代來源分支。", + "branch_resolution.stored_branch_unavailable": "儲存的分支已無法使用。", "branch_resolution.missing_local_and_origin": "缺少 local/{branch_name} 與 origin/{branch_name}。", - "branch_resolution.create_branch_from_choices": "要不要從以下 branch 之一建立 {branch_name}?", - "branch_resolution.choose_restore_method": "請選擇如何還原儲存的 branch。", - "lifecycle.provider_selection_required": "建立或延續 session 前,必須先選擇 provider。", + "branch_resolution.create_branch_from_choices": "要不要從以下分支之一建立 {branch_name}?", + "branch_resolution.choose_restore_method": "請選擇如何還原儲存的分支。", + "lifecycle.provider_selection_required": "建立或延續工作階段前,必須先選擇 供應方。", "lifecycle.no_project_selected_example": "尚未選擇專案。\n請先執行 /project 。\n範例:/project backend", - "lifecycle.session_name_exists": "Session 名稱已存在:{session_name}\n請使用其他 session 名稱。", - "lifecycle.creating_session": "正在建立 session...", - "lifecycle.failed_create_session": "建立 session 失敗。", - "lifecycle.session_created_successfully": "Session 建立成功:{session_name}\nSession ID:{session_id}\n專案:{project_folder}\nProvider:{provider}\nBranch:{branch_name}", - "provider.cli_not_found": "找不到 {provider_label} CLI:{bin_name}\n執行 /provider 選擇可用的 provider,或更新 bot 設定。", + "lifecycle.session_name_exists": "工作階段名稱已存在:{session_name}\n請使用其他工作階段名稱。", + "lifecycle.creating_session": "正在建立工作階段...", + "lifecycle.failed_create_session": "建立工作階段失敗。", + "lifecycle.session_created_successfully": "工作階段建立成功:{session_name}\n工作階段 ID:{session_id}\n專案:{project_folder}\n供應方:{provider}\n分支:{branch_name}", + "provider.cli_not_found": "找不到 {provider_label} CLI:{bin_name}\n執行 /provider 選擇可用的供應方,或更新機械人設定。", "provider.status_available": "可用", "provider.status_missing": "缺少", "provider.status_current": "目前", "provider.usage_provider": "用法:/provider", - "provider.current_provider_prompt": "目前 provider:{provider}\n請選擇新 session 使用的 provider。", + "provider.current_provider_prompt": "目前供應方:{provider}\n請選擇新工作階段使用的供應方。", "provider.not_selected": "(未選擇)", - "provider.cli_not_found_install_first": "找不到 {provider_label} CLI:{bin_name}\n請更新 bot 設定,或先安裝該 CLI。", - "provider.current_provider_set": "目前 provider 已設為:{provider}", - "status.current_session_details": "目前 session:{session_name}\nSession ID:{session_id}\n專案:{project_folder}\nProvider:{provider}\nBranch:{branch_name}", - "status.current_branch_placeholder": "(目前 branch)", - "git.commit_disabled": "/commit 已停用。\n請在 bot 環境中把 ENABLE_COMMIT_COMMAND 設為 true 以啟用它。", + "provider.cli_not_found_install_first": "找不到 {provider_label} CLI:{bin_name}\n請更新機械人設定,或先安裝該 CLI。", + "provider.current_provider_set": "目前供應方已設為:{provider}", + "status.current_session_details": "目前工作階段:{session_name}\n工作階段 ID:{session_id}\n專案:{project_folder}\n供應方:{provider}\n分支:{branch_name}", + "status.current_branch_placeholder": "(目前分支)", + "git.commit_disabled": "/commit 已停用。\n請在機械人環境中把 ENABLE_COMMIT_COMMAND 設為 true 以啟用它。", "git.usage_commit": "用法:/commit git add ... && git commit ...", "git.no_valid_commit_commands": "找不到有效的 git commit 指令。", "git.commit_generate_prompt": "要為目前已變更的檔案產生 git commit 指令嗎?", @@ -187,7 +189,7 @@ "git.project_not_trusted_for_mutation": "此專案尚未被信任可執行會修改 Git 的操作。請使用由 /project 建立的專案,或先將它標記為受信任。", "git.unsafe_path_arguments": "不允許不安全的路徑參數。只能使用目前專案內的檔案。", "diff.task_completed": "任務已完成。", - "diff.session_label": "Session:{session_name}", + "diff.session_label": "工作階段:{session_name}", "diff.project_label": "專案:{project_folder}", "diff.changed_files": "已變更檔案:", "diff.tracked_files": "已追蹤且有變更的檔案:", @@ -204,7 +206,7 @@ "cli.then_run": "接著執行:coding-agent-telegram", "bot.command.compact": "壓縮目前使用中的工作階段", "status.usage_compact": "用法:/compact", - "runtime.compacting_session": "正在壓縮目前使用中的 session...", - "runtime.compact_summary_missing": "provider 沒有返回可用的壓縮交接摘要。", - "runtime.session_compacted": "session 壓縮成功。已切換到 {session_name} ({session_id})。" + "runtime.compacting_session": "正在壓縮目前使用中的工作階段...", + "runtime.compact_summary_missing": "供應方 沒有返回可用的壓縮交接摘要。", + "runtime.session_compacted": "工作階段壓縮成功。已切換到 {session_name} ({session_id})。" } diff --git a/src/coding_agent_telegram/resources/locales/zh-TW.json b/src/coding_agent_telegram/resources/locales/zh-TW.json index 1655c78..57a21f5 100644 --- a/src/coding_agent_telegram/resources/locales/zh-TW.json +++ b/src/coding_agent_telegram/resources/locales/zh-TW.json @@ -1,14 +1,14 @@ { "bot.command.abort": "中止目前代理執行", - "bot.command.branch": "建立並切換到 Git branch", + "bot.command.branch": "建立並切換到 Git 分支", "bot.command.commit": "執行已驗證的 Git commit 指令", "bot.command.current": "顯示目前使用中的工作階段", "bot.command.diff": "顯示相對 HEAD 已變更的檔案名稱", "bot.command.new": "建立新工作階段", - "bot.command.pull": "pull 目前工作階段 branch", + "bot.command.pull": "拉取目前工作階段分支", "bot.command.project": "設定目前專案資料夾", "bot.command.provider": "為新工作階段選擇提供者", - "bot.command.push": "push 目前工作階段 branch", + "bot.command.push": "推送目前工作階段分支", "bot.command.switch": "列出工作階段或切換", "bot.error.command_failed": "⚠️ 指令失敗。請檢查伺服器日誌。", "bot.error.session_store": "⚠️ {error}", @@ -18,27 +18,28 @@ "common.no_project_selected": "尚未選擇專案。\n請先執行 /project 。", "common.project_busy": "專案 '{project_folder}' 目前有代理正在執行。\n完成前只支援 /current 和 /abort。", "common.project_folder_missing": "⚠️ 此工作階段的專案資料夾已不存在:{project_folder}", - "git.branch_unknown": "⚠️ 無法判斷目前工作階段的 branch。", + "common.button_expired": "⚠️ 此按鈕已過期。請重新執行命令。", + "git.branch_unknown": "⚠️ 無法判斷目前工作階段的分支。", "git.cancel_button": "取消", - "git.pull_cancelled": "已取消 pull。", - "git.pull_completed": "已完成 pull。", - "git.pull_confirm_button": "確認 pull", - "git.pull_confirm_prompt": "要從 `origin` pull branch `{branch_name}` 嗎?", - "git.pull_confirm_prompt_with_default": "要從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}` 嗎?", - "git.pull_in_progress": "正在從 `origin` pull branch `{branch_name}`...", - "git.pull_in_progress_with_default": "正在從 `origin` pull 預設 branch `{default_branch}` 與 branch `{branch_name}`...", - "git.push_cancelled": "已取消 push。", + "git.pull_cancelled": "已取消拉取。", + "git.pull_completed": "已完成拉取。", + "git.pull_confirm_button": "確認拉取", + "git.pull_confirm_prompt": "要從 `origin` 拉取分支 `{branch_name}` 嗎?", + "git.pull_confirm_prompt_with_default": "要從 `origin` 拉取預設分支 `{default_branch}` 與分支 `{branch_name}` 嗎?", + "git.pull_in_progress": "正在從 `origin` 拉取分支 `{branch_name}`...", + "git.pull_in_progress_with_default": "正在從 `origin` 拉取預設分支 `{default_branch}` 與分支 `{branch_name}`...", + "git.push_cancelled": "已取消推送。", "git.push_cancelled_checkout_failed": "已取消 push。先切換到 `{branch_name}` 失敗。", - "git.push_confirm_button": "確認 push", - "git.push_confirm_prompt": "要將 branch `{branch_name}` push 到 `origin` 嗎?", - "git.push_in_progress": "正在將 branch `{branch_name}` push 到 `origin`...", + "git.push_confirm_button": "確認推送", + "git.push_confirm_prompt": "要將分支 `{branch_name}` 推送到 `origin` 嗎?", + "git.push_in_progress": "正在將分支 `{branch_name}` 推送到 `origin`...", "git.usage_diff": "用法:/diff", "git.usage_pull": "用法:/pull", "git.usage_push": "用法:/push", "message.photo_only_codex": "目前只有 Codex 工作階段支援圖片附件。", "message.question_queued": "問題已加入佇列,編號為 Q{question_number}。目前代理工作完成後會開始處理。", "message.voice_speech_to_text_disabled": "語音訊息功能尚未啟用。\n請先設定 ENABLE_OPENAI_WHISPER_SPEECH_TO_TEXT=true,並安裝本機 Whisper 依賴。", - "message.unsupported_message_type": "不支援的訊息類型。\n此 bot 目前接受文字訊息、圖片、語音訊息與音訊檔案。", + "message.unsupported_message_type": "不支援的訊息類型。\n此機器人目前接受文字訊息、圖片、語音訊息與音訊檔案。", "queue.button_group": "合併問題", "queue.button_no": "否", "queue.button_single": "逐一處理", @@ -55,14 +56,15 @@ "queue.processing_grouped": "正在把佇列問題作為一個批次處理。", "queue.processing_single": "正在逐一處理佇列問題。", "queue.working_on_queued": "正在處理佇列中的問題:", - "runtime.active_run_stall": "目前代理執行似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行 bot 的機器上有隱藏的權限對話框正在等待確認。", + "runtime.active_run_stall": "目前代理執行似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行機器人的機器上有隱藏的權限對話框正在等待確認。", "runtime.agent_run_aborted": "已透過 /abort 中止代理執行。", "runtime.agent_run_failed": "代理執行失敗。", "runtime.live_agent_output": "代理即時輸出", "runtime.photo_too_large": "圖片太大。支援的最大大小為 5 MB。", + "runtime.voice_audio_too_large": "音訊太大,無法使用本機語音轉文字。支援的最大大小為 {max_size_mb} MB。", "runtime.provider_output_index": "{provider} 輸出 {index}/{total}", "runtime.provider_output_single": "{provider} 輸出", - "runtime.replacement_session_stall": "替代工作階段建立似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行 bot 的機器上有隱藏的權限對話框正在等待確認。", + "runtime.replacement_session_stall": "替代工作階段建立似乎卡住了。\n本機代理程序仍在執行,但沒有產生輸出。\n在 macOS 上,這可能是因為執行機器人的機器上有隱藏的權限對話框正在等待確認。", "runtime.resume_created_new": "恢復失敗,因此已建立新的工作階段。\n新的工作階段 ID:{session_id}\n新的工作階段名稱:{session_name}", "runtime.resume_id_changed": "恢復成功,但工作階段 ID 已變更。\n新的工作階段 ID:{session_id}\n新的工作階段名稱:{session_name}", "runtime.sensitive_diff_omitted": "{path}\n此檔案包含敏感內容,已省略。", @@ -76,13 +78,13 @@ "status.no_running_agent": "目前專案找不到正在執行的代理程序。", "status.usage_abort": "用法:/abort", "switch.available_sessions": "可用工作階段(第 {page}/{total_pages} 頁):", - "switch.source_bot_managed": "Bot 管理工作階段", + "switch.source_bot_managed": "機器人管理工作階段", "switch.source_native_cli": "原生 CLI 工作階段", "switch.current_project_filter": "原生工作階段的目前專案篩選:{project_folder}", "switch.current_project_filter_none": "原生工作階段的目前專案篩選:(無)", "switch.initialized_label": "初始化來源", "switch.use_label": "使用:", - "switch.native_import_note": "選擇原生 CLI 工作階段會將其匯入 state.json,並讓 bot 切換到該工作階段。", + "switch.native_import_note": "選擇原生 CLI 工作階段會將其匯入 state.json,並讓機器人切換到該工作階段。", "switch.pages_label": "頁碼:/switch page 1 ... /switch page {total_pages}", "switch.no_sessions_found": "找不到工作階段。", "switch.invalid_page_number": "頁碼無效。\n用法:/switch page ", @@ -90,7 +92,7 @@ "switch.switched_to_session": "已切換到工作階段", "switch.project_label": "專案", "switch.provider_label": "提供者", - "switch.branch_label": "Branch", + "switch.branch_label": "分支", "switch.source_label": "來源", "switch.imported_into_state_json": "已匯入 state.json。", "switch.status_active": "使用中", @@ -99,37 +101,37 @@ "bootstrap.env_created_change_line": "如果之後想切換語言,請修改 {env_path} 裡的 APP_LOCALE。", "queue.button_cancel": "取消", "queue.cancelled": "已取消佇列中的問題。", - "project.branch_switch_requires_source": "要切換到 branch {new_branch},請先選擇來源。", - "project.branch_create_from_source": "要建立新 branch {new_branch},請先選擇來源 branch。", - "project.branch_source_missing": "在專案 '{project_folder}' 找不到 branch 來源。\n缺少 local/{source_branch} 與 origin/{source_branch}。", + "project.branch_switch_requires_source": "要切換到分支 {new_branch},請先選擇來源。", + "project.branch_create_from_source": "要建立新分支 {new_branch},請先選擇來源分支。", + "project.branch_source_missing": "在專案 '{project_folder}' 找不到分支來源。\n缺少 local/{source_branch} 與 origin/{source_branch}。", "project.project_label": "專案:{project_folder}", - "project.branch_target_label": "目標 branch:{new_branch}", - "project.choose_branch_source": "請選擇 branch 來源:", - "project.current_branch_in_repo_label": "儲存庫目前 branch:{branch_name}", - "project.default_branch_label": "預設 branch:{branch_name}", + "project.branch_target_label": "目標分支:{new_branch}", + "project.choose_branch_source": "請選擇分支來源:", + "project.current_branch_in_repo_label": "儲存庫目前分支:{branch_name}", + "project.default_branch_label": "預設分支:{branch_name}", "project.refresh_warnings": "重新整理警告:", - "project.local_branches": "本機 branch:", + "project.local_branches": "本機分支:", "project.annotation_default": "預設", - "project.annotation_current_branch_in_repo": "目前 branch", + "project.annotation_current_branch_in_repo": "目前分支", "project.none": "(無)", - "project.select_branch_with": "請用以下指令選擇 branch:", + "project.select_branch_with": "請用以下指令選擇分支:", "project.usage_project": "用法:/project \n範例:/project backend", "project.invalid_project_folder": "專案資料夾無效。只允許資料夾名稱。", "project.path_not_directory": "專案路徑存在,但不是資料夾:{folder}", - "project.active_session_mismatch_title": "使用中的 session 與專案不一致", - "project.current_session_html": "目前 session:{session_name}", - "project.session_project_html": "Session 專案:{project_folder}", - "project.start_new_session_for_project_html": "如果你要在新選擇的專案中工作,請用 /new 建立新的 session。", + "project.active_session_mismatch_title": "使用中的工作階段與專案不一致", + "project.current_session_html": "目前工作階段:{session_name}", + "project.session_project_html": "工作階段專案:{project_folder}", + "project.start_new_session_for_project_html": "如果你要在新選擇的專案中工作,請用 /new 建立新的工作階段。", "project.project_changed_to": "已切換專案至:{folder}", - "project.branch_selection_required": "在此專案中建立或延續 session 前,必須先選擇 branch。", - "project.active_session_label": "目前 session:{session_name}", - "project.session_project_label": "Session 專案:{project_folder}", + "project.branch_selection_required": "在此專案中建立或延續工作階段前,必須先選擇分支。", + "project.active_session_label": "目前工作階段:{session_name}", + "project.session_project_label": "工作階段專案:{project_folder}", "project.project_set_title": "專案已設定", "project.project_html": "專案:{project_folder}", - "project.current_branch_html": "目前 branch:{branch_name}", - "project.branch_usage_html": "如果你要使用專屬的工作 branch,請用 /branch <new_branch>/branch <origin_branch> <new_branch>。", - "project.default_branch_behavior_html": "如果沒有指定 <origin_branch>,bot 會使用預設 branch:{branch_name}。", - "project.current_branch_behavior_html": "如果你沒有設定,bot 會在目前 branch 上工作:{branch_name}", + "project.current_branch_html": "目前分支:{branch_name}", + "project.branch_usage_html": "如果你要使用專屬的工作分支,請用 /branch <new_branch>/branch <origin_branch> <new_branch>。", + "project.default_branch_behavior_html": "如果沒有指定 <origin_branch>,機器人會使用預設分支:{branch_name}。", + "project.current_branch_behavior_html": "如果你沒有設定,機器人會在目前分支上工作:{branch_name}", "project.trust_prompt_html": "你信任這個專案嗎?\n專案:{project_folder}", "project.invalid_trust_decision": "無效的信任選擇。", "project.project_folder_missing_only": "專案資料夾不存在:{project_folder}", @@ -137,41 +139,41 @@ "project.already_trusted": "專案已經是受信任狀態:{folder}", "project.trusted": "已信任專案:{folder}", "project.usage_branch": "用法:/branch \n或:/branch ", - "project.default_branch_unknown": "無法判斷此儲存庫的預設 branch。", + "project.default_branch_unknown": "無法判斷此儲存庫的預設分支。", "project.project_folder_missing_retry": "專案資料夾不存在:{project_folder}\n請重新執行 /project {project_folder}。", "branch_resolution.use_branch": "使用 {branch_name}", - "branch_resolution.create_from_instead_of_origin": "要不要改成從以下 branch 建立 {new_branch},而不是從 origin/{source_branch}?", - "branch_resolution.discrepancy_detected": "執行目前 session 前偵測到 branch 不一致。", - "branch_resolution.session_label": "Session:{session_name}", - "branch_resolution.stored_branch_label": "儲存的 branch:{branch_name}", - "branch_resolution.choose_branch_to_use": "請選擇要使用的 branch。", - "branch_resolution.no_pending_decision": "找不到待處理的 branch 決定。", - "branch_resolution.no_pending_discrepancy": "找不到待處理的 branch 不一致狀態。", - "branch_resolution.no_active_session": "目前沒有可用的使用中 session。", - "branch_resolution.using_current_branch": "使用目前 branch:{branch_name}", - "branch_resolution.stored_branch_unavailable_no_fallback": "儲存的 branch 已無法使用:{branch_name}\n也沒有可用的替代來源 branch。", - "branch_resolution.stored_branch_unavailable": "儲存的 branch 已無法使用。", + "branch_resolution.create_from_instead_of_origin": "要不要改成從以下分支建立 {new_branch},而不是從 origin/{source_branch}?", + "branch_resolution.discrepancy_detected": "執行目前工作階段前偵測到分支不一致。", + "branch_resolution.session_label": "工作階段:{session_name}", + "branch_resolution.stored_branch_label": "儲存的分支:{branch_name}", + "branch_resolution.choose_branch_to_use": "請選擇要使用的分支。", + "branch_resolution.no_pending_decision": "找不到待處理的分支決定。", + "branch_resolution.no_pending_discrepancy": "找不到待處理的分支不一致狀態。", + "branch_resolution.no_active_session": "目前沒有可用的使用中工作階段。", + "branch_resolution.using_current_branch": "使用目前分支:{branch_name}", + "branch_resolution.stored_branch_unavailable_no_fallback": "儲存的分支已無法使用:{branch_name}\n也沒有可用的替代來源分支。", + "branch_resolution.stored_branch_unavailable": "儲存的分支已無法使用。", "branch_resolution.missing_local_and_origin": "缺少 local/{branch_name} 與 origin/{branch_name}。", - "branch_resolution.create_branch_from_choices": "要不要從以下 branch 之一建立 {branch_name}?", - "branch_resolution.choose_restore_method": "請選擇如何還原儲存的 branch。", - "lifecycle.provider_selection_required": "建立或延續 session 前,必須先選擇 provider。", + "branch_resolution.create_branch_from_choices": "要不要從以下分支之一建立 {branch_name}?", + "branch_resolution.choose_restore_method": "請選擇如何還原儲存的分支。", + "lifecycle.provider_selection_required": "建立或延續工作階段前,必須先選擇提供者。", "lifecycle.no_project_selected_example": "尚未選擇專案。\n請先執行 /project 。\n範例:/project backend", - "lifecycle.session_name_exists": "Session 名稱已存在:{session_name}\n請使用其他 session 名稱。", - "lifecycle.creating_session": "正在建立 session...", - "lifecycle.failed_create_session": "建立 session 失敗。", - "lifecycle.session_created_successfully": "Session 建立成功:{session_name}\nSession ID:{session_id}\n專案:{project_folder}\nProvider:{provider}\nBranch:{branch_name}", - "provider.cli_not_found": "找不到 {provider_label} CLI:{bin_name}\n執行 /provider 選擇可用的 provider,或更新 bot 設定。", + "lifecycle.session_name_exists": "工作階段名稱已存在:{session_name}\n請使用其他工作階段名稱。", + "lifecycle.creating_session": "正在建立工作階段...", + "lifecycle.failed_create_session": "建立工作階段失敗。", + "lifecycle.session_created_successfully": "工作階段建立成功:{session_name}\n工作階段 ID:{session_id}\n專案:{project_folder}\n提供者:{provider}\n分支:{branch_name}", + "provider.cli_not_found": "找不到 {provider_label} CLI:{bin_name}\n執行 /provider 選擇可用的提供者,或更新機器人設定。", "provider.status_available": "可用", "provider.status_missing": "缺少", "provider.status_current": "目前", "provider.usage_provider": "用法:/provider", - "provider.current_provider_prompt": "目前 provider:{provider}\n請選擇新 session 使用的 provider。", + "provider.current_provider_prompt": "目前提供者:{provider}\n請選擇新工作階段使用的提供者。", "provider.not_selected": "(未選擇)", - "provider.cli_not_found_install_first": "找不到 {provider_label} CLI:{bin_name}\n請更新 bot 設定,或先安裝該 CLI。", - "provider.current_provider_set": "目前 provider 已設為:{provider}", - "status.current_session_details": "目前 session:{session_name}\nSession ID:{session_id}\n專案:{project_folder}\nProvider:{provider}\nBranch:{branch_name}", - "status.current_branch_placeholder": "(目前 branch)", - "git.commit_disabled": "/commit 已停用。\n請在 bot 環境中把 ENABLE_COMMIT_COMMAND 設為 true 以啟用它。", + "provider.cli_not_found_install_first": "找不到 {provider_label} CLI:{bin_name}\n請更新機器人設定,或先安裝該 CLI。", + "provider.current_provider_set": "目前提供者已設為:{provider}", + "status.current_session_details": "目前工作階段:{session_name}\n工作階段 ID:{session_id}\n專案:{project_folder}\n提供者:{provider}\n分支:{branch_name}", + "status.current_branch_placeholder": "(目前分支)", + "git.commit_disabled": "/commit 已停用。\n請在機器人環境中把 ENABLE_COMMIT_COMMAND 設為 true 以啟用它。", "git.usage_commit": "用法:/commit git add ... && git commit ...", "git.no_valid_commit_commands": "找不到有效的 git commit 指令。", "git.commit_generate_prompt": "要為目前已變更的檔案產生 git commit 指令嗎?", @@ -187,7 +189,7 @@ "git.project_not_trusted_for_mutation": "此專案尚未被信任可執行會修改 Git 的操作。請使用由 /project 建立的專案,或先將它標記為受信任。", "git.unsafe_path_arguments": "不允許不安全的路徑參數。只能使用目前專案內的檔案。", "diff.task_completed": "任務已完成。", - "diff.session_label": "Session:{session_name}", + "diff.session_label": "工作階段:{session_name}", "diff.project_label": "專案:{project_folder}", "diff.changed_files": "已變更檔案:", "diff.tracked_files": "已追蹤且有變更的檔案:", @@ -204,7 +206,7 @@ "cli.then_run": "接著執行:coding-agent-telegram", "bot.command.compact": "壓縮目前使用中的工作階段", "status.usage_compact": "用法:/compact", - "runtime.compacting_session": "正在壓縮目前使用中的 session...", - "runtime.compact_summary_missing": "provider 沒有回傳可用的壓縮交接摘要。", - "runtime.session_compacted": "session 壓縮成功。已切換到 {session_name} ({session_id})。" + "runtime.compacting_session": "正在壓縮目前使用中的工作階段...", + "runtime.compact_summary_missing": "提供者沒有回傳可用的壓縮交接摘要。", + "runtime.session_compacted": "工作階段壓縮成功。已切換到 {session_name} ({session_id})。" } From 3a81decbc809c280627f6e5316ad8378be582066 Mon Sep 17 00:00:00 2001 From: DCHA <426225+daocha@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:40:29 +0800 Subject: [PATCH 3/5] Fix tests (#44) Co-authored-by: DCHA Agent <259406208+dcha-agent@users.noreply.github.com> --- tests/test_command_router.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_command_router.py b/tests/test_command_router.py index e2fc1ac..927d36c 100644 --- a/tests/test_command_router.py +++ b/tests/test_command_router.py @@ -743,9 +743,9 @@ def test_project_command_is_localized_in_zh_tw(tmp_path: Path): message = bot.messages[0][1] assert "已切換專案至:backend" in message - assert "必須先選擇 branch" in message - assert "儲存庫目前 branch:main" in message - assert "請用以下指令選擇 branch:" in message + assert "必須先選擇分支" in message + assert "儲存庫目前分支:main" in message + assert "請用以下指令選擇分支:" in message def test_project_command_warns_when_active_session_belongs_to_another_project(tmp_path: Path): @@ -1019,9 +1019,9 @@ def test_branch_command_is_localized_in_zh_tw(tmp_path: Path): asyncio.run(router.handle_branch(update, context)) message = bot.messages[-1][1] - assert "要建立新 branch feature-1" in message - assert "請選擇 branch 來源:" in message - assert "目標 branch:feature-1" in message + assert "要建立新分支 feature-1" in message + assert "請選擇分支來源:" in message + assert "目標分支:feature-1" in message token = router._register_branch_source_token("origin", "main", "feature-1") query = SimpleNamespace( @@ -1046,7 +1046,7 @@ async def fake_edit(text, reply_markup=None): asyncio.run(router.handle_branch_source_callback(callback_update, context)) - assert "目前 branch:feature-1" in edited[-1][0] + assert "目前分支:feature-1" in edited[-1][0] def test_branch_command_for_new_branch_offers_current_and_default_sources(tmp_path: Path): @@ -5507,8 +5507,8 @@ def test_handle_provider_localizes_prompt_text(tmp_path: Path): context = SimpleNamespace(args=[], bot=bot) asyncio.run(router.handle_provider(update, context)) - assert "目前 provider:codex" in bot.messages[-1][1] - assert "請選擇新 session 使用的 provider。" in bot.messages[-1][1] + assert "目前提供者:codex" in bot.messages[-1][1] + assert "請選擇新工作階段使用的提供者。" in bot.messages[-1][1] def test_handle_provider_sends_usage_when_args_provided(tmp_path: Path): From a1324e86833a55c8b8cfab9afef3c82204207ac9 Mon Sep 17 00:00:00 2001 From: DCHA <426225+daocha@users.noreply.github.com> Date: Fri, 3 Apr 2026 22:46:38 +0800 Subject: [PATCH 4/5] Fix translations (#45) Co-authored-by: DCHA Agent <259406208+dcha-agent@users.noreply.github.com> --- src/coding_agent_telegram/resources/locales/de.json | 2 +- src/coding_agent_telegram/resources/locales/fr.json | 2 +- src/coding_agent_telegram/resources/locales/ja.json | 8 ++++---- src/coding_agent_telegram/resources/locales/ko.json | 8 ++++---- src/coding_agent_telegram/resources/locales/th.json | 8 ++++---- src/coding_agent_telegram/resources/locales/zh-HK.json | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/coding_agent_telegram/resources/locales/de.json b/src/coding_agent_telegram/resources/locales/de.json index cf7ee86..c0b6dbd 100644 --- a/src/coding_agent_telegram/resources/locales/de.json +++ b/src/coding_agent_telegram/resources/locales/de.json @@ -19,7 +19,7 @@ "common.project_busy": "Auf Projekt '{project_folder}' läuft gerade ein Agent.\nBis zum Abschluss werden nur /current und /abort unterstützt.", "common.project_folder_missing": "⚠️ Der Projektordner für diese Sitzung existiert nicht mehr: {project_folder}", "common.button_expired": "⚠️ Diese Schaltfläche ist abgelaufen. Bitte führe den Befehl erneut aus.", - "git.branch_unknown": "⚠️ Die Zweig der aktuellen Sitzung konnte nicht ermittelt werden.", + "git.branch_unknown": "⚠️ Der Zweig der aktuellen Sitzung konnte nicht ermittelt werden.", "git.cancel_button": "Abbrechen", "git.usage_diff": "Verwendung: /diff", "git.usage_pull": "Verwendung: /pull", diff --git a/src/coding_agent_telegram/resources/locales/fr.json b/src/coding_agent_telegram/resources/locales/fr.json index 03b3ea6..f4b361b 100644 --- a/src/coding_agent_telegram/resources/locales/fr.json +++ b/src/coding_agent_telegram/resources/locales/fr.json @@ -92,7 +92,7 @@ "switch.switched_to_session": "session activée", "switch.project_label": "Projet", "switch.provider_label": "Fournisseur", - "switch.branch_label": "Branchee", + "switch.branch_label": "Branche", "switch.source_label": "Origine", "switch.imported_into_state_json": "Importé dans state.json.", "switch.status_active": "actif", diff --git a/src/coding_agent_telegram/resources/locales/ja.json b/src/coding_agent_telegram/resources/locales/ja.json index bb074ec..14fcd9f 100644 --- a/src/coding_agent_telegram/resources/locales/ja.json +++ b/src/coding_agent_telegram/resources/locales/ja.json @@ -204,9 +204,9 @@ "cli.created_env_if_missing": "{env_path} が存在しない場合は作成しました。", "cli.update_fields_in_env": "{env_path} の次の項目を更新してください:", "cli.then_run": "その後、次を実行してください: coding-agent-telegram", - "bot.command.compact": "アクティブなセッションを compact", + "bot.command.compact": "アクティブなセッションを圧縮", "status.usage_compact": "使い方: /compact", - "runtime.compacting_session": "アクティブな セッション を compact 中です...", - "runtime.compact_summary_missing": "プロバイダー から利用可能な compact handoff summary が返されませんでした。", - "runtime.session_compacted": "セッション の compact に成功しました。{session_name} ({session_id}) に切り替えました。" + "runtime.compacting_session": "アクティブなセッションを圧縮しています...", + "runtime.compact_summary_missing": "プロバイダーから利用可能な簡潔な引き継ぎ要約が返されませんでした。", + "runtime.session_compacted": "セッションの圧縮に成功しました。{session_name} ({session_id}) に切り替えました。" } diff --git a/src/coding_agent_telegram/resources/locales/ko.json b/src/coding_agent_telegram/resources/locales/ko.json index 41063ee..e31ab2e 100644 --- a/src/coding_agent_telegram/resources/locales/ko.json +++ b/src/coding_agent_telegram/resources/locales/ko.json @@ -204,9 +204,9 @@ "cli.created_env_if_missing": "{env_path} 가 없으면 생성했습니다.", "cli.update_fields_in_env": "{env_path} 에서 다음 항목을 업데이트하세요:", "cli.then_run": "그다음 실행: coding-agent-telegram", - "bot.command.compact": "활성 세션 compact", + "bot.command.compact": "활성 세션 압축", "status.usage_compact": "사용법: /compact", - "runtime.compacting_session": "활성 세션 을 compact 하는 중입니다...", - "runtime.compact_summary_missing": "프로바이더 가 사용할 수 있는 compact handoff summary 를 반환하지 않았습니다.", - "runtime.session_compacted": "세션 compact 를 완료했습니다. {session_name} ({session_id}) 로 전환했습니다." + "runtime.compacting_session": "활성 세션을 압축하는 중입니다...", + "runtime.compact_summary_missing": "프로바이더가 사용할 수 있는 간결한 인계 요약을 반환하지 않았습니다.", + "runtime.session_compacted": "세션 압축을 완료했습니다. {session_name} ({session_id})로 전환했습니다." } diff --git a/src/coding_agent_telegram/resources/locales/th.json b/src/coding_agent_telegram/resources/locales/th.json index 3ee0453..c4252b2 100644 --- a/src/coding_agent_telegram/resources/locales/th.json +++ b/src/coding_agent_telegram/resources/locales/th.json @@ -204,9 +204,9 @@ "cli.created_env_if_missing": "ได้สร้าง {env_path} แล้วหากไฟล์นี้ยังไม่มีอยู่", "cli.update_fields_in_env": "อัปเดตค่าต่อไปนี้ใน {env_path}:", "cli.then_run": "จากนั้นรัน: coding-agent-telegram", - "bot.command.compact": "ย่อ เซสชัน ที่กำลังใช้งาน", + "bot.command.compact": "ย่อเซสชันที่กำลังใช้งาน", "status.usage_compact": "วิธีใช้: /compact", - "runtime.compacting_session": "กำลังย่อ เซสชัน ที่กำลังใช้งาน...", - "runtime.compact_summary_missing": "ผู้ให้บริการ ไม่ได้ส่ง compact handoff summary ที่ใช้งานได้กลับมา", - "runtime.session_compacted": "ย่อ เซสชัน สำเร็จแล้ว และสลับไปที่ {session_name} ({session_id})" + "runtime.compacting_session": "กำลังย่อเซสชันที่กำลังใช้งาน...", + "runtime.compact_summary_missing": "ผู้ให้บริการไม่ได้ส่งสรุปการส่งต่องานแบบย่อที่ใช้งานได้กลับมา", + "runtime.session_compacted": "ย่อเซสชันสำเร็จแล้ว และสลับไปที่ {session_name} ({session_id})" } diff --git a/src/coding_agent_telegram/resources/locales/zh-HK.json b/src/coding_agent_telegram/resources/locales/zh-HK.json index 8b8fa4c..ad528c1 100644 --- a/src/coding_agent_telegram/resources/locales/zh-HK.json +++ b/src/coding_agent_telegram/resources/locales/zh-HK.json @@ -207,6 +207,6 @@ "bot.command.compact": "壓縮目前使用中的工作階段", "status.usage_compact": "用法:/compact", "runtime.compacting_session": "正在壓縮目前使用中的工作階段...", - "runtime.compact_summary_missing": "供應方 沒有返回可用的壓縮交接摘要。", + "runtime.compact_summary_missing": "供應方沒有返回可用的壓縮交接摘要。", "runtime.session_compacted": "工作階段壓縮成功。已切換到 {session_name} ({session_id})。" } From 8f6ad7b168d4e1e9d60d14e08f098fe9967118a8 Mon Sep 17 00:00:00 2001 From: DCHA <426225+daocha@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:02:42 +0800 Subject: [PATCH 5/5] Add /diff /pull commands in README (#46) Co-authored-by: DCHA Agent <259406208+dcha-agent@users.noreply.github.com> --- README.de.md | 8 ++++++++ README.fr.md | 8 ++++++++ README.ja.md | 8 ++++++++ README.ko.md | 8 ++++++++ README.md | 8 ++++++++ README.nl.md | 8 ++++++++ README.th.md | 10 +++++++++- README.vi.md | 8 ++++++++ README.zh-CN.md | 8 ++++++++ README.zh-HK.md | 8 ++++++++ README.zh-TW.md | 8 ++++++++ 11 files changed, 89 insertions(+), 1 deletion(-) diff --git a/README.de.md b/README.de.md index faf1f62..343f01e 100644 --- a/README.de.md +++ b/README.de.md @@ -252,6 +252,14 @@ Der Bot akzeptiert derzeit: /commit <git commands> Geprüfte git commit-bezogene Befehle im Projekt der aktiven Session ausführen. Nur verfügbar, wenn ENABLE_COMMIT_COMMAND=true. Schreibende Git-Befehle erfordern ein vertrauenswürdiges Projekt. + + /diff + Geänderte Dateinamen im Projekt der aktiven Session anzeigen, getrennt nach versionierten und nicht versionierten Dateien. Für versionierte Dateien gibt es Inline-Buttons zum Öffnen des jeweiligen Diffs. + + + /pull + Nach Bestätigung origin in den Branch der aktiven Session pullen. Wenn zutreffend, aktualisiert der Bot zusätzlich den Standard-Branch. + /push origin <branch> für die aktuelle aktive Session pushen. Der Bot fragt vor dem Push nach einer Bestätigung. diff --git a/README.fr.md b/README.fr.md index e2c87c2..c221f31 100644 --- a/README.fr.md +++ b/README.fr.md @@ -252,6 +252,14 @@ Le bot accepte actuellement : /commit <git commands> Exécuter des commandes liées à git commit validées dans le projet de la session active. Disponible uniquement si ENABLE_COMMIT_COMMAND=true. Les commandes Git mutantes exigent un projet trusted. + + /diff + Afficher les noms des fichiers modifiés du projet de la session active, séparés entre fichiers suivis et non suivis. Les fichiers suivis proposent des boutons inline pour ouvrir le diff de chaque fichier. + + + /pull + Après confirmation, exécuter un pull depuis origin pour la branche de la session active. Le bot rafraîchit aussi la branche par défaut si nécessaire. + /push Pousser origin <branch> pour la session active courante. Le bot demande une confirmation avant le push. diff --git a/README.ja.md b/README.ja.md index e1c2c86..410c329 100644 --- a/README.ja.md +++ b/README.ja.md @@ -252,6 +252,14 @@ https://api.telegram.org/bot/getUpdates /commit <git commands> アクティブなセッション の project 内で、検証済みの git commit 関連コマンドを実行します。ENABLE_COMMIT_COMMAND=true のときだけ利用できます。変更を伴う Git コマンドには trusted project が必要です。 + + /diff + アクティブなセッションの project で変更されたファイル名を、追跡済みファイルと未追跡ファイルに分けて表示します。追跡済みファイルには各ファイルの diff を開く inline ボタンが付きます。 + + + /pull + 確認後に、アクティブなセッションのブランチで origin から pull します。必要に応じてデフォルト ブランチも更新します。 + /push 現在の アクティブなセッション に対して origin <branch> を push します。push 前に bot が確認します。 diff --git a/README.ko.md b/README.ko.md index 8c0aa02..2143d8c 100644 --- a/README.ko.md +++ b/README.ko.md @@ -252,6 +252,14 @@ https://api.telegram.org/bot/getUpdates /commit <git commands> 활성 세션 project 안에서 검증된 git commit 관련 명령을 실행합니다. ENABLE_COMMIT_COMMAND=true 일 때만 사용할 수 있습니다. 변경성 Git 명령은 trusted project 가 필요합니다. + + /diff + 활성 세션 project 의 변경된 파일 이름을 추적 중인 파일과 추적되지 않은 파일로 나누어 표시합니다. 추적 중인 파일에는 각 파일의 diff 를 여는 inline 버튼이 함께 표시됩니다. + + + /pull + 확인 후 활성 세션 브랜치에 대해 origin 에서 pull 합니다. 필요하면 기본 브랜치도 함께 새로고칩니다. + /push 현재 활성 세션 에 대해 origin <branch> 를 push 합니다. push 전에 bot 이 확인합니다. diff --git a/README.md b/README.md index e355714..9cdd06b 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,14 @@ The bot currently accepts: /commit <git commands> Run validated git commit-related commands inside the active session project. Available only when ENABLE_COMMIT_COMMAND=true. Mutating git commands require a trusted project. + + /diff + Show changed filenames for the active session project, separated into tracked and untracked files. Tracked files include inline buttons to open per-file diffs. + + + /pull + Pull from origin for the active session branch after confirmation. The bot also refreshes the default branch when applicable. + /push Push origin <branch> for the current active session. The bot asks for confirmation before pushing. diff --git a/README.nl.md b/README.nl.md index 0294598..887bb7a 100644 --- a/README.nl.md +++ b/README.nl.md @@ -252,6 +252,14 @@ De bot accepteert momenteel: /commit <git commands> Voer gevalideerde git commit-gerelateerde commando’s uit binnen het project van de actieve sessie. Alleen beschikbaar als ENABLE_COMMIT_COMMAND=true. Muterende Git-commando’s vereisen een trusted project. + + /diff + Toon gewijzigde bestandsnamen voor het project van de actieve sessie, gescheiden in gevolgde en niet-gevolgde bestanden. Gevolgde bestanden krijgen inline-knoppen om de diff per bestand te openen. + + + /pull + Voer na bevestiging een pull uit vanaf origin voor de branch van de actieve sessie. De bot ververst ook de standaardbranch wanneer dat van toepassing is. + /push Push origin <branch> voor de huidige actieve sessie. De bot vraagt om bevestiging voordat hij pusht. diff --git a/README.th.md b/README.th.md index 2a2ab53..0f27168 100644 --- a/README.th.md +++ b/README.th.md @@ -250,7 +250,15 @@ https://api.telegram.org/bot/getUpdates /commit <git commands> - รันคำสั่งที่เกี่ยวข้องกับ git commit ซึ่งผ่านการตรวจสอบแล้วภายใน project ของ เซสชันที่ใช้งานอยู่ ใช้ได้เมื่อ ENABLE_COMMIT_COMMAND=true เท่านั้น คำสั่ง Git ที่มีการแก้ไขต้องใช้ project ที่ trusted + รันคำสั่งที่เกี่ยวข้องกับ git commit ซึ่งผ่านการตรวจสอบแล้วภายใน project ของเซสชันที่ใช้งานอยู่ ใช้ได้เมื่อ ENABLE_COMMIT_COMMAND=true เท่านั้น คำสั่ง Git ที่มีการแก้ไขต้องใช้ project ที่ trusted + + + /diff + แสดงรายชื่อไฟล์ที่เปลี่ยนใน project ของเซสชันที่ใช้งานอยู่ โดยแยกไฟล์ที่ติดตามอยู่กับไฟล์ที่ยังไม่ได้ติดตามออกจากกัน ไฟล์ที่ติดตามอยู่จะมีปุ่ม inline สำหรับเปิด diff ของแต่ละไฟล์ + + + /pull + ดึงจาก origin สำหรับ branch ของเซสชันที่ใช้งานอยู่หลังจากยืนยันแล้ว และ bot จะรีเฟรช default branch ให้ด้วยเมื่อเกี่ยวข้อง /push diff --git a/README.vi.md b/README.vi.md index 22cbc12..f7cd261 100644 --- a/README.vi.md +++ b/README.vi.md @@ -252,6 +252,14 @@ Hiện tại bot chấp nhận: /commit <git commands> Chạy các lệnh liên quan đến git commit đã được kiểm tra trong project của phiên hoạt động. Chỉ có khi ENABLE_COMMIT_COMMAND=true. Các lệnh Git có thay đổi yêu cầu project đã trusted. + + /diff + Hiển thị tên các tệp đã thay đổi trong project của phiên đang hoạt động, tách riêng tệp đã theo dõi và tệp chưa theo dõi. Các tệp đã theo dõi có nút inline để mở diff của từng tệp. + + + /pull + Sau khi xác nhận, thực hiện pull từ origin cho branch của phiên đang hoạt động. Bot cũng làm mới branch mặc định khi cần. + /push Push origin <branch> cho phiên hoạt động hiện tại. Bot sẽ hỏi xác nhận trước khi push. diff --git a/README.zh-CN.md b/README.zh-CN.md index 075ef20..bbb35fc 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -252,6 +252,14 @@ bot 当前接受: /commit <git commands> 在活动会话的项目内执行已校验的 git commit 相关命令。仅当 ENABLE_COMMIT_COMMAND=true 时可用。会修改内容的 Git 命令要求项目已 trusted。 + + /diff + 显示活动会话项目里的已变更文件名,并分开列出已跟踪文件与未跟踪文件。已跟踪文件会附带 inline 按钮,可直接打开单个文件的 diff。 + + + /pull + 确认后,从 origin 拉取活动会话当前分支。适用时,bot 也会一并刷新默认分支。 + /push 为当前活动会话执行 origin <branch> push。push 前 bot 会要求确认。 diff --git a/README.zh-HK.md b/README.zh-HK.md index 2ac553e..f787b8a 100644 --- a/README.zh-HK.md +++ b/README.zh-HK.md @@ -252,6 +252,14 @@ bot 目前接受: /commit <git commands> 在作用中工作階段的專案內執行已驗證的 git commit 相關指令。只在 ENABLE_COMMIT_COMMAND=true 時可用。會修改內容的 Git 指令要求專案已 trusted。 + + /diff + 顯示作用中工作階段專案內已變更的檔案名稱,並分開列出已追蹤與未追蹤檔案。已追蹤檔案會附帶 inline 按鈕,可直接開啟單一檔案的 diff。 + + + /pull + 確認後,從 origin 拉取作用中工作階段目前的分支。適用時,bot 也會一併重新整理預設分支。 + /push 為目前作用中工作階段執行 origin <branch> push。push 前 bot 會要求確認。 diff --git a/README.zh-TW.md b/README.zh-TW.md index af8c7ee..e8a5d40 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -252,6 +252,14 @@ bot 目前接受: /commit <git commands> 在作用中工作階段的專案內執行已驗證的 git commit 相關指令。僅在 ENABLE_COMMIT_COMMAND=true 時可用。會修改內容的 Git 指令要求專案已 trusted。 + + /diff + 顯示作用中工作階段專案內已變更的檔案名稱,並分開列出已追蹤與未追蹤檔案。已追蹤檔案會附帶 inline 按鈕,可直接開啟單一檔案的 diff。 + + + /pull + 確認後,從 origin 拉取作用中工作階段目前的分支。適用時,bot 也會一併重新整理預設分支。 + /push 為目前作用中工作階段執行 origin <branch> push。push 前 bot 會要求確認。