import { roomMessages, type RoomMessages } from './i18n/rooms';

export const LOCALES = ['ko', 'en', 'zh', 'ja'] as const;

export type Locale = (typeof LOCALES)[number];

export interface Messages {
  app: {
    loading: string;
  };
  actions: {
    close: string;
    refresh: string;
    refreshing: string;
    retry: string;
  };
  nav: {
    aria: string;
    drawerAria: string;
    drawerNavAria: string;
    menuOpen: string;
    menuClose: string;
    operations: string;
    updated: string;
    health: string;
    inbox: string;
    usage: string;
    rooms: string;
    scheduled: string;
    settings: string;
  };
  language: {
    label: string;
  };
  settings: {
    title: string;
    nicknameLabel: string;
    nicknamePlaceholder: string;
    nicknameHelp: string;
    languageLabel: string;
    navAria: string;
    contentAria: string;
    sidebarAria: string;
    nav: {
      general: { title: string; detail: string };
      models: { title: string; detail: string };
      runtime: { title: string; detail: string };
      moa: { title: string; detail: string };
      codex: { title: string; detail: string };
      accounts: { title: string; detail: string };
    };
    apply: {
      aria: string;
      kicker: string;
      title: string;
      hint: string;
      restart: string;
    };
    sections: {
      general: { kicker: string; title: string; description: string };
      models: { kicker: string; title: string; description: string };
      runtime: { kicker: string; title: string; description: string };
      moa: { kicker: string; title: string; description: string };
      codex: { kicker: string; title: string; description: string };
      accounts: { kicker: string; title: string; description: string };
    };
    common: {
      loading: string;
      none: string;
      saving: string;
      saved: string;
      savedRestartHint: string;
      save: string;
      delete: string;
      refresh: string;
      refreshing: string;
      refreshAll: string;
      switch: string;
      switching: string;
      default: string;
      inUse: string;
      add: string;
    };
    models: {
      roleOwner: string;
      roleReviewer: string;
      roleArbiter: string;
      roleOwnerHint: string;
      roleReviewerHint: string;
      roleArbiterHint: string;
      modelLabel: string;
      effortLabel: string;
      modelDefault: string;
      modelCustom: string;
      modelCustomLabel: string;
      modelPlaceholder: string;
      groupCodex: string;
      groupClaude: string;
      groupCustom: string;
      effortDefault: string;
      effortOptions: {
        low: string;
        medium: string;
        high: string;
        xhigh: string;
        max: string;
      };
      agentTypeLabel: string;
      agentTypeCodex: string;
      agentTypeClaude: string;
      effortInvalid: string;
      effortSaveBlocked: string;
      save: string;
      empty: string;
    };
    codex: {
      fastMode: string;
      features: string;
      codexFast: string;
      codexFastHint: string;
      claudeFast: string;
      claudeFastHint: string;
      fastModeApplyHint: string;
      goal: string;
      goalHint: string;
      goalApplyHint: string;
    };
    accounts: {
      claude: string;
      codex: string;
      noAccounts: string;
      autoRefresh: string;
      codexRefreshHint: string;
      tokenPlaceholder: string;
      deleteConfirm: string;
      refreshFailed: string;
      paymentExpired: string;
      paymentUntil: string;
      paymentUntilDays: string;
      primaryAccount: string;
      activeAccount: string;
      addTokenLabel: string;
      refreshTitle: string;
      switchTitle: string;
    };
    moa: {
      master: string;
      masterHint: string;
      save: string;
      empty: string;
      test: string;
      testing: string;
      testAfterSave: string;
      notTested: string;
      statusOk: string;
      statusFail: string;
      apiKeyPlaceholder: string;
      apiKeySet: string;
      modelLabel: string;
      baseUrlLabel: string;
      formatLabel: string;
    };
    runtime: {
      defaultHint: string;
      selectRoomLabel: string;
      selectAgentLabel: string;
      scopeCodexUser: string;
      scopeClaudeUser: string;
      scopeRunner: string;
      emptyRooms: string;
      emptySkills: string;
      agentCodex: string;
      agentClaude: string;
    };
  };
  error: {
    api: string;
    network: string;
    timeout: string;
    server: string;
    notFound: string;
    auth: string;
    unknown: string;
  };
  control: {
    aria: string;
    queue: string;
    activeRooms: string;
    pendingRooms: string;
  };
  pwa: {
    app: string;
    install: string;
    installed: string;
    ready: string;
    cached: string;
    online: string;
    offline: string;
    fresh: string;
    stale: string;
    secureRequired: string;
    updated: string;
  };
  metrics: {
    agents: string;
    rooms: string;
    ciWatchers: string;
  };
  panels: {
    health: string;
    heartbeat: string;
    healthSignals: string;
    inbox: string;
    inboxQueue: string;
    usage: string;
    usageWindow: string;
    rooms: string;
    queue: string;
    scheduled: string;
  };
  service: {
    empty: string;
    heartbeat: string;
    service: string;
    rooms: string;
    updated: string;
  };
  health: {
    system: string;
    signals: string;
    services: string;
    fresh: string;
    stale: string;
    queue: string;
    ciFailures: string;
    affectedServices: string;
    restart: string;
    restartStack: string;
    restarting: string;
    restartHint: string;
    confirmRestart: string;
    restartLog: string;
    restartTarget: string;
    restartStatus: string;
    restartRequested: string;
    restartServices: string;
    levels: {
      ok: string;
      stale: string;
      down: string;
    };
  };
  inbox: {
    empty: string;
    cardsAria: string;
    summary: string;
    total: string;
    filters: string;
    all: string;
    noSummary: string;
    occurred: string;
    source: string;
    target: string;
    openTask: string;
    openRoom: string;
    kinds: {
      'pending-room': string;
      'reviewer-request': string;
      approval: string;
      'arbiter-request': string;
      'ci-failure': string;
      mention: string;
    };
    severity: {
      info: string;
      warn: string;
      error: string;
    };
    actions: {
      run: string;
      busy: string;
      runReview: string;
      finalize: string;
      runArbiter: string;
      decline: string;
      dismiss: string;
      confirmDecline: string;
    };
  };
  rooms: RoomMessages;
  usage: {
    empty: string;
    current: string;
    tightest: string;
    watch: string;
    remaining: string;
    speed: string;
    inUse: string;
    reset: string;
    noReset: string;
    limitBasis: {
      h5: string;
      d7: string;
    };
    quota: {
      h5: string;
      d7: string;
    };
    usage: string;
    groupPrimary: string;
    groupCodex: string;
    risk: {
      ok: string;
      warn: string;
      critical: string;
    };
    speedLabel: {
      ok: string;
      warn: string;
      critical: string;
    };
  };
  tasks: {
    empty: string;
    cardsAria: string;
    groups: {
      watchers: string;
      scheduled: string;
      paused: string;
      completed: string;
    };
    count: string;
    groupEmpty: string;
    task: string;
    status: string;
    schedule: string;
    next: string;
    last: string;
    context: string;
    ciWatch: string;
    emptyPrompt: string;
    prompt: string;
    until: string;
    suspendedUntil: string;
    lastResult: string;
    result: string;
    resultOk: string;
    resultFail: string;
    noResult: string;
    noTime: string;
    now: string;
    createTitle: string;
    createSubtitle: string;
    filterAll: string;
    details: string;
    room: string;
    selectRoom: string;
    scheduleType: string;
    scheduleValue: string;
    scheduleValueHint: string;
    promptPlaceholder: string;
    editPromptPlaceholder: string;
    scheduleTypes: {
      once: string;
      interval: string;
      cron: string;
    };
    contextModes: {
      isolated: string;
      group: string;
    };
    actions: {
      create: string;
      edit: string;
      save: string;
      pause: string;
      resume: string;
      cancel: string;
      busy: string;
      confirmCancel: string;
    };
  };
  status: {
    processing: string;
    waiting: string;
    inactive: string;
    active: string;
    paused: string;
    completed: string;
  };
  units: {
    second: string;
    minute: string;
    hour: string;
    task: string;
    messageShort: string;
    chars: string;
  };
}

