/**
 * i18n — tiny, flat-keyed translation store.
 *
 * Design choices:
 *   - Single dictionary per language, keyed by dot-path strings (e.g.
 *     `nav.memories`). Keeps lookup O(1) and makes ad-hoc string
 *     interpolation trivial.
 *   - Language preference persists in localStorage; default language
 *     is inferred from `navigator.language` (zh-* → zh, else en).
 *   - Uses @preact/signals so components re-render automatically on
 *     language switch without subscription plumbing.
 *
 * Adding a key:
 *   1. Add it to both `en` and `zh` dictionaries.
 *   2. Use `t("your.key")` in a component. TypeScript enforces the
 *      key exists because `TranslationKey` is derived from `en`.
 */
import { signal, computed } from "@preact/signals";

// ─── Dictionaries ───────────────────────────────────────────────────────
// English is the source of truth for the key set. When adding a key,
// add to English first so TypeScript can derive the union type.

const en = {
  // Navigation (sidebar).
  "nav.overview": "Overview",
  "nav.memories": "Memories",
  "nav.tasks": "Tasks",
  "nav.skills": "Skills",
  "nav.policies": "Experiences",
  "nav.worldModels": "Environment knowledge",
  "nav.analytics": "Analytics",
  "nav.logs": "Logs",
  "nav.import": "Import / Export",
  "nav.admin": "Team Admin",
  "nav.settings": "Settings",
  "nav.help": "Help",
  "nav.section.work": "Workspace",
  "nav.section.insights": "Insights",
  "nav.section.system": "System",

  // Header.
  "header.brand": "MemOS",
  "header.subtitle": "Memory viewer",
  "header.search.placeholder": "Search anywhere…",
  "header.search.noResults": "No results found",
  "header.search.viewAll": "View all",
  "header.lang.en": "EN",
  "header.lang.zh": "中",
  "header.theme.light": "Switch to light",
  "header.theme.dark": "Switch to dark",
  "header.theme.auto": "Match system",
  "header.notif.title": "Notifications",
  "header.notif.empty": "No notifications",
  "header.notif.clear": "Clear all",
  "header.logout": "Sign out",
  "header.agent.current": "This viewer's agent",
  "header.agent.peers": "Other agents running on this machine",

  // Common.
  "common.search": "Search",
  "common.filter": "Filter",
  "common.clear": "Clear",
  "common.all": "All",
  "common.apply": "Apply",
  "common.cancel": "Cancel",
  "common.save": "Save",
  "common.reset": "Reset",
  "common.delete": "Delete",
  "common.download": "Download",
  "common.upload": "Upload",
  "common.export": "Export",
  "common.import": "Import",
  "common.refresh": "Refresh",
  "common.close": "Close",
  "common.back": "Back",
  "common.next": "Next",
  "common.prev": "Previous",
  "common.loading": "Loading…",
  "common.saving": "Saving…",
  "common.saved": "Saved",
  "common.empty": "Nothing here yet",
  "common.retry": "Retry",
  "common.more": "More",
  "common.never": "Never",
  "common.loadMore": "Load more",
  "common.selected": "{n} selected",
  "common.selectPage": "Select page",
  "common.deselectPage": "Deselect page",
  "common.deselect": "Deselect",
  "common.bulkDelete": "Delete selected",
  "common.bulkDelete.confirm": "Delete {n} selected items? This cannot be undone.",
  // Relative-time labels — used by the activity dashboard and any
  // future surface that wants "5 s ago" / "3 min ago" formatting.
  "common.justNow": "just now",
  "common.secondsAgo": "{n} s ago",
  "common.minutesAgo": "{n} min ago",
  "common.hoursAgo": "{n} h ago",
  "common.daysAgo": "{n} d ago",
  "pager.page": "Page {n}",
  "pager.pageOfAtLeast": "Page {n} / {total}+",
  "pager.pageOfTotal": "Page {n} / {total}",
  "pager.totalPerPage": "Total {total} items, {pageSize} per page",
  "pager.totalCompact": "{total} items · {pageSize}/page",
  "pager.pageSize.label": "Items per page",
  "pager.pageSize.option": "{pageSize} / page",
  "pager.jump.label": "Go to",
  "pager.jump.short": "Jump",
  "pager.jump.to": "Go to",
  "pager.jump.pageUnit": "page",
  "pager.jump.go": "Go",
  "pager.jump.goShort": "Go",

  // Settings extensions.
  "settings.test": "Test",
  "settings.hub.admin": "Team members",
  "settings.hub.status": "Hub status",
  "admin.approve": "Approve",
  "admin.deny": "Deny",
  "admin.remove": "Remove",
  "admin.remove.confirm": "Remove this member from the Hub? Their shared team content will be removed too.",
  "settings.test.ok": "Connection OK",
  "settings.apiKey.saved":
    "(already saved — leave blank to keep, type to replace)",
  "settings.saveAndRestart": "Save and restart",
  "settings.tab.account": "Account",
  "settings.skillEvolver.title": "Skill evolver model",
  "settings.skillEvolver.desc":
    "Dedicated model the agent uses to turn proven experiences into reusable skills. Leave blank to reuse the summarizer model.",
  "settings.skillEvolver.inherit":
    "Inherits from the summarizer model. Pick a provider to override.",
  "settings.account.protection": "Password protection",
  "settings.account.protection.desc":
    "Require a password before the viewer can be opened on this machine.",
  "settings.account.on": "Enabled",
  "settings.account.off": "Disabled",
  "settings.account.newPassword": "New password",
  "settings.account.confirm": "Confirm password",
  "settings.account.enable": "Enable",
  "settings.account.logout": "Sign out",
  "settings.account.resetHint":
    "Remove ~/.openclaw/memos-plugin/.auth.json to reset.",
  "settings.account.resetPassword": "Reset password",
  "settings.account.resetConfirm":
    "This will delete the saved password and log you out. On the next visit you'll be asked to set a new password. Continue?",
  "settings.account.resetConfirmBtn": "Yes, reset password",
  "settings.danger.title": "Danger Zone",
  "settings.danger.desc": "Irreversible operations — proceed with caution.",
  "settings.danger.clearAll": "Clear all data",
  "settings.danger.confirm":
    "This will permanently delete ALL memories, tasks, skills, policies, world models, and logs. Configuration will be preserved. This action cannot be undone!",
  "settings.danger.confirmBtn": "Yes, delete everything",

  // Auth.
  "auth.login.title": "Memory viewer locked",
  "auth.login.subtitle": "Enter the password to continue.",
  "auth.login.password": "Password",
  "auth.login.submit": "Unlock",
  "auth.setup.title": "Set a viewer password",
  "auth.setup.subtitle": "Protect your local memories before first use.",
  "auth.setup.newPassword": "New password",
  "auth.setup.confirm": "Confirm password",
  "auth.setup.submit": "Set password and enter",
  "auth.setup.hint":
    "The password is stored locally as a scrypt hash. Remove ~/.openclaw/memos-plugin/.auth.json to reset.",
  "auth.err.empty": "Password cannot be empty.",
  "auth.err.required": "Password is required.",
  "auth.err.tooShort": "Password is too short.",
  "auth.err.mismatch": "Passwords do not match.",
  "auth.err.badPassword": "Incorrect password.",

  // Restart overlay.
  "analytics.tools.title": "Tool response time",
  "analytics.tools.subtitle":
    "Per-tool response time and failure counts over the selected time range.",

  "restart.restarting": "Configuration saved. Service is restarting…",
  "restart.restarting.hermes":
    "Configuration saved. Closing the current Hermes session…",
  "restart.waitingUp": "Waiting for the service to come back online…",
  "restart.autoRefresh": "The page will refresh automatically once the service is ready.",
  "restart.failed": "Restart didn't complete — the service didn't come back in time.",
  "restart.failedHint.openclaw":
    "Try manually: openclaw gateway stop && openclaw gateway start",
  "restart.failedHint.hermes":
    "Try manually: stop the current Hermes session and rerun `hermes chat`",
  "common.selectAll": "Select all",
  "common.deleteSelected": "Delete selected",

  // Status pills.
  //
  // Unified lifecycle: candidate → active → archived. These three values
  // cover L2 policies ("经验"), Skills ("技能"), and L3 world models
  // ("环境认知"). Older aliases `probationary` / `retired` were merged
  // into `candidate` / `archived` in migration 012 — do not reintroduce
  // them.
  "status.active": "Active",
  "status.candidate": "Candidate",
  "status.archived": "Archived",
  "status.draft": "Draft",
  "status.completed": "Completed",
  "status.skipped": "Skipped",
  "status.running": "Running",
  "status.failed": "Failed",

  // Overview.
  "overview.title": "System overview",
  "overview.metric.memories": "Memories",
  "overview.metric.episodes": "Tasks",
  "overview.metric.policies": "Experiences",
  "overview.metric.worldModels": "Environment knowledge",
  "overview.metric.skills": "Skills",
  "overview.metric.llm": "Summary model",
  "overview.metric.embedder": "Embedding model",
  "overview.metric.skillEvolver": "Skill evolver model",
  "overview.metric.skillEvolver.inherit": "inherits from summary model",
  "overview.metric.model.unconfigured": "Not configured",
  "overview.metric.model.unreachable": "Unreachable",
  "overview.metric.model.reachable": "Reachable",
  "overview.metric.model.connected": "Connected",
  "overview.metric.model.connectedAt": "Last OK call at {ts}",
  "overview.metric.model.failed": "Last call failed",
  "overview.metric.model.idle": "Not called yet",
  "overview.metric.model.fallback": "Falling back to host model",
  "overview.metric.model.fallback.tooltip":
    "Primary provider unavailable, host LLM is handling the call. Original error: {msg}",
  "overview.metric.policies.breakdown": "{active} active · {candidate} candidate",
  "overview.metric.skills.breakdown": "{active} active · {candidate} candidate",
  // Live activity dashboard — the third row of the overview page.
  // Shows six per-category tiles (memory / experience / environment
  // knowledge / skill / retrieval / feedback) with a five-minute
  // sparkline and the most recent event in plain language.
  "overview.live.title": "Live activity",
  "overview.live.tile.count": "events in last 5 min",
  "overview.live.tile.empty": "No events in last 5 min",

  // Tile labels (also used by the per-event "category pill"). Keep
  // these in sync with overview.metric.* / nav.* labels — same noun,
  // different surface.
  "overview.live.cat.session": "Conversation",
  "overview.live.cat.task": "Task",
  "overview.live.cat.memory": "Memory",
  "overview.live.cat.experience": "Experience",
  "overview.live.cat.world": "Environment knowledge",
  "overview.live.cat.skill": "Skill",
  "overview.live.cat.retrieval": "Retrieval",
  "overview.live.cat.feedback": "Feedback",
  "overview.live.cat.system": "System",
  "overview.live.cat.hub": "Hub",

  // Per-event titles. Telegraphic noun-verb compounds (matching the
  // operational-log style the rest of the product uses), one per
  // CoreEventType. Detail text — IDs, counts, milliseconds — is
  // formatted in TS and concatenated to the title at render time.
  "overview.live.event.session.opened": "Session opened",
  "overview.live.event.session.closed": "Session ended",
  "overview.live.event.episode.opened": "Task started",
  "overview.live.event.episode.closed": "Task ended",
  "overview.live.event.trace.created": "Memory stored",
  "overview.live.event.trace.value_updated": "Memory updated",
  "overview.live.event.trace.priority_decayed": "Memory decayed",
  "overview.live.event.l2.candidate_added": "Experience candidate",
  "overview.live.event.l2.candidate_expired": "Experience candidate expired",
  "overview.live.event.l2.induced": "Experience generated",
  "overview.live.event.l2.associated": "Experience associated",
  "overview.live.event.l2.revised": "Experience revised",
  "overview.live.event.l2.boundary_shrunk": "Experience boundary shrunk",
  "overview.live.event.l3.abstracted": "Environment knowledge generated",
  "overview.live.event.l3.revised": "Environment knowledge updated",
  "overview.live.event.skill.crystallized": "Skill crystallised",
  "overview.live.event.skill.eta_updated": "Skill ETA updated",
  "overview.live.event.skill.boundary_updated": "Skill boundary updated",
  "overview.live.event.skill.archived": "Skill archived",
  "overview.live.event.skill.repaired": "Skill repaired",
  "overview.live.event.retrieval.triggered": "Retrieval triggered",
  "overview.live.event.retrieval.tier1.hit": "Tier 1 retrieval hit",
  "overview.live.event.retrieval.tier2.hit": "Tier 2 retrieval hit",
  "overview.live.event.retrieval.tier3.hit": "Tier 3 retrieval hit",
  "overview.live.event.retrieval.empty": "Retrieval empty",
  "overview.live.event.feedback.received": "Feedback received",
  "overview.live.event.feedback.classified": "Feedback classified",
  "overview.live.event.reward.computed": "Reward computed",
  "overview.live.event.decision_repair.generated": "Decision repair generated",
  "overview.live.event.decision_repair.validated": "Decision repair validated",
  "overview.live.event.hub.client_connected": "Hub client connected",
  "overview.live.event.hub.client_disconnected": "Hub client disconnected",
  "overview.live.event.hub.share_published": "Hub share published",
  "overview.live.event.hub.share_received": "Hub share received",
  "overview.live.event.system.started": "System started",
  "overview.live.event.system.shutdown": "System shutdown",
  "overview.live.event.system.error": "System error",
  "overview.live.event.system.config_changed": "Config changed",
  "overview.live.event.system.update_available": "Update available",

  // Detail templates. Many events share patterns (label + id, count +
  // latency, …) so the same template is reused across multiple types.
  "overview.live.detail.id": "{label} {id}",
  "overview.live.detail.idReason": "{label} {id} · {reason}",
  "overview.live.detail.candidate": "Candidate {sig}",
  "overview.live.detail.induced": "{sig} · from {n} successful tasks",
  "overview.live.detail.similarity": "{label} {id} · similarity {pct}%",
  "overview.live.detail.retrievalHit": "{count} hits · {ms}ms",
  "overview.live.detail.feedbackTone": "Tone: {tone}",
  "overview.live.detail.reward": "r = {r} · from {source}",
  "overview.live.detail.version": "v{version}",
  "overview.live.detail.raw": "{value}",

  // Host bridge status.
  "bridge.connected": "Memory bridge connected",
  "bridge.reconnecting": "Memory bridge connected",
  "bridge.disconnected": "Memory bridge disconnected",
  "bridge.unknown": "Memory bridge unknown",
  "bridge.tooltip": "Memory bridge: connection between Hermes and the local memory core",
  "bridge.tooltip.lastOk": "Last success: {ts}",
  "bridge.tooltip.lastError": "Last error: {msg}",

  // Memories.
  "memories.title": "Memories",
  "memories.subtitle": "Every message the agent remembered while you were working together.",
  "memories.search.placeholder": "Search memories (semantic)…",
  "memories.filter.role": "Role",
  "memories.filter.role.user": "User",
  "memories.filter.role.assistant": "Assistant",
  "memories.filter.role.tool": "Tool",
  "memories.filter.role.system": "System",
  "memories.filter.namespace": "Instance",
  "memories.filter.namespace.all": "All instances",
  "memories.filter.namespace.count": "{n} records",
  "memories.filter.owner": "All agents",
  "memories.filter.scope.device": "This device",
  "memories.filter.scope.team": "Team",
  "memories.filter.sort.newest": "Newest first",
  "memories.filter.sort.oldest": "Oldest first",
  "memories.filter.dateFrom": "From",
  "memories.filter.dateTo": "To",
  "memories.empty": "No memories match this filter.",
  "memories.empty.hint": "Try clearing filters or ask the agent to do something memorable.",
  "memories.act.expand": "Expand",
  "memories.act.collapse": "Collapse",
  "memories.act.edit": "Edit",
  "memories.act.share": "Share",
  "memories.act.unshare": "Unshare",
  "memories.act.delete": "Delete",
  "memories.bulk.selectPage": "Select page",
  "memories.bulk.deselect": "Deselect",
  "memories.bulk.delete": "Delete selected",
  "memories.bulk.export": "Copy as text",
  "memories.bulk.share": "Share as public",
  "memories.bulk.unshare": "Unshare",
  "memories.share.bulkDone": "Shared {n} items",
  "memories.share.bulkRemoved": "Unshared {n} items",
  "memories.edit.title": "Edit memory",
  "memories.edit.summary": "Summary",
  "memories.edit.user": "User text",
  "memories.edit.assistant": "Assistant text",
  "memories.edit.tags": "Tags (comma-separated)",
  "memories.edit.saved": "Memory updated",
  "memories.field.takeaway": "Reflection",
  "memories.field.summary": "Summary",
  "memories.card.reflection": "reflection",
  "memories.field.user": "User",
  "memories.field.assistant": "Assistant",
  "memories.field.ts": "Timestamp",
  "memories.field.value": "Value (V)",
  "memories.field.alpha": "Reflection weight (α)",
  "memories.field.priority": "Priority",
  "memories.field.rHuman": "Human feedback (R_human)",
  "memories.field.share": "Share",
  "memories.field.status": "Status",
  "memories.field.startedAt": "Started",
  "memories.field.endedAt": "Ended",
  "memories.field.createdAt": "Created",
  "memories.field.updatedAt": "Updated",
  "memories.field.session": "Session",
  "memories.field.rTask": "Task score (R_task)",
  "memories.field.eta": "Reliability (η)",
  "memories.field.gain": "Gain",
  "memories.field.support": "Support count",
  "memories.field.toolCalls": "Tool calls",
  "memories.field.episodeTimeline": "Steps in this task",
  "memories.field.steps": "Steps in this turn ({n})",
  "memories.card.steps": "{n} steps",
  "memories.score.skipped": "Scoring skipped",
  "memories.score.pending": "Pending score",
  // Tooltip helpers for memory metadata fields. Shown when the user
  // hovers the small "?" icon next to each label so they can find out
  // what the score means without leaving the drawer.
  "memories.help.value":
    "How important this memory looked when it was captured (0–1). Higher = the agent thought it was worth remembering.",
  "memories.help.alpha":
    "How much weight the agent gave to its own reflection on this memory (0–1).",
  "memories.help.priority":
    "Combined score that decides retrieval order. Higher = recalled earlier when searching memories.",
  "memories.help.rHuman":
    "User feedback signal for this turn (-1…1). Positive when you confirmed the assistant got it right, negative when you corrected it.",
  "memories.help.episodeTimeline":
    "Other memories captured during the same task, in chronological order. Click a step to jump to it.",
  "memories.help.share":
    "Visibility scope. Private is visible only to the creating agent; Public is visible to other agents in the same local agent framework; Hub is visible to the team.",
  "memories.detail.fromTask": "From task {id}",
  "memories.detail.oneMemory": "memory",
  "memories.detail.fallbackTitle": "Memory detail",
  "memories.share.title": "Share memory",
  "memories.share.scope": "Visibility",
  "memories.share.scope.private": "Private (creator agent only)",
  "memories.share.scope.public": "Public (same agent framework)",
  "memories.share.scope.hub": "Hub (team)",
  "memories.share.done": "Sharing updated",
  "memories.share.removed": "Share removed",
  "memories.delete.confirm": "Delete this memory? This cannot be undone.",
  "memories.delete.bulkConfirm": "Delete {n} memories? This cannot be undone.",
  "memories.delete.done": "Memory deleted",
  "memories.delete.bulkDone": "Deleted {n} memories",
  "memories.copy.done": "Copied {n} rows",

  // Experiences — patterns the agent has learned from past conversations.
  "policies.title": "Experiences",
  "policies.subtitle":
    "Patterns of action the agent has learned work well in specific situations.",
  "policies.search.placeholder": "Search experiences…",
  "policies.filter.all": "All",
  "policies.filter.candidate": "Candidate",
  "policies.filter.active": "Active",
  "policies.filter.archived": "Archived",
  "policies.empty": "No experiences yet.",
  "policies.empty.hint":
    "Experiences appear after a few successful conversations share the same approach.",
  "policies.lightweight.empty":
    "Only basic summary memories are being saved. Enable memory self-evolution to automatically distill reusable experiences.",
  "policies.col.trigger": "Trigger",
  "policies.col.procedure": "Procedure",
  "policies.col.verification": "Verification",
  "policies.col.boundary": "Boundary",
  "policies.act.activate": "Activate",
  "policies.act.archive": "Archive",
  "policies.act.retire": "Archive",
  "policies.act.reinstate": "Reinstate",
  "policies.act.candidate": "Candidate",
  "policies.col.title": "Title",
  "policies.delete.confirm": "Delete this experience? This cannot be undone.",
  "policies.guidance.title": "Decision guidance",
  "policies.guidance.empty": "No guidance yet — preference and anti-pattern entries will appear as the agent receives feedback on this experience.",
  "policies.guidance.add": "Add guidance",
  "policies.guidance.emptyInput": "Enter at least one prefer or avoid line.",
  "policies.guidance.preferInputHint": "One action per line",
  "policies.guidance.avoidInputHint": "One action per line",
  "policies.guidance.preferPlaceholder": "e.g. Use standard library when possible",
  "policies.guidance.avoidPlaceholder": "e.g. Don't wrap entire function in try/except",
  "policies.guidance.prefer": "Prefer",
  "policies.guidance.avoid": "Avoid",
  "policies.guidance.preferTitle":
    "Preferred actions learnt from positive feedback on this experience",
  "policies.guidance.avoidTitle":
    "Anti-patterns to avoid, synthesised from past failures",
  "policies.guidance.preferSection": "Preferred actions",
  "policies.guidance.avoidSection": "Avoid these actions",
  "policies.xlink.skills": "Linked skills",
  "policies.xlink.worldModels": "Linked environment knowledge",
  "policies.xlink.sourceEpisodes": "Source tasks",
  "skills.xlink.sourcePolicies": "Source experiences",
  "skills.xlink.sourceWorldModels": "Source environment knowledge",

  // Environment knowledge (aggregated understanding of the workspace).
  "worldModels.title": "Environment knowledge",
  "worldModels.subtitle":
    "What the agent has learned about your workflow — tools, file layouts, recurring constraints.",
  "worldModels.search.placeholder": "Search environment knowledge…",
  "worldModels.empty": "Nothing here yet.",
  "worldModels.empty.hint":
    "Environment knowledge builds up once several experiences share the same structure.",
  "worldModels.lightweight.empty":
    "Only basic summary memories are being saved. Enable memory self-evolution to automatically build environment knowledge.",
  "worldModels.col.body": "Description",
  "worldModels.structure.title": "Structured cognition (with evidence)",
  "worldModels.structure.environment": "Environment topology (ℰ)",
  "worldModels.structure.inference": "Inference rules (ℐ)",
  "worldModels.structure.constraints": "Constraints (𝒞)",
  "worldModels.col.policies": "Related experiences",
  "worldModels.field.id": "ID",
  "worldModels.field.version": "Version",
  "worldModels.version.title": "World model version (bumps on every L3 merge)",
  "worldModels.delete.confirm": "Delete this entry? This cannot be undone.",
  "worldModels.edit.title": "Title",
  "worldModels.edit.body": "Description",

  // Tasks.
  "tasks.title": "Tasks",
  "tasks.subtitle": "Each task is a focused span of conversation. Click a row to see what was said and whether an experience or skill was generated.",
  "tasks.search.placeholder": "Search tasks…",
  "tasks.empty": "No tasks yet.",
  "tasks.empty.filtered": "No tasks match the current filter.",
  "tasks.lightweight.empty":
    "Only basic summary memories are being saved. Enable memory self-evolution to automatically organize conversations into task views.",
  "tasks.untitled": "Untitled task",
  "tasks.detail.id": "Task {id}",
  "tasks.detail.fallbackTitle": "Task detail",
  "tasks.detail.meta": "Metadata",
  "tasks.detail.relatedMemories": "Related memories",
  "tasks.detail.relatedSkill": "Linked skill",
  "tasks.detail.share": "Share to team",
  "tasks.detail.unshare": "Unshare",
  "tasks.detail.chat": "Conversation",
  "tasks.detail.chat.empty": "No messages captured for this task.",
  "tasks.chat.role.user": "You",
  "tasks.chat.role.assistant": "Assistant",
  "tasks.chat.role.tool": "Tool",
  "tasks.chat.role.thinking": "Thinking",
  "tasks.chat.tool.assistantTextBefore": "Assistant text before tool",
  "tasks.chat.tool.thinking": "Thinking",
  "tasks.chat.tool.input": "Input",
  "tasks.chat.tool.output": "Output",
  "tasks.chat.tool.ok": "ok",
  "tasks.chat.tool.noPayload": "(no input or output recorded)",
  "tasks.chat.tool.parallelBatch": "⚡ {n} tools in parallel · {ms}ms wall-clock",
  "tasks.chat.tool.parallelBatch.savings": "(would have been {sum}ms in series)",
  "tasks.chat.expand": "Show more",
  "tasks.chat.collapse": "Show less",
  "tasks.skipped.default":
    "This conversation was too brief to generate a summary or score — the task won't appear in search results.",
  "tasks.failed.default":
    "This task was scored R={rTask} and counted as a failed exchange. Future recalls will down-rank similar attempts.",
  "tasks.skip.reason.tooFewTurns":
    "Not enough messages to learn from — at least a user question and an assistant reply are needed.",
  "tasks.skip.reason.tooFewExchanges":
    "Not enough conversation turns ({exchanges}); at least {min} complete user-assistant exchanges are required to generate a summary.",
  "tasks.skip.reason.noUserMessages":
    "This task has no user messages; it only contains system or tool-generated content.",
  "tasks.skip.reason.contentTooShort":
    "The conversation is too short ({chars} characters) to generate a meaningful summary.",
  "tasks.skip.reason.trivialUserContent":
    "The conversation only contains simple greetings or test data (for example hello, test, or ok), so no summary is needed.",
  "tasks.skip.reason.trivialBothSides":
    "Both the user and assistant messages are simple greetings or test data, so no summary is needed.",
  "tasks.skip.reason.toolHeavy":
    "This task is mostly tool output ({tools}/{total} messages) and does not contain enough user interaction to learn from.",
  "tasks.skip.reason.repeatedContent":
    "The conversation contains too much repeated content ({unique} unique messages / {total} user messages), so no useful information can be extracted.",
  "tasks.skip.reason.noAssistant":
    "The user message was captured but no assistant reply came back — the agent host may have crashed, filtered the turn, or been interrupted. Nothing to summarize yet.",
  "tasks.active.reason.interrupted":
    "This topic was interrupted before the assistant reply completed. It will stay in the same task until the next related message arrives.",
  "tasks.active.reason.paused":
    "This topic is paused after the session closed. If you continue the same topic soon, the next turn will be added to this task.",
  "tasks.skip.reason.abandoned":
    "The pipeline closed this task without a reward (e.g. the relation classifier decided the next turn was a brand-new task). Check the session timeline for the full arc.",
  "tasks.skip.reason.rewardPending":
    "The reward pipeline hasn't scored this task yet — either it's still running, or the scoring LLM failed silently. Check the Logs panel for `reward.*` events.",
  "tasks.skip.reason.default": "No useful content to remember from this conversation.",
  "tasks.fail.reason.withReward":
    "Scored R={rTask} — no new L2 policy or skill will be induced. Raw traces are kept as anti-pattern evidence; Decision Repair will use them to suggest avoidance next time.",
  "tasks.fail.reason.default": "Something went wrong while wrapping up this task.",
  "tasks.skill.queued": "Skill pipeline queued",
  "tasks.skill.generating": "Generating skill…",
  "tasks.skill.generated": "Skill generated",
  "tasks.skill.upgraded": "Skill upgraded",
  "tasks.skill.not_generated": "Below induction threshold",
  "tasks.skill.skipped": "Scored as negative example (R ≤ -0.5)",
  "tasks.skill.openSkill": "Open skill",
  "tasks.skillReason.queued.inProgress":
    "Task still in progress; skill pipeline has not started yet.",
  "tasks.skillReason.queued.rewardPending":
    "Reward scoring not yet complete; skill pipeline will start after scoring.",
  "tasks.skillReason.queued.policyPending":
    "Experience is not yet active — needs more supporting tasks to crystallize into a skill (current support: {support}, required: ≥ {skillMinSupport}).",
  "tasks.skillReason.queued.ready":
    "Experience is ready (gain={gain}, support={support}); skill crystallization will trigger automatically after the next reward scoring.",
  "tasks.skillReason.skipped":
    "Task scored significantly negative (R={rTask}), treated as counterexample; no L2 experience or skill will be derived, but L1 traces are retained as negative examples for future Decision Repair.",
  "tasks.skillReason.not_generated.belowThreshold":
    "Task score R={rTask} is below the induction threshold (≥ {threshold}) — the conversation was normal, but not strong enough to generalize into an L2 experience; similar tasks will accumulate over time.",
  "tasks.skillReason.not_generated.noPolicy":
    "No L2 experience induced yet — requires at least {minEpisodesForInduction} similar task(s) (minEpisodesForInduction) with V ≥ {minTraceValue} to trigger L2 induction, then support ≥ {skillMinSupport} and gain ≥ {skillMinGain} to crystallize into a skill.",
  "tasks.skillReason.generated":
    "Skill \"{skillName}\" crystallized from experience {policyId}.",
  "tasks.skillReason.upgraded":
    "Skill \"{skillName}\" upgraded from experience {policyId}.",
  "tasks.abandonReason.uncleanExit":
    "Plugin did not exit cleanly last time; incomplete tasks were automatically closed on startup.",

  // Skills.
  "skills.title": "Skills",
  "skills.subtitle": "Reusable capabilities the agent built from proven conversations.",
  "skills.search.placeholder": "Search skills…",
  "skills.filter.visibility": "Visibility",
  "skills.filter.visibility.public": "Public",
  "skills.filter.visibility.private": "Private",
  "skills.empty": "No skills yet.",
  "skills.empty.hint":
    "The agent turns a reliable experience into a callable skill once it's proven useful across several similar tasks.",
  "skills.lightweight.empty":
    "Only basic summary memories are being saved. Enable memory self-evolution to automatically crystallize reusable skills.",
  "skills.detail.desc": "Invocation guide",
  "skills.detail.files": "Skill files",
  "skills.detail.content": "SKILL.md content",
  "skills.detail.versions": "Version history",
  "skills.detail.related": "Related tasks",
  "skills.detail.download": "Download .zip",
  "skills.detail.makePublic": "Make public",
  "skills.detail.makePrivate": "Make private",
  "skills.detail.archive": "Archive",
  "skills.detail.version": "Version",
  "skills.detail.lastUpdated": "Updated {at}",
  "skills.detail.evolution": "Evolution timeline",
  "skills.detail.decisionGuidance": "Decision guidance (prefer / avoid)",
  "skills.detail.decisionGuidance.prefer": "Prefer",
  "skills.detail.decisionGuidance.avoid": "Avoid",
  "skills.detail.evidenceAnchors": "Evidence anchors ({n} traces)",
  "skills.detail.evolution.empty":
    "No evolution events recorded yet — the timeline fills in as the skill is crystallised, rebuilt, or archived.",
  "skills.act.delete.confirm": "Permanently delete skill \"{name}\"? This cannot be undone.",
  "skills.edit.name": "Name",
  "skills.edit.invocationGuide": "Invocation guide",
  "skills.version.title": "Skill version (bumps on every rebuild)",
  "skills.trials.pass": "{count} pass",
  "skills.trials.pass.label": "Trial pass",
  "skills.trials.pass.detail": "{passed} / {attempted}",
  "skills.usage.count": "{count} uses",
  "skills.usage.count.label": "Uses",
  "skills.usage.lastUsed": "used {at}",
  "skills.usage.lastUsed.label": "Last used",
  "skills.updated.ago": "updated {at}",
  "skills.timeline.kind.crystallized": "Crystallised",
  "skills.timeline.kind.started": "Crystallise start",
  "skills.timeline.kind.rebuilt": "Rebuilt",
  "skills.timeline.kind.etaUpdated": "η updated",
  "skills.timeline.kind.statusChanged": "Status changed",
  "skills.timeline.kind.archived": "Archived",
  "skills.timeline.kind.verifyFailed": "Verification failed",
  "skills.timeline.kind.failed": "Failed",

  // Analytics.
  "analytics.title": "Analytics",
  "analytics.subtitle":
    "Evolution dashboard — tasks → experiences → environment knowledge → skills.",
  "analytics.range.label": "Range",
  "analytics.range.7d": "7 days",
  "analytics.range.30d": "30 days",
  "analytics.range.90d": "90 days",
  "analytics.card.total": "Total memories",
  "analytics.card.writesToday": "Writes today",
  "analytics.card.sessions": "Sessions",
  "analytics.card.embeddings": "Embeddings",
  "analytics.chart.writes": "Memory writes per day",
  "analytics.chart.toolPerf": "Tool response time (per-minute avg)",
  "analytics.chart.skillEvolutions": "Skill crystallizations per day",
  "analytics.chart.skillEvolutions.empty":
    "No skills crystallized yet — keep the plugin running to collect evidence.",
  "analytics.axis.date": "Date",
  "analytics.axis.time": "Time",
  "analytics.axis.count": "Count",
  "analytics.axis.latencyMs": "Latency (ms)",
  "analytics.kpi.evolutionRate": "Skill evolution rate",
  "analytics.kpi.evolutionRate.hint": "tasks → skills conversion",
  "analytics.kpi.policyCoverage": "Policy activation rate",
  "analytics.kpi.policyCoverage.hint": "active / total L2 policies",
  "analytics.kpi.activePolicies": "Active policies",
  "analytics.kpi.activePolicies.hint": "L2 experiences currently promoted",
  "analytics.kpi.avgQuality": "Avg quality score",
  "analytics.kpi.avgQuality.hint": "mean gain across active policies",
  "analytics.kpi.skillsTotal": "Skills",
  "analytics.kpi.worldModels": "World models",
  "analytics.evolutions.title": "Recent skill evolutions",
  "analytics.evolutions.subtitle":
    "Latest crystallizations — which policy bucket minted which skill, newest first.",
  "analytics.evolutions.col.time": "Time",
  "analytics.evolutions.col.skill": "Skill",
  "analytics.evolutions.col.status": "Status",
  "analytics.evolutions.col.policies": "Source policies",
  "analytics.evolutions.empty":
    "No skills have crystallized in this window yet.",
  "analytics.tools.range.1h": "1 hour",
  "analytics.tools.range.6h": "6 hours",
  "analytics.tools.range.24h": "24 hours",
  "analytics.tools.range.3d": "3 days",
  "analytics.tools.range.7d": "7 days",
  "analytics.tools.range.30d": "30 days",
  "analytics.tools.empty":
    "No tool calls were recorded in this window.",
  "analytics.tools.chart.insufficient":
    "Not enough data points in this window to draw a trend chart.",
  "analytics.tools.legend.showAll": "Show all",
  "analytics.tools.unavailable.title": "Calls without latency data",
  "analytics.tools.unavailable.subtitle":
    "These tools were recorded, but their start/end timestamps were missing or identical, so they are not plotted as response-time data.",

  // Logs.
  "logs.title": "Logs",
  "logs.subtitle":
    "Structured trail of memos_search and memory_add calls, including local candidates, Hub candidates, and memories kept by the LLM filter.",
  "logs.filter.tool": "Tool",
  "logs.filter.level": "Level",
  "logs.autoRefresh": "Live",
  "logs.empty": "No log lines in this window.",
  "logs.empty.title": "No memory calls yet",
  "logs.empty.hint":
    "Rows show up here when the agent runs memos_search or captures a turn.",
  "logs.search.placeholder": "Search logs…",
  "logs.tag.memoryAdd": "Memory add",
  "logs.tag.memorySearch": "Memory search",
  "logs.tag.task": "Task",
  "logs.tag.skill": "Skill",
  "logs.tag.policy": "Experience",
  "logs.tag.world": "World model",
  "logs.tag.session": "Session",
  "logs.tag.system": "System",
  "logs.system.role": "{role} call failed",
  "logs.system.role.embedding": "Embedding model",
  "logs.system.role.llm": "Summary model",
  "logs.system.role.skillEvolver": "Skill evolver model",
  "logs.tool.search": "Search",
  "logs.tool.add": "Ingest",
  "logs.tool.skill_generate": "Generate",
  "logs.tool.skill_evolve": "Evolve",
  "logs.tool.policy_generate": "Generate",
  "logs.tool.policy_evolve": "Evolve",
  "logs.tool.world_generate": "Generate",
  "logs.tool.world_evolve": "Evolve",
  "logs.tool.task_done": "Done",
  "logs.tool.task_failed": "Failed",
  "logs.group.all": "All",
  "logs.group.memory": "Memory",
  "logs.group.skill": "Skill",
  "logs.group.policy": "Experience",
  "logs.group.world": "Domain",
  "logs.group.task": "Task",
  "logs.totalRows": "{n} rows",
  "logs.search.query": "Query",
  "logs.search.initial": "Initial retrieval",
  "logs.search.hub": "Hub remote",
  "logs.search.filtered": "LLM-filtered",
  "logs.search.droppedByLlm": "Dropped by LLM",
  "logs.search.noCandidates": "No candidates.",
  "logs.search.noneRelevant": "Candidates were returned but the LLM dropped them all.",
  "logs.search.funnel": "Retrieval funnel",
  "logs.add.warnings": "Warnings",
  "logs.add.details": "Per-turn items",
  "pager.pageN": "Page {n} / {total}",

  // Import / Export.
  "import.title": "Import / Export",
  "import.subtitle": "Move memories in and out of this machine.",
  "import.export.title": "Export current database",
  "import.export.desc":
    "Save every memory, experience, environment knowledge entry and skill to a portable JSON bundle. Safe to share with other local installs.",
  "import.export.btn": "Export JSON bundle",
  "import.import.title": "Import a bundle",
  "import.import.desc":
    "Restore memories, experiences, environment knowledge and skills from a JSON bundle. Your existing data is preserved; imported rows are added alongside with new ids.",
  "import.import.btn": "Choose JSON bundle…",
  "import.migrate.title": "Migrate from legacy plugin (memos-local-openclaw / memos-local-hermes)",
  "import.migrate.desc":
    "Scan the legacy plugin's SQLite database for the currently running agent (openclaw → ~/.openclaw/memos-local, hermes → ~/.hermes/memos-state/memos-local) and copy matching rows into the new store. Non-destructive — the legacy file stays put.",
  "import.migrate.scan": "Scan legacy DB",
  "import.migrate.run": "Run migration",
  "import.migrate.found":
    "Found legacy {agent} DB at {path}. Candidates — traces: {traces}, skills: {skills}, tasks: {tasks}.",
  "import.migrate.notFoundAt": "No legacy database found at {path}.",
  "import.migrate.notFound": "No legacy database found.",
  "import.hermes.title": "Import Hermes native memories",
  "import.hermes.desc":
    "Read ~/.hermes/memories/MEMORY.md and import each entry separated by a single § line into this memory plugin.",
  "import.hermes.scan": "Scan native memory file",
  "import.hermes.run": "Import native memories",
  "import.hermes.stop": "Stop import",
  "import.hermes.running": "Importing Hermes native memories…",
  "import.hermes.stopping": "Stopping after the current batch…",
  "import.hermes.found": "Found {total} native Hermes memories at {path}.",
  "import.hermes.notFoundAt": "No Hermes native memory file found at {path}.",
  "import.hermes.progress":
    "{done} / {total} processed · imported {imported}, skipped {skipped}",
  "import.hermes.done": "Imported {imported}; skipped {skipped}.",
  "import.hermes.stopped": "Import stopped. Imported {imported}; skipped {skipped}.",
  "import.openclaw.title": "Import OpenClaw native memories",
  "import.openclaw.desc":
    "Read ~/.openclaw/agents/*/sessions/*.jsonl and import user/assistant messages into this memory plugin.",
  "import.openclaw.scan": "Scan OpenClaw sessions",
  "import.openclaw.run": "Import OpenClaw memories",
  "import.openclaw.stop": "Stop import",
  "import.openclaw.running": "Importing OpenClaw native memories…",
  "import.openclaw.stopping": "Stopping after the current batch…",
  "import.openclaw.found":
    "Found {total} messages across {sessions} sessions / {files} files at {path}.",
  "import.openclaw.notFoundAt": "No OpenClaw session JSONL files found under {path}.",
  "import.openclaw.progress":
    "{done} / {total} processed · imported {imported}, skipped {skipped}",
  "import.openclaw.done": "Imported {imported}; skipped {skipped}.",
  "import.openclaw.stopped": "Import stopped. Imported {imported}; skipped {skipped}.",
  "import.native.metric.items": "Items",
  "import.native.metric.messages": "messages",
  "import.native.metric.memories": "memories",
  "import.native.metric.sessions": "Sessions",
  "import.native.metric.file": "Source file",
  "import.native.metric.jsonl": "JSONL files",
  "import.native.metric.memoryMd": "MEMORY.md",
  "import.native.stat.imported": "Imported",
  "import.native.stat.skipped": "Skipped",
  "import.native.stat.processed": "Processed",
  "import.embeddingRepair.btn": "Repair embeddings",
  "import.embeddingRepair.running": "Repairing missing embeddings…",
  "import.embeddingRepair.progress":
    "Updated {updated}, failed {failed}, remaining {remaining}.",
  "import.embeddingRepair.done": "Embedding repair complete: updated {updated}, failed {failed}.",

  // Admin.
  "admin.title": "Team administration",
  "admin.subtitle": "Manage team sharing members and pending approvals.",
  "admin.disabled.title": "Team sharing is disabled",
  "admin.disabled.desc": "Enable it in Settings → Team Sharing to invite users and approve joins.",
  "admin.unsaved.desc": "Team sharing settings have unsaved changes. Save first to reconnect and refresh the live status.",
  "admin.tab.pending": "Pending",
  "admin.tab.users": "Users",
  "admin.tab.groups": "Groups",
  "admin.client.unknownMember": "This device",
  "admin.client.notJoined": "No join request has been submitted from this device.",
  "admin.client.pendingDesc": "Your join request has been sent. Ask the Hub owner to approve it on the Hub machine.",
  "admin.client.connectedDesc": "This device is approved and connected to the Hub.",
  "admin.client.refreshDesc": "Refresh checks the Hub for the latest approval state.",
  "admin.client.connected": "connected",
  "admin.client.pending": "waiting for approval",
  "admin.client.rejected": "rejected",
  "admin.client.blocked": "blocked",
  "admin.client.removed": "removed",
  "admin.client.tokenExpired": "token expired",
  "admin.client.invalidTeamToken": "invalid team token",
  "admin.client.missingTeamToken": "team token required",
  "admin.client.hubChanged": "hub changed",
  "admin.client.notRegistered": "not registered",
  "admin.client.usernameTaken": "nickname already used",
  "admin.client.disconnected": "not connected",

  // Settings.
  "settings.title": "Settings",
  "settings.tab.models": "AI models",
  "settings.tab.hub": "Team sharing",
  "settings.tab.general": "General",
  "settings.warn.title": "Model configuration",
  "settings.warn.emb":
    "The bundled embedder is small. For better recall we recommend a dedicated model (e.g. bge-m3).",
  "settings.warn.sum":
    "Summarizer is required — without it, task summaries and scoring fall back to crude heuristics.",
  "settings.warn.skill":
    "Skill induction benefits from a stronger reasoning model. Any capable LLM works.",
  "settings.provider": "Provider",
  "settings.endpoint": "Endpoint",
  "settings.apiKey": "API key",
  "settings.model": "Model",
  "settings.temperature": "Temperature",
  "settings.embedding.title": "Embedding",
  "settings.embedding.desc": "Vector embedding model used by retrieval and deduplication.",
  "settings.embedding.localHint":
    "Currently using built-in MiniLM-L6-v2 (384-dim, ~23 MB). Select another provider for better retrieval accuracy.",
  "settings.embedding.maintenance.title": "Embedding maintenance",
  "settings.embedding.maintenance.stats":
    "Ready {ready}/{total}; missing {missing}; dimension mismatch {mismatch}; current dim {dim}.",
  "settings.embedding.maintenance.unavailable":
    "Configure an embedding provider before repairing or rebuilding vectors.",
  "settings.embedding.batchSize.label": "Items per request",
  "settings.embedding.batchSize.option": "{n} items per request",
  "settings.embedding.batchSize.hint":
    "Larger batches usually rebuild faster, but may hit provider limits or timeouts.",
  "settings.embedding.repair": "Repair missing/mismatched",
  "settings.embedding.rebuild": "Rebuild all vectors",
  "settings.embedding.rebuild.running": "Rebuilding embeddings…",
  "settings.embedding.rebuild.progress":
    "Updated {updated}, failed {failed}, remaining repairs {remaining}.",
  "settings.embedding.rebuild.done": "Embedding rebuild complete: updated {updated}, failed {failed}.",
  "settings.summarizer.title": "Summarizer",
  "settings.summarizer.desc":
    "Model that turns your conversations into short task summaries and the takeaways the agent keeps.",
  "settings.summarizer.inherit":
    "Currently using the agent's model. Select a provider to override.",
  "settings.model.tip.title": "Model selection tips",
  "settings.model.tip.embedding":
    "Embedding — the built-in model is small. For better recall, configure a dedicated model such as bge-m3 or text-embedding-3-large.",
  "settings.model.tip.summarizer":
    "Summarizer — required. Use a fast, non-reasoning model (e.g. gpt-4o-mini, claude-haiku) to keep summary latency low. Do NOT use reasoning/thinking models here.",
  "settings.model.tip.skillEvolver":
    "Skill evolver — leave blank to reuse the summarizer, or configure a stronger thinking/reasoning model (e.g. gpt-5-thinking, claude-sonnet) for best-quality skill crystallization.",
  "settings.skill.title": "Skill evolution",
  "settings.skill.desc":
    "Model that turns proven experiences into reusable skills and keeps environment knowledge up to date.",
  "settings.hub.enabled": "Enable team sharing",
  "settings.hub.subtitle": "Share your skills and (optionally) memories with teammates.",
  "settings.hub.role": "Role",
  "settings.hub.role.hub": "Host a hub",
  "settings.hub.role.client": "Join a hub",
  "settings.hub.address": "Hub address",
  "settings.hub.port": "Hub port",
  "settings.hub.teamName": "Team name",
  "settings.hub.nickname": "Personal nickname",
  "settings.hub.teamToken": "Team token",
  "settings.hub.help.title": "How to configure team sharing",
  "settings.hub.help.role":
    "Host a hub when this machine is the team endpoint; join a hub when you connect to another teammate's hub address.",
  "settings.hub.help.tokens":
    "Team token is the only code members need to join. Joining creates a pending request; after approval the plugin stores the member credential automatically.",
  "settings.hub.mode.hub.title": "Hub server mode",
  "settings.hub.mode.hub.desc": "This machine hosts the team endpoint. It shows pending requests and approved members.",
  "settings.hub.mode.client.title": "Join Hub mode",
  "settings.hub.mode.client.desc": "This machine joins another Hub with the Hub address, team token, and your personal nickname. It only shows this device's connection state.",
  "settings.hub.teamToken.placeholder": "Shared workspace token",
  "settings.hub.nickname.placeholder": "Your display name on this team",
  "settings.general.lang": "Display language",
  "settings.general.theme": "Theme",
  "settings.general.theme.light": "Light",
  "settings.general.theme.dark": "Dark",
  "settings.general.theme.auto": "System",
  "settings.general.lightweightMemory": "Enable memory self-evolution",
  "settings.general.lightweightMemory.desc":
    "Automatically distill tasks, experiences, environment knowledge and skills beyond basic summary memory. Uses more model calls and is best for heavy memory workflows. Save and restart to apply.",
  "settings.general.detailedLogs": "Show detailed debug logs",
  "settings.general.detailedLogs.desc":
    "Enable chain view, failure-only filtering, and task, experience, skill, environment and system log categories.",
  "settings.general.telemetry": "Enable anonymous usage stats",
  "settings.general.telemetry.desc":
    "Only tool names, response times and the version number are collected. No memory content, queries or personal data ever leave this machine.",

  // Errors / empty states.
  "error.generic": "Something went wrong.",
  "error.loadFailed": "Couldn't load data.",

  // Model-setup banner — one-time onboarding nudge, dismissed via ✕.
  "banner.modelSetup.aria": "Model configuration reminder",
  "banner.modelSetup.title": "Model setup reminder",
  "banner.modelSetup.msg":
    "Make sure the three model slots — Embedding, Summarizer, and Skill evolver — are configured. Without them memory recall, summarisation and skill crystallization will not work.",
  "banner.modelSetup.cta": "Open Settings → AI Models",
  "banner.modelSetup.dismiss": "Dismiss",
} as const;

