Conventions
Single source of truth for code naming, i18n translation glossary, and Chinese voice guide.
This page is the single source of truth for code naming, the i18n translation glossary, and the Chinese voice guide. Anything that used to live in packages/views/locales/glossary.md or in scattered comments now lives here.
If you write Melso code, change a translation, or write Chinese product copy, this is the page to reference.
1. Code naming
Routes
Pre-workspace routes (the routes that exist before the user is in a workspace) MUST use either a single word or the /{noun}/{verb} pattern.
- ✅
/login,/inbox,/workspaces/new - ❌
/new-workspace,/create-team,/accept-invite
Hyphenated word groups at the root collide with user-chosen workspace slugs and force endless reserved-slug audits. Reserving the noun (workspaces) automatically protects the entire /workspaces/* subtree.
Workspace-scoped routes
Always live under /{slug}/{section} — /{slug}/issues, /{slug}/agents, /{slug}/settings. Never duplicate workspace routing logic; use useNavigation().push() from shared code, never framework-specific link APIs.
Packages and modules
The monorepo enforces strict package boundaries:
| Package | May depend on | Must NOT depend on |
|---|---|---|
packages/core | nothing app-specific | react-dom, localStorage, process.env, next/*, UI libraries |
packages/ui | nothing | @multica/core, business logic |
packages/views | core/, ui/ | next/*, react-router-dom, stores |
apps/web/platform/ | next/* | other apps |
apps/desktop/.../platform/ | react-router-dom, electron | other apps |
apps/mobile/ | types and pure functions from @multica/core | Web/Desktop React pages, stores, and platform implementations |
If logic appears in both apps, it MUST be extracted to a shared package. There are no exceptions for "small" duplication. Mobile owns a separate UI, data layer, and release process; it shares only types and pure functions.
Files and components
- Files:
kebab-case.tsx/kebab-case.ts(e.g.agent-row-actions.tsx) - Components:
PascalCase(e.g.AgentRowActions) - Hooks:
useCamelCase(e.g.useWorkspaceId) - Tests: colocated as
<file>.test.ts(x) - Stores (Zustand):
<feature>-store.ts, exported asuse<Feature>Store
Database (Go + sqlc)
- Tables:
snake_casesingular (user,workspace,agent_runtime) - Columns:
snake_case(workspace_id,created_at,last_seen_at) - Foreign keys:
<table>_id - Booleans:
is_<state>or<state>_at(timestamp form preferred for state changes) - Migration files:
NNN_descriptive_name.up.sql+.down.sql— always provide both directions - Do not create database foreign keys or use cascade deletes or updates; enforce relationships and cleanup in the application layer.
- Every index must use
CREATE INDEX CONCURRENTLYorCREATE UNIQUE INDEX CONCURRENTLY, with each concurrent index in its own single-statement migration file.
Go
- Standard
gofmt+go vet. No exceptions. - Handler files mirror domain:
agent.go,auth.go,runtime.go - Tests:
<file>_test.gocolocated - For UUID parsing in handlers, follow the rule in the root
CLAUDE.md—parseUUIDOrBadRequestfor boundary input,parseUUID(panicking) for trusted round-trips, neverutil.ParseUUIDdirectly without checking the error.
TypeScript
- API responses on the wire are
snake_case; the api client converts tocamelCaseat the boundary. Inside TS code, always camelCase. - Types:
PascalCase(Issue,AgentRuntime); neverIPrefix, never_tsuffix. - Enums: prefer string literal unions; reserve
enumfor runtime-iterable cases. - TanStack Query keys: factory functions in
<feature>/queries.ts, e.g.issueKeys.detail(id).
API boundaries
- Parse network responses with
parseWithFallbackand zod schemas frompackages/core/api/schema.ts; do not cast them directly withas T. - When adding or changing an endpoint, update its schema and cover missing or malformed fields in tests.
- Downstream UI must provide defaults for optional fields, and every
switchover a server enum must include adefaultbranch. - An installed Desktop client may connect to a newer backend; never assume that frontend and backend versions always match.
Runtime display names
AgentRuntime.name is the daemon's raw technical name (e.g. Codex (host)); the user's alias lives in custom_name. User-visible copy must never render runtime.name directly — use the shared helpers so aliases and provider stay consistent (MUL-5248, #5260):
- Standalone runtime label (lists, chips, confirm dialogs, document titles):
runtimeDisplayLabel(runtime)→ alias + provider, falling back to the daemon name. - When a provider icon/text already sits beside it:
runtimeDisplayName(runtime)→ alias only, no duplicated provider. - Inside a machine group: machine header uses
machine.title, child rows useruntimeRowLabel(runtime, machine.title). - Runtime selectors group by machine via
buildRuntimeMachines; do not build a flat list of raw names.
Raw runtime.name is allowed only for internal identity — hostname parsing, grouping, search haystacks, and protocol payloads — never for JSX text, i18n params, <Select> labels, or window titles.
Issue keys
Every issue has a human-readable key like MUL-123: workspace issue_prefix (uppercase letters and digits, typically 3 chars, max 10) + sequence number. Workspace admins can change the prefix in Settings → General; changing it renumbers every existing issue, so external references that embed the old prefix (PR titles, branch names, links in docs and chat) stop resolving.
Comments in code
English only. The repo enforces this for both Go and TypeScript. If you find a Chinese comment in code, it's a bug — replace it.
Commit messages
Conventional format: feat(scope), fix(scope), refactor(scope), docs, test(scope), chore(scope). Atomic commits grouped by intent.
2. i18n translation glossary
This is the mandatory glossary for every translation PR. It used to live at packages/views/locales/glossary.md; that file is now a stub pointing here.
The core distinction: everyday noun vs Melso-specific term
Melso's product nouns split into two categories:
- Everyday noun — the word a user would say out loud for it. Translate fully, whether or not it is a database entity:
issue→ 任务,workspace→ 工作区,project→ 项目. - Melso-specific term — a concept no local word carries (
skill), or a schema-level identifier a user may have to type or match (todo,in_progress,task_id). Render as lowercase English so it reads like a type name.
The Chinese pages under apps/docs/content/docs/*.zh.mdx are the de facto voice standard for everything else on this page. They still carry the old English issue in prose and are being swept separately — follow the table below, not those pages, for this term.
issue is the product's task — translate it
issue is the English product name for the thing a user files and an agent works on. In every other locale it is the everyday word for "task":
| Entity | en | zh-Hans | ja | ko |
|---|---|---|---|---|
the filed unit of work (issue) | Issue | 任务 | タスク | 태스크 |
one agent execution run (task) | Task | task | 作業 | 작업 |
Both nouns are user-visible and they are not the same thing — an issue can carry many runs. Never let one locale spell both with the same word, which is why the run keeps a visually distinct form in each locale.
What does not move:
- API / DB fields stay
issue/task/skilleverywhere:issue_status,task_id,skill_uuid. - Code references and literal commands stay English:
melso issue ..., the/issueSlack and Lark slash command. skillkeeps lowercase English in Chinese text — a Melso-specific concept with no established Chinese term; titles may capitalize asSkills.issuein the "problem" sense (runtime health) is a plain noun, not the entity:{{count}} issueson a machine card is{{count}} 个异常, not任务.
Why issue is translated while skill is not: users file and read issues all day, and "issue" has no meaning in Chinese, Japanese, or Korean outside of dev jargon. The everyday task word is what people already say for this. skill is a Melso-specific concept where no local word carries the meaning.
Other product nouns follow the same "translate what has a settled local word" test:
project→ "项目": settled mainstream Chinese word. Feishu / Tower / Teambition / PingCode / GitHub Projects — every Chinese product translates it. No product keepsprojectin Chinese context.autopilot→ "自动化": in Chinese, "autopilot" associates with Tesla's "自动驾驶" and doesn't match what the feature does (run tasks on a schedule). Notion and Feishu both use "自动化"; that's the industry consensus.
Don't translate — brands and acronyms
| Category | Terms |
|---|---|
| Brands | Melso, GitHub, Slack, Google, Anthropic, OpenAI, Claude, Codex, Cursor, Linear, Jira |
| Acronyms | API, CLI, URL, SDK, OAuth, JWT, SSO, WebSocket, HTTP, JSON, YAML, SQL |
Translate fully — concepts
| English | Chinese |
|---|---|
| Workspace | 工作区 |
| Agent | 智能体 |
| Project | 项目 |
| Autopilot | 自动化 |
| Daemon | 守护进程 |
| Runtime | 运行时 |
| Inbox | 收件箱 |
| Comment | 评论 |
| Reply | 回复 |
| Notifications | 通知 |
| Member | 成员 |
| Label | 标签 |
| Settings | 设置 |
| Onboarding | 上手引导 |
Translate fully — generic UI words
| English | Chinese |
|---|---|
| Invite / Invitation | 邀请 |
| Search | 搜索 |
| 邮箱 (label) / 邮件 (action) | |
| Password | 密码 |
| Sign in / Log in | 登录 |
| Sign up | 注册 |
| Sign out / Log out | 退出登录 |
| Save / Cancel / Delete | 保存 / 取消 / 删除 |
| Confirm / Continue / Back | 确认 / 继续 / 返回 |
| Edit / New / Create / Add | 编辑 / 新建 / 创建 / 添加 |
| Remove / Send / Open / Close | 移除 / 发送 / 打开 / 关闭 |
| Preview / Download / Upload | 预览 / 下载 / 上传 |
| Done / Loading... | 完成 / 加载中... |
| Profile / Account / Appearance | 个人资料 / 账号 / 外观 |
| Theme / Language | 主题 / 语言 |
| Light / Dark / System | 浅色 / 深色 / 跟随系统 |
| Active / Archived | 活跃 (or 启用) / 已归档 |
| Status / Priority | 状态 / 优先级 |
| Assignee / Reporter | 负责人 / 报告人 |
| Description / Title | 描述 / 标题 |
| Date / Time | 日期 / 时间 |
| Today / Yesterday / Tomorrow | 今天 / 昨天 / 明天 |
| Empty / Failed / Success | 空 / 失败 / 成功 |
| Error / Warning | 错误 / 警告 |
Roles and status enums (lowercase English, not translated)
These are schema-level identifiers; render as lowercase English even in Chinese context.
- Roles:
owner/admin/member - Issue status:
backlog/todo/in_progress/in_review/done/blocked/cancelled
In UI, surface them in English (optionally code-style wrapped):
- "你需要 owner 权限"
- "已切换到 in_progress"
Word combination rules
Always put a single space between an English word (entity / brand / acronym) and surrounding Chinese:
- "Create new issue" → "新建任务"(
任务is Chinese, so no space) - "Assign to agent" → "分配给智能体"
- "Configure runtime" → "配置运行时"
- "Stop daemon" → "停止守护进程"
Plurals and counts
i18next uses _one / _other; Chinese has no grammatical number, only fill _other.
// en/issues.json
{
"issue_count_one": "{{count}} issue",
"issue_count_other": "{{count}} issues"
}
// zh-Hans/issues.json
{
"issue_count_other": "{{count}} 个任务"
}Common count formats:
{{count}} issues→{{count}} 个任务{{count}} agents→{{count}} 个智能体{{count}} workspaces→{{count}} 个工作区{{count}} comments→{{count}} 条评论{{count}} members→{{count}} 位成员{{count}} skills→{{count}} 个 skill
Interpolation
Use {{var}}. Chinese translations may reorder for natural sentence flow.
// en
{ "welcome_message": "Welcome back, {{name}}!" }
// zh-Hans
{ "welcome_message": "欢迎回来,{{name}}!" }Translation key naming
Three-level nesting: feature.component.action.
{
"feature_or_component": {
"subcomponent_or_section": {
"action_or_label": "..."
}
}
}Examples:
issues.toolbar.batch_update_successissues.detail.comment_form.placeholderinbox.empty.titlesettings.preferences.language.title
Web-only / desktop-only copy
- Shared copy: top level of the namespace JSON
- Web-only:
websection - Desktop-only:
desktopsection
See auth.json for the canonical example (the web section contains prefer_desktop / desktop_handoff.*).
3. Chinese voice and style
Punctuation
- Full-width punctuation in Chinese:
,。:;!? - Quotes: straight double quotes
"..."to match the English source. Do not use「」or curly quotes. - Ellipsis: three dots
...not the single character…. Match the English source. - Mixed Chinese-English: a single space on each side of the English word (see Word combination rules).
Style principles
- Concise and direct. Avoid translation-ese: "对于 X 来说"、"作为 X"、"我们的"。
- Error messages: gentle but clear. "无法保存修改" beats "保存修改失败了!".
- Buttons: verb first, 2–4 characters. "取消"、"保存修改"、"立即同步".
- Tooltips: full short sentence. "复制链接到剪贴板".
- Placeholders: example-style. "输入任务标题...".
Where to look when in doubt
When the glossary doesn't cover a term, look at:
apps/docs/content/docs/*.zh.mdx— the de facto Chinese voice standard, 20+ pages of consistent translationpackages/views/locales/zh-Hans/auth.jsonandeditor.json— JSON structure + selector API patternspackages/views/auth/login-page.tsx— component-level selector API call sitepackages/views/settings/components/preferences-tab.tsx— language switcher reference
Updating this page
If you change a rule here, also:
- Apply it in the relevant locale JSONs / CLAUDE.md / docs page
- Note the change in the PR description so reviewers know to look for downstream sweep
This page is the contract; nothing else overrides it.