MicroFish/.kiro/specs/i18n-frontend-ui-strings/design.md

34 KiB
Raw Blame History

Technical Design — i18n-frontend-ui-strings

Overview

Purpose: Externalize the remaining hard-coded Chinese UI strings in five frontend Vue files (Process.vue, Step2EnvSetup.vue, Step3Simulation.vue, Step4Report.vue, Step5Interaction.vue) to vue-i18n keys, restructure backend-coupled regex parsers in Step4Report.vue so they survive the upcoming backend prompt translation, and add a small audit script to verify acceptance.

Users: English-locale users of the MiroFish UI (the production tablet/desktop dashboard). No backend or API consumer is affected.

Impact: Translates ~50 user-visible strings, refactors three string-equality stage checks into a lookup, and centralizes 29 backend-coupled regexes into a top-of-file constants block. No behaviour change for Chinese-locale users.

Goals

  • Every flagged file:line from issue #23 either substituted with a t() call backed by entries in both locales/en.json and locales/zh.json, or explicitly classified as a deliberate Chinese token (parser marker for backend compatibility) and added to the audit allowlist.
  • Step4Report.vue parsers continue to function while the backend remains 100% Chinese, and are positioned for a single-file update when the backend prompt translation lands.
  • Step2EnvSetup.vue stage-watcher tolerates legacy Chinese display strings, current snake_case identifiers, and any future English display strings without further frontend edits.
  • A frontend/scripts/audit-i18n-strings.sh (or .js) check runs in under a minute, requires no backend, and reports zero unallowlisted CJK literals across the five files.

