fix(i18n): polish Japanese dashboard copy

This commit is contained in:
Tam Nhu Tran
2026-03-10 16:46:11 +07:00
parent 8b5158bec5
commit e28f87bc9c
4 changed files with 131 additions and 86 deletions
@@ -171,8 +171,7 @@ export function HistorySyncLearningMap({
{legacyTargetCount > 0 && (
<div className="mt-2 rounded-md border border-amber-500/50 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-300">
{legacyTargetCount} legacy account
{legacyTargetCount > 1 ? 's still need' : ' still needs'} explicit confirmation.
{t('historySyncLearningMap.legacyConfirmation', { count: legacyTargetCount })}
</div>
)}
</CollapsibleContent>
+90 -83
View File
@@ -418,6 +418,8 @@ const resources = {
historySyncGroup: 'History Sync Group',
historySyncGroupDesc:
'Same group means shared project context lane. Default fallback is default.',
legacyConfirmation_one: '{{count}} legacy account still needs explicit confirmation.',
legacyConfirmation_other: '{{count}} legacy accounts still need explicit confirmation.',
},
accountsTable: {
name: 'Name',
@@ -1567,6 +1569,8 @@ const resources = {
modeSwitchDesc: '同步对话框可让用户在隔离/共享间切换并选择更深连续性。',
historySyncGroup: '历史同步分组',
historySyncGroupDesc: '同一分组即共享项目上下文。默认回退为 default。',
legacyConfirmation_one: '{{count}} 个旧账号仍需要显式确认。',
legacyConfirmation_other: '{{count}} 个旧账号仍需要显式确认。',
},
accountsTable: {
name: '名称',
@@ -2719,6 +2723,8 @@ const resources = {
historySyncGroup: 'Nhóm đồng bộ hóa lịch sử',
historySyncGroupDesc:
'Cùng một nhóm nghĩa là chia sẻ cùng bối cảnh project. Nếu để trống sẽ dùng nhóm mặc định.',
legacyConfirmation_one: '{{count}} tài khoản cũ vẫn cần xác nhận rõ ràng.',
legacyConfirmation_other: '{{count}} tài khoản cũ vẫn cần xác nhận rõ ràng.',
},
accountsTable: {
name: 'Tên',
@@ -3613,8 +3619,7 @@ const resources = {
presetNameOptional: 'プリセット名 (任意)',
presetNamePlaceholder: '例: My Custom Config',
codexTipPrefix: 'Codex のヒント: サフィックス',
codexTipSuffix:
'で effort を固定できます。サフィックスなしのモデルは Thinking 設定を使います。',
codexTipSuffix: 'で推論強度を固定できます。サフィックスなしのモデルは思考設定を使います。',
defaultModel: 'デフォルトモデル',
defaultModelDesc: '特定のティア指定がない場合に使用',
opusModel: 'Opus (最上位)',
@@ -3667,7 +3672,7 @@ const resources = {
createAuthProfileDialog: {
title: '新しいアカウントを作成',
description:
'認証プロファイルには Claude CLI へのログインが必要です。以下のコマンドをターミナルで実行してください。同期モード、グループ、継続性の深さは後で Accounts テーブルから編集できます。',
'認証プロファイルには Claude CLI へのログインが必要です。以下のコマンドをターミナルで実行してください。同期モード、グループ、継続性の深さは後でアカウント一覧から編集できます。',
profileName: 'プロファイル名',
profileNamePlaceholder: '例: work, personal, client',
invalidProfileName: '名前は英字で始め、英数字、ハイフン、アンダースコアのみ使用できます。',
@@ -3687,7 +3692,7 @@ const resources = {
afterRunStep1: 'ブラウザで Claude のログインを完了します',
afterRunStep2: 'ここに戻って更新し、新しいアカウントを確認します',
poolingHint:
'Claude OAuth のプールルーティングを使う場合は、Accounts ページのアクションボタンから CLIProxy Claude pool を利用してください。',
'Claude OAuth のプールルーティングを使う場合は、アカウントページのアクションボタンから CLIProxy Claude Pool を利用してください。',
close: '閉じる',
copied: 'コピーしました!',
copyCommand: 'コマンドをコピー',
@@ -3806,7 +3811,7 @@ const resources = {
title: 'プロファイルをローカル CLIProxy に同期',
description: 'CCS の API プロファイルをローカルの CLIProxy config.yaml に同期します。',
noProfiles: '同期するプロファイルがありません。',
createProfilesFirst: '先に Profiles タブで API プロファイルを作成してください。',
createProfilesFirst: '先にプロファイルタブで API プロファイルを作成してください。',
modelLabel: 'モデル:',
ready: '準備完了',
profilesToSync_one: '{{count}} 件のプロファイルを同期',
@@ -3830,7 +3835,7 @@ const resources = {
tabHeaders: 'ヘッダー',
tabRequest: 'リクエスト',
tabResponse: 'レスポンス',
tabRaw: 'Raw',
tabRaw: '生データ',
modelLabel: 'モデル:',
quotaResetsIn: 'クォータのリセットまで ',
method: 'メソッド',
@@ -3839,7 +3844,7 @@ const resources = {
endpoint: 'エンドポイント',
url: 'URL',
timestamp: 'タイムスタンプ',
na: 'N/A',
na: '該当なし',
noHeaders: '利用可能なヘッダーはありません',
noRequestBody: 'リクエストボディなし',
noResponseBody: 'レスポンスボディなし',
@@ -3848,7 +3853,7 @@ const resources = {
rateLimitedMsgNoModel:
'レート制限です。別のアカウントに切り替えるか、クォータのリセットを待ってください。',
authFailedMsg:
'認証に失敗しました。CLIProxy Settings で再認証するか、API キーを確認してください。',
'認証に失敗しました。CLIProxy 設定で再認証するか、API キーを確認してください。',
notFoundMsg:
'エンドポイントが見つかりません。このプロバイダーには存在しない可能性があります。',
serverErrorMsg:
@@ -3863,12 +3868,12 @@ const resources = {
'"{{name}}" が他の CCS auth アカウントと履歴や継続性を共有する方法を設定します。',
syncMode: '同期モード',
selectContextMode: 'コンテキストモードを選択',
isolatedOption: 'isolated(同期なし)',
sharedOption: 'shared(同期あり)',
isolatedOption: '分離(同期なし)',
sharedOption: '共有(同期あり)',
sharedModeHint:
'Shared モードでは、同じ履歴同期グループ内のアカウントでワークスペースコンテキストを再利用します。',
'共有モードでは、同じ履歴同期グループ内のアカウントでワークスペースコンテキストを再利用します。',
isolatedModeHint:
'Isolated モードでは、このアカウントを他の CCS auth アカウントから完全に離します。',
'分離モードでは、このアカウントを他の CCS auth アカウントから完全に切り離します。',
historySyncGroup: '履歴同期グループ',
groupPlaceholder: 'default',
groupHint:
@@ -3876,8 +3881,8 @@ const resources = {
invalidGroup: '英字で始まる有効なグループ名を入力してください。',
continuityDepth: '継続性レベル',
selectContinuityDepth: '継続性レベルを選択',
standardOption: 'standard(プロジェクトのみ)',
deeperOption: 'deeper continuity(上級者向け)',
standardOption: '標準(プロジェクトのみ)',
deeperOption: '拡張継続性(上級者向け)',
deeperHint:
'上級モードでは、session-env、file-history、shell-snapshots、todos も同期します。',
standardHint: '標準モードでは、プロジェクトのワークスペースコンテキストのみ同期します。',
@@ -3889,22 +3894,24 @@ const resources = {
},
historySyncLearningMap: {
title: '履歴同期の仕組み',
description: 'Isolated -> Shared -> Deeper。変更は各行の Sync から行います。',
description: '分離 -> 共有 -> 拡張共有。変更は各行の「同期」から行います。',
learningMap: '概要図',
isolated: 'Isolated',
shared: 'Shared',
deeper: 'Deeper',
isolated: '分離',
shared: '共有',
deeper: '拡張共有',
cliproxyManaged_one:
'{{count}} 件の CLIProxy Claude プールアカウントは Action Center / CLIProxy ページで管理されます。',
'{{count}} 件の CLIProxy Claude Pool アカウントはCLIProxy ページで管理ます。',
cliproxyManaged_other:
'{{count}} 件の CLIProxy Claude プールアカウントは Action Center / CLIProxy ページで管理されます。',
'{{count}} 件の CLIProxy Claude Pool アカウントはCLIProxy ページで管理ます。',
showDetails: '詳細を表示: グループ、切り替え、レガシーポリシー',
modeSwitch: 'モード切り替え',
modeSwitchDesc:
'Sync ダイアログから isolated / shared の切り替えと deeper continuity の選択ができます。',
'「同期」ダイアログでは、分離/共有の切り替えと、より深い継続性の選択ができます。',
historySyncGroup: '履歴同期グループ',
historySyncGroupDesc:
'同じグループ同じプロジェクトコンテキストレーンを共有します。既定のフォールバックは default す。',
'同じグループのアカウントは、同じプロジェクトコンテキストレーンを共有します。未設定時は default を使います。',
legacyConfirmation_one: '{{count}} 件のレガシーアカウントで明示的な確認がまだ必要です。',
legacyConfirmation_other: '{{count}} 件のレガシーアカウントで明示的な確認がまだ必要です。',
},
accountsTable: {
name: '名前',
@@ -3915,7 +3922,7 @@ const resources = {
historySync: '履歴同期',
actions: '操作',
syncTitle: '同期モード、グループ、継続性レベルを編集',
confirmLegacyTitle: 'この legacy アカウントの現在のモードを明示的に確定',
confirmLegacyTitle: 'このレガシーアカウントの現在のモードを明示的に確定する',
sync: '同期',
confirm: '確定',
active: '有効',
@@ -3924,17 +3931,17 @@ const resources = {
cannotDeleteDefault: '既定のアカウントは削除できません',
noAccounts:
'CCS auth アカウントが見つかりません。追加するには ccs auth create を使ってください。',
resetToDefault: 'CCS Default に戻す',
resetToDefault: 'CCS の既定値に戻す',
deleteDialogTitle: 'アカウントを削除',
deleteDialogDesc:
'アカウント "{{name}}" を削除しますか? この操作でプロファイルと関連するすべてのセッションデータが削除されます。この操作は元に戻せません。',
cancel: 'キャンセル',
delete: '削除',
sharedGroupStandard: 'shared{{group}}、標準)',
sharedGroupDeeper: 'shared{{group}}、deeper',
sharedGroupLegacy: 'shared{{group}}、標準・legacy',
isolatedLegacy: 'isolated(レガシー既定)',
isolated: 'isolated',
sharedGroupStandard: '共有{{group}}、標準)',
sharedGroupDeeper: '共有{{group}}、拡張',
sharedGroupLegacy: '共有{{group}}、標準・レガシー',
isolatedLegacy: '分離(旧既定',
isolated: '分離',
},
addAccountDialog: {
title: '{{displayName}} アカウントを追加',
@@ -3946,7 +3953,7 @@ const resources = {
'認証をクリックすると OAuth URL を取得します。任意のブラウザーで開いてサインインしてください。',
powerUserEnabled: '上級者モードが有効です',
powerUserSkipped:
'Settings > Proxy で AGY responsibility checklist をスキップしています。OAuth / アカウントリスクはすべて自己責任す。',
'設定 > プロキシで AGY の責任確認チェックをスキップしています。OAuth / アカウントに関するリスクはすべて自己責任となります。',
authMethod: '認証方法',
selectKiroAuthMethod: 'Kiro の認証方法を選択',
nicknameRequired: 'ニックネーム(必須)',
@@ -4042,7 +4049,7 @@ const resources = {
secondLine:
'OAuth とアカウントアクセスのリスクをすべて自己責任で負う場合のみ続行してください。',
issueLabel: 'Issue #509 を読む',
proxySettingsLabel: 'Gemini + AGY の設定: Settings > Proxy',
proxySettingsLabel: 'Gemini + AGY の設定: 設定 > プロキシ',
},
cliproxyDialog: {
name: '名前',
@@ -4103,9 +4110,9 @@ const resources = {
configFileNotFound: '設定ファイルが見つかりません',
},
settingsTabs: {
web: 'Web',
env: '環境',
think: 'Thinking',
web: 'Web検索',
env: '環境変数',
think: '思考',
proxy: 'プロキシ',
auth: '認証',
backup: 'バックアップ',
@@ -4138,11 +4145,11 @@ const resources = {
},
settingsGlobalEnv: {
description:
'Claude 以外のサブスクリプションプロファイル(gemini、codex、agy、ghcp など)に注入する環境変数',
enabled: 'Global Env を有効化',
disabled: 'Global Env を無効化',
enabledDesc: 'Env 変数をサードパーティープロファイル注入します',
disabledDesc: 'Env 変数は注入されません',
'Claude 以外のサブスクリプションプロファイル(gemini、codex、agy、ghcp など)に注入する環境変数',
enabled: 'グローバル環境変数を有効化',
disabled: 'グローバル環境変数を無効化',
enabledDesc: '環境変数をサードパーティープロファイル注入します',
disabledDesc: '環境変数は注入されません',
envVars: '環境変数',
noneConfigured: '環境変数は未設定です',
addNew: '変数を追加',
@@ -4219,7 +4226,7 @@ const resources = {
safety: '安全設定',
agyModeTitle: 'Antigravity パワーユーザーモード',
agyModeDesc:
'Add Account 画面と `ccs agy` フローで AGY の責任チェックリストをスキップします。',
'アカウント追加画面と `ccs agy` フローでAGY の責任確認チェックをスキップします。',
agyWarning:
'OAuth の停止/BAN リスクパターン (#509) を十分理解している場合にのみ使用してください。CCS はアカウント喪失の責任を負いません。',
finalConfirm: '最終確認',
@@ -4275,15 +4282,14 @@ const resources = {
backendApi: 'CLIProxyAPI',
},
settingsThinking: {
description: '対応モデルの拡張 Thinking / Reasoning を設定します。',
description: '対応モデルの高度な思考 / 推論設定を行います。',
supportedProviders: '対応プロバイダー',
modeTitle: 'Thinking モード',
modeAutoDesc: 'モデルのティアに応じて Thinking を自動設定します',
modeOffDesc: '拡張 Thinking を無効化します',
modeTitle: '思考モード',
modeAutoDesc: 'モデルのティアに応じて思考レベルを自動設定します',
modeOffDesc: '高度な思考を無効化します',
modeManualDesc: '永続的な上書きレベルを設定するか、実行ごとに CLI フラグを使います',
tierDefaults: 'ティア別デフォルト',
tierDefaultsDesc:
'Auto モード時に各モデルティアへ適用するデフォルトの Thinking レベルです。',
tierDefaultsDesc: '自動モード時に各モデルティアへ適用する既定の思考レベルです。',
persistentOverride: '永続オーバーライド',
persistentOverrideDesc: 'すべてのセッションに適用されます。CLI フラグが優先されます。',
enterCustomBudget: 'カスタム予算を入力',
@@ -4293,15 +4299,15 @@ const resources = {
'特定のプロバイダーに対してティア別デフォルトを上書きします。必要に応じてカスタムのプロバイダーキーを追加してください。',
addProviderPlaceholder: 'プロバイダーキーを追加(例: qwen)',
showWarnings: '警告を表示',
showWarningsDesc: 'Thinking 値が制限または調整された場合に警告を表示します',
cliEnvOverride: 'CLI / Env オーバーライド',
supportLine1Prefix: 'Thinking budget:',
supportLine1Suffix: '(token ベース)',
supportLine2Prefix: 'Reasoning effort:',
supportLine2SuffixPrefix: '(suffix または ',
showWarningsDesc: '思考値が制限または調整された場合に警告を表示します',
cliEnvOverride: 'CLI / 環境変数の上書き',
supportLine1Prefix: '思考予算:',
supportLine1Suffix: '(トークンベース',
supportLine2Prefix: '推論強度:',
supportLine2SuffixPrefix: '(サフィックス、または ',
supportLine2SuffixPostfix: ': medium/high/xhigh)',
supportLine3Prefix: 'Codex の suffix は effort を固定します(例: ',
supportLine3Suffix: ')。suffix なしのモデルは Thinking mode を使用します。',
supportLine3Prefix: 'Codex のサフィックスは推論強度を固定します(例: ',
supportLine3Suffix: ')。サフィックスなしのモデルは思考モードを使います。',
},
analytics: {
title: '分析',
@@ -4322,15 +4328,15 @@ const resources = {
tokensSubtitle: '{{value}} トークン',
},
apiProfiles: {
title: 'APIプロファイル',
title: 'API プロファイル',
new: '新規',
searchPlaceholder: 'プロファイルを検索...',
loadingProfiles: 'プロファイルを読み込み中...',
failedLoadTitle: 'プロファイルを読み込めませんでした',
failedLoadDesc: 'APIプロファイルを取得できませんでした。もう一度お試しください。',
failedLoadDesc: 'API プロファイルを取得できませんでした。もう一度お試しください。',
retry: '再試行',
noProfilesYet: 'APIプロファイルがまだありません',
noProfilesDesc: '最初のプロファイルを作成して、カスタムAPIエンドポイントに接続します',
noProfilesYet: 'API プロファイルがまだありません',
noProfilesDesc: '最初のプロファイルを作成して、カスタム API エンドポイントに接続します',
createProfile: 'プロファイルを作成',
noProfileMatch: '"{{query}}" に一致するプロファイルはありません',
profileCount: '{{count}} 件のプロファイル',
@@ -4347,14 +4353,14 @@ const resources = {
},
accountsPage: {
title: 'アカウント',
managePrefix: '',
manageSuffix: 'のアカウント管理とプール導線を一元化。',
mobileManageSuffix: 'の継続性設定を管理。',
managePrefix: '一つの画面で',
manageSuffix: 'のアカウント管理とプール導線をまとめて扱えます。',
mobileManageSuffix: 'の継続性設定をまとめて管理できます。',
primaryActions: '主要アクション',
createAccount: 'アカウントを作成',
authClaudeInPool: 'PoolClaudeを認証',
openClaudePoolSettings: 'Claude Pool設定を開く',
openCliProxyClaudePool: 'CLIProxy Claude Poolを開く',
authClaudeInPool: 'PoolClaude を認証',
openClaudePoolSettings: 'Claude Pool設定を開く',
openCliProxyClaudePool: 'CLIProxy Claude Pool を開く',
migrationFollowup: '移行フォローアップ',
legacyContextPending: '{{count}} 件のアカウントで初回モード確認がまだ必要です。',
legacyContextPending_other: '{{count}} 件のアカウントで初回モード確認がまだ必要です。',
@@ -4362,7 +4368,7 @@ const resources = {
legacyContinuityPending_other:
'{{count}} 件の共有アカウントが標準のレガシー継続深度のままです。',
confirmingLegacy: 'レガシーポリシーを確認中...',
confirmLegacy: 'レガシーポリシーを確認 ({{count}})',
confirmLegacy: 'レガシーポリシーを確認{{count}}',
noLegacyFollowup: '保留中のレガシーフォローアップはありません。',
continuityGuide: '継続性ガイド',
expandWhenNeeded: '必要なときだけ展開。',
@@ -4370,19 +4376,20 @@ const resources = {
sharedStandardDesc:
'プロジェクトワークスペースの同期のみ。ほとんどのチームに最適な既定値です。',
sharedDeeper: '共有拡張',
sharedDeeperPrefix: '追加:',
sharedDeeperPrefix: '追加',
isolated: '分離',
isolatedDesc: 'リンクなし。厳密な分離に最適です。',
quickCommands: 'クイックコマンド',
quickCommandsDesc: 'コピーしてターミナルで実行します。',
workspaceBadge: 'ccs auth ワークスペース',
historySyncBadge: '履歴同期コントロール',
historySyncBadge: '履歴同期設定',
authAccounts: '認証アカウント',
tableScopePrefix: '',
tableScopeMiddle: 'のアカウントのみを表示します。モード/グループ/深度の変更は',
tableScopePrefix: 'この表では',
tableScopeMiddle: 'のアカウントのみを表示します。モード/グループ/継続性の変更は',
tableScopeSuffix: 'を使ってください。',
accountMatrix: 'アカウントマトリクス',
sharedTotalDesc: '共有合計: {{count}}。操作にはSync設定とレガシー確認が含まれます。',
accountMatrix: 'アカウント一覧',
sharedTotalDesc:
'共有アカウント合計: {{count}}。ここから同期設定とレガシー確認を行えます。',
loadingAccounts: 'アカウントを読み込み中...',
},
health: {
@@ -4444,7 +4451,7 @@ const resources = {
viaProvider: '{{provider}} 経由',
emptyTitle: 'CCS プロファイルマネージャー',
emptyDesc:
'CLIProxyプロバイダー向けのOAuth認証、アカウント設定、モデル選択を管理します。CCSが各AIバックエンドへのリクエストをどうルーティングするかを設定できます。',
'CLIProxy プロバイダー向けの OAuth 認証、アカウント設定、モデル選択を管理します。CCS が各 AI バックエンドへのリクエストをどうルーティングするかを設定できます。',
emptyControlPanelPrefix: 'ライブ利用統計とリアルタイム監視は',
controlPanel: 'コントロールパネルで確認できます',
quickSetup: 'クイックセットアップ',
@@ -4627,23 +4634,23 @@ const resources = {
customModelsTitle: 'カスタムモデル',
modelName: '名前 / モデル',
provider: 'プロバイダー',
baseUrl: 'Base URL',
baseUrl: 'ベース URL',
noCustomModels: 'カスタムモデルはありません',
docsAlignedNotes: 'ドキュメント整合メモ',
factoryDocs: 'Factory Docs',
providerFactCheckDocs: 'プロバイダー検証ドキュメント',
providerValues: 'プロバイダー値: {{value}}',
settingsHierarchy: '設定階層: {{value}}',
settingsTitle: 'Droid BYOK設定',
fixJsonBeforeQuickSettings: 'クイック設定を使う前にJSON構文を修正してください。',
fixJsonBeforeReasoning: 'BYOK推論設定を更新する前にJSON構文を修正してください。',
unableUpdateReasoning: '選択したBYOKモデルの推論設定を更新できません。',
fixJsonBeforeBudget: 'thinking budgetを更新する前にJSON構文を修正してください。',
anthropicOnlyBudget: 'thinking budget はAnthropicBYOKモデルでのみ利用できます。',
invalidJson: 'JSONが不正です: {{value}}',
saved: 'Droid設定を保存しました',
changedExternally: 'Droid設定が外部で変更されました。更新して再試行してください。',
failedSave: 'Droid設定の保存に失敗しました',
settingsTitle: 'Droid BYOK 設定',
fixJsonBeforeQuickSettings: 'クイック設定を使う前に JSON 構文を修正してください。',
fixJsonBeforeReasoning: 'BYOK 推論設定を更新する前に JSON 構文を修正してください。',
unableUpdateReasoning: '選択した BYOK モデルの推論設定を更新できません。',
fixJsonBeforeBudget: '思考予算を更新する前に JSON 構文を修正してください。',
anthropicOnlyBudget: '思考予算は AnthropicBYOK モデルでのみ利用できます。',
invalidJson: 'JSON が不正です: {{value}}',
saved: 'Droid 設定を保存しました',
changedExternally: 'Droid 設定が外部で変更されました。更新して再試行してください。',
failedSave: 'Droid 設定の保存に失敗しました',
},
sharedPage: {
commands: 'コマンド',
+1 -1
View File
@@ -232,7 +232,7 @@ export function AccountsPage() {
{t('accountsPage.tableScopePrefix')}
<code className="mx-1 rounded bg-muted px-1 py-0.5">ccs auth</code>
{t('accountsPage.tableScopeMiddle')}
<code className="mx-1 rounded bg-muted px-1 py-0.5">Sync</code>
<code className="mx-1 rounded bg-muted px-1 py-0.5">{t('accountsTable.sync')}</code>
{t('accountsPage.tableScopeSuffix')}
</p>
</div>
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen, userEvent, waitFor } from '@tests/setup/test-utils';
import i18n from '@/lib/i18n';
import { LanguageSwitcher } from '@/components/layout/language-switcher';
import { HistorySyncLearningMap } from '@/components/account/history-sync-learning-map';
import { TabNavigation } from '@/pages/settings/components/tab-navigation';
import {
getInitialLocale,
@@ -126,6 +127,44 @@ describe('Dashboard i18n', () => {
expect(screen.getByText('认证')).toBeInTheDocument();
});
it('shows Japanese labels on translated settings tabs', async () => {
await i18n.changeLanguage('ja');
render(<TabNavigation activeTab="websearch" onTabChange={() => {}} />);
expect(screen.getByText('Web検索')).toBeInTheDocument();
expect(screen.getByText('環境変数')).toBeInTheDocument();
expect(screen.getByText('思考')).toBeInTheDocument();
});
it('renders Japanese history sync guidance without fallback English copy', async () => {
await i18n.changeLanguage('ja');
render(
<HistorySyncLearningMap
isolatedCount={1}
sharedStandardCount={2}
deeperSharedCount={3}
sharedGroups={['default']}
legacyTargetCount={2}
cliproxyCount={1}
/>
);
expect(screen.getByText('履歴同期の仕組み')).toBeInTheDocument();
expect(
screen.getByText('1 件の CLIProxy Claude Pool アカウントは、CLIProxy ページで管理します。')
).toBeInTheDocument();
await userEvent.click(
screen.getByRole('button', { name: '詳細を表示: グループ、切り替え、レガシーポリシー' })
);
expect(
screen.getByText('2 件のレガシーアカウントで明示的な確認がまだ必要です。')
).toBeInTheDocument();
});
it.each(SUPPORTED_LOCALES.filter((locale) => locale !== 'en'))(
'keeps %s translation keys in parity with en and preserves placeholders',
(locale) => {