From a633b4f3266593d3101ec03e6519ca0abad8c33c Mon Sep 17 00:00:00 2001 From: digua Date: Wed, 11 Mar 2026 22:07:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E7=B9=81=E4=BD=93?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E5=92=8C=E6=97=A5=E8=AF=AD=E5=9B=BD=E9=99=85?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelogs_ja.json | 530 ++++++++++++++++++ docs/changelogs_tw.json | 530 ++++++++++++++++++ electron/main/ai/summary/index.ts | 6 +- electron/main/ai/tools/utils/format.ts | 2 +- electron/main/i18n/index.ts | 25 +- electron/main/i18n/locales/ja-JP.ts | 346 ++++++++++++ electron/main/i18n/locales/zh-TW.ts | 346 ++++++++++++ electron/main/nlp/segmenter.ts | 27 +- electron/main/nlp/stopwords.ts | 43 +- electron/main/nlp/types.ts | 2 +- electron/main/worker/query/nlp.ts | 2 +- skills/sync-changelog/SKILL.md | 35 +- skills/sync-changelog/agents/openai.yaml | 4 +- .../references/japanese-release-style.md | 33 ++ .../traditional-chinese-release-style.md | 31 + .../scripts/commit_release_changelogs.sh | 15 +- .../scripts/sync_to_chatlab_fun.sh | 18 +- src/assets/docs/agreement_ja.md | 39 ++ src/assets/docs/agreement_zh_tw.md | 39 ++ src/components/UI/DatePicker.vue | 6 +- .../analysis/AIChat/ChatMessage.vue | 2 +- src/components/analysis/AITab.vue | 2 +- .../common/settings/AI/AIModelEditModal.vue | 2 +- .../common/settings/BasicSettingsTab.vue | 5 +- src/config/prompts.ts | 7 +- src/i18n/index.ts | 19 +- src/i18n/locales/en-US/home.json | 4 + src/i18n/locales/ja-JP/ai.json | 301 ++++++++++ src/i18n/locales/ja-JP/analysis.json | 157 ++++++ src/i18n/locales/ja-JP/common.json | 129 +++++ src/i18n/locales/ja-JP/home.json | 124 ++++ src/i18n/locales/ja-JP/index.ts | 27 + src/i18n/locales/ja-JP/layout.json | 34 ++ src/i18n/locales/ja-JP/members.json | 84 +++ src/i18n/locales/ja-JP/providers.json | 75 +++ src/i18n/locales/ja-JP/quotes.json | 128 +++++ src/i18n/locales/ja-JP/records.json | 87 +++ src/i18n/locales/ja-JP/settings.json | 379 +++++++++++++ src/i18n/locales/ja-JP/tools.json | 49 ++ src/i18n/locales/ja-JP/views.json | 82 +++ src/i18n/locales/zh-CN/home.json | 4 + src/i18n/locales/zh-TW/ai.json | 301 ++++++++++ src/i18n/locales/zh-TW/analysis.json | 157 ++++++ src/i18n/locales/zh-TW/common.json | 129 +++++ src/i18n/locales/zh-TW/home.json | 124 ++++ src/i18n/locales/zh-TW/index.ts | 27 + src/i18n/locales/zh-TW/layout.json | 34 ++ src/i18n/locales/zh-TW/members.json | 84 +++ src/i18n/locales/zh-TW/providers.json | 75 +++ src/i18n/locales/zh-TW/quotes.json | 128 +++++ src/i18n/locales/zh-TW/records.json | 87 +++ src/i18n/locales/zh-TW/settings.json | 379 +++++++++++++ src/i18n/locales/zh-TW/tools.json | 49 ++ src/i18n/locales/zh-TW/views.json | 82 +++ src/i18n/types.ts | 53 +- src/pages/home/components/AgreementModal.vue | 13 +- src/pages/home/components/ChangelogModal.vue | 9 +- src/pages/home/components/HomeFooter.vue | 20 +- src/pages/home/components/ImportArea.vue | 3 +- .../manage/components/BatchManageTab.vue | 2 +- src/stores/prompt.ts | 5 +- src/stores/settings.ts | 8 +- 62 files changed, 5470 insertions(+), 79 deletions(-) create mode 100644 docs/changelogs_ja.json create mode 100644 docs/changelogs_tw.json create mode 100644 electron/main/i18n/locales/ja-JP.ts create mode 100644 electron/main/i18n/locales/zh-TW.ts create mode 100644 skills/sync-changelog/references/japanese-release-style.md create mode 100644 skills/sync-changelog/references/traditional-chinese-release-style.md create mode 100644 src/assets/docs/agreement_ja.md create mode 100644 src/assets/docs/agreement_zh_tw.md create mode 100644 src/i18n/locales/ja-JP/ai.json create mode 100644 src/i18n/locales/ja-JP/analysis.json create mode 100644 src/i18n/locales/ja-JP/common.json create mode 100644 src/i18n/locales/ja-JP/home.json create mode 100644 src/i18n/locales/ja-JP/index.ts create mode 100644 src/i18n/locales/ja-JP/layout.json create mode 100644 src/i18n/locales/ja-JP/members.json create mode 100644 src/i18n/locales/ja-JP/providers.json create mode 100644 src/i18n/locales/ja-JP/quotes.json create mode 100644 src/i18n/locales/ja-JP/records.json create mode 100644 src/i18n/locales/ja-JP/settings.json create mode 100644 src/i18n/locales/ja-JP/tools.json create mode 100644 src/i18n/locales/ja-JP/views.json create mode 100644 src/i18n/locales/zh-TW/ai.json create mode 100644 src/i18n/locales/zh-TW/analysis.json create mode 100644 src/i18n/locales/zh-TW/common.json create mode 100644 src/i18n/locales/zh-TW/home.json create mode 100644 src/i18n/locales/zh-TW/index.ts create mode 100644 src/i18n/locales/zh-TW/layout.json create mode 100644 src/i18n/locales/zh-TW/members.json create mode 100644 src/i18n/locales/zh-TW/providers.json create mode 100644 src/i18n/locales/zh-TW/quotes.json create mode 100644 src/i18n/locales/zh-TW/records.json create mode 100644 src/i18n/locales/zh-TW/settings.json create mode 100644 src/i18n/locales/zh-TW/tools.json create mode 100644 src/i18n/locales/zh-TW/views.json diff --git a/docs/changelogs_ja.json b/docs/changelogs_ja.json new file mode 100644 index 0000000..4877c6b --- /dev/null +++ b/docs/changelogs_ja.json @@ -0,0 +1,530 @@ +[ + { + "version": "0.12.1", + "date": "2026-02-27", + "summary": "チャット履歴の前処理とデバッグ機能を追加し、Agent/LLM アーキテクチャを再構成。あわせて国際化と Windows テーマ表示の不整合を修正しました。", + "changes": [ + { + "type": "feat", + "items": [ + "チャット履歴の前処理パイプラインを追加", + "前処理設定画面と設定管理機能を追加", + "Agent でセッション単位のコンテキストタイムラインと実行状態を表示", + "AI デバッグモードを追加し、ログの可観測性を向上" + ] + }, + { + "type": "fix", + "items": ["英語設定時に一部 UI が未翻訳だった問題を修正", "Windows で overlay 色を動的更新した際にテーマ表示が揃わない問題を修正"] + }, + { + "type": "refactor", + "items": [ + "Agent の単一実装をモジュール構成へ分割", + "ツールシステムを AgentTool + TypeBox 構成へ再編し、i18n を補完", + "LLM アクセス層を統一し、pi-ai ベースへ集約", + "データフローと IPC 契約を見直し、フロントエンド側も対応", + "共有型を導入し、ChatStatusBar の国際化を改善", + "一部のチャートをプラグイン構成へ再編" + ] + }, + { + "type": "chore", + "items": [ + "過剰設計だった sessionLog モジュールを削除", + "@ai-sdk 関連依存と旧 LLM サービス実装を削除", + "ベクトルモデル設定の入口を一時的に非表示化", + "プロジェクト説明文を更新" + ] + }, + { + "type": "style", + "items": ["ESLint の自動修正を実行し、コードスタイルを統一"] + } + ] + }, + { + "version": "0.11.2", + "date": "2026-02-15", + "summary": "チャット履歴の取り込みと管理画面を改善し、複数プラットフォームの履歴互換性を強化しました。", + "changes": [ + { + "type": "feat", + "items": [ + "LINE と WhatsApp パーサーの形式互換性を強化", + "チャット履歴の判定レイヤーを改善し、ポーリング検出とフォールバック機構に対応", + "管理画面で Shift 複数選択に対応", + "管理画面でチャット要約数と AI チャット数を表示", + "ホーム画面のレイアウトを見直し、使えるスペースを拡大", + "Windows の右上コントロールバーの見た目を改善" + ] + }, + { + "type": "docs", + "items": ["プロジェクト文書を更新"] + } + ] + }, + { + "version": "0.11.0", + "date": "2026-02-13", + "summary": "Telegram の取り込みに対応し、増分インポート体験と国際化設定を改善。あわせて索引不整合や画面のちらつきも修正しました。", + "changes": [ + { + "type": "feat", + "items": [ + "AI 呼び出し、ログ、メインプロセス設定まわりの国際化対応を拡充", + "Telegram チャット履歴のインポートに対応", + "増分インポートの操作フローと関連文言を改善", + "利用規約リンクを開く際の体験を改善" + ] + }, + { + "type": "fix", + "items": [ + "増分インポート後に索引が無効になる問題を修正(resolve #81)", + "iPhone から書き出した WhatsApp 履歴を認識できない問題を修正(resolve #82)", + "チャット画面切り替え時の二重ちらつきを修正" + ] + }, + { + "type": "chore", + "items": ["TypeScript 設定を最適化", "i18n ビルド設定を調整", "skill 関連の開発設定を整理"] + } + ] + }, + { + "version": "0.10.0", + "date": "2026-02-11", + "summary": "やり取り頻度の分析機能を追加し、セッション検索の流れを改善。あわせて増分索引とデータベース走査まわりの問題を修正しました。", + "changes": [ + { + "type": "feat", + "items": ["やり取り頻度を分析できるビューを追加し、メンバー間の動きが見やすくなりました", "セッション検索のロジックと処理フローを改善"] + }, + { + "type": "fix", + "items": [ + "増分更新後にセッション索引の生成範囲が不正確になる問題を修正(fix #79)", + "移行やセッション走査時に非チャット用 SQLite ファイルを誤処理する問題を修正" + ] + }, + { + "type": "refactor", + "items": ["セッション検索モジュールを再構成し、保守性を向上"] + }, + { + "type": "chore", + "items": ["transformers 関連依存を削除し、開発設定を更新"] + } + ] + }, + { + "version": "0.9.4", + "date": "2026-02-08", + "summary": "期間絞り込みと AI 設定まわりを改善し、API Key のローカル暗号化に対応。加えて LINE 履歴の解析問題を修正しました。", + "changes": [ + { + "type": "feat", + "items": [ + "期間絞り込みの選択肢をより柔軟に拡充", + "API Key のローカル暗号化保存に対応", + "初回利用ユーザーには更新履歴を表示しないよう変更", + "AI チャット下部の設定ステータス表示を改善", + "データディレクトリ移行後にすぐ再起動できるよう対応" + ] + }, + { + "type": "fix", + "items": ["LINE チャット履歴の解析問題を修正"] + }, + { + "type": "docs", + "items": ["プロジェクト文書を更新"] + } + ] + }, + { + "version": "0.9.3", + "date": "2026-02-03", + "summary": "カスタムデータディレクトリに対応し、多数の既知問題を修正しました。", + "changes": [ + { + "type": "feat", + "items": [ + "設定画面にデータディレクトリ位置の設定項目を追加", + "データ保存先の移行ロジックを改善", + "ディレクトリ切り替え時の確認ダイアログを追加", + "パーサーロジックを改善(WeFlow / Echotrace)" + ] + }, + { + "type": "fix", + "items": [ + "Windows で大量メッセージを絞り込むとクラッシュする問題を修正", + "外部中継 API が tool_call を返した際に会話が異常終了する問題を修正", + "一部の WhatsApp 履歴を正しく認識できない問題を修正", + "管理画面のヘッダー階層表示の問題を修正" + ] + }, + { + "type": "refactor", + "items": ["session 検索モジュールを再構成", "移行ログ出力を改善"] + } + ] + }, + { + "version": "0.9.2", + "date": "2026-02-02", + "summary": "ランキングをグラフ表示へ刷新し、ワードクラウドやローカル AI 推論モデルを改善。履歴絞り込みと日付選択も見直し、起動後の主要ルート事前読込にも対応しました。", + "changes": [ + { + "type": "feat", + "items": [ + "ランキングをチャート表示へ刷新", + "ワードクラウドの見え方を改善", + "推論モデルを最適化", + "チャット履歴の検索と絞り込みの連携を改善", + "日付選択 UI の操作性を改善", + "起動後に主要ルートを先読み" + ] + }, + { + "type": "chore", + "items": ["preload をモジュール化", "analytics ロジックを改善", "ESLint を更新し、コード整形を実施"] + } + ] + }, + { + "version": "0.9.1", + "date": "2026-01-30", + "summary": "LINE 履歴の取り込み、バッチ管理、チャット検索に対応し、既知の問題もいくつか修正しました。", + "changes": [ + { + "type": "feat", + "items": [ + "一括管理を追加し、まとめて削除・結合に対応", + "チャット検索に対応", + "LINE チャット履歴のインポートに対応", + "WeFlow が出力する JSON 形式に対応", + "メンバー一覧をバックエンド分页読み込みへ変更", + "一部文言を改善" + ] + }, + { + "type": "fix", + "items": ["Windows 更新時に Worker が残ってアプリを閉じられない問題を修正"] + } + ] + }, + { + "version": "0.9.0", + "date": "2026-01-28", + "summary": "NLP 分かち書きに対応し、名言集タブにワードクラウドを追加。Views タブでより多くのグラフを表示できるようになり、システムプロキシ追従にも対応しました。", + "changes": [ + { + "type": "feat", + "items": [ + "ユーザーセレクターの性能を改善し、仮想リスト読み込みに対応", + "ランキングを Views タブへ移動", + "分かち書きを導入し、ワードクラウド用のサブタブを追加", + "グループチャットタブの文言を改善", + "ネットワークプロキシがシステム設定に追従", + "更新履歴の表示判定ロジックを改善" + ] + }, + { + "type": "style", + "items": ["Markdown 表示スタイルを改善"] + } + ] + }, + { + "version": "0.8.0", + "date": "2026-01-26", + "summary": "セッション要約とベクトル検索を追加し、更新後のリリースノート表示や一部 UI 体験も改善。あわせて既知の問題を修正しました。", + "changes": [ + { + "type": "feat", + "items": [ + "チャットセッションに要約機能を追加", + "セッション要約の一括生成ロジックを追加", + "ベクトルモデル設定と関連検索に対応", + "チャット履歴の取り込み失敗時に、より詳しいログを記録", + "新バージョン更新後に更新履歴を自動表示", + "ホームに共通リンク付き Footer を追加", + "サイドバーから Help & Feedback を削除" + ] + }, + { + "type": "fix", + "items": ["shuakami-jsonl の解析エラーを修正(fix #50)"] + } + ] + }, + { + "version": "0.7.0", + "date": "2026-01-23", + "summary": "AI チャット体験と更新処理を改善し、チャート基盤を chart.js から ECharts へ移行しました。", + "changes": [ + { + "type": "feat", + "items": [ + "更新ロジックを改善", + "AI チャットのエラーログを改善", + "チャット下部からモデルを素早く切り替え可能に", + "既定プロンプトを見直し、少しユーモアを加味", + "chart.js を ECharts に置き換え", + "登録規約ロジックを削除" + ] + } + ] + }, + { + "version": "0.6.0", + "date": "2026-01-21", + "summary": "AI SDK を導入して AI チャットの安定性を高め、思考内容ブロックを追加。あわせて一部スタイルも調整しました。", + "changes": [ + { + "type": "feat", + "items": [ + "ログ位置を特定しやすくする機能を追加", + "AI SDK を導入", + "思考内容ブロックを追加", + "ホーム上部のドラッグ領域にグローバルモーダルが隠れる問題を解消", + "Windows 右上の閉じるボタンの見た目を改善" + ] + } + ] + }, + { + "version": "0.5.2", + "date": "2026-01-20", + "summary": "結合インポートに対応し、いくつかの問題を修正しました。", + "changes": [ + { + "type": "feat", + "items": ["結合インポートに対応", "メインパネルにチャット履歴の開始日時と終了日時を表示", "ドラッグ&ドロップ領域を改善"] + }, + { + "type": "fix", + "items": [ + "macOS x64 ビルド問題を解消するためビルド設定を改善", + "Windows のメッセージビューアで閉じるボタンの見た目が崩れる問題を修正", + "macOS パッケージング時は対象アーキテクチャ上でビルドが必要なよう修正(fixes #36)" + ] + } + ] + }, + { + "version": "0.5.1", + "date": "2026-01-16", + "summary": "いくつかの問題を修正しました。", + "changes": [ + { + "type": "feat", + "items": ["文言を改善"] + }, + { + "type": "fix", + "items": ["Windows でアプリを閉じてもプロセスが残る問題を修正(#33)", "数値入力欄の不具合を修正(resolve #34)"] + } + ] + }, + { + "version": "0.5.0", + "date": "2026-01-14", + "summary": "Instagram 履歴の取り込みに対応し、ホームでは一括インポート、チャット画面では増分インポートが使えるようになりました。", + "changes": [ + { + "type": "feat", + "items": [ + "Instagram チャット履歴のインポートに対応", + "各種ロジックを改善", + "システムプロンプトのプリセット機能を改善", + "増分インポートに対応", + "一括インポートに対応", + "スタイルを改善", + "Windows でネイティブウィンドウ操作とテーマ同期に対応(#31)" + ] + }, + { + "type": "chore", + "items": ["componenst.d.ts を削除"] + } + ] + }, + { + "version": "0.4.1", + "date": "2026-01-13", + "summary": "見た目と操作まわりを中心に改善しました。", + "changes": [ + { + "type": "feat", + "items": [ + "プロンプトのプレビューに対応", + "AI チャットのステータスバーを改善", + "マイグレーションテーブルのロジックを改善", + "サイドバーでアバター表示に対応", + "スタイルを改善", + "ネイティブウィンドウコントロールバーを置き換え", + "グローバル背景色を改善", + "アプリ終了時に Worker をクリーンアップ" + ] + }, + { + "type": "fix", + "items": ["テーマ設定の「システムに従う」が効かない問題を修正", "アップデートダイアログのレイアウト崩れを修正"] + } + ] + }, + { + "version": "0.4.0", + "date": "2026-01-12", + "summary": "shuakami-jsonl の取り込みに対応し、AI チャットをより省 Token 化。取り込み時のセッション索引生成とビューアからの快速ジャンプにも対応し、アップデート高速化ミラーも追加しました。", + "changes": [ + { + "type": "feat", + "items": [ + "shuakami-jsonl に対応", + "Loading 表示を改善", + "カスタムフィルターを追加", + "プリセット文言システムを再構成し、共通プリセットに対応", + "Token 節約のためシステムプロンプトを簡潔化", + "セッション関連の function calling を追加", + "メッセージから前後文脈へジャンプする処理を追加", + "チャット履歴ビューアでセッション索引表示と高速ジャンプに対応", + "設定ダイアログを再構成し、セッション索引設定を追加", + "チャット履歴取り込み時にセッション索引を生成", + "設定ダイアログを再構成", + "基本コンポーネントの操作スタイルを改善", + "ホーム画面の見た目を改善", + "アップデート高速化ロジックを改善", + "高速化ミラーを追加" + ] + } + ] + }, + { + "version": "0.3.1", + "date": "2026-01-09", + "summary": "Discord インポートに対応し、各パーサーで返信メッセージを取り込めるよう改善。保存先もより標準的な場所へ移し、取り込み診断も詳しくなりました。", + "changes": [ + { + "type": "feat", + "items": [ + "テーブル更新処理をメインプロセス側へ移動", + "自動更新確認時に beta 版を無視", + "データ保存先を userData 配下へ移動", + "各パーサーで返信メッセージ取り込みを再対応", + "プラットフォームメッセージ ID と返信 ID に対応し、同時にテーブル移行も実施", + "Tyrrrz/DiscordChatExporter 形式の取り込みに対応", + "member テーブルでロールに対応", + "ChatLab 形式の検出挙動を強化", + "クリック取り込みとドラッグ取り込みの挙動を統一", + "より詳細な形式診断に対応" + ] + }, + { + "type": "fix", + "items": ["一部ユーザーで platformId が空になる問題を修正"] + } + ] + }, + { + "version": "0.3.0", + "date": "2026-01-08", + "summary": "国際化対応をひと通り整え、中国語と英語の切り替えに対応。あわせていくつかの機能も改善しました。", + "changes": [ + { + "type": "feat", + "items": [ + "SQL ラボのエクスポートに対応", + "AI チャットのエクスポートに対応", + "国際化対応をひと通り完了", + "AI モデルエラー時に明示的なエラーを表示", + "SQL 結果からメッセージビューアへジャンプ可能に", + "システム prompt を改善し、prompt マーケットに対応" + ] + } + ] + }, + { + "version": "0.2.0", + "date": "2025-12-29", + "summary": "プロキシ設定に対応し、インポート時のエラーログ表示を追加。あわせて UI 操作と一部機能を改善しました。", + "changes": [ + { + "type": "feat", + "items": [ + "メッセージマネージャーでシステムメッセージ表示に対応", + "インポート処理を改善し、エラー時にログを表示", + "WhatsApp の英語形式メッセージ取り込みに対応", + "プロキシ設定に対応(resolve #7)", + "AI モデル画面の操作性を改善", + "ユーザー設定 API のチュートリアルを追加", + "無料の GLM モデル 2 種を追加し、Doubao プロバイダーと最新モデルを追加", + "AI 応答で think 内容を出力しないよう調整" + ] + } + ] + }, + { + "version": "0.1.3", + "date": "2025-12-25", + "summary": "いくつかの問題を修正しました。", + "changes": [ + { + "type": "fix", + "items": ["Echotrace パーサーの不具合を修正"] + } + ] + }, + { + "version": "0.1.2", + "date": "2025-12-25", + "summary": "ダークモードに対応し、AI チャットのシステムプロンプトでユーザー情報を渡せるようになりました。", + "changes": [ + { + "type": "feat", + "items": [ + "AI チャットのシステムプロンプトでユーザー情報を受け渡し可能に", + "チャット履歴ビューア右側に Owner を表示", + "データベース更新に対応", + "メンバータブで Owner 視点の設定に対応", + "ダークモードに対応" + ] + }, + { + "type": "fix", + "items": ["個人チャットをグループチャットと誤判定する問題を修正"] + } + ] + }, + { + "version": "0.1.1", + "date": "2025-12-24", + "summary": "WhatsApp チャット履歴の取り込みに対応し、旧版 QQ 討論組形式の分析も可能になりました。", + "changes": [ + { + "type": "feat", + "items": ["チャットセッション下部に Token 使用量を表示", "WhatsApp ネイティブ形式メッセージに対応", "旧版 QQ txt 討論組形式に対応"] + }, + { + "type": "fix", + "items": ["メッセージマネージャーの z-index が低すぎる問題を修正"] + } + ] + }, + { + "version": "0.1.0", + "date": "2025-12-23", + "summary": "プロジェクトを公開し、初回リリースしました。", + "changes": [ + { + "type": "feat", + "items": ["init"] + } + ] + } +] diff --git a/docs/changelogs_tw.json b/docs/changelogs_tw.json new file mode 100644 index 0000000..5c68a67 --- /dev/null +++ b/docs/changelogs_tw.json @@ -0,0 +1,530 @@ +[ + { + "version": "0.12.1", + "date": "2026-02-27", + "summary": "新增聊天紀錄前處理與除錯能力,重構 Agent/LLM 架構,並修復國際化與 Windows 主題顯示問題。", + "changes": [ + { + "type": "feat", + "items": [ + "新增聊天紀錄前處理流程", + "新增前處理設定介面與設定管理能力", + "Agent 支援依會話顯示上下文時間軸與執行狀態", + "新增 AI 除錯模式並提升日誌可觀測性" + ] + }, + { + "type": "fix", + "items": ["修復英文設定下部分介面未完成國際化的問題", "修復 Windows 動態更新 overlay 色彩時主題不一致的問題"] + }, + { + "type": "refactor", + "items": [ + "將 Agent 單體實作拆分為模組化架構", + "將工具系統重構為 AgentTool + TypeBox 結構並補齊 i18n", + "統一 LLM 存取層,收斂為 pi-ai 方案", + "重構資料流方向與 IPC 協議,並完成前端適配", + "引入共享型別並優化 ChatStatusBar 國際化", + "將部分圖表重構為外掛化架構" + ] + }, + { + "type": "chore", + "items": [ + "移除過度設計的 sessionLog 模組", + "移除 @ai-sdk 相關依賴與舊版 LLM 服務實作", + "暫時隱藏向量模型設定入口", + "更新專案描述文案" + ] + }, + { + "type": "style", + "items": ["執行 ESLint 自動修正,統一程式碼風格"] + } + ] + }, + { + "version": "0.11.2", + "date": "2026-02-15", + "summary": "優化聊天紀錄匯入機制與管理頁體驗,並提升多平台聊天紀錄的相容性。", + "changes": [ + { + "type": "feat", + "items": [ + "強化 LINE 與 WhatsApp 解析器的格式相容性", + "優化聊天紀錄嗅探層,支援輪詢偵測與回退機制", + "管理頁支援 Shift 多選", + "管理頁新增顯示聊天摘要數量與 AI 對話數量", + "優化首頁版面,提升可用空間", + "優化 Windows 右上角控制列樣式" + ] + }, + { + "type": "docs", + "items": ["更新專案文件"] + } + ] + }, + { + "version": "0.11.0", + "date": "2026-02-13", + "summary": "支援 Telegram 匯入,優化增量匯入體驗,補齊國際化設定,並修復索引失效與頁面閃爍等問題。", + "changes": [ + { + "type": "feat", + "items": [ + "補齊 AI 呼叫、日誌與主進程設定的國際化支援", + "支援 Telegram 聊天紀錄匯入", + "優化增量匯入互動與相關文案", + "優化開啟協議時的互動體驗" + ] + }, + { + "type": "fix", + "items": [ + "修復增量匯入後索引失效的問題(resolve #81)", + "修復 WhatsApp 使用 iPhone 匯出後無法辨識的問題(resolve #82)", + "修復切換對話頁面時出現雙重閃爍的問題" + ] + }, + { + "type": "chore", + "items": ["優化 TypeScript 設定", "調整 i18n 建置設定", "優化技能相關工程設定"] + } + ] + }, + { + "version": "0.10.0", + "date": "2026-02-11", + "summary": "新增互動頻率分析能力,優化會話查詢流程,並修復增量索引與資料庫掃描相關問題。", + "changes": [ + { + "type": "feat", + "items": ["新增互動頻率分析視圖,更直觀地觀察成員互動趨勢", "優化會話查詢相關邏輯與處理流程"] + }, + { + "type": "fix", + "items": [ + "修復增量更新後會話索引產生範圍不準確的問題(fix #79)", + "修復遷移與會話掃描時誤處理非聊天 SQLite 檔案的問題" + ] + }, + { + "type": "refactor", + "items": ["重構會話查詢模組,提升查詢結構可維護性"] + }, + { + "type": "chore", + "items": ["移除 transformers 相關依賴並更新工程設定"] + } + ] + }, + { + "version": "0.9.4", + "date": "2026-02-08", + "summary": "優化時間篩選與 AI 設定體驗,新增 API Key 本機加密,並修復 LINE 聊天紀錄解析問題。", + "changes": [ + { + "type": "feat", + "items": [ + "時間篩選支援更多彈性選項", + "API Key 支援本機加密儲存", + "新用戶首次進入時不再顯示版本日誌", + "優化 AI 對話底部的設定狀態顯示", + "資料目錄遷移後支援立即重新啟動軟體" + ] + }, + { + "type": "fix", + "items": ["修復 LINE 聊天紀錄解析問題"] + }, + { + "type": "docs", + "items": ["更新專案文件"] + } + ] + }, + { + "version": "0.9.3", + "date": "2026-02-03", + "summary": "支援自訂資料目錄,並修復多項已知問題。", + "changes": [ + { + "type": "feat", + "items": [ + "設定中新增資料目錄位置選項", + "優化資料儲存目錄遷移邏輯", + "切換目錄時新增確認彈窗", + "優化解析邏輯(WeFlow / Echotrace)" + ] + }, + { + "type": "fix", + "items": [ + "修復 Windows 自訂篩選時訊息量過大導致崩潰的問題", + "修復第三方中轉 API 呼叫 tool_call 導致對話異常結束的問題", + "修復部分 WhatsApp 聊天紀錄無法正確辨識的問題", + "修復管理頁表頭層級顯示問題" + ] + }, + { + "type": "refactor", + "items": ["重構 session 查詢模組", "補強遷移日誌輸出"] + } + ] + }, + { + "version": "0.9.2", + "date": "2026-02-02", + "summary": "排行榜改為圖表呈現,優化詞雲與本機 AI 推理模型,改進聊天紀錄篩選與日期選擇器,並在啟動後預先載入關鍵路由。", + "changes": [ + { + "type": "feat", + "items": [ + "排行榜改為圖表展示", + "優化詞雲效果", + "優化推理模型", + "優化訊息會話搜尋與篩選的聯動體驗", + "優化日期選擇器互動", + "啟動後預先載入關鍵路由" + ] + }, + { + "type": "chore", + "items": ["將 preload 模組化拆分", "優化 analytics 邏輯", "升級 ESLint 並整理程式碼格式"] + } + ] + }, + { + "version": "0.9.1", + "date": "2026-01-30", + "summary": "支援 LINE 聊天紀錄匯入,新增批次管理與聊天搜尋,並修復一些已知問題。", + "changes": [ + { + "type": "feat", + "items": [ + "新增批次管理,支援批次刪除與合併", + "支援聊天搜尋", + "支援 LINE 聊天紀錄匯入", + "相容 WeFlow 匯出的 JSON 格式", + "成員列表改為後端分頁載入", + "優化部分文案" + ] + }, + { + "type": "fix", + "items": ["修復 Windows 更新時因 Worker 占用導致軟體無法關閉的問題"] + } + ] + }, + { + "version": "0.9.0", + "date": "2026-01-28", + "summary": "支援 NLP 分詞能力,語錄頁新增詞雲;新增視圖功能可展示更多圖表;支援系統代理跟隨,並優化部分頁面與樣式。", + "changes": [ + { + "type": "feat", + "items": [ + "優化使用者選擇器效能,支援虛擬載入", + "將排行榜移至視圖分頁", + "引入分詞能力,並新增詞雲子分頁", + "優化群聊頁分頁文案", + "網路代理支援跟隨系統代理", + "優化版本日誌顯示判斷邏輯" + ] + }, + { + "type": "style", + "items": ["優化 Markdown 渲染樣式"] + } + ] + }, + { + "version": "0.8.0", + "date": "2026-01-26", + "summary": "新增會話摘要與向量檢索能力,版本更新後會顯示更新內容,優化部分介面互動,並修復一些已知問題。", + "changes": [ + { + "type": "feat", + "items": [ + "聊天會話支援摘要功能", + "新增批次產生會話摘要邏輯", + "支援向量模型設定與相關檢索", + "匯入聊天紀錄報錯時記錄更詳細的日誌", + "每次更新到新版本後自動開啟版本日誌", + "首頁新增 Footer,顯示常用連結", + "側邊欄移除幫助與回饋" + ] + }, + { + "type": "fix", + "items": ["修復 shuakami-jsonl 解析錯誤(fix #50)"] + } + ] + }, + { + "version": "0.7.0", + "date": "2026-01-23", + "summary": "優化 AI 對話體驗,改進更新流程,並以 ECharts 取代 chart.js。", + "changes": [ + { + "type": "feat", + "items": [ + "優化更新流程", + "補強 AI 對話錯誤日誌", + "聊天底部支援快速切換對話模型", + "優化預設提示詞,增加一點幽默感", + "以 ECharts 取代 chart.js", + "移除註冊協議邏輯" + ] + } + ] + }, + { + "version": "0.6.0", + "date": "2026-01-21", + "summary": "接入 AI SDK 提升 AI 對話穩定性,新增思考內容區塊,並優化部分樣式。", + "changes": [ + { + "type": "feat", + "items": [ + "新增定位日誌功能", + "接入 AI SDK", + "新增思考內容區塊", + "解決全域彈窗被首頁上方拖曳區域遮擋的問題", + "優化 Windows 右上角關閉按鈕樣式" + ] + } + ] + }, + { + "version": "0.5.2", + "date": "2026-01-20", + "summary": "支援合併匯入,並修復一些問題。", + "changes": [ + { + "type": "feat", + "items": ["支援合併匯入", "主面板顯示聊天紀錄起訖時間", "優化拖曳區域"] + }, + { + "type": "fix", + "items": [ + "優化建置設定以修復 macOS x64 編譯問題", + "修復訊息紀錄檢視器在 Windows 下的關閉按鈕樣式問題", + "macOS 打包時需在對應架構上編譯(fixes #36)" + ] + } + ] + }, + { + "version": "0.5.1", + "date": "2026-01-16", + "summary": "修復一些問題。", + "changes": [ + { + "type": "feat", + "items": ["優化文案"] + }, + { + "type": "fix", + "items": ["修復 Windows 下關閉軟體後程序未退出的問題(#33)", "修復數字輸入框 BUG(resolve #34)"] + } + ] + }, + { + "version": "0.5.0", + "date": "2026-01-14", + "summary": "支援 Instagram 聊天紀錄匯入;首頁支援批次匯入;聊天頁支援增量匯入。", + "changes": [ + { + "type": "feat", + "items": [ + "支援 Instagram 聊天紀錄匯入", + "優化整體邏輯", + "優化系統提示詞預設功能", + "支援增量匯入", + "支援批次匯入", + "優化樣式", + "Windows 端支援原生視窗控制並實作主題同步(#31)" + ] + }, + { + "type": "chore", + "items": ["移除 componenst.d.ts"] + } + ] + }, + { + "version": "0.4.1", + "date": "2026-01-13", + "summary": "優化部分樣式與互動體驗。", + "changes": [ + { + "type": "feat", + "items": [ + "提示詞支援預覽", + "優化 AI 對話狀態列", + "優化遷移表邏輯", + "側邊欄支援顯示頭像", + "優化樣式", + "替換原生視窗控制列", + "優化全域背景色", + "關閉軟體時清理 Worker" + ] + }, + { + "type": "fix", + "items": ["修復主題模式設定跟隨系統不生效的問題", "修復更新彈窗提示內容排版問題"] + } + ] + }, + { + "version": "0.4.0", + "date": "2026-01-12", + "summary": "匯入支援 shuakami-jsonl 格式;AI 對話更省 Token;匯入聊天紀錄時可建立會話索引,查看器也支援依索引快速跳轉;軟體更新支援加速鏡像。", + "changes": [ + { + "type": "feat", + "items": [ + "相容 shuakami-jsonl", + "優化 Loading 體驗", + "新增自訂篩選", + "重構預設詞系統,支援通用預設詞", + "精簡系統提示詞以節省 Token", + "新增會話相關 function calling 呼叫", + "處理訊息跳轉到上下文的邏輯", + "聊天紀錄檢視器支援查看會話索引與快速跳轉", + "重構設定彈窗,新增會話索引設定", + "匯入聊天紀錄時支援產生會話索引", + "重構設定彈窗", + "優化基礎元件互動樣式", + "優化首頁樣式", + "優化更新加速邏輯", + "新增加速鏡像" + ] + } + ] + }, + { + "version": "0.3.1", + "date": "2026-01-09", + "summary": "已適配 Discord 匯入;各解析器支援回覆類型匯入;軟體儲存目錄遷移至更標準的位置;匯入支援角色;匯入報錯提供更詳細的診斷與提示,並帶來一些細節優化。", + "changes": [ + { + "type": "feat", + "items": [ + "資料表升級改由主進程執行", + "自動檢查更新時忽略 beta 版本", + "將資料儲存目錄遷移到 userData 下", + "各解析器重新支援回覆訊息匯入", + "支援平台訊息 ID 與回覆 ID,並同步進行資料表遷移", + "支援 Tyrrrz/DiscordChatExporter 訊息格式匯入", + "member 表支援角色", + "強化 ChatLab 格式偵測行為", + "確保點擊匯入與拖曳匯入的邏輯一致", + "支援更詳細的格式診斷" + ] + }, + { + "type": "fix", + "items": ["修復部分使用者 platformId 為空的情況"] + } + ] + }, + { + "version": "0.3.0", + "date": "2026-01-08", + "summary": "完成完整國際化支援,支援中英文切換,並進一步優化部分功能。", + "changes": [ + { + "type": "feat", + "items": [ + "SQL 實驗室支援匯出", + "AI 對話支援匯出", + "完成最終國際化", + "AI 模型出錯時顯示明確錯誤", + "SQL 結果支援跳轉到訊息檢視器", + "優化系統 prompt,支援 prompt 市集" + ] + } + ] + }, + { + "version": "0.2.0", + "date": "2025-12-29", + "summary": "支援代理設定;匯入時支援顯示錯誤日誌;優化部分介面互動,並帶來一些功能更新。", + "changes": [ + { + "type": "feat", + "items": [ + "訊息管理器支援顯示系統訊息", + "優化匯入流程,錯誤時會顯示匯入日誌", + "WhatsApp 支援英文格式訊息匯入", + "支援設定代理(resolve #7)", + "優化 AI 模型介面互動", + "新增使用者設定 API 教學", + "新增 2 個免費 GLM 模型,並加入豆包服務商與最新模型", + "AI 回覆不再輸出 think 內容" + ] + } + ] + }, + { + "version": "0.1.3", + "date": "2025-12-25", + "summary": "修復一些問題。", + "changes": [ + { + "type": "fix", + "items": ["修復 Echotrace 解析器錯誤"] + } + ] + }, + { + "version": "0.1.2", + "date": "2025-12-25", + "summary": "支援深色模式;AI 對話中的系統提示詞可帶入使用者身分。", + "changes": [ + { + "type": "feat", + "items": [ + "AI 對話中的系統提示詞可帶入使用者身分", + "聊天紀錄檢視器中,Owner 顯示在右側", + "支援資料庫升級", + "成員分頁支援設定 Owner 視角", + "支援深色模式" + ] + }, + { + "type": "fix", + "items": ["修復將私聊誤判為群聊的問題"] + } + ] + }, + { + "version": "0.1.1", + "date": "2025-12-24", + "summary": "已適配 WhatsApp 聊天紀錄匯入;支援舊版 QQ 討論組格式分析。", + "changes": [ + { + "type": "feat", + "items": ["聊天會話底部顯示 Token 消耗", "支援 WhatsApp 原生格式訊息", "支援舊版 QQ txt 討論組格式"] + }, + { + "type": "fix", + "items": ["修復訊息管理器層級過低的問題"] + } + ] + }, + { + "version": "0.1.0", + "date": "2025-12-23", + "summary": "專案正式開源發布。", + "changes": [ + { + "type": "feat", + "items": ["init"] + } + ] + } +] diff --git a/electron/main/ai/summary/index.ts b/electron/main/ai/summary/index.ts index 1d0ac01..54cefb1 100644 --- a/electron/main/ai/summary/index.ts +++ b/electron/main/ai/summary/index.ts @@ -304,7 +304,7 @@ function splitIntoSegments( * 生成摘要的 Prompt */ function buildSummaryPrompt(content: string, lengthLimit: number, locale: string): string { - if (locale === 'zh-CN') { + if (locale.startsWith('zh')) { return `请用简洁的语言(${lengthLimit}字以内)总结以下对话的主要内容或话题。只输出摘要内容,不要添加任何前缀、解释或引号。 ${content}` @@ -318,7 +318,7 @@ ${content}` * 生成子摘要的 Prompt */ function buildSubSummaryPrompt(content: string, locale: string): string { - if (locale === 'zh-CN') { + if (locale.startsWith('zh')) { return `请用一句话(不超过50字)概括以下对话片段的主要内容。只输出摘要内容,不要添加任何前缀、解释或引号。 ${content}` @@ -333,7 +333,7 @@ ${content}` */ function buildMergePrompt(subSummaries: string[], lengthLimit: number, locale: string): string { const summaryList = subSummaries.map((s, i) => `${i + 1}. ${s}`).join('\n') - if (locale === 'zh-CN') { + if (locale.startsWith('zh')) { return `以下是一段对话的多个片段摘要,请将它们合并成一个完整的总结(${lengthLimit}字以内)。只输出摘要内容,不要添加任何前缀、解释或引号。 ${summaryList}` diff --git a/electron/main/ai/tools/utils/format.ts b/electron/main/ai/tools/utils/format.ts index cb57127..81614d0 100644 --- a/electron/main/ai/tools/utils/format.ts +++ b/electron/main/ai/tools/utils/format.ts @@ -3,7 +3,7 @@ */ export function isChineseLocale(locale?: string): boolean { - return locale === 'zh-CN' + return locale?.startsWith('zh') ?? false } export const i18nTexts = { diff --git a/electron/main/i18n/index.ts b/electron/main/i18n/index.ts index 2d701a7..e8b9a14 100644 --- a/electron/main/i18n/index.ts +++ b/electron/main/i18n/index.ts @@ -13,6 +13,8 @@ import * as path from 'path' import { getSettingsDir, ensureDir } from '../paths' import zhCN from './locales/zh-CN' import enUS from './locales/en-US' +import zhTW from './locales/zh-TW' +import jaJP from './locales/ja-JP' const LOCALE_FILE = 'locale.json' @@ -35,6 +37,17 @@ function saveLocale(lng: string): void { } } +/** + * 从系统 locale 探测应用 locale + */ +function detectSystemLocale(): string { + const sysLocale = app.getLocale() + if (sysLocale === 'zh-TW' || sysLocale === 'zh-Hant') return 'zh-TW' + if (sysLocale.startsWith('zh')) return 'zh-CN' + if (sysLocale.startsWith('ja')) return 'ja-JP' + return 'en-US' +} + /** * 初始化主进程国际化 * @@ -42,18 +55,15 @@ function saveLocale(lng: string): void { * 同时注册 IPC 监听器接收渲染进程的语言切换请求 */ export async function initLocale(): Promise { - let lng = 'en-US' // 默认回退 + let lng = 'en-US' try { const filePath = getLocaleFilePath() if (fs.existsSync(filePath)) { - // 读取用户保存的语言偏好 const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')) if (data.locale) lng = data.locale } else { - // 无配置文件,探测系统语言 - const sysLocale = app.getLocale() - lng = sysLocale.startsWith('zh') ? 'zh-CN' : 'en-US' + lng = detectSystemLocale() } } catch (e) { console.error('[i18n] Error loading locale config:', e) @@ -65,13 +75,14 @@ export async function initLocale(): Promise { resources: { 'zh-CN': { translation: zhCN }, 'en-US': { translation: enUS }, + 'zh-TW': { translation: zhTW }, + 'ja-JP': { translation: jaJP }, }, - interpolation: { escapeValue: false }, // Node 环境不需要防 XSS + interpolation: { escapeValue: false }, }) console.log(`[i18n] Initialized with locale: ${lng}`) - // 监听渲染进程的语言切换请求(补全半完成的 IPC 机制) ipcMain.on('locale:change', async (_event, newLocale: string) => { if (newLocale !== i18next.language) { await i18next.changeLanguage(newLocale) diff --git a/electron/main/i18n/locales/ja-JP.ts b/electron/main/i18n/locales/ja-JP.ts new file mode 100644 index 0000000..5c45c88 --- /dev/null +++ b/electron/main/i18n/locales/ja-JP.ts @@ -0,0 +1,346 @@ +/** + * メインプロセス日本語翻訳 + */ +export default { + // ===== 共通 ===== + common: { + error: 'エラー', + }, + + // ===== P0: アップデートダイアログ ===== + update: { + newVersionTitle: '新バージョン v{{version}} が見つかりました', + newVersionMessage: '新バージョン v{{version}} が見つかりました', + newVersionDetail: '今すぐダウンロードしてインストールしますか?', + downloadNow: '今すぐダウンロード', + cancel: 'キャンセル', + downloadComplete: 'ダウンロード完了', + readyToInstall: '新バージョンの準備ができました。今すぐインストールしますか?', + install: 'インストール', + remindLater: '後で通知', + installOnQuit: '後で(アプリ終了時に自動インストール)', + upToDate: '最新バージョンです', + }, + + // ===== P0: ファイル/ディレクトリダイアログ ===== + dialog: { + selectChatFile: 'チャット履歴ファイルを選択', + chatRecords: 'チャット履歴', + allFiles: 'すべてのファイル', + import: 'インポート', + selectDirectory: 'ディレクトリを選択', + selectFolder: 'フォルダーを選択', + selectFolderError: 'フォルダー選択中にエラーが発生しました:', + }, + + // ===== P1: データベースマイグレーション ===== + database: { + migrationV1Desc: 'meta テーブルに owner_id フィールドを追加', + migrationV1Message: '「Owner」機能に対応。メンバー一覧で自分の立場を設定できます', + migrationV2Desc: 'roles、reply_to_message_id、platform_message_id フィールドを追加', + migrationV2Message: 'メンバーロール、メッセージ返信関係、返信内容プレビューをサポート', + migrationV3Desc: 'セッションインデックス関連テーブル(chat_session、message_context)と session_gap_threshold フィールドを追加', + migrationV3Message: 'セッションのタイムライン表示と AI 拡張分析に対応', + integrityError: 'データベース構造が不完全です:meta テーブルがありません。このデータベースファイルを削除して再インポートすることをお勧めします。', + checkFailed: 'データベースチェックに失敗しました: {{error}}', + }, + + // ===== ツールシステム ===== + tools: { + notRegistered: 'ツール "{{toolName}}" は登録されていません', + }, + + // ===== P2: AI ツール説明(Function Calling) ===== + ai: { + tools: { + search_messages: { + desc: 'キーワードでグループチャット履歴を検索する。ユーザーが特定のトピックやキーワードに関連するチャット内容を探したい場合に使用する。時間範囲や送信者でメッセージをフィルタリングできる。分単位の精度で時間クエリをサポートする。', + params: { + keywords: '検索キーワードリスト。OR ロジックでいずれかのキーワードを含むメッセージにマッチする。送信者のみでフィルタリングする場合は空配列 [] を渡す', + sender_id: '送信者のメンバー ID。特定メンバーの送信メッセージをフィルタリングする。get_members ツールでメンバー ID を取得できる', + limit: '返却メッセージ数の上限。デフォルト 1000、最大 50000', + year: '指定年のメッセージをフィルタリング(例:2024)', + month: '指定月のメッセージをフィルタリング(1-12)。year と併用する必要がある', + day: '指定日のメッセージをフィルタリング(1-31)。year と month と併用する必要がある', + hour: '指定時間のメッセージをフィルタリング(0-23)。year、month、day と併用する必要がある', + start_time: '開始時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 14:00")。指定すると year/month/day/hour パラメータを上書きする', + end_time: '終了時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 18:30")。指定すると year/month/day/hour パラメータを上書きする', + }, + }, + get_recent_messages: { + desc: '指定期間内のグループチャットメッセージを取得する。「最近みんな何を話していた?」「X月にグループで何が話題だった?」などの概要的な質問に適している。分単位の精度で時間クエリをサポートする。', + params: { + limit: '返却メッセージ数の上限。デフォルト 100(Token を節約したい場合の目安。必要に応じて増やせる)', + year: '指定年のメッセージをフィルタリング(例:2024)', + month: '指定月のメッセージをフィルタリング(1-12)。year と併用する必要がある', + day: '指定日のメッセージをフィルタリング(1-31)。year と month と併用する必要がある', + hour: '指定時間のメッセージをフィルタリング(0-23)。year、month、day と併用する必要がある', + start_time: '開始時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 14:00")。指定すると year/month/day/hour パラメータを上書きする', + end_time: '終了時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 18:30")。指定すると year/month/day/hour パラメータを上書きする', + }, + }, + get_member_stats: { + desc: 'グループメンバーのアクティビティ統計データを取得する。「最もアクティブなのは誰?」「発言数が一番多いのは?」などの質問に適している。', + params: { + top_n: '上位 N 名のメンバーを返却。デフォルト 10', + }, + }, + get_time_stats: { + desc: 'グループチャットの時間分布統計を取得する。「いつが一番アクティブ?」「みんな何時にチャットしている?」などの質問に適している。', + params: { + type: '統計タイプ:hourly(時間別)、weekday(曜日別)、daily(日別)', + }, + }, + get_members: { + desc: 'グループメンバー一覧を取得する。メンバーの基本情報、別名、メッセージ統計を含む。「グループに誰がいる?」「○○の別名は?」「QQ 番号が xxx の人は?」などの質問に向いている。', + params: { + search: '任意の検索キーワード。メンバーのニックネーム、別名、QQ 番号で絞り込む', + limit: '返却メンバー数の上限。デフォルトは全件返却', + }, + }, + get_member_name_history: { + desc: 'メンバーのニックネーム変更履歴を取得する。「○○の以前の名前は?」「○○のニックネームの変遷」「○○の旧名」などの質問に適している。事前に get_members ツールでメンバー ID を取得する必要がある。', + params: { + member_id: 'メンバーのデータベース ID。get_members ツールで取得できる', + }, + }, + get_conversation_between: { + desc: '2 人のグループメンバー間の会話履歴を取得する。「A と B は何を話していた?」「2 人のやり取りを見たい」などの質問に向いている。事前に get_members でメンバー ID を取得する必要がある。分単位の時間指定にも対応する。', + params: { + member_id_1: '1人目のメンバーのデータベース ID', + member_id_2: '2人目のメンバーのデータベース ID', + limit: '返却メッセージ数の上限。デフォルト 100', + year: '指定年のメッセージをフィルタリング', + month: '指定月のメッセージをフィルタリング(1-12)。year と併用する必要がある', + day: '指定日のメッセージをフィルタリング(1-31)。year と month と併用する必要がある', + hour: '指定時間のメッセージをフィルタリング(0-23)。year、month、day と併用する必要がある', + start_time: '開始時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 14:00")。指定すると year/month/day/hour パラメータを上書きする', + end_time: '終了時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 18:30")。指定すると year/month/day/hour パラメータを上書きする', + }, + }, + get_message_context: { + desc: 'メッセージ ID に基づいて前後のコンテキストメッセージを取得する。特定のメッセージの前後のチャット内容を確認したいシーンに適している。例えば「このメッセージの前後で何を話していた?」「あるメッセージのコンテキストを見る」など。単一または複数のメッセージ ID をサポートする。', + params: { + message_ids: + 'コンテキストを取得するメッセージ ID リスト。単一 ID または複数 ID が可能。メッセージ ID は search_messages などのツールの返却結果から取得できる', + context_size: 'コンテキストサイズ。前後それぞれ何件のメッセージを取得するか。デフォルト 20', + }, + }, + search_sessions: { + desc: 'チャットセッション(会話セグメント)を検索する。セッションはメッセージの時間間隔に基づいて自動分割された会話単位である。特定トピックの議論を見つけたり、ある期間内の会話回数を把握するのに適している。マッチしたセッションリストと各セッションの最初の5件のメッセージプレビューを返却する。', + params: { + keywords: '任意の検索キーワードリスト。これらのキーワードを含むセッションのみ返却する(OR ロジックマッチ)', + limit: '返却セッション数の上限。デフォルト 20', + year: '指定年のセッションをフィルタリング(例:2024)', + month: '指定月のセッションをフィルタリング(1-12)。year と併用する必要がある', + day: '指定日のセッションをフィルタリング(1-31)。year と month と併用する必要がある', + start_time: '開始時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 14:00")', + end_time: '終了時刻。形式 "YYYY-MM-DD HH:mm"(例:"2024-03-15 18:30")', + }, + }, + get_session_messages: { + desc: '指定セッションの完全なメッセージリストを取得する。search_sessions で関連セッションを見つけた後、そのセッションの完全なコンテキストを取得するために使用する。セッションの全メッセージと参加者情報を返却する。', + params: { + session_id: 'セッション ID。search_sessions の返却結果から取得できる', + limit: '返却メッセージ数の上限。デフォルト 1000。非常に長いセッションでは Token 節約のため件数を制限できる', + }, + }, + get_session_summaries: { + desc: `セッション要約リストを取得し、グループチャットの過去の議論トピックをすばやく把握する。 + +適用シーン: +1. グループで最近何が話題になっていたか知りたい +2. キーワードで過去に議論されたトピックを検索 +3. 「グループで旅行について話したことある?」などの概要的な質問 + +返却される要約は各セッションの短い概要であり、興味のあるセッションをすばやく特定し、get_session_messages で詳細を取得できる。`, + params: { + keywords: '要約内で検索するキーワードリスト(OR ロジックマッチ)', + limit: '返却セッション数の上限。デフォルト 20', + year: '指定年のセッションをフィルタリング', + month: '指定月のセッションをフィルタリング(1-12)', + day: '指定日のセッションをフィルタリング(1-31)', + start_time: '開始時刻。形式 "YYYY-MM-DD HH:mm"', + end_time: '終了時刻。形式 "YYYY-MM-DD HH:mm"', + }, + }, + semantic_search_messages: { + desc: `Embedding ベクトル類似度を使用して過去の会話をセマンティック検索する。キーワードマッチではなく意味を理解する。 + +⚠️ 使用シーン(search_messages のキーワード検索を優先し、以下のシーンで本ツールを検討する): +1. 「似た表現」を探す:例えば「"会いたい" のような表現をしたことがある?」 +2. キーワード検索の結果が不十分:search_messages の結果が少なすぎたり関連性が低い場合、本ツールで補完する +3. あいまいな感情/関係分析:例えば「相手の私への態度は?」「私たちの間の雰囲気は?」 + +❌ 適さないシーン(search_messages を使用): +- 明確なキーワードがある検索(例:「旅行」「誕生日」「残業」) +- 特定の人物の発言を探す +- 特定の時間帯のメッセージを探す`, + params: { + query: 'セマンティック検索クエリ。自然言語で探したい内容の種類を記述する', + top_k: '返却結果数。デフォルト 10(推奨 5-20)', + candidate_limit: '候補セッション数。デフォルト 50(大きいほど遅くなるがより正確な場合がある)', + year: '指定年のセッションをフィルタリング', + month: '指定月のセッションをフィルタリング(1-12)', + day: '指定日のセッションをフィルタリング(1-31)', + start_time: '開始時刻。形式 "YYYY-MM-DD HH:mm"', + end_time: '終了時刻。形式 "YYYY-MM-DD HH:mm"', + }, + }, + // ===== SQL 分析ツール ===== + daily_message_type_breakdown: { + desc: 'メッセージタイプ別に直近 N 日間のメッセージ分布を集計する(テキスト、画像、音声、スタンプなどの件数)。グループチャットのコミュニケーション方法の傾向を把握するのに適している。', + params: { days: '直近何日間のデータを集計するか' }, + rowTemplate: '{type_name}:{msg_count} 件({percentage}%)', + summaryTemplate: '直近 {rowCount} 種類のメッセージタイプ分布:', + fallback: 'この期間にメッセージ記録がありません', + }, + peak_chat_hours_by_member: { + desc: '指定メンバーの直近 N 日間の時間帯別発言数分布を分析し、最もアクティブな時間帯を特定する。事前に get_members で member_id を取得する必要がある。', + params: { + member_id: 'メンバー ID(get_members で取得)', + days: '直近何日間のデータを集計するか', + }, + rowTemplate: '{hour}:00 — {msg_count} 件のメッセージ', + summaryTemplate: '該当メンバーの時間帯別発言数(全 {rowCount} アクティブ時間帯):', + fallback: '指定期間内に該当メンバーの発言記録がありません', + }, + member_activity_trend: { + desc: '指定メンバーの直近 N 日間の日別発言数の変化トレンドを表示する。ある人物がよりアクティブになったか、より静かになったかを観察するのに適している。事前に get_members で member_id を取得する必要がある。', + params: { + member_id: 'メンバー ID(get_members で取得)', + days: '直近何日間のトレンドを表示するか', + }, + rowTemplate: '{day}:{msg_count} 件', + summaryTemplate: '該当メンバーの直近 {rowCount} 日間の発言記録:', + fallback: '指定期間内に該当メンバーの発言記録がありません', + }, + silent_members: { + desc: 'N 日以上発言していない「休眠メンバー」を検出する。コミュニティ運営で離脱リスクのある利用者を見つけるのに向いている。', + params: { days: '何日間未発言でサイレントとみなすか' }, + rowTemplate: '{name} — {silent_days} 日間サイレント', + summaryTemplate: '全 {rowCount} 名の休眠メンバーを検出:', + fallback: '指定日数を超えて未発言のメンバーは見つかりませんでした。コミュニティのアクティビティは良好です!', + }, + reply_interaction_ranking: { + desc: 'グループ内の返信インタラクションランキングを分析し、誰が誰に最も多く返信しているかを特定する。コミュニティのコアインタラクション関係やオピニオンリーダーの発見に適している。', + params: { + days: '直近何日間のデータを集計するか', + limit: '上位何組のインタラクション関係を返却するか', + }, + rowTemplate: '{replier_name} → {original_name}:{reply_count} 回返信', + summaryTemplate: '返信インタラクション Top {rowCount}:', + fallback: 'この期間に返信インタラクション記録がありません', + }, + mutual_interaction_pairs: { + desc: '最も頻繁にインタラクションするメンバーペアを特定する。双方向のメッセージ時間近接度に基づく(一方の発言後5分以内にもう一方も発言した場合を1回のインタラクションとみなす)。親密な友人の組み合わせの発見に適している。', + params: { + days: '直近何日間のデータを集計するか', + limit: '上位何組を返却するか', + }, + rowTemplate: '{member_a} ↔ {member_b}:{interaction_count} 回のインタラクション', + summaryTemplate: '最も頻繁にインタラクションする {rowCount} 組のペア:', + fallback: 'この期間に明らかなインタラクション関係は検出されませんでした', + }, + member_message_length_stats: { + desc: '各メンバーの平均メッセージ長(テキストメッセージのみ)を集計する。長いメッセージは通常、より丁寧なコミュニケーションを意味する。深い交流を行う人物の発見に適している。', + params: { + days: '直近何日間のデータを集計するか', + top_n: '上位何名を返却するか', + }, + rowTemplate: '{name} — 平均 {avg_length} 字/件(全 {msg_count} 件、最長 {max_length} 字)', + summaryTemplate: 'メッセージ長 Top {rowCount}(長い = より丁寧):', + fallback: 'この期間に十分なテキストメッセージデータがありません', + }, + unanswered_messages: { + desc: '直近 N 日間で返信されていないメッセージを検索する。未解決の問題である可能性がある。テキストメッセージかつ10文字以上のもののみ集計する(短い挨拶を除外)。', + params: { + days: '直近何日間のデータを検索するか', + limit: '最大何件返却するか', + }, + rowTemplate: '[{send_time}] {sender_name}:{content_preview}', + summaryTemplate: '全 {rowCount} 件の返信されていない可能性のあるメッセージ:', + fallback: 'この期間のすべてのメッセージに返信がありました。対応品質は良好です!', + }, + message_type_distribution: { + desc: '直近 N 日間の各メッセージタイプの件数分布(テキスト、画像、音声、ファイルなど)を集計する。コミュニケーション方法の傾向と最適化の方向性を把握するのに役立つ。', + params: { days: '直近何日間のデータを集計するか' }, + rowTemplate: '{type_name}:{msg_count} 件({percentage}%)', + summaryTemplate: 'メッセージタイプ分布(全 {rowCount} 種類):', + fallback: 'この期間にメッセージ記録がありません', + }, + }, + + // ===== AI Agent システムプロンプト ===== + agent: { + answerWithoutTools: '取得済みの情報に基づいて回答してください。これ以上ツールを呼び出さないでください。', + toolError: 'エラー: {{error}}', + currentDateIs: '現在の日付は', + chatContext: { + private: '会話', + group: 'グループチャット', + }, + ownerNote: `現在のユーザー情報: +- ユーザーの{{chatContext}}における立場は「{{displayName}}」(platformId: {{platformId}}) +- ユーザーが「私」「自分の」と言った場合、「{{displayName}}」を指す +- 「私」の発言を検索する際は、sender_id パラメータで該当メンバーをフィルタリングする +`, + memberNotePrivate: `メンバー検索戦略: +- 個人チャットは2人だけなので、直接メンバー一覧を取得できる +- ユーザーが「相手」「彼/彼女」と言った場合、get_members でもう一方の情報を取得する +`, + memberNoteGroup: `メンバー検索戦略: +- ユーザーが特定のグループメンバーに言及した場合(例:「田中さんは何を言った?」「太郎の発言」など)、まず get_members でメンバー一覧を取得する +- グループメンバーには3種類の名前がある:accountName(元のニックネーム)、groupNickname(グループニックネーム)、aliases(ユーザー定義の別名) +- get_members の search パラメータでこれら3種類の名前をあいまい検索できる +- メンバーを見つけたら、その id フィールドを search_messages の sender_id パラメータとして使用して発言を取得する +`, + timeParamsIntro: '時間パラメータ:ユーザーが言及した精度に応じて year/month/day/hour を組み合わせる', + timeParamExample1: '"10月" → year: {{year}}, month: 10', + timeParamExample2: '"10月1日" → year: {{year}}, month: 10, day: 1', + timeParamExample3: '"10月1日午後3時" → year: {{year}}, month: 10, day: 1, hour: 15', + defaultYearNote: '年が指定されていない場合はデフォルトで{{year}}年。該当月がまだ来ていない場合は{{prevYear}}年を使用する', + responseInstruction: 'ユーザーの質問に応じて適切なツールを選択してデータを取得し、データに基づいて回答する。', + fallbackRoleDefinition: { + group: `あなたはプロフェッショナルだがカジュアルなスタイルのグループチャット履歴分析アシスタントです。 +ユーザーのグループチャット履歴データの理解と分析を支援し、適度にネットスラングや顔文字を使って雰囲気を和らげますが、結論の正確性に影響しないようにします。 + +## 回答要件 +1. ツールから返却されたデータに基づいて回答し、情報を捏造しない +2. データが不十分で質問に答えられない場合は、その旨を説明する +3. 回答は簡潔明瞭に、Markdown 形式を使用する +4. 適度にネットスラングや顔文字を加えてよい(強度は控えめに) +5. ネタは事実の正確さと結論の明確さに影響を与えてはならず、低俗または不快な表現は避ける`, + private: `あなたはプロフェッショナルだがカジュアルなスタイルの個人チャット履歴分析アシスタントです。 +ユーザーの個人チャット履歴データの理解と分析を支援し、適度にネットスラングや顔文字を使って雰囲気を和らげますが、結論の正確性に影響しないようにします。 + +## 回答要件 +1. ツールから返却されたデータに基づいて回答し、情報を捏造しない +2. データが不十分で質問に答えられない場合は、その旨を説明する +3. 回答は簡潔明瞭に、Markdown 形式を使用する +4. 適度にネットスラングや顔文字を加えてよい(強度は控えめに) +5. ネタは事実の正確さと結論の明確さに影響を与えてはならず、低俗または不快な表現は避ける`, + }, + }, + }, + + // ===== P3: LLM 設定 ===== + llm: { + notConfigured: 'LLM サービスが未設定です。先に設定で API Key を設定してください', + maxConfigs: '設定は最大 {{count}} 個まで追加できます', + configNotFound: '設定が存在しません', + noActiveConfig: 'アクティブな設定がありません', + }, + + // ===== P4: 要約生成 ===== + summary: { + sessionNotFound: 'セッションが存在しないか、データベースを開けませんでした', + tooFewMessages: 'メッセージ数が{{count}}件未満のため、要約の生成は不要です', + tooFewValidMessages: '有効なメッセージ数が{{count}}件未満のため、要約の生成は不要です', + sessionNotExist: 'セッションが存在しません', + messagesTooFew: 'メッセージが少なすぎます', + validMessagesTooFew: '有効なメッセージが少なすぎます', + systemPromptDirect: 'あなたは会話要約の専門家であり、簡潔な言葉で会話内容を要約することに長けている。', + systemPromptMerge: 'あなたは会話要約の専門家であり、複数の要約を一つのまとまった概要に統合することに長けている。', + }, +} diff --git a/electron/main/i18n/locales/zh-TW.ts b/electron/main/i18n/locales/zh-TW.ts new file mode 100644 index 0000000..2ff6a92 --- /dev/null +++ b/electron/main/i18n/locales/zh-TW.ts @@ -0,0 +1,346 @@ +/** + * 主進程繁體中文翻譯 + */ +export default { + // ===== 通用 ===== + common: { + error: '錯誤', + }, + + // ===== P0: 更新彈窗 ===== + update: { + newVersionTitle: '發現新版本 v{{version}}', + newVersionMessage: '發現新版本 v{{version}}', + newVersionDetail: '是否立即下載並安裝新版本?', + downloadNow: '立即下載', + cancel: '取消', + downloadComplete: '下載完成', + readyToInstall: '新版本已準備就緒,是否現在安裝?', + install: '安裝', + remindLater: '之後提醒', + installOnQuit: '稍後(應用程式退出後自動安裝)', + upToDate: '已是最新版本', + }, + + // ===== P0: 檔案/目錄對話框 ===== + dialog: { + selectChatFile: '選擇聊天紀錄檔案', + chatRecords: '聊天紀錄', + allFiles: '所有檔案', + import: '匯入', + selectDirectory: '選擇目錄', + selectFolder: '選擇資料夾', + selectFolderError: '選擇資料夾時發生錯誤:', + }, + + // ===== P1: 資料庫遷移 ===== + database: { + migrationV1Desc: '新增 owner_id 欄位到 meta 表', + migrationV1Message: '支援「Owner」功能,可在成員清單中設定自己的身分', + migrationV2Desc: '新增 roles、reply_to_message_id、platform_message_id 欄位', + migrationV2Message: '支援成員角色、訊息回覆關係和回覆內容預覽', + migrationV3Desc: '新增會話索引相關表(chat_session、message_context)和 session_gap_threshold 欄位', + migrationV3Message: '支援會話時間軸瀏覽和 AI 增強分析功能', + integrityError: '資料庫結構不完整:缺少 meta 表。建議刪除此資料庫檔案後重新匯入。', + checkFailed: '資料庫檢查失敗:{{error}}', + }, + + // ===== 工具系統 ===== + tools: { + notRegistered: '工具 "{{toolName}}" 未註冊', + }, + + // ===== P2: AI 工具描述(Function Calling) ===== + ai: { + tools: { + search_messages: { + desc: '根據關鍵詞搜尋群聊紀錄。適用於使用者想要查找特定話題、關鍵詞相關的聊天內容。可以指定時間範圍和傳送者來篩選訊息。支援精確到分鐘級別的時間查詢。', + params: { + keywords: '搜尋關鍵詞清單,會用 OR 邏輯匹配包含任一關鍵詞的訊息。如果只需要按傳送者篩選,可以傳空陣列 []', + sender_id: '傳送者的成員 ID,用於篩選特定成員傳送的訊息。可以透過 get_members 工具取得成員 ID', + limit: '回傳訊息數量限制,預設 1000,最大 50000', + year: '篩選指定年份的訊息,如 2024', + month: '篩選指定月份的訊息(1-12),需要配合 year 使用', + day: '篩選指定日期的訊息(1-31),需要配合 year 和 month 使用', + hour: '篩選指定小時的訊息(0-23),需要配合 year、month 和 day 使用', + start_time: '開始時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 14:00"。指定後會覆蓋 year/month/day/hour 參數', + end_time: '結束時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 18:30"。指定後會覆蓋 year/month/day/hour 參數', + }, + }, + get_recent_messages: { + desc: '取得指定時間段內的群聊訊息。適用於回答「最近大家聊了什麼」、「X月群裡聊了什麼」等概覽性問題。支援精確到分鐘級別的時間查詢。', + params: { + limit: '回傳訊息數量限制,預設 100(可節省 Token,必要時再增加)', + year: '篩選指定年份的訊息,如 2024', + month: '篩選指定月份的訊息(1-12),需要配合 year 使用', + day: '篩選指定日期的訊息(1-31),需要配合 year 和 month 使用', + hour: '篩選指定小時的訊息(0-23),需要配合 year、month 和 day 使用', + start_time: '開始時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 14:00"。指定後會覆蓋 year/month/day/hour 參數', + end_time: '結束時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 18:30"。指定後會覆蓋 year/month/day/hour 參數', + }, + }, + get_member_stats: { + desc: '取得群成員的活躍度統計資料。適用於回答「誰最活躍」、「發言最多的是誰」等問題。', + params: { + top_n: '回傳前 N 名成員,預設 10', + }, + }, + get_time_stats: { + desc: '取得群聊的時間分佈統計。適用於回答「什麼時候最活躍」、「大家一般幾點聊天」等問題。', + params: { + type: '統計類型:hourly(按小時)、weekday(按星期)、daily(按日期)', + }, + }, + get_members: { + desc: '取得群成員清單,包含基本資料、別名與訊息統計。適用於查詢「群裡有哪些人」、「某人的別名是什麼」、「誰的 QQ 號是 xxx」等問題。', + params: { + search: '可選的搜尋關鍵詞,用於篩選成員暱稱、別名或 QQ 號', + limit: '回傳成員數量限制,預設回傳全部', + }, + }, + get_member_name_history: { + desc: '取得成員的暱稱變更歷史紀錄。適用於回答「某人以前叫什麼名字」、「某人的暱稱變化」、「某人曾用名」等問題。需要先透過 get_members 工具取得成員 ID。', + params: { + member_id: '成員的資料庫 ID,可以透過 get_members 工具取得', + }, + }, + get_conversation_between: { + desc: '取得兩位群成員之間的對話紀錄。適用於回答「A 和 B 之間聊了什麼」、「檢視兩人的對話」等問題。需要先透過 get_members 取得成員 ID。支援精確到分鐘級別的時間查詢。', + params: { + member_id_1: '第一個成員的資料庫 ID', + member_id_2: '第二個成員的資料庫 ID', + limit: '回傳訊息數量限制,預設 100', + year: '篩選指定年份的訊息', + month: '篩選指定月份的訊息(1-12),需要配合 year 使用', + day: '篩選指定日期的訊息(1-31),需要配合 year 和 month 使用', + hour: '篩選指定小時的訊息(0-23),需要配合 year、month 和 day 使用', + start_time: '開始時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 14:00"。指定後會覆蓋 year/month/day/hour 參數', + end_time: '結束時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 18:30"。指定後會覆蓋 year/month/day/hour 參數', + }, + }, + get_message_context: { + desc: '根據訊息 ID 取得前後的上下文訊息。適用於想了解某條訊息前後內容的情境,例如「這則訊息前後在聊什麼」、「檢視某條訊息的上下文」等。支援單筆或批次訊息 ID。', + params: { + message_ids: + '要查詢上下文的訊息 ID 清單,可以是單個 ID 或多個 ID。訊息 ID 可以從 search_messages 等工具的回傳結果中取得', + context_size: '上下文大小,即取得前後各多少條訊息,預設 20', + }, + }, + search_sessions: { + desc: '搜尋聊天會話(對話段落)。會話是根據訊息時間間隔自動切分的對話單元。適用於查找特定話題的討論、了解某個時間段內發生了幾次對話等場景。回傳匹配的會話清單及每個會話的前5條訊息預覽。', + params: { + keywords: '可選的搜尋關鍵詞清單,只回傳包含這些關鍵詞的會話(OR 邏輯匹配)', + limit: '回傳會話數量限制,預設 20', + year: '篩選指定年份的會話,如 2024', + month: '篩選指定月份的會話(1-12),需要配合 year 使用', + day: '篩選指定日期的會話(1-31),需要配合 year 和 month 使用', + start_time: '開始時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 14:00"', + end_time: '結束時間,格式 "YYYY-MM-DD HH:mm",如 "2024-03-15 18:30"', + }, + }, + get_session_messages: { + desc: '取得指定會話的完整訊息清單。用於在 search_sessions 找到相關會話後,取得該會話的完整上下文。回傳會話的所有訊息及參與者資訊。', + params: { + session_id: '會話 ID,可以從 search_sessions 的回傳結果中取得', + limit: '回傳訊息數量限制,預設 1000。對於超長會話可以限制回傳數量以節省 token', + }, + }, + get_session_summaries: { + desc: `取得會話摘要清單,快速了解群聊歷史討論的主題。 + +適用場景: +1. 了解群裡最近在聊什麼話題 +2. 按關鍵詞搜尋討論過的話題 +3. 概覽性問題如「群裡有沒有討論過旅遊」 + +回傳的摘要是對每個會話的簡短總結,可以幫助快速定位感興趣的會話,然後用 get_session_messages 取得詳情。`, + params: { + keywords: '在摘要中搜尋的關鍵詞清單(OR 邏輯匹配)', + limit: '回傳會話數量限制,預設 20', + year: '篩選指定年份的會話', + month: '篩選指定月份的會話(1-12)', + day: '篩選指定日期的會話(1-31)', + start_time: '開始時間,格式 "YYYY-MM-DD HH:mm"', + end_time: '結束時間,格式 "YYYY-MM-DD HH:mm"', + }, + }, + semantic_search_messages: { + desc: `使用 Embedding 向量相似度搜尋歷史對話,理解語義而非關鍵詞匹配。 + +⚠️ 使用場景(優先使用 search_messages 關鍵詞搜尋,以下場景再考慮本工具): +1. 找「類似的話」或「類似的表達」:如「有沒有說過類似『我想你了』這樣的話」 +2. 關鍵詞搜尋結果不足:當 search_messages 回傳結果太少或不相關時,可用本工具補充 +3. 模糊的情感/關係分析:如「對方對我的態度是怎樣的」、「我們之間的氛圍」 + +❌ 不適合的場景(請用 search_messages): +- 有明確關鍵詞的搜尋(如「旅遊」、「生日」、「加班」) +- 查找特定人物的發言 +- 查找特定時間段的訊息`, + params: { + query: '語義檢索查詢,用自然語言描述你想要找的內容類型', + top_k: '回傳結果數量,預設 10(建議 5-20)', + candidate_limit: '候選會話數量,預設 50(越大越慢但可能更準確)', + year: '篩選指定年份的會話', + month: '篩選指定月份的會話(1-12)', + day: '篩選指定日期的會話(1-31)', + start_time: '開始時間,格式 "YYYY-MM-DD HH:mm"', + end_time: '結束時間,格式 "YYYY-MM-DD HH:mm"', + }, + }, + // ===== SQL 分析工具 ===== + daily_message_type_breakdown: { + desc: '按訊息類型統計近 N 天的訊息分佈(文字、圖片、語音、表情等各有多少條)。適用於了解群聊的溝通方式偏好。', + params: { days: '統計最近多少天的資料' }, + rowTemplate: '{type_name}:{msg_count} 條(佔 {percentage}%)', + summaryTemplate: '近 {rowCount} 種訊息類型的分佈:', + fallback: '該時間範圍內沒有訊息紀錄', + }, + peak_chat_hours_by_member: { + desc: '分析指定成員在近 N 天內每小時的發言量分佈,找出其最活躍的時段。需要先透過 get_members 取得 member_id。', + params: { + member_id: '成員 ID(透過 get_members 取得)', + days: '統計最近多少天的資料', + }, + rowTemplate: '{hour}:00 — {msg_count} 條訊息', + summaryTemplate: '該成員各時段發言量(共 {rowCount} 個活躍時段):', + fallback: '該成員在指定時間範圍內沒有發言紀錄', + }, + member_activity_trend: { + desc: '查看指定成員近 N 天的每日發言數量變化趨勢。適用於觀察某人是否變得更活躍或更沉默。需要先透過 get_members 取得 member_id。', + params: { + member_id: '成員 ID(透過 get_members 取得)', + days: '查看最近多少天的趨勢', + }, + rowTemplate: '{day}:{msg_count} 條', + summaryTemplate: '該成員近 {rowCount} 天有發言紀錄:', + fallback: '該成員在指定時間範圍內沒有發言紀錄', + }, + silent_members: { + desc: '偵測超過 N 天未發言的「沉默成員」。適用於社群營運中發現流失風險使用者。', + params: { days: '多少天未發言算沉默' }, + rowTemplate: '{name} — 已沉默 {silent_days} 天', + summaryTemplate: '共發現 {rowCount} 位沉默成員:', + fallback: '沒有發現超過指定天數未發言的成員,社群活躍度良好!', + }, + reply_interaction_ranking: { + desc: '分析群內的回覆互動關係排行,找出誰回覆誰最多。適用於發現社群中的核心互動關係和意見領袖。', + params: { + days: '統計最近多少天的資料', + limit: '回傳前多少對互動關係', + }, + rowTemplate: '{replier_name} → {original_name}:{reply_count} 次回覆', + summaryTemplate: '回覆互動 Top {rowCount}:', + fallback: '該時間範圍內沒有回覆互動紀錄', + }, + mutual_interaction_pairs: { + desc: '找出互動最頻繁的成員對,基於雙向訊息時間接近度(一方發言後 5 分鐘內另一方也發言即視為一次互動)。適用於發現關係親密的好友組合。', + params: { + days: '統計最近多少天的資料', + limit: '回傳前多少對', + }, + rowTemplate: '{member_a} ↔ {member_b}:{interaction_count} 次互動', + summaryTemplate: '互動最頻繁的 {rowCount} 對好友:', + fallback: '該時間範圍內沒有偵測到明顯的互動關係', + }, + member_message_length_stats: { + desc: '統計各成員的平均訊息長度(僅文字訊息),長訊息通常意味著更用心的交流。適用於發現深度交流者。', + params: { + days: '統計最近多少天的資料', + top_n: '回傳前多少名', + }, + rowTemplate: '{name} — 平均 {avg_length} 字/條(共 {msg_count} 條,最長 {max_length} 字)', + summaryTemplate: '訊息長度 Top {rowCount}(更長 = 更用心):', + fallback: '該時間範圍內沒有足夠的文字訊息資料', + }, + unanswered_messages: { + desc: '查找近 N 天內未被回覆的訊息,這些可能是未解決的客戶問題。僅統計文字訊息且內容超過 10 字的(過濾簡短寒暄)。', + params: { + days: '查找最近多少天的資料', + limit: '最多回傳多少條', + }, + rowTemplate: '[{send_time}] {sender_name}:{content_preview}', + summaryTemplate: '共發現 {rowCount} 條可能未被回覆的訊息:', + fallback: '該時間範圍內所有訊息都已得到回覆,服務品質很好!', + }, + message_type_distribution: { + desc: '統計近 N 天內各種訊息類型的數量分佈(文字、圖片、語音、檔案等),幫助了解客服溝通的方式偏好和最佳化方向。', + params: { days: '統計最近多少天的資料' }, + rowTemplate: '{type_name}:{msg_count} 條({percentage}%)', + summaryTemplate: '訊息類型分佈(共 {rowCount} 種類型):', + fallback: '該時間範圍內沒有訊息紀錄', + }, + }, + + // ===== AI Agent 系統提示詞 ===== + agent: { + answerWithoutTools: '請根據已取得的資訊給出回答,不要再呼叫工具。', + toolError: '錯誤: {{error}}', + currentDateIs: '目前日期是', + chatContext: { + private: '對話', + group: '群聊', + }, + ownerNote: `目前使用者身份: +- 使用者在{{chatContext}}中的身份是「{{displayName}}」(platformId: {{platformId}}) +- 當使用者提到「我」、「我的」時,指的就是「{{displayName}}」 +- 查詢「我」的發言時,使用 sender_id 參數篩選該成員 +`, + memberNotePrivate: `成員查詢策略: +- 私聊只有兩個人,可以直接取得成員清單 +- 當使用者提到「對方」、「他/她」時,透過 get_members 取得另一方資訊 +`, + memberNoteGroup: `成員查詢策略: +- 當使用者提到特定群成員(如「張三說過什麼」、「小明的發言」等)時,應先呼叫 get_members 取得成員清單 +- 群成員有三種名稱:accountName(原始暱稱)、groupNickname(群暱稱)、aliases(使用者自訂別名) +- 透過 get_members 的 search 參數可以模糊搜尋這三種名稱 +- 找到成員後,使用其 id 欄位作為 search_messages 的 sender_id 參數來取得該成員的發言 +`, + timeParamsIntro: '時間參數:按使用者提到的精度組合 year/month/day/hour', + timeParamExample1: '「10月」→ year: {{year}}, month: 10', + timeParamExample2: '「10月1號」→ year: {{year}}, month: 10, day: 1', + timeParamExample3: '「10月1號下午3點」→ year: {{year}}, month: 10, day: 1, hour: 15', + defaultYearNote: '未指定年份預設{{year}}年,若該月份未到則用{{prevYear}}年', + responseInstruction: '根據使用者的問題,選擇合適的工具取得資料,然後基於資料給出回答。', + fallbackRoleDefinition: { + group: `你是一個專業但風格輕鬆的群聊紀錄分析助手。 +你的任務是幫助使用者理解和分析他們的群聊紀錄資料,同時可以適度使用網路熱梗和表情/顏文字活躍氣氛,但不影響結論的準確性。 + +## 回答要求 +1. 基於工具回傳的資料回答,不要編造資訊 +2. 如果資料不足以回答問題,請說明 +3. 回答要簡潔明瞭,使用 Markdown 格式 +4. 可以適度加入網路熱梗、表情/顏文字(強度適中) +5. 玩梗不得影響事實準確與結論清晰,避免低俗或冒犯性表達`, + private: `你是一個專業但風格輕鬆的私聊紀錄分析助手。 +你的任務是幫助使用者理解和分析他們的私聊紀錄資料,同時可以適度使用網路熱梗和表情/顏文字活躍氣氛,但不影響結論的準確性。 + +## 回答要求 +1. 基於工具回傳的資料回答,不要編造資訊 +2. 如果資料不足以回答問題,請說明 +3. 回答要簡潔明瞭,使用 Markdown 格式 +4. 可以適度加入網路熱梗、表情/顏文字(強度適中) +5. 玩梗不得影響事實準確與結論清晰,避免低俗或冒犯性表達`, + }, + }, + }, + + // ===== P3: LLM 配置 ===== + llm: { + notConfigured: 'LLM 服務尚未設定,請先在設定中填入 API Key', + maxConfigs: '最多只能新增 {{count}} 組設定', + configNotFound: '找不到設定', + noActiveConfig: '沒有啟用中的設定', + }, + + // ===== P4: 摘要生成 ===== + summary: { + sessionNotFound: '會話不存在或資料庫開啟失敗', + tooFewMessages: '訊息數量少於 {{count}} 條,無需產生摘要', + tooFewValidMessages: '有效訊息數量少於 {{count}} 條,無需產生摘要', + sessionNotExist: '找不到會話', + messagesTooFew: '訊息太少', + validMessagesTooFew: '有效訊息太少', + systemPromptDirect: '你是一個對話摘要專家,擅長用簡潔的語言總結對話內容。', + systemPromptMerge: '你是一個對話摘要專家,擅長將多個摘要合併成一個連貫的總結。', + }, +} diff --git a/electron/main/nlp/segmenter.ts b/electron/main/nlp/segmenter.ts index 51d51c5..0b30185 100644 --- a/electron/main/nlp/segmenter.ts +++ b/electron/main/nlp/segmenter.ts @@ -236,7 +236,6 @@ function segmentEnglish(text: string): string[] { return [...segments].filter((segment) => segment.isWordLike).map((segment) => segment.segment.toLowerCase()) } catch { - // 降级:简单按空格分词 return cleaned .toLowerCase() .split(/\s+/) @@ -244,6 +243,23 @@ function segmentEnglish(text: string): string[] { } } +/** + * 日语分词(使用 Intl.Segmenter) + */ +function segmentJapanese(text: string): string[] { + const cleaned = cleanText(text) + if (!cleaned) return [] + + try { + const segmenter = new Intl.Segmenter('ja', { granularity: 'word' }) + const segments = segmenter.segment(cleaned) + + return [...segments].filter((segment) => segment.isWordLike).map((segment) => segment.segment) + } catch { + return cleaned.split('').filter((ch) => ch.trim().length > 0) + } +} + /** * 分词选项 */ @@ -267,18 +283,21 @@ export interface SegmentOptions { */ export function segment(text: string, locale: SupportedLocale, options: SegmentOptions = {}): string[] { const { minLength, posFilterMode = 'meaningful', customPosTags, enableStopwords = true } = options - const defaultMinLength = locale === 'zh-CN' ? 2 : 3 + const isChinese = locale.startsWith('zh') + const isJapanese = locale === 'ja-JP' + const defaultMinLength = isChinese || isJapanese ? 2 : 3 const effectiveMinLength = minLength ?? defaultMinLength let words: string[] - if (locale === 'zh-CN') { + if (isChinese) { words = segmentChinese(text, { posFilterMode, customPosTags }) + } else if (isJapanese) { + words = segmentJapanese(text) } else { words = segmentEnglish(text) } - // 过滤无效词 return words.filter((word) => isValidWord(word, locale, effectiveMinLength, enableStopwords)) } diff --git a/electron/main/nlp/stopwords.ts b/electron/main/nlp/stopwords.ts index 6f2dba7..c5cf416 100644 --- a/electron/main/nlp/stopwords.ts +++ b/electron/main/nlp/stopwords.ts @@ -546,15 +546,55 @@ export const ENGLISH_STOPWORDS = new Set([ 'later', ]) +/** 日语停用词 */ +export const JAPANESE_STOPWORDS = new Set([ + // 助詞 + 'の', 'に', 'は', 'を', 'た', 'が', 'で', 'て', 'と', 'し', 'れ', 'さ', + 'ある', 'いる', 'も', 'する', 'から', 'な', 'こと', 'として', 'い', 'や', + 'れる', 'など', 'なっ', 'ない', 'この', 'ため', 'その', 'あっ', 'よう', + 'また', 'もの', 'という', 'あり', 'まで', 'られ', 'なる', 'へ', 'か', + 'だ', 'これ', 'によって', 'により', 'おり', 'より', 'による', 'ず', 'なり', + 'られる', 'において', 'ば', 'なかっ', 'なく', 'しかし', 'について', 'せ', + 'だっ', 'その後', 'できる', 'それ', 'う', 'ので', 'なお', 'のみ', 'でき', + 'き', 'つ', 'における', 'および', 'いう', 'さらに', 'でも', 'ら', 'たり', + 'その他', 'に関する', 'たち', 'ます', 'ん', 'なら', 'に対して', + // 代名詞 + '私', '僕', '俺', '自分', 'あなた', '彼', '彼女', 'それ', 'これ', 'あれ', + 'ここ', 'そこ', 'あそこ', 'どこ', 'みんな', '皆', + // 接続詞 + 'そして', 'しかし', 'でも', 'だから', 'それで', 'だけど', 'けど', + 'ところで', 'さて', 'つまり', 'すなわち', 'ただし', 'もし', 'また', + // 副詞 + 'とても', 'すごく', 'もう', 'まだ', 'よく', 'ちょっと', 'ちょと', 'もっと', + 'やっぱり', 'やはり', 'たぶん', 'きっと', 'ぜんぜん', 'ほんとに', 'ほんと', + 'かなり', 'だいたい', 'ほとんど', 'まあ', 'なんか', 'なんとなく', + // 感動詞・フィラー + 'あ', 'ああ', 'えー', 'うん', 'えっ', 'おお', 'へー', 'ふーん', 'はい', + 'いいえ', 'うーん', 'まあ', 'ねえ', 'ほら', 'あのね', 'えっと', 'その', + // 動詞(高頻度) + 'いる', 'ある', 'する', 'なる', 'できる', 'いく', 'くる', 'みる', 'おもう', + 'いう', 'やる', 'くれる', 'もらう', 'あげる', 'しまう', 'おく', + // 形容詞(高頻度) + 'いい', 'ない', 'よい', 'すごい', 'おおきい', 'ちいさい', + // 助動詞 + 'です', 'ます', 'でした', 'ました', 'ません', 'だった', 'でしょう', + // チャットでの高頻語 + 'www', 'ww', 'lol', 'ok', 'おけ', 'りょ', 'おつ', 'わら', '笑', + 'それな', 'たしかに', 'マジ', 'まじ', 'ガチ', 'がち', +]) + /** * 获取停用词集合 * @param locale 语言 * @returns 停用词集合 */ export function getStopwords(locale: string): Set { - if (locale === 'zh-CN') { + if (locale.startsWith('zh')) { return CHINESE_STOPWORDS } + if (locale === 'ja-JP') { + return JAPANESE_STOPWORDS + } return ENGLISH_STOPWORDS } @@ -566,7 +606,6 @@ export function getStopwords(locale: string): Set { */ export function isStopword(word: string, locale: string): boolean { const stopwords = getStopwords(locale) - // 英文统一转小写比较 const normalizedWord = locale === 'en-US' ? word.toLowerCase() : word return stopwords.has(normalizedWord) } diff --git a/electron/main/nlp/types.ts b/electron/main/nlp/types.ts index cb0bc8c..9f786de 100644 --- a/electron/main/nlp/types.ts +++ b/electron/main/nlp/types.ts @@ -3,7 +3,7 @@ */ /** 支持的语言 */ -export type SupportedLocale = 'zh-CN' | 'en-US' +export type SupportedLocale = 'zh-CN' | 'en-US' | 'zh-TW' | 'ja-JP' /** 分词结果 */ export interface SegmentResult { diff --git a/electron/main/worker/query/nlp.ts b/electron/main/worker/query/nlp.ts index 7ad9d76..183d211 100644 --- a/electron/main/worker/query/nlp.ts +++ b/electron/main/worker/query/nlp.ts @@ -79,7 +79,7 @@ export function getWordFrequency(params: WordFrequencyParams): WordFrequencyResu // 收集词性统计(用于显示每个词性有多少词,仅中文有效) let posTagStats: PosTagStat[] | undefined - if ((locale as SupportedLocale) === 'zh-CN') { + if ((locale as string).startsWith('zh')) { const posStatsMap = collectPosTagStats(texts, minWordLength ?? 2, enableStopwords) posTagStats = [...posStatsMap.entries()].map(([tag, count]) => ({ tag, count })) } diff --git a/skills/sync-changelog/SKILL.md b/skills/sync-changelog/SKILL.md index 4cf0d0a..90437a1 100644 --- a/skills/sync-changelog/SKILL.md +++ b/skills/sync-changelog/SKILL.md @@ -1,6 +1,6 @@ --- name: sync-changelog -description: 将 docs/changelogs_cn.json 的当前版本日志生成适合英文母语者阅读的英文版本,更新 docs/changelogs_en.json,并在当前项目创建 release 提交(包含 package.json 与中英文 changelog);随后同步中英文 changelog 到同级仓库 ../chatlab.fun 并创建文档提交。用于用户提出“同步版本日志”“生成并同步 changelog”“发布前同步中英文日志”等请求。仅创建 commit,不执行 push。 +description: 将 docs/changelogs_cn.json 的当前版本日志同步为多语言版本:生成适合英文母语者阅读的英文版、适合繁体中文用户阅读的繁中版、适合日语母语者阅读的日文版,分别更新 docs/changelogs_en.json、docs/changelogs_tw.json、docs/changelogs_ja.json,并在当前项目创建 release 提交(包含 package.json 与四种语言 changelog);随后同步四种语言 changelog 到同级仓库 ../chatlab.fun 并创建文档提交。用于用户提出“同步版本日志”“生成并同步 changelog”“发布前同步多语言日志”等请求。仅创建 commit,不执行 push。 --- # sync-changelog @@ -32,19 +32,30 @@ scripts/preflight_main_clean.sh . "package.json,docs/changelogs_cn.json" 1. 从 `docs/changelogs_cn.json` 读取第一个对象作为当前版本。 2. 读取版本号 `version`(例如 `0.9.6`)。 -3. 检查 `docs/changelogs_en.json` 是否存在: - - 不存在则立即退出,不允许自动创建。 +3. 检查以下文件是否存在: + - `docs/changelogs_en.json` + - `docs/changelogs_tw.json` + - `docs/changelogs_ja.json` +4. 任一目标文件不存在都立即退出,不允许自动创建。 -## 3. 生成英文 changelog(AI 翻译) +## 3. 生成多语言 changelog(AI 翻译) -1. 将当前版本中文内容转写为英文,要求: +1. 将当前版本中文内容分别转写为英文、繁体中文、日文,统一要求: - 不做逐字直译。 - - 使用自然、简洁、适合英文母语用户的 release notes 语气。 - 保持原始结构:`version/date/summary/changes(type/items)`。 -2. 更新 `docs/changelogs_en.json`: + - 不改动 `version`、`date`、`changes.type`。 +2. 语言要求: + - 英文:使用自然、简洁、适合英文母语用户的 release notes 语气。 + - 繁体中文:以台湾常见产品文案口吻重写,避免简体直转。 + - 日文:使用自然、简洁、适合日本用户阅读的产品更新说明语气,避免中文式表达。 +3. 分别更新: + - `docs/changelogs_en.json` + - `docs/changelogs_tw.json` + - `docs/changelogs_ja.json` +4. 每个目标文件都遵循相同规则: - 若已存在该版本,替换该版本对象。 - 若不存在,插入到数组首位。 -3. 写入后执行格式化(若项目有 Prettier,优先使用 Prettier)。 +5. 写入后执行格式化(若项目有 Prettier,优先使用 Prettier)。 ## 4. 在当前仓库创建发布提交 @@ -52,6 +63,8 @@ scripts/preflight_main_clean.sh . "package.json,docs/changelogs_cn.json" - `package.json` - `docs/changelogs_cn.json` - `docs/changelogs_en.json` + - `docs/changelogs_tw.json` + - `docs/changelogs_ja.json` 2. commit message:`release: v`(示例:`release: v0.9.6`)。 3. 仅创建 commit,不 push。 @@ -62,9 +75,11 @@ scripts/preflight_main_clean.sh . "package.json,docs/changelogs_cn.json" 1. 从当前仓库复制: - `docs/changelogs_cn.json` -> `../chatlab.fun/docs/public/cn/changelogs.json` - `docs/changelogs_en.json` -> `../chatlab.fun/docs/public/en/changelogs.json` + - `docs/changelogs_tw.json` -> `../chatlab.fun/docs/public/tw/changelogs.json` + - `docs/changelogs_ja.json` -> `../chatlab.fun/docs/public/ja/changelogs.json` 2. 目标路径必须存在;不存在则报错退出,不自动创建目录。 3. 在 `../chatlab.fun` 提交: - - 仅提交上述两个文件。 + - 仅提交上述四个文件。 - commit message:`docs: changelogs update` 4. 仅创建 commit,不 push。 @@ -82,6 +97,8 @@ scripts/preflight_main_clean.sh . "package.json,docs/changelogs_cn.json" ## 参考 - `references/english-release-style.md` +- `references/traditional-chinese-release-style.md` +- `references/japanese-release-style.md` - `scripts/preflight_main_clean.sh` - `scripts/commit_release_changelogs.sh` - `scripts/sync_to_chatlab_fun.sh` diff --git a/skills/sync-changelog/agents/openai.yaml b/skills/sync-changelog/agents/openai.yaml index 8323931..d809b7c 100644 --- a/skills/sync-changelog/agents/openai.yaml +++ b/skills/sync-changelog/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: '同步版本日志' - short_description: '生成英文 changelog 并在双仓库创建提交(不推送)' - default_prompt: 'Use $sync-changelog to translate the latest Chinese changelog entry into natural English, commit release notes in this repo, and sync both changelog files to ../chatlab.fun with a docs commit.' + short_description: '生成英/繁中/日文 changelog 并在双仓库创建提交(不推送)' + default_prompt: 'Use $sync-changelog to turn the latest Chinese changelog entry into natural English, Traditional Chinese, and Japanese release notes, commit all localized changelog files in this repo, and sync them to ../chatlab.fun with a docs commit.' diff --git a/skills/sync-changelog/references/japanese-release-style.md b/skills/sync-changelog/references/japanese-release-style.md new file mode 100644 index 0000000..695b696 --- /dev/null +++ b/skills/sync-changelog/references/japanese-release-style.md @@ -0,0 +1,33 @@ +# japanese-release-style + +用于将 `docs/changelogs_cn.json` 当前版本条目改写为日文版本。 + +## 目标 + +- 面向日本用户,写成自然的产品更新说明 +- 避免中文式表达和逐字直译 +- 保持简洁、可信、易扫读 + +## 规则 + +1. 保留原有结构: + - `summary` + - `changes[].type` + - `changes[].items[]` +2. `summary` 使用简短版本概述,不写成长段解释 +3. `changes.items` 每条只写一个变化点 +4. 语气以产品 release notes 为准: + - 简洁 + - 说明明确 + - 不夸张宣传 +5. 优先使用常见 UI / 产品文案表达,避免: + - 中文语序 + - 生硬片假名堆砌 + - “〜を実現しました” 一类过度宣传句式 +6. 可以适度意译,但不能新增原文没有的承诺或结论 + +## 自检 + +- 是否像日本软件产品的更新说明 +- 是否比中文直译更自然 +- 是否忠实保留原始改动 diff --git a/skills/sync-changelog/references/traditional-chinese-release-style.md b/skills/sync-changelog/references/traditional-chinese-release-style.md new file mode 100644 index 0000000..3dda3c1 --- /dev/null +++ b/skills/sync-changelog/references/traditional-chinese-release-style.md @@ -0,0 +1,31 @@ +# traditional-chinese-release-style + +用于将 `docs/changelogs_cn.json` 当前版本条目改写为繁体中文版本。 + +## 目标 + +- 面向繁体中文用户,优先采用台湾常见产品文案口吻 +- 比简体原文更顺口,但不改变事实 +- 保持 release notes 风格:短句、直白、便于扫读 + +## 规则 + +1. 不做简体直接替换,要根据语境重写 +2. 保留原有结构: + - `summary` + - `changes[].type` + - `changes[].items[]` +3. `summary` 要像版本摘要,不要写成长段说明 +4. `changes.items` 每条只表达一个改动点 +5. 使用常见繁体产品词汇,例如: + - 配置 -> 設定 + - 查看 -> 檢視 + - 生成 -> 產生 + - 返回 -> 返回上一頁 / 回到... +6. 避免中国大陆口语化表达、生硬技术直译、过长句子 + +## 自检 + +- 是否读起来像台湾产品更新说明 +- 是否保留了原始改动含义 +- 是否比简体更简洁顺口 diff --git a/skills/sync-changelog/scripts/commit_release_changelogs.sh b/skills/sync-changelog/scripts/commit_release_changelogs.sh index dfe46de..1a6abc6 100755 --- a/skills/sync-changelog/scripts/commit_release_changelogs.sh +++ b/skills/sync-changelog/scripts/commit_release_changelogs.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -# 在当前项目提交中英文 changelog,提交信息为 release: v +# 在当前项目提交多语言 changelog,提交信息为 release: v # 用法:commit_release_changelogs.sh REPO_PATH="${1:-}" if [[ -z "$REPO_PATH" ]]; then @@ -11,10 +11,12 @@ fi CN_FILE="$REPO_PATH/docs/changelogs_cn.json" EN_FILE="$REPO_PATH/docs/changelogs_en.json" +TW_FILE="$REPO_PATH/docs/changelogs_tw.json" +JA_FILE="$REPO_PATH/docs/changelogs_ja.json" PKG_FILE="$REPO_PATH/package.json" -if [[ ! -f "$CN_FILE" || ! -f "$EN_FILE" || ! -f "$PKG_FILE" ]]; then - echo "错误: changelog 或 package.json 不存在,无法提交" >&2 +if [[ ! -f "$CN_FILE" || ! -f "$EN_FILE" || ! -f "$TW_FILE" || ! -f "$JA_FILE" || ! -f "$PKG_FILE" ]]; then + echo "错误: 多语言 changelog 或 package.json 不存在,无法提交" >&2 exit 1 fi @@ -26,7 +28,12 @@ fi # 仅暂存发布必需文件,避免误提交其他改动。 # 注意:package.json 中的版本号需要和 release 提交一起落盘。 -git -C "$REPO_PATH" add package.json docs/changelogs_cn.json docs/changelogs_en.json +git -C "$REPO_PATH" add \ + package.json \ + docs/changelogs_cn.json \ + docs/changelogs_en.json \ + docs/changelogs_tw.json \ + docs/changelogs_ja.json # 若没有差异则不提交,避免空提交失败。 if git -C "$REPO_PATH" diff --cached --quiet; then diff --git a/skills/sync-changelog/scripts/sync_to_chatlab_fun.sh b/skills/sync-changelog/scripts/sync_to_chatlab_fun.sh index 1927c7d..44de158 100755 --- a/skills/sync-changelog/scripts/sync_to_chatlab_fun.sh +++ b/skills/sync-changelog/scripts/sync_to_chatlab_fun.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -# 同步当前仓库 changelog 到 ../chatlab.fun 并提交 docs: changelogs update +# 同步当前仓库多语言 changelog 到 ../chatlab.fun 并提交 docs: changelogs update # 用法:sync_to_chatlab_fun.sh SOURCE_REPO="${1:-}" TARGET_REPO="${2:-}" @@ -13,25 +13,35 @@ fi SRC_CN="$SOURCE_REPO/docs/changelogs_cn.json" SRC_EN="$SOURCE_REPO/docs/changelogs_en.json" +SRC_TW="$SOURCE_REPO/docs/changelogs_tw.json" +SRC_JA="$SOURCE_REPO/docs/changelogs_ja.json" DST_CN="$TARGET_REPO/docs/public/cn/changelogs.json" DST_EN="$TARGET_REPO/docs/public/en/changelogs.json" +DST_TW="$TARGET_REPO/docs/public/tw/changelogs.json" +DST_JA="$TARGET_REPO/docs/public/ja/changelogs.json" # 目标文件必须预先存在,不允许自动创建。 -if [[ ! -f "$DST_CN" || ! -f "$DST_EN" ]]; then +if [[ ! -f "$DST_CN" || ! -f "$DST_EN" || ! -f "$DST_TW" || ! -f "$DST_JA" ]]; then echo "错误: chatlab.fun 目标 changelog 文件不存在,请先手动准备" >&2 exit 1 fi -if [[ ! -f "$SRC_CN" || ! -f "$SRC_EN" ]]; then +if [[ ! -f "$SRC_CN" || ! -f "$SRC_EN" || ! -f "$SRC_TW" || ! -f "$SRC_JA" ]]; then echo "错误: 源仓库 changelog 文件不存在" >&2 exit 1 fi cp "$SRC_CN" "$DST_CN" cp "$SRC_EN" "$DST_EN" +cp "$SRC_TW" "$DST_TW" +cp "$SRC_JA" "$DST_JA" # 仅提交目标文档文件,避免提交其他改动。 -git -C "$TARGET_REPO" add docs/public/cn/changelogs.json docs/public/en/changelogs.json +git -C "$TARGET_REPO" add \ + docs/public/cn/changelogs.json \ + docs/public/en/changelogs.json \ + docs/public/tw/changelogs.json \ + docs/public/ja/changelogs.json if git -C "$TARGET_REPO" diff --cached --quiet; then echo "错误: chatlab.fun 没有可提交的 changelog 变更" >&2 diff --git a/src/assets/docs/agreement_ja.md b/src/assets/docs/agreement_ja.md new file mode 100644 index 0000000..da64493 --- /dev/null +++ b/src/assets/docs/agreement_ja.md @@ -0,0 +1,39 @@ +ChatLab へようこそ。ChatLab は、チャット履歴の分析に特化した、無料・オープンソース・ローカルファーストのアプリです。 + +あなた自身のチャットデータを主体的に扱えることを大切にしていますが、その前提は合法かつ適切な利用であることです。以下の内容を必ずご確認ください。 + +## 1. コア機能と独立性 + +- **分析専用で、取得機能は提供しません**:本ツールは、あなたがすでにエクスポートしたチャット履歴を分析するためのものです。復号、パケットキャプチャ、抽出ツールは一切提供していません。第三者製チャットアプリからデータを取得する行為は、利用者自身の責任で行ってください。 +- **独立した第三者ツールです**:本プロジェクトは独立したオープンソースプロジェクトであり、特定の企業、サービス、団体とは関係ありません。 + +## 2. データのプライバシーとセキュリティ + +- **ローカル完結の分析**:初期設定では、チャット履歴の保存と分析はすべて端末内で行われ、**内容はアップロードされません**。 +- **AI 機能利用時の例外**:AI 機能を有効にした場合、関連するチャットデータは、あなたが設定した外部モデルサービス(DeepSeek、Qwen など)に送信されます。**国家機密や高度に機微な個人情報は送信しないでください**。この点に関するリスクは利用者自身が負うものとします。 +- **必要なネットワーク通信**:アプリは更新通知、ヘルプ文書、システムプロンプトなどを取得するために通信を行います。これらは最新情報を取得するためのもので、チャット内容を送信するものではありません。 +- **匿名の利用統計**:製品改善のため、バージョン番号や OS 種別などの**非機微情報**を収集することがあります。個人を特定する情報は含まれず、設定からいつでも無効にできます。 + +## 3. データ利用の範囲と制限 + +- **正当な権限があるデータのみ扱ってください**:処理できるのは、**自分が参加した**チャット履歴に限られます。分析対象に他者のプライバシー、特に**個人チャットの相手やグループチャットの参加者**が含まれる場合は、事前に相手の同意を得てください。 +- **禁止される用途**: + - 他者の未承認のプライバシーを窃取、監視、または分析する目的での使用は**厳禁**です。 + - 違法な手段(不正アクセス、アカウント窃取、データハイジャックなど)で取得したチャット記録の分析は**厳禁**です。 + - 分析結果を嫌がらせ、詐欺、晒し行為(人肉検索)、または他者の権利を侵害するいかなる行為に使用することは**厳禁**です。 + - 本ソフトウェアが生成した分析コンテンツ(AI 生成コンテンツを含む)を使用して、虚偽または誤解を招く情報を捏造または拡散することは**厳禁**です。 + +## 4. リスクに関する注意事項 + +- **公式配布元のみ利用してください**:本ソフトウェアは [chatlab.fun](https://chatlab.fun) または [GitHub Releases](https://github.com/hellodigua/ChatLab/releases) からのみ入手してください。 +- **サプライチェーンリスクに注意してください**:本プロジェクトは完全オープンソースのため、第三者が再配布版を作成できます。非公式版には悪意あるコードが含まれる可能性があり、API トークン、チャット履歴、ローカルデータの漏えいにつながるおそれがあります。 +- **結果の正確性は保証されません**:ソフトウェアや AI が生成する分析結果には、誤りやハルシネーションが含まれる可能性があります。参考情報として扱い、法的証拠や正式な判断材料には使用しないでください。 + +## 5. 免責事項 + +- **利用目的**:本ソフトウェアは、技術研究、学習、交流を目的として提供されています。 +- **各プラットフォームの規約遵守は利用者の責任です**:チャット履歴の取得や分析が、元のサービスの利用規約に抵触した結果としてアカウント制限などが発生しても、作者は責任を負いません。 +- **利用は自己責任です**:本ソフトウェアの利用によって生じたいかなる結果も、利用者自身の責任となります。これにはデータ損失、プライバシー上の争い、法的責任などが含まれます。 +- **本声明への同意**:本ソフトウェアをダウンロード、インストール、または使用した時点で、本声明の内容を読み、理解し、同意したものとみなされます。同意できない場合は、直ちに使用を中止し、関連ファイルを削除してください。 + +改訂日:2026年1月7日 diff --git a/src/assets/docs/agreement_zh_tw.md b/src/assets/docs/agreement_zh_tw.md new file mode 100644 index 0000000..eafe8f7 --- /dev/null +++ b/src/assets/docs/agreement_zh_tw.md @@ -0,0 +1,39 @@ +歡迎使用 ChatLab。這是一款免費、開源、以本機分析為主,專注於聊天紀錄分析的應用程式。 + +我們希望你能自由掌握自己的聊天資料,但前提必須是合法且合規。請務必先閱讀以下說明: + +## 1. 核心功能與獨立性 + +- **僅分析,不提供匯出能力**:本工具僅用於分析你已自行匯出的聊天紀錄。我們不提供任何解密、抓包或匯出工具;從第三方聊天軟體取得資料屬於你的個人行為。 +- **獨立第三方工具**:本專案為獨立開源專案,與任何第三方公司、平台或組織均無隸屬關係。 + +## 2. 資料隱私與安全 + +- **純本機分析**:預設情況下,聊天紀錄的儲存與分析都在你的裝置本機完成,**不會上傳內容**。 +- **AI 功能例外**:若你主動啟用 AI 功能,相關聊天資料會傳送至你設定的第三方模型服務商(如 DeepSeek、通義千問等)。**請勿傳送涉及國家機密或高度敏感的個人隱私資訊**,相關風險需自行承擔。 +- **必要連網功能**:應用程式會連網取得更新通知、說明文件及系統提示詞等內容。此類連線僅用於取得最新資訊,不會主動上傳聊天資料。 +- **匿名使用統計**:為了改善產品,軟體可能收集**非敏感**資料(如版本號、作業系統類型等)。這些資料不包含個人資訊,僅用於協助後續開發決策,且可在設定中隨時關閉。 + +## 3. 資料授權與使用限制 + +- **合法授權原則**:你僅可處理你**本人參與**的聊天紀錄。若分析內容涉及他人隱私,尤其是**私聊對象或群聊成員**,請務必先取得對方的知情同意。 +- **禁止非法用途**: + - **嚴禁**用於竊取、監控或分析未經授權的他人隱私。 + - **嚴禁**分析透過非法手段(如非法入侵、帳號竊取、資料劫持等)取得的聊天記錄。 + - **嚴禁**將分析結果用於騷擾、詐騙、人肉搜尋或任何侵犯他人權益的行為。 + - **嚴禁**利用本軟體產生的分析內容(含 AI 產生內容)編造、傳播虛假資訊或誤導性資訊。 + +## 4. 風險警告 + +- **官方下載管道**:請僅透過 [chatlab.fun](https://chatlab.fun) 或 [GitHub Release](https://github.com/hellodigua/ChatLab/releases) 下載本軟體。 +- **供應鏈風險**:本專案程式碼完全開源,任何人都可再次打包。非官方版本可能植入惡意程式碼,導致 API Token、聊天紀錄或本機資料外洩,請務必提高警覺。 +- **結果準確性**:軟體與 AI 產生的分析結果可能有誤或出現「幻覺」,僅供參考,不應作為法律證據或正式決策依據。 + +## 5. 免責聲明 + +- **軟體用途**:本軟體僅供技術研究、學習與交流使用。 +- **平台合規性**:取得聊天紀錄行為的合規性由使用者自行負責。若分析行為違反原始資料來源平台的服務條款,導致帳號受限或其他後果,作者不承擔任何責任。 +- **責任自負**:使用本軟體所產生的一切後果,包括但不限於資料遺失、隱私糾紛或法律責任,均由使用者自行承擔。 +- **接受聲明**:下載、安裝或使用本軟體,即表示你已閱讀、理解並同意本聲明全部內容;若不同意,請立即停止使用並刪除相關程式。 + +修訂日期:2026-01-07 diff --git a/src/components/UI/DatePicker.vue b/src/components/UI/DatePicker.vue index c982944..90d0f5c 100644 --- a/src/components/UI/DatePicker.vue +++ b/src/components/UI/DatePicker.vue @@ -36,7 +36,11 @@ const emit = defineEmits<{ const popoverOpen = ref(false) // 日历组件的 locale -const calendarLocale = computed(() => (locale.value === 'zh-CN' ? 'zh-CN' : 'en-US')) +const calendarLocale = computed(() => { + if (locale.value.startsWith('zh')) return 'zh-CN' + if (locale.value === 'ja-JP') return 'ja-JP' + return 'en-US' +}) // 辅助函数:将字符串日期转换为 CalendarDate function stringToCalendarDate(dateStr: string): CalendarDate | undefined { diff --git a/src/components/analysis/AIChat/ChatMessage.vue b/src/components/analysis/AIChat/ChatMessage.vue index 1d7b69e..2c2db2b 100644 --- a/src/components/analysis/AIChat/ChatMessage.vue +++ b/src/components/analysis/AIChat/ChatMessage.vue @@ -98,7 +98,7 @@ function formatTimeParams(params: Record): string { // 使用 year/month/day/hour 组合 if (params.year) { - if (locale.value === 'zh-CN') { + if (locale.value.startsWith('zh')) { let result = `${params.year}年` if (params.month) { result += `${params.month}月` diff --git a/src/components/analysis/AITab.vue b/src/components/analysis/AITab.vue index a2a1731..f286181 100644 --- a/src/components/analysis/AITab.vue +++ b/src/components/analysis/AITab.vue @@ -10,7 +10,7 @@ const { t, locale } = useI18n() // 关注链接配置 const followLink = computed(() => { - if (locale.value === 'zh-CN') { + if (locale.value.startsWith('zh')) { return { url: 'https://www.xiaohongshu.com/user/profile/6841741e000000001d0091b4', name: '@地瓜', diff --git a/src/components/common/settings/AI/AIModelEditModal.vue b/src/components/common/settings/AI/AIModelEditModal.vue index 9fb2a6a..e638c62 100644 --- a/src/components/common/settings/AI/AIModelEditModal.vue +++ b/src/components/common/settings/AI/AIModelEditModal.vue @@ -109,7 +109,7 @@ const presetProviders = computed(() => { // 排除 openai-compatible(通过另一个配置类型访问) if (p.id === 'openai-compatible') return false // 非中文环境下,排除中国市场特定提供商 - if (settingsStore.locale !== 'zh-CN' && CHINA_MARKET_PROVIDERS.includes(p.id)) { + if (!settingsStore.locale.startsWith('zh') && CHINA_MARKET_PROVIDERS.includes(p.id)) { return false } return true diff --git a/src/components/common/settings/BasicSettingsTab.vue b/src/components/common/settings/BasicSettingsTab.vue index 9e7a205..70288b7 100644 --- a/src/components/common/settings/BasicSettingsTab.vue +++ b/src/components/common/settings/BasicSettingsTab.vue @@ -7,6 +7,7 @@ import { useSettingsStore } from '@/stores/settings' import { useColorMode } from '@vueuse/core' import { availableLocales, type LocaleType } from '@/i18n' import NetworkSettingsSection from './NetworkSettingsSection.vue' +import UITabs from '@/components/UI/Tabs.vue' const { t } = useI18n() @@ -70,8 +71,8 @@ watch( {{ t('settings.basic.language.description') }}

-
- +
+
diff --git a/src/config/prompts.ts b/src/config/prompts.ts index e494db9..553d6e9 100644 --- a/src/config/prompts.ts +++ b/src/config/prompts.ts @@ -13,7 +13,7 @@ import type { PromptPreset } from '@/types/ai' // ==================== 类型定义 ==================== -export type LocaleType = 'zh-CN' | 'en-US' +export type LocaleType = 'zh-CN' | 'en-US' | 'zh-TW' | 'ja-JP' // ==================== 国际化内容配置 ==================== @@ -201,10 +201,11 @@ export function getLockedPromptSectionPreview( ownerInfo?: OwnerInfoPreview, locale: LocaleType = 'zh-CN' ): string { - const content = i18nContent[locale] || i18nContent['zh-CN'] + const contentKey = locale.startsWith('zh') ? 'zh-CN' : 'en-US' + const content = i18nContent[contentKey] || i18nContent['zh-CN'] const now = new Date() - const dateLocale = locale === 'zh-CN' ? 'zh-CN' : 'en-US' + const dateLocale = locale.startsWith('zh') ? 'zh-CN' : locale === 'ja-JP' ? 'ja-JP' : 'en-US' const currentDate = now.toLocaleDateString(dateLocale, { year: 'numeric', month: 'long', diff --git a/src/i18n/index.ts b/src/i18n/index.ts index e4d2774..08ca3d1 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,7 +1,9 @@ import { createI18n } from 'vue-i18n' import zhCN from './locales/zh-CN' import enUS from './locales/en-US' -import { detectSystemLocale, type LocaleType } from './types' +import zhTW from './locales/zh-TW' +import jaJP from './locales/ja-JP' +import { detectSystemLocale, isValidLocale, type LocaleType } from './types' // 导出类型 export type { LocaleType } from './types' @@ -11,6 +13,9 @@ export { detectSystemLocale, isFeatureSupported, featureLocaleRestrictions, + isChineseLike, + getDayjsLocale, + isValidLocale, } from './types' // 用于标记用户是否明确设置过语言的 key @@ -26,12 +31,11 @@ function getInitialLocale(): LocaleType { const hasUserSetLocale = localStorage.getItem(LOCALE_SET_KEY) if (hasUserSetLocale) { - // 用户已设置过,尝试从 Pinia persist 恢复 try { const piniaSettings = localStorage.getItem(PINIA_SETTINGS_KEY) if (piniaSettings) { const parsed = JSON.parse(piniaSettings) - if (parsed.locale === 'zh-CN' || parsed.locale === 'en-US') { + if (isValidLocale(parsed.locale)) { return parsed.locale } } @@ -40,7 +44,6 @@ function getInitialLocale(): LocaleType { } } - // 首次启动或无法恢复,检测系统语言 return detectSystemLocale() } @@ -48,12 +51,14 @@ function getInitialLocale(): LocaleType { * 创建 i18n 实例 */ export const i18n = createI18n({ - legacy: false, // 使用 Composition API 模式 - locale: getInitialLocale(), // 首次启动检测系统语言 - fallbackLocale: 'en-US', // 回退语言 + legacy: false, + locale: getInitialLocale(), + fallbackLocale: 'en-US', messages: { 'zh-CN': zhCN, 'en-US': enUS, + 'zh-TW': zhTW, + 'ja-JP': jaJP, }, }) diff --git a/src/i18n/locales/en-US/home.json b/src/i18n/locales/en-US/home.json index f7783e2..5819a66 100644 --- a/src/i18n/locales/en-US/home.json +++ b/src/i18n/locales/en-US/home.json @@ -100,6 +100,10 @@ "import": "Import ({count})", "noChats": "No chats found" }, + "footer": { + "website": "Website", + "terms": "Terms of Use" + }, "changelog": { "title": "Changelog", "subtitle": "View all version updates", diff --git a/src/i18n/locales/ja-JP/ai.json b/src/i18n/locales/ja-JP/ai.json new file mode 100644 index 0000000..891607d --- /dev/null +++ b/src/i18n/locales/ja-JP/ai.json @@ -0,0 +1,301 @@ +{ + "tab": { + "chatExplorer": "対話で探索", + "sqlLab": "SQL ラボ", + "featureInDev": "{name}機能は開発中です", + "comingSoon": "近日公開...", + "followNotice": "新機能のお知らせは公式 Xiaohongshu で発信しています" + }, + "chat": { + "welcome": { + "configReady": "✅ AI サービスの設定が完了しました。すぐにチャットを始められます。", + "configNeeded": "**ご注意**:ご利用前にサイドバー下部の「設定」から AI サービスを設定してください ⚙️" + }, + "capture": "会話を画像保存", + "scrollToBottom": "下へ移動", + "input": { + "placeholder": "質問を入力..." + }, + "message": { + "userAvatar": "ユーザーアバター", + "calling": "実行中", + "tools": { + "search_messages": "チャット履歴を検索", + "get_recent_messages": "最近のメッセージを取得", + "get_member_stats": "メンバー統計を取得", + "get_time_stats": "時間分布を取得", + "get_members": "メンバー一覧を取得", + "get_member_name_history": "ニックネーム履歴を取得", + "get_conversation_between": "やり取り履歴を取得", + "get_message_context": "コンテキストを取得", + "semantic_search_messages": "🔍 セマンティック検索" + }, + "generating": "回答を作成中...", + "think": { + "labels": { + "think": "思考", + "analysis": "分析", + "reasoning": "推論", + "reflection": "振り返り", + "other": "思考({tag})" + }, + "loading": "思考中...", + "duration": "{seconds}秒" + }, + "toolParams": { + "keywords": "キーワード", + "time": "時間", + "getMessages": "{count} 件のメッセージを取得", + "limit": "{count} 件に制限", + "contextWithMessages": "{msgCount} 件のメッセージの前後各 {contextSize} 件のコンテキスト", + "context": "前後各 {size} 件のコンテキスト", + "topMembers": "上位 {count} 名のメンバー", + "search": "検索", + "getMemberList": "メンバー一覧を取得", + "memberId": "メンバーID", + "timeStats": { + "hourly": "時間別", + "weekday": "曜日別", + "daily": "日別" + } + } + }, + "thinking": { + "processingResult": "結果を整理中", + "called": "実行したツール:", + "analyzing": "問題を分析中..." + }, + "statusBar": { + "preset": { + "default": "既定", + "groupTitle": "グループチャット用プロンプト", + "privateTitle": "個人チャット用プロンプト", + "manage": "プロンプト管理" + }, + "model": { + "title": "AI モデル設定", + "notConfigured": "AI 未設定", + "empty": "設定がありません", + "manage": "設定を管理", + "switchFailed": "モデルの切り替えに失敗しました" + }, + "messageLimit": { + "label": "メッセージ上限:", + "title": "1回の送信で含む最大メッセージ数。クリックして設定" + }, + "agent": { + "label": "Agent", + "contextTokens": "コンテキスト", + "stats": "セグメント {segment} ・タグ {tags}", + "phase": { + "preparing": "準備中", + "thinking": "思考中", + "tool_running": "ツール実行中", + "responding": "応答中", + "completed": "完了", + "aborted": "停止", + "error": "エラー" + }, + "phaseShort": { + "preparing": "準備", + "thinking": "思考", + "tool_running": "ツール", + "responding": "生成", + "completed": "完了", + "aborted": "停止", + "error": "エラー" + } + }, + "tokenUsageTitle": "この会話で使用した Token 合計", + "export": { + "label": "エクスポート", + "title": "現在のチャットをエクスポート" + }, + "log": { + "label": "ログ", + "title": "現在の AI ログファイルを開く", + "openFailed": "ログを開けませんでした", + "openFailedDesc": "しばらくしてから再度お試しください" + }, + "status": { + "connected": "AI 接続中", + "notConfigured": "グローバル設定で AI サービスを設定してください" + } + }, + "conversation": { + "title": "AI チャット履歴", + "newChat": "新規チャット", + "empty": "履歴はありません", + "startNew": "新しいチャットを始める", + "titlePlaceholder": "タイトルを入力...", + "export": { + "createdAt": "作成日時", + "user": "ユーザー", + "assistant": "AI アシスタント", + "noMessages": "会話にメッセージがありません" + } + }, + "dataSource": { + "title": "データソース", + "keywords": "キーワード:", + "empty": "データがありません", + "emptyHint": "質問を送信すると、関連する記録がここに表示されます", + "totalRecords": "全 {count} 件の記録", + "loadMore": "さらに読み込む" + } + }, + "sqlLab": { + "editor": { + "errorEmptySQL": "SQL 文を入力してください", + "placeholder": "SQL クエリを入力...", + "tip": "ヒント:左側の列名をダブルクリックすると SQL に挿入できます", + "history": "履歴", + "aiGenerate": "AI 生成", + "shortcut": "Ctrl/⌘ + Enter で実行", + "run": "実行" + }, + "schema": { + "tables": "テーブル", + "doubleClickToInsert": "ダブルクリックで SQL に挿入", + "primaryKey": "主キー" + }, + "result": { + "queryError": "クエリエラー", + "rows": "{count} 行", + "readableTime": "タイムスタンプを日付に変換", + "viewChat": "チャット履歴を見る", + "summarize": "要約する", + "pageSize": "表示件数", + "pageInfo": "{current} / {total} ページ", + "emptyResult": "クエリ結果が空です", + "initialState": "SQL 文を入力して実行すると結果が表示されます", + "initialHint": "SELECT のみ対応しています。速度向上のため LIMIT の指定をおすすめします", + "summaryTitle": "AI 結果サマリー", + "analyzing": "AI が結果を分析中...", + "errorSummary": "要約に失敗しました" + }, + "generate": { + "title": "AI で SQL 生成", + "description": "自然言語でクエリ内容を説明すると、AI が対応する SQL 文を自動生成します。", + "placeholder": "例:発言数が最も多い上位10名のメンバーを検索、1日あたりのメッセージ数を集計、「旅行」というキーワードを含むメッセージを検索...", + "errorEmptyPrompt": "クエリ要件を入力してください", + "errorGenerate": "AI 生成に失敗しました", + "aiOutput": "AI 出力", + "waitingAI": "AI の応答待ち...", + "sqlStatement": "SQL 文", + "explanation": "説明", + "generateSQL": "SQL を生成", + "useSQL": "SQL を使用", + "run": "実行" + }, + "history": { + "title": "AI 生成履歴", + "execute": "実行", + "empty": "履歴はありません", + "justNow": "たった今", + "minutesAgo": "{count} 分前", + "hoursAgo": "{count} 時間前", + "today": "今日" + } + }, + "assistant": { + "selector": { + "title": "アシスタントを選んでチャットを始めましょう", + "subtitle": "各アシスタントは得意分野が異なります。目的に合うものを選んでください", + "noAssistants": "現在のシーンで利用可能なアシスタントはありません", + "manage": "アシスタント管理", + "addNew": "アシスタントを追加" + }, + "config": { + "viewTitle": "設定を確認", + "createTitle": "アシスタントを作成", + "editTitle": "アシスタント設定", + "betaWarning": "現在はベータ版のため、設定仕様が今後変わる可能性があります。プロンプトや関連設定は控えを残してください。", + "tabs": { + "basic": "基本設定", + "tools": "ツール管理" + }, + "name": "名前", + "systemPrompt": "システムプロンプト", + "chatType": "対応チャットタイプ", + "chatTypeAll": "すべて", + "chatTypeGroup": "グループチャット", + "chatTypePrivate": "個人チャット", + "locale": "対応言語", + "localeHint": "未選択なら全言語が対象です", + "presetQuestions": "プリセット質問", + "addQuestion": "質問を追加...", + "builtinTools": "標準ツール", + "selectAll": "すべて選択", + "deselectAll": "すべて解除", + "builtinToolsHint": "何も選ばなければ全ツールを使います。選択した場合は、そのツールだけが有効になります。", + "toolGroupQuery": "データ検索ツール", + "toolGroupSql": "SQL 分析ツール", + "customSqlTools": "カスタム SQL ツール", + "customSqlToolsHint": "宣言型 SQL ツールは AI の Function Calling として自動登録され、パラメータ付き SQL を実行して結果を整形します。", + "toolUntitled": "ツール {index}(無名)", + "toolName": "ツール名", + "toolNamePlaceholder": "例: top_active_members", + "toolDesc": "ツールの説明", + "toolDescPlaceholder": "最もアクティブなメンバーのランキングを検索", + "toolParams": "パラメータ定義(JSON Schema)", + "toolQuery": "SQL クエリ(@paramName でパラメータを参照)", + "toolQueryPlaceholder": "SELECT sender_name, COUNT(*) as cnt FROM message ...", + "toolRowTemplate": "行テンプレート({columnName} プレースホルダー)", + "toolRowTemplatePlaceholder": "{sender_name}: {cnt} 件のメッセージ", + "toolFallback": "結果なし時のメッセージ", + "toolFallbackPlaceholder": "関連データが見つかりませんでした", + "toolFallbackDefault": "関連データが見つかりませんでした", + "toolSummary": "サマリーテンプレート(任意、{rowCount} は行数)", + "toolSummaryPlaceholder": "全 {rowCount} 件のレコード:", + "addCustomTool": "カスタム SQL ツールを追加", + "resetDefault": "デフォルトに戻す", + "create": "作成" + }, + "market": { + "title": "アシスタント管理", + "tabs": { + "local": "ローカルアシスタント", + "market": "アシスタントマーケット" + }, + "localHint": "インポート済みのアシスタントを管理。設定変更や削除が可能です", + "marketHint": "内蔵アシスタントテンプレートを閲覧。インポート後すぐに使用・編集できます", + "default": "デフォルト", + "updateAvailable": "更新あり", + "reimport": "再インポート", + "imported": "インポート済み", + "import": "インポート", + "viewConfig": "設定を表示", + "duplicate": "複製", + "addAssistant": "アシスタントを追加", + "noLocal": "インポート済みのアシスタントはありません", + "noCatalog": "利用可能なアシスタントテンプレートはありません" + }, + "fallbackName": "アシスタント", + "duplicateSuffix": "(コピー)", + "toast": { + "loadFailed": "読み込みに失敗しました", + "createSuccess": "作成しました", + "createFailed": "作成に失敗しました", + "saveSuccess": "保存しました", + "saveFailed": "保存に失敗しました", + "resetSuccess": "デフォルトに戻しました", + "resetFailed": "リセットに失敗しました", + "unknownError": "不明なエラー" + }, + "builtinToolDesc": { + "search_messages": "チャットメッセージを検索", + "get_recent_messages": "最近のメッセージを取得", + "get_message_context": "メッセージのコンテキストを取得", + "get_conversation_between": "2人のやり取りを取得", + "get_members": "グループメンバー一覧を取得", + "get_member_stats": "メンバー統計を取得", + "get_member_name_history": "メンバーの名前変更履歴を取得", + "get_time_stats": "時間統計を取得", + "search_sessions": "セッションを検索", + "get_session_messages": "セッションメッセージを取得", + "get_session_summaries": "セッション要約を取得", + "semantic_search_messages": "セマンティック検索" + } + } +} diff --git a/src/i18n/locales/ja-JP/analysis.json b/src/i18n/locales/ja-JP/analysis.json new file mode 100644 index 0000000..40ea5d8 --- /dev/null +++ b/src/i18n/locales/ja-JP/analysis.json @@ -0,0 +1,157 @@ +{ + "groupChat": { + "title": "グループ分析", + "loading": "分析データを読み込み中...", + "loadError": "セッションデータを読み込めません", + "description": "{dateRange}、{memberCount} 人のメンバーが合計 {messageCount} 件のメッセージをやり取りしました" + }, + "privateChat": { + "title": "個人チャット分析", + "loading": "分析データを読み込み中...", + "loadError": "セッションデータを読み込めません", + "description": "{dateRange}、合計 {messageCount} 件のメッセージ" + }, + "tabs": { + "overview": "概要", + "view": "閲覧", + "ranking": "ランキング", + "groupQuotes": "名言集", + "quotes": "名言集", + "members": "メンバー", + "member": "メンバー", + "ai": "AIラボ" + }, + "yearFilter": { + "allTime": "全期間", + "year": "{year}年" + }, + "tooltip": { + "chatViewer": "チャット記録ビューア", + "sessionIndex": "セッション索引", + "incrementalImport": "追加インポート" + }, + "incremental": { + "title": "チャット記録を追加", + "description": "「{name}」に追加するチャット履歴ファイルを選択してください。重複は自動で除外されます。", + "selectFile": "チャット記録ファイルを選択", + "dropHint": "クリックして選択、またはファイルをここにドラッグ", + "analyzing": "ファイルを分析中...", + "totalInFile": "ファイル内のメッセージ数", + "newMessages": "新規メッセージ", + "duplicates": "重複メッセージ", + "noNewMessages": "新しいメッセージはありません。すべて取り込み済みです", + "importing": "インポート中...", + "import": "{count} 件を取り込む", + "success": "インポート成功", + "successDetail": "{count} 件の新しいメッセージを追加しました", + "failed": "インポートに失敗しました" + }, + "subTabs": { + "view": { + "message": "メッセージ", + "interaction": "インタラクション分析", + "ranking": "ランキング" + }, + "quotes": { + "wordcloud": "ワードクラウド", + "hotRepeat": "盛り上がった復唱", + "catchphrase": "口癖", + "keywordAnalysis": "キーワード分析" + }, + "member": { + "memberList": "メンバー一覧", + "relationships": "関係性", + "cluster": "やり取り頻度", + "nicknameHistory": "ニックネーム変更履歴" + } + }, + "overview": { + "messageTypeDistribution": "メッセージタイプ分布", + "memberDistribution": "メンバー別発言割合", + "others": "その他", + "messageRatio": "メッセージ割合", + "messageUnit": "件", + "memberComparison": "双方のメッセージ割合", + "statCards": { + "dailyAvgMessages": "1日あたりのメッセージ", + "messagesCount": "{count} 件", + "daysCount": "合計 {count} 日間", + "imageMessages": "画像メッセージ", + "imagesCount": "{count} 枚", + "peakHour": "最も活発な時間帯:", + "mostActiveWeekday": "最も活発な曜日", + "messagesOnDay": "{count} 件のメッセージ", + "weekendActivity": "週末の活発度", + "weekendRatio": "週末メッセージの割合", + "mostActiveDate": "最も活発な日", + "activeDays": "アクティブ日数", + "slashDays": "/ {count} 日", + "consecutiveStreak": "連続日数", + "daysStreak": "{count} 日間", + "longestStreak": "最長連続アクティブ", + "activityRate": "アクティブ率", + "activeDaysRatio": "メッセージのある日の割合" + }, + "dailyTrend": { + "title": "日別メッセージトレンド" + }, + "identity": { + "privateChat": "プライベートチャット", + "groupChat": "グループチャット", + "analysisReport": "分析レポート", + "totalMessages": "メッセージ総数", + "durationDays": "期間日数", + "dailyAvgMessages": "日平均メッセージ" + }, + "tools": "よく使うツール" + }, + "messageExport": { + "title": "チャット記録をエクスポート" + }, + "filter": { + "title": "詳細フィルター", + "conditionMode": "条件フィルター", + "sessionMode": "セッションフィルター", + "history": "履歴", + "execute": "絞り込む", + "export": "フィルター結果をエクスポート", + "keywords": "キーワード", + "keywordsHint": "複数のキーワードに対応、OR条件でマッチします", + "keywordPlaceholder": "キーワードを入力してEnterで追加", + "timeRange": "期間", + "allTime": "すべて", + "today": "今日", + "lastWeek": "過去7日間", + "lastMonth": "過去1ヶ月", + "last3Months": "過去3ヶ月", + "lastYear": "過去1年", + "customTime": "カスタム", + "senders": "送信者", + "sendersHint": "任意。空欄の場合は制限なし", + "searchMember": "メンバーを検索...", + "noMembers": "メンバーがいません", + "selectedCount": "{count} 人を選択済み", + "contextSize": "前後表示", + "messages": "件のメッセージ", + "searchSession": "日付を検索...", + "selectedSessions": "{count} 個のセッションを選択中", + "noSessions": "セッション索引がありません", + "filtering": "フィルター中...", + "emptyHint": "フィルター条件を設定してから実行してください", + "noResults": "一致するメッセージが見つかりません", + "blockTitle": "会話ブロック {index}", + "stats": { + "blocks": "会話ブロック", + "messages": "メッセージ数", + "hits": "ヒット数", + "chars": "文字数" + }, + "historyTitle": "フィルター履歴", + "noHistory": "履歴がありません", + "loadMore": "もっと読み込む", + "allLoaded": "すべて読み込み済み", + "exportSuccess": "エクスポートに成功しました", + "exportFailed": "エクスポートに失敗しました", + "exportPreparing": "エクスポート準備中..." + } +} diff --git a/src/i18n/locales/ja-JP/common.json b/src/i18n/locales/ja-JP/common.json new file mode 100644 index 0000000..fe075a7 --- /dev/null +++ b/src/i18n/locales/ja-JP/common.json @@ -0,0 +1,129 @@ +{ + "confirm": "確認", + "cancel": "キャンセル", + "delete": "削除", + "save": "保存", + "edit": "編集", + "add": "追加", + "rename": "名前変更", + "capture": "スクリーンショット", + "close": "閉じる", + "back": "戻る", + "done": "完了", + "retry": "リトライ", + "loading": "読み込み中...", + "initializing": "初期化中...", + "noData": "データがありません", + "export": "エクスポート", + "exportSuccess": "エクスポートに成功しました", + "exportFailed": "エクスポートに失敗しました", + "generating": "生成中...", + "regenerate": "再生成", + "openFolder": "フォルダーを開く", + "errorNoAIConfig": "先に設定でAIサービスを構成してください", + "reset": "リセット", + "error": { + "general": "操作に失敗しました。再試行してください", + "network": "ネットワークエラー", + "unknown": "不明なエラー" + }, + "attachment": { + "image": "画像", + "video": "動画", + "audio": "音声", + "file": "ファイル", + "sticker": "スタンプ", + "link": "リンク" + }, + "unit": { + "messages": "件のメッセージ", + "members": "人", + "days": "日" + }, + "messageType": { + "text": "テキスト", + "image": "画像", + "voice": "音声", + "video": "動画", + "file": "ファイル", + "emoji": "絵文字", + "link": "リンク", + "location": "位置情報", + "redPacket": "お年玉", + "transfer": "送金", + "poke": "ツンツン", + "call": "通話", + "share": "共有", + "reply": "返信", + "forward": "転送", + "contact": "連絡先カード", + "system": "システム", + "recall": "取り消し", + "other": "その他", + "unknown": "不明" + }, + "weekday": { + "mon": "月曜", + "tue": "火曜", + "wed": "水曜", + "thu": "木曜", + "fri": "金曜", + "sat": "土曜", + "sun": "日曜" + }, + "month": { + "jan": "1月", + "feb": "2月", + "mar": "3月", + "apr": "4月", + "may": "5月", + "jun": "6月", + "jul": "7月", + "aug": "8月", + "sep": "9月", + "oct": "10月", + "nov": "11月", + "dec": "12月" + }, + "agreement": { + "title": "プライバシーポリシーと利用規約", + "subtitle": "利用前に必ずお読みください", + "disagree": "同意しない", + "agree": "内容を確認して同意する", + "updateNotice": "規約内容が更新されました。再度お読みいただき、ご確認ください" + }, + "datePicker": { + "selectDate": "日付を選択", + "clearDate": "日付をクリア" + }, + "timeSelect": { + "mode": { + "recent": "最近", + "quarter": "四半期別", + "year": "年別", + "custom": "カスタム" + }, + "recent": { + "halfYear": "半年間", + "oneYear": "1年間", + "twoYears": "2年間", + "fiveYears": "5年間", + "all": "すべて" + }, + "display": { + "recent180": "最近半年間", + "recent365": "最近1年間", + "recent730": "最近2年間", + "recent1825": "最近5年間" + }, + "quarter": { + "label": "{year}年 第{quarter}四半期" + }, + "year": { + "label": "{year}年" + } + }, + "userSelect": { + "allMembers": "すべてのメンバー" + } +} diff --git a/src/i18n/locales/ja-JP/home.json b/src/i18n/locales/ja-JP/home.json new file mode 100644 index 0000000..2c6e540 --- /dev/null +++ b/src/i18n/locales/ja-JP/home.json @@ -0,0 +1,124 @@ +{ + "title": "ChatLab", + "subtitle": "チャット履歴分析ラボ", + "features": { + "privacy": { + "title": "プライバシー最優先", + "description": "チャット履歴はローカルに保存・分析され、外部へ送信されません。" + }, + "analysis": { + "title": "多角的分析", + "description": "ランキングから名言集まで、多角的にグループチャットを分析し、埋もれた面白さを見つけます。" + }, + "ai": { + "title": "AI Agent", + "description": "内蔵 AI Agent がグループチャットに関する質問に答え、データの裏にある流れや面白い話題を掘り起こします。" + } + }, + "import": { + "dropHint": "マウスを離してファイルをインポート", + "clickHint": "クリックして選択、またはチャット記録をドラッグ&ドロップでインポート", + "multipleHint": "複数ファイルの同時選択・ドラッグに対応しています", + "selectFiles": "チャット記録ファイルを選択", + "chatRecords": "チャット記録", + "allFiles": "すべてのファイル", + "progress": { + "detecting": "フォーマットを検出中...", + "reading": "ファイルを読み込み中...", + "parsing": "メッセージを解析中...", + "saving": "データベースに書き込み中...", + "done": "インポート完了", + "error": "インポート中断" + }, + "processed": "{count} 件のメッセージを処理しました", + "tutorial": "こちらでチャット記録のインポート方法を確認", + "viewLog": "インポートログを表示", + "cannotReadPath": "ファイルパスを読み取れません", + "preparing": "インポートを準備中...", + "errors": { + "no_file_selected": "ファイルが選択されていません", + "import_failed": "インポートに失敗しました", + "unrecognized_format": "認識できないファイル形式です", + "no_messages": "メッセージを検出できませんでした。ファイル形式が正しいか確認してください" + }, + "diagnostics": { + "format": "検出フォーマット:", + "received": "受信メッセージ:", + "written": " / 書き込み:", + "skipped": " / スキップ:" + }, + "batch": { + "importing": "一括インポート中", + "progressCount": "{current} / {total} 件目", + "cancel": "インポートをキャンセル", + "waiting": "待機中", + "skipped": "スキップ済み", + "success": "インポート成功", + "completed": "一括インポート完了", + "summary": "成功 {success} 件、失敗 {failed} 件、スキップ {cancelled} 件", + "view": "表示" + }, + "options": { + "title": "その他のオプション", + "mergeImport": "結合インポート", + "mergeImportHint": "チェックすると、複数ファイルが1つのチャット記録に結合されます" + }, + "merge": { + "importing": "結合中", + "parsing": "ファイルを解析中...", + "parsingFile": "解析中: {name}", + "merging": "メッセージを結合中...", + "completed": "結合完了", + "completedHint": "{count} 件のファイルを結合しました", + "messageCount": "{count} 件のメッセージ", + "failed": "結合に失敗しました", + "cancel": "結合をキャンセル" + } + }, + "migration": { + "title": "データベースのアップグレードが必要です", + "description": "新機能をサポートするため、{count} 件のデータベースをアップグレードする必要があります。", + "note": "アップグレードは通常数秒で完了し、データは失われません。", + "upgradeContent": "今回のアップグレード内容:", + "upgradeNow": "今すぐアップグレード", + "upgrading": "アップグレード中...", + "failed": "移行に失敗しました", + "partialFailed": "一部のデータベースのアップグレードに失敗しました", + "errorHint": "これは通常、データベースファイルの破損が原因です。サイドバーで失敗したセッションを削除し、再度インポートすることをお勧めします。", + "close": "閉じる", + "retry": "リトライ" + }, + "chatSelector": { + "title": "インポートするチャットを選択", + "scanning": "チャットリストをスキャン中...", + "scanFailed": "スキャンに失敗しました", + "retry": "リトライ", + "selectAll": "すべて選択", + "chatCount": "全 {count} 件のチャット", + "selected": "{count} 件を選択中", + "messageCount": "{count} 件のメッセージ", + "import": "インポート ({count})", + "noChats": "チャットが見つかりません" + }, + "footer": { + "website": "公式サイト", + "terms": "利用規約" + }, + "changelog": { + "title": "バージョン履歴", + "subtitle": "すべてのバージョン更新内容を確認", + "viewChangelog": "バージョン履歴", + "latest": "最新バージョン", + "current": "現在のバージョン", + "total": "全 {count} バージョン", + "close": "閉じる", + "loadError": "バージョン履歴の読み込みに失敗しました", + "retry": "リトライ", + "types": { + "feat": "新機能", + "fix": "バグ修正", + "chore": "その他の改善", + "style": "スタイル改善" + } + } +} diff --git a/src/i18n/locales/ja-JP/index.ts b/src/i18n/locales/ja-JP/index.ts new file mode 100644 index 0000000..af418d7 --- /dev/null +++ b/src/i18n/locales/ja-JP/index.ts @@ -0,0 +1,27 @@ +import common from './common.json' +import layout from './layout.json' +import settings from './settings.json' +import home from './home.json' +import analysis from './analysis.json' +import tools from './tools.json' +import providers from './providers.json' +import ai from './ai.json' +import quotes from './quotes.json' +import members from './members.json' +import records from './records.json' +import views from './views.json' + +export default { + common, + layout, + settings, + home, + analysis, + tools, + providers, + ai, + quotes, + members, + records, + views, +} diff --git a/src/i18n/locales/ja-JP/layout.json b/src/i18n/locales/ja-JP/layout.json new file mode 100644 index 0000000..0ca5d97 --- /dev/null +++ b/src/i18n/locales/ja-JP/layout.json @@ -0,0 +1,34 @@ +{ + "brand": "ChatLab", + "newAnalysis": "新しいチャットを分析", + "tools": "ツール", + "chatHistory": "チャット履歴", + "noRecords": "記録がありません", + "searchPlaceholder": "チャット名を検索", + "noSearchResult": "一致するチャット記録が見つかりません", + "contextMenu": { + "rename": "名前変更", + "pin": "ピン留め", + "unpin": "ピン留め解除", + "delete": "削除" + }, + "renameModal": { + "title": "名前変更", + "placeholder": "新しい名前を入力してください" + }, + "deleteModal": { + "title": "削除の確認", + "message": "チャット記録「{name}」を削除しますか?この操作は取り消せません。" + }, + "tooltip": { + "expand": "サイドバーを展開", + "collapse": "サイドバーを折りたたむ", + "hint": "右クリックでチャット記録の削除や名前変更ができます", + "search": "チャット記録を検索" + }, + "sessionInfo": "{count} 件のメッセージ · {time}", + "footer": { + "helpAndFeedback": "フィードバックとヘルプ", + "settings": "設定" + } +} diff --git a/src/i18n/locales/ja-JP/members.json b/src/i18n/locales/ja-JP/members.json new file mode 100644 index 0000000..6e7a81e --- /dev/null +++ b/src/i18n/locales/ja-JP/members.json @@ -0,0 +1,84 @@ +{ + "ownerSelector": { + "title": "あなたを選択してください", + "unset": "未設定", + "selectMember": "メンバーを選択", + "currentOwner": "現在:{name}", + "hint": "1. チャット履歴ビューアの右側に表示されます 2. AI との会話ではあなた本人として扱われます" + }, + "list": { + "title": "メンバー管理", + "description": "全 {count} 名のメンバー。別名の追加やメンバーの削除ができます", + "searchPlaceholder": "グループニックネーム、アカウント名、ID、または別名で検索", + "noMatch": "一致するメンバーが見つかりません", + "empty": "メンバーデータがありません", + "table": { + "accountName": "アカウント名", + "groupNickname": "グループニックネーム", + "messageCount": "メッセージ数", + "customAlias": "カスタム別名", + "actions": "操作" + }, + "aliasPlaceholder": "入力して Enter で追加", + "delete": "削除", + "pagination": "{start} - {end} 件表示、全 {total} 名のメンバー", + "tip": "ヒント:別名を追加すると、チャット履歴の相手をより正確に識別できます。別名は検索や AI 分析で使用されます。", + "modal": { + "title": "メンバーを削除しますか?", + "content": "メンバー {name} とその {count} 件のメッセージを削除します。この操作は元に戻せません。", + "cancel": "キャンセル", + "confirm": "削除を確認" + } + }, + "nicknameHistory": { + "note": "備考:QQ の旧バージョンクライアントはチャット履歴を txt 形式でエクスポートできます。この形式では最も正確なニックネーム変更履歴が取得できます", + "title": "ニックネーム変更履歴", + "loading": "読み込み中...", + "hasChanges": "{count} 名のメンバーがニックネームを変更しました", + "noChanges": "ニックネームを変更したメンバーはいません", + "current": "現在", + "empty": "このグループのすべてのメンバーはニックネームを変更していません", + "loadingText": "ニックネーム変更履歴を読み込み中..." + }, + "relationships": { + "loading": "{'@'} インタラクションデータを分析中...", + "topMentioners": "📢 最も {'@'} する人", + "totalMentions": "全 {count} 回の {'@'}", + "topMentioned": "🎯 最も {'@'} される人", + "popularMember": "グループの人気者", + "times": "回", + "oneWay": { + "title": "🐕 片思い検出", + "description": "{count} 組の片思い関係を発見(一方の {'@'} が80%以上を占める)", + "neverRespond": "返信なし", + "reverse": "逆方向 {count} 回", + "ratio": "{value}% 片方向" + }, + "twoWay": { + "title": "💕 両思い検出", + "description": "{count} 組の頻繁な相互やり取りを発見(互いに {'@'} かつバランス度 ≥30%)", + "interactions": "回のやり取り", + "balance": "{value}% バランス" + }, + "emptyTitle": "📱 {'@'} インタラクション分析", + "empty": "{'@'} インタラクションデータがありません", + "modal": { + "title": "{name} の {'@'} 関係", + "topMentioned": "最も {'@'} する相手", + "topMentioners": "最も {'@'} してくる相手", + "timesCount": "{count} 回", + "close": "閉じる" + } + }, + "private": { + "title": "会話メンバー", + "description": "全 {count} 名のメンバー。検索や AI 分析に使用する別名を追加できます", + "messageCount": "メッセージ数", + "percentage": "割合 {value}%", + "customAlias": "カスタム別名", + "aliasPlaceholder": "入力して Enter で別名を追加", + "empty": "メンバーデータがありません", + "tipTitle": "ヒント", + "tipContent": "別名を追加すると、チャット履歴の相手をより正確に識別できます。別名は検索や AI 分析で使用されます。" + } +} diff --git a/src/i18n/locales/ja-JP/providers.json b/src/i18n/locales/ja-JP/providers.json new file mode 100644 index 0000000..2485c07 --- /dev/null +++ b/src/i18n/locales/ja-JP/providers.json @@ -0,0 +1,75 @@ +{ + "deepseek": { + "name": "DeepSeek", + "description": "DeepSeek の大規模言語モデル", + "models": { + "deepseek-chat": "汎用チャットモデル", + "deepseek-coder": "コード生成モデル" + } + }, + "qwen": { + "name": "通義千問", + "description": "Alibaba Cloud Qwen 大規模言語モデル", + "models": { + "qwen-turbo": "Qwen の高速モデル", + "qwen-plus": "Qwen の高性能モデル", + "qwen-max": "Qwen のフラッグシップモデル" + } + }, + "gemini": { + "name": "Gemini", + "description": "Google Gemini 大規模言語モデル", + "models": { + "gemini-3-flash-preview": "高速プレビュー版", + "gemini-3-pro-preview": "高性能プレビュー版" + } + }, + "minimax": { + "name": "MiniMax", + "description": "MiniMax 大規模言語モデル。マルチモーダルと長文脈に対応", + "models": { + "MiniMax-M2": "フラッグシップモデル", + "MiniMax-M2-Stable": "安定版" + } + }, + "glm": { + "name": "GLM", + "description": "Zhipu AI の大規模言語モデル、ChatGLM シリーズ", + "models": { + "glm-4-plus": "フラグシップモデル、最高性能", + "glm-4-flash": "高速モデル、コスト性能良好", + "glm-4": "標準モデル", + "glm-4v-plus": "マルチモーダルビジョンモデル", + "glm-4.6v-flash": "4.6V 無料モデル", + "glm-4.5-flash": "4.5 無料モデル" + } + }, + "kimi": { + "name": "Kimi", + "description": "Moonshot AI 大規模言語モデル。超長文コンテキストに対応", + "models": { + "moonshot-v1-8k": "8K コンテキスト", + "moonshot-v1-32k": "32K コンテキスト", + "moonshot-v1-128k": "128K 超ロングコンテキスト" + } + }, + "doubao": { + "name": "豆包", + "description": "ByteDance Doubao 大規模言語モデル", + "models": { + "doubao-seed-1-6-lite-251015": "Doubao 1.6 軽量モデル", + "doubao-seed-1-6-251015": "Doubao 1.6 高性能モデル", + "doubao-seed-1-6-flash-250828": "Doubao 1.6 高速モデル", + "doubao-1-5-lite-32k-250115": "Doubao 1.5 Pro モデル" + } + }, + "openai-compatible": { + "name": "OpenAI 互換", + "description": "OpenAI API互換サービスに対応(Ollama、LocalAI、vLLM など)", + "models": { + "llama3.2": "Meta Llama 3.2 モデル", + "qwen2.5": "通義千問 2.5 モデル", + "deepseek-r1": "DeepSeek R1 推論モデル" + } + } +} diff --git a/src/i18n/locales/ja-JP/quotes.json b/src/i18n/locales/ja-JP/quotes.json new file mode 100644 index 0000000..73035d9 --- /dev/null +++ b/src/i18n/locales/ja-JP/quotes.json @@ -0,0 +1,128 @@ +{ + "wordcloud": { + "loading": "単語頻度を分析中...", + "sidebar": { + "filter": "フィルター設定", + "style": "スタイル設定" + }, + "stats": { + "messagesLabel": "メッセージ数", + "wordsLabel": "総単語数", + "uniqueLabel": "ユニーク単語" + }, + "config": { + "maxWords": "表示単語数", + "sizeScale": "フォントサイズ", + "userFilter": "ユーザーフィルター", + "posFilter": "品詞フィルター", + "enableStopwords": "ストップワードを除外" + }, + "size": { + "small": "小", + "medium": "中", + "large": "大", + "xlarge": "特大" + }, + "posFilter": { + "all": "すべての品詞", + "meaningful": "主要品詞", + "custom": "カスタム", + "customHint": "品詞を指定", + "selectMeaningful": "主要品詞", + "selectAll": "すべて選択", + "clearAll": "クリア" + }, + "empty": { + "title": "ワードクラウドデータがありません", + "description": "現在の絞り込み条件では、ワードクラウドを作れるほどテキストメッセージがありません" + } + }, + "keywords": { + "title": "🔍 キーワードランキング", + "description": "グループチャットのキーワード使用ランキングを分析します。カスタムランキングも作成できます", + "countTemplate": "全 {count} 名のメンバー", + "templateLabel": "テンプレート:", + "newTemplate": "+ 新規作成", + "searchPlaceholder": "入力して検索", + "clear": "クリア", + "templateHint": "* テンプレートを右クリックで編集・削除できます", + "keyword": "キーワード", + "ranking": "ランキング", + "multiColorMode": "マルチカラーモード", + "times": "{count}回", + "timesWithPercent": "回 ({percent}%)", + "loading": "データを分析中...", + "empty": "データがありません。キーワードを追加するかテンプレートを切り替えてください", + "contextMenu": { + "edit": "編集", + "delete": "削除" + }, + "modal": { + "createTitle": "テンプレートを作成", + "editTitle": "テンプレートを編集", + "templateName": "テンプレート名", + "templateNamePlaceholder": "例:ポジティブ", + "keywords": "キーワード", + "keywordPlaceholder": "入力して Enter で追加", + "cancel": "キャンセル", + "save": "保存", + "update": "更新" + }, + "templates": { + "laugh": { + "name": "笑い度", + "keywords": "笑,ウケる,草,www,lol,ワロタ,233", + "description": "グループ内の笑い指数を集計" + }, + "sad": { + "name": "落ち込み度", + "keywords": "辛い,しんどい,泣いた,無理,きつい,疲れた", + "description": "グループ内のネガティブ感情を集計" + }, + "praise": { + "name": "褒め上手", + "keywords": "すごい,さすが,天才,神,やばい,尊い,最高", + "description": "グループで最も褒め上手なメンバーを集計" + }, + "slacker": { + "name": "サボり", + "keywords": "サボり,帰りたい,お腹空いた,眠い,仕事したくない,定時", + "description": "グループで最も退勤を待ち望んでいるメンバーを集計" + }, + "gossip": { + "name": "ゴシップ好き", + "keywords": "マジで,詳しく,本当に,えぇ,やばい,ありえない", + "description": "グループで最もゴシップ好きなメンバーを集計" + }, + "polite": { + "name": "礼儀正しさ", + "keywords": "ありがとう,すみません,了解,お疲れ様,よろしく,恐れ入ります", + "description": "グループで最も礼儀正しいメンバーを集計" + }, + "curious": { + "name": "質問好き", + "keywords": "なぜ,なんで,どうやって,わからない,教えて", + "description": "グループで最も質問が多いメンバーを集計" + } + } + }, + "hotRepeat": { + "title": "🔥 盛り上がった復唱", + "loading": "リピートデータを読み込み中...", + "description": "1 回の復唱で最も多くの人が参加したフレーズ", + "countTemplate": "全 {count} 件の復唱", + "people": "{count}人", + "times": "{count} 回", + "colon": ":", + "viewChat": "チャット履歴を見る", + "empty": "リピートデータがありません" + }, + "catchphrase": { + "title": "💬 口癖分析", + "loading": "口癖データを分析中...", + "description": "{count} 名の高頻度フレーズを分析しました", + "countTemplate": "全 {count} 名のメンバー", + "times": "{count}回", + "empty": "口癖データがありません" + } +} diff --git a/src/i18n/locales/ja-JP/records.json b/src/i18n/locales/ja-JP/records.json new file mode 100644 index 0000000..3309b62 --- /dev/null +++ b/src/i18n/locales/ja-JP/records.json @@ -0,0 +1,87 @@ +{ + "drawer": { + "title": "チャット履歴ビューア", + "loadedCount": "{count} 件のメッセージを読み込みました" + }, + "messageList": { + "loading": "読み込み中...", + "noMessages": "メッセージがありません", + "tryAdjustFilter": "フィルター条件を調整してみてください", + "loadingMore": "さらに読み込み中...", + "scrollUpForMore": "↑ 上にスクロールしてさらに読み込む", + "scrollDownForMore": "↓ 下にスクロールしてさらに読み込む" + }, + "filter": { + "messageId": "メッセージ ID", + "memberNotSupported": "メンバー(未対応)", + "keywordsPlaceholder": "キーワード(複数はカンマ区切り、Enter で検索)", + "startDate": "開始日", + "endDate": "終了日", + "reset": "リセット", + "filter": "フィルター" + }, + "timeline": { + "timeline": "セッション", + "noSessions": "セッションがありません", + "generateSummary": "要約を作成", + "tooFewMessages": "メッセージが少なすぎます" + }, + "batchSummary": { + "title": "要約をまとめて作成", + "byRange": "範囲で選択", + "byTime": "時間で選択", + "selectRange": "範囲を選択", + "rangeStart": "最古", + "rangeInfo": "約", + "sessionsUnit": "セッション", + "rangeEnd": "最新", + "timeRange": "時間範囲を選択", + "today": "今日", + "yesterday": "昨日", + "week": "直近7日間", + "month": "直近30日間", + "custom": "カスタム", + "found": "見つかった", + "hasSummary": "件は要約済み", + "tooFewMessages": "件はメッセージ不足", + "pending": "未生成:", + "unit": "件", + "noPending": "生成可能なセッションがありません", + "noSessions": "この時間範囲にセッションがありません", + "checking": "確認中...", + "loading": "読み込み中...", + "progress": "進捗", + "session": "セッション", + "statusSuccess": "成功", + "statusSkipped": "スキップ", + "statusFailed": "失敗", + "success": "成功:", + "failed": "失敗:", + "skipped": "スキップ:", + "start": "生成開始", + "stop": "停止" + }, + "messageItem": { + "viewContext": "コンテキストを表示", + "contextTitle": "メッセージコンテキスト(前後各10件)", + "noContext": "コンテキストがありません", + "replyTo": "返信" + }, + "sessionIndex": { + "title": "セッションインデックス", + "subtitle": "チャット履歴をタイムラインでたどれます", + "notGenerated": "セッションインデックスが未生成です", + "notGeneratedHint": "セッションインデックスを生成すると、チャット履歴ビューアーのタイムラインから各セッションにすばやくジャンプできます。", + "whatIsIt": "セッションインデックスの役割:", + "benefit1": "メッセージの時間間隔に基づいてセッション境界を自動識別", + "benefit2": "チャット履歴ビューアーにタイムラインナビゲーションを提供", + "benefit3": "AI が会話の文脈を理解しやすくなる", + "generated": "セッションインデックス生成済み", + "sessionCount": "全 {count} セッションを識別しました", + "regenerateHint": "セッション間隔のしきい値を調整した場合は、インデックスを再生成できます。", + "generate": "インデックスを生成", + "regenerate": "再生成", + "generating": "生成中...", + "cancel": "キャンセル" + } +} diff --git a/src/i18n/locales/ja-JP/settings.json b/src/i18n/locales/ja-JP/settings.json new file mode 100644 index 0000000..b4ecb64 --- /dev/null +++ b/src/i18n/locales/ja-JP/settings.json @@ -0,0 +1,379 @@ +{ + "title": "設定", + "tabs": { + "basic": "基本設定", + "ai": "AI 設定", + "aiConfig": "チャットモデル", + "aiRAG": "埋め込みモデル", + "aiPrompt": "チャット設定", + "aiPreset": "プロンプト設定", + "aiPreprocess": "前処理", + "storage": "データとストレージ", + "storageManage": "ストレージ管理", + "sessionManage": "セッション管理", + "about": "ChatLabについて" + }, + "basic": { + "language": { + "title": "言語", + "description": "アプリの表示言語を選択します" + }, + "appearance": { + "title": "外観設定", + "themeMode": "テーマモード", + "auto": "システムに従う", + "light": "ライトモード", + "dark": "ダークモード" + }, + "screenshot": { + "title": "スクリーンショット設定", + "mobileAdapt": "モバイル対応", + "mobileAdaptDesc": "スクリーンショット時に幅を自動調整し、モバイル端末での閲覧に最適化します" + }, + "network": { + "title": "ネットワーク設定", + "proxyMode": "プロキシモード", + "proxyModeDesc": "ネットワークアクセス時に使用するプロキシ方式", + "modeOff": "オフ", + "modeSystem": "システムに従う", + "modeManual": "手動設定", + "proxyAddress": "プロキシアドレス", + "proxyPlaceholder": "http://127.0.0.1:7890", + "proxyHelp": "HTTP/HTTPS プロキシに対応。形式例:http://127.0.0.1:7890", + "testConnection": "接続テスト", + "testing": "テスト中...", + "connectionSuccess": "プロキシ接続に成功しました!", + "connectionFailed": "接続に失敗しました", + "enterProxyFirst": "先にプロキシアドレスを入力してください", + "invalidProxyUrl": "有効なプロキシアドレスを入力してください。形式例:http://127.0.0.1:7890", + "onlyHttpSupported": "http:// または https:// プロトコルのみ対応", + "saveFailed": "保存に失敗しました" + } + }, + "aiConfig": { + "title": "チャットモデル", + "loading": "読み込み中...", + "inUse": "使用中", + "defaultModel": "デフォルトモデル", + "localService": "ローカルサービス", + "customEndpoint": "カスタムエンドポイント", + "edit": "編集", + "delete": "削除", + "empty": { + "title": "AI サービスが未設定です", + "description": "設定を追加して AI を使い始めましょう" + }, + "addConfig": "設定を追加", + "maxConfigs": "設定は最大 10 件までです", + "modal": { + "addConfig": "設定を追加", + "editConfig": "設定を編集", + "officialApi": "公式 API", + "officialApiDesc": "DeepSeek や Gemini など", + "localService": "ローカルサービス", + "localServiceDesc": "Ollama など", + "openaiCompatible": "OpenAI 互換", + "openaiCompatibleDesc": "カスタムエンドポイント", + "configName": "設定名", + "optional": "(任意)", + "configNamePlaceholderPreset": "空欄の場合、サービス名が使用されます", + "configNamePlaceholderCustom": "空欄の場合、API エンドポイントが使用されます", + "aiProvider": "AI プロバイダー", + "apiKeyPlaceholder": "API Key を入力", + "apiKeyPlaceholderEdit": "新しい API Key を入力(空欄で現在の値を維持)", + "apiKeyPlaceholderLocal": "ローカルサービスでは通常不要です", + "apiKeyHintLocal": "サービスに認証が設定されている場合はこちらに入力", + "validate": "接続確認", + "validationSuccess": "接続を確認しました", + "validationFailed": "接続の確認に失敗しました", + "validationError": "検証に失敗:", + "model": "モデル", + "modelName": "モデル名", + "modelNamePlaceholder": "例: gpt-4、claude-3", + "modelNamePlaceholderLocal": "例: qwen3、deepseek-r1", + "modelNameHint": "API がサポートするモデル名を入力", + "modelNameHintLocal": "ローカルにデプロイしたモデル名を入力", + "apiEndpoint": "API エンドポイント", + "apiEndpointHint": "OpenAI 互換形式の API エンドポイント", + "disableThinking": "思考モードをオフ", + "disableThinkingDesc": "Qwen3 や DeepSeek-R1 などのモデル向け。オフにすると標準のツール実行形式を使います", + "isReasoningModel": "推論モデル", + "isReasoningModelDesc": "有効にすると推論過程を抽出し、ツール実行を無効にします(DeepSeek-R1、QwQ など)", + "advancedOptions": "詳細設定", + "customService": "カスタムサービス", + "unnamedConfig": "名称未設定" + } + }, + "storage": { + "title": "ローカルストレージ管理", + "description": "ChatLab がローカルに保存したデータファイルを管理します", + "dataLocation": { + "title": "データディレクトリの場所", + "description": "ローカルデータとキャッシュの保存先をカスタマイズ", + "choose": "場所を選択", + "reset": "デフォルトに戻す", + "open": "開く", + "restartTip": "ディレクトリ変更後はアプリを再起動してください。旧ディレクトリのキャッシュは自動で移行されます", + "confirmTitle": "データディレクトリの切り替えを確認", + "confirmMessage": "データの保存先を切り替えます。既存データは新しいディレクトリに移行されます。", + "newPath": "新しいディレクトリパス", + "defaultPath": "デフォルトの場所", + "confirmWarning": "アプリ再起動後、旧データディレクトリは自動的に削除されます。新しいディレクトリパスが正しいことを確認してください。", + "cancel": "キャンセル", + "confirm": "切り替えを確認", + "migrationSuccessTitle": "データ移行が完了しました", + "migrationSuccessMessage": "変更を反映するにはアプリの再起動が必要です。", + "relaunchNow": "今すぐ再起動" + }, + "totalUsage": "合計使用量:", + "loading": "読み込み中...", + "files": "ファイル", + "notExist": "存在しません", + "clear": "クリア", + "open": "開く", + "notes": { + "title": "注意事項", + "logSafe": "ログファイルは主にバグ調査用です。安全にクリアできます", + "noRecover": "クリアしたファイルは復元できません。慎重に操作してください" + }, + "cache": { + "databases": { + "name": "チャット履歴データベース", + "description": "インポートしたチャット履歴の分析データ" + }, + "ai": { + "name": "AI 会話データベース", + "description": "AI チャット履歴と設定ファイル" + }, + "downloads": { + "name": "ダウンロードディレクトリ", + "description": "スクリーンショットや分析結果などを含む" + }, + "logs": { + "name": "ログファイル", + "description": "アプリの実行ログ(インポート、AI、エラーなどのログを含む)" + } + }, + "session": { + "title": "セッションインデックス設定", + "description": "セッションインデックスは時間間隔に応じてチャット履歴を自動で区切り、AI 分析や閲覧をしやすくします", + "defaultThreshold": "デフォルト分割間隔", + "thresholdUnit": "分", + "thresholdHelp": "この時間を超える間隔があるメッセージは新しいセッションに分割されます", + "notGenerated": "セッションインデックスが未生成です", + "generateHint": "生成すると、AI が会話の流れをより正確に把握できます", + "generate": "インデックスを生成", + "regenerate": "再生成", + "generating": "作成中", + "sessionCount": "{count} セッション", + "generated": "セッションインデックス生成済み", + "generateSuccess": "セッションインデックスの生成に成功しました。全 {count} セッション", + "generateError": "生成に失敗しました", + "batchTitle": "インデックスを一括生成", + "totalSessions": "全 {count} チャット", + "generatedCount": "生成済み {count} 件", + "notGeneratedCount": "未生成 {count} 件", + "loadingStatus": "読み込み中...", + "batchGenerate": "未生成分を作成", + "batchRegenerate": "すべて再生成" + } + }, + "aiPrompt": { + "chatSettings": { + "title": "チャット設定" + }, + "exportSettings": { + "title": "エクスポート設定" + }, + "maxMessages": { + "title": "送信メッセージ数の上限", + "description": "1 回のリクエストで AI に送る最大メッセージ数です。増やすほど Token 消費は増えますが、分析精度も上がります(初めてなら 2000 推奨)" + }, + "maxHistory": { + "title": "AI コンテキスト制限", + "description": "会話ごとに保持する直近のやり取り数です(1 往復 = ユーザーの質問 + AI の回答)。文脈が長くなりすぎて Token を消費するのを防ぎます" + }, + "exportFormat": { + "title": "会話エクスポート形式", + "description": "AI チャットをエクスポートする際のファイル形式", + "txtLabel": "TXT" + }, + "sqlExportFormat": { + "title": "SQL Lab エクスポート形式", + "description": "SQL クエリ結果をエクスポートする際のファイル形式" + }, + "presets": { + "title": "システムプロンプト", + "add": "プリセットを追加", + "import": "プリセットをインポート", + "description": "プロンプトはグループチャットと個人チャットの両方の分析に使用されます。分析タイプに応じて内容が自動的に調整されます" + }, + "preset": { + "builtIn": "内蔵", + "groupOnly": "グループチャットのみ", + "privateOnly": "個人チャットのみ", + "view": "表示", + "edit": "編集", + "copy": "コピー", + "delete": "削除" + }, + "modal": { + "editBuiltin": "システムプロンプトを編集", + "editCustom": "カスタムプロンプトを編集", + "addCustom": "カスタムプロンプトを追加", + "presetName": "プリセット名", + "presetNamePlaceholder": "プリセットの名前を入力", + "applicableTo": "適用シーン", + "applicableToHint": "(チェックすると対応する分析タイプで使用可能になります)", + "groupChat": "グループチャット分析", + "privateChat": "個人チャット分析", + "systemPrompt": "システムプロンプト", + "systemPromptPlaceholder": "AI アシスタントの役割、タスク、回答要件を定義...", + "preview": "完全プロンプトプレビュー", + "previewHint": "(プレビューはグループチャットモードです。実際は分析タイプに応じて自動調整されます)", + "resetToDefault": "デフォルトに戻す", + "saveChanges": "変更を保存", + "addPreset": "プリセットを追加" + }, + "importPreset": { + "title": "プリセットをインポート", + "description": "リモートから推奨のシステムプロンプトプリセットを取得します", + "loading": "リモートプリセットを読み込み中...", + "loadError": "リモートプリセットの読み込みに失敗しました", + "noPresets": "利用可能なリモートプリセットがありません", + "commonPresets": "共通プリセット", + "groupPresets": "グループチャット専用プリセット", + "privatePresets": "個人チャット専用プリセット", + "add": "追加", + "added": "追加済み", + "preview": "プレビュー", + "systemPrompt": "システムプロンプト", + "noDescription": "説明がありません", + "fetchingContent": "コンテンツを読み込み中...", + "fetchError": "コンテンツの読み込みに失敗しました" + } + }, + "about": { + "title": "ChatLabについて", + "description": "ローカルで動くチャット履歴分析ツール。SQL と AI Agent で会話を振り返れます。", + "version": "バージョン", + "checkUpdate": "アップデートを確認", + "checking": "確認中...", + "unknown": "不明", + "privacy": { + "title": "プライバシー設定", + "analytics": "匿名利用統計", + "analyticsDesc": "有効にすると、バージョン番号やOS情報などの非機密データを収集し、製品の改善に役立てます" + }, + "developer": { + "title": "開発者オプション", + "debugMode": "DEBUG モード", + "debugModeDesc": "有効にすると、AI ログに完全な生メッセージ内容が切り詰められずに記録されます。ログファイルが大幅に増大する可能性があります" + } + }, + "embedding": { + "title": "埋め込みモデル", + "description": "Embedding ベクトル類似度で質問の意味を理解します。有効にすると AI がセマンティック検索を行えます", + "configList": "埋め込み設定", + "addConfig": "設定を追加", + "editConfig": "設定を編集", + "noConfigs": "設定がありません。上のボタンをクリックして追加してください", + "active": "使用中", + "setActive": "使用する", + "deleteConfirm": "設定「{name}」を削除してもよろしいですか?", + "configName": "設定名", + "configNamePlaceholder": "例:Ollama Embedding", + "apiSource": "API ソース", + "apiSourceHint": "「チャットモデルを流用」を選ぶと、現在有効なチャットモデルのエンドポイントとキーをそのまま使います", + "reuseLLM": "チャットモデルを流用", + "customAPI": "カスタム API", + "model": "モデル名", + "modelPlaceholder": "例: nomic-embed-text", + "modelHint": "Ollama でよく使用:nomic-embed-text、mxbai-embed-large", + "baseUrl": "API エンドポイント", + "baseUrlPlaceholder": "例: http://localhost:11434/v1", + "apiKey": "API Key", + "apiKeyPlaceholder": "API Key を入力", + "optional": "(任意)", + "validate": "接続テスト", + "validateSuccess": "接続に成功しました!", + "validateFailed": "接続に失敗しました", + "saveFailed": "保存に失敗しました", + "vectorStore": "ベクトルキャッシュ", + "vectorStoreDesc": "計算済みのベクトルをキャッシュし、再計算を避けて速度を向上させます", + "cached": "キャッシュ済み", + "size": "使用量", + "clear": "クリア", + "clearVectorStoreConfirm": "すべてのベクトルキャッシュをクリアしてもよろしいですか?次回検索時に再計算が必要になります。" + }, + "aiPreprocess": { + "title": "チャット履歴の前処理", + "description": "チャット履歴を AI に送信する前に前処理を行い、Token を節約しノイズを除去します。元のデータは変更されません。", + "dataCleaning": "データクリーニング", + "dataCleaningDesc": "XML カードメッセージ(共有リンク、ミニアプリなど)を自動クリーニングし、有効な内容を抽出します。Token を大幅に節約できます。オンのままにすることを推奨します。", + "mergeConsecutive": "連続発言の結合", + "mergeConsecutiveDesc": "同一人物の短時間内の連続メッセージを1つに結合します", + "mergeWindow": "結合タイムウィンドウ(秒)", + "denoise": "スマートノイズ除去", + "denoiseDesc": "感動詞のみ、絵文字のみ、システムプレースホルダー([画像] など)といった無意味なメッセージを除去します", + "desensitize": "データマスキング", + "desensitizeDesc": "ルールに基づいて機密情報(電話番号、メールアドレス、身分証番号など)をプレースホルダーに置換します", + "desensitizeRules": "マスキングルール", + "desensitizeRulesDesc": "有効にするマスキングルールにチェックを入れてください。リスト順に優先マッチングされます", + "desensitizeBuiltin": "プリセットルール", + "desensitizeCustom": "カスタムルール", + "desensitizeAddCustom": "カスタムルールを追加", + "desensitizeRuleName": "ルール名", + "desensitizeRulePattern": "正規表現", + "desensitizeRuleReplacement": "置換テキスト", + "desensitizeRuleAdd": "追加", + "desensitizeRuleInvalidRegex": "正規表現の構文エラー", + "desensitizeRuleNamePlaceholder": "例:社員番号", + "desensitizeRulePatternPlaceholder": "例:EMP-\\d+", + "desensitizeRuleReplacementPlaceholder": "置換テキスト。例:[社員番号]", + "anonymizeNames": "ニックネーム匿名化", + "anonymizeNamesDesc": "実際のニックネームの代わりに番号(U1、U2…)を AI に送信し、幻覚を減らして Token を節約します", + "blacklist": "ブラックリストキーワード", + "blacklistDesc": "以下のキーワードのいずれかを含むメッセージは丸ごと除外されます", + "blacklistPlaceholder": "キーワードを入力して Enter で追加", + "blacklistAdd": "追加" + }, + "desensitize": { + "rules": { + "cn_phone": "中国の携帯電話番号", + "cn_phone_desc": "1xx xxxx xxxx 形式の11桁の番号", + "cn_id_card": "中国の身分証番号", + "cn_id_card_desc": "18桁、末尾に X を含む場合あり", + "cn_bank_card": "中国の銀行カード番号", + "cn_bank_card_desc": "16-19桁の連続数字", + "cn_landline": "中国の固定電話番号", + "cn_landline_desc": "市外局番-番号(例:010-12345678)", + "us_ssn": "米国社会保障番号(SSN)", + "us_ssn_desc": "XXX-XX-XXXX 形式", + "us_phone": "米国電話番号", + "us_phone_desc": "+1 (XXX) XXX-XXXX 形式", + "us_drivers_license": "米国運転免許証番号", + "us_drivers_license_desc": "アルファベット + 7-8桁の数字", + "jp_phone": "日本の電話番号", + "jp_phone_desc": "0x0-XXXX-XXXX 形式", + "jp_my_number": "マイナンバー", + "jp_my_number_desc": "12桁の数字(マイナンバー)", + "kr_phone": "韓国の電話番号", + "kr_phone_desc": "01X-XXXX-XXXX 形式", + "kr_rrn": "韓国の住民登録番号", + "kr_rrn_desc": "XXXXXX-XXXXXXX 形式(주민번호)", + "api_key_prefix": "API Key / Token(既知プレフィックス)", + "api_key_prefix_desc": "sk-、ghp_、AKIA などで始まる長い文字列", + "bearer_token": "Bearer Token", + "bearer_token_desc": "Bearer に続く認証トークン値", + "email": "メールアドレス", + "email_desc": "user{'@'}domain.com の標準形式", + "credit_card": "クレジットカード番号", + "credit_card_desc": "Visa / MasterCard / AmEx などのカード番号", + "ipv4": "IPv4 アドレス", + "ipv4_desc": "例:192.168.1.1", + "url": "URL リンク", + "url_desc": "http:// または https:// で始まるリンク" + } + } +} diff --git a/src/i18n/locales/ja-JP/tools.json b/src/i18n/locales/ja-JP/tools.json new file mode 100644 index 0000000..4ff534c --- /dev/null +++ b/src/i18n/locales/ja-JP/tools.json @@ -0,0 +1,49 @@ +{ + "title": "管理", + "description": "チャット履歴を管理", + "tabs": { + "batchManage": "一括管理" + }, + "batchManage": { + "title": "一括削除", + "description": "削除するチャット履歴を選択してください", + "searchPlaceholder": "チャット名を検索", + "searchResult": "{count} / {total} 件を検出", + "noSearchResult": "一致するチャット履歴が見つかりません", + "selectAll": "すべて選択", + "shiftClickHint": "Shiftキーを押しながらクリックで範囲選択できます", + "selected": "{count} 件を選択中", + "empty": "チャット履歴がありません", + "delete": "選択した項目を削除", + "confirmTitle": "削除の確認", + "confirmMessage": "選択した {count} 件のチャット履歴を削除しますか?この操作は取り消せません。", + "deleting": "削除中...", + "success": "{count} 件のチャット履歴を削除しました", + "error": "削除に失敗しました:{error}", + "messageCount": "{count} 件のメッセージ", + "importedAt": "{time} にインポート", + "columns": { + "name": "名前", + "platform": "プラットフォーム", + "messages": "メッセージ数", + "summaries": "要約", + "aiChats": "AI チャット", + "importedAt": "インポート日時" + }, + "clickToEdit": "クリックして名前を編集", + "merge": "選択項目を結合", + "mergeHint": "結合するには同じプラットフォームのチャット履歴を 2 件以上選択してください", + "mergeConfirmTitle": "チャット履歴を結合", + "mergeConfirmMessage": "選択した {count} 件のチャット履歴を結合します。元の履歴は削除され、新しい統合履歴が作成されます。", + "mergeSteps": { + "exporting": "チャット履歴をエクスポート中...", + "parsing": "ファイルを解析中...", + "checking": "競合を検出中...", + "merging": "データを結合中...", + "cleaning": "一時ファイルを削除中..." + }, + "mergeSuccess": "{count} 件のチャット履歴を結合しました", + "mergeError": "結合に失敗しました:{error}", + "mergedSuffix": "結合" + } +} diff --git a/src/i18n/locales/ja-JP/views.json b/src/i18n/locales/ja-JP/views.json new file mode 100644 index 0000000..6767a29 --- /dev/null +++ b/src/i18n/locales/ja-JP/views.json @@ -0,0 +1,82 @@ +{ + "cluster": { + "title": "やり取り頻度", + "rankingView": "ランキング表示", + "memberView": "メンバー表示", + "matrixView": "マトリクス表示", + "modelSettings": "モデル設定", + "lookAhead": "隣接数", + "lookAheadDesc": "各メッセージの後ろ側で何人分の発言を対象にするか", + "decaySeconds": "時間減衰(秒)", + "decaySecondsDesc": "値が大きいほど遠いメッセージの重みが高くなる", + "applySettings": "設定を適用", + "noData": "データがありません", + "selectMember": "メンバーを選択", + "selectMemberHint": "左側からメンバーを選択してください", + "msgCount": "発言数", + "relationCount": "関係数", + "relationsByIntimacy": "発言近接度ランキング", + "noRelations": "関係データがありません", + "intimacy": "近接度", + "interactionRanking": "やり取りランキング", + "score": "近接度", + "coOccurrence": "共起", + "times": "回", + "totalMembers": "グループメンバー", + "totalMessages": "メッセージ総数", + "involvedMembers": "参加メンバー", + "edgeCount": "関係数" + }, + "message": { + "typeDistribution": "メッセージタイプ分布", + "hourlyDistribution": "時間帯別分布", + "weekdayDistribution": "曜日別分布", + "monthlyDistribution": "月別分布", + "yearlyDistribution": "年別分布", + "timeHeatmap": "時間ヒートマップ", + "heatmapHint": "チャット時間帯の傾向を表示", + "calendarHeatmap": "メッセージカレンダー", + "calendarHint": "日別メッセージ分布", + "calendarTooltipMessages": "メッセージ", + "lengthDetailTitle": "短文メッセージ分布(1-25文字)", + "lengthDetailHint": "文字ごとの統計", + "lengthGroupedTitle": "文字数区間分布", + "lengthGroupedHint": "5文字ごとにグループ化", + "noTextMessages": "テキストメッセージがありません", + "noData": "データがありません" + }, + "interaction": { + "mentionGraph": "メンション関係図", + "layout": "レイアウト", + "circular": "サークル", + "force": "フォースレイアウト", + "directed": "有向", + "reset": "リセット", + "graphHint": "全 {nodes} 名のメンバー、{links} 件のインタラクション", + "noInteraction": "メンションデータがありません" + }, + "timeline": { + "title": "タイムライン表示", + "description": "メッセージの時間分布や活動周期の変化を可視化します" + }, + "charts": { + "rankListPro": { + "fullRanking": "全順位を表示", + "memberCount": "全 {count} 名のメンバー" + }, + "rankList": { + "unit": "件" + }, + "nicknameHistory": { + "periodToNow": "{start} 〜 現在", + "periodRange": "{start} 〜 {end}", + "current": "現在", + "empty": "ニックネーム履歴がありません" + }, + "listPro": { + "countTemplate": "全 {count} 件", + "fullRanking": "全順位を表示", + "empty": "データがありません" + } + } +} diff --git a/src/i18n/locales/zh-CN/home.json b/src/i18n/locales/zh-CN/home.json index b532783..806e5b1 100644 --- a/src/i18n/locales/zh-CN/home.json +++ b/src/i18n/locales/zh-CN/home.json @@ -100,6 +100,10 @@ "import": "导入 ({count})", "noChats": "未发现任何聊天" }, + "footer": { + "website": "官网", + "terms": "使用条款" + }, "changelog": { "title": "版本日志", "subtitle": "查看所有版本更新内容", diff --git a/src/i18n/locales/zh-TW/ai.json b/src/i18n/locales/zh-TW/ai.json new file mode 100644 index 0000000..d4962e6 --- /dev/null +++ b/src/i18n/locales/zh-TW/ai.json @@ -0,0 +1,301 @@ +{ + "tab": { + "chatExplorer": "對話式探索", + "sqlLab": "SQL 實驗室", + "featureInDev": "{name} 功能開發中", + "comingSoon": "敬請期待...", + "followNotice": "新功能上線通知,歡迎關注我的小紅書" + }, + "chat": { + "welcome": { + "configReady": "✅ AI 服務已設定完成,可以開始對話了!", + "configNeeded": "**注意**:使用前請先在側邊欄底部的「設定」中設定 AI 服務 ⚙️" + }, + "capture": "截圖對話", + "scrollToBottom": "回到底部", + "input": { + "placeholder": "輸入你的問題..." + }, + "message": { + "userAvatar": "使用者頭像", + "calling": "執行", + "tools": { + "search_messages": "搜尋聊天紀錄", + "get_recent_messages": "取得最近訊息", + "get_member_stats": "取得成員統計", + "get_time_stats": "取得時間分佈", + "get_members": "取得成員列表", + "get_member_name_history": "取得暱稱歷史", + "get_conversation_between": "取得對話紀錄", + "get_message_context": "取得上下文", + "semantic_search_messages": "🔍 語意搜尋" + }, + "generating": "正在產生回覆...", + "think": { + "labels": { + "think": "已思考", + "analysis": "分析", + "reasoning": "推理", + "reflection": "反思", + "other": "思考({tag})" + }, + "loading": "思考中", + "duration": "耗時 {seconds} 秒" + }, + "toolParams": { + "keywords": "關鍵字", + "time": "時間", + "getMessages": "取得 {count} 則訊息", + "limit": "限制 {count} 則", + "contextWithMessages": "{msgCount} 則訊息的前後各 {contextSize} 則上下文", + "context": "前後各 {size} 則上下文", + "topMembers": "前 {count} 名成員", + "search": "搜尋", + "getMemberList": "取得成員列表", + "memberId": "成員 ID", + "timeStats": { + "hourly": "按小時", + "weekday": "按星期", + "daily": "按日期" + } + } + }, + "thinking": { + "processingResult": "正在整理結果", + "called": "已執行:", + "analyzing": "正在分析問題..." + }, + "statusBar": { + "preset": { + "default": "預設", + "groupTitle": "群聊提示詞預設", + "privateTitle": "私聊提示詞預設", + "manage": "管理提示詞" + }, + "model": { + "title": "AI 模型設定", + "notConfigured": "AI 尚未設定", + "empty": "尚無設定", + "manage": "管理模型", + "switchFailed": "切換模型失敗" + }, + "messageLimit": { + "label": "訊息上限:", + "title": "設定每次送出的最大訊息數" + }, + "agent": { + "label": "Agent", + "contextTokens": "上下文", + "stats": "段 {segment} · 標籤 {tags}", + "phase": { + "preparing": "準備中", + "thinking": "思考中", + "tool_running": "工具執行中", + "responding": "回覆中", + "completed": "已完成", + "aborted": "已停止", + "error": "錯誤" + }, + "phaseShort": { + "preparing": "準備", + "thinking": "思考", + "tool_running": "工具", + "responding": "回覆", + "completed": "完成", + "aborted": "停止", + "error": "錯誤" + } + }, + "tokenUsageTitle": "本次對話累計 Token 用量", + "export": { + "label": "匯出", + "title": "匯出當前對話" + }, + "log": { + "label": "日誌", + "title": "開啟當前 AI 日誌檔案", + "openFailed": "開啟日誌失敗", + "openFailedDesc": "請稍後重試" + }, + "status": { + "connected": "AI 已連線", + "notConfigured": "請先在全域設定中設定 AI 服務" + } + }, + "conversation": { + "title": "AI 對話紀錄", + "newChat": "新對話", + "empty": "暫無歷史紀錄", + "startNew": "開始新對話", + "titlePlaceholder": "輸入標題...", + "export": { + "createdAt": "建立時間", + "user": "使用者", + "assistant": "AI 助手", + "noMessages": "對話沒有訊息" + } + }, + "dataSource": { + "title": "資料來源", + "keywords": "關鍵字:", + "empty": "暫無資料", + "emptyHint": "傳送問題後,相關紀錄會顯示在這裡", + "totalRecords": "共 {count} 筆紀錄", + "loadMore": "載入更多" + } + }, + "sqlLab": { + "editor": { + "errorEmptySQL": "請輸入 SQL 語句", + "placeholder": "輸入 SQL 查詢語句...", + "tip": "提示:雙擊左側欄位名可插入到 SQL", + "history": "歷史", + "aiGenerate": "AI 產生", + "shortcut": "Ctrl/⌘ + Enter 執行", + "run": "執行" + }, + "schema": { + "tables": "資料表", + "doubleClickToInsert": "雙擊插入到 SQL", + "primaryKey": "主鍵" + }, + "result": { + "queryError": "查詢錯誤", + "rows": "{count} 行", + "readableTime": "時間戳轉日期", + "viewChat": "檢視聊天紀錄", + "summarize": "整理摘要", + "pageSize": "每頁", + "pageInfo": "第 {current} / {total} 頁", + "emptyResult": "查詢結果為空", + "initialState": "輸入 SQL 語句後執行,即可查看結果", + "initialHint": "僅支援 SELECT 查詢,建議添加 LIMIT 以提升效能", + "summaryTitle": "AI 分析摘要", + "analyzing": "AI 正在分析結果...", + "errorSummary": "總結失敗" + }, + "generate": { + "title": "AI 產生 SQL", + "description": "用自然語言描述你想查詢的內容,AI 會自動產生對應的 SQL 語句。", + "placeholder": "例如:查找發言最多的前 10 個成員、統計每天的訊息數量、找出包含「買房」關鍵字的訊息...", + "errorEmptyPrompt": "請輸入查詢需求", + "errorGenerate": "AI 產生失敗", + "aiOutput": "AI 輸出", + "waitingAI": "等待 AI 回應...", + "sqlStatement": "SQL 語句", + "explanation": "說明", + "generateSQL": "產生 SQL", + "useSQL": "使用 SQL", + "run": "執行" + }, + "history": { + "title": "AI 產生紀錄", + "execute": "執行", + "empty": "暫無歷史紀錄", + "justNow": "剛才", + "minutesAgo": "{count} 分鐘前", + "hoursAgo": "{count} 小時前", + "today": "今天" + } + }, + "assistant": { + "selector": { + "title": "選擇一個助手開始對話", + "subtitle": "每位助手都擅長不同分析任務,請選擇最符合需求的一位", + "noAssistants": "當前場景暫無可用助手", + "manage": "管理助手", + "addNew": "新增助手" + }, + "config": { + "viewTitle": "檢視設定", + "createTitle": "新增助手", + "editTitle": "編輯助手", + "betaWarning": "目前仍為測試功能,設定邏輯後續可能調整,請先備份提示詞與相關設定。", + "tabs": { + "basic": "基本設定", + "tools": "工具管理" + }, + "name": "名稱", + "systemPrompt": "系統提示詞", + "chatType": "適用聊天類型", + "chatTypeAll": "全部", + "chatTypeGroup": "群聊", + "chatTypePrivate": "私聊", + "locale": "適用語言", + "localeHint": "不選代表套用全部語言", + "presetQuestions": "預設問題", + "addQuestion": "新增問題...", + "builtinTools": "內建工具", + "selectAll": "全選", + "deselectAll": "全不選", + "builtinToolsHint": "若未勾選任何工具,代表全部可用;勾選後僅開放所選工具。", + "toolGroupQuery": "資料查詢", + "toolGroupSql": "SQL 分析", + "customSqlTools": "自訂 SQL 工具", + "customSqlToolsHint": "宣告式 SQL 工具會自動註冊為 AI 函式呼叫。執行參數化 SQL 查詢並格式化結果。", + "toolUntitled": "工具 {index}(未命名)", + "toolName": "工具名稱", + "toolNamePlaceholder": "如 top_active_members", + "toolDesc": "工具描述", + "toolDescPlaceholder": "查詢最活躍成員排行", + "toolParams": "參數定義(JSON Schema)", + "toolQuery": "SQL 查詢(使用 @paramName 引用參數)", + "toolQueryPlaceholder": "SELECT sender_name, COUNT(*) as cnt FROM message ...", + "toolRowTemplate": "行模板({columnName} 佔位符)", + "toolRowTemplatePlaceholder": "{sender_name}: {cnt} 則訊息", + "toolFallback": "空結果提示", + "toolFallbackPlaceholder": "未找到相關資料", + "toolFallbackDefault": "未找到相關資料", + "toolSummary": "彙總模板(可選,{rowCount} 為行數)", + "toolSummaryPlaceholder": "共找到 {rowCount} 筆紀錄:", + "addCustomTool": "新增自訂 SQL 工具", + "resetDefault": "還原預設", + "create": "建立" + }, + "market": { + "title": "助手管理", + "tabs": { + "local": "本機助手", + "market": "助手市場" + }, + "localHint": "管理已匯入的助手,可調整設定或刪除", + "marketHint": "瀏覽內建助手範本,匯入後即可使用和自由編輯", + "default": "預設", + "updateAvailable": "更新可用", + "reimport": "重新匯入", + "imported": "已匯入", + "import": "匯入", + "viewConfig": "檢視設定", + "duplicate": "複製", + "addAssistant": "新增助手", + "noLocal": "暫無已匯入的助手", + "noCatalog": "暫無可用助手範本" + }, + "fallbackName": "助手", + "duplicateSuffix": "(副本)", + "toast": { + "loadFailed": "載入失敗", + "createSuccess": "建立成功", + "createFailed": "建立失敗", + "saveSuccess": "儲存成功", + "saveFailed": "儲存失敗", + "resetSuccess": "已還原預設", + "resetFailed": "恢復失敗", + "unknownError": "未知錯誤" + }, + "builtinToolDesc": { + "search_messages": "搜尋聊天訊息", + "get_recent_messages": "取得最近訊息", + "get_message_context": "取得訊息上下文", + "get_conversation_between": "取得兩人對話", + "get_members": "取得群成員列表", + "get_member_stats": "取得成員統計", + "get_member_name_history": "取得成員改名歷史", + "get_time_stats": "取得時間統計", + "search_sessions": "搜尋會話", + "get_session_messages": "取得會話訊息", + "get_session_summaries": "取得會話摘要", + "semantic_search_messages": "語意搜尋訊息" + } + } +} diff --git a/src/i18n/locales/zh-TW/analysis.json b/src/i18n/locales/zh-TW/analysis.json new file mode 100644 index 0000000..8fc5a5e --- /dev/null +++ b/src/i18n/locales/zh-TW/analysis.json @@ -0,0 +1,157 @@ +{ + "groupChat": { + "title": "群聊分析", + "loading": "載入分析資料...", + "loadError": "無法載入聊天資料", + "description": "{dateRange},{memberCount} 位成員共聊了 {messageCount} 條訊息" + }, + "privateChat": { + "title": "私聊分析", + "loading": "載入分析資料...", + "loadError": "無法載入聊天資料", + "description": "{dateRange},共 {messageCount} 條訊息" + }, + "tabs": { + "overview": "總覽", + "view": "檢視", + "ranking": "榜單", + "groupQuotes": "金句", + "quotes": "金句", + "members": "成員", + "member": "成員", + "ai": "AI 實驗室" + }, + "yearFilter": { + "allTime": "全部時間", + "year": "{year}年" + }, + "tooltip": { + "chatViewer": "聊天紀錄檢視器", + "sessionIndex": "會話索引", + "incrementalImport": "追加匯入" + }, + "incremental": { + "title": "新增聊天紀錄", + "description": "選擇要加入「{name}」的聊天紀錄檔案,系統會自動排除重複內容。", + "selectFile": "選擇聊天紀錄檔案", + "dropHint": "點擊選擇或拖曳檔案到此處", + "analyzing": "正在分析檔案...", + "totalInFile": "檔案訊息數", + "newMessages": "新增訊息", + "duplicates": "重複訊息", + "noNewMessages": "檔案中沒有新訊息,所有訊息都已存在", + "importing": "正在匯入...", + "import": "匯入 {count} 則訊息", + "success": "匯入成功", + "successDetail": "已新增 {count} 則新訊息", + "failed": "匯入失敗" + }, + "subTabs": { + "view": { + "message": "訊息", + "interaction": "互動分析", + "ranking": "榜單" + }, + "quotes": { + "wordcloud": "詞雲", + "hotRepeat": "熱門複讀", + "catchphrase": "口頭禪", + "keywordAnalysis": "關鍵字分析" + }, + "member": { + "memberList": "成員列表", + "relationships": "互動關係", + "cluster": "互動頻率", + "nicknameHistory": "暱稱變更" + } + }, + "overview": { + "messageTypeDistribution": "訊息類型分佈", + "memberDistribution": "成員發言分布", + "others": "其他人", + "messageRatio": "訊息佔比", + "messageUnit": "條", + "memberComparison": "雙方訊息佔比", + "statCards": { + "dailyAvgMessages": "日均訊息", + "messagesCount": "{count} 條", + "daysCount": "共 {count} 天", + "imageMessages": "圖片訊息", + "imagesCount": "{count} 張", + "peakHour": "最活躍時段:", + "mostActiveWeekday": "最活躍星期", + "messagesOnDay": "{count} 條訊息", + "weekendActivity": "週末活躍度", + "weekendRatio": "週末訊息佔比", + "mostActiveDate": "最活躍日期", + "activeDays": "活躍天數", + "slashDays": "/ {count} 天", + "consecutiveStreak": "連續活躍", + "daysStreak": "{count} 天", + "longestStreak": "最長連續活躍", + "activityRate": "活躍率", + "activeDaysRatio": "有訊息的天數佔比" + }, + "dailyTrend": { + "title": "每日訊息趨勢" + }, + "identity": { + "privateChat": "私聊", + "groupChat": "群聊", + "analysisReport": "資料分析報告", + "totalMessages": "訊息總數", + "durationDays": "跨度天數", + "dailyAvgMessages": "日均訊息" + }, + "tools": "常用工具" + }, + "messageExport": { + "title": "匯出聊天紀錄" + }, + "filter": { + "title": "自訂篩選", + "conditionMode": "條件篩選", + "sessionMode": "會話篩選", + "history": "歷史紀錄", + "execute": "開始篩選", + "export": "匯出篩選結果", + "keywords": "關鍵字", + "keywordsHint": "支援多個關鍵字,並以 OR 邏輯比對", + "keywordPlaceholder": "輸入關鍵字,按 Enter 新增", + "timeRange": "時間範圍", + "allTime": "全部", + "today": "今天", + "lastWeek": "近7天", + "lastMonth": "近1個月", + "last3Months": "近3個月", + "lastYear": "近1年", + "customTime": "自訂", + "senders": "傳送者", + "sendersHint": "可選,留空表示不限", + "searchMember": "搜尋成員...", + "noMembers": "暫無成員", + "selectedCount": "已選 {count} 位", + "contextSize": "上下文延伸", + "messages": "條訊息", + "searchSession": "搜尋日期...", + "selectedSessions": "已選擇 {count} 個會話", + "noSessions": "暫無會話索引", + "filtering": "正在篩選...", + "emptyHint": "設定篩選條件後再按下開始篩選", + "noResults": "未找到匹配的訊息", + "blockTitle": "對話區塊 {index}", + "stats": { + "blocks": "對話區塊", + "messages": "訊息數", + "hits": "命中數", + "chars": "字元數" + }, + "historyTitle": "篩選歷史", + "noHistory": "暫無歷史紀錄", + "loadMore": "載入更多", + "allLoaded": "已載入全部", + "exportSuccess": "匯出成功", + "exportFailed": "匯出失敗", + "exportPreparing": "正在準備匯出..." + } +} diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json new file mode 100644 index 0000000..05598e4 --- /dev/null +++ b/src/i18n/locales/zh-TW/common.json @@ -0,0 +1,129 @@ +{ + "confirm": "確定", + "cancel": "取消", + "delete": "刪除", + "save": "儲存", + "edit": "編輯", + "add": "新增", + "rename": "重新命名", + "capture": "截圖", + "close": "關閉", + "back": "返回上一頁", + "done": "完成", + "retry": "重試", + "loading": "載入中...", + "initializing": "初始化中...", + "noData": "暫無資料", + "export": "匯出", + "exportSuccess": "匯出成功", + "exportFailed": "匯出失敗", + "generating": "產生中...", + "regenerate": "重新產生", + "openFolder": "開啟資料夾", + "errorNoAIConfig": "請先在設定中設定 AI 服務", + "reset": "重設", + "error": { + "general": "操作失敗,請重試", + "network": "網路錯誤", + "unknown": "未知錯誤" + }, + "attachment": { + "image": "圖片", + "video": "影片", + "audio": "音訊", + "file": "檔案", + "sticker": "貼圖", + "link": "連結" + }, + "unit": { + "messages": "條訊息", + "members": "位成員", + "days": "天" + }, + "messageType": { + "text": "文字", + "image": "圖片", + "voice": "語音", + "video": "影片", + "file": "檔案", + "emoji": "表情", + "link": "連結", + "location": "位置", + "redPacket": "紅包", + "transfer": "轉帳", + "poke": "拍一拍", + "call": "通話", + "share": "分享", + "reply": "回覆", + "forward": "轉發", + "contact": "名片", + "system": "系統", + "recall": "收回", + "other": "其他", + "unknown": "未知" + }, + "weekday": { + "mon": "週一", + "tue": "週二", + "wed": "週三", + "thu": "週四", + "fri": "週五", + "sat": "週六", + "sun": "週日" + }, + "month": { + "jan": "1月", + "feb": "2月", + "mar": "3月", + "apr": "4月", + "may": "5月", + "jun": "6月", + "jul": "7月", + "aug": "8月", + "sep": "9月", + "oct": "10月", + "nov": "11月", + "dec": "12月" + }, + "agreement": { + "title": "隱私權政策與使用條款", + "subtitle": "使用前請先詳閱", + "disagree": "我不同意", + "agree": "我已閱讀並同意上述內容", + "updateNotice": "條款內容已更新,請重新閱讀並確認" + }, + "datePicker": { + "selectDate": "選擇日期", + "clearDate": "清除日期" + }, + "timeSelect": { + "mode": { + "recent": "最近", + "quarter": "按季", + "year": "按年", + "custom": "自訂" + }, + "recent": { + "halfYear": "近半年", + "oneYear": "近一年", + "twoYears": "近兩年", + "fiveYears": "近五年", + "all": "全部" + }, + "display": { + "recent180": "最近半年", + "recent365": "最近一年", + "recent730": "最近兩年", + "recent1825": "最近五年" + }, + "quarter": { + "label": "{year}年 第{quarter}季" + }, + "year": { + "label": "{year}年" + } + }, + "userSelect": { + "allMembers": "全部成員" + } +} diff --git a/src/i18n/locales/zh-TW/home.json b/src/i18n/locales/zh-TW/home.json new file mode 100644 index 0000000..56e0440 --- /dev/null +++ b/src/i18n/locales/zh-TW/home.json @@ -0,0 +1,124 @@ +{ + "title": "ChatLab", + "subtitle": "聊天紀錄分析工作台", + "features": { + "privacy": { + "title": "隱私至上", + "description": "聊天紀錄只在本機儲存與分析,資料不外傳。" + }, + "analysis": { + "title": "多維度分析", + "description": "從排行榜到金句,多角度解析群聊資料,挖出隱藏的趣味。" + }, + "ai": { + "title": "AI Agent", + "description": "內建 AI Agent,能回答各種群聊問題,幫你找出資料背後的脈絡與趣事。" + } + }, + "import": { + "dropHint": "鬆開滑鼠匯入檔案", + "clickHint": "點一下選擇,或直接拖曳聊天紀錄匯入", + "multipleHint": "支援同時選擇或拖曳多個檔案", + "selectFiles": "選擇聊天紀錄檔案", + "chatRecords": "聊天紀錄", + "allFiles": "所有檔案", + "progress": { + "detecting": "正在偵測格式...", + "reading": "正在讀取檔案...", + "parsing": "正在解析訊息...", + "saving": "正在寫入資料庫...", + "done": "匯入完成", + "error": "匯入中斷" + }, + "processed": "已處理 {count} 則訊息", + "tutorial": "查看聊天紀錄匯入教學", + "viewLog": "檢視匯入日誌", + "cannotReadPath": "無法讀取檔案路徑", + "preparing": "準備匯入...", + "errors": { + "no_file_selected": "未選擇檔案", + "import_failed": "匯入失敗", + "unrecognized_format": "無法識別的檔案格式", + "no_messages": "未解析到任何訊息,請檢查檔案格式是否正確" + }, + "diagnostics": { + "format": "偵測格式:", + "received": "收到訊息:", + "written": " / 寫入:", + "skipped": " / 跳過:" + }, + "batch": { + "importing": "批次匯入中", + "progressCount": "第 {current} / {total} 個", + "cancel": "取消匯入", + "waiting": "等待中", + "skipped": "已跳過", + "success": "匯入成功", + "completed": "批次匯入完成", + "summary": "成功 {success} 個,失敗 {failed} 個,跳過 {cancelled} 個", + "view": "檢視" + }, + "options": { + "title": "更多選項", + "mergeImport": "合併匯入", + "mergeImportHint": "勾選後,多個檔案會合併成同一份聊天紀錄" + }, + "merge": { + "importing": "正在合併", + "parsing": "正在解析檔案...", + "parsingFile": "正在解析:{name}", + "merging": "正在合併訊息...", + "completed": "合併完成", + "completedHint": "{count} 個檔案已合併", + "messageCount": "{count} 條訊息", + "failed": "合併失敗", + "cancel": "取消合併" + } + }, + "migration": { + "title": "資料庫需要更新", + "description": "偵測到 {count} 個資料庫需要升級以支援新功能。", + "note": "更新通常只需幾秒鐘,不會遺失任何資料。", + "upgradeContent": "本次升級內容:", + "upgradeNow": "立即升級", + "upgrading": "正在升級...", + "failed": "遷移失敗", + "partialFailed": "部分資料庫升級失敗", + "errorHint": "這通常是因為資料庫檔案損壞。建議在側邊欄中刪除失敗的會話,然後重新匯入。", + "close": "關閉", + "retry": "重試" + }, + "chatSelector": { + "title": "選擇要匯入的聊天", + "scanning": "正在掃描聊天列表...", + "scanFailed": "掃描失敗", + "retry": "重試", + "selectAll": "全選", + "chatCount": "共 {count} 個聊天", + "selected": "已選 {count} 個", + "messageCount": "{count} 條訊息", + "import": "匯入 ({count})", + "noChats": "未發現任何聊天" + }, + "footer": { + "website": "官網", + "terms": "使用條款" + }, + "changelog": { + "title": "更新紀錄", + "subtitle": "查看所有版本更新內容", + "viewChangelog": "更新紀錄", + "latest": "最新版本", + "current": "目前版本", + "total": "共 {count} 個版本", + "close": "關閉", + "loadError": "載入更新紀錄失敗", + "retry": "重試", + "types": { + "feat": "新功能", + "fix": "問題修復", + "chore": "其他優化", + "style": "樣式優化" + } + } +} diff --git a/src/i18n/locales/zh-TW/index.ts b/src/i18n/locales/zh-TW/index.ts new file mode 100644 index 0000000..af418d7 --- /dev/null +++ b/src/i18n/locales/zh-TW/index.ts @@ -0,0 +1,27 @@ +import common from './common.json' +import layout from './layout.json' +import settings from './settings.json' +import home from './home.json' +import analysis from './analysis.json' +import tools from './tools.json' +import providers from './providers.json' +import ai from './ai.json' +import quotes from './quotes.json' +import members from './members.json' +import records from './records.json' +import views from './views.json' + +export default { + common, + layout, + settings, + home, + analysis, + tools, + providers, + ai, + quotes, + members, + records, + views, +} diff --git a/src/i18n/locales/zh-TW/layout.json b/src/i18n/locales/zh-TW/layout.json new file mode 100644 index 0000000..7bf2cbe --- /dev/null +++ b/src/i18n/locales/zh-TW/layout.json @@ -0,0 +1,34 @@ +{ + "brand": "ChatLab", + "newAnalysis": "分析新聊天", + "tools": "實用工具", + "chatHistory": "聊天紀錄", + "noRecords": "暫無紀錄", + "searchPlaceholder": "搜尋聊天名稱", + "noSearchResult": "未找到匹配的聊天紀錄", + "contextMenu": { + "rename": "重新命名", + "pin": "置頂", + "unpin": "取消置頂", + "delete": "刪除" + }, + "renameModal": { + "title": "重新命名", + "placeholder": "請輸入新名稱" + }, + "deleteModal": { + "title": "確認刪除", + "message": "確定要刪除聊天紀錄「{name}」嗎?此操作無法復原。" + }, + "tooltip": { + "expand": "展開側邊欄", + "collapse": "收起側邊欄", + "hint": "右鍵可刪除或重新命名聊天紀錄", + "search": "搜尋聊天紀錄" + }, + "sessionInfo": "{count} 條訊息 · {time}", + "footer": { + "helpAndFeedback": "回饋與協助", + "settings": "設定" + } +} diff --git a/src/i18n/locales/zh-TW/members.json b/src/i18n/locales/zh-TW/members.json new file mode 100644 index 0000000..bf30834 --- /dev/null +++ b/src/i18n/locales/zh-TW/members.json @@ -0,0 +1,84 @@ +{ + "ownerSelector": { + "title": "選擇你是誰", + "unset": "未設定", + "selectMember": "選擇成員", + "currentOwner": "目前:{name}", + "hint": "1. 會顯示在聊天紀錄檢視器右側 2. AI 對話中也會標示你的身分" + }, + "list": { + "title": "成員管理", + "description": "共 {count} 位成員,可為成員新增別名備註或移除成員", + "searchPlaceholder": "搜尋群暱稱、帳號名稱、ID 或別名", + "noMatch": "沒有找到符合的成員", + "empty": "暫無成員資料", + "table": { + "accountName": "帳號名稱", + "groupNickname": "群暱稱", + "messageCount": "訊息數", + "customAlias": "自訂別名", + "actions": "操作" + }, + "aliasPlaceholder": "輸入後按 Enter 新增", + "delete": "刪除", + "pagination": "顯示第 {start} - {end} 筆,共 {total} 位成員", + "tip": "提示:新增別名後,搜尋與 AI 分析時會更容易辨識聊天對象。", + "modal": { + "title": "確認刪除成員?", + "content": "即將刪除成員 {name} 及其 {count} 則訊息,此操作不可復原。", + "cancel": "取消", + "confirm": "確認刪除" + } + }, + "nicknameHistory": { + "note": "備註:QQ 舊版客戶端支援匯出 txt 聊天紀錄,該版本能取得較完整的暱稱變更紀錄", + "title": "暱稱變更紀錄", + "loading": "載入中...", + "hasChanges": "{count} 位成員曾修改過暱稱", + "noChanges": "暫無成員修改暱稱", + "current": "當前", + "empty": "該群組所有成員均未修改過暱稱", + "loadingText": "正在載入暱稱變更紀錄..." + }, + "relationships": { + "loading": "正在分析 {'@'} 互動資料...", + "topMentioners": "📢 誰最喜歡 {'@'} 別人", + "totalMentions": "共 {count} 次 {'@'}", + "topMentioned": "🎯 誰最常被 {'@'}", + "popularMember": "群裡的人氣王", + "times": "次", + "oneWay": { + "title": "🐕 單向關注偵測", + "description": "發現 {count} 對單向關注關係(一方 {'@'} 另一方佔比 ≥80%)", + "neverRespond": "從不回應", + "reverse": "反向 {count} 次", + "ratio": "{value}% 單向" + }, + "twoWay": { + "title": "💕 雙向奔赴偵測", + "description": "發現 {count} 對互動頻繁的 CP(互相 {'@'} 且平衡度 ≥30%)", + "interactions": "次互動", + "balance": "{value}% 平衡" + }, + "emptyTitle": "📱 {'@'} 互動分析", + "empty": "暫無 {'@'} 互動資料", + "modal": { + "title": "{name} 的 {'@'} 關係", + "topMentioned": "最常 {'@'} 的人", + "topMentioners": "最常 {'@'} TA 的人", + "timesCount": "{count} 次", + "close": "關閉" + } + }, + "private": { + "title": "對話成員", + "description": "共 {count} 位成員,可為成員新增別名備註用於搜尋和 AI 分析", + "messageCount": "訊息數", + "percentage": "佔比 {value}%", + "customAlias": "自訂別名", + "aliasPlaceholder": "輸入後按 Enter 新增別名", + "empty": "暫無成員資料", + "tipTitle": "提示", + "tipContent": "新增別名可以更好地識別聊天紀錄中的對話對象,別名將用於搜尋和 AI 分析中。" + } +} diff --git a/src/i18n/locales/zh-TW/providers.json b/src/i18n/locales/zh-TW/providers.json new file mode 100644 index 0000000..7e3a278 --- /dev/null +++ b/src/i18n/locales/zh-TW/providers.json @@ -0,0 +1,75 @@ +{ + "deepseek": { + "name": "DeepSeek", + "description": "DeepSeek 大型語言模型", + "models": { + "deepseek-chat": "通用對話模型", + "deepseek-coder": "程式碼生成模型" + } + }, + "qwen": { + "name": "通義千問", + "description": "阿里雲通義千問大型語言模型", + "models": { + "qwen-turbo": "通義千問高速模型", + "qwen-plus": "通義千問高效能模型", + "qwen-max": "通義千問旗艦模型" + } + }, + "gemini": { + "name": "Gemini", + "description": "Google Gemini 大型語言模型", + "models": { + "gemini-3-flash-preview": "高速預覽版", + "gemini-3-pro-preview": "進階預覽版" + } + }, + "minimax": { + "name": "MiniMax", + "description": "MiniMax 大型語言模型,支援多模態與長上下文", + "models": { + "MiniMax-M2": "旗艦模型", + "MiniMax-M2-Stable": "穩定版本" + } + }, + "glm": { + "name": "GLM", + "description": "智譜 AI 大型語言模型,ChatGLM 系列", + "models": { + "glm-4-plus": "旗艦模型,整體表現最佳", + "glm-4-flash": "高速模型,性價比高", + "glm-4": "標準模型", + "glm-4v-plus": "多模態視覺模型", + "glm-4.6v-flash": "4.6V 免費模型", + "glm-4.5-flash": "4.5 免費模型" + } + }, + "kimi": { + "name": "Kimi", + "description": "Moonshot AI 大型語言模型,支援超長上下文", + "models": { + "moonshot-v1-8k": "8K 上下文", + "moonshot-v1-32k": "32K 上下文", + "moonshot-v1-128k": "128K 超長上下文" + } + }, + "doubao": { + "name": "豆包", + "description": "字節跳動豆包 AI 大型語言模型", + "models": { + "doubao-seed-1-6-lite-251015": "豆包 1.6 輕量模型,性價比高", + "doubao-seed-1-6-251015": "豆包 1.6 強化模型", + "doubao-seed-1-6-flash-250828": "豆包 1.6 高速模型", + "doubao-1-5-lite-32k-250115": "豆包 1.5 Pro 模型" + } + }, + "openai-compatible": { + "name": "OpenAI 相容", + "description": "支援任何相容 OpenAI API 的服務(如 Ollama、LocalAI、vLLM 等)", + "models": { + "llama3.2": "Meta Llama 3.2 模型", + "qwen2.5": "通義千問 2.5 模型", + "deepseek-r1": "DeepSeek R1 推理模型" + } + } +} diff --git a/src/i18n/locales/zh-TW/quotes.json b/src/i18n/locales/zh-TW/quotes.json new file mode 100644 index 0000000..32d28a9 --- /dev/null +++ b/src/i18n/locales/zh-TW/quotes.json @@ -0,0 +1,128 @@ +{ + "wordcloud": { + "loading": "正在分析詞頻...", + "sidebar": { + "filter": "篩選條件", + "style": "樣式設定" + }, + "stats": { + "messagesLabel": "訊息數", + "wordsLabel": "總詞數", + "uniqueLabel": "不重複詞數" + }, + "config": { + "maxWords": "顯示詞數", + "sizeScale": "字體大小", + "userFilter": "使用者篩選", + "posFilter": "詞性過濾", + "enableStopwords": "過濾停用詞" + }, + "size": { + "small": "小", + "medium": "中", + "large": "大", + "xlarge": "特大" + }, + "posFilter": { + "all": "全部詞性", + "meaningful": "常用詞性", + "custom": "自訂", + "customHint": "自選詞性", + "selectMeaningful": "常用詞性", + "selectAll": "全選", + "clearAll": "清空" + }, + "empty": { + "title": "暫無詞雲資料", + "description": "目前篩選條件下的文字訊息不足,無法產生詞雲" + } + }, + "keywords": { + "title": "🔍 關鍵字排行榜", + "description": "分析群聊中的關鍵字使用情況,也能自訂不同榜單", + "countTemplate": "共 {count} 位成員", + "templateLabel": "範本:", + "newTemplate": "+ 新增範本", + "searchPlaceholder": "輸入後搜尋", + "clear": "清空", + "templateHint": "* 右鍵範本可編輯或刪除", + "keyword": "關鍵字", + "ranking": "排行榜", + "multiColorMode": "多色模式", + "times": "{count} 次", + "timesWithPercent": "次 ({percent}%)", + "loading": "正在分析資料...", + "empty": "暫無資料,請嘗試新增關鍵字或切換範本", + "contextMenu": { + "edit": "編輯", + "delete": "刪除" + }, + "modal": { + "createTitle": "建立範本", + "editTitle": "編輯範本", + "templateName": "範本名稱", + "templateNamePlaceholder": "如:正能量", + "keywords": "關鍵字", + "keywordPlaceholder": "輸入後按 Enter 新增", + "cancel": "取消", + "save": "儲存", + "update": "更新" + }, + "templates": { + "laugh": { + "name": "歡樂值", + "keywords": "哈哈,xswl,lol,笑死,233", + "description": "統計群內的快樂指數" + }, + "sad": { + "name": "沮喪量", + "keywords": "想死,難受,哭了,崩潰,裂開,無語,累了", + "description": "統計群內的負面情緒" + }, + "praise": { + "name": "捧哏", + "keywords": "牛逼,666,厲害,強,nb,大佬,羨慕,好強", + "description": "統計群內最會誇人的成員" + }, + "slacker": { + "name": "摸魚", + "keywords": "摸魚,下班,餓了,困了,不想上班,什麼時候下班", + "description": "統計群內最想下班的打工人" + }, + "gossip": { + "name": "吃瓜", + "keywords": "吃瓜,細說,真的假的,展開說說,尊嘟假嘟,臥槽,離譜", + "description": "統計群內最愛吃瓜的成員" + }, + "polite": { + "name": "禮貌", + "keywords": "謝謝,麻煩,收到,好的,辛苦,打擾,請教", + "description": "統計群內最客氣的成員" + }, + "curious": { + "name": "疑問", + "keywords": "為什麼,啥,怎麼,不懂,求教", + "description": "統計群內問題最多的成員" + } + } + }, + "hotRepeat": { + "title": "🔥 熱門複讀", + "loading": "正在載入複讀資料...", + "description": "單次複讀參與人數最多的內容", + "countTemplate": "共 {count} 筆熱門複讀", + "people": "{count}人", + "times": "{count} 次", + "colon": ":", + "viewChat": "檢視聊天紀錄", + "empty": "暫無複讀資料" + }, + "catchphrase": { + "title": "💬 口頭禪分析", + "loading": "正在分析口頭禪資料...", + "description": "已分析 {count} 位成員的高頻發言", + "countTemplate": "共 {count} 位成員", + "times": "{count} 次", + "empty": "暫無口頭禪資料" + } +} diff --git a/src/i18n/locales/zh-TW/records.json b/src/i18n/locales/zh-TW/records.json new file mode 100644 index 0000000..151458c --- /dev/null +++ b/src/i18n/locales/zh-TW/records.json @@ -0,0 +1,87 @@ +{ + "drawer": { + "title": "聊天紀錄檢視器", + "loadedCount": "已載入 {count} 條訊息" + }, + "messageList": { + "loading": "載入中...", + "noMessages": "暫無訊息", + "tryAdjustFilter": "嘗試調整篩選條件", + "loadingMore": "載入更多...", + "scrollUpForMore": "↑ 向上捲動載入更多", + "scrollDownForMore": "↓ 向下捲動載入更多" + }, + "filter": { + "messageId": "訊息 ID", + "memberNotSupported": "成員(暫不支援)", + "keywordsPlaceholder": "關鍵字(多個請用逗號分隔,按 Enter 搜尋)", + "startDate": "起始日期", + "endDate": "結束日期", + "reset": "重置", + "filter": "篩選" + }, + "timeline": { + "timeline": "會話", + "noSessions": "暫無會話", + "generateSummary": "產生摘要", + "tooFewMessages": "訊息太少" + }, + "batchSummary": { + "title": "批次產生摘要", + "byRange": "依範圍", + "byTime": "依時間", + "selectRange": "選擇範圍", + "rangeStart": "最早", + "rangeInfo": "約", + "sessionsUnit": "個會話", + "rangeEnd": "最近", + "timeRange": "選擇時間範圍", + "today": "今天", + "yesterday": "昨天", + "week": "最近7天", + "month": "最近30天", + "custom": "自訂", + "found": "找到", + "hasSummary": "個已有摘要", + "tooFewMessages": "個訊息太少", + "pending": "待產生:", + "unit": "個", + "noPending": "沒有可產生摘要的會話", + "noSessions": "該時間範圍內沒有會話", + "checking": "檢查中...", + "loading": "載入中...", + "progress": "進度", + "session": "會話", + "statusSuccess": "成功", + "statusSkipped": "跳過", + "statusFailed": "失敗", + "success": "成功:", + "failed": "失敗:", + "skipped": "跳過:", + "start": "開始產生", + "stop": "停止" + }, + "messageItem": { + "viewContext": "檢視上下文", + "contextTitle": "訊息上下文(前後各10條)", + "noContext": "暫無上下文", + "replyTo": "回覆" + }, + "sessionIndex": { + "title": "會話索引", + "subtitle": "用於聊天紀錄的時間軸導覽", + "notGenerated": "尚未產生會話索引", + "notGeneratedHint": "產生會話索引後,您可以在聊天紀錄檢視器中透過時間軸快速切換到不同會話。", + "whatIsIt": "會話索引的作用:", + "benefit1": "根據訊息時間間隔自動識別會話邊界", + "benefit2": "在聊天紀錄檢視器中提供時間軸導覽", + "benefit3": "協助 AI 更理解對話上下文", + "generated": "會話索引已產生", + "sessionCount": "共識別出 {count} 個會話", + "regenerateHint": "如果您調整了會話間隔門檻,可重新產生索引。", + "generate": "產生索引", + "regenerate": "重新產生", + "generating": "產生中...", + "cancel": "取消" + } +} diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json new file mode 100644 index 0000000..05be336 --- /dev/null +++ b/src/i18n/locales/zh-TW/settings.json @@ -0,0 +1,379 @@ +{ + "title": "設定", + "tabs": { + "basic": "基本設定", + "ai": "AI 設定", + "aiConfig": "對話模型", + "aiRAG": "向量模型", + "aiPrompt": "聊天設定", + "aiPreset": "提示詞設定", + "aiPreprocess": "前處理", + "storage": "資料與儲存", + "storageManage": "儲存管理", + "sessionManage": "會話管理", + "about": "關於 ChatLab" + }, + "basic": { + "language": { + "title": "語言", + "description": "選擇軟體顯示語言" + }, + "appearance": { + "title": "外觀設定", + "themeMode": "主題模式", + "auto": "依系統設定", + "light": "淺色模式", + "dark": "深色模式" + }, + "screenshot": { + "title": "截圖設定", + "mobileAdapt": "行動畫面適配", + "mobileAdaptDesc": "截圖時會自動縮放寬度,方便在行動裝置上查看" + }, + "network": { + "title": "網路設定", + "proxyMode": "代理模式", + "proxyModeDesc": "存取網路時使用的代理方式", + "modeOff": "關閉", + "modeSystem": "跟隨系統", + "modeManual": "手動設定", + "proxyAddress": "代理地址", + "proxyPlaceholder": "http://127.0.0.1:7890", + "proxyHelp": "支援 HTTP/HTTPS 代理,格式如:http://127.0.0.1:7890", + "testConnection": "測試連線", + "testing": "測試中...", + "connectionSuccess": "代理連線成功!", + "connectionFailed": "連線失敗", + "enterProxyFirst": "請先輸入代理地址", + "invalidProxyUrl": "請輸入有效的代理地址,格式如 http://127.0.0.1:7890", + "onlyHttpSupported": "僅支援 http:// 或 https:// 協定", + "saveFailed": "儲存失敗" + } + }, + "aiConfig": { + "title": "聊天模型", + "loading": "載入中...", + "inUse": "使用中", + "defaultModel": "預設模型", + "localService": "本機服務", + "customEndpoint": "自訂端點", + "edit": "編輯", + "delete": "刪除", + "empty": { + "title": "尚未設定 AI 服務", + "description": "新增一組設定後即可開始使用 AI 功能" + }, + "addConfig": "新增設定", + "maxConfigs": "已達設定上限(最多 10 組)", + "modal": { + "addConfig": "新增設定", + "editConfig": "編輯設定", + "officialApi": "官方API", + "officialApiDesc": "DeepSeek、Gemini 等", + "localService": "本機服務", + "localServiceDesc": "Ollama 等", + "openaiCompatible": "OpenAI 相容", + "openaiCompatibleDesc": "自訂端點", + "configName": "設定名稱", + "optional": "(選填)", + "configNamePlaceholderPreset": "留空則使用服務商名稱", + "configNamePlaceholderCustom": "留空則使用 API 端點網址", + "aiProvider": "AI 服務商", + "apiKeyPlaceholder": "輸入你的 API Key", + "apiKeyPlaceholderEdit": "輸入新的 API Key(留空保持原有)", + "apiKeyPlaceholderLocal": "本機服務通常不需要", + "apiKeyHintLocal": "如果服務設定了認證,在此輸入", + "validate": "驗證", + "validationSuccess": "連線驗證成功", + "validationFailed": "連線驗證失敗", + "validationError": "驗證失敗:", + "model": "模型", + "modelName": "模型名稱", + "modelNamePlaceholder": "如 gpt-4、claude-3", + "modelNamePlaceholderLocal": "如 qwen3、deepseek-r1", + "modelNameHint": "輸入 API 支援的模型名稱", + "modelNameHintLocal": "輸入本機部署的模型名稱", + "apiEndpoint": "API 端點", + "apiEndpointHint": "相容 OpenAI 格式的 API 端點", + "disableThinking": "關閉思考模式", + "disableThinkingDesc": "針對 Qwen3、DeepSeek-R1 等模型,停用後使用標準工具呼叫格式", + "isReasoningModel": "推理模型", + "isReasoningModelDesc": "啟用後將提取思考過程並停用工具呼叫(如 DeepSeek-R1、QwQ 等)", + "advancedOptions": "進階設定", + "customService": "自訂服務", + "unnamedConfig": "未命名設定" + } + }, + "storage": { + "title": "本機儲存管理", + "description": "管理 ChatLab 在本機儲存的資料檔案", + "dataLocation": { + "title": "資料目錄位置", + "description": "自訂本機資料與快取的存放位置", + "choose": "選擇位置", + "reset": "還原預設", + "open": "開啟", + "restartTip": "切換目錄後請重新啟動應用程式,舊目錄下快取會跟隨遷移", + "confirmTitle": "確認切換資料目錄", + "confirmMessage": "您即將切換資料儲存位置,現有資料將被遷移到新目錄。", + "newPath": "新目錄路徑", + "defaultPath": "預設位置", + "confirmWarning": "重新啟動應用程式後,舊資料目錄將被自動刪除。請確保新目錄路徑正確。", + "cancel": "取消", + "confirm": "確認切換", + "migrationSuccessTitle": "資料遷移完成", + "migrationSuccessMessage": "應用程式需要重新啟動以使變更生效。", + "relaunchNow": "立即重新啟動" + }, + "totalUsage": "總佔用:", + "loading": "載入中...", + "files": "檔案", + "notExist": "不存在", + "clear": "清理", + "open": "開啟", + "notes": { + "title": "注意事項", + "logSafe": "日誌檔主要用於除錯,可安心清理", + "noRecover": "所有檔案清理後無法恢復,請謹慎操作" + }, + "cache": { + "databases": { + "name": "聊天紀錄資料庫", + "description": "匯入的聊天紀錄分析資料" + }, + "ai": { + "name": "AI 對話資料庫", + "description": "AI 對話歷史與設定檔" + }, + "downloads": { + "name": "下載目錄", + "description": "包含截圖檔案、分析結果等" + }, + "logs": { + "name": "日誌檔案", + "description": "軟體的執行日誌,包含匯入、AI、錯誤等日誌" + } + }, + "session": { + "title": "會話索引設定", + "description": "會話索引會依時間間隔自動將聊天紀錄切分成對話段落,方便 AI 分析與瀏覽", + "defaultThreshold": "預設分割間隔", + "thresholdUnit": "分鐘", + "thresholdHelp": "超過該時間間隔的訊息將被分到新的會話段落", + "notGenerated": "尚未產生會話索引", + "generateHint": "產生索引後,可讓 AI 更精準地理解對話上下文", + "generate": "產生索引", + "regenerate": "重新產生", + "generating": "產生中", + "sessionCount": "{count} 個會話", + "generated": "已產生會話索引", + "generateSuccess": "會話索引已成功產生,共 {count} 個會話", + "generateError": "產生失敗", + "batchTitle": "批次產生索引", + "totalSessions": "共 {count} 個聊天", + "generatedCount": "已產生 {count} 個", + "notGeneratedCount": "未產生 {count} 個", + "loadingStatus": "載入中...", + "batchGenerate": "產生未索引項目", + "batchRegenerate": "全部重新產生" + } + }, + "aiPrompt": { + "chatSettings": { + "title": "聊天設定" + }, + "exportSettings": { + "title": "匯出設定" + }, + "maxMessages": { + "title": "傳送條數限制", + "description": "每次提交給 AI 的最大訊息數,數值越大 Token 消耗越多,分析也更準確(新手建議2000)" + }, + "maxHistory": { + "title": "AI 上下文限制", + "description": "每次對話只保留最近幾輪內容(1 輪 = 使用者提問 + AI 回覆),避免上下文過長而消耗過多 Token" + }, + "exportFormat": { + "title": "對話匯出格式", + "description": "匯出 AI 對話時使用的檔案格式", + "txtLabel": "TXT" + }, + "sqlExportFormat": { + "title": "SQL Lab 匯出格式", + "description": "匯出 SQL 查詢結果時使用的檔案格式" + }, + "presets": { + "title": "系統提示詞", + "add": "新增預設", + "import": "匯入預設", + "description": "提示詞同時用於群聊和私聊分析,系統會自動根據分析類型調整相關內容" + }, + "preset": { + "builtIn": "內建", + "groupOnly": "僅群聊", + "privateOnly": "僅私聊", + "view": "檢視", + "edit": "編輯", + "copy": "複製", + "delete": "刪除" + }, + "modal": { + "editBuiltin": "編輯系統提示詞", + "editCustom": "編輯自訂提示詞", + "addCustom": "新增自訂提示詞", + "presetName": "預設名稱", + "presetNamePlaceholder": "為預設取個名字", + "applicableTo": "適用場景", + "applicableToHint": "(勾選後可在對應分析類型中使用)", + "groupChat": "群聊分析", + "privateChat": "私聊分析", + "systemPrompt": "系統提示詞", + "systemPromptPlaceholder": "定義 AI 助手的角色、任務和回答要求...", + "preview": "完整提示詞預覽", + "previewHint": "(預覽為群聊模式,實際會根據分析類型自動調整)", + "resetToDefault": "還原為預設", + "saveChanges": "儲存修改", + "addPreset": "新增預設" + }, + "importPreset": { + "title": "匯入預設", + "description": "從遠端取得推薦的系統提示詞預設", + "loading": "正在載入遠端預設...", + "loadError": "載入遠端預設失敗", + "noPresets": "暫無可用的遠端預設", + "commonPresets": "通用預設", + "groupPresets": "群聊專用預設", + "privatePresets": "私聊專用預設", + "add": "新增", + "added": "已新增", + "preview": "預覽", + "systemPrompt": "系統提示詞", + "noDescription": "暫無描述", + "fetchingContent": "正在載入內容...", + "fetchError": "載入內容失敗" + } + }, + "about": { + "title": "關於 ChatLab", + "description": "在本機運作的聊天紀錄分析工具,透過 SQL 和 AI Agent 幫你回顧社交記憶。", + "version": "版本", + "checkUpdate": "檢查更新", + "checking": "檢查中...", + "unknown": "未知", + "privacy": { + "title": "隱私設定", + "analytics": "匿名使用統計", + "analyticsDesc": "開啟後,軟體會收集版本號、作業系統版本等非敏感資料,用於幫助最佳化產品" + }, + "developer": { + "title": "開發者選項", + "debugMode": "DEBUG 模式", + "debugModeDesc": "開啟後,AI 日誌中將紀錄完整的原始訊息內容,不截斷。日誌檔案可能會顯著增大" + } + }, + "embedding": { + "title": "向量模型", + "description": "透過 Embedding 向量相似度理解問題含義,啟用後 AI 可進行語義搜尋", + "configList": "Embedding 設定", + "addConfig": "新增設定", + "editConfig": "編輯設定", + "noConfigs": "尚無設定,點擊上方按鈕即可新增", + "active": "使用中", + "setActive": "設為使用", + "deleteConfirm": "確定刪除設定「{name}」嗎?", + "configName": "設定名稱", + "configNamePlaceholder": "如:Ollama Embedding", + "apiSource": "API 來源", + "apiSourceHint": "「複用對話模型」將使用目前啟用的對話模型的端點和金鑰", + "reuseLLM": "複用對話模型", + "customAPI": "自訂 API", + "model": "模型名稱", + "modelPlaceholder": "如 nomic-embed-text", + "modelHint": "Ollama 常用:nomic-embed-text、mxbai-embed-large", + "baseUrl": "API 端點", + "baseUrlPlaceholder": "如 http://localhost:11434/v1", + "apiKey": "API Key", + "apiKeyPlaceholder": "輸入 API Key", + "optional": "(可選)", + "validate": "測試連線", + "validateSuccess": "連線成功!", + "validateFailed": "連線失敗", + "saveFailed": "儲存失敗", + "vectorStore": "向量快取", + "vectorStoreDesc": "快取已計算的向量,避免重複計算提高速度", + "cached": "已快取", + "size": "佔用", + "clear": "清空", + "clearVectorStoreConfirm": "確定清空所有向量快取嗎?這將導致下次搜尋時重新計算。" + }, + "aiPreprocess": { + "title": "聊天紀錄預處理", + "description": "在聊天紀錄傳送給 AI 前進行預處理,節省 Token 並過濾雜訊。原始資料不會被修改。", + "dataCleaning": "資料清洗", + "dataCleaningDesc": "自動清理 XML 卡片訊息(如分享連結、小程式等),提取有效內容,大幅節省 Token。建議保持開啟。", + "mergeConsecutive": "合併連續發言", + "mergeConsecutiveDesc": "同一人在短時間內的連續訊息合併為一條", + "mergeWindow": "合併時間視窗(秒)", + "denoise": "智慧去雜訊", + "denoiseDesc": "過濾純語氣詞、純表情、系統佔位符(如 [圖片])等無意義訊息", + "desensitize": "資料脫敏", + "desensitizeDesc": "根據規則將敏感資訊(手機號、電子郵件、證件號等)替換為佔位符", + "desensitizeRules": "脫敏規則", + "desensitizeRulesDesc": "勾選需要啟用的脫敏規則,按清單順序優先匹配", + "desensitizeBuiltin": "預置規則", + "desensitizeCustom": "自訂規則", + "desensitizeAddCustom": "新增自訂規則", + "desensitizeRuleName": "規則名稱", + "desensitizeRulePattern": "正規表達式", + "desensitizeRuleReplacement": "替換文字", + "desensitizeRuleAdd": "新增", + "desensitizeRuleInvalidRegex": "正規表達式語法錯誤", + "desensitizeRuleNamePlaceholder": "例如:員工工號", + "desensitizeRulePatternPlaceholder": "例如:EMP-\\d+", + "desensitizeRuleReplacementPlaceholder": "替換文字,如 [工號]", + "anonymizeNames": "暱稱匿名化", + "anonymizeNamesDesc": "用編號(U1、U2…)替代真實暱稱傳送給 AI,減少幻覺並節省 Token", + "blacklist": "黑名單關鍵詞", + "blacklistDesc": "包含以下任一關鍵詞的訊息將被整條過濾", + "blacklistPlaceholder": "輸入關鍵詞後按 Enter 新增", + "blacklistAdd": "新增" + }, + "desensitize": { + "rules": { + "cn_phone": "中國手機號", + "cn_phone_desc": "1xx xxxx xxxx 格式的 11 位號碼", + "cn_id_card": "中國身份證號", + "cn_id_card_desc": "18 位,末位可含 X", + "cn_bank_card": "中國銀行卡號", + "cn_bank_card_desc": "16-19 位連續數字", + "cn_landline": "中國市話號碼", + "cn_landline_desc": "區號-號碼,如 010-12345678", + "us_ssn": "美國社會安全號 (SSN)", + "us_ssn_desc": "XXX-XX-XXXX 格式", + "us_phone": "美國電話號碼", + "us_phone_desc": "+1 (XXX) XXX-XXXX 格式", + "us_drivers_license": "美國駕照號", + "us_drivers_license_desc": "字母開頭 + 7-8 位數字", + "jp_phone": "日本電話號碼", + "jp_phone_desc": "0x0-XXXX-XXXX 格式", + "jp_my_number": "日本個人編號", + "jp_my_number_desc": "12 位數字(マイナンバー)", + "kr_phone": "韓國電話號碼", + "kr_phone_desc": "01X-XXXX-XXXX 格式", + "kr_rrn": "韓國身份證號", + "kr_rrn_desc": "XXXXXX-XXXXXXX 格式(주민번호)", + "api_key_prefix": "API Key / Token(已知前綴)", + "api_key_prefix_desc": "sk-、ghp_、AKIA 等開頭的長字串", + "bearer_token": "Bearer Token", + "bearer_token_desc": "Bearer 後的授權權杖值", + "email": "電子郵件地址", + "email_desc": "user{'@'}domain.com 標準格式", + "credit_card": "信用卡號", + "credit_card_desc": "Visa / MasterCard / AmEx 等卡號", + "ipv4": "IPv4 地址", + "ipv4_desc": "如 192.168.1.1", + "url": "URL 連結", + "url_desc": "http:// 或 https:// 開頭的連結" + } + } +} diff --git a/src/i18n/locales/zh-TW/tools.json b/src/i18n/locales/zh-TW/tools.json new file mode 100644 index 0000000..96f8ad5 --- /dev/null +++ b/src/i18n/locales/zh-TW/tools.json @@ -0,0 +1,49 @@ +{ + "title": "管理", + "description": "管理聊天紀錄", + "tabs": { + "batchManage": "批次管理" + }, + "batchManage": { + "title": "批次刪除", + "description": "選擇要刪除的聊天紀錄", + "searchPlaceholder": "搜尋聊天名稱", + "searchResult": "找到 {count} / {total} 筆", + "noSearchResult": "未找到匹配的聊天紀錄", + "selectAll": "全選", + "shiftClickHint": "按住 Shift 點擊可範圍多選", + "selected": "已選 {count} 個", + "empty": "暫無聊天紀錄", + "delete": "刪除所選項目", + "confirmTitle": "確認刪除", + "confirmMessage": "確定要刪除選取的 {count} 個聊天紀錄嗎?此操作無法復原。", + "deleting": "正在刪除...", + "success": "成功刪除 {count} 個聊天紀錄", + "error": "刪除失敗:{error}", + "messageCount": "{count} 條訊息", + "importedAt": "匯入於 {time}", + "columns": { + "name": "名稱", + "platform": "平台", + "messages": "訊息數", + "summaries": "摘要", + "aiChats": "AI 對話", + "importedAt": "匯入時間" + }, + "clickToEdit": "點擊即可編輯名稱", + "merge": "合併所選項目", + "mergeHint": "選擇 2 個以上同平台的聊天紀錄才能合併", + "mergeConfirmTitle": "合併聊天紀錄", + "mergeConfirmMessage": "即將合併所選的 {count} 個聊天紀錄。合併後原始紀錄會被刪除,並建立一筆新的合併紀錄。", + "mergeSteps": { + "exporting": "正在匯出聊天紀錄...", + "parsing": "正在解析檔案...", + "checking": "正在偵測衝突...", + "merging": "正在合併資料...", + "cleaning": "正在清理暫存檔案..." + }, + "mergeSuccess": "成功合併 {count} 個聊天紀錄", + "mergeError": "合併失敗:{error}", + "mergedSuffix": "合併" + } +} diff --git a/src/i18n/locales/zh-TW/views.json b/src/i18n/locales/zh-TW/views.json new file mode 100644 index 0000000..f628c84 --- /dev/null +++ b/src/i18n/locales/zh-TW/views.json @@ -0,0 +1,82 @@ +{ + "cluster": { + "title": "互動頻率", + "rankingView": "排行榜檢視", + "memberView": "成員檢視", + "matrixView": "矩陣檢視", + "modelSettings": "模型參數", + "lookAhead": "鄰近人數", + "lookAheadDesc": "每則訊息往後納入幾位發言者", + "decaySeconds": "時間衰減(秒)", + "decaySecondsDesc": "值越大,遠距離訊息權重越高", + "applySettings": "套用設定", + "noData": "暫無資料", + "selectMember": "選擇成員", + "selectMemberHint": "請從左側選擇一個成員", + "msgCount": "發言數", + "relationCount": "關係數", + "relationsByIntimacy": "發言鄰近度排行", + "noRelations": "暫無關係資料", + "intimacy": "鄰近度", + "interactionRanking": "互動排行榜", + "score": "鄰近度", + "coOccurrence": "共現", + "times": "次", + "totalMembers": "群成員", + "totalMessages": "訊息總數", + "involvedMembers": "參與成員", + "edgeCount": "關係數" + }, + "message": { + "typeDistribution": "訊息類型分佈", + "hourlyDistribution": "小時分佈", + "weekdayDistribution": "星期分佈", + "monthlyDistribution": "月份分佈", + "yearlyDistribution": "年份分佈", + "timeHeatmap": "時間熱力圖", + "heatmapHint": "顯示聊天時間分布規律", + "calendarHeatmap": "訊息日曆", + "calendarHint": "每日訊息分佈", + "calendarTooltipMessages": "訊息", + "lengthDetailTitle": "短訊息分佈 (1-25字)", + "lengthDetailHint": "逐字統計", + "lengthGroupedTitle": "長度區間分佈", + "lengthGroupedHint": "每5字一組", + "noTextMessages": "暫無文字訊息", + "noData": "暫無資料" + }, + "interaction": { + "mentionGraph": "艾特互動關係圖", + "layout": "佈局", + "circular": "環形", + "force": "力導向", + "directed": "有向", + "reset": "重置", + "graphHint": "共 {nodes} 位成員,{links} 條互動關係", + "noInteraction": "暫無艾特互動資料" + }, + "timeline": { + "title": "時間軸檢視", + "description": "以視覺化方式呈現訊息時間分布與活躍週期變化" + }, + "charts": { + "rankListPro": { + "fullRanking": "完整排行榜", + "memberCount": "共 {count} 位成員" + }, + "rankList": { + "unit": "條" + }, + "nicknameHistory": { + "periodToNow": "{start} ~ 至今", + "periodRange": "{start} ~ {end}", + "current": "目前", + "empty": "暫無暱稱紀錄" + }, + "listPro": { + "countTemplate": "共 {count} 項", + "fullRanking": "完整排行榜", + "empty": "暫無資料" + } + } +} diff --git a/src/i18n/types.ts b/src/i18n/types.ts index dad10b0..7a1453d 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -1,7 +1,7 @@ /** * 支持的语言类型 */ -export type LocaleType = 'zh-CN' | 'en-US' +export type LocaleType = 'zh-CN' | 'en-US' | 'zh-TW' | 'ja-JP' /** * 语言配置项 @@ -16,10 +16,24 @@ export interface LocaleOption { * 可用的语言列表 */ export const availableLocales: LocaleOption[] = [ - { code: 'zh-CN', name: 'Chinese (Simplified)', nativeName: '简体中文' }, { code: 'en-US', name: 'English (US)', nativeName: 'English' }, + { code: 'zh-CN', name: 'Chinese (Simplified)', nativeName: '简体中文' }, + { code: 'zh-TW', name: 'Chinese (Traditional)', nativeName: '繁體中文' }, + { code: 'ja-JP', name: 'Japanese', nativeName: '日本語' }, ] +/** + * 所有合法的 locale code 集合,用于校验 + */ +const validLocales = new Set(availableLocales.map((l) => l.code)) + +/** + * 校验是否为合法的 locale code + */ +export function isValidLocale(locale: string): locale is LocaleType { + return validLocales.has(locale) +} + /** * 默认语言 */ @@ -30,12 +44,42 @@ export const defaultLocale: LocaleType = 'zh-CN' */ export function detectSystemLocale(): LocaleType { const systemLocale = navigator.language + if (systemLocale === 'zh-TW' || systemLocale === 'zh-Hant') { + return 'zh-TW' + } if (systemLocale.startsWith('zh')) { return 'zh-CN' } + if (systemLocale.startsWith('ja')) { + return 'ja-JP' + } return 'en-US' } +// ========== Locale 辅助函数 ========== + +/** + * 判断是否为中文系 locale(zh-CN / zh-TW), + * 用于分词策略、条数后缀、AI prompt 语言等场景 + */ +export function isChineseLike(locale: string): boolean { + return locale.startsWith('zh') +} + +/** + * locale -> dayjs locale 映射 + */ +const dayjsLocaleMap: Record = { + 'zh-CN': 'zh-cn', + 'zh-TW': 'zh-tw', + 'en-US': 'en', + 'ja-JP': 'ja', +} + +export function getDayjsLocale(locale: LocaleType): string { + return dayjsLocaleMap[locale] ?? 'en' +} + /** * 功能模块的语言支持配置 * 某些功能可能只支持特定语言 @@ -52,9 +96,7 @@ export interface FeatureLocaleSupport { * 用于控制某些功能只在特定语言下显示 */ export const featureLocaleRestrictions: Record = { - // 榜单(龙王、夜猫等)只在中文下显示 - groupRanking: ['zh-CN'], - // 以后可以在这里添加更多限制 + groupRanking: ['zh-CN', 'zh-TW'], } /** @@ -62,7 +104,6 @@ export const featureLocaleRestrictions: Record = { */ export function isFeatureSupported(feature: string, currentLocale: LocaleType): boolean { const supportedLocales = featureLocaleRestrictions[feature] - // 如果没有配置限制,则支持所有语言 if (!supportedLocales || supportedLocales.length === 0) { return true } diff --git a/src/pages/home/components/AgreementModal.vue b/src/pages/home/components/AgreementModal.vue index 6c321ab..b9043c8 100644 --- a/src/pages/home/components/AgreementModal.vue +++ b/src/pages/home/components/AgreementModal.vue @@ -5,9 +5,10 @@ import { useI18n } from 'vue-i18n' import MarkdownIt from 'markdown-it' import { useSettingsStore } from '@/stores/settings' import { availableLocales, type LocaleType } from '@/i18n' -// 导入中英文协议文档 import agreementZh from '@/assets/docs/agreement_zh.md?raw' import agreementEn from '@/assets/docs/agreement_en.md?raw' +import agreementZhTw from '@/assets/docs/agreement_zh_tw.md?raw' +import agreementJa from '@/assets/docs/agreement_ja.md?raw' const { t } = useI18n() const settingsStore = useSettingsStore() @@ -64,9 +65,15 @@ md.renderer.rules.link_open = (tokens, idx, options, _env, self) => { return self.renderToken(tokens, idx, options) } -// 根据当前语言选择协议文本 +const agreementMap: Record = { + 'zh-CN': agreementZh, + 'zh-TW': agreementZhTw, + 'en-US': agreementEn, + 'ja-JP': agreementJa, +} + const agreementText = computed(() => { - return locale.value === 'zh-CN' ? agreementZh : agreementEn + return agreementMap[locale.value] ?? agreementEn }) // 渲染后的 HTML diff --git a/src/pages/home/components/ChangelogModal.vue b/src/pages/home/components/ChangelogModal.vue index 4caa092..aec9c9f 100644 --- a/src/pages/home/components/ChangelogModal.vue +++ b/src/pages/home/components/ChangelogModal.vue @@ -82,9 +82,14 @@ interface ChangelogItem { // Changelog 数据 const changelogs = ref([]) -// 获取 changelog URL function getChangelogUrl(lang: string) { - const langPath = lang === 'zh-CN' ? 'cn' : 'en' + const langPathMap: Record = { + 'zh-CN': 'cn', + 'zh-TW': 'tw', + 'en-US': 'en', + 'ja-JP': 'ja', + } + const langPath = langPathMap[lang] ?? 'en' return `https://chatlab.fun/${langPath}/changelogs.json` } diff --git a/src/pages/home/components/HomeFooter.vue b/src/pages/home/components/HomeFooter.vue index 3972840..38f88e1 100644 --- a/src/pages/home/components/HomeFooter.vue +++ b/src/pages/home/components/HomeFooter.vue @@ -11,8 +11,15 @@ const { t, locale } = useI18n() // 配置 URL 根据语言动态获取 const CONFIG_BASE_URL = 'https://chatlab.fun' +const langPathMap: Record = { + 'zh-CN': 'cn', + 'zh-TW': 'tw', + 'en-US': 'en', + 'ja-JP': 'ja', +} + const configUrl = computed(() => { - const langPath = locale.value === 'zh-CN' ? 'cn' : 'en' + const langPath = langPathMap[locale.value] ?? 'en' return `${CONFIG_BASE_URL}/${langPath}/config.json` }) @@ -25,18 +32,17 @@ interface FooterLink { icon: string title: string url?: string - action?: 'changelog' + action?: 'changelog' | 'terms' } // 默认链接配置(根据语言返回) function getDefaultLinks(): FooterLink[] { - const isChinese = locale.value === 'zh-CN' return [ { id: 'website', icon: 'i-heroicons-globe-alt', - title: isChinese ? '官网' : 'Website', - url: isChinese ? 'https://chatlab.fun/cn/' : 'https://chatlab.fun/en/', + title: t('home.footer.website'), + url: `https://chatlab.fun/${langPathMap[locale.value] ?? 'en'}/`, }, { id: 'github', @@ -47,7 +53,7 @@ function getDefaultLinks(): FooterLink[] { { id: 'terms', icon: 'i-heroicons-document-text', - title: isChinese ? '使用条款' : 'Terms of Use', + title: t('home.footer.terms'), action: 'terms', }, { @@ -71,7 +77,7 @@ const socialConfig = ref({}) // 根据语言获取社交链接 const socialLink = computed(() => { - const isChinese = locale.value === 'zh-CN' + const isChinese = locale.value.startsWith('zh') if (isChinese && socialConfig.value.xiaohongshu?.show) { return { diff --git a/src/pages/home/components/ImportArea.vue b/src/pages/home/components/ImportArea.vue index 1194c4c..fc88c09 100644 --- a/src/pages/home/components/ImportArea.vue +++ b/src/pages/home/components/ImportArea.vue @@ -333,7 +333,8 @@ async function handleGoToSession(sessionId: string) { // 教程链接:根据语言动态生成 const tutorialUrl = computed(() => { const { locale } = useI18n() - const langPath = locale.value === 'zh-CN' ? '/cn' : '' + const pathMap: Record = { 'zh-CN': '/cn', 'zh-TW': '/tw', 'ja-JP': '/ja' } + const langPath = pathMap[locale.value] ?? '' return `https://chatlab.fun${langPath}/usage/how-to-export.html?utm_source=app` }) diff --git a/src/pages/manage/components/BatchManageTab.vue b/src/pages/manage/components/BatchManageTab.vue index f6c28cd..38f5eef 100644 --- a/src/pages/manage/components/BatchManageTab.vue +++ b/src/pages/manage/components/BatchManageTab.vue @@ -130,7 +130,7 @@ function isSelected(id: string): boolean { function formatTime(timestamp: number): string { return dayjs .unix(timestamp) - .locale(locale.value === 'zh-CN' ? 'zh-cn' : 'en') + .locale(({ 'zh-CN': 'zh-cn', 'zh-TW': 'zh-tw', 'ja-JP': 'ja' } as Record)[locale.value] ?? 'en') .fromNow() } diff --git a/src/stores/prompt.ts b/src/stores/prompt.ts index e630702..6217ef8 100644 --- a/src/stores/prompt.ts +++ b/src/stores/prompt.ts @@ -243,7 +243,7 @@ export const usePromptStore = defineStore( function duplicatePromptPreset(presetId: string) { const source = allPromptPresets.value.find((p) => p.id === presetId) if (source) { - const copySuffix = locale.value === 'zh-CN' ? '(副本)' : '(Copy)' + const copySuffix = locale.value.startsWith('zh') ? '(副本)' : '(Copy)' return addPromptPreset({ name: `${source.name} ${copySuffix}`, systemPrompt: source.systemPrompt, @@ -285,7 +285,8 @@ export const usePromptStore = defineStore( * @returns 远程预设索引列表,获取失败返回空数组 */ async function fetchRemotePresets(locale: string): Promise { - const langPath = locale === 'zh-CN' ? 'cn' : 'en' + const langPathMap: Record = { 'zh-CN': 'cn', 'zh-TW': 'tw', 'en-US': 'en', 'ja-JP': 'ja' } + const langPath = langPathMap[locale] ?? 'en' const indexUrl = `${REMOTE_PRESET_BASE_URL}/${langPath}/system-prompt.json` try { diff --git a/src/stores/settings.ts b/src/stores/settings.ts index 27e627e..8770cf4 100644 --- a/src/stores/settings.ts +++ b/src/stores/settings.ts @@ -2,8 +2,10 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import dayjs from 'dayjs' import 'dayjs/locale/zh-cn' +import 'dayjs/locale/zh-tw' import 'dayjs/locale/en' -import { type LocaleType, setLocale as setI18nLocale, getLocale } from '@/i18n' +import 'dayjs/locale/ja' +import { type LocaleType, setLocale as setI18nLocale, getLocale, getDayjsLocale } from '@/i18n' import type { PreprocessConfig } from '@electron/preload/index' const LOCALE_SET_KEY = 'chatlab_locale_set_by_user' @@ -50,7 +52,7 @@ export const useSettingsStore = defineStore( setI18nLocale(newLocale) - dayjs.locale(newLocale === 'zh-CN' ? 'zh-cn' : 'en') + dayjs.locale(getDayjsLocale(newLocale)) window.electron?.ipcRenderer.send('locale:change', newLocale) @@ -74,7 +76,7 @@ export const useSettingsStore = defineStore( } } - dayjs.locale(locale.value === 'zh-CN' ? 'zh-cn' : 'en') + dayjs.locale(getDayjsLocale(locale.value)) await ensureDesensitizeRules()