mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 01:21:58 +00:00
fix: i18n parser & setting
This commit is contained in:
parent
f20bfaa884
commit
dbdd9e485a
@ -1,21 +1,18 @@
|
||||
import Env from './env';
|
||||
import { isJSSlot, isI18nData, isJSExpression } from '@ali/lowcode-types';
|
||||
import { isPlainObject } from '@ali/lowcode-utils';
|
||||
const I18nUtil = require('@ali/ve-i18n-util');
|
||||
import i18nUtil from './i18n-util';
|
||||
|
||||
interface I18nObject {
|
||||
type?: string;
|
||||
use?: string;
|
||||
key?: string;
|
||||
[lang: string]: string | undefined;
|
||||
}
|
||||
|
||||
export function i18nReducer(obj?: any): any {
|
||||
// FIXME: 表达式使用 mock 值,未来live 模式直接使用原始值
|
||||
export function deepValueParser(obj?: any): any {
|
||||
if (isJSExpression(obj)) {
|
||||
obj = obj.mock;
|
||||
}
|
||||
if (!obj) {
|
||||
return obj;
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => i18nReducer(item));
|
||||
return obj.map((item) => deepValueParser(item));
|
||||
}
|
||||
if (isPlainObject(obj)) {
|
||||
if (isI18nData(obj)) {
|
||||
@ -23,19 +20,20 @@ export function i18nReducer(obj?: any): any {
|
||||
let locale = Env.getLocale();
|
||||
if (obj.key) {
|
||||
// FIXME: 此处需要升级I18nUtil,改成响应式
|
||||
return I18nUtil.get(obj.key, locale);
|
||||
return i18nUtil.get(obj.key, locale);
|
||||
}
|
||||
if (locale !== 'zh_CN' && locale !== 'zh_TW' && !obj[locale]) {
|
||||
locale = 'en_US';
|
||||
}
|
||||
return obj[obj.use || locale] || obj.zh_CN;
|
||||
}
|
||||
if (isJSSlot(obj) || isJSExpression(obj)) {
|
||||
|
||||
if (isJSSlot(obj)) {
|
||||
return obj;
|
||||
}
|
||||
const out: I18nObject = {};
|
||||
const out: any = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
out[key] = i18nReducer(obj[key]);
|
||||
out[key] = deepValueParser(obj[key]);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
79
packages/editor-preset-vision/src/i18n-util/index.d.ts
vendored
Normal file
79
packages/editor-preset-vision/src/i18n-util/index.d.ts
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
declare enum LANGUAGES {
|
||||
zh_CN = 'zh_CN',
|
||||
en_US = 'en_US'
|
||||
}
|
||||
|
||||
export interface I18nRecord {
|
||||
type?: 'i18n';
|
||||
[key: string]: string;
|
||||
/**
|
||||
* i18n unique key
|
||||
*/
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export interface I18nRecordData {
|
||||
gmtCreate: Date;
|
||||
gmtModified: Date;
|
||||
i18nKey: string;
|
||||
i18nText: I18nRecord;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface II18nUtilConfigs {
|
||||
items?: {};
|
||||
/**
|
||||
* 是否禁用初始化加载
|
||||
*/
|
||||
disableInstantLoad?: boolean;
|
||||
/**
|
||||
* 初始化的时候是否全量加载
|
||||
*/
|
||||
disableFullLoad?: boolean;
|
||||
loader?: (configs: ILoaderConfigs) => Promise<I18nRecordData[]>;
|
||||
remover?: (key: string, dic: I18nRecord) => Promise<void>;
|
||||
saver?: (key: string, dic: I18nRecord) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ILoaderConfigs {
|
||||
/**
|
||||
* search keywords
|
||||
*/
|
||||
keyword?: string;
|
||||
/**
|
||||
* should load all i18n items
|
||||
*/
|
||||
isFull?: boolean;
|
||||
/**
|
||||
* search i18n item based on uniqueKey
|
||||
*/
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export interface II18nUtil {
|
||||
init(config: II18nUtilConfigs): void;
|
||||
isInitialized(): boolean;
|
||||
isReady(): boolean;
|
||||
attach(prop: object, value: I18nRecord, updator: () => any);
|
||||
search(keyword: string, silent?: boolean);
|
||||
load(configs: ILoaderConfigs): Promise<I18nRecord[]>;
|
||||
/**
|
||||
* Get local i18n Record
|
||||
* @param key
|
||||
* @param lang
|
||||
*/
|
||||
get(key: string, lang: string): string | I18nRecord;
|
||||
getFromRemote(key: string): Promise<I18nRecord>;
|
||||
getItem(key: string, forceData?: boolean): any;
|
||||
getItems(): I18nRecord[];
|
||||
update(key: string, doc: I18nRecord, lang: LANGUAGES);
|
||||
create(doc: I18nRecord, lang: LANGUAGES): string;
|
||||
remove(key: string): Promise<void>;
|
||||
|
||||
onReady(func: () => any);
|
||||
onRowsChange(func: () => any);
|
||||
onChange(func: (dic: I18nRecord) => any);
|
||||
}
|
||||
|
||||
declare const i18nUtil: II18nUtil;
|
||||
export default i18nUtil;
|
||||
310
packages/editor-preset-vision/src/i18n-util/index.js
Normal file
310
packages/editor-preset-vision/src/i18n-util/index.js
Normal file
@ -0,0 +1,310 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { obx } from '@ali/lowcode-editor-core';
|
||||
|
||||
|
||||
let keybase = Date.now();
|
||||
function keygen(maps) {
|
||||
let key;
|
||||
do {
|
||||
key = `i18n-${(keybase).toString(36)}`;
|
||||
keybase += 1;
|
||||
} while (key in maps);
|
||||
return key;
|
||||
}
|
||||
|
||||
class DocItem {
|
||||
constructor(parent, doc, unInitial) {
|
||||
this.parent = parent;
|
||||
const { use, ...strings } = doc;
|
||||
this.doc = obx.val({
|
||||
type: 'i18n',
|
||||
...strings,
|
||||
});
|
||||
this.emitter = new EventEmitter;
|
||||
this.inited = unInitial !== true;
|
||||
}
|
||||
|
||||
getKey() {
|
||||
return this.doc.key;
|
||||
}
|
||||
|
||||
getDoc(lang) {
|
||||
if (lang) {
|
||||
return this.doc[lang];
|
||||
}
|
||||
return this.doc;
|
||||
}
|
||||
|
||||
setDoc(doc, lang, initial) {
|
||||
if (lang) {
|
||||
this.doc[lang] = doc;
|
||||
} else {
|
||||
const { use, strings } = doc || {};
|
||||
Object.assign(this.doc, strings);
|
||||
}
|
||||
this.emitter.emit('change', this.doc);
|
||||
|
||||
if (initial) {
|
||||
this.inited = true;
|
||||
} else if (this.inited) {
|
||||
this.parent._saveChange(this.doc.key, this.doc);
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (!this.inited) return Promise.reject('not initialized');
|
||||
|
||||
const { key, ...doc } = this.doc; // eslint-disable-line
|
||||
this.emitter.emit('change', doc);
|
||||
return this.parent.remove(this.getKey());
|
||||
}
|
||||
|
||||
onChange(func) {
|
||||
this.emitter.on('change', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('change', func);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class I18nUtil {
|
||||
constructor() {
|
||||
this.emitter = new EventEmitter;
|
||||
// original data source from remote
|
||||
this.i18nData = {};
|
||||
// current i18n records on the left pane
|
||||
this.items = [];
|
||||
this.maps = {};
|
||||
// full list of i18n records for synchronized call
|
||||
this.fullList = [];
|
||||
this.fullMap = {};
|
||||
|
||||
this.config = {};
|
||||
this.ready = false;
|
||||
this.isInited = false;
|
||||
}
|
||||
|
||||
_prepareItems(items, isFull = false, isSilent = false) {
|
||||
this[isFull ? 'fullList' : 'items'] = items.map((dict) => {
|
||||
let item = this[isFull ? 'fullMap' : 'maps'][dict.key];
|
||||
if (item) {
|
||||
item.setDoc(dict, null, true);
|
||||
} else {
|
||||
item = new DocItem(this, dict);
|
||||
this[isFull ? 'fullMap' : 'maps'][dict.key] = item;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
if (this.ready && !isSilent) {
|
||||
this.emitter.emit('rowschange');
|
||||
this.emitter.emit('change');
|
||||
} else {
|
||||
this.ready = true;
|
||||
this.emitter.emit('ready');
|
||||
}
|
||||
}
|
||||
|
||||
_load(configs = {}, silent) {
|
||||
if (!this.config.loader) {
|
||||
console.error(new Error('Please load loader while init I18nUtil.'));
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return this.config.loader(configs).then((data) => {
|
||||
if (configs.i18nKey) {
|
||||
return Promise.resolve(data.i18nText);
|
||||
}
|
||||
this._prepareItems(data.data, configs.isFull, silent);
|
||||
// set pagination data to i18nData
|
||||
this.i18nData = data;
|
||||
if (!silent) {
|
||||
this.emitter.emit('rowschange');
|
||||
this.emitter.emit('change');
|
||||
}
|
||||
return Promise.resolve(this.items.map(i => i.getDoc()));
|
||||
});
|
||||
}
|
||||
|
||||
_saveToItems(key, dict) {
|
||||
let item = null;
|
||||
item = this.items.find(doc => doc.getKey() === key);
|
||||
if (!item) {
|
||||
item = this.fullList.find(doc => doc.getKey() === key);
|
||||
}
|
||||
|
||||
if (item) {
|
||||
item.setDoc(dict);
|
||||
} else {
|
||||
item = new DocItem(this, {
|
||||
key,
|
||||
...dict,
|
||||
});
|
||||
this.items.unshift(item);
|
||||
this.fullList.unshift(item);
|
||||
this.maps[key] = item;
|
||||
this.fullMap[key] = item;
|
||||
this._saveChange(key, dict, true);
|
||||
}
|
||||
}
|
||||
|
||||
_saveChange(key, dict, rowschange) {
|
||||
if (rowschange) {
|
||||
this.emitter.emit('rowschange');
|
||||
}
|
||||
this.emitter.emit('change');
|
||||
if (dict === null) {
|
||||
delete this.maps[key];
|
||||
delete this.fullMap[key];
|
||||
}
|
||||
return this._save(key, dict);
|
||||
}
|
||||
|
||||
_save(key, dict) {
|
||||
const saver = dict === null ? this.config.remover : this.config.saver;
|
||||
if (!saver) return Promise.reject('Saver function is not set');
|
||||
return saver(key, dict);
|
||||
}
|
||||
|
||||
init(config) {
|
||||
if (this.isInited) return;
|
||||
this.config = config || {};
|
||||
if (this.config.items) {
|
||||
// inject to current page
|
||||
this._prepareItems(this.config.items);
|
||||
}
|
||||
if (!this.config.disableInstantLoad) {
|
||||
this._load({ isFull: !this.config.disableFullLoad });
|
||||
}
|
||||
this.isInited = true;
|
||||
}
|
||||
|
||||
isInitialized() {
|
||||
return this.isInited;
|
||||
}
|
||||
|
||||
isReady() {
|
||||
return this.ready;
|
||||
}
|
||||
|
||||
// add events updater when i18n record change
|
||||
// we should notify engine's view to change
|
||||
attach(prop, value, updator) {
|
||||
const isI18nValue = value && value.type === 'i18n' && value.key;
|
||||
const key = isI18nValue ? value.key : null;
|
||||
if (prop.i18nLink) {
|
||||
if (isI18nValue && (key === prop.i18nLink.key)) {
|
||||
return prop.i18nLink;
|
||||
}
|
||||
prop.i18nLink.detach();
|
||||
}
|
||||
|
||||
if (isI18nValue) {
|
||||
return {
|
||||
key,
|
||||
detach: this.getItem(key, value).onChange(updator),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索 i18n 词条
|
||||
*
|
||||
* @param {any} keyword 搜索关键字
|
||||
* @param {boolean} [silent=false] 是否刷新左侧的 i18n 数据
|
||||
* @returns
|
||||
*
|
||||
* @memberof I18nUtil
|
||||
*/
|
||||
search(keyword, silent = false) {
|
||||
return this._load({ keyword }, silent);
|
||||
}
|
||||
|
||||
load(configs = {}) {
|
||||
return this._load(configs);
|
||||
}
|
||||
|
||||
get(key, lang) {
|
||||
const item = this.getItem(key);
|
||||
if (item) {
|
||||
return item.getDoc(lang);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getFromRemote(key) {
|
||||
return this._load({ i18nKey: key });
|
||||
}
|
||||
|
||||
getItem(key, forceData) {
|
||||
if (forceData && !this.maps[key] && !this.fullList[key]) {
|
||||
const item = new DocItem(this, {
|
||||
key,
|
||||
...forceData,
|
||||
}, true);
|
||||
this.maps[key] = item;
|
||||
this.fullMap[key] = item;
|
||||
this.fullList.push(item);
|
||||
this.items.push(item);
|
||||
}
|
||||
return this.maps[key] || this.fullMap[key];
|
||||
}
|
||||
|
||||
getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
update(key, doc, lang) {
|
||||
let dict = this.get(key) || {};
|
||||
if (!lang) {
|
||||
dict = doc;
|
||||
} else {
|
||||
dict[lang] = doc;
|
||||
}
|
||||
this._saveToItems(key, dict);
|
||||
}
|
||||
|
||||
create(doc, lang) {
|
||||
const dict = lang ? { [lang]: doc } : doc;
|
||||
const key = keygen(this.fullMap);
|
||||
this._saveToItems(key, dict);
|
||||
return key;
|
||||
}
|
||||
|
||||
remove(key) {
|
||||
const index = this.items.findIndex(item => item.getKey() === key);
|
||||
const indexG = this.fullList.findIndex(item => item.getKey() === key);
|
||||
if (index > -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
if (indexG > -1) {
|
||||
this.fullList.splice(index, 1);
|
||||
}
|
||||
return this._saveChange(key, null, true);
|
||||
}
|
||||
|
||||
onReady(func) {
|
||||
this.emitter.on('ready', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('ready', func);
|
||||
};
|
||||
}
|
||||
|
||||
onRowsChange(func) {
|
||||
this.emitter.on('rowschange', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('rowschange', func);
|
||||
};
|
||||
}
|
||||
|
||||
onChange(func) {
|
||||
this.emitter.on('change', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('change', func);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new I18nUtil();
|
||||
@ -3,7 +3,7 @@ import Popup from '@ali/ve-popups';
|
||||
import Icons from '@ali/ve-icons';
|
||||
import logger from '@ali/vu-logger';
|
||||
import { render } from 'react-dom';
|
||||
import I18nUtil from '@ali/ve-i18n-util';
|
||||
import I18nUtil from './i18n-util';
|
||||
import { hotkey as Hotkey } from '@ali/lowcode-editor-core';
|
||||
import { createElement } from 'react';
|
||||
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS, VERSION as Version } from './base/const';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user