mcpField
E
experience-ui-bundle-localize

experience-ui-bundle-localize

github.com/forcedotcom开发1.1k2.2k 次安装1 天前更新
代码仓库

关于 experience-ui-bundle-localize

用于为 Salesforce UI Bundle 添加本地化能力的技能,面向维护 React 或 Angular 界面的开发者。它将界面字符串提取为 Custom Labels,并通过 Platform SDK 和 GraphQL 在运行时获取标签供 i18n 库呈现。

主要功能

  1. 01扫描并识别面向用户的字符串
  2. 02将字符串替换为 Custom Labels
  3. 03通过清单注册标签键
  4. 04借助 Platform SDK 与 GraphQL 获取标签
  5. 05检测框架、Bundle 类型及组织 API 版本

适用场景

  1. 01为现有 React 或 Angular UI Bundle 添加多语言文本
  2. 02检查无障碍标签等界面字符串是否已本地化
  3. 03为 Salesforce 组织配置运行时标签加载
  4. 04确认项目及组织是否满足本地化前置条件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill experience-ui-bundle-localize

安装后在 Claude Code 中输入/experience-ui-bundle-localize确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/angular/
check-i18n-wired.sh
i18n-setup.md
interpolation.md
localize.md
references/common/
gotchas.md
label-xml.md
platform-sdk-i18n.md
verifying.md
references/react/
check-i18n-wired.sh
i18n-setup.md
interpolation.md
localize.md
scripts/
check-manifest-registered.sh
check-org-api-version.sh
detect-bundle-type.sh
detect-framework.sh
scripts/tests/
test-detect-bundle-type.sh

详细介绍

本地化 UI Bundle

本地化 UI Bundle:将面向用户的字符串提取到 Salesforce 自定义标签中,通过 Platform SDK 后端接入运行时 i18n 库,并验证各语言区域中的标签。

本文是与框架无关的工作流与防护指南核心。与框架相关的细节——使用哪种 i18n 库、翻译调用约定、扫描哪些文件、接线方式以及文档深度——请参阅各框架的参考文档。

一段话理解