type TranslationKey = keyof typeof en;

const zh: Record<TranslationKey, string> = {
  "nav.overview": "总览",
  "nav.memories": "记忆",
  "nav.tasks": "任务",
  "nav.skills": "技能",
  "nav.policies": "经验",
  "nav.worldModels": "环境认知",
  "nav.analytics": "统计",
  "nav.logs": "日志",
  "nav.import": "导入 / 导出",
  "nav.admin": "团队管理",
  "nav.settings": "设置",
  "nav.help": "帮助",
  "nav.section.work": "工作区",
  "nav.section.insights": "洞察",
  "nav.section.system": "系统",

  "header.brand": "MemOS",
  "header.subtitle": "记忆面板",
  "header.search.placeholder": "全局搜索…",
  "header.search.noResults": "未找到匹配结果",
  "header.search.viewAll": "查看全部",
  "header.lang.en": "EN",
  "header.lang.zh": "中",
  "header.theme.light": "切换为浅色",
  "header.theme.dark": "切换为深色",
  "header.theme.auto": "跟随系统",
  "header.notif.title": "通知",
  "header.notif.empty": "暂无通知",
  "header.notif.clear": "全部清除",
  "header.logout": "退出登录",
  "header.agent.current": "当前 Agent",
  "header.agent.peers": "本机其他 Agent",

  "common.search": "搜索",
  "common.filter": "筛选",
  "common.clear": "清空",
  "common.all": "全部",
  "common.apply": "应用",
  "common.cancel": "取消",
  "common.save": "保存",
  "common.reset": "重置",
  "common.delete": "删除",
  "common.download": "下载",
  "common.upload": "上传",
  "common.export": "导出",
  "common.import": "导入",
  "common.refresh": "刷新",
  "common.close": "关闭",
  "common.back": "返回",
  "common.next": "下一页",
  "common.prev": "上一页",
  "common.loading": "加载中…",
  "common.saving": "保存中…",
  "common.saved": "已保存",
  "common.empty": "暂无内容",
  "common.retry": "重试",
  "common.more": "更多",
  "common.loadMore": "加载更多",
  "common.selected": "已选 {n} 项",
  "common.selectPage": "全选当前页",
  "common.deselectPage": "取消当前页选择",
  "common.deselect": "取消选择",
  "common.bulkDelete": "批量删除",
  "common.bulkDelete.confirm": "确认删除 {n} 项？此操作不可撤销。",
  "common.justNow": "刚刚",
  "common.secondsAgo": "{n} 秒前",
  "common.minutesAgo": "{n} 分钟前",
  "common.hoursAgo": "{n} 小时前",
  "common.daysAgo": "{n} 天前",
  "pager.page": "第 {n} 页",
  "pager.pageOfAtLeast": "第 {n} 页 / 共 {total}+ 页",
  "pager.pageOfTotal": "第 {n} 页 / 共 {total} 页",
  "pager.totalPerPage": "共 {total} 条，每页 {pageSize} 条",
  "pager.totalCompact": "{total} 条 · {pageSize}/页",
  "pager.pageSize.label": "每页条数",
  "pager.pageSize.option": "{pageSize} 条/页",
  "pager.jump.label": "跳至",
  "pager.jump.short": "跳页",
  "pager.jump.to": "到",
  "pager.jump.pageUnit": "页",
  "pager.jump.go": "跳转",
  "pager.jump.goShort": "跳",

  "settings.test": "测试",
  "settings.hub.admin": "团队成员",
  "settings.hub.status": "Hub 状态",
  "admin.approve": "通过",
  "admin.deny": "拒绝",
  "admin.remove": "删除",
  "admin.remove.confirm": "确认从 Hub 删除该成员？该成员已共享到团队的内容也会一起移除。",
  "settings.test.ok": "连接成功",
  "settings.apiKey.saved": "（已保存 — 留空保持不变，输入以替换）",
  "settings.saveAndRestart": "保存并重启",
  "settings.tab.account": "账户",
  "settings.skillEvolver.title": "技能进化模型",
  "settings.skillEvolver.desc":
    "结晶新技能时专用的模型。留空则使用摘要模型。",
  "settings.skillEvolver.inherit": "当前使用 agent 的模型。选择 Provider 即可覆盖。",
  "settings.account.protection": "密码保护",
  "settings.account.protection.desc": "启用后，打开记忆面板需要输入密码。",
  "settings.account.on": "已启用",
  "settings.account.off": "未启用",
  "settings.account.newPassword": "新密码",
  "settings.account.confirm": "再次输入",
  "settings.account.enable": "启用",
  "settings.account.logout": "退出登录",
  "settings.account.resetHint":
    "删除 ~/.openclaw/memos-plugin/.auth.json 可重置。",
  "settings.account.resetPassword": "重置密码",
  "settings.account.resetConfirm":
    "此操作会删除已保存的密码并退出登录，下次访问时需要重新设置密码。是否继续？",
  "settings.account.resetConfirmBtn": "确认，重置密码",
  "settings.danger.title": "危险操作",
  "settings.danger.desc": "不可撤销的操作，请谨慎。",
  "settings.danger.clearAll": "清除所有数据",
  "settings.danger.confirm":
    "此操作将永久删除所有记忆、任务、技能、经验、环境认知和日志。配置文件将被保留。此操作不可撤销！",
  "settings.danger.confirmBtn": "确认，删除所有数据",

  "auth.login.title": "记忆面板已锁定",
  "auth.login.subtitle": "请输入密码继续。",
  "auth.login.password": "密码",
  "auth.login.submit": "解锁",
  "auth.setup.title": "设置面板密码",
  "auth.setup.subtitle": "首次进入前为本地记忆设置密码。",
  "auth.setup.newPassword": "新密码",
  "auth.setup.confirm": "再次输入",
  "auth.setup.submit": "设置密码并进入",
  "auth.setup.hint":
    "密码以 scrypt 哈希存储在本机。删除 ~/.openclaw/memos-plugin/.auth.json 可重置。",
  "auth.err.empty": "密码不能为空。",
  "auth.err.required": "请输入密码。",
  "auth.err.tooShort": "密码太短。",
  "auth.err.mismatch": "两次输入不一致。",
  "auth.err.badPassword": "密码不正确。",

  "analytics.tools.title": "工具响应耗时",
  "analytics.tools.subtitle": "所选时间窗口内，各工具的延迟和失败次数。来源：最近的记忆行。",

  "restart.restarting": "配置已保存，服务正在重启…",
  "restart.restarting.hermes": "配置已保存，正在关闭当前 Hermes 会话…",
  "restart.waitingUp": "正在等待服务重新上线…",
  "restart.autoRefresh": "服务就绪后页面将自动刷新。",
  "restart.failed": "重启超时 — 服务未能在预期时间内恢复。",
  "restart.failedHint.openclaw":
    "请手动重启：openclaw gateway stop && openclaw gateway start",
  "restart.failedHint.hermes":
    "请手动重启：停止当前 Hermes 会话后重新执行 `hermes chat`",
  "common.never": "从未",
  "common.selectAll": "全选",
  "common.deleteSelected": "删除所选",

  // Status pills (unified to candidate / active / archived — 候选 / 已启用 / 已归档).
  // The previous alias pairs (probationary / retired) were merged in
  // migration 012. Pill text intentionally matches filter-chip text so
  // users don't see "激活" in one place and "已启用" in another.
  "status.active": "已启用",
  "status.candidate": "候选",
  "status.archived": "已归档",
  "status.draft": "草稿",
  "status.completed": "已完成",
  "status.skipped": "已跳过",
  "status.running": "运行中",
  "status.failed": "失败",

  "overview.title": "系统总览",
  "overview.metric.memories": "记忆数量",
  "overview.metric.episodes": "任务数量",
  "overview.metric.policies": "经验数量",
  "overview.metric.worldModels": "环境认知数量",
  "overview.metric.skills": "技能数量",
  "overview.metric.llm": "摘要模型",
  "overview.metric.embedder": "嵌入模型",
  "overview.metric.skillEvolver": "技能进化模型",
  "overview.metric.skillEvolver.inherit": "（沿用摘要模型）",
  "overview.metric.model.unconfigured": "未配置",
  "overview.metric.model.unreachable": "不可达",
  "overview.metric.model.reachable": "已连接",
  "overview.metric.model.connected": "已连接",
  "overview.metric.model.connectedAt": "上次成功调用于 {ts}",
  "overview.metric.model.failed": "上次调用失败",
  "overview.metric.model.idle": "暂未调用",
  "overview.metric.model.fallback": "已降级到 Agent 内置模型",
  "overview.metric.model.fallback.tooltip":
    "原配置模型不可用，已自动切换到 Agent 内置模型继续工作。原始错误：{msg}",
  "overview.metric.policies.breakdown": "{active} 已启用 · {candidate} 候选",
  "overview.metric.skills.breakdown": "{active} 已启用 · {candidate} 候选",
  "overview.live.title": "实时活动",
  "overview.live.tile.count": "最近 5 分钟事件",
  "overview.live.tile.empty": "最近 5 分钟无事件",

  "overview.live.cat.session": "对话",
  "overview.live.cat.task": "任务",
  "overview.live.cat.memory": "记忆",
  "overview.live.cat.experience": "经验",
  "overview.live.cat.world": "环境认知",
  "overview.live.cat.skill": "技能",
  "overview.live.cat.retrieval": "检索",
  "overview.live.cat.feedback": "反馈",
  "overview.live.cat.system": "系统",
  "overview.live.cat.hub": "Hub",

  "overview.live.event.session.opened": "对话开启",
  "overview.live.event.session.closed": "对话结束",
  "overview.live.event.episode.opened": "任务开始",
  "overview.live.event.episode.closed": "任务结束",
  "overview.live.event.trace.created": "记忆存储",
  "overview.live.event.trace.value_updated": "记忆更新",
  "overview.live.event.trace.priority_decayed": "记忆衰减",
  "overview.live.event.l2.candidate_added": "候选经验新增",
  "overview.live.event.l2.candidate_expired": "候选经验过期",
  "overview.live.event.l2.induced": "经验生成",
  "overview.live.event.l2.associated": "经验关联",
  "overview.live.event.l2.revised": "经验修订",
  "overview.live.event.l2.boundary_shrunk": "经验边界收紧",
  "overview.live.event.l3.abstracted": "环境认知生成",
  "overview.live.event.l3.revised": "环境认知更新",
  "overview.live.event.skill.crystallized": "技能晶化",
  "overview.live.event.skill.eta_updated": "技能预期更新",
  "overview.live.event.skill.boundary_updated": "技能边界更新",
  "overview.live.event.skill.archived": "技能归档",
  "overview.live.event.skill.repaired": "技能修复",
  "overview.live.event.retrieval.triggered": "检索触发",
  "overview.live.event.retrieval.tier1.hit": "第一层检索命中",
  "overview.live.event.retrieval.tier2.hit": "第二层检索命中",
  "overview.live.event.retrieval.tier3.hit": "第三层检索命中",
  "overview.live.event.retrieval.empty": "检索无结果",
  "overview.live.event.feedback.received": "收到反馈",
  "overview.live.event.feedback.classified": "反馈分类",
  "overview.live.event.reward.computed": "奖励计算",
  "overview.live.event.decision_repair.generated": "决策修补",
  "overview.live.event.decision_repair.validated": "决策修补已校验",
  "overview.live.event.hub.client_connected": "Hub 客户端连接",
  "overview.live.event.hub.client_disconnected": "Hub 客户端断开",
  "overview.live.event.hub.share_published": "Hub 分享发布",
  "overview.live.event.hub.share_received": "Hub 收到分享",
  "overview.live.event.system.started": "系统启动",
  "overview.live.event.system.shutdown": "系统关闭",
  "overview.live.event.system.error": "系统异常",
  "overview.live.event.system.config_changed": "配置变更",
  "overview.live.event.system.update_available": "可用更新",

  "overview.live.detail.id": "{label} {id}",
  "overview.live.detail.idReason": "{label} {id} · {reason}",
  "overview.live.detail.candidate": "候选 {sig}",
  "overview.live.detail.induced": "{sig} · 来自 {n} 次成功任务",
  "overview.live.detail.similarity": "{label} {id} · 相似度 {pct}%",
  "overview.live.detail.retrievalHit": "命中 {count} 条 · {ms}ms",
  "overview.live.detail.feedbackTone": "情绪 {tone}",
  "overview.live.detail.reward": "r = {r} · 来自 {source}",
  "overview.live.detail.version": "v{version}",
  "overview.live.detail.raw": "{value}",

  "bridge.connected": "记忆通道已开启",
  "bridge.reconnecting": "记忆通道已开启",
  "bridge.disconnected": "记忆通道已断开",
  "bridge.unknown": "记忆通道未知",
  "bridge.tooltip": "记忆通道：Hermes 与本地记忆核心之间的连接",
  "bridge.tooltip.lastOk": "上次成功：{ts}",
  "bridge.tooltip.lastError": "上次错误：{msg}",

  "memories.title": "记忆",
  "memories.subtitle": "Agent 每步的执行与反思痕迹。",
  "memories.search.placeholder": "搜索记忆（支持语义）…",
  "memories.filter.role": "角色",
  "memories.filter.role.user": "用户",
  "memories.filter.role.assistant": "助手",
  "memories.filter.role.tool": "工具",
  "memories.filter.role.system": "系统",
  "memories.filter.namespace": "实例",
  "memories.filter.namespace.all": "全部实例",
  "memories.filter.namespace.count": "{n} 条记录",
  "memories.filter.owner": "全部 Agent",
  "memories.filter.scope.device": "本机",
  "memories.filter.scope.team": "团队",
  "memories.filter.sort.newest": "最新在前",
  "memories.filter.sort.oldest": "最早在前",
  "memories.filter.dateFrom": "起",
  "memories.filter.dateTo": "止",
  "memories.empty": "没有匹配的记忆。",
  "memories.empty.hint": "可尝试清空筛选，或让 Agent 做点值得记住的事。",
  "memories.act.expand": "展开",
  "memories.act.collapse": "收起",
  "memories.act.edit": "编辑",
  "memories.act.share": "共享",
  "memories.act.unshare": "取消共享",
  "memories.act.delete": "删除",
  "memories.bulk.selectPage": "全选当前页",
  "memories.bulk.deselect": "取消选择",
  "memories.bulk.delete": "批量删除",
  "memories.bulk.export": "复制为文本",
  "memories.bulk.share": "批量共享",
  "memories.bulk.unshare": "取消共享",
  "memories.share.bulkDone": "已共享 {n} 项",
  "memories.share.bulkRemoved": "已取消共享 {n} 项",
  "memories.edit.title": "编辑记忆",
  "memories.edit.summary": "摘要",
  "memories.edit.user": "用户文本",
  "memories.edit.assistant": "助手文本",
  "memories.edit.tags": "标签（逗号分隔）",
  "memories.edit.saved": "记忆已更新",
  "memories.field.takeaway": "反思",
  "memories.field.summary": "记忆摘要",
  "memories.card.reflection": "反思",
  "memories.field.user": "用户",
  "memories.field.assistant": "助手",
  "memories.field.ts": "时间",
  "memories.field.value": "价值 V",
  "memories.field.alpha": "反思权重 α",
  "memories.field.priority": "优先级",
  "memories.field.rHuman": "用户反馈分 R_human",
  "memories.field.share": "共享状态",
  "memories.field.status": "状态",
  "memories.field.startedAt": "开始时间",
  "memories.field.endedAt": "结束时间",
  "memories.field.createdAt": "创建时间",
  "memories.field.updatedAt": "更新时间",
  "memories.field.session": "会话",
  "memories.field.rTask": "任务评分 R_task",
  "memories.field.eta": "可靠性 η",
  "memories.field.gain": "增益",
  "memories.field.support": "支撑任务数",
  "memories.field.toolCalls": "工具调用",
  "memories.field.episodeTimeline": "本任务的其他步骤",
  "memories.field.steps": "本轮步骤（共 {n} 步）",
  "memories.card.steps": "{n} 步",
  "memories.score.skipped": "跳过评分",
  "memories.score.pending": "待评分",
  "memories.help.value":
    "记忆被捕获时的重要性评分（0–1）。值越高表示助手当时越觉得这条记忆值得保留。",
  "memories.help.alpha":
    "助手对自己反思的信任度权重（0–1）。值越高表示这条反思在后续检索时影响越大。",
  "memories.help.priority":
    "综合评分，决定检索时的排序优先级。值越高，相关搜索时越靠前。",
  "memories.help.rHuman":
    "用户对这一轮交互的反馈信号（-1 到 1）。正值表示你确认了助手做得对，负值表示你纠正过它。",
  "memories.help.episodeTimeline":
    "同一任务（一次完整的提问—响应过程）下，按时间顺序展示其他相关的记忆步骤。",
  "memories.help.share":
    "可见范围：私密仅创建该记忆的 Agent 可见；公开表示本机同一 Agent 框架内的其他 Agent 可见；Hub 表示团队内可见。",
  "memories.detail.fromTask": "来自任务 {id}",
  "memories.detail.oneMemory": "记忆",
  "memories.detail.fallbackTitle": "记忆详情",
  "memories.share.title": "共享记忆",
  "memories.share.scope": "可见范围",
  "memories.share.scope.private": "私密（仅创建者 Agent）",
  "memories.share.scope.public": "公开（同一 Agent 框架）",
  "memories.share.scope.hub": "Hub（团队）",
  "memories.share.done": "共享已更新",
  "memories.share.removed": "已取消共享",
  "memories.delete.confirm": "删除这条记忆？该操作不可撤销。",
  "memories.delete.bulkConfirm": "删除已选的 {n} 条记忆？该操作不可撤销。",
  "memories.delete.done": "记忆已删除",
  "memories.delete.bulkDone": "已删除 {n} 条记忆",
  "memories.copy.done": "已复制 {n} 条",

  "policies.title": "经验",
  "policies.subtitle": "Agent 在反复任务里总结出的经验：在什么情况下该怎么做。",
  "policies.search.placeholder": "搜索经验…",
  "policies.filter.all": "全部",
  "policies.filter.candidate": "候选",
  "policies.filter.active": "已启用",
  "policies.filter.archived": "已归档",
  "policies.empty": "尚未结晶出经验。",
  "policies.empty.hint": "当几次成功对话用的是同一套做法后，这里会出现相应的经验条目。",
  "policies.lightweight.empty":
    "当前仅保存基础摘要记忆。启用记忆自进化后，系统会自动提炼可复用经验。",
  "policies.col.trigger": "触发",
  "policies.col.procedure": "流程",
  "policies.col.verification": "验证",
  "policies.col.boundary": "边界",
  "policies.act.activate": "启用",
  "policies.act.archive": "归档",
  "policies.act.retire": "归档",
  "policies.act.reinstate": "重新启用",
  "policies.act.candidate": "候选",
  "policies.col.title": "标题",
  "policies.delete.confirm": "删除这条经验？该操作不可撤销。",
  "policies.guidance.title": "决策指引",
  "policies.guidance.empty": "暂无决策指引 — 当这条经验收到用户正面或负面反馈后，系统会自动生成「推荐做法」和「避免做法」。",
  "policies.guidance.add": "添加指引",
  "policies.guidance.emptyInput": "请至少输入一条推荐或避免做法。",
  "policies.guidance.preferInputHint": "每行一条做法",
  "policies.guidance.avoidInputHint": "每行一条做法",
  "policies.guidance.preferPlaceholder": "例如：优先使用标准库",
  "policies.guidance.avoidPlaceholder": "例如：不要用 try/except 包裹整个函数体",
  "policies.guidance.prefer": "偏好",
  "policies.guidance.avoid": "避免",
  "policies.guidance.preferTitle": "从这条经验的成功案例里沉淀的偏好做法",
  "policies.guidance.avoidTitle": "从失败案例里沉淀的反模式，应避免",
  "policies.guidance.preferSection": "推荐做法",
  "policies.guidance.avoidSection": "避免做法",
  "policies.xlink.skills": "关联技能",
  "policies.xlink.worldModels": "关联环境认知",
  "policies.xlink.sourceEpisodes": "来源任务",
  "skills.xlink.sourcePolicies": "来源经验",
  "skills.xlink.sourceWorldModels": "来源环境认知",

  "worldModels.title": "环境认知",
  "worldModels.subtitle": "助手对你工作场景的整体认知——常用工具、文件布局、反复出现的约束。",
  "worldModels.search.placeholder": "搜索环境认知…",
  "worldModels.empty": "暂无环境认知。",
  "worldModels.empty.hint": "当多条经验展现出相同的规律时，会自动凝聚成这里的环境认知。",
  "worldModels.lightweight.empty":
    "当前仅保存基础摘要记忆。启用记忆自进化后，系统会自动建立环境认知。",
  "worldModels.col.body": "内容",
  "worldModels.structure.title": "结构化认知（带证据锚点）",
  "worldModels.structure.environment": "环境拓扑（ℰ）",
  "worldModels.structure.inference": "行为规律（ℐ）",
  "worldModels.structure.constraints": "约束禁忌（𝒞）",
  "worldModels.col.policies": "关联经验",
  "worldModels.field.id": "ID",
  "worldModels.field.version": "版本",
  "worldModels.version.title": "环境认知版本（每次 L3 合并 +1）",
  "worldModels.delete.confirm": "删除这条环境认知？该操作不可撤销。",
  "worldModels.edit.title": "标题",
  "worldModels.edit.body": "内容",

  "tasks.title": "任务",
  "tasks.subtitle": "每个任务都是一段聚焦的对话。点进去可以看到说过什么，以及是否生成了经验或技能。",
  "tasks.search.placeholder": "搜索任务…",
  "tasks.empty": "暂无任务。",
  "tasks.empty.filtered": "当前筛选条件下没有匹配的任务。",
  "tasks.lightweight.empty":
    "当前仅保存基础摘要记忆。启用记忆自进化后，系统会自动整理任务视图。",
  "tasks.untitled": "未命名任务",
  "tasks.detail.id": "任务 {id}",
  "tasks.detail.fallbackTitle": "任务详情",
  "tasks.detail.meta": "元数据",
  "tasks.detail.relatedMemories": "相关记忆",
  "tasks.detail.relatedSkill": "关联技能",
  "tasks.detail.share": "分享到团队",
  "tasks.detail.unshare": "取消分享",
  "tasks.detail.chat": "对话记录",
  "tasks.detail.chat.empty": "该任务未记录任何消息。",
  "tasks.chat.role.user": "你",
  "tasks.chat.role.assistant": "助手",
  "tasks.chat.role.tool": "工具",
  "tasks.chat.role.thinking": "思考",
  "tasks.chat.tool.assistantTextBefore": "工具前回复",
  "tasks.chat.tool.thinking": "工具前思考",
  "tasks.chat.tool.input": "输入",
  "tasks.chat.tool.output": "输出",
  "tasks.chat.tool.ok": "成功",
  "tasks.chat.tool.noPayload": "（未记录输入或输出）",
  "tasks.chat.tool.parallelBatch": "⚡ {n} 个工具并行 · 实际耗时 {ms}ms",
  "tasks.chat.tool.parallelBatch.savings": "（串行需 {sum}ms）",
  "tasks.chat.expand": "展开全文",
  "tasks.chat.collapse": "收起",
  "tasks.skipped.default": "对话内容过少，未生成摘要，该任务不会出现在检索结果中。",
  "tasks.failed.default": "任务评分 R={rTask}，被视为失败交互，未来相似任务的检索权重会被下调。",
  "tasks.skip.reason.tooFewTurns": "对话轮次不足，需要至少 2 轮完整的问答交互才能生成摘要。",
  "tasks.skip.reason.tooFewExchanges":
    "对话轮次不足（{exchanges} 轮），需要至少 {min} 轮完整的问答交互才能生成摘要。",
  "tasks.skip.reason.noUserMessages":
    "该任务没有用户消息，仅包含系统或工具自动生成的内容。",
  "tasks.skip.reason.contentTooShort":
    "对话内容过短（{chars} 字符），信息量不足以生成有意义的摘要。",
  "tasks.skip.reason.trivialUserContent":
    "对话内容为简单问候或测试数据（如 hello、test、ok），无需生成摘要。",
  "tasks.skip.reason.trivialBothSides":
    "对话内容（用户和助手双方）为简单问候或测试数据，无需生成摘要。",
  "tasks.skip.reason.toolHeavy":
    "该任务主要由工具执行结果组成（{tools}/{total} 条），缺少足够的用户交互内容。",
  "tasks.skip.reason.repeatedContent":
    "对话中存在大量重复内容（{unique} 条独立消息 / {total} 条用户消息），无法提取有效信息。",
  "tasks.skip.reason.noAssistant": "只捕获到用户消息，没收到 assistant 回复——可能是 Agent 宿主崩溃、turn 被 bootstrap 过滤、或用户打断。暂时没有可总结的内容。",
  "tasks.active.reason.interrupted": "这个 topic 在 assistant 回复完成前被打断了。下次继续同一 topic 时，会归入同一个任务。",
  "tasks.active.reason.paused": "这个 topic 因 session 关闭而暂停；短时间内继续同一 topic，会继续追加到这个任务。",
  "tasks.skip.reason.abandoned": "管线在未完成打分前主动结束了这条任务（例如 relation 分类器判定下一条属于全新任务），可以去 Session 时间轴看完整链路。",
  "tasks.skip.reason.rewardPending": "Reward 管线还没给它打分——可能仍在计算中，也可能 LLM 打分失败了；到 Logs 面板搜 `reward.*` 事件看看。",
  "tasks.skip.reason.default": "对话未达到生成摘要的条件。",
  "tasks.fail.reason.withReward": "任务评分 {rTask}：不会沉淀出新的 L2 经验或技能。原始轨迹将作为反面教材保留，后续 Decision Repair 会据此生成规避建议。",
  "tasks.fail.reason.default": "任务收尾时出现了问题。",
  "tasks.skill.queued": "技能流水线等待中",
  "tasks.skill.generating": "技能生成中…",
  "tasks.skill.generated": "已生成技能",
  "tasks.skill.upgraded": "已升级技能",
  "tasks.skill.not_generated": "未达沉淀阈值",
  "tasks.skill.skipped": "本任务评为反例 (R ≤ -0.5)",
  "tasks.skill.openSkill": "打开技能",
  "tasks.skillReason.queued.inProgress":
    "任务仍在进行中，技能流水线尚未启动。",
  "tasks.skillReason.queued.rewardPending":
    "Reward 评分尚未完成，技能流水线将在评分后启动。",
  "tasks.skillReason.queued.policyPending":
    "经验尚未激活——需要更多支撑任务才能结晶为技能（当前 support={support}，需 ≥{skillMinSupport}）。",
  "tasks.skillReason.queued.ready":
    "经验已就绪（gain={gain}，support={support}），技能结晶将在下次 reward 评分后自动触发。",
  "tasks.skillReason.skipped":
    "任务评分为明显负分 (R={rTask})，视为反例；不会沉淀出新的 L2 经验或技能，但原始 L1 轨迹会作为反面教材保留，在后续 Decision Repair 中生成规避建议。",
  "tasks.skillReason.not_generated.belowThreshold":
    "任务评分 R={rTask} 未达到沉淀阈值 (≥ {threshold})——对话本身正常，只是还不够强到能泛化成 L2 经验；多做几个相似任务后会自动积累。",
  "tasks.skillReason.not_generated.noPolicy":
    "暂未归纳出 L2 经验——需要至少 {minEpisodesForInduction} 个相似任务（minEpisodesForInduction），且 V 值 ≥ {minTraceValue} 才能触发 L2 诱导，之后 support ≥ {skillMinSupport} 且 gain ≥ {skillMinGain} 才会结晶为技能。",
  "tasks.skillReason.generated":
    "技能「{skillName}」已从经验 {policyId} 结晶。",
  "tasks.skillReason.upgraded":
    "技能「{skillName}」已从经验 {policyId} 升级。",
  "tasks.abandonReason.uncleanExit":
    "插件上次未正常退出，启动时自动关闭未完成的任务。",

  "skills.title": "技能",
  "skills.subtitle": "从成功任务中沉淀出来、可以重复调用的能力。",
  "skills.search.placeholder": "搜索技能…",
  "skills.filter.visibility": "可见性",
  "skills.filter.visibility.public": "公开",
  "skills.filter.visibility.private": "私有",
  "skills.empty": "尚无技能。",
  "skills.empty.hint": "当一条经验在多个相似任务里都好用，插件会把它沉淀成一个可直接调用的技能。",
  "skills.lightweight.empty":
    "当前仅保存基础摘要记忆。启用记忆自进化后，系统会自动沉淀可复用技能。",
  "skills.detail.desc": "调用指南",
  "skills.detail.files": "技能文件",
  "skills.detail.content": "SKILL.md 内容",
  "skills.detail.versions": "版本历史",
  "skills.detail.related": "相关任务",
  "skills.detail.download": "下载 .zip",
  "skills.detail.makePublic": "设为公开",
  "skills.detail.makePrivate": "设为私有",
  "skills.detail.archive": "归档",
  "skills.detail.version": "当前版本",
  "skills.detail.lastUpdated": "{at} 更新",
  "skills.detail.evolution": "进化时间线",
  "skills.detail.decisionGuidance": "决策指引（偏好 / 反模式）",
  "skills.detail.decisionGuidance.prefer": "偏好",
  "skills.detail.decisionGuidance.avoid": "避免",
  "skills.detail.evidenceAnchors": "证据锚点（{n} 条记忆）",
  "skills.detail.evolution.empty":
    "尚无进化事件——当技能被结晶、重建或归档后，这里会自动出现。",

  "skills.act.delete.confirm": "确认永久删除技能 \"{name}\"？该操作不可撤销。",
  "skills.edit.name": "名称",
  "skills.edit.invocationGuide": "调用指南",
  "skills.version.title": "技能版本（每次重建 +1）",
  "skills.trials.pass": "pass {count} 次",
  "skills.trials.pass.label": "Trial 成功",
  "skills.trials.pass.detail": "{passed} / {attempted}",
  "skills.usage.count": "调用 {count} 次",
  "skills.usage.count.label": "调用次数",
  "skills.usage.lastUsed": "最近调用 {at}",
  "skills.usage.lastUsed.label": "最近调用",
  "skills.updated.ago": "{at} 更新",
  "skills.timeline.kind.crystallized": "结晶完成",
  "skills.timeline.kind.started": "开始结晶",
  "skills.timeline.kind.rebuilt": "重建",
  "skills.timeline.kind.etaUpdated": "η 更新",
  "skills.timeline.kind.statusChanged": "状态变更",
  "skills.timeline.kind.archived": "已归档",
  "skills.timeline.kind.verifyFailed": "验证失败",
  "skills.timeline.kind.failed": "失败",

  "analytics.title": "统计",
  "analytics.subtitle": "进化看板 — 任务 / 经验 / 环境认知 / 技能。",
  "analytics.range.label": "区间",
  "analytics.range.7d": "7 天",
  "analytics.range.30d": "30 天",
  "analytics.range.90d": "90 天",
  "analytics.card.total": "总记忆数",
  "analytics.card.writesToday": "今日写入",
  "analytics.card.sessions": "会话数",
  "analytics.card.embeddings": "嵌入向量",
  "analytics.chart.writes": "每日记忆写入",
  "analytics.chart.toolPerf": "工具响应时间（分钟均值）",
  "analytics.chart.skillEvolutions": "每日技能进化次数",
  "analytics.chart.skillEvolutions.empty":
    "暂无技能结晶 — 让插件继续运行，等证据累积到门槛即可。",
  "analytics.axis.date": "日期",
  "analytics.axis.time": "时间",
  "analytics.axis.count": "数量",
  "analytics.axis.latencyMs": "耗时（ms）",
  "analytics.kpi.evolutionRate": "技能进化率",
  "analytics.kpi.evolutionRate.hint": "任务 → 技能 转化比例",
  "analytics.kpi.policyCoverage": "规则覆盖率",
  "analytics.kpi.policyCoverage.hint": "活跃 / 全部 L2 经验",
  "analytics.kpi.activePolicies": "活跃规则数",
  "analytics.kpi.activePolicies.hint": "当前处于 active 的 L2 经验",
  "analytics.kpi.avgQuality": "平均质量分",
  "analytics.kpi.avgQuality.hint": "活跃经验 gain 的均值",
  "analytics.kpi.skillsTotal": "技能数",
  "analytics.kpi.worldModels": "环境认知",
  "analytics.evolutions.title": "最近进化事件",
  "analytics.evolutions.subtitle":
    "最近一批技能结晶 — 来自哪个 L2 经验，从新到旧排列。",
  "analytics.evolutions.col.time": "时间",
  "analytics.evolutions.col.skill": "技能",
  "analytics.evolutions.col.status": "状态",
  "analytics.evolutions.col.policies": "来源经验",
  "analytics.evolutions.empty":
    "该时间段内尚无技能结晶。",
  "analytics.tools.range.1h": "最近 1 小时",
  "analytics.tools.range.6h": "最近 6 小时",
  "analytics.tools.range.24h": "最近 24 小时",
  "analytics.tools.range.3d": "最近 3 天",
  "analytics.tools.range.7d": "最近 7 天",
  "analytics.tools.range.30d": "最近 30 天",
  "analytics.tools.empty":
    "该时间段内没有工具调用。",
  "analytics.tools.chart.insufficient":
    "当前时间范围内数据点不足，无法绘制趋势图。",
  "analytics.tools.legend.showAll": "显示全部",
  "analytics.tools.unavailable.title": "有调用但缺少耗时数据",
  "analytics.tools.unavailable.subtitle":
    "这些工具调用已被记录，但开始/结束时间缺失或相同，因此不会进入响应耗时图。",

  "logs.title": "日志",
  "logs.subtitle": "记忆检索和写入的结构化轨迹：本地候选、Hub 候选、LLM 筛选后保留的记忆。",
  "logs.empty.title": "尚无记忆调用",
  "logs.empty.hint": "Agent 触发 memos_search 或写入一轮对话后，这里会出现。",
  "logs.search.placeholder": "搜索日志…",
  "logs.tag.memoryAdd": "记忆添加",
  "logs.tag.memorySearch": "记忆检索",
  "logs.tag.task": "任务",
  "logs.tag.skill": "技能",
  "logs.tag.policy": "经验",
  "logs.tag.world": "环境认知",
  "logs.tag.session": "会话",
  "logs.tag.system": "系统",
  "logs.system.role": "{role}调用失败",
  "logs.system.role.embedding": "嵌入模型",
  "logs.system.role.llm": "摘要模型",
  "logs.system.role.skillEvolver": "技能进化模型",
  "logs.tool.search": "检索",
  "logs.tool.add": "写入",
  "logs.tool.skill_generate": "生成",
  "logs.tool.skill_evolve": "进化",
  "logs.tool.policy_generate": "生成",
  "logs.tool.policy_evolve": "进化",
  "logs.tool.world_generate": "生成",
  "logs.tool.world_evolve": "进化",
  "logs.tool.task_done": "完成",
  "logs.tool.task_failed": "失败",
  "logs.group.all": "全部",
  "logs.group.memory": "记忆",
  "logs.group.skill": "技能",
  "logs.group.policy": "经验",
  "logs.group.world": "环境认知",
  "logs.group.task": "任务",
  "logs.totalRows": "共 {n} 条",
  "logs.search.query": "查询",
  "logs.search.initial": "初步召回",
  "logs.search.hub": "Hub 远端",
  "logs.search.filtered": "LLM 筛选",
  "logs.search.droppedByLlm": "LLM 剔除",
  "logs.search.noCandidates": "没有候选。",
  "logs.search.noneRelevant": "有候选但被 LLM 全部剔除。",
  "logs.search.funnel": "召回漏斗",
  "logs.add.warnings": "警告",
  "logs.add.details": "每轮条目",
  "pager.pageN": "第 {n} 页 / 共 {total} 页",
  "logs.filter.tool": "工具",
  "logs.filter.level": "级别",
  "logs.autoRefresh": "实时",
  "logs.empty": "此时间窗口无日志。",

  "import.title": "导入 / 导出",
  "import.subtitle": "将记忆在本机之间迁入迁出。",
  "import.export.title": "导出当前数据库",
  "import.export.desc": "把记忆、经验、环境认知和技能全部导成 JSON 包，可分享给其他本机实例。",
  "import.export.btn": "导出 JSON 包",
  "import.import.title": "导入一个包",
  "import.import.desc":
    "从 JSON 包中恢复记忆、经验、环境认知和技能。原有数据保留，导入内容以新 id 追加。",
  "import.import.btn": "选择 JSON 文件…",
  "import.migrate.title": "从旧插件迁入（memos-local-openclaw / memos-local-hermes）",
  "import.migrate.desc":
    "扫描当前运行 agent 对应的旧插件 SQLite 数据库（openclaw → ~/.openclaw/memos-local，hermes → ~/.hermes/memos-state/memos-local），把匹配的记录拷贝到新存储。非破坏性，旧文件不动。",
  "import.migrate.scan": "扫描旧数据库",
  "import.migrate.run": "执行迁移",
  "import.migrate.found":
    "在 {path} 找到 {agent} 旧数据库。可迁移条目 — 记忆：{traces}，技能：{skills}，任务：{tasks}。",
  "import.migrate.notFoundAt": "在 {path} 没有找到旧数据库。",
  "import.migrate.notFound": "没有找到旧数据库。",
  "import.hermes.title": "导入 Hermes 原生记忆",
  "import.hermes.desc":
    "读取 ~/.hermes/memories/MEMORY.md，并把用单独一行 § 分隔的每条记忆导入当前记忆插件。",
  "import.hermes.scan": "扫描原生记忆文件",
  "import.hermes.run": "导入原生记忆",
  "import.hermes.stop": "停止导入",
  "import.hermes.running": "正在导入 Hermes 原生记忆…",
  "import.hermes.stopping": "正在停止，当前批次完成后结束…",
  "import.hermes.found": "在 {path} 找到 {total} 条 Hermes 原生记忆。",
  "import.hermes.notFoundAt": "在 {path} 没有找到 Hermes 原生记忆文件。",
  "import.hermes.progress": "已处理 {done} / {total} · 已导入 {imported}，跳过 {skipped}",
  "import.hermes.done": "导入完成：已导入 {imported}，跳过 {skipped}。",
  "import.hermes.stopped": "导入已停止：已导入 {imported}，跳过 {skipped}。",
  "import.openclaw.title": "导入 OpenClaw 原生记忆",
  "import.openclaw.desc":
    "读取 ~/.openclaw/agents/*/sessions/*.jsonl，把其中的 user/assistant 消息导入当前记忆插件。",
  "import.openclaw.scan": "扫描 OpenClaw 会话",
  "import.openclaw.run": "导入 OpenClaw 记忆",
  "import.openclaw.stop": "停止导入",
  "import.openclaw.running": "正在导入 OpenClaw 原生记忆…",
  "import.openclaw.stopping": "正在停止，当前批次完成后结束…",
  "import.openclaw.found": "在 {path} 下找到 {files} 个文件、{sessions} 个会话，共 {total} 条消息。",
  "import.openclaw.notFoundAt": "在 {path} 下没有找到 OpenClaw 会话 JSONL 文件。",
  "import.openclaw.progress": "已处理 {done} / {total} · 已导入 {imported}，跳过 {skipped}",
  "import.openclaw.done": "导入完成：已导入 {imported}，跳过 {skipped}。",
  "import.openclaw.stopped": "导入已停止：已导入 {imported}，跳过 {skipped}。",
  "import.native.metric.items": "条目",
  "import.native.metric.messages": "消息",
  "import.native.metric.memories": "记忆",
  "import.native.metric.sessions": "会话",
  "import.native.metric.file": "来源文件",
  "import.native.metric.jsonl": "JSONL 文件",
  "import.native.metric.memoryMd": "MEMORY.md",
  "import.native.stat.imported": "已导入",
  "import.native.stat.skipped": "跳过",
  "import.native.stat.processed": "已处理",
  "import.embeddingRepair.btn": "修复向量",
  "import.embeddingRepair.running": "正在修复缺失向量…",
  "import.embeddingRepair.progress": "已更新 {updated}，失败 {failed}，剩余 {remaining}。",
  "import.embeddingRepair.done": "向量修复完成：已更新 {updated}，失败 {failed}。",

  "admin.title": "团队管理",
  "admin.subtitle": "管理团队分享成员与待审批申请。",
  "admin.disabled.title": "团队分享尚未启用",
  "admin.disabled.desc": "可在 设置 → 团队分享 中启用并邀请用户。",
  "admin.unsaved.desc": "团队分享配置有未保存修改。请先保存，插件会按新配置重新连接并刷新实时状态。",
  "admin.tab.pending": "待审批",
  "admin.tab.users": "用户",
  "admin.tab.groups": "群组",
  "admin.client.unknownMember": "本机",
  "admin.client.notJoined": "本机尚未提交加入申请。",
  "admin.client.pendingDesc": "加入申请已提交，请在 Hub 所在机器上审批。",
  "admin.client.connectedDesc": "本机已通过审批并连接到 Hub。",
  "admin.client.refreshDesc": "刷新会向 Hub 查询最新审批状态。",
  "admin.client.connected": "已连接",
  "admin.client.pending": "等待审批",
  "admin.client.rejected": "已拒绝",
  "admin.client.blocked": "已拉黑",
  "admin.client.removed": "已删除",
  "admin.client.tokenExpired": "凭证已失效",
  "admin.client.invalidTeamToken": "团队 Token 无效",
  "admin.client.missingTeamToken": "需要团队 Token",
  "admin.client.hubChanged": "Hub 已变更",
  "admin.client.notRegistered": "未注册",
  "admin.client.usernameTaken": "个人昵称已被占用",
  "admin.client.disconnected": "未连接",

  "settings.title": "设置",
  "settings.tab.models": "AI 模型",
  "settings.tab.hub": "团队分享",
  "settings.tab.general": "通用",
  "settings.warn.title": "模型配置提醒",
  "settings.warn.emb": "插件内置嵌入模型较小。为获得更精准的记忆检索，建议配置 bge-m3 等专业嵌入模型。",
  "settings.warn.sum": "摘要模型为必填。若不配置，反思与奖励信号只能走粗糙启发式。",
  "settings.warn.skill": "技能诱导建议配置更强的推理模型以提升稳定性。",
  "settings.provider": "提供商",
  "settings.endpoint": "端点",
  "settings.apiKey": "API Key",
  "settings.model": "模型",
  "settings.temperature": "温度",
  "settings.embedding.title": "嵌入模型",
  "settings.embedding.desc": "用于记忆检索与去重的向量嵌入模型。",
  "settings.embedding.localHint":
    "当前使用内置 MiniLM-L6-v2（384 维，约 23 MB）。选择其他 Provider 可获得更精准的检索效果。",
  "settings.embedding.maintenance.title": "向量维护",
  "settings.embedding.maintenance.stats":
    "可用 {ready}/{total}；缺失 {missing}；维度不匹配 {mismatch}；当前维度 {dim}。",
  "settings.embedding.maintenance.unavailable": "请先配置嵌入模型，再修复或重建向量。",
  "settings.embedding.batchSize.label": "每次请求条数",
  "settings.embedding.batchSize.option": "每次请求 {n} 条",
  "settings.embedding.batchSize.hint": "每次请求条数越大通常重建越快，但可能触发模型服务限流或超时。",
  "settings.embedding.repair": "修复缺失/错维",
  "settings.embedding.rebuild": "全量重建向量",
  "settings.embedding.rebuild.running": "正在重建向量…",
  "settings.embedding.rebuild.progress": "已更新 {updated}，失败 {failed}，待修复 {remaining}。",
  "settings.embedding.rebuild.done": "向量重建完成：已更新 {updated}，失败 {failed}。",
  "settings.summarizer.title": "摘要模型",
  "settings.summarizer.desc": "把原始对话压缩成任务摘要和要点的模型。",
  "settings.summarizer.inherit":
    "当前使用 agent 的模型。选择 Provider 即可覆盖。",
  "settings.model.tip.title": "模型选择提示",
  "settings.model.tip.embedding":
    "嵌入模型：插件内置模型较小。为获得更精准的记忆检索，建议配置 bge-m3、text-embedding-3-large 等专业嵌入模型。",
  "settings.model.tip.summarizer":
    "摘要模型（必填）：建议使用小而快的非思考型模型（如 gpt-4o-mini、claude-haiku），保证摘要速度流畅。切勿使用推理/思考型模型。",
  "settings.model.tip.skillEvolver":
    "技能进化模型：留空则复用摘要模型。如需高质量技能结晶，建议单独配置思考型推理模型（如 gpt-5-thinking、claude-sonnet）。",
  "settings.skill.title": "技能进化",
  "settings.skill.desc": "用于把稳定的经验转成可调用技能，并维护环境认知的模型。",
  "settings.hub.enabled": "启用团队分享",
  "settings.hub.subtitle": "与团队成员分享你的技能和（可选的）记忆。",
  "settings.hub.role": "角色",
  "settings.hub.role.hub": "托管 Hub",
  "settings.hub.role.client": "加入 Hub",
  "settings.hub.address": "Hub 地址",
  "settings.hub.port": "Hub 端口",
  "settings.hub.teamName": "团队名称",
  "settings.hub.nickname": "个人昵称",
  "settings.hub.teamToken": "团队 Token",
  "settings.hub.help.title": "团队分享配置说明",
  "settings.hub.help.role":
    "本机作为团队入口时选择托管 Hub；连接到其他成员的 Hub 地址时选择加入 Hub。",
  "settings.hub.help.tokens":
    "成员只需要团队 Token 即可申请加入；提交后会创建待审批申请，审批通过后插件会自动保存成员凭证。",
  "settings.hub.mode.hub.title": "Hub 服务端模式",
  "settings.hub.mode.hub.desc": "本机作为团队入口，负责接收加入申请、审批成员并展示成员列表。",
  "settings.hub.mode.client.title": "加入 Hub 模式",
  "settings.hub.mode.client.desc": "本机通过 Hub 地址、团队 Token 和个人昵称加入别人的 Hub，只显示本机连接状态。",
  "settings.hub.teamToken.placeholder": "共享工作区 Token",
  "settings.hub.nickname.placeholder": "你在团队中显示的名称",
  "settings.general.lang": "显示语言",
  "settings.general.theme": "主题",
  "settings.general.theme.light": "浅色",
  "settings.general.theme.dark": "深色",
  "settings.general.theme.auto": "跟随系统",
  "settings.general.lightweightMemory": "启用记忆自进化",
  "settings.general.lightweightMemory.desc":
    "在基础摘要记忆之外，自动沉淀任务、经验、环境认知和技能。会调用更多模型能力，适合重度记忆使用场景。保存并重启后生效。",
  "settings.general.detailedLogs": "显示详细调试日志",
  "settings.general.detailedLogs.desc":
    "开启后显示链路视图、仅看失败筛选，以及任务、经验、技能、环境认知和系统日志分类。",
  "settings.general.telemetry": "启用匿名数据统计",
  "settings.general.telemetry.desc":
    "仅收集工具名称、响应时间和版本号，不涉及任何记忆内容或个人数据。",

  "error.generic": "发生了错误。",
  "error.loadFailed": "数据加载失败。",

  "banner.modelSetup.aria": "模型配置提示",
  "banner.modelSetup.title": "请检查模型配置",
  "banner.modelSetup.msg":
    "请确认已配置三个模型：嵌入模型、摘要模型、技能进化模型。未配置时记忆召回、摘要、技能结晶等核心能力都无法工作。",
  "banner.modelSetup.cta": "前往设置 → AI 模型",
  "banner.modelSetup.dismiss": "关闭",
};

