-
Notifications
You must be signed in to change notification settings - Fork 1
Description
评估方法
对每种类型,我从 4 个维度评估:
- 当前状态 — 代码里实际怎么写的
- 翻译覆盖 —
en.ts/zh.ts里有没有 - Salesforce 做法 — 它翻不翻,怎么翻
- ObjectStack 建议 — 要不要做,怎么做
1️⃣ Object Label(对象显示名)
代码现状:
label: 'Order Item' // 纯字符串| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ objects.order_item.label: '订单明细' — 已有 |
| 运行时 | ❌ 未接入,页面标题仍显示英文 |
| Salesforce | ✅ <CustomObjectTranslation><label> — 每对象必翻 |
| 主流低代码 | Airtable/Notion: 不分语言。Salesforce/Dynamics: 必翻 |
| 优先级 | P0 — 最高频可见 |
| 评估结论 | 必须做。用户进入任何列表页第一眼看到的就是这个 |
额外优化:Salesforce 还支持 pluralLabel(复数形式),如 "Accounts" vs "Account"。ObjectStack 当前不需要,因为 React 端不区分单复数标题,但翻译节点可预留此能力。
2️⃣ Object Description(对象描述)
| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ objects.order_item.description: '客户订单中的单个行项目' — 已有 |
| 运行时 | ❌ 未接入 |
| Salesforce | ✅ 翻(对象详情页、帮助提示) |
| 优先级 | P1 — 用户偶尔看到(对象详情头部、搜索结果) |
| 评估结论 | 做。已有翻译数据,接入成本为 0 |
3️⃣ Field Label(字段显示名)
代码现状:
unit_price: Field.number({ label: 'Unit Price', ... })| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ fields.order_item.unit_price: '单价' — 已有,覆盖全部 ~98 个字段 |
| 运行时 | ❌ 表头、表单都显示英文 |
| Salesforce | ✅ <fields><name>...<label> — 核心翻译项 |
| 优先级 | P0 — 最高频可见 |
| 评估结论 | 必须做。这是截图中问题最明显的地方:Name / Order / Product / Quantity 全是英文 |
4️⃣ Field Description / Help Text(字段帮助文本)
代码现状:
Spec 的 FormFieldSchema 支持 helpText:
interface FormField {
field: string;
label?: string;
placeholder?: string;
helpText?: string; // ← 存在但 CRM 对象未使用
}| 维度 | 状况 |
|---|---|
| CRM 对象定义 | ❌ 当前无任何对象使用 helpText |
| 翻译文件 | ❌ 无 |
| Salesforce | ✅ <fields><helpText> — 鼠标悬停时的帮助提示 |
| 主流低代码 | OutSystems: 支持。Appsmith: 支持 tooltip |
| 优先级 | P3 — 未来扩展 |
| 评估结论 | 暂不做。CRM 示例没用 helpText,但翻译节点应预留 _helpText 位置。当元数据开始使用 helpText 时,翻译框架自动就能覆盖 |
建议:在 ObjectTranslationNode 里预留:
_helpText?: Record<string, string>; // field_name → translated help text5️⃣ Field Placeholder(字段占位符文本)
代码现状: Spec FormFieldSchema 支持 placeholder,但 CRM 示例同样未使用。
| 维度 | 状况 |
|---|---|
| CRM 使用 | ❌ 无 |
| Salesforce | ❌ 不翻(placeholder 不在 Translation Workbench 范围) |
| 主流低代码 | 多数不翻 placeholder |
| 优先级 | P3 — 未来扩展 |
| 评估结论 | 暂不做。但如果 CRM 开始用 placeholder,应纳入 _placeholder 节点 |
6️⃣ Select / Picklist Option Label(选项值翻译)
代码现状:
role: Field.select([
{ value: 'decision_maker', label: 'Decision Maker', color: 'red' },
{ value: 'influencer', label: 'Influencer', color: 'blue' },
])| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ fieldOptions.opportunity_contact.role.decision_maker: '决策者' — 覆盖全部 ~47 个选项 |
| 运行时 | ❌ 表格里的 Badge(Product / Service)仍显示英文 |
| Salesforce | ✅ <fields><picklistValues><translation> — 核心翻译项 |
| 主流低代码 | 所有平台都翻。这是国际化最容易漏的地方 |
| 优先级 | P0 |
| 评估结论 | 必须做。截图中 "Product" / "Service" Badge 正是选项值未翻译的体现 |
额外优化:Salesforce 的 Global Value Set(全局选项集)可以跨对象复用。当 lead_source 同时出现在 Contact 和 Opportunity 时,翻译只需写一次。ObjectStack 的 fieldOptions 当前是按对象隔离的,存在重复(如 contact.lead_source 和 opportunity.lead_source 值完全相同)。
建议:增加 _globalOptions 节点,用于跨对象复用的选项集翻译,避免重复维护。
7️⃣ View / List View Label(视图标签)
代码现状:
listViews: {
all: { label: 'All Opportunities', ... },
pipeline: { label: 'Pipeline', type: 'kanban', ... },
}| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ views.opportunity.pipeline: '管道' — 已有 |
| 运行时 | ❌ 视图切换 Tab 显示英文 |
| Salesforce | ✅ <layouts><sections> + ListView label |
| 优先级 | P1 |
| 评估结论 | 做。视图 Tab 是用户高频交互点 |
8️⃣ Form Section Title(表单分区标题)
代码现状:
{ label: 'Deal Information', columns: '2', fields: [...] }
{ label: 'Financials & Stage', columns: '2', fields: [...] }| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ formSections.opportunity.dealInfo: '交易信息' — 已有 |
| 运行时 | ❌ 表单分区标题显示英文 |
| Salesforce | ✅ <layouts><sections> — Layout section labels |
| 优先级 | P1 |
| 评估结论 | 做。编辑记录时必经之路 |
注意问题: 当前翻译 key 用的是 formSections.opportunity.dealInfo(camelCase),但 section 在 view 元数据中没有显式 name 字段——它���由 label 字符串隐式标识的。这意味着 runtime 需要建立 label→sectionId 的映射。建议在 view 定义中给每个 section 加一个 name 或 id 字段。
9️⃣ Action Label(操作按钮标签)
代码现状:
name: 'event_send_invitation',
label: 'Send Invitation',| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ actions.event_send_invitation.label: '发送邀请' — 已有 |
| 运行时 | ❌ 未接入 |
| Salesforce | ✅ Quick Actions label 可翻 |
| 优先级 | P1 |
| 评估结论 | 做。按钮是核心操作交互 |
🔟 Action confirmText(操作确认提示)
代码现状:
confirmText: 'Are you sure you want to cancel this event?'| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ actions.event_cancel.confirmText: '确定要取消此事件吗?' |
| Salesforce | |
| Dynamics 365 | ✅ Custom confirmation dialog text 可翻 |
| 优先级 | P2 |
| 评估结论 | 做。但优先级低于按钮标签本身 |
1️⃣1️⃣ Action successMessage(操作成功提示)
| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ actions.event_cancel.successMessage: '事件已取消' |
| Salesforce | ❌ 不翻(Toast 是系统级) |
| 优先级 | P2 |
| 评估结论 | 做。ObjectStack 自定义了 toast 消息,需要翻译 |
1️⃣2️⃣ Action Param Label(操作参数标签) ⚠️ 被遗漏的类型
代码现状:
params: [
{ name: 'subject', label: 'Subject', type: 'text', required: true },
{ name: 'body', label: 'Message', type: 'textarea' },
]| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ actionParams.subject: '主题' — 已有但是全局 flat 结构,未按对象/action 分组 |
| 运行时 | ❌ 弹窗参数表单显示英文 |
| Salesforce | ✅ Quick Action param labels 可翻 |
| 优先级 | P1 — 用户执行操作时必然会看到参数弹窗 |
| 评估结论 | 必须做。且当前 key 设计有问题 |
关键问题: 当前 actionParams 是全局 flat 的(actionParams.subject: '���题'),但不同 action 的 "subject" 参数可能需要不同翻译。例如"发送邮件"的 subject 是"主题","日志"的 subject 可能是"标题"。应该改为按 action 分组。
1️⃣3️⃣ Action Param Validation Message(参数验证错误)
代码现状:
newErrors[p.name] = `${p.label} is required`; // 硬编码英文!| 维度 | 状况 |
|---|---|
| 翻译文件 | ❌ 无 |
| Salesforce | ✅ Validation rule errorMessage 可翻 |
| 优先级 | P2 |
| 评估结论 | 做。当前 "${p.label} is required" 是硬编码英文,应该改为 t('validation.required', { field: translatedLabel }) |
这不是元数据翻译,是框架级 UI 文案翻译,属于不同层面的问题。
1️⃣4️⃣ Confirm Dialog Button Text(确认弹窗按钮文案)
代码现状:
<Button variant="outline" onClick={onCancel}>Cancel</Button> // 硬编码
<Button onClick={handleSubmit}>Continue</Button> // 硬编码| 维度 | 状况 |
|---|---|
| Salesforce | ✅ confirm.confirmText / confirm.cancelText 可翻 |
| Spec 支持 | ✅ ActionSchema.confirm.confirmText / cancelText 已有类型定义 |
| 翻译文件 | ❌ 无 |
| 优先级 | P2 |
| 评估结论 | 做,但分两层:① 元数据层的自定义 confirmText/cancelText ② 框架默认 "Cancel"/"Continue" |
1️⃣5️⃣ Navigation Section Label(导航分组标题)
代码现状: CRM App 导航有分组(Sales / Reports),在 crm.app.ts 中作为 NavigationArea.label 定义。
| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ navigation.sales: '销售', navigation.reports: '报表' |
| 运行时 | ✅ 已生效(Navigation 已用 I18nLabel) |
| 优先级 | 已完成 ✅ |
1️⃣6️⃣ Dashboard Title / Description / Widget Title
| 维度 | 状况 |
|---|---|
| 运行时 | ✅ Title 和 Widget Title 已生效(已用 I18nLabel) |
| Description | ❌ 仍为纯字符串 'Revenue metrics, pipeline analytics...' |
| 优先级 | P2(description) |
| 评估结论 | Description 补上。Title/Widget Title 不需要动 |
1️⃣7️⃣ Dashboard Column Labels(仪表盘表格列名)
代码现状: 翻译文件有 dashboard.columns.opportunityName: '商机名称',但 dashboard widget 的 table 列定义中是直接引用字段名,理论上会被字段翻译覆盖。
| 维度 | 状况 |
|---|---|
| 是否独立翻译项 | |
| Salesforce | Dashboard 不单独翻列名,用字段翻译 |
| 评估结论 | 移除 dashboard.columns 翻译。这些列名就是字段名,应该复用字段翻译,不要维护两份 |
1️⃣8️⃣ Report Label / Description / Column Labels
| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ 全有 |
| 运行时 | ❌ 未接入 |
| Salesforce | ✅ Report type label 可翻,但列名复用字段翻译 |
| 优先级 | P1(label)/ P2(description)/ |
关键优化:reports.columns 里的 orderNumber/account/amount 和 fields.order.name/fields.order.account 是同一个字段。应该删除 reports.columns,让报表列自动从字段翻译获取。
1️⃣9️⃣ Page Title / Content(页面标题和内容)
| 维度 | 状况 |
|---|---|
| 翻译文件 | ✅ pages.gettingStarted.title: '开始使用 CRM' 等 |
| 运行时 | ❌ 页面元数据中用纯字符串 |
| Salesforce | ✅ In-App Guidance / Custom Page labels 可翻 |
| 优先级 | P2 — 页面内容较长,翻译量大但频率低 |
| 评估结论 | 做。但页面内容体量大(markdown 段落),可考虑后期接 AI 翻译 |
2️⃣0️⃣ Select Option Color(选项颜色) — 不需要翻译
| 维度 | 状况 |
|---|---|
| 代码 | { value: 'hot', label: 'Hot', color: 'red' } |
| 评估结论 | ❌ 不翻译。颜色是视觉属性,与语言无关 |
2️⃣1️⃣ Object Icon — 不需要翻译
| 评估结论 | ❌ 不翻译。图标与语言无关 |
2️⃣2️⃣ Field format / searchable / required 等属性 — 不需要翻译
这些是数据行为属性,不是面向用户的文案。
📊 完整评估汇总表
| # | 类型 | 当前有翻译 | 运行时已生效 | 是否冗余 | Salesforce翻 | 优先级 | 结论 |
|---|---|---|---|---|---|---|---|
| 1 | Object label | ✅ | ❌ | ✅ | P0 | ✅ 做 | |
| 2 | Object description | ✅ | ❌ | ✅ | P1 | ✅ 做 | |
| 3 | Object pluralLabel | ❌ | — | ✅ | P3 | 🔮 预留 | |
| 4 | Field label | ✅ | ❌ | ✅ | P0 | ✅ 做 | |
| 5 | Field helpText | ❌ | — | ✅ | P3 | 🔮 预留 | |
| 6 | Field placeholder | ❌ | — | ❌ | P3 | 🔮 预留 | |
| 7 | Select option label | ✅ | ❌ | ✅ | P0 | ✅ 做 | |
| 8 | View label | ✅ | ❌ | ✅ | P1 | ✅ 做 | |
| 9 | Form section title | ✅ | ❌ | ✅ | P1 | ✅ 做 | |
| 10 | Action label | ✅ | ❌ | ✅ | P1 | ✅ 做 | |
| 11 | Action confirmText | ✅ | ❌ | P2 | ✅ 做 | ||
| 12 | Action successMessage | ✅ | ❌ | ❌ | P2 | ✅ 做 | |
| 13 | Action param label | ❌ | ✅ | P1 | ✅ 做 + 重构 key | ||
| 14 | Action param validation | ❌ | ❌ | ✅ | P2 | ✅ 框架层 | |
| 15 | Confirm dialog buttons | ❌ | ❌ | ✅ | P2 | ✅ 框架层 | |
| 16 | Navigation label | ✅ | ✅ | ✅ | ✅ 已完成 | ||
| 17 | Dashboard title | ✅ | ✅ | ✅ | ✅ 已完成 | ||
| 18 | Dashboard description | ✅ | ❌ | ✅ | P2 | ✅ 做 | |
| 19 | Widget title | ✅ | ✅ | ✅ | ✅ 已完成 | ||
| 20 | ✅ | ❌ | ❌ | — | ❌ 删除,复用字段翻译 | ||
| 21 | Report label | ✅ | ❌ | ✅ | P1 | ✅ 做 | |
| 22 | Report description | ✅ | ❌ | ✅ | P2 | ✅ 做 | |
| 23 | ✅ | ❌ | ❌ | — | ❌ 删除,复用字段翻译 | ||
| 24 | Page title/content | ✅ | ❌ | ✅ | P2 | ✅ 做 | |
| 25 | App label | ✅ | ✅ | ✅ | ✅ 已完成 | ||
| 26 | App description | ✅ | ✅ | ✅ | ✅ 已完成 |
🔑 发现的 5 个关键优化点
优化 1:删除冗余翻译(dashboard.columns + reports.columns)
dashboard.columns 和 reports.columns 里的 key 和字段翻译重复:
| 冗余 key | 实际来源 |
|---|---|
dashboard.columns.amount: '金额' |
= fields.opportunity.amount: '金额' |
reports.columns.orderNumber: '订单编号' |
= fields.order.name: '订单编号' |
reports.columns.account: '客户' |
= fields.order.account: '客户' |
应该删除这两个节点,让 dashboard table 和 report table 自动从字段翻译获取列名。这减少 ~16 个冗余翻译条目。
优化 2:Action Params 重构为按 Action 分组
当前 flat 结构无法区分同名参数:
// 当前问题
actionParams: {
subject: 'Subject', // send_email 的 subject?还是 log_call 的 subject?
message: 'Message', // 哪个 action 的 message?
}应该改为嵌套在 action 节点下:
// 推荐
o.account._actions.send_email.params.subject: '邮件主题'
o.account._actions.send_email.params.body: '邮件正文'
o.contact._actions.log_call.params.call_subject: '通话主题' // 不同翻译优化 3:全局选项集(Global Value Set)复用
lead_source 的选项同时出现在 Contact 和 Opportunity 中,翻译完全重复。参考 Salesforce GlobalValueSetTranslation,建议增加全局选项集翻译节点,被多个对象的字段引用。
优化 4:预留 Field helpText / placeholder
虽然 CRM 当前未使用,但翻译节点应预留位置,为未来 helpText/placeholder 国际化做准备。
优化 5:框架级 UI 文案独立翻译层
action-param-dialog.tsx 中的 "Cancel" / "Continue" / "{field} is required" 是框架组件硬编码。这不属于元数据翻译,而是 ObjectUI 框架本身的 UI 文案,需要单独的翻译层(类似 antd 的 locale)。
📐 最终翻译节点结构(修订版)
const zh = {
o: {
account: {
// ── 对象元属性 ──
_label: '客户',
_description: '用于客户关系管理的公司和组织记录',
// _pluralLabel: '客户', // 🔮 预留
// ── 字段标签(非保留 key = 字段名)──
name: '客户名称',
industry: '行业',
rating: '评级',
type: '类型',
// ...
// ── 字段扩展属性 ──
// _helpText: { industry: '...' }, // 🔮 预留
// _placeholder: { name: '...' }, // 🔮 预留
// ── 选项值 ──
_options: {
industry: { technology: '科技', finance: '金融' },
rating: { hot: '热门', warm: '温暖', cold: '冷淡' },
type: { customer: '客户', partner: '合作伙伴' },
},
// ── 视图 ──
_views: {
all_accounts: '所有客户',
active_accounts: '活跃客户',
},
// ── 表单分区 ──
_sections: {
basicInfo: '基本信息',
financial: '财务信息',
address: '地址',
additionalDetails: '其他信息',
},
// ── 操作(含参数)──
_actions: {
send_email: {
label: '发送邮件',
successMessage: '邮件发送成功',
params: { // ← 新增:参数按 action 归属
subject: '邮件主题',
body: '邮件正文',
},
},
assign_owner: {
label: '分配负责人',
successMessage: '负责人分配成功',
params: { newOwner: '新负责人' },
},
merge: {
label: '合并客户',
confirmText: '确认要合并这些客户吗?此操作不可撤销。',
},
},
},
// ... 其他对象
},
// ── 全局选项集(跨对象复用)──
_globalOptions: { // ← 新增
lead_source: {
web: '网络', phone: '电话', partner: '合作伙伴',
referral: '推荐', trade_show: '展会', other: '其他',
},
},
// ── 全局翻译 ──
app: { name: 'CRM', description: '销售管道、客户和客户关系管理' },
nav: { dashboard: '仪表盘', contacts: '联系人', /* ... */ },
dashboard: {
title: 'CRM 概览',
description: '营收指标、管道分析和交易洞察', // ← 新增
widgets: { totalRevenue: '总营收', /* ... */ },
trendLabel: '较上月',
// columns: 已删除,复用字段翻译
},
reports: {
salesReport: { label: '销售报表', description: '按客户和产品类别的月度销售分析' },
pipelineReport: { label: '管道报表', description: '销售管道阶段分析' },
// columns: 已删除,复用字段翻译
},
pages: { /* 不变 */ },
};对比现有结构的变更清单
| 变更 | 原因 | 影响 |
|---|---|---|
删除 dashboard.columns |
冗余,复用字段翻译 | -4 keys × 10 locales = -40 条 |
删除 reports.columns |
冗余,复用字段翻译 | -12 keys × 10 locales = -120 条 |
重构 actionParams → 嵌入 _actions.*.params |
消除同名参数歧义 | ~18 keys 迁移 |
新增 _globalOptions |
消除跨对象选项值重复 | 抽取 lead_source 等共享选项集 |
预留 _helpText / _placeholder / _pluralLabel |
未来扩展 | 0 当前改动,纯约定 |
| 结构重组 Category-first → Object-first | 翻译者友好、工具友好 | 一次性迁移 |
最终减少 ~160 条冗余翻译,新增全局选项集复用机制,为翻译工作台的自动检测扫清了结构障碍。