Merge branch 'release/1.0.35' of gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine into release/1.0.35

This commit is contained in:
力皓 2021-02-01 10:56:35 +08:00
commit b8f1a3678a
6 changed files with 99 additions and 15 deletions

View File

@ -236,7 +236,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
readonly componentsConsumer = new ResourceConsumer<Asset | undefined>(() => this.componentsAsset); readonly componentsConsumer = new ResourceConsumer<Asset | undefined>(() => this.componentsAsset);
readonly injectionConsumer = new ResourceConsumer(() => { readonly injectionConsumer = new ResourceConsumer(() => {
return {}; return {
i18n: this.project.i18n,
};
}); });
readonly asyncLibraryMap: { [key: string]: {} } = {}; readonly asyncLibraryMap: { [key: string]: {} } = {};

View File

@ -10,7 +10,7 @@ export class Project {
@obx.val readonly documents: DocumentModel[] = []; @obx.val readonly documents: DocumentModel[] = [];
private data: ProjectSchema = { version: '1.0.0', componentsMap: [], componentsTree: [] }; private data: ProjectSchema = { version: '1.0.0', componentsMap: [], componentsTree: [], i18n: {} };
private _simulator?: ISimulatorHost; private _simulator?: ISimulatorHost;
@ -40,15 +40,24 @@ export class Project {
this._config = value; this._config = value;
} }
@obx.ref private _i18n: any = {};
get i18n(): any {
return this._i18n;
}
set i18n(value: any) {
this._i18n = value || {};
}
/** /**
* schema * schema
*/ */
getSchema(): ProjectSchema { getSchema(): ProjectSchema {
return { return {
...this.data, ...this.data,
// todo: future change this filter // TODO: future change this filter
componentsMap: this.currentDocument?.getComponentsMap(), componentsMap: this.currentDocument?.getComponentsMap(),
componentsTree: this.documents.filter((doc) => !doc.isBlank()).map((doc) => doc.schema), componentsTree: this.documents.filter((doc) => !doc.isBlank()).map((doc) => doc.schema),
i18n: this._i18n || {},
}; };
} }
@ -57,6 +66,7 @@ export class Project {
* @param schema * @param schema
*/ */
setSchema(schema?: ProjectSchema) { setSchema(schema?: ProjectSchema) {
// FIXME: 这里的行为和 getSchema 并不对等,感觉不太对
const doc = this.documents.find((doc) => doc.actived); const doc = this.documents.find((doc) => doc.actived);
doc && doc.import(schema?.componentsTree[0]); doc && doc.import(schema?.componentsTree[0]);
} }
@ -73,9 +83,11 @@ export class Project {
version: '1.0.0', version: '1.0.0',
componentsMap: [], componentsMap: [],
componentsTree: [], componentsTree: [],
i18n: {},
...schema, ...schema,
}; };
this.config = schema?.config || this.config; this.config = schema?.config || this.config;
this.i18n = schema?.i18n || this.i18n;
if (autoOpen) { if (autoOpen) {
if (autoOpen === true) { if (autoOpen === true) {
@ -137,6 +149,9 @@ export class Project {
if (key === 'config') { if (key === 'config') {
this.config = value; this.config = value;
} }
if (key === 'i18n') {
this.i18n = value;
}
Object.assign(this.data, { [key]: value }); Object.assign(this.data, { [key]: value });
} }
@ -160,6 +175,9 @@ export class Project {
if (key === 'config') { if (key === 'config') {
return this.config; return this.config;
} }
if (key === 'i18n') {
return this.i18n;
}
return Reflect.get(this.data, key); return Reflect.get(this.data, key);
} }

View File

@ -7,6 +7,8 @@ import { SimulatorRendererContainer, DocumentInstance } from './renderer';
import { Router, Route, Switch } from 'react-router'; import { Router, Route, Switch } from 'react-router';
import './renderer.less'; import './renderer.less';
const DEFAULT_SIMULATOR_LOCALE = 'zh-CN';
// patch cloneElement avoid lost keyProps // patch cloneElement avoid lost keyProps
const originCloneElement = window.React.cloneElement; const originCloneElement = window.React.cloneElement;
(window as any).React.cloneElement = (child: any, { _leaf, ...props }: any = {}, ...rest: any[]) => { (window as any).React.cloneElement = (child: any, { _leaf, ...props }: any = {}, ...rest: any[]) => {
@ -137,8 +139,13 @@ class Renderer extends Component<{
const { documentInstance, rendererContainer: renderer } = this.props; const { documentInstance, rendererContainer: renderer } = this.props;
const { container } = documentInstance; const { container } = documentInstance;
const { designMode, device } = container; const { designMode, device } = container;
const messages = container.context?.utils?.i18n?.messages || {};
const locale = container.context?.utils?.i18n?.currentLocale || Object.keys(messages)[0] || DEFAULT_SIMULATOR_LOCALE;
return ( return (
<LowCodeRenderer <LowCodeRenderer
locale={locale}
messages={messages}
schema={documentInstance.schema} schema={documentInstance.schema}
components={container.components} components={container.components}
appHelper={container.context} appHelper={container.context}

View File

@ -267,12 +267,28 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
return parseQuery(search); return parseQuery(search);
}, },
}, },
i18n: {
setLocale: (loc: string) => {
const newCtx = {
...this._appContext,
};
newCtx.utils.i18n.currentLocale = loc;
this._appContext = newCtx;
},
currentLocale: undefined,
messages: {},
},
}, },
constants: {}, constants: {},
requestHandlersMap: this._requestHandlersMap, requestHandlersMap: this._requestHandlersMap,
}; };
host.injectionConsumer.consume((data) => { host.injectionConsumer.consume((data) => {
// sync utils, i18n, contants,... config // TODO: sync utils, i18n, contants,... config
const newCtx = {
...this._appContext,
};
newCtx.utils.i18n.messages = data.i18n || {};
this._appContext = newCtx;
}); });
} }

