资讯详情

Sails 动态内容国际化(Translating Dynamic Content)实战指南:从 JSON stringfile 到数据库驱动的多语言方案

📅 2026/9/20 23:57:39 | 华诺云谱 👁 阅读
Sails 动态内容国际化(Translating Dynamic Content)实战指南:从 JSON stringfile 到数据库驱动的多语言方案
Sails 动态内容国际化Translating Dynamic Content实战指南从 JSON stringfile 到数据库驱动的多语言方案【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails导读当你的 Sails 应用需要翻译的不只是页面上的固定文案而是存储在数据库里的跨语言业务数据例如通过 CMS 以多种语言录入的商品数据、文章正文、用户生成内容时传统的config/locales/*.json静态 stringfile 便不再够用。本篇基于 Sails 官方文档 TranslatingDynamicContent.md 展开结合仓库中 i18n hook 源码 与 集成测试系统讲解两条落地路线以编程方式动态编辑 locale 翻译文件以及将翻译内容存入数据库、按 locale id 建模读取。读完本文你将掌握req.getLocale()、req.setLocale()、sails.__的正确用法并能够为CMS 多语言录入 Sails 后端渲染这类典型场景设计出与项目既有国际化约定保持一致的数据模型与查询方案。一、先厘清边界静态 stringfile 与动态内容的本质区别Sails 内置的国际化和本地化支持自 v1 起基于轻量级i18n-2包实现见 Internationalization.md面向的是静态词句即那些预先确定、数量有限、可以在config/locales/目录下以 JSON 键值对形式维护的文案。i18n hook 在初始化时会把整个 locales 目录加载进内存之后在每次请求中通过expressMiddleware把翻译能力注入res.locals参见 i18n/index.js。然而如果你的后端正在存储跨语言数据interlingual data——例如商品在多个语言下都有独立描述、用户在不同语言下提交了不同版本的内容——此时每个语种的字符串数量可能达到数万条且会持续动态增长。继续依赖简单的 JSON locale 文件有两个明显障碍规模与维护成本失控stringfile 会膨胀到难以人工维护且每次新增内容都要同步修改、重新部署。编辑时机错位除非你有办法在运行时动态编辑 locale 翻译并且愿意承担并发写文件的风险否则静态文件方案难以承载 CMS 的多语言录入流程。原文档给出的判断非常清晰只有当你能以某种方式动态编辑locale 翻译时JSON stringfile 才值得继续沿用否则就应该把动态翻译字符串放进数据库。下面逐一展开这两条路线。二、路线一以编程方式动态编辑 locale 翻译JSON stringfile2.1 适用前提具备编程式编辑能力原文档明确指出如果你的后端存储的是跨语言数据就不应依赖简单 JSON locale 文件——除非你计划以编程方式动态编辑这些翻译。也就是说你可以在运行时通过自研实现直接读写 stringfilefs读写 JSON注意加锁/原子写入或对接第三方翻译服务将服务返回的翻译批量写回 stringfile。Sails / node-i18n 的 JSON stringfile 与 webtranslateit.com 等翻译平台使用的格式是兼容的这意味着你可以把config/locales/下的文件直接交给这类翻译服务做人工或机器翻译后回写无需转换格式。Sails 的 stringfile 就是最简单的 JSON 键值对例如仓库测试夹具 config/locales/es.json{ hello: Hola }2.2 stringfile 的格式约束与注意点要编程式编辑 stringfile必须遵守 Locales.md 中定义的文件约定每个文件对应一个 locale文件名即语言标识例如config/locales/es.json、config/locales/de.json键与值都是字符串键区分大小写且必须精确匹配Hello与hello是两个不同的键支持占位符例如Hello %s, how are you today?翻译时%s会被运行时传入的参数替换需要表达嵌套结构时在键中使用.例如editProfile.username.label: Username键的命名风格取决于谁来维护 stringfile如果是人工手工编辑统一小写短键如hello、howAreYouToday通常比长英文句子更利于维护。2.3 编程式写入示例一个可复用的 helper下面是一个把新翻译写回 stringfile 的可运行示例可作为 Sails 自定义 helper 使用通过 action 触发后由 CMS 后台调用// api/helpers/update-locale-file.js const fs require(fs); const path require(path); module.exports { friendlyName: Update locale file, description: Programmatically write a translated string into a Sails locale stringfile., inputs: { locale: { type: string, required: true, description: e.g. es }, key: { type: string, required: true, description: Translation key, e.g. Welcome }, value: { type: string, required: true, description: Translated string } }, fn: async function ({ locale, key, value }) { const filePath path.resolve(sails.config.appPath, sails.config.i18n.localesDirectory, locale .json); // 读入现有翻译文件不存在则视为空对象 let translations {}; if (fs.existsSync(filePath)) { translations JSON.parse(fs.readFileSync(filePath, utf8)); } // 写入新键并原子回写 translations[key] value; fs.writeFileSync(filePath, JSON.stringify(translations, null, 2) \n); return { filePath, key, value }; } };需要注意两点运行时缓存i18n hook 在initialize阶段通过new i18nFactory({...})一次性加载 locales 目录见 i18n/index.js因此直接改文件后当前进程内的翻译缓存不会自动刷新。写入后应重新读取文件如新开请求用req.i18n实例已重新构造或在写完后触发应用重启/热重载。i18n-2 的自动补写行为仓库集成测试 hook.i18n.test.js 验证了一个容易被忽视的行为——调用sailsApp.__(Login)后config/locales/de.json会被自动补写缺失的键Login: Login。这是 i18n-2 的 update files 特性意味着 stringfile 可能在请求处理过程中被后台改动。如果你的数据模型不允许翻译文件被隐式修改应当通过配置关闭该行为或在文件系统层面做好并发保护。三、路线二把动态翻译内容存进数据库这是原文档推荐的另一条路线也是承载 CMS 多语言数据的主流方案。核心思路不要把翻译和被翻译的业务实体混在同一个字段里而是围绕 locale id 设计数据模型让每条翻译记录都能按语言标识en、es、de…被独立存储与检索。3.1 按 locale id 建模一对多翻译模型以商品为例业务实体与翻译分离一个商品对应多语言的多条翻译记录// api/models/Product.js module.exports { attributes: { sku: { type: string, required: true, unique: true }, translations: { collection: producttranslation, via: product } } };// api/models/ProductTranslation.js module.exports { attributes: { // locale id 即语言标识例如 en / es / de locale: { type: string, required: true, isIn: [en, es, de, fr] }, name: { type: string, required: true }, description: { type: string }, product: { model: product } } };这种实体 按 locale 展开的翻译子表结构有两点收益与 Sails 的国际化约定保持一致locale id 直接复用 stringfile 的命名约定小写、BCP 47 风格前端与后端、静态文案与动态内容共享同一套语言标识语义统一检索直观拿到当前请求的语言后按locale字段过滤即可必要时再补充一个默认语言回退fallback。3.2 用 req.getLocale() 决定返回哪份翻译原文档特别强调借助req.getLocale()方法判断当前请求应使用哪份翻译内容从而keep consistent with the conventions used elsewhere in your app与应用中其他地方使用的约定保持一致。req.getLocale()由 i18n hook 的expressMiddleware在每个请求上绑定源码见 i18n/index.jshook 会基于请求头为当前请求新建一个i18n-2实例并把getLocale、setLocale挂到req上。默认情况下它读取的是请求的Accept-Language头——即用户浏览器/设备的语言设置。仓库集成测试 hook.i18n.test.js 验证了这一行为对/test_req_getlocale路由发起带Accept-language: es头的请求req.getLocale()返回es。在 action 中组合使用// api/controllers/product/view-product.js const _ require(sailshq/lodash); module.exports { friendlyName: View product, exits: { notFound: { responseType: notFound } }, fn: async function () { const product await Product.findOne({ sku: this.req.param(sku) }) .populate(translations); if (!product) { throw notFound; } // 用当前请求的语言标识挑选翻译 const locale this.req.getLocale(); let translation _.find(product.translations, { locale: locale }); // 找不到时回退到默认语言例如 en保证任何语言请求都有兜底内容 if (!translation) { translation _.find(product.translations, { locale: en }) || {}; } return { product: product, translation: translation }; } };在视图中动态数据同样以响应 locals 的形式输出而静态页面文案继续使用__()/i18n()走 stringfile——两者互不干扰h1% translation.name %/h1 p% translation.description %/p p% __(Add to cart) %/p !-- 静态文案仍走 stringfile --3.3 用户语言偏好req.setLocale() 与动态内容的配合默认的语言检测对跟随用户偏好切换语言的场景并不够用——用户可能希望手动选择语言而这个偏好存在 session 或数据库账号里。此时用req.setLocale()覆盖自动检测结果即可。官方文档 req.setLocale.md 给出的典型用法是登录用户在 action 入口处把其账号保存的语言偏好写进本次请求// 基于 actions2 的写法 if (this.req.me.preferredLocale) { this.req.setLocale(this.req.me.preferredLocale); } return exits.success(); // 传统写法非 Web app 模板 / 非 actions2 var me await User.findOne({ id: req.session.userId }); if (me.preferredLocale) { req.setLocale(me.preferredLocale); } return res.view(pages/homepage);测试 hook.i18n.test.js 证明了它的生效链路路由中执行req.setLocale(es)后req.i18n.__(Welcome)返回Bienvenido。这意味着你在setLocale之后再调用req.getLocale()、req.i18n.__()都会以新 locale 为准——先覆盖语言偏好再查询数据库翻译两步配合即可实现用户语言偏好 动态多语言内容的完整闭环fn: async function () { // 1. 用户偏好优先session 或数据库账号字段 if (this.req.me this.req.me.preferredLocale) { this.req.setLocale(this.req.me.preferredLocale); } // 2. 此刻 getLocale() 返回的已是最终生效的语言 const locale this.req.getLocale(); // 3. 按该语言读取动态翻译…… }四、配置与约束sails.config.i18n 关键参数无论走哪条路线动态内容的语言标识体系都与 sails.config.i18n 的配置紧密相关。该配置按约定放在config/i18n.js核心属性如下对照 i18n hook 源码 defaults 与configure校验逻辑 L44-L60属性类型默认值说明localesarray[en,es,fr,de]应用支持的 locale 列表。注意配置值与对应翻译文件名必须全小写。hook 的configure阶段会强制校验其必须是字符串数组否则直接抛错localesDirectorystringconfig/locales/stringfile 所在目录的应用相对路径也支持绝对路径同样会被校验必须是字符串defaultLocalestringen站点默认语言。对发送Accept-Language头的请求绝大多数浏览器会被覆盖但对非浏览器客户端移动设备、IoT、cURL、Postman 等仍很有用示例仓库测试 hook.i18n.test.js 中真实使用的配置// config/i18n.js module.exports.i18n { locales: [en, de], defaultLocale: de };两个值得注意的边界行为均有源码/测试依据defaultLocale 与 locales 顺序hook 在configure阶段会把defaultLocale移动到列表顶部以规避 i18n-2 的一个已知问题见 i18n/index.js禁用 i18n hook 的兜底行为当sails.config.i18n.locales为空数组时hook 会自我禁用并把sails.__、sails.i18n替换为原样返回输入字符串并打印警告的透传函数见 i18n/index.js。如果应用走数据库翻译路线且不需要 stringfile可通过loadHooks/hooks配置彻底移除 i18n hook详见 Internationalization.md 的 Disabling or customizing 一节。另外若采用路线一编程式编辑 stringfiledefaultLocale还关系到sails.__在非请求上下文中如 shell-scripts 命令行脚本使用的语言sails.__(Welcome)默认按defaultLocale翻译例如测试中默认 locale 为de时返回Willkommen见 hook.i18n.test.js。五、两条路线的选型建议考量维度路线一编程式编辑 stringfile路线二数据库存储翻译数据规模适合文案量有限、变更不频繁适合 CMS 多语言录入、海量动态内容与既有约定的兼容直接复用__()/i18n()体系零改造成本需自行建模但 locale id 约定与 stringfile 一致更新实时性受 i18n 内存缓存与自动补写行为影响需处理刷新随数据库事务即时生效天然适合多写并发维护主体翻译服务 / 后台脚本批量回写业务用户通过 CMS 直接编辑典型场景少数固定页面文案 外部翻译平台协作电商商品、多语言文章、UGC 内容无论选择哪条路线请始终遵循原文档的核心原则让动态翻译数据与应用的国际化约定对齐——语言标识统一使用小写 locale iden、es、de…并借助req.getLocale()/req.setLocale()在请求生命周期内确定并覆盖生效语言。这样静态 stringfile 与数据库动态内容可以无缝共存前端渲染、后端接口、shell 脚本sails.__三条路径对当前语言的理解保持一致。更多关于 stringfile 编写规范与语言检测细节可继续阅读 Locales.md整体国际化机制与视图用法见 Internationalization.md。【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。