Non-Goals

  • Translating backend log messages, ontology/report agent prompts, or other backend code (covered by issues #24, #25 and the open i18n-*-prompts specs).
  • Translating Chinese comments in source files (covered by issues #7 and #9).
  • Frontend changes outside the five named files.
  • Adding a CI gate for this audit (tracked under issue #26).
  • Restoring the missing .kiro/specs/i18n-e2e-english-verification/audit/scripts/run_audit.sh.

Boundary Commitments

This Spec Owns

  • frontend/src/views/Process.vue: every user-visible Chinese literal flagged in the ticket and any sibling Chinese literal discovered while editing the same block.
  • frontend/src/components/Step2EnvSetup.vue: stage-watcher logic at lines 678692 (STAGE_PHASE_MAP).
  • frontend/src/components/Step3Simulation.vue: the '启动失败' fallback at line 423/427.
  • frontend/src/components/Step4Report.vue: all 29 regex parser markers (lines 555943), the no-reply marker checks (lines 850/854/1325), the '选择理由' literal (line 1464), the '等待开始' literal (line 1774), and the log-classification literals (lines 20052006).
  • frontend/src/components/Step5Interaction.vue: the chat-history templating (lines 721, 723).
  • locales/en.json and locales/zh.json: new keys added by this spec, mirrored across both files with structurally aligned shape.
  • frontend/scripts/audit-i18n-strings.sh (new): the small grep-based verifier for Requirement 6.

Out of Boundary

  • Backend prompt strings in backend/app/services/zep_tools.py, report_agent.py, etc. — the responsibility of i18n-report-agent-prompts and issue #25.
  • Other Vue files. Even if their templates also contain Chinese literals, they are out of scope for this spec.
  • Vue Router, auth, telemetry, accessibility — not affected.
  • A more elaborate keys-parity tool — explicit non-goal; the existing wc -l agreement and a one-liner jq diff suffice.

Allowed Dependencies

  • vue-i18n 11 (already adopted; default and fallback locale 'zh'; messages loaded from /locales/*.json).
  • frontend/src/i18n/index.js (no changes).
  • locales/{en,zh,languages}.json (new keys only).
  • The structure of backend-emitted markers in zep_tools.py (read-only reference; not modified here).

Revalidation Triggers

  • The downstream backend prompt translation (issue #25 / i18n-report-agent-prompts) lands. → Update REPORT_MARKERS in Step4Report.vue to alternate Chinese/English wording (single-file edit).
  • A new pipeline stage is added in Step2EnvSetup.vue. → Add a row to STAGE_PHASE_MAP.
  • locales/en.json or locales/zh.json shape changes (new namespace, key removal). → Re-run audit-i18n-strings.sh and the parity diff.

Architecture

Existing Architecture Analysis

The frontend already uses vue-i18n 11 throughout four of the five files. The pattern is uniform: <template> uses $t(), <script setup> uses const { t } = useI18n() then t(). The file frontend/src/i18n/index.js constructs the createI18n instance from import.meta.glob('/locales/*.json'). There is no SSR, no async-loaded translation chunks, and no per-route message-tree splitting — all keys are eagerly loaded.

Process.vue is the sole outlier: it has zero i18n adoption today. It will receive the entire pattern (a useI18n() import, then template + script substitutions). No structural change to the file beyond the substitutions and the new import.

Architecture Pattern & Boundary Map

flowchart LR
    subgraph FE[Frontend (Vue 3 + vue-i18n 11)]
        direction TB
        Process[views/Process.vue]
        Step2[components/Step2EnvSetup.vue]
        Step3[components/Step3Simulation.vue]
        Step4[components/Step4Report.vue]
        Step5[components/Step5Interaction.vue]
        i18nIdx[i18n/index.js]
        Audit[scripts/audit-i18n-strings.sh]
    end
    subgraph Locales[/locales/]
        En[en.json]
        Zh[zh.json]
    end
    subgraph BE[Backend (Python)]
        ZepTools[services/zep_tools.py]
    end

    Process -->|t-key lookup| i18nIdx
    Step2 -->|t-key lookup| i18nIdx
    Step3 -->|t-key lookup| i18nIdx
    Step4 -->|t-key lookup| i18nIdx
    Step5 -->|t-key lookup| i18nIdx
    i18nIdx --> En
    i18nIdx --> Zh

    ZepTools -. emits Chinese markers .-> Step4
    Step4 -- REPORT_MARKERS regex set --> Step4

    Audit -. greps CJK literals minus allowlist .-> Process
    Audit -. greps CJK literals minus allowlist .-> Step2
    Audit -. greps CJK literals minus allowlist .-> Step3
    Audit -. greps CJK literals minus allowlist .-> Step4
    Audit -. greps CJK literals minus allowlist .-> Step5

Architecture Integration:

  • Selected pattern: pure extension of the existing vue-i18n pattern, with two micro-refactors: an in-file STAGE_PHASE_MAP constant (Step2) and an in-file REPORT_MARKERS constants block (Step4). Both keep responsibility inside the matching Step component, in line with the steering principle "pipeline-stage logic lives in the matching Step component."
  • Domain/feature boundaries: each file owns its own substitutions; locales/{en,zh}.json are the shared translation surface; audit-i18n-strings.sh is the verification surface.
  • Existing patterns preserved: const { t } = useI18n() in <script setup>; $t() in <template>; namespace structure (step1.*, process.* new, graph.* reused).
  • New components rationale: audit-i18n-strings.sh is the only new file. It exists because the original audit script referenced in the ticket has been deleted; without a verifier, Requirement 6 cannot be discharged.
  • Steering compliance: matches tech.md ("User-visible strings live in repo-root /locales/*.json") and structure.md ("Pipeline-aligned modules"). No new dependencies. No new abstractions across the layer boundary.

Technology Stack

Layer Choice / Version Role in Feature Notes
Frontend / CLI vue-i18n 11 (already adopted) Translates user-visible strings via the t() API Global instance constructed in frontend/src/i18n/index.js; default + fallback locale 'zh'
Backend / Services (read-only reference) Python 3.11 / Flask 3 Source of Chinese marker strings parsed by Step4Report.vue backend/app/services/zep_tools.py is the canonical emitter; not modified by this spec
Data / Storage locales/en.json, locales/zh.json Translation source 1031 lines each (aligned per #20); new keys added under existing namespaces
Messaging / Events n/a
Infrastructure / Runtime bash 5+ (or Node 18+) for audit-i18n-strings.sh Verifies Requirement 6 Pure ripgrep + jq one-liner

File Structure Plan

Directory Structure

frontend/
├── src/
│   ├── components/
│   │   ├── Step2EnvSetup.vue          # MODIFIED — add STAGE_PHASE_MAP, drop Chinese stage equality checks
│   │   ├── Step3Simulation.vue        # MODIFIED — replace '启动失败' fallback with t('step3.startFailed')
│   │   ├── Step4Report.vue            # MODIFIED — add REPORT_MARKERS block, route literals through t()
│   │   └── Step5Interaction.vue       # MODIFIED — route chat-history templating through t()
│   ├── views/
│   │   └── Process.vue                # MODIFIED — add useI18n() import, route every flagged literal through t()
│   └── i18n/
│       └── index.js                   # UNCHANGED
└── scripts/
    └── audit-i18n-strings.sh          # NEW — Requirement 6 verifier

locales/
├── en.json                            # MODIFIED — add new keys for process.*, step3.startFailed (if missing), step4.*, step5.*
└── zh.json                            # MODIFIED — mirror new keys with the original Chinese wording

Modified Files

  • frontend/src/views/Process.vue — add import { useI18n } from 'vue-i18n' and const { t } = useI18n() in <script setup>. Replace every flagged Chinese literal in template + script with t('process.<key>') or t('graph.<key>') (where graph.* already covers the surface). Substitute fallback literals ('未命名't('process.fallbackNodeName'), '未知't('common.unknown') — already exists). Replace the alert('环境搭建功能开发中...') with alert(t('process.envSetupComingSoon')).
  • frontend/src/components/Step2EnvSetup.vue — add const STAGE_PHASE_MAP = { 'generating_profiles': 1, '生成Agent人设': 1, 'generating_config': 2, '生成模拟配置': 2, 'copying_scripts': 2, '准备模拟脚本': 2 } near other module constants. Rewrite the watch(currentStage, …) body to phase.value = STAGE_PHASE_MAP[newStage] ?? phase.value, with the t('log.startGeneratingConfig') log emission gated on the transition into phase 2 (matching today's behaviour). The Chinese console-warning strings are non-user-visible — leave alone (out of scope per ticket boundary).
  • frontend/src/components/Step3Simulation.vue — replace '启动失败' literal at line 423/427 with t('step3.startFailed') (key already exists in en.json per the locale audit; confirm during implementation).
  • frontend/src/components/Step4Report.vue — add a REPORT_MARKERS constants block at the top of <script setup>. Replace each inline Chinese regex with a reference into REPORT_MARKERS (e.g., text.match(REPORT_MARKERS.analysisQuery.regex)). Route the user-visible Chinese literals ('选择理由', '等待开始', '--' no-data placeholder if it surfaces) through t('step4.<key>'). The interview.redditAnswer !== '(该平台未获得回复)' checks become !REPORT_MARKERS.noReply.is(interview.redditAnswer). Log-classifier literals stay (deliberate, allowlisted).
  • frontend/src/components/Step5Interaction.vue — replace lines 721/723 with t('step5.chatRolePrompter') / t('step5.chatRoleYou') / t('step5.chatHistoryPrefix', { history: historyContext }) / t('step5.chatNewQuestionPrefix', { message }). The Chinese phrasing is preserved exactly in zh.json so the production Chinese path is byte-identical.
  • locales/en.json and locales/zh.json — add the new keys grouped under existing namespaces. Both files updated in lockstep; zh.json carries the exact Chinese wording removed from the source files.
  • frontend/scripts/audit-i18n-strings.sh — new shell script (≤30 lines). Greps the five files for any non-allowlisted CJK code points and exits non-zero on hits.

System Flows

Flow 1: Audit verifier (Requirement 6)

flowchart TD
    Start[Run audit-i18n-strings.sh] --> Grep[ripgrep CJK regex over the 5 files]
    Grep --> Filter[Filter out allowlist tokens<br/>e.g. REPORT_MARKERS literals,<br/>log severity literals]
    Filter --> HasHits{Any remaining hits?}
    HasHits -- yes --> Report[Print file:line:snippet for each hit]
    Report --> Exit1[exit 1]
    HasHits -- no --> Parity[Parity check: en.json vs zh.json keys]
    Parity --> ParityOk{Sets equal?}
    ParityOk -- yes --> Exit0[exit 0]
    ParityOk -- no --> ReportParity[Print missing keys per file]
    ReportParity --> Exit1

The allowlist is encoded in the script itself (a small array of literal strings the verifier accepts). REPORT_MARKERS constants and the log-severity literals ('错误', '警告') are the only entries. Any future addition requires editing the allowlist explicitly — no implicit acceptance.

Flow 2: Stage-watcher (Requirement 4)

flowchart LR
    Backend[Backend emits stage string<br/>e.g. '生成Agent人设' or 'generating_profiles'] --> Watch[Step2EnvSetup.vue watcher]
    Watch --> Map[STAGE_PHASE_MAP[newStage]]
    Map --> Found{Match?}
    Found -- yes --> Update[phase.value = mapped phase]
    Found -- no --> Noop[phase.value unchanged]
    Update --> SideEffect[Trigger startConfigPolling / addLog as today]

The map covers all three stage names in both their Chinese display form and snake_case identifier form (six entries total). When the backend translation lands an English form (e.g. 'generating profiles' or any other wording), one row is added to the map.

Requirements Traceability

Requirement Summary Components Interfaces Flows
1.1 Process.vue template/script literals routed through t() Process.vue useI18n().t
1.2 Process.vue renders no Chinese under en locale Process.vue, audit-i18n-strings.sh t() lookup Flow 1
1.3 Process.vue Chinese unchanged under zh locale Process.vue, locales/zh.json t() fallback
1.4 Fallback names use translated keys Process.vue t('process.fallbackNodeName')
1.5 New literals added to both locales locales/{en,zh}.json Flow 1 (parity check)
2.1 Step3 '启动失败' routed through t() Step3Simulation.vue t('step3.startFailed')
2.2 Step4 user-visible literals routed through t() Step4Report.vue t('step4.*')
2.3 Step5 chat-history templating routed through t() Step5Interaction.vue t('step5.chat*')
2.4 Step components render no Chinese under en All four t() Flow 1
2.5 Step2 stage transitions preserved Step2EnvSetup.vue STAGE_PHASE_MAP Flow 2
2.6 Existing useI18n() is the translation utility All four
3.1 New keys exist in both locale files locales/{en,zh}.json Flow 1
3.2 zh.json preserves exact Chinese wording locales/zh.json
3.3 en.json carries idiomatic English locales/en.json
3.4 Keys grouped under existing namespaces locales/{en,zh}.json
3.5 Locale files structurally aligned locales/{en,zh}.json Flow 1
4.1 Stage matcher accepts Chinese, snake_case, future English Step2EnvSetup.vue STAGE_PHASE_MAP Flow 2
4.2 Chinese builds keep current behaviour Step2EnvSetup.vue STAGE_PHASE_MAP Flow 2
4.3 Removing Chinese stage names later doesn't break Step2EnvSetup.vue STAGE_PHASE_MAP Flow 2
4.4 Single source of truth for stage matching Step2EnvSetup.vue STAGE_PHASE_MAP Flow 2
5.15.11 Bilingual / future-bilingual parser tolerance Step4Report.vue REPORT_MARKERS
6.1 Verifier reports zero hard-coded Chinese audit-i18n-strings.sh shell exit code Flow 1
6.2 Verifier check documented design.md (this section), audit-i18n-strings.sh header Flow 1
6.3 Deliberate Chinese tokens allowlisted audit-i18n-strings.sh inline allowlist Flow 1
6.4 Verifier runs locally in <1 minute, no backend audit-i18n-strings.sh shell Flow 1

Components and Interfaces

Component Domain/Layer Intent Req Coverage Key Dependencies (P0/P1) Contracts
Process.vue (modified) Frontend / view Workflow orchestrator; rendering pipeline-stage UI 1.11.5, 3.13.5 vue-i18n (P0); locales/*.json (P0) State
Step2EnvSetup.vue (modified) Frontend / step component Setup step; tracks backend stage transitions 2.5, 2.6, 4.14.4 vue-i18n (P0) State
Step3Simulation.vue (modified) Frontend / step component Simulation runner UI 2.1, 2.4, 2.6 vue-i18n (P0) State
Step4Report.vue (modified) Frontend / step component Report renderer; parses backend report markdown 2.2, 2.4, 2.6, 5.15.11 vue-i18n (P0); backend/app/services/zep_tools.py markers (P0, read-only) State
Step5Interaction.vue (modified) Frontend / step component Chat / interview UI 2.3, 2.4, 2.6 vue-i18n (P0) State
locales/en.json, locales/zh.json Frontend / data Translation source; structurally aligned 3.13.5 Data
audit-i18n-strings.sh Frontend / tooling Local verification of remaining CJK literals across the 5 files 6.16.4 bash 5 + ripgrep + jq (P0) CLI

Frontend / Step component

Step4Report.vueREPORT_MARKERS constants block

Field Detail
Intent Centralise the 29 backend-coupled Chinese marker strings/regexes into a single top-of-file constant block, so future backend translation requires a single-file edit.
Requirements 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 5.10, 5.11

Responsibilities & Constraints

  • The constants block owns every regex that pattern-matches against backend-emitted markdown structure (section headers, counter lines, interview fields, no-reply markers, search-query markers, log-severity tokens).
  • Each constant carries an inline comment naming the canonical backend source line in zep_tools.py so future maintainers can find it.
  • Each constant exposes either .regex (a RegExp) or .is(value) (an exact-match predicate).
  • For markers whose translated wording is decided (today: only noReply's English equivalent if/when it lands), use a single RegExp with alternation: /(?:(该平台未获得回复)|\(该平台未获得回复\)|\[无回复\])/. For markers whose translated wording is undecided, encode the Chinese form only and document the backend coordination point.
  • The constants block does not import anything from vue-i18n — these are backend-coupled patterns, not user-visible strings.

Dependencies

  • Inbound: every parser function in Step4Report.vue (P0)
  • Outbound: none
  • External: read-only knowledge of backend-emitted markers in backend/app/services/zep_tools.py:47-1720 (P0)

Contracts: State [x]

State Management
// In <script setup>:
const REPORT_MARKERS = Object.freeze({
  // backend: zep_tools.py:175 — f"分析问题: {self.query}"
  analysisQuery:    { regex: /分析问题:\s*(.+?)(?:\n|$)/ },
  // backend: zep_tools.py:176 — f"预测场景: {self.simulation_requirement}"
  predictionScene:  { regex: /预测场景:\s*(.+?)(?:\n|$)/ },
  // backend: zep_tools.py:178 — f"- 相关预测事实: {self.total_facts}条"
  factsCount:       { regex: /相关预测事实:\s*(\d+)/ },
  entitiesCount:    { regex: /涉及实体:\s*(\d+)/ },
  relationsCount:   { regex: /关系链:\s*(\d+)/ },
  subQueriesHeader: { regex: /### 分析的子问题\n/ },
  keyFactsHeader:   { regex: /### 【关键事实】/ },
  coreEntitiesHdr:  { regex: /### 【核心实体】\n/ },
  entitySummary:    { regex: /摘要:\s*"?(.+?)"?(?:\n|$)/ },
  relatedFactsCnt:  { regex: /相关事实:\s*(\d+)/ },
  relationChainHdr: { regex: /### 【关系链】\n/ },
  activeFactsCnt:   { regex: /当前有效事实:\s*(\d+)/ },
  activeFactsHdr:   { regex: /### 【当前有效事实】/ },
  historicalHdr:    { regex: /### 【历史\/过期事实】/ },
  involvedEntities: { regex: /### 【涉及实体】\n/ },
  interviewTopic:   { regex: /\*\*采访主题:\*\*\s*(.+?)(?:\n|$)/ },
  interviewCount:   { regex: /\*\*采访人数:\*\*\s*(\d+)\s*\/\s*(\d+)/ },
  selectionReason:  { regex: /### 采访对象选择理由\n/ },
  agentBio:         { regex: /_简介:\s*([\s\S]*?)_\n/ },
  twitterAnswer:    { regex: /【Twitter平台回答】\n?/ },
  redditAnswer:     { regex: /【Reddit平台回答】\n?/ },
  keyQuotesHeader:  { regex: /\*\*关键引言:\*\*\n/ },
  interviewSummary: { regex: /### 采访摘要与核心观点\n/ },
  searchQuery:      { regex: /搜索查询:\s*(.+?)(?:\n|$)/ },
  relatedFactsHdr:  { regex: /### 相关事实:\n/ },
  relatedEdgesHdr:  { regex: /### 相关边:\n/ },
  relatedNodesHdr:  { regex: /### 相关节点:\n/ },
  // No-reply marker — checked as exact-match predicate, not a regex.
  // backend: zep_tools.py:1424-1425
  noReply: {
    is(value) {
      return value === '(该平台未获得回复)'
          || value === '(该平台未获得回复)'
          || value === '[无回复]'
    },
  },
  // Log severity classification — backend logs may interleave English and Chinese severity
  // markers (e.g. Python's logging emits 'ERROR' but legacy logs include '错误'/'警告').
  // Kept bilingual; deliberate per Requirement 5.11.
  logSeverity: {
    isError(line)   { return line.includes('ERROR')   || line.includes('错误') },
    isWarning(line) { return line.includes('WARNING') || line.includes('警告') },
  },
})
  • Preconditions: REPORT_MARKERS is constructed once at module-evaluation time (Object.freeze to prevent accidental mutation).
  • Postconditions: every parser function in the file references this object instead of literal regex/string. The diff for each parser is mechanical (text.match(/分析问题:\s*…/) → text.match(REPORT_MARKERS.analysisQuery.regex)).
  • Invariants: the file contains no Chinese regex literals outside this block. The audit allowlist (audit-i18n-strings.sh) explicitly accepts the literals inside this block; any new Chinese literal added elsewhere in the file is flagged.

Implementation Notes

  • Integration: replace each call site one at a time; a quick npm run dev smoke test (open a finished project, view its report, scroll through interviews and search results) confirms parity. No unit tests are added — the project's testing posture is "exercise the feature in a browser" per tech.md.
  • Validation: a sentinel regex for the audit (e.g. /[一-鿿]/u) confirms zero CJK literals outside the allowlist.
  • Risks: missing a parser site → silent regression. Mitigation: audit script catches any inline Chinese regex; manual smoke test confirms a real report renders identically.

Step2EnvSetup.vueSTAGE_PHASE_MAP

Field Detail
Intent Replace three string-equality checks with a single source-of-truth lookup, accepting Chinese, snake_case, and future English stage names.
Requirements 4.1, 4.2, 4.3, 4.4, 2.5, 2.6

State Management

const STAGE_PHASE_MAP = Object.freeze({
  '生成Agent人设':       1,
  'generating_profiles': 1,
  '生成模拟配置':        2,
  'generating_config':   2,
  '准备模拟脚本':        2,
  'copying_scripts':     2,
})

watch(currentStage, (newStage, oldStage) => {
  const newPhase = STAGE_PHASE_MAP[newStage]
  if (newPhase === undefined) return
  phase.value = newPhase
  if (newPhase === 2 && STAGE_PHASE_MAP[oldStage] !== 2 && !configTimer) {
    addLog(t('log.startGeneratingConfig'))
    startConfigPolling()
  }
})
  • Preconditions: currentStage is a string emitted by the backend stage tracker.
  • Postconditions: phase.value updates exactly when currentStage is a recognised stage name; otherwise unchanged.
  • Invariants: adding a new stage requires only adding a row to STAGE_PHASE_MAP; no other change to Step2EnvSetup.vue.

Frontend / data

locales/en.json, locales/zh.json — new keys

The new keys are listed below as a single design contract. Implementation sequences them by file (Process → Step3 → Step4 → Step5) to keep PR review chunks reviewable.

{
  "process": {
    "buildTitle": "Graph Build / 图谱构建",
    "graphPanelTitle": "Real-time Knowledge Graph / 实时知识图谱",
    "nodes": "nodes / 节点",
    "edges": "relations / 关系",
    "refreshGraph": "Refresh graph / 刷新图谱",
    "exitFullscreen": "Exit fullscreen / 退出全屏",
    "enterFullscreen": "Fullscreen / 全屏显示",
    "realtimeUpdating": "Updating in real time… / 实时更新中…",
    "graphLoading": "Loading graph data… / 图谱数据加载中…",
    "waitingOntology": "Waiting for ontology generation / 等待本体生成",
    "waitingOntologyHint": "Graph build will start automatically once ontology generation completes / 生成完成后将自动开始构建图谱",
    "graphBuildingTitle": "Graph build in progress / 图谱构建中",
    "graphBuildingHint": "Data will appear shortly… / 数据即将显示…",
    "buildFlow": "Build flow / 构建流程",
    "ontologyGeneration": "Ontology generation / 本体生成",
    "interfaceNote": "Interface / 接口说明",
    "ontologyDescription": "Upload documents; the LLM analyses them and automatically generates an ontology (entity types + relation types) suitable for opinion simulation / 上传文档后LLM分析文档内容自动生成适合舆论模拟的本体结构实体类型 + 关系类型)",
    "generationProgress": "Generation progress / 生成进度",
    "generatedEntityTypes": "Generated entity types / 生成的实体类型",
    "generatedRelationTypes": "Generated relation types / 生成的关系类型",
    "waitingOntologyShort": "Waiting for ontology generation… / 等待本体生成…",
    "graphBuildSection": "Graph build / 图谱构建",
    "graphBuildDescription": "Using the generated ontology, chunks the documents and calls the Zep API to build the knowledge graph (entities + relations) / 基于生成的本体,将文档分块后调用 Zep API 构建知识图谱,提取实体和关系",
    "waitingOntologyComplete": "Waiting for ontology generation to finish… / 等待本体生成完成…",
    "entityNodes": "Entity nodes / 实体节点",
    "relationEdges": "Relation edges / 关系边",
    "entityTypes": "Entity types / 实体类型",
    "buildComplete": "Build complete / 构建完成",
    "buildCompleteHint": "Ready to proceed to the next step / 准备进入下一步骤",
    "enterEnvSetup": "Enter environment setup / 进入环境搭建",
    "projectInfo": "Project info / 项目信息",
    "projectName": "Project name / 项目名称",
    "projectId": "Project ID / 项目ID",
    "graphId": "Graph ID / 图谱ID",
    "simulationRequirement": "Simulation requirement / 模拟需求",
    "buildFailed": "Build failed / 构建失败",
    "buildSuccess": "Build complete / 构建完成",
    "buildInProgress": "Graph build in progress / 图谱构建中",
    "ontologyInProgress": "Ontology generation in progress / 本体生成中",
    "buildInitializing": "Initializing… / 初始化中",
    "envSetupComingSoon": "Environment setup feature coming soon… / 环境搭建功能开发中…",
    "stepCompleted": "Completed / 已完成",
    "stepInProgress": "In progress / 进行中",
    "stepWaiting": "Waiting / 等待中",
    "noFilesError": "No pending uploads. Please return to the home page and start over. / 没有待上传的文件,请返回首页重新操作",
    "uploadingFiles": "Uploading files and analysing documents… / 正在上传文件并分析文档...",
    "ontologyGenerationFailed": "Ontology generation failed / 本体生成失败",
    "projectInitFailedPrefix": "Project initialization failed: / 项目初始化失败: ",
    "loadProjectFailed": "Failed to load project / 加载项目失败",
    "loadProjectFailedPrefix": "Failed to load project: / 加载项目失败: ",
    "processingFailed": "Processing failed / 处理失败",
    "graphBuildStarting": "Starting graph build… / 正在启动图谱构建...",
    "graphBuildTaskStarted": "Graph build task started… / 图谱构建任务已启动...",
    "graphBuildStartFailed": "Failed to start graph build / 启动图谱构建失败",
    "graphBuildStartFailedPrefix": "Failed to start graph build: / 启动图谱构建失败: ",
    "graphProcessing": "Processing… / 处理中...",
    "graphBuildComplete": "Build complete; loading graph… / 构建完成,正在加载图谱...",
    "graphBuildFailedPrefix": "Graph build failed: / 图谱构建失败: ",
    "waitingGraphData": "Waiting for graph data… / 等待图谱数据...",
    "fallbackNodeName": "Unnamed / 未命名"
  },
  "step3": {
    "startFailed": "Failed to start / 启动失败"
  },
  "step4": {
    "selectionReason": "Selection reason / 选择理由",
    "awaitingStart": "Awaiting start / 等待开始"
  },
  "step5": {
    "chatRolePrompter": "Questioner / 提问者",
    "chatRoleYou": "You / 你",
    "chatHistoryPrefix": "Here is our previous conversation:\n{history}\n\nMy new question is: {message} / 以下是我们之前的对话:\n{history}\n\n现在我的新问题是{message}"
  }
}

Note: the above table is a content sketch — the actual en.json and zh.json entries each carry their own language only (no "/ 中文" sidecars). The table format is purely for reviewer convenience; the implementation tasks split it into language-specific entries.

Frontend / tooling

frontend/scripts/audit-i18n-strings.sh

Field Detail
Intent Local verifier; greps the five files for non-allowlisted CJK literals and confirms en.json/zh.json key parity.
Requirements 6.1, 6.2, 6.3, 6.4

Responsibilities & Constraints

  • Greps a regex matching CJK code points ([一-鿿 -〿＀-￯]) over the five files only.
  • Filters out lines whose grep match is fully contained in the allowlist (the REPORT_MARKERS constants block range, the bilingual log-severity helper, the i18n message tables in locales/, and any other deliberate token annotated with a // i18n-allow:<reason> trailing comment).
  • Runs jq -S 'paths(scalars) | join(".")' locales/en.json | sort -u against the same for zh.json and diffs the result; reports missing keys.
  • Exits 0 on success, 1 with a human-readable list on failure.

Dependencies

  • External: bash 5+, ripgrep, jq.

Contracts: CLI [x]

CLI Contract
$ bash frontend/scripts/audit-i18n-strings.sh
# (no output) → exit 0
# OR
# frontend/src/views/Process.vue:42: <some Chinese literal>
# locale parity: missing in en.json: foo.bar
# exit 1

Implementation Notes

  • Integration: invoked manually before opening a PR. Not added to CI by this spec (out of boundary).
  • Validation: dogfooded by running it before and after the file changes — the diff between the two runs is the spec's progress meter.
  • Risks: the allowlist mechanism could be abused. Mitigation: keep the allowlist short and explicit (no glob patterns; the exact literal strings only).

Data Models

Logical Data Model

The only "data" introduced by this spec is the new entries in locales/en.json and locales/zh.json. Their structure follows the existing namespaced JSON shape ({ namespace: { key: "value", … } }). No schema changes; no new namespaces beyond process.*.

Data Contracts & Integration

API Data Transfer: none.

Event Schemas: none.

Cross-Service Data Management: none.

The only cross-component "data contract" is the implicit shape of REPORT_MARKERS (each entry exposes either .regex or .is(...)). It is private to Step4Report.vue.

Error Handling

Error Strategy

This spec is presentation-layer only; there is no new error path. Existing error paths are preserved:

  • A missing translation key falls back through vue-i18n's built-in fallback chain to the 'zh' fallback locale; if the key is missing in both files, vue-i18n returns the key string itself. The audit script catches the structural divergence before runtime.
  • A backend marker that doesn't match any REPORT_MARKERS.* regex returns null from the parser, which the existing rendering logic handles (renders the section as-is or skips it). No new error class.

Error Categories and Responses

  • User Errors: n/a.
  • System Errors: n/a.
  • Business Logic Errors: an unrecognised stage name in STAGE_PHASE_MAP is silently ignored (today's behaviour). The watcher does not throw.

Monitoring

No new logs. The existing addLog(t('log.startGeneratingConfig')) call is preserved.

Testing Strategy

The project's testing posture is "exercise the feature in a browser" (per tech.md). The implementation tasks include:

  • Smoke tests (manual):

    1. Switch to en locale; load a finished project; walk Step1 → Step5; visually confirm no Chinese in DOM (excluding backend-emitted content currently in Chinese).
    2. Switch to zh locale; same walkthrough; visually confirm everything matches the production Chinese text.
    3. Open a finished project's report; scroll through key facts, core entities, relation chains, sub-queries, interview answers (Twitter + Reddit), and search results. Confirm parity with main branch behaviour.
    4. Trigger the chat flow in Step5 with one or two follow-up messages on each locale; confirm the prompt strings the LLM sees are well-formed.
  • Audit verification:

    1. Run bash frontend/scripts/audit-i18n-strings.sh; expect exit 0 and no output.
    2. Run wc -l locales/en.json locales/zh.json; expect equal line counts (pre-#20 invariant maintained).
  • Regression (automated): none added — the project doesn't have a frontend test harness, and adding one is explicitly an out-of-scope decision per tech.md.

Optional Sections

(Security, performance, scalability, migration are not relevant to this spec.)

Supporting References

  • research.md — discovery findings, design decisions, risk register.
  • requirements.md — EARS-format acceptance criteria.
  • backend/app/services/zep_tools.py:47-1720 — canonical source of every backend marker the frontend parses.
  • frontend/src/i18n/index.js — the vue-i18n instance configuration (no changes).
  • Issue #25 / .kiro/specs/i18n-report-agent-prompts/ — the downstream backend prompt translation; future trigger for editing REPORT_MARKERS.