oak-frontend-base/lib/features/locales.js

208 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Locales = void 0;
const Feature_1 = require("../types/Feature");
const assert_1 = require("oak-domain/lib/utils/assert");
const i18n_js_1 = require("i18n-js");
const constant_1 = require("../constant/constant");
class Locales extends Feature_1.Feature {
static REFRESH_STALE_INTERVAL = 30 * 24 * 3600 * 1000; // 正式环境下的主动刷新缓存策略
cache;
localStorage;
environment;
language;
defaultLng;
i18n;
async initializeLng() {
const savedLng = await this.localStorage.load(constant_1.LOCAL_STORAGE_KEYS.localeLng);
if (savedLng) {
this.language = savedLng;
}
else {
await this.detectLanguage();
}
}
constructor(cache, localStorage, environment, defaultLng) {
super();
this.cache = cache;
this.localStorage = localStorage;
this.defaultLng = defaultLng;
this.environment = environment;
this.language = defaultLng;
// 也是异步行为,不知道是否有影响 by Xc
this.initializeLng();
this.i18n = new i18n_js_1.I18n(undefined, {
defaultLocale: defaultLng,
locale: this.language,
});
this.reloadDataset();
// i18n miss的默认策略
this.i18n.missingBehavior = 'loadData';
this.i18n.missingTranslation.register("loadData", (i18n, scope, options) => {
this.loadData(scope);
(0, assert_1.assert)(typeof scope === 'string');
return scope.split('.').pop();
});
// 同时注册一个返回空字符串的策略
this.i18n.missingTranslation.register("returnNull", (i18n, scope, options) => {
return '';
});
}
async detectLanguage() {
const env = await this.environment.getEnv();
const { language } = env;
this.language = language;
await this.localStorage.save(constant_1.LOCAL_STORAGE_KEYS.localeLng, language);
}
async reloadDataset() {
await this.cache.onInitialized();
const i18ns = this.cache.get('i18n', {
data: {
id: 1,
data: 1,
namespace: 1,
language: 1,
$$updateAt$$: 1,
},
});
const dataset = {};
i18ns.forEach(({ namespace, data, language }) => {
if (dataset[language]) {
dataset[language][namespace] = data;
}
else {
dataset[language] = {
[namespace]: data,
};
}
});
this.i18n.store(dataset);
if (i18ns.length > 0) {
/**
* 前台启动时的数据刷新策略:
* dev环境无条件刷新production环境只会主动刷新超过REFRESH_STALE_INTERVAL(30天)的i18n数据
* 这样会导致在此期间内如果不发生key miss数据不会更新
* 程序员要谨慎对待这一特性对于重要的i18n在版本间尽量不要更新原有的key值。
*/
if (process.env.NODE_ENV === 'development') {
const nss = i18ns.map(ele => ele.namespace);
await this.loadServerData(nss);
}
else {
const now = Date.now();
const nss = i18ns.filter(ele => now - ele.$$updateAt$$ > Locales.REFRESH_STALE_INTERVAL).map(ele => ele.namespace);
if (nss.length > 0) {
await this.loadServerData(nss);
}
}
}
}
async loadServerData(nss) {
const { data: newI18ns } = await this.cache.refresh('i18n', {
data: {
id: 1,
data: 1,
namespace: 1,
language: 1,
$$createAt$$: 1,
$$updateAt$$: 1,
},
filter: {
namespace: {
$in: nss,
},
},
}, undefined, undefined, {
dontPublish: true,
useLocalCache: {
keys: nss,
gap: process.env.NODE_ENV === 'development' ? 10 * 1000 : 3600 * 1000,
onlyReturnFresh: true,
},
ignoreContext: true,
});
if (newI18ns.length > 0) {
const dataset = {};
newI18ns.forEach(({ namespace, data, language }) => {
if (dataset[language]) {
dataset[language][namespace] = data;
}
else {
dataset[language] = {
[namespace]: data,
};
}
});
this.i18n.store(dataset);
this.publish();
}
}
/**
* 当发生key缺失时向服务器请求最新的i18n数据对i18n缓存数据的行为优化放在cache中统一进行
* @param ns
*/
async loadData(key) {
(0, assert_1.assert)(typeof key === 'string');
const [ns] = key.split('.');
if (process.env.NODE_ENV === 'development') {
(0, assert_1.assert)(!['undefined', 'notExist'].includes(ns));
}
await this.loadServerData([ns]);
if (!this.hasKey(key)) {
console.warn(`命名空间${ns}中的${key}缺失且可能请求不到更新的数据`);
if (process.env.NODE_ENV === 'development') {
console.warn('请增加好相应的键值后执行make:locale');
}
}
}
/**
* 暴露给小程序的Wxs调用
* @param key
*/
loadMissedLocale(key) {
this.loadData(key);
}
/**
* translate函数这里编译器会在params里注入两个参数 #oakNamespace 和 #oakModule用以标识文件路径
* @param key
* @param params
* @returns
*/
t(key, params) {
// return key as string;
const ns = params['#oakNamespace'];
const module = params['#oakModule'];
let key2 = key;
if (key.includes('::')) {
// 公共模块
key2 = `${module}-l-${key}`.replace('::', '.');
}
else if (key.includes(':')) {
// entity
key2 = key.replace(':', '.');
}
else {
// 自身模块
key2 = `${ns}.${key}`;
}
return this.i18n.t(key2, params);
}
// 获得当前locales的状态小程序需要dataset去Wxs里渲染同时reRender也要利用version触发render
getState() {
return {
lng: this.language,
defaultLng: this.defaultLng,
dataset: this.i18n.translations,
version: this.i18n.version,
};
}
// 查看有无某值,不触发获取数据
hasKey(key, params) {
this.i18n.missingBehavior = 'returnNull';
const result = this.i18n.t(key, params);
this.i18n.missingBehavior = 'loadData';
return result;
}
}
exports.Locales = Locales;