export const languageNames: Record<Locale, string> = {
  ko: '한국어',
  en: 'English',
  zh: '简体中文',
  ja: '日本語',
};

export const localeTags: Record<Locale, string> = {
  ko: 'ko-KR',
  en: 'en-US',
  zh: 'zh-CN',
  ja: 'ja-JP',
};

export const messages = {
  ko: {
    app: {
      loading: '대시보드 로딩 중',
    },
    actions: {
      close: '닫기',
      refresh: '새로고침',
      refreshing: '새로고침 중',
      retry: '다시 시도',
    },
    nav: {
      aria: '대시보드 섹션',
      drawerAria: '대시보드 메뉴',
      drawerNavAria: '대시보드 메뉴 섹션',
      menuOpen: '메뉴 열기',
      menuClose: '메뉴 닫기',
      operations: '운영',
      updated: '갱신',
      health: '상태',
      inbox: 'Inbox',
      usage: '사용량',
      rooms: '룸',
      scheduled: '예약',
      settings: '설정',
    },
    language: {
      label: '언어',
    },
    settings: {
      title: '설정',
      nicknameLabel: '닉네임',
      nicknamePlaceholder: 'Web Dashboard',
      nicknameHelp: '룸에서 메시지 보낼 때 표시될 이름',
      languageLabel: '언어',
      navAria: '설정 섹션',
      contentAria: '설정 항목',
      sidebarAria: '설정 탐색과 적용',
      nav: {
        general: { title: '일반', detail: '표시 · 언어' },
        models: { title: '모델', detail: 'owner · reviewer · arbiter' },
        runtime: { title: '스킬', detail: '방별 ON · OFF' },
        moa: { title: 'MoA', detail: '참조 모델 · 연결 테스트' },
        codex: { title: 'Codex', detail: 'fast mode · /goal' },
        accounts: { title: '계정', detail: 'Claude · Codex' },
      },
      apply: {
        aria: '변경 적용',
        kicker: 'Apply',
        title: '저장 후 재시작',
        hint: '모델, MoA, Codex, 계정 변경은 스택 재시작 후 반영됩니다.',
        restart: '스택 재시작',
      },
      sections: {
        general: {
          kicker: 'Dashboard identity',
          title: '일반',
          description: '브라우저에서 보이는 이름과 언어만 즉시 바뀝니다.',
        },
        models: {
          kicker: 'Agent routing',
          title: '모델',
          description:
            '역할별 모델과 추론 강도를 선택합니다. 저장 후 스택 재시작이 필요합니다.',
        },
        runtime: {
          kicker: 'Agent skills',
          title: '스킬',
          description:
            '방마다 에이전트가 쓸 스킬을 켜거나 끕니다. 전역 일괄 설정은 없고, 기본값은 모두 켜짐입니다.',
        },
        moa: {
          kicker: 'Arbiter references',
          title: 'MoA 참조 모델',
          description:
            'Kimi, GLM 같은 외부 참조 모델을 켜고 연결 상태를 바로 확인합니다.',
        },
        codex: {
          kicker: 'Codex runtime',
          title: 'Codex 옵션',
          description:
            '빠른 응답과 실험 기능을 관리합니다. /goal은 여기에서 찾을 수 있습니다.',
        },
        accounts: {
          kicker: 'Credentials',
          title: '계정',
          description: 'Claude OAuth와 Codex 계정 상태를 확인하고 전환합니다.',
        },
      },
      common: {
        loading: '불러오는 중…',
        none: '없음',
        saving: '저장 중…',
        saved: '저장됨',
        savedRestartHint:
          '저장됨. 적용하려면 사이드바의 스택 재시작을 눌러 주세요.',
        save: '저장',
        delete: '삭제',
        refresh: '갱신',
        refreshing: '갱신중…',
        refreshAll: '전체 갱신',
        switch: '전환',
        switching: '전환중…',
        default: '기본',
        inUse: '사용중',
        add: '추가',
      },
      models: {
        roleOwner: 'Owner',
        roleReviewer: 'Reviewer',
        roleArbiter: 'Arbiter',
        roleOwnerHint: '작업을 직접 수행하는 에이전트',
        roleReviewerHint: '변경 사항을 검토하는 에이전트',
        roleArbiterHint: '최종 판단을 내리는 에이전트',
        modelLabel: '모델',
        effortLabel: '추론 강도',
        modelDefault: '기본값 (.env 상속)',
        modelCustom: '직접 입력…',
        modelCustomLabel: '모델 ID',
        modelPlaceholder: '예: gpt-5.5, claude-opus-4-8',
        groupCodex: 'Codex',
        groupClaude: 'Claude',
        groupCustom: '사용자 지정',
        effortDefault: '기본값 (.env 상속)',
        effortOptions: {
          low: '낮음',
          medium: '보통',
          high: '높음',
          xhigh: '매우 높음',
          max: '최대',
        },
        agentTypeLabel: '에이전트',
        agentTypeCodex: 'Codex',
        agentTypeClaude: 'Claude Code',
        effortInvalid:
          '"{value}"는 {agent}에서 지원하지 않는 추론 강도입니다. 다른 값을 선택해 주세요.',
        effortSaveBlocked:
          '지원하지 않는 추론 강도가 있습니다. 저장하기 전에 수정해 주세요.',
        save: '모델 저장',
        empty: '모델 정보 없음',
      },
      codex: {
        fastMode: '패스트 모드',
        features: 'Codex 기능',
        codexFast: 'Codex (GPT)',
        codexFastHint:
          '~/.codex/config.toml [features].fast_mode — GPT 5.5 등에서 응답 속도를 높입니다. 사용량이 더 듭니다.',
        claudeFast: 'Claude',
        claudeFastHint:
          '~/.claude/settings.json fastMode — Opus 4.8에서 세션 settings.json으로 동기화됩니다.',
        fastModeApplyHint:
          '스택 재시작 없이 다음 Codex/Claude 작업부터 적용됩니다.',
        goal: 'Goals (/goal)',
        goalHint:
          '~/.codex/config.toml [features].goals — Codex 0.133 기준 opt-in 기능입니다. 기본값은 꺼짐(OFF)이며, 켜면 /goal 장기 목표 추적을 쓸 수 있습니다.',
        goalApplyHint: '스택 재시작 없이 다음 Codex 작업부터 적용됩니다.',
      },
      accounts: {
        claude: 'Claude',
        codex: 'Codex',
        noAccounts: '계정 없음',
        autoRefresh: '토큰 자동갱신',
        codexRefreshHint:
          'OAuth 토큰은 6시간마다 자동 갱신됩니다. plan 변경/해지가 즉시 반영되게 하려면 수동으로 “전체 갱신”을 누르세요.',
        tokenPlaceholder:
          'Claude OAuth 토큰 (claude CLI 로그인 후 ~/.claude/.credentials.json 에서 accessToken 값을 페이스트)',
        deleteConfirm:
          '{provider} 계정 #{index} 디렉터리를 삭제합니다. 되돌릴 수 없습니다. 계속할까요?',
        refreshFailed: '일부 갱신 실패: {indexes}',
        paymentExpired: '결제 만료 {date} ({days}일 전)',
        paymentUntil: '결제 {date}까지 ({days}일)',
        paymentUntilDays: '결제 {date}까지 ({days}일)',
        primaryAccount: '기본 계정',
        activeAccount: '사용 중',
        addTokenLabel: 'OAuth 토큰 추가',
        refreshTitle: '구독 상태를 다시 조회합니다',
        switchTitle: '다음 Codex 호출부터 이 계정을 사용합니다',
      },
      moa: {
        master: 'MoA 사용',
        masterHint:
          'Arbiter 호출 전에 외부 참조 모델 의견을 수집합니다. 저장 후 스택 재시작이 필요합니다.',
        save: 'MoA 저장',
        empty: 'MoA 설정 없음',
        test: '연결 테스트',
        testing: '테스트중…',
        testAfterSave: '저장 후 테스트',
        notTested: '연결 테스트 전',
        statusOk: '정상',
        statusFail: '실패',
        apiKeyPlaceholder: 'new API key',
        apiKeySet: 'API key set',
        modelLabel: '모델 ID',
        baseUrlLabel: 'Base URL',
        formatLabel: 'API 형식',
      },
      runtime: {
        defaultHint:
          '기본값은 모든 스킬이 켜져 있습니다. 여기서 끈 스킬만 해당 방·에이전트에 저장됩니다. 전체(모든 방) 일괄 설정은 아직 없습니다.',
        selectRoomLabel: '방 선택',
        selectAgentLabel: '에이전트',
        scopeCodexUser: 'Codex 스킬',
        scopeClaudeUser: 'Claude 스킬',
        scopeRunner: '공통 러너 스킬',
        emptyRooms: '등록된 Discord 방이 없습니다.',
        emptySkills: '이 방·에이전트에 연결된 스킬이 없습니다.',
        agentCodex: 'Codex',
        agentClaude: 'Claude Code',
      },
    },
    error: {
      api: '문제 발생',
      network: '네트워크 연결 확인 후 다시 시도해주세요.',
      timeout: '응답 시간이 초과됐어요. 잠시 후 다시 시도해주세요.',
      server: '서버에 일시적인 문제가 있어요. 잠시 후 다시 시도해주세요.',
      notFound: '요청한 정보를 찾을 수 없어요.',
      auth: '권한이 없어요. 다시 로그인해주세요.',
      unknown: '알 수 없는 오류가 발생했어요.',
    },
    control: {
      aria: '컨트롤 플레인 요약',
      queue: '작업 큐',
      activeRooms: '처리 + 대기 룸',
      pendingRooms: '메시지 대기 룸',
    },
    pwa: {
      app: 'PWA',
      install: '설치',
      installed: '설치됨',
      ready: '오프라인 준비',
      cached: '캐시됨',
      online: '온라인',
      offline: '오프라인',
      fresh: '최신',
      stale: '지연',
      secureRequired: 'HTTPS 필요',
      updated: '갱신',
    },
    metrics: {
      agents: '에이전트',
      rooms: '룸',
      ciWatchers: 'CI 감시',
    },
    panels: {
      health: '상태',
      heartbeat: '하트비트',
      healthSignals: '운영 신호',
      inbox: 'Inbox',
      inboxQueue: '처리할 액션',
      usage: '사용량',
      usageWindow: '남음 / 속도',
      rooms: '룸',
      queue: '큐',
      scheduled: '예약',
    },
    service: {
      empty: '하트비트 없음. 서비스 로그 확인.',
      heartbeat: '하트비트',
      service: '서비스',
      rooms: '룸',
      updated: '갱신',
    },
    health: {
      system: '시스템',
      signals: '헬스 신호',
      services: '서비스',
      fresh: '정상',
      stale: '지연',
      queue: '큐',
      ciFailures: 'CI 실패',
      affectedServices: '이상 서비스',
      restart: '재시작',
      restartStack: '스택 재시작',
      restarting: '재시작 중...',
      restartHint: '서비스 전체 재시작',
      confirmRestart: 'Restart stack now?',
      restartLog: 'Restart log',
      restartTarget: 'Target',
      restartStatus: 'Status',
      restartRequested: 'Requested',
      restartServices: 'Services',
      levels: {
        ok: '정상',
        stale: '주의',
        down: '중단',
      },
    },
    inbox: {
      empty: '처리할 Inbox 액션 없음.',
      cardsAria: 'Inbox 항목',
      summary: 'Inbox 요약',
      total: '전체',
      filters: 'Inbox 필터',
      all: '전체',
      noSummary: '요약 없음',
      occurred: '발생',
      source: '소스',
      target: '대상',
      openTask: '예약 보기',
      openRoom: '룸 보기',
      kinds: {
        'pending-room': '대기 룸',
        'reviewer-request': '리뷰 요청',
        approval: '승인',
        'arbiter-request': '중재 요청',
        'ci-failure': 'CI 실패',
        mention: '멘션',
      },
      severity: {
        info: '정보',
        warn: '주의',
        error: '위험',
      },
      actions: {
        run: 'Run',
        busy: 'Running...',
        runReview: 'Run review',
        finalize: 'Finalize',
        runArbiter: 'Run arbiter',
        decline: 'Decline',
        dismiss: 'Dismiss',
        confirmDecline: 'Decline this item?',
      },
    },
    rooms: roomMessages.ko,
    usage: {
      empty: '사용량 스냅샷 없음. 수집기 확인.',
      current: '사용중',
      tightest: '가장 적음',
      watch: '주의',
      remaining: '남음',
      speed: '속도',
      inUse: '사용중',
      reset: '리셋',
      noReset: '리셋 없음',
      limitBasis: {
        h5: '5시간 기준',
        d7: '7일 기준',
      },
      quota: {
        h5: '5시간',
        d7: '7일',
      },
      usage: '사용량',
      groupPrimary: 'Claude / Kimi',
      groupCodex: 'Codex',
      risk: {
        ok: '여유',
        warn: '주의',
        critical: '위험',
      },
      speedLabel: {
        ok: '보통',
        warn: '빠름',
        critical: '너무 빠름',
      },
    },
    tasks: {
      empty: '예약 작업 없음.',
      cardsAria: '예약 작업 카드',
      groups: {
        watchers: 'CI 감시',
        scheduled: '예약',
        paused: '일시정지',
        completed: '완료',
      },
      count: '개',
      groupEmpty: '해당 없음',
      task: '작업',
      status: '상태',
      schedule: '스케줄',
      next: '다음',
      last: '최근',
      context: '컨텍스트',
      ciWatch: 'CI 감시',
      emptyPrompt: '(빈 미리보기)',
      prompt: '프롬프트',
      until: '까지',
      suspendedUntil: '정지 해제',
      lastResult: '최근 결과',
      result: '결과 없음',
      resultOk: '정상',
      resultFail: '실패',
      noResult: '결과 없음',
      noTime: '-',
      now: '지금',
      createTitle: '작업 예약',
      createSubtitle: '룸에 반복 작업 걸기',
      filterAll: '전체',
      details: '상세',
      room: '방',
      selectRoom: '방 선택',
      scheduleType: '방식',
      scheduleValue: '시간값',
      scheduleValueHint: 'ISO 시간, 10m 같은 간격, 또는 cron',
      promptPlaceholder: '에이전트가 실행할 작업을 적어주세요.',
      editPromptPlaceholder: '비우면 기존 프롬프트 유지',
      scheduleTypes: {
        once: '한 번',
        interval: '간격',
        cron: '크론',
      },
      contextModes: {
        isolated: '격리',
        group: '방 맥락',
      },
      actions: {
        create: '생성',
        edit: '수정',
        save: '저장',
        pause: '일시정지',
        resume: '재개',
        cancel: '취소',
        busy: '처리 중',
        confirmCancel: '이 예약 작업을 취소할까요?',
      },
    },
    status: {
      processing: '처리중',
      waiting: '대기',
      inactive: '휴면',
      active: '활성',
      paused: '일시정지',
      completed: '완료',
    },
    units: {
      second: '초',
      minute: '분',
      hour: '시간',
      task: '작업',
      messageShort: '메시지',
      chars: '자',
    },
  },
  en: {
    app: {
      loading: 'Loading dashboard',
    },
    actions: {
      close: 'Close',
      refresh: 'Refresh',
      refreshing: 'Refreshing',
      retry: 'Retry',
    },
    nav: {
      aria: 'Dashboard sections',
      drawerAria: 'Dashboard menu',
      drawerNavAria: 'Dashboard menu sections',
      menuOpen: 'Open menu',
      menuClose: 'Close menu',
      operations: 'Ops',
      updated: 'Updated',
      health: 'Health',
      inbox: 'Inbox',
      usage: 'Usage',
      rooms: 'Rooms',
      scheduled: 'Scheduled',
      settings: 'Settings',
    },
    language: {
      label: 'Language',
    },
    settings: {
      title: 'Settings',
      nicknameLabel: 'Nickname',
      nicknamePlaceholder: 'Web Dashboard',
      nicknameHelp: 'Name shown when sending messages from this dashboard',
      languageLabel: 'Language',
      navAria: 'Settings sections',
      contentAria: 'Settings items',
      sidebarAria: 'Settings navigation and apply',
      nav: {
        general: { title: 'General', detail: 'Display · language' },
        models: { title: 'Models', detail: 'owner · reviewer · arbiter' },
        runtime: { title: 'Skills', detail: 'per-room on/off' },
        moa: { title: 'MoA', detail: 'reference models · test' },
        codex: { title: 'Codex', detail: 'fast mode · /goal' },
        accounts: { title: 'Accounts', detail: 'Claude · Codex' },
      },
      apply: {
        aria: 'Apply changes',
        kicker: 'Apply',
        title: 'Restart after save',
        hint: 'Model, MoA, Codex, and account changes apply after a stack restart.',
        restart: 'Restart stack',
      },
      sections: {
        general: {
          kicker: 'Dashboard identity',
          title: 'General',
          description:
            'Only the display name and language change immediately in the browser.',
        },
        models: {
          kicker: 'Agent routing',
          title: 'Models',
          description:
            'Pick a model and inference depth for each role. Restart the stack after saving.',
        },
        runtime: {
          kicker: 'Agent skills',
          title: 'Skills',
          description:
            'Turn skills on or off per room and agent. Default is all on; there is no global bulk setting yet.',
        },
        moa: {
          kicker: 'Arbiter references',
          title: 'MoA reference models',
          description:
            'Enable external reference models like Kimi or GLM and verify connectivity.',
        },
        codex: {
          kicker: 'Codex runtime',
          title: 'Codex options',
          description:
            'Manage fast responses and experimental features. /goal lives here.',
        },
        accounts: {
          kicker: 'Credentials',
          title: 'Accounts',
          description: 'Review and switch Claude OAuth and Codex accounts.',
        },
      },
      common: {
        loading: 'Loading…',
        none: 'None',
        saving: 'Saving…',
        saved: 'Saved',
        savedRestartHint: 'Saved. Use Restart stack in the sidebar to apply.',
        save: 'Save',
        delete: 'Delete',
        refresh: 'Refresh',
        refreshing: 'Refreshing…',
        refreshAll: 'Refresh all',
        switch: 'Switch',
        switching: 'Switching…',
        default: 'Default',
        inUse: 'In use',
        add: 'Add',
      },
      models: {
        roleOwner: 'Owner',
        roleReviewer: 'Reviewer',
        roleArbiter: 'Arbiter',
        roleOwnerHint: 'Runs the main work',
        roleReviewerHint: 'Reviews proposed changes',
        roleArbiterHint: 'Makes the final call',
        modelLabel: 'Model',
        effortLabel: 'Inference depth',
        modelDefault: 'Default (.env)',
        modelCustom: 'Custom…',
        modelCustomLabel: 'Model ID',
        modelPlaceholder: 'e.g. gpt-5.5, claude-opus-4-8',
        groupCodex: 'Codex',
        groupClaude: 'Claude',
        groupCustom: 'Custom',
        effortDefault: 'Default (.env)',
        effortOptions: {
          low: 'Low',
          medium: 'Medium',
          high: 'High',
          xhigh: 'Very high',
          max: 'Max',
        },
        agentTypeLabel: 'Agent',
        agentTypeCodex: 'Codex',
        agentTypeClaude: 'Claude Code',
        effortInvalid:
          '"{value}" is not supported for {agent}. Choose another effort level.',
        effortSaveBlocked:
          'Unsupported effort levels are set. Fix them before saving.',
        save: 'Save models',
        empty: 'No model config',
      },
      codex: {
        fastMode: 'Fast mode',
        features: 'Codex features',
        codexFast: 'Codex (GPT)',
        codexFastHint:
          '~/.codex/config.toml [features].fast_mode — faster responses on GPT 5.5 and newer; higher usage.',
        claudeFast: 'Claude',
        claudeFastHint:
          '~/.claude/settings.json fastMode — synced into session settings.json for Opus 4.8.',
        fastModeApplyHint:
          'Applies on the next Codex/Claude run. No stack restart required.',
        goal: 'Goals (/goal)',
        goalHint:
          '~/.codex/config.toml [features].goals — opt-in on Codex 0.133. Off by default; enables /goal long-running objectives when on.',
        goalApplyHint:
          'Applies on the next Codex run. No stack restart required.',
      },
      accounts: {
        claude: 'Claude',
        codex: 'Codex',
        noAccounts: 'No accounts',
        autoRefresh: 'Auto token refresh',
        codexRefreshHint:
          'OAuth tokens refresh every 6 hours. Use Refresh all for immediate plan changes.',
        tokenPlaceholder:
          'Claude OAuth token (paste accessToken from ~/.claude/.credentials.json after claude CLI login)',
        deleteConfirm:
          'Delete {provider} account #{index} directory. This cannot be undone. Continue?',
        refreshFailed: 'Some refreshes failed: {indexes}',
        paymentExpired: 'Expired {date} ({days}d ago)',
        paymentUntil: 'Paid until {date} ({days}d)',
        paymentUntilDays: 'Paid until {date} ({days}d)',
        primaryAccount: 'Primary',
        activeAccount: 'Active',
        addTokenLabel: 'Add OAuth token',
        refreshTitle: 'Refresh subscription status',
        switchTitle: 'Use this account for the next Codex call',
      },
      moa: {
        master: 'Enable MoA',
        masterHint:
          'Collect external reference opinions before arbiter calls. Stack restart required after save.',
        save: 'Save MoA',
        empty: 'No MoA settings',
        test: 'Test connection',
        testing: 'Testing…',
        testAfterSave: 'Save to test',
        notTested: 'Not tested yet',
        statusOk: 'OK',
        statusFail: 'Failed',
        apiKeyPlaceholder: 'new API key',
        apiKeySet: 'API key set',
        modelLabel: 'Model ID',
        baseUrlLabel: 'Base URL',
        formatLabel: 'API format',
      },
      runtime: {
        defaultHint:
          'All skills start enabled. Only per-room, per-agent disables are saved. Global bulk settings are not available yet.',
        selectRoomLabel: 'Room',
        selectAgentLabel: 'Agent',
        scopeCodexUser: 'Codex skill',
        scopeClaudeUser: 'Claude skill',
        scopeRunner: 'Shared runner skill',
        emptyRooms: 'No registered Discord rooms.',
        emptySkills: 'No skills for this room and agent.',
        agentCodex: 'Codex',
        agentClaude: 'Claude Code',
      },
    },
    error: {
      api: 'Something went wrong',
      network: 'Check your network connection and try again.',
      timeout: 'The request timed out. Try again in a moment.',
      server: 'Server hiccup. Try again in a bit.',
      notFound: "We couldn't find what you asked for.",
      auth: 'Sign in again to continue.',
      unknown: 'An unexpected error occurred.',
    },
    control: {
      aria: 'Control plane summary',
      queue: 'Work queue',
      activeRooms: 'processing + waiting rooms',
      pendingRooms: 'rooms with pending messages',
    },
    pwa: {
      app: 'PWA',
      install: 'Install',
      installed: 'Installed',
      ready: 'Offline ready',
      cached: 'Cached',
      online: 'Online',
      offline: 'Offline',
      fresh: 'Fresh',
      stale: 'Stale',
      secureRequired: 'HTTPS required',
      updated: 'Updated',
    },
    metrics: {
      agents: 'agents',
      rooms: 'rooms',
      ciWatchers: 'CI watchers',
    },
    panels: {
      health: 'Health',
      heartbeat: 'Heartbeat',
      healthSignals: 'Signals',
      inbox: 'Inbox',
      inboxQueue: 'Action queue',
      usage: 'Usage',
      usageWindow: 'Remaining / speed',
      rooms: 'Rooms',
      queue: 'Queue',
      scheduled: 'Scheduled',
    },
    service: {
      empty: 'No heartbeat yet. Check service logs.',
      heartbeat: 'heartbeat',
      service: 'service',
      rooms: 'rooms',
      updated: 'updated',
    },
    health: {
      system: 'System',
      signals: 'Health signals',
      services: 'Services',
      fresh: 'Fresh',
      stale: 'stale',
      queue: 'Queue',
      ciFailures: 'CI failures',
      affectedServices: 'Affected services',
      restart: 'Restart',
      restartStack: 'Restart stack',
      restarting: 'Restarting...',
      restartHint: 'Restart all services',
      confirmRestart: 'Restart stack now?',
      restartLog: 'Restart log',
      restartTarget: 'Target',
      restartStatus: 'Status',
      restartRequested: 'Requested',
      restartServices: 'Services',
      levels: {
        ok: 'OK',
        stale: 'Watch',
        down: 'Down',
      },
    },
    inbox: {
      empty: 'No Inbox actions.',
      cardsAria: 'Inbox items',
      summary: 'Inbox summary',
      total: 'Total',
      filters: 'Inbox filters',
      all: 'All',
      noSummary: 'No summary',
      occurred: 'Occurred',
      source: 'Source',
      target: 'Target',
      openTask: 'Open scheduled',
      openRoom: 'Open room',
      kinds: {
        'pending-room': 'Pending room',
        'reviewer-request': 'Review request',
        approval: 'Approval',
        'arbiter-request': 'Arbiter request',
        'ci-failure': 'CI failure',
        mention: 'Mention',
      },
      severity: {
        info: 'Info',
        warn: 'Warn',
        error: 'Risk',
      },
      actions: {
        run: 'Run',
        busy: 'Running...',
        runReview: 'Run review',
        finalize: 'Finalize',
        runArbiter: 'Run arbiter',
        decline: 'Decline',
        dismiss: 'Dismiss',
        confirmDecline: 'Decline this item?',
      },
    },
    rooms: roomMessages.en,
    usage: {
      empty: 'No usage snapshot. Check collector.',
      current: 'Active',
      tightest: 'Tightest',
      watch: 'Watch',
      remaining: 'Left',
      speed: 'Rate',
      inUse: 'active',
      reset: 'reset',
      noReset: 'no reset',
      limitBasis: {
        h5: '5h basis',
        d7: '7d basis',
      },
      quota: {
        h5: '5h',
        d7: '7d',
      },
      usage: 'usage',
      groupPrimary: 'Claude / Kimi',
      groupCodex: 'Codex',
      risk: {
        ok: 'OK',
        warn: 'Watch',
        critical: 'Risk',
      },
      speedLabel: {
        ok: 'OK',
        warn: 'Fast',
        critical: 'Too fast',
      },
    },
    tasks: {
      empty: 'No scheduled work.',
      cardsAria: 'Scheduled task cards',
      groups: {
        watchers: 'CI watch',
        scheduled: 'Scheduled',
        paused: 'Paused',
        completed: 'Completed',
      },
      count: 'items',
      groupEmpty: 'None',
      task: 'task',
      status: 'status',
      schedule: 'schedule',
      next: 'next',
      last: 'last',
      context: 'context',
      ciWatch: 'CI Watch',
      emptyPrompt: '(empty preview)',
      prompt: 'prompt',
      until: 'until',
      suspendedUntil: 'resume',
      lastResult: 'last result',
      result: 'no result',
      resultOk: 'ok',
      resultFail: 'failed',
      noResult: 'no result',
      noTime: '-',
      now: 'now',
      createTitle: 'Create task',
      createSubtitle: 'Schedule work from web',
      filterAll: 'All',
      details: 'Details',
      room: 'Room',
      selectRoom: 'Select room',
      scheduleType: 'Type',
      scheduleValue: 'Value',
      scheduleValueHint: 'ISO time, ms, or cron',
      promptPlaceholder: 'What should the agent do?',
      editPromptPlaceholder: 'Leave blank to keep prompt',
      scheduleTypes: {
        once: 'Once',
        interval: 'Interval',
        cron: 'Cron',
      },
      contextModes: {
        isolated: 'Isolated',
        group: 'Group',
      },
      actions: {
        create: 'Create',
        edit: 'Edit',
        save: 'Save',
        pause: 'Pause',
        resume: 'Resume',
        cancel: 'Cancel',
        busy: 'Working',
        confirmCancel: 'Cancel this scheduled work?',
      },
    },
    status: {
      processing: 'processing',
      waiting: 'waiting',
      inactive: 'inactive',
      active: 'active',
      paused: 'paused',
      completed: 'completed',
    },
    units: {
      second: 's',
      minute: 'm',
      hour: 'h',
      task: 'task',
      messageShort: 'msg',
      chars: 'chars',
    },
  },
  zh: {
    app: {
      loading: '正在加载仪表盘',
    },
    actions: {
      close: '关闭',
      refresh: '刷新',
      refreshing: '刷新中',
      retry: '重试',
    },
    nav: {
      aria: '仪表盘分区',
      drawerAria: '仪表盘菜单',
      drawerNavAria: '仪表盘菜单分区',
      menuOpen: '打开菜单',
      menuClose: '关闭菜单',
      operations: '运营',
      updated: '更新',
      health: '健康',
      inbox: '收件',
      usage: '用量',
      rooms: '房间',
      scheduled: '计划',
      settings: '设置',
    },
    language: {
      label: '语言',
    },
    settings: {
      title: '设置',
      nicknameLabel: '昵称',
      nicknamePlaceholder: 'Web Dashboard',
      nicknameHelp: '从此面板发送消息时显示的名称',
      languageLabel: '语言',
      navAria: '设置分区',
      contentAria: '设置项',
      sidebarAria: '设置导航与应用',
      nav: {
        general: { title: '常规', detail: '显示 · 语言' },
        models: { title: '模型', detail: 'owner · reviewer · arbiter' },
        runtime: { title: '技能', detail: '按房间开关' },
        moa: { title: 'MoA', detail: '参考模型 · 连接测试' },
        codex: { title: 'Codex', detail: 'fast mode · /goal' },
        accounts: { title: '账户', detail: 'Claude · Codex' },
      },
      apply: {
        aria: '应用更改',
        kicker: 'Apply',
        title: '保存后重启',
        hint: '模型、MoA、Codex、账户更改需在栈重启后生效。',
        restart: '重启栈',
      },
      sections: {
        general: {
          kicker: 'Dashboard identity',
          title: '常规',
          description: '仅浏览器中的显示名称和语言会立即更改。',
        },
        models: {
          kicker: 'Agent routing',
          title: '模型',
          description: '为各角色选择模型和推理强度。保存后需重启栈。',
        },
        runtime: {
          kicker: 'Agent skills',
          title: '技能',
          description: '按房间和代理开关技能。默认全部开启，尚无全局批量设置。',
        },
        moa: {
          kicker: 'Arbiter references',
          title: 'MoA 参考模型',
          description: '启用 Kimi、GLM 等外部参考模型并验证连接。',
        },
        codex: {
          kicker: 'Codex runtime',
          title: 'Codex 选项',
          description: '管理快速响应和实验功能。/goal 在此设置。',
        },
        accounts: {
          kicker: 'Credentials',
          title: '账户',
          description: '查看并切换 Claude OAuth 和 Codex 账户。',
        },
      },
      common: {
        loading: '加载中…',
        none: '无',
        saving: '保存中…',
        saved: '已保存',
        savedRestartHint: '已保存。请在侧边栏点击重启栈以应用。',
        save: '保存',
        delete: '删除',
        refresh: '刷新',
        refreshing: '刷新中…',
        refreshAll: '全部刷新',
        switch: '切换',
        switching: '切换中…',
        default: '默认',
        inUse: '使用中',
        add: '添加',
      },
      models: {
        roleOwner: 'Owner',
        roleReviewer: 'Reviewer',
        roleArbiter: 'Arbiter',
        roleOwnerHint: '执行主要工作的代理',
        roleReviewerHint: '审查变更的代理',
        roleArbiterHint: '做最终裁决的代理',
        modelLabel: '模型',
        effortLabel: '推理强度',
        modelDefault: '默认 (.env)',
        modelCustom: '自定义…',
        modelCustomLabel: '模型 ID',
        modelPlaceholder: '例如 gpt-5.5, claude-opus-4-8',
        groupCodex: 'Codex',
        groupClaude: 'Claude',
        groupCustom: '自定义',
        effortDefault: '默认 (.env)',
        effortOptions: {
          low: '低',
          medium: '中',
          high: '高',
          xhigh: '很高',
          max: '最大',
        },
        agentTypeLabel: '代理',
        agentTypeCodex: 'Codex',
        agentTypeClaude: 'Claude Code',
        effortInvalid: '“{value}” 不适用于 {agent}，请选择其他推理强度。',
        effortSaveBlocked: '存在不支持的推理强度，请先修正再保存。',
        save: '保存模型',
        empty: '无模型配置',
      },
      codex: {
        fastMode: '快速模式',
        features: 'Codex 功能',
        codexFast: 'Codex (GPT)',
        codexFastHint:
          '~/.codex/config.toml [features].fast_mode — GPT 5.5 等更快响应，用量更高。',
        claudeFast: 'Claude',
        claudeFastHint:
          '~/.claude/settings.json fastMode — 同步到会话 settings.json，适用于 Opus 4.8。',
        fastModeApplyHint: '无需重启栈，下次 Codex/Claude 任务起生效。',
        goal: 'Goals (/goal)',
        goalHint:
          '~/.codex/config.toml [features].goals — Codex 0.133 可选功能，默认关闭；开启后可使用 /goal。',
        goalApplyHint: '无需重启栈，下次 Codex 任务起生效。',
      },
      accounts: {
        claude: 'Claude',
        codex: 'Codex',
        noAccounts: '无账户',
        autoRefresh: '自动刷新令牌',
        codexRefreshHint:
          'OAuth 令牌每 6 小时自动刷新。计划变更请手动全部刷新。',
        tokenPlaceholder:
          'Claude OAuth 令牌（claude CLI 登录后从 ~/.claude/.credentials.json 粘贴 accessToken）',
        deleteConfirm: '将删除 {provider} 账户 #{index} 目录，无法撤销。继续？',
        refreshFailed: '部分刷新失败: {indexes}',
        paymentExpired: '已过期 {date}（{days} 天前）',
        paymentUntil: '付费至 {date}（{days} 天）',
        paymentUntilDays: '付费至 {date}（{days} 天）',
        primaryAccount: '主账户',
        activeAccount: '使用中',
        addTokenLabel: '添加 OAuth 令牌',
        refreshTitle: '重新查询订阅状态',
        switchTitle: '下次 Codex 调用起使用此账户',
      },
      moa: {
        master: '启用 MoA',
        masterHint: 'Arbiter 调用前收集外部参考意见。保存后需重启栈。',
        save: '保存 MoA',
        empty: '无 MoA 设置',
        test: '连接测试',
        testing: '测试中…',
        testAfterSave: '保存后测试',
        notTested: '尚未测试',
        statusOk: '正常',
        statusFail: '失败',
        apiKeyPlaceholder: 'new API key',
        apiKeySet: 'API key set',
        modelLabel: '模型 ID',
        baseUrlLabel: 'Base URL',
        formatLabel: 'API 格式',
      },
      runtime: {
        defaultHint:
          '默认所有技能开启。仅保存按房间、按代理的关闭项。尚无全局批量设置。',
        selectRoomLabel: '选择房间',
        selectAgentLabel: '代理',
        scopeCodexUser: 'Codex 技能',
        scopeClaudeUser: 'Claude 技能',
        scopeRunner: '共享 runner 技能',
        emptyRooms: '没有已注册的 Discord 房间。',
        emptySkills: '此房间和代理没有可用技能。',
        agentCodex: 'Codex',
        agentClaude: 'Claude Code',
      },
    },
    error: {
      api: '发生问题',
      network: '请检查网络连接后重试。',
      timeout: '请求超时,请稍后重试。',
      server: '服务器暂时出现问题,请稍后重试。',
      notFound: '找不到所请求的信息。',
      auth: '没有权限,请重新登录。',
      unknown: '发生未知错误。',
    },
    control: {
      aria: '控制平面摘要',
      queue: '任务队列',
      activeRooms: '处理中 + 等待房间',
      pendingRooms: '有待处理消息的房间',
    },
    pwa: {
      app: 'PWA',
      install: '安装',
      installed: '已安装',
      ready: '离线可用',
      cached: '已缓存',
      online: '在线',
      offline: '离线',
      fresh: '最新',
      stale: '延迟',
      secureRequired: '需要 HTTPS',
      updated: '更新',
    },
    metrics: {
      agents: '代理',
      rooms: '房间',
      ciWatchers: 'CI 监控',
    },
    panels: {
      health: '健康',
      heartbeat: '心跳',
      healthSignals: '运行信号',
      inbox: '收件',
      inboxQueue: '待处理操作',
      usage: '用量',
      usageWindow: '剩余 / 速度',
      rooms: '房间',
      queue: '队列',
      scheduled: '计划',
    },
    service: {
      empty: '暂无心跳。检查服务日志。',
      heartbeat: '心跳',
      service: '服务',
      rooms: '房间',
      updated: '更新',
    },
    health: {
      system: '系统',
      signals: '健康信号',
      services: '服务',
      fresh: '正常',
      stale: '延迟',
      queue: '队列',
      ciFailures: 'CI 失败',
      affectedServices: '异常服务',
      restart: '重启',
      restartStack: '重启 stack',
      restarting: '重启中...',
      restartHint: '重启全部服务',
      confirmRestart: '现在重启 stack?',
      restartLog: '重启记录',
      restartTarget: '目标',
      restartStatus: '状态',
      restartRequested: '请求',
      restartServices: '服务',
      levels: {
        ok: '正常',
        stale: '关注',
        down: '中断',
      },
    },
    inbox: {
      empty: '暂无待处理收件操作。',
      cardsAria: '收件项',
      summary: '收件摘要',
      total: '全部',
      filters: '收件筛选',
      all: '全部',
      noSummary: '无摘要',
      occurred: '发生',
      source: '来源',
      target: '目标',
      openTask: '查看计划',
      openRoom: '查看房间',
      kinds: {
        'pending-room': '待处理房间',
        'reviewer-request': '评审请求',
        approval: '审批',
        'arbiter-request': '仲裁请求',
        'ci-failure': 'CI 失败',
        mention: '提及',
      },
      severity: {
        info: '信息',
        warn: '关注',
        error: '风险',
      },
      actions: {
        run: '运行',
        busy: '运行中...',
        runReview: '运行评审',
        finalize: '完成',
        runArbiter: '运行仲裁',
        decline: '拒绝',
        dismiss: '忽略',
        confirmDecline: '确认拒绝此项？',
      },
    },
    rooms: roomMessages.zh,
    usage: {
      empty: '暂无用量快照。检查采集器。',
      current: '使用中',
      tightest: '剩余最少',
      watch: '关注',
      remaining: '剩余',
      speed: '速度',
      inUse: '使用中',
      reset: '重置',
      noReset: '无重置',
      limitBasis: {
        h5: '按5小时',
        d7: '按7天',
      },
      quota: {
        h5: '5小时',
        d7: '7天',
      },
      usage: '用量',
      groupPrimary: 'Claude / Kimi',
      groupCodex: 'Codex',
      risk: {
        ok: '充足',
        warn: '关注',
        critical: '接近上限',
      },
      speedLabel: {
        ok: '正常',
        warn: '较快',
        critical: '过快',
      },
    },
    tasks: {
      empty: '暂无计划任务。',
      cardsAria: '计划任务卡片',
      groups: {
        watchers: 'CI 监控',
        scheduled: '计划',
        paused: '暂停',
        completed: '完成',
      },
      count: '项',
      groupEmpty: '无',
      task: '任务',
      status: '状态',
      schedule: '计划',
      next: '下次',
      last: '最近',
      context: '上下文',
      ciWatch: 'CI 监控',
      emptyPrompt: '（空预览）',
      prompt: '提示',
      until: '直到',
      suspendedUntil: '恢复',
      lastResult: '最近结果',
      result: '无结果',
      resultOk: '正常',
      resultFail: '失败',
      noResult: '无结果',
      noTime: '-',
      now: '现在',
      createTitle: 'Create task',
      createSubtitle: 'Schedule work from web',
      filterAll: 'All',
      details: 'Details',
      room: 'Room',
      selectRoom: 'Select room',
      scheduleType: 'Type',
      scheduleValue: 'Value',
      scheduleValueHint: 'ISO time, ms, or cron',
      promptPlaceholder: 'What should the agent do?',
      editPromptPlaceholder: 'Leave blank to keep prompt',
      scheduleTypes: {
        once: 'Once',
        interval: 'Interval',
        cron: 'Cron',
      },
      contextModes: {
        isolated: 'Isolated',
        group: 'Group',
      },
      actions: {
        create: 'Create',
        edit: 'Edit',
        save: 'Save',
        pause: 'Pause',
        resume: 'Resume',
        cancel: 'Cancel',
        busy: 'Working',
        confirmCancel: 'Cancel this scheduled work?',
      },
    },
    status: {
      processing: '处理中',
      waiting: '等待',
      inactive: '空闲',
      active: '活跃',
      paused: '暂停',
      completed: '完成',
    },
    units: {
      second: '秒',
      minute: '分',
      hour: '小时',
      task: '任务',
      messageShort: '消息',
      chars: '字',
    },
  },
  ja: {
    app: {
      loading: 'ダッシュボードを読み込み中',
    },
    actions: {
      close: '閉じる',
      refresh: '更新',
      refreshing: '更新中',
      retry: '再試行',
    },
    nav: {
      aria: 'ダッシュボードセクション',
      drawerAria: 'ダッシュボードメニュー',
      drawerNavAria: 'ダッシュボードメニューセクション',
      menuOpen: 'メニューを開く',
      menuClose: 'メニューを閉じる',
      operations: '運用',
      updated: '更新',
      health: '状態',
      inbox: '受信',
      usage: '使用量',
      rooms: 'ルーム',
      scheduled: '予定',
      settings: '設定',
    },
    language: {
      label: '言語',
    },
    settings: {
      title: '設定',
      nicknameLabel: 'ニックネーム',
      nicknamePlaceholder: 'Web Dashboard',
      nicknameHelp: 'このダッシュボードからメッセージを送る時に表示される名前',
      languageLabel: '言語',
      navAria: '設定セクション',
      contentAria: '設定項目',
      sidebarAria: '設定ナビと適用',
      nav: {
        general: { title: '一般', detail: '表示 · 言語' },
        models: { title: 'モデル', detail: 'owner · reviewer · arbiter' },
        runtime: { title: 'スキル', detail: 'ルーム別 ON/OFF' },
        moa: { title: 'MoA', detail: '参照モデル · 接続テスト' },
        codex: { title: 'Codex', detail: 'fast mode · /goal' },
        accounts: { title: 'アカウント', detail: 'Claude · Codex' },
      },
      apply: {
        aria: '変更を適用',
        kicker: 'Apply',
        title: '保存後に再起動',
        hint: 'モデル、MoA、Codex、アカウントの変更はスタック再起動後に反映されます。',
        restart: 'スタック再起動',
      },
      sections: {
        general: {
          kicker: 'Dashboard identity',
          title: '一般',
          description: 'ブラウザ上の表示名と言語だけがすぐに変わります。',
        },
        models: {
          kicker: 'Agent routing',
          title: 'モデル',
          description:
            '役割ごとにモデルと推論強度を選びます。保存後にスタック再起動が必要です。',
        },
        runtime: {
          kicker: 'Agent skills',
          title: 'スキル',
          description:
            'ルームとエージェントごとにスキルを ON/OFF します。デフォルトはすべて ON で、全体一括設定はまだありません。',
        },
        moa: {
          kicker: 'Arbiter references',
          title: 'MoA 参照モデル',
          description:
            'Kimi や GLM などの外部参照モデルを有効化し接続を確認します。',
        },
        codex: {
          kicker: 'Codex runtime',
          title: 'Codex オプション',
          description:
            '高速応答と実験機能を管理します。/goal はここにあります。',
        },
        accounts: {
          kicker: 'Credentials',
          title: 'アカウント',
          description: 'Claude OAuth と Codex アカウントの状態確認と切り替え。',
        },
      },
      common: {
        loading: '読み込み中…',
        none: 'なし',
        saving: '保存中…',
        saved: '保存済み',
        savedRestartHint:
          '保存済み。サイドバーのスタック再起動で適用してください。',
        save: '保存',
        delete: '削除',
        refresh: '更新',
        refreshing: '更新中…',
        refreshAll: '一括更新',
        switch: '切替',
        switching: '切替中…',
        default: 'デフォルト',
        inUse: '使用中',
        add: '追加',
      },
      models: {
        roleOwner: 'Owner',
        roleReviewer: 'Reviewer',
        roleArbiter: 'Arbiter',
        roleOwnerHint: '作業を実行するエージェント',
        roleReviewerHint: '変更をレビューするエージェント',
        roleArbiterHint: '最終判断を下すエージェント',
        modelLabel: 'モデル',
        effortLabel: '推論強度',
        modelDefault: 'デフォルト (.env)',
        modelCustom: 'カスタム…',
        modelCustomLabel: 'モデル ID',
        modelPlaceholder: '例: gpt-5.5, claude-opus-4-8',
        groupCodex: 'Codex',
        groupClaude: 'Claude',
        groupCustom: 'カスタム',
        effortDefault: 'デフォルト (.env)',
        effortOptions: {
          low: '低',
          medium: '中',
          high: '高',
          xhigh: '最高',
          max: '最大',
        },
        agentTypeLabel: 'エージェント',
        agentTypeCodex: 'Codex',
        agentTypeClaude: 'Claude Code',
        effortInvalid:
          '「{value}」は {agent} では使えない推論強度です。別の値を選んでください。',
        effortSaveBlocked:
          'サポート外の推論強度があります。保存前に修正してください。',
        save: 'モデル保存',
        empty: 'モデル情報なし',
      },
      codex: {
        fastMode: 'ファストモード',
        features: 'Codex 機能',
        codexFast: 'Codex (GPT)',
        codexFastHint:
          '~/.codex/config.toml [features].fast_mode — GPT 5.5 などで応答を高速化。使用量は増えます。',
        claudeFast: 'Claude',
        claudeFastHint:
          '~/.claude/settings.json fastMode — セッション settings.json に同期。Opus 4.8 向け。',
        fastModeApplyHint:
          'スタック再起動不要。次の Codex/Claude 実行から反映されます。',
        goal: 'Goals (/goal)',
        goalHint:
          '~/.codex/config.toml [features].goals — Codex 0.133 の opt-in 機能。デフォルト OFF。ON で /goal を利用。',
        goalApplyHint: 'スタック再起動不要。次の Codex 実行から反映されます。',
      },
      accounts: {
        claude: 'Claude',
        codex: 'Codex',
        noAccounts: 'アカウントなし',
        autoRefresh: 'トークン自動更新',
        codexRefreshHint:
          'OAuth トークンは 6 時間ごとに自動更新されます。プラン変更は「一括更新」を押してください。',
        tokenPlaceholder:
          'Claude OAuth トークン（claude CLI ログイン後 ~/.claude/.credentials.json の accessToken を貼り付け）',
        deleteConfirm:
          '{provider} アカウント #{index} ディレクトリを削除します。元に戻せません。続行しますか？',
        refreshFailed: '一部の更新に失敗: {indexes}',
        paymentExpired: '期限切れ {date}（{days} 日前）',
        paymentUntil: '支払い {date} まで（{days} 日）',
        paymentUntilDays: '支払い {date} まで（{days} 日）',
        primaryAccount: 'プライマリ',
        activeAccount: '使用中',
        addTokenLabel: 'OAuth トークン追加',
        refreshTitle: 'サブスクリプション状態を再取得',
        switchTitle: '次回 Codex 呼び出しからこのアカウントを使用',
      },
      moa: {
        master: 'MoA を使用',
        masterHint:
          'Arbiter 呼び出し前に外部参照モデルの意見を集めます。保存後にスタック再起動が必要です。',
        save: 'MoA 保存',
        empty: 'MoA 設定なし',
        test: '接続テスト',
        testing: 'テスト中…',
        testAfterSave: '保存後にテスト',
        notTested: '未テスト',
        statusOk: '正常',
        statusFail: '失敗',
        apiKeyPlaceholder: 'new API key',
        apiKeySet: 'API key set',
        modelLabel: 'モデル ID',
        baseUrlLabel: 'Base URL',
        formatLabel: 'API 形式',
      },
      runtime: {
        defaultHint:
          'デフォルトですべてのスキルは ON です。ルーム・エージェント単位の OFF のみ保存されます。全体一括設定はまだありません。',
        selectRoomLabel: 'ルーム',
        selectAgentLabel: 'エージェント',
        scopeCodexUser: 'Codex スキル',
        scopeClaudeUser: 'Claude スキル',
        scopeRunner: '共通 runner スキル',
        emptyRooms: '登録済み Discord ルームがありません。',
        emptySkills: 'このルームとエージェントに利用可能なスキルがありません。',
        agentCodex: 'Codex',
        agentClaude: 'Claude Code',
      },
    },
    error: {
      api: '問題が発生しました',
      network: 'ネットワーク接続を確認してから再試行してください。',
      timeout:
        'リクエストがタイムアウトしました。少し経ってから再試行してください。',
      server:
        'サーバーに一時的な問題があります。少し経ってから再試行してください。',
      notFound: 'お探しの情報が見つかりませんでした。',
      auth: '権限がありません。再ログインしてください。',
      unknown: '不明なエラーが発生しました。',
    },
    control: {
      aria: 'コントロールプレーン概要',
      queue: '作業キュー',
      activeRooms: '処理中 + 待機ルーム',
      pendingRooms: 'メッセージ待ちルーム',
    },
    pwa: {
      app: 'PWA',
      install: 'インストール',
      installed: 'インストール済み',
      ready: 'オフライン可',
      cached: 'キャッシュ済み',
      online: 'オンライン',
      offline: 'オフライン',
      fresh: '最新',
      stale: '遅延',
      secureRequired: 'HTTPS 必須',
      updated: '更新',
    },
    metrics: {
      agents: 'エージェント',
      rooms: 'ルーム',
      ciWatchers: 'CI監視',
    },
    panels: {
      health: '状態',
      heartbeat: 'ハートビート',
      healthSignals: '運用シグナル',
      inbox: '受信',
      inboxQueue: '対応アクション',
      usage: '使用量',
      usageWindow: '残量 / 速度',
      rooms: 'ルーム',
      queue: 'キュー',
      scheduled: '予定',
    },
    service: {
      empty: 'ハートビートなし。サービスログを確認。',
      heartbeat: 'ハートビート',
      service: 'サービス',
      rooms: 'ルーム',
      updated: '更新',
    },
    health: {
      system: 'システム',
      signals: '状態シグナル',
      services: 'サービス',
      fresh: '正常',
      stale: '遅延',
      queue: 'キュー',
      ciFailures: 'CI失敗',
      affectedServices: '異常サービス',
      restart: '再起動',
      restartStack: 'Stack再起動',
      restarting: '再起動中...',
      restartHint: '全サービス再起動',
      confirmRestart: 'Stackを再起動しますか?',
      restartLog: '再起動ログ',
      restartTarget: '対象',
      restartStatus: '状態',
      restartRequested: '要求',
      restartServices: 'サービス',
      levels: {
        ok: '正常',
        stale: '注意',
        down: '停止',
      },
    },
    inbox: {
      empty: '対応する受信アクションはありません。',
      cardsAria: '受信項目',
      summary: '受信サマリー',
      total: '全体',
      filters: '受信フィルター',
      all: '全体',
      noSummary: '概要なし',
      occurred: '発生',
      source: 'ソース',
      target: '対象',
      openTask: '予定を見る',
      openRoom: 'ルームを見る',
      kinds: {
        'pending-room': '待機ルーム',
        'reviewer-request': 'レビュー依頼',
        approval: '承認',
        'arbiter-request': '仲裁依頼',
        'ci-failure': 'CI失敗',
        mention: 'メンション',
      },
      severity: {
        info: '情報',
        warn: '注意',
        error: '危険',
      },
      actions: {
        run: '実行',
        busy: '実行中...',
        runReview: 'レビュー実行',
        finalize: 'Finalize',
        runArbiter: '仲裁実行',
        decline: '拒否',
        dismiss: '非表示',
        confirmDecline: 'この項目を拒否しますか？',
      },
    },
    rooms: roomMessages.ja,
    usage: {
      empty: '使用量スナップショットなし。収集器を確認。',
      current: '使用中',
      tightest: '残量最少',
      watch: '注意',
      remaining: '残量',
      speed: '速度',
      inUse: '使用中',
      reset: 'リセット',
      noReset: 'リセットなし',
      limitBasis: {
        h5: '5時間基準',
        d7: '7日基準',
      },
      quota: {
        h5: '5時間',
        d7: '7日',
      },
      usage: '使用量',
      groupPrimary: 'Claude / Kimi',
      groupCodex: 'Codex',
      risk: {
        ok: '余裕',
        warn: '注意',
        critical: '上限注意',
      },
      speedLabel: {
        ok: '通常',
        warn: '速い',
        critical: '速すぎ',
      },
    },
    tasks: {
      empty: '予定作業なし。',
      cardsAria: '予定作業カード',
      groups: {
        watchers: 'CI監視',
        scheduled: '予定',
        paused: '一時停止',
        completed: '完了',
      },
      count: '件',
      groupEmpty: 'なし',
      task: '作業',
      status: '状態',
      schedule: '予定',
      next: '次回',
      last: '直近',
      context: 'コンテキスト',
      ciWatch: 'CI監視',
      emptyPrompt: '（空のプレビュー）',
      prompt: 'プロンプト',
      until: 'まで',
      suspendedUntil: '再開',
      lastResult: '直近結果',
      result: '結果なし',
      resultOk: '正常',
      resultFail: '失敗',
      noResult: '結果なし',
      noTime: '-',
      now: '今',
      createTitle: 'Create task',
      createSubtitle: 'Schedule work from web',
      filterAll: 'All',
      details: 'Details',
      room: 'Room',
      selectRoom: 'Select room',
      scheduleType: 'Type',
      scheduleValue: 'Value',
      scheduleValueHint: 'ISO time, ms, or cron',
      promptPlaceholder: 'What should the agent do?',
      editPromptPlaceholder: 'Leave blank to keep prompt',
      scheduleTypes: {
        once: 'Once',
        interval: 'Interval',
        cron: 'Cron',
      },
      contextModes: {
        isolated: 'Isolated',
        group: 'Group',
      },
      actions: {
        create: 'Create',
        edit: 'Edit',
        save: 'Save',
        pause: 'Pause',
        resume: 'Resume',
        cancel: 'Cancel',
        busy: 'Working',
        confirmCancel: 'Cancel this scheduled work?',
      },
    },
    status: {
      processing: '処理中',
      waiting: '待機',
      inactive: '休止',
      active: '有効',
      paused: '一時停止',
      completed: '完了',
    },
    units: {
      second: '秒',
      minute: '分',
      hour: '時間',
      task: '作業',
      messageShort: 'メッセージ',
      chars: '字',
    },
  },
} satisfies Record<Locale, Messages>;

export function isLocale(value: string | null | undefined): value is Locale {
  return LOCALES.includes(value as Locale);
}

export function matchLocale(value: string | null | undefined): Locale | null {
  if (!value) return null;
  const normalized = value.toLowerCase();
  if (normalized.startsWith('ko')) return 'ko';
  if (normalized.startsWith('en')) return 'en';
  if (normalized.startsWith('zh')) return 'zh';
  if (normalized.startsWith('ja')) return 'ja';
  return null;
}
