Files
pangolin/client/lib/l10n/strings_ja.dart
T
wangjia 6f232d4043
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 25s
ci-pangolin / Cleartext Scan — Android 禁明文 (push) Successful in 19s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 18s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (push) Failing after 13m6s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 13m16s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Failing after 13m25s
ci-pangolin / Go — build + test (push) Failing after 13m35s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (push) Failing after 13m45s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Failing after 13m54s
ci-pangolin / Flutter — analyze + test (push) Failing after 14m5s
ci-pangolin / OpenAPI Sync Check (push) Failing after 14m16s
ci-pangolin / Lint — shellcheck (push) Failing after 14m27s
fix(routing): 堵住 ip_cidr direct 旁路系统层 + 客户端保存失败可见提示
分支审核发现两处 Important,合并前修复。

① [安全] direct 的 ip_cidr 用户规则可自伤式旁路整条隧道:
   Validate 原先只校 CIDR 语法。用户提交 ip_cidr=0.0.0.0/0 action=direct
   (或 172.16.0.0/12,含隧道 DNS 172.19.0.2)会并入 TUN 入站
   route_exclude_address(OS/auto_route 层,位于系统强制层之下),被排除的
   流量根本不进 sing-box → hijack-dns 与整条隧道被静默旁路,违反「系统层
   用户不可越」铁律。
   - Validate: direct 的 ip_cidr 拒绝 catch-all(/0)及与保留段 172.16.0.0/12
     重叠(写入闸)。
   - clientconfig 渲染层:新增 routing.SafeToExclude 守卫,只有安全的 direct
     ip_cidr 才并入 route_exclude_address(纵深防护,兜底写入闸之前的历史坏行)。
   - 测试 TestValidateDirectIPCIDRReservedGuard 钉死:拒 catch-all/隧道段重叠、
     放行 proxy catch-all 与不重叠 direct。

② [健壮性] 客户端保存失败静默回滚 + 抛未捕获异步异常 + 对话框无字段校验:
   _persist 失败会 rethrow(约定调用方 catch),但屏幕层所有回调
   (setMode/setBuiltin/addRule/removeRule/reorder/resetToDefault)均未 catch,
   规则闪现即消失、无提示,且 rethrow 变 zone 未处理异常。
   - 新增 _guardSave 守卫:await + 失败弹 SnackBar(AuthApiException 显服务端
     双语文案含校验错,其余回退通用「保存失败」),包裹全部变更类回调。
   - 添加规则对话框:_valueError 字段级预校验(ip_cidr 用 InternetAddress
     校验、geo 白名单仅 cn),非法即禁用保存并内联红字提示;语义级(保留段)
     仍由服务端权威判定经 SnackBar 呈现。
   - l10n 单源新增 routingSaveFailed / routingRuleValueInvalid(6 语),regen。

go test ./... 全绿;flutter analyze 无 error;flutter test 265 全过无 golden 回归;
codegen 幂等、原型 i18n 无漂移。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A79VtQA1BwTuQN1ThpvYpo
2026-08-01 00:32:52 +08:00