View File

@ -10,6 +10,7 @@ import {
getValue, getValue,
parseData, parseData,
parseExpression, parseExpression,
parseI18n,
isEmpty, isEmpty,
isSchema, isSchema,
isFileSchema, isFileSchema,
@ -19,7 +20,7 @@ import {
transformArrayToMap, transformArrayToMap,
transformStringToFunction, transformStringToFunction,
checkPropTypes, checkPropTypes,
generateI18n, getI18n,
acceptsRef, acceptsRef,
getFileCssName, getFileCssName,
capitalizeFirstLetter, capitalizeFirstLetter,
@ -77,9 +78,8 @@ export default function baseRenererFactory() {
this.appHelper = props.__appHelper; this.appHelper = props.__appHelper;
this.__compScopes = {}; this.__compScopes = {};
this.__instanceMap = {}; this.__instanceMap = {};
const { locale, messages } = props;
this.i18n = generateI18n(locale, messages);
this.__bindCustomMethods(props); this.__bindCustomMethods(props);
this.__initI18nAPIs(props);
} }
__afterInit(props: IRendererProps) { } __afterInit(props: IRendererProps) { }
@ -259,6 +259,15 @@ export default function baseRenererFactory() {
); */ ); */
}; };
__initI18nAPIs = () => {
this.i18n = (key: string, values = {}) => {
const { locale, messages } = this.props;
return getI18n(key, values, locale, messages);
};
this.getLocale = () => this.props.locale;
this.setLocale = (loc: string) => this.appHelper?.utils?.i18n?.setLocale && this.appHelper?.utils?.i18n?.setLocale(loc);
};
__render = () => { __render = () => {
const schema = this.props.__schema; const schema = this.props.__schema;
this.__setLifeCycleMethods('render'); this.__setLifeCycleMethods('render');
@ -328,6 +337,9 @@ export default function baseRenererFactory() {
if (isJSExpression(schema)) { if (isJSExpression(schema)) {
return parseExpression(schema, self); return parseExpression(schema, self);
} }
if (isI18n(schema)) {
return parseI18n(schema, self);
}
if (isJSSlot(schema)) { if (isJSSlot(schema)) {
return this.__createVirtualDom(schema.value, self, parentInfo); return this.__createVirtualDom(schema.value, self, parentInfo);
} }
@ -581,11 +593,16 @@ export default function baseRenererFactory() {
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props); if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
} }
const handleI18n = (props: any) => props[props.use || 'zh_CN']; const handleLegaoI18n = (props: any) => props[props.use || 'zh_CN'];
// 兼容乐高设计态 i18n 数据 // 兼容乐高设计态 i18n 数据
if (isI18n(props)) { if (isI18n(props)) {
props = handleI18n(props); const i18nProp = handleLegaoI18n(props);
if (i18nProp) {
props = i18nProp;
} else {
return parseI18n(props, self);
}
} }
// 兼容乐高设计态的变量绑定 // 兼容乐高设计态的变量绑定

View File

@ -46,7 +46,8 @@ const EXPRESSION_TYPE = {
JSEXPRESSION: 'JSExpression', JSEXPRESSION: 'JSExpression',
JSFUNCTION: 'JSFunction', JSFUNCTION: 'JSFunction',
JSSLOT: 'JSSlot', JSSLOT: 'JSSlot',
JSBLOCK: 'JSBlock' JSBLOCK: 'JSBlock',
I18N: 'i18n',
}; };
const EXPRESSION_REG = /^\{\{(\{.*\}|.*?)\}\}$/; const EXPRESSION_REG = /^\{\{(\{.*\}|.*?)\}\}$/;
const hasSymbol = typeof Symbol === 'function' && Symbol.for; const hasSymbol = typeof Symbol === 'function' && Symbol.for;
@ -112,6 +113,10 @@ export function isJSExpression(obj: any) {
return isJSExpressionObj || isJSExpressionStr; return isJSExpressionObj || isJSExpressionStr;
} }
export function isI18n(obj) {
return obj && typeof obj === 'object' && EXPRESSION_TYPE.I18N === obj.type;
}
/** /**
* @name wait * @name wait
* @description * @description
@ -180,6 +185,19 @@ export function generateI18n(locale = 'zh-CN', messages: any = {}) {
}; };
} }
/**
*
* @param {*} key
* @param {*} values
* @param {*} locale zh-CNen-US
* @param {*} messages
*/
export function getI18n(key: string, values = {}, locale = 'zh-CN', messages = {}) {
if (!messages || !messages[locale] || !messages[locale][key]) return '';
const formater = new IntlMessageFormat(messages[locale][key], locale);
return formater.format(values);
}
/** /**
* ref * ref
* @param {*} Comp * @param {*} Comp
@ -373,6 +391,8 @@ export function transformStringToFunction(str: string) {
export function parseData(schema: any, self: any): any { export function parseData(schema: any, self: any): any {
if (isJSExpression(schema)) { if (isJSExpression(schema)) {
return parseExpression(schema, self); return parseExpression(schema, self);
} else if (isI18n(schema)) {
return parseI18n(schema, self);
} else if (typeof schema === 'string') { } else if (typeof schema === 'string') {
return schema.trim(); return schema.trim();
} else if (Array.isArray(schema)) { } else if (Array.isArray(schema)) {
@ -423,10 +443,14 @@ export function capitalizeFirstLetter(word: string) {
return word[0].toUpperCase() + word.slice(1); return word[0].toUpperCase() + word.slice(1);
} }
export function isI18n(obj: any) {
return obj && typeof obj === 'object' && obj?.type === 'i18n';
}
export function isVariable(obj: any) { export function isVariable(obj: any) {
return obj && typeof obj === 'object' && obj?.type === 'variable'; return obj && typeof obj === 'object' && obj?.type === 'variable';
} }
/* 将 i18n 结构,降级解释为对 i18n 接口的调用 */
export function parseI18n(i18nInfo: any, self: any) {
return parseExpression({
type: EXPRESSION_TYPE.JSEXPRESSION,
value: `this.i18n('${i18nInfo.key}')`,
}, self);
}