首页/新闻资讯/正文详情

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

发布时间:2026/9/20 23:58:17 来源:云帆数科 栏目:资讯中心
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),仅供参考

相关推荐

企业级AI Agent落地:如何打通知识库与数据库?开箱即用方案实践
企业级AI Agent落地:如何打通知识库与数据库?开箱即用方案实践

作为一个常年折腾数据系统的人,我今年最大的感受是:身边问“AI Agent 到底怎么落地”的团队明显变多了,而且问到最后,几乎都会落到同一个具体场景——把内部知识库和业务数据库打通,让 AI 能直接回答业务问题、查询订单… · 2026/9/20 23:58:17

光谱预处理流水线:SNV、MSC与Savitzky-Golay工程实践指南
光谱预处理流水线:SNV、MSC与Savitzky-Golay工程实践指南

简介:本资源是一套面向化学计量学与光谱分析初学者及科研人员的MATLAB预处理与建模实践工具包,聚焦红外、拉曼及高光谱数据的标准化、散射校正与定量建模全流程。资源完整覆盖SNV标准化、MSC多散射校正、Savitzky-Golay等平滑算法及PLS偏最小二乘回归建模… · 2026/9/20 23:58:17

Huff0 熵压缩:Go 语言下的 zstd 级 Huffman 编解码器实现与实战指南
Huff0 熵压缩:Go 语言下的 zstd 级 Huffman 编解码器实现与实战指南

Huff0 熵压缩:Go 语言下的 zstd 级 Huffman 编解码器实现与实战指南 【免费下载链接】slim Slim(toolkit): Dont change anything in your container image and minify it by up to 30x (and for compiled languages even more) making it secure too! (free and op… · 2026/9/20 23:58:17

Hugging Face 开源权重模型怎么接到 TaoToken:Kimi K2.7 Code 的 OpenRouter 用量观察
Hugging Face 开源权重模型怎么接到 TaoToken:Kimi K2.7 Code 的 OpenRouter 用量观察

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 0:48:27

样本量要多大才够 —— 别让 “样本太少“ 成为硬伤
样本量要多大才够 —— 别让 “样本太少“ 成为硬伤

做问卷调查,最常被问到的问题就是:"你发了多少份问卷?" 样本量太小,统计结果不可靠;样本量太大,收集起来费劲。到底多少样本量才合适?汇写(https://www.huixielunwen.com/… · 2026/9/21 0:48:27

Kimi Code 连 BrickCore MCP 报 401 / authenticated: false?TaoToken 只管模型 Key,headers 照填 MCP_API_KEY
Kimi Code 连 BrickCore MCP 报 401 / authenticated: false?TaoToken 只管模型 Key,headers 照填 MCP_API_KEY

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 0:48:27

昇腾Atlas 300V推理卡部署YOLO实战:模型转换与推理优化指南
昇腾Atlas 300V推理卡部署YOLO实战:模型转换与推理优化指南

最近在技术群里,关于 Atlas 300V 24G 的提问越来越多了,翻来覆去无非是两个问题:它到底是不是运算加速卡?能不能拿来部署 YOLO?第一个问题通常是刚接触昇腾这类加速卡的人问的;第二个问题,基本是… · 2026/9/21 0:48:27

Asko HI1355G多米诺电磁炉安装使用全解析
Asko HI1355G多米诺电磁炉安装使用全解析

简介:《Asko雅士高多米诺电磁炉HI1355G安装使用手册》是一份面向家庭用户与安装维修人员的官方指南,适用于HI1995G、HI1655G等型号,重点解决安全安装、正确操作与日常维护问题。资源包共1个PDF文件,约1.09MB,电子文档便… · 2026/9/21 0:48:27

RAG技术优化:检索增强生成系统的关键策略与实践
RAG技术优化:检索增强生成系统的关键策略与实践

1. RAG技术体系概述检索增强生成(Retrieval-Augmented Generation)作为当前NLP领域的前沿技术,通过将信息检索与文本生成相结合,有效解决了传统大语言模型的知识固化问题。我在实际项目中发现,标准的RAG流程通常包含四… · 2026/9/21 0:47:27

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化
Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡… · 2026/9/21 0:02:39

Word表格编号全攻略:从列表编号到题注交叉引用
Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技… · 2026/9/21 0:02:39

从第一个站到第二个站:独立开发者的静态网站选型与落地实践
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&… · 2026/9/20 0:00:41

Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行
Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 0:00:18

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程
agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and … · 2026/9/21 0:00:18

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,… · 2026/9/21 0:00:18

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码