608 lines
22 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// strings_ja.dart — 日本語文案(自动生成 · 勿手改)
//
// GENERATED by design/codegen/gen_l10n_dart.mjs from design/i18n/strings.json.
// 改文案:改 strings.json 后重跑 codegen;新增 key 需同步 app_text.dart 抽象声明。
import 'app_text.dart';
class StringsJa extends AppText {
const StringsJa();
@override
AppLang get lang => AppLang.ja;
@override
String get brand => 'Pangolin';
@override
String get online => '● オンライン';
@override
String get offline => '○ オフライン';
@override
String get capOff => 'タップして接続';
@override
String get capConnecting => '接続中…';
@override
String get capOn => '接続済み · 暗号化';
@override
String get connectNow => '接続';
@override
String get secure => 'セキュア';
@override
String get tabConnect => '接続';
@override
String get tabServers => 'サーバー';
@override
String get tabStats => '統計';
@override
String get tabMe => 'アカウント';
@override
String get currentNode => '現在のノード';
@override
String get download => '下り';
@override
String get upload => '上り';
@override
String get latency => 'Ping';
@override
String get quotaToday => '本日の残り';
@override
String get minutes => '';
@override
String get quotaFree => '無料 · 1日10分';
@override
String get planFreeTag => '無料';
@override
String get watchAd => '広告を見て開始';
@override
String get adUnlocked => '本日は利用可能';
@override
String get quotaLeftLabel => '残り';
@override
String get quotaUsedUp => '本日の無料時間を使い切りました';
@override
String get watchAdMore => '広告を見て時間を追加';
@override
String get quotaExhaustedNotice => '本日の無料時間を使い切りました。アップグレードすると続けて接続できます。';
@override
String get adPlaying => '広告を再生中…';
@override
String get adFailed => '時間の追加に失敗しました。もう一度お試しください';
@override
String get quotaDesktopTitle => '本日の無料時間を使い切りました';
@override
String get quotaDesktopBody => 'モバイルアプリで広告を見ると時間を追加できます。無制限に使うにはアップグレードしてください。';
@override
String get gotIt => '了解';
@override
String get chooseNode => 'サーバーを選択';
@override
String get searchPh => '国・都市を検索';
@override
String get smartSelect => 'スマート選択';
@override
String get nodeSwitchBody => '現在の接続を切断し、このノードに再接続します。';
@override
String get nodeSwitchConfirm => '切り替える';
@override
String get nodeUnhealthySwitched => '現在のノードに接続できないため、自動的に切り替えました';
@override
String get nodeUnhealthyError => '現在のノードに接続できません。再接続するかノードを切り替えてください';
@override
String get nodeReconnecting => 'ネットワークが不安定です。再接続中…';
@override
String get routingRulesReconnecting => 'ルールが更新されました。再接続中…';
@override
String get smartSub => 'お使いのネットワークに最適なノードを選びます';
@override
String get recommended => 'おすすめ';
@override
String get statsTitle => '統計';
@override
String get trafficMonth => '通信量';
@override
String get avgPing => '平均Ping';
@override
String get durMonth => '接続時間';
@override
String get weekTraffic => '今週 (GB)';
@override
String get byDevice => 'デバイス別';
@override
String get noDeviceUsage => 'デバイスの利用履歴はまだありません';
@override
String get periodMonth => '今月';
@override
String get periodWeek => '今週';
@override
String get periodToday => '今日';
@override
String get statDown => 'ダウンロード';
@override
String get statUp => 'アップロード';
@override
String get statDuration => '接続時間';
@override
String get chartTwoWeeks => '過去2週間';
@override
String get allDevices => 'すべてのデバイス';
@override
String get meTitle => 'アカウント';
@override
String get proMember => 'PRO会員';
@override
String get freePlanName => '無料';
@override
String get expires => '有効期限';
@override
String get upgradeBtn => '更新 / アップグレード';
@override
String get accInfoTitle => 'アカウント情報';
@override
String get accEmail => 'メールアドレス';
@override
String get accPassword => 'パスワード';
@override
String get accChange => '変更';
@override
String get deviceLimitTitle => 'デバイス数の上限に達しました';
@override
String get deviceLimitDesc => '現在のプランでは最大 %s 台まで利用できます。続けるには1台を削除してください。';
@override
String get deviceLimitRemoveOldest => '最後に使用したのが最も古いデバイスを削除';
@override
String get myDevices => 'マイデバイス';
@override
String get devicesSub => 'PROでは最大3台まで';
@override
String get thisDevice => 'このデバイス';
@override
String get remove => '削除';
@override
String get devOnline => 'オンライン';
@override
String get devOffline => 'オフライン';
@override
String get devLastOnline => '最終オンライン';
@override
String get devNeverLogin => 'ログイン履歴なし';
@override
String get devForceLogout => '強制ログアウト';
@override
String get devClearLogin => 'ログイン情報を消去';
@override
String get devRename => '名前を変更';
@override
String get devRenameHint => 'デバイス名';
@override
String get devSave => '保存';
@override
String get devForceLogoutConfirm => 'このデバイスのログインセッションを無効にします。オフラインになり、再度サインインが必要になります。デバイスはリストに残ります。';
@override
String get devClearLoginConfirm => 'このデバイスをリストから完全に削除し、ログインセッションとデータ通信の認証情報を無効にします。再び利用するには、もう一度サインインが必要です。';
@override
String get devCancel => 'キャンセル';
@override
String get sessionRevokedTitle => 'このデバイスはサインアウトされました';
@override
String get sessionRevokedBody => '別のデバイスから、このデバイスがサインアウトされました。もう一度サインインしてください。';
@override
String get sessionRevokedOk => '再度サインイン';
@override
String get redeemEntry => 'コード引き換え';
@override
String get contactEntry => 'お問い合わせ';
@override
String get signOut => 'サインアウト';
@override
String get language => '言語';
@override
String get darkAppearance => 'ダークモード';
@override
String get stateOn => 'オン';
@override
String get followLight => 'オフ';
@override
String get protocol => 'プロトコル';
@override
String get settingsTitle => '設定';
@override
String get autostart => 'スタートアップ時に起動';
@override
String get autostartSub => 'システム起動時に自動的に実行';
@override
String get autoConnect => '自動接続';
@override
String get autoConnectSub => '起動時に自動的に接続';
@override
String get smartRoute => 'スマートルーティング';
@override
String get smartRouteSub => '海外は高速化、国内は直接接続';
@override
String get killSwitch => 'キルスイッチ';
@override
String get killSwitchSub => '接続が切れたら通信を遮断';
@override
String get checkUpdate => 'アップデートを確認';
@override
String get webUserCenter => 'ユーザーセンター (Web)';
@override
String get updateNotesFallback => 'このアップデートには不具合の修正と安定性の向上が含まれています。';
@override
String get updateLater => '後で';
@override
String get updateDownload => 'アップデートをダウンロード';
@override
String get updateUpToDate => '最新の状態です';
@override
String get updateCheckFailed => 'アップデートの確認に失敗しました。後でもう一度お試しください';
@override
String get freePlan => '無料';
@override
String get proPlan => 'Pro';
@override
String get teamPlan => 'Team';
@override
String get perMonth => '/月';
@override
String get current => '現在';
@override
String get upgrade => 'アップグレード';
@override
String get choose => '選択';
@override
String get mostPopular => '人気';
@override
String get redeemTitle => 'コード引き換え・購入';
@override
String get redeemCodeTitle => 'コードを引き換える';
@override
String get redeemPh => 'アクティベーションコードを入力';
@override
String get redeemBtn => '引き換える';
@override
String get redeemOk => '認証完了 · PROが利用可能に';
@override
String get buyTitle => '購入方法';
@override
String get buySub => 'アプリ内決済はご利用いただけません。以下からコードを入手してください:';
@override
String get chStore => 'セルフサービスストア';
@override
String get chEmail => 'メールサポート';
@override
String get contactTitle => 'お問い合わせ';
@override
String get contactIntro => 'お困りですか?以下のいずれかの方法でお問い合わせください。通常は数分以内に返信します。';
@override
String get contactEmail => 'メールサポート';
@override
String get contactStore => 'セルフサービスストア';
@override
String get contactWebsite => '公式サイト';
@override
String get contactHoursTitle => 'サポート時間';
@override
String get contactHours => '毎日 9:00 24:00 (GMT+8)';
@override
String get authTagline => '高速 · 安定 · かんたん';
@override
String get tabLogin => 'ログイン';
@override
String get tabRegister => '新規登録';
@override
String get emailLabel => 'メールアドレス';
@override
String get emailPh => 'your@email.com';
@override
String get pwLabel => 'パスワード';
@override
String get pwPh => 'パスワードを入力';
@override
String get setPwPh => 'パスワードを設定(複数デバイスでのログイン用)';
@override
String get codeLabel => '確認コード';
@override
String get sendCode => 'コードを送信';
@override
String get resend => '再送信';
@override
String get doLogin => 'ログイン';
@override
String get doNext => '次へ';
@override
String get doCreate => 'アカウントを作成';
@override
String get forgotPw => 'パスワードをお忘れですか?';
@override
String get tos => '続行すると、利用規約とプライバシーポリシーに同意したことになります';
@override
String get obSkip => 'スキップ';
@override
String get obNext => '次へ';
@override
String get obAllow => '許可して続行';
@override
String get obStart => '始める';
@override
String get ob1Title => '世界中に接続';
@override
String get ob1Sub => '80以上の拠点、最速ルートを自動選択、抜群の安定性。';
@override
String get ob2Title => 'ネットワーク設定を許可';
@override
String get ob2Sub => '暗号化トンネルを構築するため、システムがネットワークプロファイルの追加を求めます。';
@override
String get ob3Title => '設計段階からプライバシー重視';
@override
String get ob3Sub => 'エンドツーエンドで暗号化。閲覧ログは一切保持しません。';
@override
String get purchaseTitle => 'プランを購入';
@override
String get paymentTitle => 'お支払い';
@override
String get buyNow => '今すぐ購入';
@override
String get payMethodAlipay => 'Alipay';
@override
String get payMethodCrypto => 'USDT (TRC20)';
@override
String get choosePayMethod => 'お支払い方法を選択';
@override
String get proMonthly => 'Pro・月額';
@override
String get proQuarterly => 'Pro · 3か月';
@override
String get proYearly => 'Pro · 年額';
@override
String get perQuarter => '/3か月';
@override
String get perYear => '/年';
@override
String get payAmountLabel => '送金額';
@override
String get payAddressLabel => '送金先アドレス';
@override
String get payNetworkLabel => 'ネットワーク';
@override
String get payExactAmountHint => '正確な金額をお送りください。着金後、自動で有効になります';
@override
String get copied => 'コピーしました';
@override
String get openAlipay => 'Alipayで支払う';
@override
String get openAlipayHint => 'お支払い後に戻ると、このページは自動的に更新されます';
@override
String get openAlipayFailed => 'Alipayを開けませんでした。インストール済みか確認するか、後でもう一度お試しください';
@override
String get awaitingPayment => 'お支払い待ち';
@override
String get paySucceeded => '有効になりました';
@override
String get payExpiresAt => '有効期限';
@override
String get payDone => '完了';
@override
String get payFailed => 'お支払いに失敗しました';
@override
String get payRetry => '再試行';
@override
String get switchPayMethod => '別のお支払い方法に変更';
@override
String get cancelOrder => '注文をキャンセル';
@override
String get qrNotSupported => '内容をコピーしてAlipayで開いてください';
@override
String get orderCreateFailed => '注文の作成に失敗しました。しばらくして再試行してください';
@override
String get payUnsupportedRenderType => '未対応です。支払い方法を変更してください';
@override
String get payNoActiveOrder => '進行中の注文はありません';
@override
String get payChannel => '支払いチャネル';
@override
String get payChannelUsdt => 'USDT · 暗号資産';
@override
String get payChannelCny => '人民元 · Alipay';
@override
String get perMonthSuffix => ' /月';
@override
String get ordersTitle => '注文履歴';
@override
String get orderDetailTitle => '注文の詳細';
@override
String get ordersEmpty => '注文はありません';
@override
String get orderNoLabel => '注文番号';
@override
String get orderPlanLabel => 'プラン';
@override
String get orderMethodLabel => '支払い方法';
@override
String get orderAmountLabel => '金額';
@override
String get orderCreatedLabel => '作成日時';
@override
String get orderPaidLabel => '支払日時';
@override
String get orderStatusPaid => '有効化済み';
@override
String get orderStatusCanceled => 'キャンセル済み';
@override
String get orderContinuePay => '支払いを続ける';
@override
String get orderRefresh => '更新';
@override
String get orderRebuy => '再購入';
@override
String get payMethodNezha => 'Nazhapay';
@override
String get inviteEntry => '友達を招待してメンバーシップを獲得';
@override
String get inviteEntrySub => '友達が登録 · 双方に会員日数を付与';
@override
String get inviteTitle => '友達を招待';
@override
String get inviteCodeLabel => '招待コード';
@override
String get inviteLinkLabel => '招待リンク';
@override
String get inviteCopyCode => 'コードをコピー';
@override
String get inviteCopyLink => 'リンクをコピー';
@override
String get inviteRewardsTitle => '特典ルール';
@override
String get inviteRewardRegister => '友達が登録 · 双方に3日間付与';
@override
String get inviteRewardFirstBuy => '友達が初回購入 · 双方に7日間付与';
@override
String get inviteRewardJoinTg => 'TGチャンネルに参加 · 3日間付与';
@override
String get inviteProgressTitle => '獲得した特典';
@override
String get inviteTasksTitle => 'もっと会員特典を得る';
@override
String get inviteJoinTgTitle => 'Telegramチャンネルに参加';
@override
String get inviteJoinTgBtn => 'チャンネルに参加';
@override
String get inviteVerifyBtn => '確認して受け取る';
@override
String get inviteTaskDone => '受け取り済み +3日';
@override
String get inviteStatInvited => '招待した人数';
@override
String get inviteStatConverted => '転換した人数';
@override
String get inviteStatDays => '累計獲得日数';
@override
String get inviteCodeFieldLabel => '招待コード(任意)';
@override
String get notifTitle => '通知';
@override
String get notifEmpty => '通知はありません';
@override
String get notifTypeNews => 'ニュース';
@override
String get notifTypeFeature => '新機能';
@override
String get notifTypeImportant => '重要';
@override
String get notifTypeReward => '入金';
@override
String get notifTypeVersion => 'アップデート';
@override
String get notifTypePromo => 'キャンペーン';
@override
String get settingsGroupConn => '接続';
@override
String get settingsGroupAppearance => '外観';
@override
String get settingsGroupAbout => 'このアプリについて';
@override
String get orderStatusExpired => '期限切れ';
@override
String get promoBadge => '期間限定オファー';
@override
String get promoOriginalPrice => '通常価格';
@override
String get routingRulesTitle => 'ルーティングルール';
@override
String get routingRulesSub => '直接接続 / トンネル / 拒否をカスタム';
@override
String get routingLanDirect => 'LAN・プライベート直接接続';
@override
String get routingLanForced => '常時オン';
@override
String get routingForcedPill => '🔒 強制';
@override
String get routingProxyMode => 'プロキシモード';
@override
String get routingModeGlobal => 'グローバル';
@override
String get routingModeSmart => 'スマート';
@override
String get routingModeDirect => '直接';
@override
String get routingBuiltin => '組み込みルール';
@override
String get routingCnDirect => '国内は直接接続';
@override
String get routingMyRulesOrdered => 'マイルール · 順序あり、最初の一致が有効';
@override
String get routingMyRulesFirst => 'マイルール · 最初の一致が有効';
@override
String get routingFinalLong => '最終(どのルールにも一致しない)';
@override
String get routingFinalShort => '最終(一致なし)';
@override
String get routingFinalFallback => 'FINAL フォールバック';
@override
String get routingTypeDomainSuffix => 'ドメインサフィックス';
@override
String get routingTypeDomainSuffixCi => 'ドメインサフィックス · CI ソース';
@override
String get routingTypeDomainSuffixMirror => 'ドメインサフィックス · ミラー';
@override
String get routingTypeGeosite => 'GeoSite ルールセット';
@override
String get routingTypeDomain => 'ドメイン';
@override
String get routingTypeDomainKeyword => 'ドメインキーワード';
@override
String get routingTypeIpCidr => 'IP範囲(CIDR)';
@override
String get routingTypeGeoip => 'GeoIP ルールセット';
@override
String get routingActDirect => '直接接続';
@override
String get routingActProxy => 'トンネル';
@override
String get routingActReject => '拒否';
@override
String get routingForcedRow => '⚠ このドメインはシステムにより強制的にトンネル経由となり、このルールは無効です';
@override
String get routingRuleShadowed => '上のルールに上書きされました';
@override
String get routingModeNote => '「マイルール」はスマート分流モードでのみ有効です。全体プロキシ / 全体直接接続では独自ルールは無視されます(システム強制ルールは引き続き適用)。';
@override
String get routingRuleValueHint => '例:example.com または 192.168.1.0/24';
@override
String get routingNoRules => 'カスタムルールはまだありません';
@override
String get routingAddRule => 'ルールを追加';
@override
String get routingSaveFailed => '保存に失敗しました。もう一度お試しください';
@override
String get routingRuleValueInvalid => '形式が正しくありません';
@override
String get routingImport => 'テキストから読み込み';
@override
String get routingReset => 'デフォルトに戻す';
@override
String get routingResetConfirmTitle => 'デフォルトのルールに戻しますか?';
@override
String get routingResetConfirmBody => 'カスタムルールをすべて削除し、出荷時の分流テーブルに戻します。';
@override
String get routingResetConfirmAction => 'リセット';
@override
String get routingBack => '設定に戻る';
@override
List<String> get days7 => const ['', '', '', '', '', '', ''];
@override
List<String> get featsFree => const ['基本ノード1つのみ', '1日10分', '広告を見て開始', '登録で7日間無料トライアル'];
@override
List<String> get featsPro => const ['80以上の拠点', '無制限 · 高速', '5台のデバイス', 'ストリーミング最適化'];
@override
List<String> get featsTeam => const ['Proのすべての機能', '10ライセンス', '一括請求', '優先サポート'];
@override
String adRewarded(int minutes) => '+$minutes 分を追加しました';
@override
String nodeSwitchTitle(String name) => '$name に切り替えますか?';
@override
String autoConnectingTo(String name) => '$name に自動接続中…';
@override
String updateAvailableTitle(String version) => '新しいバージョン v$version が利用可能です';
@override
String codeSentTo(String email) => '$email に確認コードを送信しました';
}