背景:站点具有多语言需求,用户访问站点期间需要加载相应的多语言json 文件。
- 如何更快响应产品变更词条需求。
- 如何更快让用户加载多语言文件是需要考虑的点。
痛点:
-
随着多语言JSON文件的词条不断增加,文件体积的增大会导致加载时间延长,进而影响用户体验。因此,优化文件加载效率对于提升用户满意度至关重要。
-
以前的多语言文件维护在项目中,每次产品词条更新都需经过CI/CD流程发布,这不仅耗时较长,还难以实现快速响应变更需求。
如何解决:
-
基于以上两点问题,全球站多语言需要解决2个问题
-
多语言 json文件需要热更新,即产品改动词条之后直接发布,前端不需要CI/CD,用户即可加载最新的多语言文件
-
分片加载: 当用户访问特定模块(如KYC页面)时,我们只会加载 kyc 相关的多语言json 文件。其余模块多语言文件并不会被加载 (比如: 用户访问kyc模块,assets 模块多语言就不会被加载)。分片加载带来的好处是: 按需加载,且请求的词条量较小,用户体验更好
项目如何接入:
- 初始化拿到所有的 多语言josn文件 在S3的绝对地址,形成一个[国家]+[语言模块]结构。 (类似以下结构)
ruby
// 这里的 data 是后端返回的 多语言文件在 S3 绝对地址,只会请求一次
const data = {
en_US_asset: 'https://xxx/public/abc.json', // 英文模式 资产模块 多语言json 文件
en_US_common: 'https://xxx/public/abcd1.json', // 英文模式 common模块 多语言json 文件
en_US_kyc: 'https://xxx/public/acb31.json', // 英文模式 kyc模块 多语言json 文件
zh_CN_asset: 'https://xxx/public/asd1.json', // 中文简体模式 资产模块 多语言json 文件
zh_CN_common: 'https://xxx/public/231.json', // 中文简体模式 common模块 多语言json 文件
zh_CN_kyc: 'https://xxx/public/12321das.json', // 中文简体模式 kyc模块 多语言json 文件
zh_TW_asset: 'https://xxx/public/1fsa1dsa.json', // 中文繁体模式 资产模块 多语言json 文件
zh_TW_common: 'https://xxx/public/asds213.json', // 中文繁体模式 common模块 多语言json 文件
zh_TW_kyc: 'https://xxx/public/sdadasd1.json', // 中文繁体模式 kyc模块 多语言json 文件
};
- 当用户访问具体某个业务模块的时候,只加载对应模块的多语言json 文件
javascript
import i18n from 'i18next';
import HTTPApi from 'i18next-http-backend';
import { awaitWrap } from 'cus-utils';
import {
getLocalLang,
EN,
ZHCN,
ZHHK,
LangMapVal,
NameSpace,
} from '@/utils/utils';
import { getS3LangUrl } from '@/service/common';
import formatS3RemoteLang, {
type TypeFormatS3RemoteLangUnion,
} from './utils/formatS3RemoteLang';
async function getRemoteS3Url() {
const [err, s3data] = await awaitWrap(getS3LangUrl());
const formatS3Data = formatS3RemoteLang(
s3data?.updateLanguageParameters ?? [],
);
// 后续看需要不要加统一错误处理
if (err) {
console.error(err, '远端多语言加载失败');
}
i18n.use(HTTPApi).init({
lng: getLocalLang(),
fallbackLng: {
zh_CN: [ZHCN],
zh_HK: [ZHHK],
en_US: [EN],
default: [EN],
},
ns: 'common',
fallbackNS: ['default'],
backend: {
loadPath: async ([lngs]: LangMapVal[], [namespaces]: NameSpace[]) => {
const key = (lngs +
'_' +
namespaces) as keyof TypeFormatS3RemoteLangUnion;
// 本地开发模式多语言 json 文件名map
if (import.meta.env.MODE === 'development') {
// mock json 文件
const data = {
en_US_asset: 'abc',
en_US_common: 'abcd1',
en_US_kyc: 'acb31',
zh_CN_asset: 'asd1',
zh_CN_common: '231',
zh_CN_kyc: '12321das',
zh_TW_asset: '1fsa1dsa',
zh_TW_common: 'asds213',
zh_TW_kyc: 'sdadasd1',
};
const devModeFilename = data[key];
return `${import.meta.env.VITE_STATIC_PUBLICPATH}locales/${devModeFilename}.json?hash=${import.meta.env.LANG_HASH}`;
}
// 这里控制 多语言按需加载
const filename = formatS3Data[key].downloadUrl;
return filename;
},
overrideMimeType: 'application/json',
},
load: 'currentOnly',
});
}
getRemoteS3Url();
export default i18n;
-
业务代码如何控制多个模块多语言json文件
- 方式一:通过 useTranslation 自定义声明 namespace
javascriptexport default function Page() { const { t } = useTranslation('kyc'); // 加载 kyc 模块多语言文件 const { t: t1 } = useTranslation('asset'); // 加载 asset 模块多语言文件 const { t: t2 } = useTranslation('common');// 加载 common 模块多语言文件 return ( <> <div>kyc词条--- {t('common.overview')}</div> <div>asset词条 --- {t1('common.overview')}</div> <div>common词条 --- {t2('common.overview')}</div> </> ); }
- 方式二:通过 withTranslation 高阶组件进行 提前注入 namespace
javascript
function Page(props) {
return (
<>
<div>kyc词条--- {props.t('common.overview')}</div>
<div>asset词条 --- {props.t('common.overview', { ns: 'asset' })}</div>
<div>common词条 --- {props.t('common.overview', { ns: 'common' })}</div>
</>
);
}
export default withTranslation(['kyc', 'asset', 'common'])(Page);
效果:
-
通过以上配置 i18n文件, 我们可以做到词条维护在 远端S3,实现词条热更新
-
用户按需加载多语言词条,用户体验更好
未来优化:
- 多语言加载一次之后,缓存在本地,当多语言文件内容 MD5 值明确发生变更之后,再次请求多语言json 文件。即用户请求一次,文件内容没发生变更,直接使用缓存过的多语言文件,避免用户每次刷新都请求多语言文件
- 使用 namespace 之后,开发者每次导出组件 都需要引入至少1个namespace,后续考虑使用 AST 进行动态注入每个 组件,尽可能少写 引入 namespace
- 目前词条是一个一个录入的,后续考虑在打包编译阶段自动识别到多语言 key,上传到多语言平台,释放开发者录入词条的时间