如何通过Webpack处理多租户XLIFF翻译文件 合并覆盖项输出可用JSON
实现方案
第一步:安装必要依赖
先装需要的工具包:
npm install xliff glob lodash.merge webpack-virtual-modules --save-dev
xliff:用来解析xlf格式文件为JSONglob:用来遍历翻译目录下的所有xlf文件lodash.merge:用来合并通用翻译和租户自定义翻译,自动覆盖重名keywebpack-virtual-modules:用来动态生成虚拟的翻译模块,不需要写入实际文件,支持热更监听
第二步:封装翻译聚合逻辑
先写一个工具函数,输入租户名和domain,输出聚合后的翻译JSON:
const { parse } = require('xliff'); const fs = require('fs'); const glob = require('glob'); const merge = require('lodash.merge'); // 支持的语言列表,可根据实际情况调整 const SUPPORT_LANGS = ['en', 'fr', 'es']; // 翻译根目录 const TRANSLATION_ROOT = './translations'; function generateTranslation(tenant, domain) { const result = {}; // 先加载通用翻译 SUPPORT_LANGS.forEach(lang => { const commonPath = `${TRANSLATION_ROOT}/common/${domain}.${lang}.xlf`; if (fs.existsSync(commonPath)) { const commonXlf = fs.readFileSync(commonPath, 'utf8'); const commonRes = parse(commonXlf).resources; // 转成key:value格式 result[lang] = Object.fromEntries(Object.entries(commonRes).map(([k, v]) => [k, v.target])); } }); // 再加载租户自定义翻译,覆盖通用翻译的同key值 SUPPORT_LANGS.forEach(lang => { const tenantPath = `${TRANSLATION_ROOT}/${tenant}/${domain}.${lang}.xlf`; if (fs.existsSync(tenantPath)) { const tenantXlf = fs.readFileSync(tenantPath, 'utf8'); const tenantRes = parse(tenantXlf).resources; const tenantMap = Object.fromEntries(Object.entries(tenantRes).map(([k, v]) => [k, v.target])); // 合并,租户的优先级更高 result[lang] = merge(result[lang], tenantMap); } }); // 如果你需要的是{key:{en:xxx, fr:xxx}}格式,取消注释这段转换代码即可 // const keyBased = {}; // Object.entries(result).forEach(([lang, pairs]) => { // Object.entries(pairs).forEach(([key, value]) => { // if (!keyBased[key]) keyBased[key] = {}; // keyBased[key][lang] = value; // }) // }) // return keyBased; return result; }
第三步:修改Webpack多租户配置
你现在是每个租户对应一个独立的webpack编译配置,我们给每个租户的配置加上虚拟模块和监听逻辑:
const VirtualModulesPlugin = require('webpack-virtual-modules'); const tenants = ['TenantA', 'TenantB']; const modules = [] // 你所有的domain列表,也可以用glob自动扫描common目录下的所有domain const DOMAINS = ['messages', 'thing']; tenants.forEach(tenant => { // 为当前租户生成所有domain的虚拟翻译模块 const virtualModules = new VirtualModulesPlugin(); DOMAINS.forEach(domain => { const translationJson = generateTranslation(tenant, domain); // 虚拟模块路径对应你引入的路径:import translations from 'translations/messages' virtualModules.writeModule(`node_modules/translations/${domain}.js`, `module.exports = ${JSON.stringify(translationJson)}`); }) modules.push({ // 你原有的租户相关webpack配置:entry、output、其他loader等都保留 entry: `./src/${tenant}/index.js`, output: { path: `./dist/${tenant}`, filename: 'bundle.js' }, plugins: [ // 加入虚拟模块插件 virtualModules, // 其他原有插件 ], // 配置监听翻译目录文件变更,触发重新编译 watchOptions: { ignored: /node_modules/, aggregateTimeout: 300, poll: 1000 }, // 如果你用webpack 5+且开启了devServer,可以加这个配置自动监听翻译文件变更刷新 // devServer: { // watchFiles: ['./translations/**/*.xlf'] // } }) }) module.exports = modules
验证使用
直接在对应租户的前端代码里引入即可:
import messages from 'translations/messages'; import thing from 'translations/thing'; // 示例使用 console.log(messages.fr['hello']); // 会优先取租户自定义的翻译,没有就用通用的
内容的提问来源于stack exchange,提问作者Pierre-Antoine Guillaume
相关产品推荐
相关产品推荐