UI Bundle 无法像 LWC 那样使用编译时标签导入(@salesforce/label/* 会由平台编译器解析,而独立的 Bundle 不会经过该编译器)。相反,应用会通过 Salesforce GraphQL UI API 在运行时获取标签,并交由标准 i18n 库进行渲染。Platform SDK 提供运行时所需的基础组件:用于读取用户语言的检测器、通过 GraphQL 获取标签的后端,以及上下文获取功能。你只需编写两个精简文件——一个简短的初始化文件,将 SDK 组件接入 i18n 库;另一个清单,列出应用使用的标签——然后将标签本身编写为 Salesforce 自定义标签元数据。具体使用的库和调用约定因框架而异;请参阅相应框架的参考文档。

步骤 0:确定任务路径

任务是……前往
Bundle 尚不存在experience-ui-bundle-frontend-generate 技能
使用其标签部署应用experience-ui-bundle-deploy 技能
配置站点语言或 sfdc_cms__languageSettingsexperience-ui-bundle-site-generate 技能
本地化现有 bundle确定框架(见下文),然后按工作流操作

确定框架。 通常,调用上下文已经确定了框架——由调用此技能的协调技能传递,或由用户在请求中说明。请使用该框架。

此技能支持的框架,恰好是 <SKILL_DIR>/references/ 下各自包含 localize.md 的参考文件夹(例如,react 对应 <SKILL_DIR>/references/react/localize.md)。这是唯一可信来源——要添加框架,只需添加参考文件夹,无需更改本文档。

  • 如果框架已知——打开 <SKILL_DIR>/references/<framework>/localize.md,并将其与本文档一并参照。该文件会说明所用库、调用约定、要扫描的文件以及接线代码。

  • 如果框架未知(独立运行且无人说明框架)——在询问任何人之前,先对应用或 uiBundle 根目录运行确定性检测器:

    bash "<SKILL_DIR>/scripts/detect-framework.sh" "<app-or-uiBundle-root>"
    

    它会结合根目录或其上级目录中的 angular.json、任何非 node_modules package.json 中的 @angular/core / react,以及源文件特征,然后打印一个标记并设置相应的退出码:

    • react 或 angular(退出码 0)→ 使用该框架。不要询问用户——检测结果是确定的。打开 <SKILL_DIR>/references/<framework>/localize.md。
    • ambiguous(退出码 2)→ 两种框架均存在。列出 <SKILL_DIR>/references/ 并询问用户要本地化哪一种。如果用户指定的框架没有对应的参考文件夹,则此处不支持该框架——停止。
    • unknown(退出码 3)→ 未检测到受支持的框架。终止工作流。 不要猜测,也不要继续:报告 bundle 中未发现 React 或 Angular 特征,因此无法继续本地化,并在此停止。

在下列步骤中,<framework> 均指此处选定的文件夹。确定性检查脚本按耦合关系分为以下两类:

  • 框架无关,位于 <SKILL_DIR>/scripts/——detect-framework.sh(上述步骤 0 检测器)、check-org-api-version.sh 和 detect-bundle-type.sh(纯 org/元数据检查),以及 check-manifest-registered.sh(通用骨架;通过 --framework <framework> 选择调用点语法)。
  • 框架专属,位于 <SKILL_DIR>/references/<framework>/ 下——check-i18n-wired.sh(其将清单接入后端的检测方式与 i18n 库相关,因此每个框架都提供自己的脚本)。

前置条件:编辑前进行验证

#要求验证方式缺失时的处理
1这是一个 uiBundles/*/src/ 项目(React 或 Angular)项目结构符合要求不是 UI Bundle → 转到正确的技能
2Platform SDK、UI Bundle 和 build-plugin 同级依赖均已安装且版本一致(≥11.49.3)检查 UI Bundle 目录中的 package.json告知用户需要统一版本并升级;无法继续
3能确定应用的挂载位置阅读入口文件(参见框架参考)没有明确的挂载位置 → 请用户指出
4目标组织确实支持 API v68.0+(UI Bundle 的运行时标签 GraphQL 随 Release 264 发布)执行下方的运行时组织版本检查组织的最高 API 版本低于 v68.0(Release 262 或更早版本)→ 无法继续;改用 Release 264+ 组织,或升级该组织
5Bundle 已通过身份验证,为 B2E;或者请求/上下文明确指出这是 B2C 站点,而非 B2B执行下方的 Bundle 类型检测,并使用请求/上下文判断站点产品类型明确为 B2B → 拒绝;站点类型不明确 → 询问用户并暂停,直到得到确认;B2C 还需满足前置条件 6
6仅针对 B2C:管理员已启用 GraphQLApiOrgPrefForGuestUsers请管理员确认组织偏好设置已启用不要启用该设置;说明未启用时,访客 GraphQL 请求会返回 HTTP 403,并停止(依赖项:W-23854208)

运行时组织版本检查(前置条件 4)。 在运行时为 UI Bundle 解析标签的 platform.labels GraphQL 路径随 Salesforce Release 264 发布(API v68.0 或更高版本)。sfdx-project.json 中的 sourceApiVersion 记录的是你声明的版本,而不是组织支持的版本,因此,使用较新 CLI 指向较旧组织时,静态文件检查可能通过,但运行时会失败。在配置任何内容之前,先查询组织实际支持的最高 API 版本:

bash <SKILL_DIR>/scripts/check-org-api-version.sh <org-alias-or-username>

退出码为 0 → 组织支持 v68.0+,继续。退出码为 1 → 组织版本过旧或无法访问;不要配置 i18n 或标签,向用户报告版本不匹配并停止。(脚本中的 sf api request rest 会将身份验证保留在 CLI 传输层,因此访问令牌不会进入上下文。)

Bundle 类型检测(前置条件 5)。 Bundle 类型决定适用的本地化分支。此检测与框架无关(纯 Salesforce 元数据)。传入 Bundle 目录的完整路径;脚本会据此推导元数据根目录,因此当前目录无关紧要:

bash <SKILL_DIR>/scripts/detect-bundle-type.sh <path-to-uiBundles/<name>/ dir>

按照退出码约定处理:0 → 已通过身份验证的应用(B2E 或核心内部应用),使用 B2E 分支;10 → 绑定到公开站点的应用容器候选项,表示元数据证明了站点绑定和访客访问,但不能证明是 B2C 还是 B2B;11 → 绑定到非公开/不受支持的站点,停止;12 → 同时存在 CustomApplication 和一个站点绑定,询问用户要将哪个运行时上下文作为本地化目标;13 → 存在多个匹配的 Experience 站点绑定,显示报告中的站点名称,并询问用户要将哪个站点/运行时上下文作为目标;2 → 未绑定/未知,报告脚本输出并停止,不要猜测。

对于退出码 10,根据明确的请求/上下文进行分流:如果说明是 B2C,确认前置条件 6 并使用 B2C 分支;如果说明是 B2B,则拒绝;如果产品类型不明确,询问用户站点是 B2C 还是 B2B,并暂停,直到得到确认。对于退出码 12 和 13,要求用户选择运行时上下文(退出码 13 还需选择站点),然后应用相应分支的回退方案和前置条件。绝不要根据 DigitalExperienceConfig、appSpace、appContainer 或 AUTHENTICATED_WITH_PUBLIC_ACCESS_ENABLED 推断为 B2C;身份验证值表示已启用访客访问。

对于 B2C,只有组织管理员可以启用 GraphQLApiOrgPrefForGuestUsers;绝不要代为配置或更改此设置。未启用时,访客标签请求会返回 HTTP 403。通过 W-23854208 跟踪其可用性。 如果不满足前置条件,请停止:向用户报告具体的阻碍,并记录一项计划,待问题解决后再继续。不要为 B2B 或未知 bundle 添加 i18n 配置或 TODO 标记。


工作流:五个步骤

每个步骤都有完成标准和一个继续前确认暂停点。框架的具体信息(文件扩展名、翻译调用、安装、初始化代码)来自 <SKILL_DIR>/references/<framework>/localize.md。

步骤 1:检测

**目标:**扫描框架的组件文件,查找面向用户的硬编码字符串。(框架参考文档会列出要扫描的文件扩展名。)

扫描内容:

  • 标记语言中向用户显示的字符串:标题中的 Welcome → 候选项
  • 面向用户的字符串属性:placeholder="Enter name" → 候选项
  • 面向用户的无障碍文本:aria-label、aria-describedby、alt → 候选项(屏幕阅读器用户会听到这些内容,因此也必须本地化)

跳过内容:

  • 导入语句
  • 对象键/属性名
  • data-* 属性(机器可读)
  • 测试 ID(data-testid、id 属性)
  • 已经封装在翻译调用中的文本
  • 控制台日志、抛给开发者的错误消息(非面向用户)
  • 类名、文件路径、技术常量

操作:

  1. 扫描 src/ 目录中的框架组件文件
  2. 提取候选项,并列出每项的文件路径和行号
  3. 向开发者展示列表

完成标准: 开发者确认列表(或对列表进行编辑以移除误报)。

暂停:“我在 M 个组件中找到 N 个面向用户的字符串。列表如下:[显示文件路径:行号 + 字符串]。看起来对吗?[确认/编辑列表/跳过部分项]”


步骤 2:提取

**目标:**为每个已确认的字符串添加 Custom Label,并将字面文本替换为翻译调用。

每个字符串的操作:

  1. 提出一个键名,格式为:<Context>_<Role>(例如,"Welcome" → Welcome_Text,"Save" → Save_Button,"Failed to save" → Save_Failed_Message)。遵循命名规则:单词使用 PascalCase,各部分之间用下划线分隔,并确保名称足够明确且唯一。
  2. 将标签添加到 force-app/main/default/labels/CustomLabels.labels-meta.xml:
    <labels>
      <fullName>Welcome_Text</fullName>
      <language>en_US</language>
      <protected>false</protected>
      <shortDescription>Welcome banner heading</shortDescription>
      <value>Welcome</value>
    </labels>
    
    (完整 XML 结构:references/common/label-xml.md)
  3. 在组件中将字符串替换为相应框架的翻译调用,并添加所需的导入/注入。具体调用方式见框架参考文档。

完成标准: 每个已确认的字符串都在其原位置拥有一个 CustomLabels 条目和一个翻译调用。

暂停:“对于每个字符串,我都会添加一个 Custom Label,并将字面文本替换为翻译调用。拟议的键名如下:[显示 字符串 → 命名空间:键名 的映射]。应用这些编辑吗?[是/逐项检查]”

步骤 3:注册

目标: 将每个键添加到标签清单中,以便 i18n 运行时知道要获取哪些内容。

操作:

  1. 将每个键添加到 src/i18n/label-manifest.ts 中的清单数组:
    export const labelManifest = [
      "c:Welcome_Text",
      "c:Save_Button",
      "c:Save_Failed_Message",
    ];
    
    如果该文件尚不存在,步骤 4 会创建它;下方的完成检查会报告文件缺失,因此不要手动检查文件是否存在。

完成标准: 在 UI 包目录中运行 check-manifest-registered.sh(它会相对于当前目录扫描 src/),并报告它返回的任何错误。该脚本负责确定性检查:它会将每个翻译调用点与清单交叉核对,并在存在调用点但缺少 label-manifest.ts 时判定为失败。已调用但未注册的键在运行时会直接以其字面名称显示,且不会报错;本检查用于防范这种静默失败陷阱。

cd <path-to-uiBundles/<name>/ dir>   # scripts scan src/ relative to here
bash <SKILL_DIR>/scripts/check-manifest-registered.sh --framework <framework>

根据退出码采取相应操作:0,每个键都已注册(或者没有需要检查的调用点),继续。1,清单缺失或所列键不在清单中;创建文件或添加键(步骤 4 会创建文件),然后重新运行。64,用法错误,源目录不存在(当前工作目录错误或参数无效);这不是“缺少键”的结果,不要创建文件或注册键,修正路径后重新运行。

暂停:“已向 label-manifest.ts 添加 N 个条目。check-manifest-registered.sh 已通过:[确认]。”


第 4 步:接线

目标: 确保 i18n 接线已就绪;如果应用尚未设置 i18n,则搭建相关配置。安装命令、接线代码以及 B2E 与 B2C 的回退配置,均见框架参考文档(references/<framework>/localize.md 和 i18n-setup.md)。

检查: 在 UI bundle 目录中运行 check-i18n-wired.sh(它会相对于当前目录扫描 src/),并报告返回结果。该脚本负责完整的确定性检查:查找框架的 i18n 接线(React:在启动时调用的 initI18n() 初始化文件;Angular:注册自定义 TranslateLoader 的 provideTranslateService/TranslateModule.forRoot,并在启动时通过 TranslateService.use 加载),如果接线存在,还会报告标签清单是否已导入且确实能到达标签后端/加载器。不要自行阅读文件并重新推导这些信息。

cd <path-to-uiBundles/<name>/ dir>   # scripts scan src/ relative to here
bash <SKILL_DIR>/references/<framework>/check-i18n-wired.sh

根据退出码进行分支处理(输出的消息会列出报告中要用的具体文件/符号,但决策依据是退出码):

  • 退出码 0 → 接线完整,清单能到达标签后端/加载器;只需添加新键。
  • 退出码 1 → 尚无 i18n 接线;按照框架参考文档搭建完整配置。
  • 退出码 2 → 接线存在但不完整(React:未在启动时调用初始化;Angular:未注册加载器,或已注册但未在启动时调用 TranslateService.use);不要重新搭建或覆盖现有配置。只添加消息指出的缺失接线,然后重新运行检查。
  • 退出码 3 → 已在启动时接线,但脚本无法确认清单能否到达后端/加载器。最后这项检查采用文本启发式:清单可能通过脚本无法识别的变量、展开语法、工厂或辅助函数接线,因此应将退出码 3 视为“编辑前先核实”,而不是“确定存在问题”。打开消息中列出的文件并确认后再调整;切勿重新搭建或重复添加已有且有效的接线。
  • 退出码 64 → 用法错误:源目录不存在(当前工作目录错误或参数有误)。这不代表“没有接线”;不要搭建配置。修正路径后重新运行。

B2C 覆盖规则——即使退出码为 0 也适用:接线检查只能证明 i18n 存在,不能证明它适用于 B2C。使用 B2E 接线的种子项目——dir = ctx.dir,且加载器没有 labelFallback——在运行 check-i18n-wired.sh 时会通过并返回退出码 0,但对 B2C 网站而言是错误的。如果网站是 B2C,不要止步于“添加新键”:打开 src/i18n/index.ts(或框架的初始化文件),并使用 resolvedLang(= SFDC_ENV.language || ctx.lang;检测器不会读取 SFDC_ENV.language)进行以下操作:(a) 添加 labelFallback: "USER_DEFAULT";(b) 根据它设置方向——使用 i18next.dir(resolvedLang),绝不能使用 ctx.dir;(c) 在其中初始化——React 在 i18next.init 中设置 lng: resolvedLang;Angular 调用 translate.use(resolvedLang)。请参阅 references/<framework>/i18n-setup.md。切勿在 B2C 网站上保留 B2E 接线。

完成标准: i18n 接线已就绪,并且在启动时调用一次;清单能到达标签后端/加载器。对于 B2C,必须应用全部三项 resolvedLang 覆盖设置——USER_DEFAULT 回退、显示语言(React lng、Angular translate.use)以及 i18next.dir(resolvedLang) 方向——不能使用种子项目的 B2E 默认值。 严格遵循框架参考文档中的具体搭建说明,绝不覆盖现有接线。

暂停:“i18n 设置[已存在/已创建]。已在启动时加载:[确认]。”


步骤 5:验证

目标: 指导开发者验证标签是否能以第二种语言呈现。

操作: 按照 references/common/verifying.md 中针对分支的流程操作。

对于 B2E,启用第二种语言,编写或获取其翻译元数据,针对目标 org 构建,仅部署目标 bundle 和标签元数据,然后更改已认证用户的 Language 并重新加载。

对于 B2C,按照框架参考文档中的详细说明,验证已配置的网站语言、URL 路由、SFDC_ENV.language、完整重新加载的语言切换器、本地化的本地预览、访客 GraphQL 访问权限以及缓存清理。

部署 bundle、标签和翻译不会发布 Experience 网站。将 sf community publish 视为单独的上线变更:显示确切的网站和目标 org,然后在运行命令前立即等待用户明确确认。

如果未能呈现: 检查 references/common/gotchas.md 中列出的问题:

  • 清单键未注册(步骤 3 漏掉了一个标签)
  • API 版本不匹配(针对不同的 org 构建)
  • 标签缓存过期(React:localStorage 中的 i18next_res_*;Angular:内存缓存,重新加载会重新获取)
  • B2C 访客 GraphQL 403(管理员未启用 GraphQLApiOrgPrefForGuestUsers)
  • B2C 路由、网站语言和 SFDC_ENV.language 不一致

完成标准: 标签可在 ≥2 种区域设置下呈现,或已确定阻止问题。

暂停:“要进行验证:在 Translation Workbench 中启用第二种语言,编写翻译(我可以搭建 XML 框架),然后构建/部署并重新加载。要我为 [language] 搭建翻译文件吗?[y / 我会手动处理]”


边缘情况:妥善处理

  • 代码已本地化:检测是否已有翻译调用用法 / 已填充的清单;建议在现有设置基础上添加,而不是重新搭建全部内容。
  • 未找到字符串:清楚地报告并停止;不要凭空添加工作。
  • 应用尚未设置 i18n:步骤 4 先搭建两个文件,然后步骤 3 才能注册内容。
  • 设置不完整(存在清单但缺少初始化,或反之),协调现有内容;绝不覆盖已有接线配置。
  • B2B 网站:明确拒绝支持。支持 B2C 并不意味着支持 B2B。

防护措施:绝不破坏以下内容

  1. 绝不对可部署元数据进行机器翻译。 搭建一个格式正确的 <Translations> 文档,每个标签都包含一个闭合的 <customLabels> 块,并对英文源文本进行 XML 转义。保留占位符,并在完成前解析这两个元数据文件。翻译人员应手动或通过 Translation Workbench 替换搭建的值;绝不为此调用机器翻译 API。
  2. 绝不注册没有标签的键。 清单条目数必须等于标签数(步骤 3 的标准)。未注册的键会以其自身的字面名称呈现,且不会显示控制台警告。这是最常见的本地化错误。
  3. 绝不覆盖已有的 i18n 接线配置。 如果步骤 4 发现已有 initI18n(),应协调现有配置(如果缺少清单导入,则补上),而不是替换整个文件。
  4. 每个文件都必须适用于客户。 任何地方都不得包含 webapps、仅供核心使用的路径或内部基础设施引用。应以外部客户使用 SFDX 项目的情境来编写。
  5. 绝不隐式发布 B2C 网站。 元数据部署和网站发布是分开的。只有在显示网站和 org 并收到针对该次发布的明确确认后,才能运行 sf community publish。

命令与目录布局

<project-root>/                          ← SFDX project root
└── force-app/main/default/
    ├── labels/CustomLabels.labels-meta.xml          ← English base labels
    ├── translations/<locale>.translation-meta.xml   ← one per translated language
    └── uiBundles/<your-bundle>/
        ├── package.json
        └── src/
            ├── i18n/
            │   ├── index.ts              ← init wiring (you write this once)
            │   └── label-manifest.ts     ← list of labels to fetch (you maintain this)
            └── components/               ← components call the translation function
命令运行位置用途
安装 i18n 依赖(参见 references/<framework>/localize.md)UI bundle 目录安装框架的 i18n 库(第 4 步)
npm run buildUI bundle 目录构建应用(会内置 API 版本;先设置 target-org)
参见 references/<framework>/verifying.md项目根目录检查并将确切的目标 bundle 和变更后的标签元数据部署到指定组织
sf project retrieve start --metadata Translations:<locale>项目根目录拉取在 Translation Workbench 中编写的翻译

预检清单:整个运行的完成标准

  • 每个已确认的字符串都有一个 CustomLabels 条目和一个翻译调用
  • label-manifest.ts 条目数 == 标签数(没有未注册的键)
  • 已接入 i18n,并在启动时调用一次;清单已接入标签后端/加载器
  • 仅限 B2C:已确认访客 GraphQL 偏好设置,已配置 USER_DEFAULT,且站点语言路由与 SFDC_ENV.language 匹配
  • 仅限 B2E:没有覆盖回退设置;保留框架参考中的默认值
  • 标签可在 ≥2 种区域设置下呈现(或已指出阻塞问题)
  • *-meta.xml 中没有写入手工编写的机器翻译(仅提供脚手架和指南)
  • 两个元数据文件均可解析为 XML;翻译脚手架只有一个 <Translations> 根元素,且每个标签都有一个闭合的块

评论

支持 Markdown