From c3df704da221faf963b28db02787172142fef111 Mon Sep 17 00:00:00 2001 From: dnjin Date: Sat, 12 Sep 2026 19:41:22 +0800 Subject: [PATCH] fear(core,ui): add Japanese --- apps/vscode-extension/package.nls.ja.json | 3 + apps/vscode-extension/src/extension.ts | 2 +- apps/vscode-extension/src/tools/i18n/index.ts | 5 +- apps/vscode-extension/src/tools/i18n/ja.ts | 122 ++++++++++++++ packages/webview/src/i18n/index.tsx | 6 +- packages/webview/src/i18n/ja.ts | 156 ++++++++++++++++++ 6 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 apps/vscode-extension/package.nls.ja.json create mode 100644 apps/vscode-extension/src/tools/i18n/ja.ts create mode 100644 packages/webview/src/i18n/ja.ts diff --git a/apps/vscode-extension/package.nls.ja.json b/apps/vscode-extension/package.nls.ja.json new file mode 100644 index 0000000..d376287 --- /dev/null +++ b/apps/vscode-extension/package.nls.ja.json @@ -0,0 +1,3 @@ +{ + "extension.showWebview": "AtCoder Helper(エディター)" +} diff --git a/apps/vscode-extension/src/extension.ts b/apps/vscode-extension/src/extension.ts index 01809c9..09c1bfb 100644 --- a/apps/vscode-extension/src/extension.ts +++ b/apps/vscode-extension/src/extension.ts @@ -206,7 +206,7 @@ export async function activate(context: vscode.ExtensionContext) { setStaleCookieHandler(() => { // vscode.window.showWarningMessage( - // "检测到 AtCoder Cookie 可能已过期:已临时使用无 Cookie 访问公开页面。如需提交/报名等登录功能,请重新登录并更新 Cookie。" + // "检测到 A tCoder Cookie 可能已过期:已临时使用无 Cookie 访问公开页面。如需提交/报名等登录功能,请重新登录并更新 Cookie。" // ); }); diff --git a/apps/vscode-extension/src/tools/i18n/index.ts b/apps/vscode-extension/src/tools/i18n/index.ts index 1341c03..c5fb84b 100644 --- a/apps/vscode-extension/src/tools/i18n/index.ts +++ b/apps/vscode-extension/src/tools/i18n/index.ts @@ -1,17 +1,18 @@ import { zh } from "./zh"; import { en } from "./en"; +import { ja } from "./ja"; import type { ZhDict } from "./zh"; type Dict = Record; -const dicts: Record = { zh, en }; +const dicts: Record = { zh, en, ja }; const fallback: Dict = en; let currentLang = "en"; export function init(language: string): void { const lang = (language || "en").toLowerCase(); - currentLang = lang.startsWith("zh") ? "zh" : "en"; + currentLang = lang.startsWith("zh") ? "zh" : lang.startsWith("ja") ? "ja" : "en"; } export type I18nKey = keyof ZhDict; diff --git a/apps/vscode-extension/src/tools/i18n/ja.ts b/apps/vscode-extension/src/tools/i18n/ja.ts new file mode 100644 index 0000000..e3ddf2b --- /dev/null +++ b/apps/vscode-extension/src/tools/i18n/ja.ts @@ -0,0 +1,122 @@ +import type { ZhDict } from "./zh"; + +export const ja: Record = { + "ext.judgeResult": "判定結果: {status}", + "ext.judgeTimeout": "判定がタイムアウトしました。しばらくしてから手動で更新して結果を確認してください", + "ext.promptDeeplKey": "DeepL API Key を入力してください", + "ext.placeholderDeeplKey": "例 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:fx", + "ext.deeplKeySaved": "DeepL API Key を保存しました", + "ext.promptCookie": "AtCoder の Cookie を貼り付けてください(REVEL_SESSION のみ)", + "ext.placeholderCookie": "REVEL_SESSION=abcdef1234567890abcdef1234567890", + "ext.cookieFormatWarn": "Cookie の形式が正しくないようです。REVEL_SESSION= の接頭辞を追加しますか?", + "ext.autoFix": "自動修正", + "ext.cancel": "キャンセル", + "ext.cookieSavedFixed": "AtCoder Cookie を保存し、形式を自動修正しました", + "ext.cookieSaved": "AtCoder Cookie を保存しました", + "ext.submissionPanelTitle": "提出 {id} - {contest}", + + "cmd.copied": "クリップボードにコピーしました", + "cmd.unknown": "不明なコマンド", + + "err.openInBrowser": "ブラウザで開く", + "err.proxyFailedTitle": "プロキシ接続に失敗し、AtCoder にアクセスできません", + "err.setNoProxy": "NO_PROXY を設定", + "err.viewWslDoc": "WSL プロキシの説明を見る", + "load.contestTasks": "{contest} の問題一覧を取得しています...", + "err.contestTasks": "問題一覧の取得に失敗しました", + "err.standingsParse": "順位表データの解析に失敗しました: {reason}", + "load.translateItem": "{name} を翻訳しています...", + "deepl.setKeyFirst": "先に DeepL API Key を設定してください", + "deepl.setKey": "API Key を設定", + "deepl.noKey": "DeepL API Key が設定されていません", + "err.translate": "翻訳に失敗しました", + "load.problem": "{contest}/{task} の問題文を取得しています...", + "err.problem": "問題文の取得に失敗しました", + "cookie.loaded": "✅ Cookie を読み込みました。ログインが必要な問題にアクセスできます", + "cookie.notSet": "Cookie が設定されていません", + "cookie.formatError": "❌ Cookie の形式が正しくありません。REVEL_SESSION= で始まる必要があります", + "cookie.tooShort": "❌ Cookie の値が短すぎます。REVEL_SESSION の値を完全にコピーしたか確認してください", + "cookie.saved": "AtCoder Cookie を保存しました", + "cookie.saveSuccess": "✅ Cookie の保存に成功しました", + "cookie.cleared": "Cookie を削除しました", + "load.register": "{contest} に登録しています ...", + "register.already": "すでに登録済みです。再登録の必要はありません", + "register.failed": "登録に失敗しました", + "load.submitPage": "{contest} の提出ページ情報を取得しています...", + "submit.pageReady": "提出ページ情報を取得しました", + "submit.cfNeedBrowser": "このコンテストへの提出には Cloudflare 認証が必要で、拡張機能では自動化できません。ブラウザで提出ページを開いて認証を完了してから提出してください。", + "submit.loginRequired": "提出にはログインが必要です。AtCoder Cookie が有効か、期限切れでないか確認してください。提出ページで Cloudflare 認証が求められる場合は、ブラウザで開いて認証を完了してください。", + "submit.loginRequiredNoCookie": "提出にはログインが必要です。先に AtCoder Cookie を設定してから再試行してください。", + "err.submitPage": "提出ページの取得に失敗しました", + "submit.paramsIncomplete": "提出パラメータが不完全です", + "load.submitting": "コードを提出しています...", + "submit.successWaiting": "コードの提出に成功しました。判定結果を取得しています...", + "err.submit": "提出に失敗しました", + "load.subHistory": "{contest} の提出履歴を取得しています...", + "err.subHistory": "提出履歴の取得に失敗しました", + "load.subDetail": "提出 {id} の詳細を取得しています...", + "err.subDetail": "提出詳細の取得に失敗しました", + "load.standings": "{contest} の順位表を取得しています...", + "err.standings": "順位表の取得に失敗しました", + "load.homepage": "AtCoder トップページのコンテスト一覧を取得しています...", + "err.homepage": "コンテスト一覧の取得に失敗しました", + "cph.exportSuccess": "success send to cph", + "cph.exportFailed": "fail to send cph", + + "err.cf": "AtCoder が Cloudflare 認証を要求しました。拡張機能では回避できません。ブラウザで AtCoder に直接アクセスしてください。\nURL: {url}", + "err.proxy": + "ネットワークプロキシ接続に失敗し、AtCoder にアクセスできません。\n" + + "考えられる原因: WSL 2 では、プロキシアドレス 127.0.0.1 は Windows ホストではなく WSL 自身を指します。\n" + + "解決方法:\n" + + " 1. WSL で実行: export NO_PROXY=.atcoder.jp\n" + + " 2. または正しいホスト IP を設定: export HTTPS_PROXY=http://$(hostname).local:7897\n" + + " 3. または Windows ホストの WSL ゲートウェイ IP に接続(/etc/resolv.conf を参照)", + "err.login": + "アクセスにはログインが必要です。AtCoder Cookie を設定してください。\n" + + "取得方法:\n" + + " 1. ブラウザで https://atcoder.jp にログイン\n" + + " 2. F12 を押して開発者ツールを開く → Application → Cookies\n" + + " 3. atcoder.jp の REVEL_SESSION を見つけ、その Value をコピー\n" + + " 4. 拡張機能の設定で入力: REVEL_SESSION=コピーした値", + "err.http403": "アクセスが拒否されました (403)。Cookie が無効か期限切れの可能性があります。AtCoder に再ログインして新しい REVEL_SESSION を取得してください", + "err.http404NoCookie": "アクセスに失敗しました (404)。問題が存在しないか、ログインが必要です。先に AtCoder Cookie を設定してください。", + "err.http404BadCookie": "アクセスに失敗しました (404)。Cookie が無効か期限切れの可能性があります。AtCoder に再ログインして新しい REVEL_SESSION を取得してください", + "err.http404NotStarted": "アクセスに失敗しました (404)。コンテスト「{contest}」はまだ開始されておらず、問題は公開されていません。開始後にもう一度お試しください。", + "err.httpStatus": "リクエストがステータス {status} で失敗しました", + "err.network": "ネットワークエラー: {msg}", + + "deepl.tooFrequent": "翻訳リクエストが多すぎます。しばらくしてからもう一度お試しください", + "deepl.rateLimited": "DeepL 翻訳がレート制限されました(HTTP 429)。しばらくしてからもう一度お試しください", + "deepl.httpError": "翻訳 API エラー ({status})", + "deepl.badResponse": "翻訳 API が異常な応答を返しました", + "deepl.unexpectedStructure": "翻訳 API が異常な応答を返しました: レスポンス構造を認識できません", + "deepl.timeout": "翻訳リクエストがタイムアウトしました", + "deepl.failed": "翻訳リクエストに失敗しました: {msg}", + "deepl.failedSimple": "翻訳リクエストに失敗しました", + + "submit.pageFetchFailed": "提出ページを取得できませんでした。コンテストが終了しているか、提出ページの構造が変更された可能性があります。", + "submit.noCsrf": "CSRF Token を取得できませんでした。Cookie が有効か確認してください", + "submit.success": "コードの提出に成功しました", + "submit.failed": "提出に失敗しました。Cookie が有効か確認してください", + + "register.success": "登録に成功しました!", + "register.failedBadCookie": "登録に失敗しました。Cookie が有効か確認してください", + "register.formIncomplete": "登録に失敗しました: 登録ページが検証結果を返しました。必須項目(氏名、メールアドレス、居住地など)がすべて入力されているか確認してください", + "register.alreadyDone": "登録済み", + "register.closed": "登録は締め切られたか、登録情報を取得できません", + "register.requestFailed": "登録リクエストに失敗しました", + + "md.statement": "### 問題文", + "md.constraints": "### 制約", + "md.inputFormat": "### 入力形式", + "md.outputFormat": "### 出力形式", + "md.sample": "### サンプル {index}", + "md.inputLabel": "入力", + "md.outputLabel": "出力", + + "cph.notRunning": + "CPH 拡張機能が検出されませんでした(localhost:27121 が応答しません)。\n" + + "Competitive Programming Helper 拡張機能をインストールして有効化してから再試行してください。", + "cph.httpError": "CPH がステータスコード {status} を返しました", + "cph.connectionFailed": "CPH への接続に失敗しました: {msg}", +}; diff --git a/packages/webview/src/i18n/index.tsx b/packages/webview/src/i18n/index.tsx index 4bf46e2..4cf8cbb 100644 --- a/packages/webview/src/i18n/index.tsx +++ b/packages/webview/src/i18n/index.tsx @@ -1,16 +1,16 @@ import React, { createContext, useContext, useMemo, useState } from "react"; import { zh } from "./zh"; import { en } from "./en"; -import type { ZhDict } from "./zh"; +import { ja } from "./ja"; type Dict = Record; -const dicts: Record = { zh, en }; +const dicts: Record = { zh, en, ja }; const fallback: Dict = en; function detectLocale(): string { const lang = (window.__ATCODER_LOCALE__ || navigator.language || "en").toLowerCase(); - return lang.startsWith("zh") ? "zh" : "en"; + return lang.startsWith("zh") ? "zh" : lang.startsWith("ja") ? "ja" : "en"; } export interface I18nValue { diff --git a/packages/webview/src/i18n/ja.ts b/packages/webview/src/i18n/ja.ts new file mode 100644 index 0000000..0e6dbdb --- /dev/null +++ b/packages/webview/src/i18n/ja.ts @@ -0,0 +1,156 @@ +import type { ZhDict } from "./en"; + +export const ja: Record = { + "status.tasksLoaded": "{count} 問の問題を読み込みました", + "status.problemLoaded": "問題文を読み込みました: {title}", + "status.loading": "読み込み中...", + "err.operationFailed": "操作に失敗しました", + "err.detail": "提出詳細の取得に失敗しました", + "err.submitPageUnavailable": "提出ページにアクセスできません", + "status.translationDone": "翻訳が完了しました", + "status.registrationSuccess": "登録に成功しました", + "status.registrationFail": "登録に失敗しました", + "status.submitPageReady": "提出ページ情報を取得しました", + "status.submitSuccess": "コードの提出に成功しました", + "status.submitFailed": "提出に失敗しました", + "status.historyLoaded": "{count} 件の提出を読み込みました", + "status.loadingContest": "{contest} の問題を読み込んでいます...", + "status.fetchingProblem": "{contest}/{task} の問題文を取得しています...", + "status.registration": "{contest} に登録しています ...", + "status.copying": "コピーしています...", + "status.translating": "翻訳しています...", + "status.exportingCph": "CPH にエクスポートしています...", + "status.fetchingSubmitPage": "提出ページを取得しています...", + "status.fetchingHistory": "提出履歴を取得しています...", + "status.submitting": "コードを提出しています...", + "status.sentToCph": "CPH に送信しました", + "status.cphExportFailed": "CPH へのエクスポートに失敗しました", + "status.cfChallenge": "AtCoder が Cloudflare 認証を要求しました。拡張機能では直接アクセスできないため、ブラウザで使用してください", + "status.loginRequired": "AtCoder へのログインが必要です。ブラウザでログインし、Cookie を設定に貼り付けてください", + "status.standingsLoaded": "{count} 件の順位を読み込みました", + "status.fetchingStandings": "{contest} の順位表を取得しています...", + "status.homepageLoaded": "{count} 件のコンテストを読み込みました", + "status.fetchingHomepage": "AtCoder トップページのコンテスト一覧を取得しています...", + "status.detailLoaded": "提出 {id} を読み込みました", + "status.cfOpenBrowser": "AtCoder が Cloudflare 認証を要求しました。ブラウザで開いて確認してください", + "status.loginRequiredSidebar": "AtCoder アカウントへのログインが必要です。サイドバーで Cookie を設定してから再試行してください", + + "text.problemStatement": "問題文", + "text.constraints": "制約", + "text.inputFormat": "入力形式", + "text.outputFormat": "出力形式", + + "ui.settings": "設定", + "ui.save": "保存", + "ui.clear": "クリア", + "ui.close": "閉じる", + "ui.refresh": "更新", + "ui.refreshing": "更新中...", + "ui.view": "表示", + "ui.details": "詳細", + "ui.task": "問題", + "ui.language": "言語", + "ui.sourceCode": "ソースコード", + "ui.submit": "提出", + "ui.submitting": "提出中...", + "ui.fetching": "取得中...", + "ui.fetchSubmitPage": "提出ページを取得", + "ui.openInBrowser": "ブラウザで開く", + "ui.browserOpen": "ブラウザで開く", + "ui.fetchingData": "データを取得しています...", + "ui.cfTitle": "Cloudflare 認証", + "ui.cfBody1": "AtCoder が Cloudflare 認証を要求したため、拡張機能から AtCoder に直接アクセスできません。", + "ui.cfBody2": "ブラウザで AtCoder を直接開いて使用してください。", + "ui.cfContestBody": "AtCoder が Cloudflare 認証を要求したため、拡張機能から直接アクセスできません。下のボタンをクリックしてブラウザで開き、認証を完了してから Cookie を再設定してください。", + "ui.registerButton": "コンテストに登録", + "ui.registered": "登録済み", + "ui.setCookieFirst": "先に AtCoder Cookie を設定してください", + "ui.ratedRegistration": "レーティング登録", + "ui.announcement": "お知らせ", + "ui.contest": "コンテスト {contest}", + "ui.contestId": "コンテスト ID: {contest}", + "ui.isRated": "レーティングコンテスト: {rated}", + "ui.yes": "はい", + "ui.no": "いいえ", + "ui.noTasks": "{contest} の問題がありません", + "ui.noHistory": "提出履歴がありません", + "ui.loadingHistory": "提出履歴を取得しています...", + "ui.noStandings": "順位表データがありません", + "ui.noContests": "コンテストがありません。更新をクリックしてください", + "ui.fetchingContests": "コンテスト一覧を取得しています...", + "ui.contestsTitle": "コンテスト一覧", + "ui.submitHistoryTitle": "提出履歴", + "ui.clickContestForHistory": "コンテストをクリックして提出履歴を表示", + "ui.submitTo": "{contest} にコードを提出", + "ui.clickToStart": "「提出ページを取得」をクリックして開始します。", + "ui.submitSuccessTitle": "提出に成功しました", + "ui.submitFailedTitle": "提出に失敗しました", + "ui.viewSubmissionHistory": "提出履歴を表示", + "ui.taskList": "問題一覧", + "ui.statement": "問題文", + "ui.translate": "翻訳", + "ui.translating": "翻訳中...", + "ui.translationMode": "翻訳モード", + "ui.free": "無料", + "ui.api": "API", + "ui.copyMarkdown": "Markdown をコピー", + "ui.exportCph": "CPH にエクスポート", + "ui.exportCphTitle": "CPH にエクスポート(Competitive Programming Helper が必要)", + "ui.sampleLabel": "サンプル {index}", + "ui.copyInput": "Input をコピー", + "ui.copyOutput": "Output をコピー", + "ui.sampleExternal": "この問題には埋め込みサンプルがありません。サンプルは外部リンクにあります。", + "ui.viewSamples": "サンプルを表示", + "ui.noSamples": "この問題のサンプルが見つかりません。", + "ui.enterContestHint": "コンテスト ID(例: abc345)を入力し、「問題を読み込む」をクリックしてください。", + "ui.standingsTitle": "順位表 - {contest}", + "ui.tabInfo": "情報", + "ui.tabRating": "順位", + "ui.fetchingStandings": "順位表を取得しています...", + "ui.openSubmitPage": "ブラウザで提出ページを開く", + "ui.codePlaceholder": "ここにコードを貼り付けるか入力してください...", + "ui.contestPlaceholder": "コンテストコードを入力(例: abc345)", + "ui.loadTasks": "問題を読み込む", + "ui.submissionTitle": "提出 {id} - {contest}", + "ui.missingContestOrId": "コンテスト ID または提出 ID がありません", + "ui.fetchingDetail": "提出詳細を取得しています...", + "ui.noDetail": "提出詳細がありません", + "ui.submitTime": "提出時刻", + "ui.score": "得点", + "ui.codeLength": "コード長", + "ui.execTime": "実行時間", + "ui.memory": "メモリ", + "ui.testPoints": "テストケース", + "ui.collapse": "折りたたむ", + "ui.expand": "展開", + "ui.copied": "コピーしました", + "ui.copy": "コピー", + + "cat.active": "開催中", + "cat.upcoming": "開催予定", + "cat.recent": "最近", + "cat.daily": "デイリー", + + "cookie.title": "AtCoder ログイン Cookie", + "cookie.onlyNeedRevel": "AtCoder はログインに {code} という 1 つの Cookie のみ必要です。", + "cookie.steps": "取得手順:", + "cookie.step1": "ブラウザで {link} を開いてログイン", + "cookie.step2": "{key} を押して開発者ツールを開く", + "cookie.step3": "{application}(Chrome)または {storage}(Edge)タブに切り替える", + "cookie.step4": "左側で {cookies} → {site} を見つける", + "cookie.step5": "{code} という名前の行を見つけ、{value} 列をダブルクリックして全選択してコピー", + "cookie.step6": "下の入力欄に貼り付ける(接頭辞を手動で追加する必要はありません)", + "cookie.chromeApp": "Application", + "cookie.edgeStorage": "ストレージ", + "cookie.placeholderHas": "Cookie は保存済みです。上書きするには新しい値を入力してください", + "cookie.placeholderEmpty": "REVEL_SESSION の値を貼り付ける", + "cookie.pasteFirst": "先に REVEL_SESSION の値をコピーしてください", + "cookie.saved": "Cookie を保存しました", + "cookie.cleared": "Cookie を削除しました", + "cookie.updated": "Cookie を更新しました。更新しています...", + "cookie.clearLabel": "Cookie を削除", + "cookie.loginTitle": "AtCoder アカウントへのログインが必要です", + "cookie.loginDesc": "提出履歴や順位表にはログインが必要です。ブラウザで AtCoder にログインし、F12 → Application → Cookies で {code} の Value をコピーして下に貼り付けてください。", + "cookie.loginRequired": "この機能を使うには AtCoder アカウントへのログインが必要です", + "cookie.loggedIn": "AtCoder にログイン済み", +};