Files
multica/packages/views/package.json
T
c1e1f11e21 MUL-6850 fix(i18n): localize onboarding status label and lint the attribute/toast copy path (#7776)
* MUL-6850 fix(i18n): localize onboarding status label and lint the attribute/toast copy path

Two leftovers from the post-merge review of #7754 / #7758.

1. `StepWelcome`'s illustration rendered `STATUS_CONFIG[...].label` — the
   server's English seed — while every other string on that screen went
   through `useT`. A zh/ja/ko member met the product on a line that read
   half in each language. Built-in statuses now resolve from the `issues`
   namespace, the same keys `useStatusLabel` uses for real issues; the
   illustration's statuses are hard-coded built-in keys, so no workspace
   status catalog lookup is involved. Covered by a new test that asserts
   against the real locale bundles.

2. The root cause behind #7758's ~30 hand-fixed strings: `packages/views`
   runs `i18next/no-literal-string` in `jsx-text-only` mode, so JSX
   attributes and `toast(...)` arguments were never linted at all.
   Widening the plugin to `jsx-only` is not viable — measured 2639 errors
   on this package, because it validates every literal anywhere inside
   JSX (`isColVisible("status")`, `{ month: "short" }`) and only ~10 are
   copy. Instead two `no-restricted-syntax` selectors pin the two doors
   copy actually escapes through, the way apps/desktop guards
   `shell.openExternal` and `router.navigate`.

   The guard surfaced 8 sites: `placeholder="Key"` / `"Value"` and two
   half-English aria-labels in the MCP key/value editor, `aria-label`s in
   the chat quick-actions row and the sub-issues select-all checkbox —
   all localized here — plus four token/command format hints (`npx`,
   `xoxb-…`, `xapp-…`, Telegram's token shape) that are exempted inline
   with a reason.

Verified: pnpm lint / typecheck / test green at the repo root
(410 files, 4864 tests). Locale parity holds across en / zh-Hans / ja / ko.

Co-authored-by: multica-agent <[email protected]>

* fix(i18n): close the attribute/toast guard's indirect-literal bypasses

The guard landed matching only a bare `Literal` child of the attribute or
the toast call, so every indirect shape still reached users untranslated
while `pnpm lint` stayed green:

  toast.error(err instanceof Error ? err.message : "Failed to save")
  toast.error(`Template failure`)
  <input title={"Expression copy"} />
  <input aria-label={`${label} key ${index}`} />

Widen both selectors to cover JSX expression containers, template
literals whose *static* chunks carry letters, and the fallback half of a
ternary / logical expression. A template that is pure interpolation of
already-translated values (`${a}: ${b}`) is still allowed, as are URLs.

The selectors move to eslint-i18n-guard.mjs so eslint-i18n-guard.test.ts
can lint fixtures against the very same strings the build enforces —
25 cases pinning both what must flag and what must not. Reverting to the
narrow selectors fails 8 of them.

Turning the guard on found 4 real sites, all now localized in en / zh-Hans
/ ja / ko:

- issue-detail.tsx — the per-row `Select {identifier}` sub-issue checkbox,
  sibling of the select-all checkbox this branch already fixed
- source-backfill-modal.tsx — both `"Failed to save"` toast fallbacks
- agent-avatar-stack.tsx — the `+N` overflow chip's `aria-label`

Co-authored-by: multica-agent <[email protected]>

* fix(i18n): separate the MCP pair aria label from its interpolated label

`{{label}} 键 {{index}}` renders "环境变量 键 1" when the label is itself
CJK — a space between two Chinese words, which the convention reserves for
an English/Chinese boundary. A bare space cannot be right for both label
kinds, since `label` is "环境变量" in one call site and "HTTP Headers" in
the other. A colon reads correctly for both:

  环境变量:键 1        HTTP Headers:键 1
  環境変数:キー 1      HTTP ヘッダー:キー 1

en and ko are unchanged — both space words natively, so "Environment
variables key 1" and "환경 변수 키 1" already read right.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: J <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Co-authored-by: J <[email protected]>
2026-08-31 14:26:15 +08:00

144 lines
5.1 KiB
JSON

{
"name": "@multica/views",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "eslint .",
"test": "vitest run"
},
"exports": {
"./navigation": "./navigation/index.ts",
"./common/actor-avatar": "./common/actor-avatar.tsx",
"./editor": "./editor/index.ts",
"./issues/components": "./issues/components/index.ts",
"./issues/hooks": "./issues/hooks/index.ts",
"./issues/utils/filter": "./issues/utils/filter.ts",
"./issues/utils/sort": "./issues/utils/sort.ts",
"./common/task-transcript": "./common/task-transcript/index.ts",
"./projects/components": "./projects/components/index.ts",
"./autopilots/components": "./autopilots/components/index.ts",
"./modals/registry": "./modals/registry.tsx",
"./modals/create-issue": "./modals/create-issue.tsx",
"./my-issues": "./my-issues/index.ts",
"./skills": "./skills/index.ts",
"./agents": "./agents/index.ts",
"./members": "./members/index.ts",
"./inbox": "./inbox/index.ts",
"./runtimes": "./runtimes/index.ts",
"./billing": "./billing/index.ts",
"./dashboard": "./dashboard/index.ts",
"./squads": "./squads/index.ts",
"./squads/components": "./squads/components/index.ts",
"./workspace/workspace-avatar": "./workspace/workspace-avatar.tsx",
"./workspace/no-access-page": "./workspace/no-access-page.tsx",
"./workspace/welcome-after-onboarding": "./workspace/welcome-after-onboarding.tsx",
"./workspace/use-workspace-seen": "./workspace/use-workspace-seen.ts",
"./layout": "./layout/index.ts",
"./layout/route-icon-components": "./layout/route-icon-components.tsx",
"./auth": "./auth/index.ts",
"./search": "./search/index.ts",
"./chat": "./chat/index.ts",
"./settings": "./settings/index.ts",
"./settings/lark-tab": "./settings/components/lark-tab.tsx",
"./lark": "./lark/index.ts",
"./slack": "./slack/index.ts",
"./dingtalk": "./dingtalk/index.ts",
"./wecom": "./wecom/index.ts",
"./telegram": "./telegram/index.ts",
"./invite": "./invite/index.ts",
"./invitations": "./invitations/index.ts",
"./onboarding": "./onboarding/index.ts",
"./platform": "./platform/index.ts",
"./i18n": "./i18n/index.ts",
"./attachments": "./attachments/index.ts",
"./locales": "./locales/index.ts",
"./locales/*": "./locales/*"
},
"dependencies": {
"@base-ui/react": "^1.3.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@floating-ui/dom": "^1.7.6",
"@multica/core": "workspace:*",
"@multica/ui": "workspace:*",
"@tanstack/react-virtual": "catalog:",
"@tiptap/core": "3.27.1",
"@tiptap/extension-code-block-lowlight": "3.27.1",
"@tiptap/extension-document": "3.27.1",
"@tiptap/extension-highlight": "3.27.1",
"@tiptap/extension-image": "3.27.1",
"@tiptap/extension-link": "3.27.1",
"@tiptap/extension-list": "3.27.1",
"@tiptap/extension-mention": "3.27.1",
"@tiptap/extension-paragraph": "3.27.1",
"@tiptap/extension-placeholder": "3.27.1",
"@tiptap/extension-table": "3.27.1",
"@tiptap/extension-table-cell": "3.27.1",
"@tiptap/extension-table-header": "3.27.1",
"@tiptap/extension-table-row": "3.27.1",
"@tiptap/extension-text": "3.27.1",
"@tiptap/extension-typography": "3.27.1",
"@tiptap/markdown": "3.27.1",
"@tiptap/pm": "3.27.1",
"@tiptap/react": "3.27.1",
"@tiptap/starter-kit": "3.27.1",
"@tiptap/suggestion": "3.27.1",
"cmdk": "^1.1.1",
"hast-util-to-html": "^9.0.5",
"katex": "catalog:",
"lowlight": "^3.3.0",
"mdast-util-from-markdown": "^2.0.3",
"mermaid": "catalog:",
"motion": "^12.38.0",
"pinyin-pro": "3.26.0",
"react-easy-crop": "^6.2.0",
"react-markdown": "^10.1.0",
"react-qr-code": "catalog:",
"react-resizable-panels": "^4.7.5",
"react-virtuoso": "catalog:",
"recharts": "3.8.0",
"rehype-katex": "catalog:",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-breaks": "^4.0.0",
"remark-cjk-friendly": "^2.3.1",
"remark-gfm": "^4.0.1",
"remark-math": "catalog:",
"sonner": "^2.0.7",
"unist-util-visit": "^5.1.0"
},
"peerDependencies": {
"@tanstack/react-query": "catalog:",
"@tanstack/react-table": "catalog:",
"i18next": "catalog:",
"lucide-react": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"react-i18next": "catalog:",
"zustand": "catalog:"
},
"devDependencies": {
"@multica/eslint-config": "workspace:*",
"@multica/plugin-sdk": "workspace:*",
"@multica/tsconfig": "workspace:*",
"@testing-library/jest-dom": "catalog:",
"@testing-library/react": "catalog:",
"@testing-library/user-event": "catalog:",
"@types/hast": "^3.0.4",
"@types/mdast": "^4.0.4",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@typescript-eslint/parser": "^8.35.0",
"@vitejs/plugin-react": "catalog:",
"eslint": "^9.0.0",
"eslint-plugin-i18next": "catalog:",
"jsdom": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
}
}