// ─── Store ──────────────────────────────────────────────────────────────

export type Locale = "en" | "zh";

const STORAGE_KEY = "memos.lang";

function detectDefault(): Locale {
  try {
    const saved = localStorage.getItem(STORAGE_KEY);
    if (saved === "en" || saved === "zh") return saved;
  } catch {
    // ignore
  }
  const nav = (typeof navigator !== "undefined" && navigator.language) || "en";
  return nav.toLowerCase().startsWith("zh") ? "zh" : "en";
}

export const locale = signal<Locale>(detectDefault());

const table = computed<Record<string, string>>(() => (locale.value === "zh" ? zh : (en as Record<string, string>)));

/**
 * Look up a translation key. When a dynamic value is needed, pass an
 * object and the translator substitutes `{key}` placeholders:
 *
 *   t("common.selected", { n: 3 })
 */
export function t(key: TranslationKey, vars?: Record<string, string | number>): string {
  const raw = table.value[key] ?? (en as Record<string, string>)[key] ?? key;
  if (!vars) return raw;
  return raw.replace(/\{(\w+)\}/g, (_, name) => String(vars[name] ?? `{${name}}`));
}

export function setLocale(next: Locale): void {
  if (locale.value === next) return;
  locale.value = next;
  try {
    localStorage.setItem(STORAGE_KEY, next);
  } catch {
    // ignore
  }
  if (typeof document !== "undefined") {
    document.documentElement.setAttribute("lang", next === "zh" ? "zh-CN" : "en");
  }
}

export function toggleLocale(): void {
  setLocale(locale.value === "en" ? "zh" : "en");
}

// Initialise <html lang> once on load so screenreaders + CSS selectors
// see the right language immediately.
if (typeof document !== "undefined") {
  document.documentElement.setAttribute("lang", locale.value === "zh" ? "zh-CN" : "en");
}
