You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by za...@apache.org on 2020/07/28 03:50:44 UTC
[incubator-echarts] branch next-i18n updated: feat(i18n): change
i18n with build, and change the init api
This is an automated email from the ASF dual-hosted git repository.
zakwu pushed a commit to branch next-i18n
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
The following commit(s) were added to refs/heads/next-i18n by this push:
new 256ff11 feat(i18n): change i18n with build, and change the init api
256ff11 is described below
commit 256ff11f60a7dbf786c9c7b91ac23dd867496f85
Author: zakwu <12...@qq.com>
AuthorDate: Tue Jul 28 11:50:22 2020 +0800
feat(i18n): change i18n with build, and change the init api
---
build/build-i18n.js | 69 +++++++++++++++++++++++++
build/build.js | 3 ++
i18n/langEN.js | 144 ----------------------------------------------------
i18n/langEN.json | 109 +++++++++++++++++++++++++++++++++++++++
i18n/langZH.js | 141 --------------------------------------------------
i18n/langZH.json | 109 +++++++++++++++++++++++++++++++++++++++
package.json | 3 +-
src/echarts.ts | 20 +++++---
src/model/Global.ts | 5 +-
test/lang.html | 47 +++++++++++++++--
10 files changed, 351 insertions(+), 299 deletions(-)
diff --git a/build/build-i18n.js b/build/build-i18n.js
new file mode 100644
index 0000000..7a768ea
--- /dev/null
+++ b/build/build-i18n.js
@@ -0,0 +1,69 @@
+const fs = require('fs');
+
+const outFilePath = './i18n';
+const umdWrapperHead = `
+(function(root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['exports'], factory);
+ } else if (
+ typeof exports === 'object' &&
+ typeof exports.nodeName !== 'string'
+ ) {
+ // CommonJS
+ factory(exports);
+ } else {
+ // Browser globals
+ factory({});
+ }
+})(this, function(exports) {
+var lang =`;
+
+const umdWrapperHeadWithEcharts = `
+(function(root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['exports', 'echarts'], factory);
+ } else if (
+ typeof exports === 'object' &&
+ typeof exports.nodeName !== 'string'
+ ) {
+ // CommonJS
+ factory(exports, require('echarts'));
+ } else {
+ // Browser globals
+ factory({}, root.echarts);
+ }
+})(this, function(exports, echarts) {
+var lang =`;
+
+const umdWrapperTail = `
+});`;
+
+async function buildI18nWrap() {
+ const targetDir = './i18n';
+ const files = fs.readdirSync(targetDir);
+ files.forEach(t => {
+ if(!t.startsWith('lang') || !t.endsWith('json')) return;
+ const fileName = t.substring(0, t.length - 5);
+ console.log(fileName + ' is building...');
+ const type = t.substr(-7, 2);
+ const echartsRegister = `
+ echarts.registerLocale('${type}', lang);
+ `;
+ const pureExports = `
+ exports.lang = lang;
+ `;
+ const code = fs.readFileSync(targetDir + '/' + t, 'utf-8');
+ fs.writeFileSync(outFilePath + '/' + fileName + '.js', umdWrapperHeadWithEcharts + code + echartsRegister + umdWrapperTail, 'utf-8');
+ fs.writeFileSync(outFilePath + '/' + fileName + '-obj.js', umdWrapperHead + code + pureExports + umdWrapperTail, 'utf-8');
+ fs.writeFileSync(outFilePath + '/' + fileName + '.ts', 'export default ' + code, 'utf-8');
+ console.log(fileName + ' done');
+ })
+}
+
+buildI18nWrap();
+
+module.exports = {
+ buildI18n: buildI18nWrap
+};
diff --git a/build/build.js b/build/build.js
index f88d256..436d9dc 100755
--- a/build/build.js
+++ b/build/build.js
@@ -30,6 +30,7 @@ const ecLangPlugin = require('./ec-lang-rollup-plugin');
const prePublish = require('./pre-publish');
const recheckDEV = require('./remove-dev').recheckDEV;
const assert = require('assert');
+const {buildI18n} = require('./build-i18n')
async function run() {
@@ -274,6 +275,8 @@ function getPath(relativePath) {
* ]
*/
async function build(configs) {
+ // buildI18n JSON before build when build
+ buildI18n();
// ensureZRenderCode.prepare();
diff --git a/i18n/langEN.js b/i18n/langEN.js
deleted file mode 100644
index ebba3a1..0000000
--- a/i18n/langEN.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * Language: English.
- */
-
-(function(root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['exports', 'echarts'], factory);
- } else if (
- typeof exports === 'object' &&
- typeof exports.nodeName !== 'string'
- ) {
- // CommonJS
- factory(exports, require('echarts'));
- } else {
- // Browser globals
- factory({}, root.echarts);
- }
-})(this, function(exports, echarts) {
- let lang = {
- legend: {
- selector: {
- all: 'All',
- inverse: 'Inv'
- }
- },
- toolbox: {
- brush: {
- title: {
- rect: 'Box Select',
- polygon: 'Lasso Select',
- lineX: 'Horizontally Select',
- lineY: 'Vertically Select',
- keep: 'Keep Selections',
- clear: 'Clear Selections'
- }
- },
- dataView: {
- title: 'Data View',
- lang: ['Data View', 'Close', 'Refresh']
- },
- dataZoom: {
- title: {
- zoom: 'Zoom',
- back: 'Zoom Reset'
- }
- },
- magicType: {
- title: {
- line: 'Switch to Line Chart',
- bar: 'Switch to Bar Chart',
- stack: 'Stack',
- tiled: 'Tile'
- }
- },
- restore: {
- title: 'Restore'
- },
- saveAsImage: {
- title: 'Save as Image',
- lang: ['Right Click to Save Image']
- }
- },
- series: {
- typeNames: {
- pie: 'Pie chart',
- bar: 'Bar chart',
- line: 'Line chart',
- scatter: 'Scatter plot',
- effectScatter: 'Ripple scatter plot',
- radar: 'Radar chart',
- tree: 'Tree',
- treemap: 'Treemap',
- boxplot: 'Boxplot',
- candlestick: 'Candlestick',
- k: 'K line chart',
- heatmap: 'Heat map',
- map: 'Map',
- parallel: 'Parallel coordinate map',
- lines: 'Line graph',
- graph: 'Relationship graph',
- sankey: 'Sankey diagram',
- funnel: 'Funnel chart',
- gauge: 'Guage',
- pictorialBar: 'Pictorial bar',
- themeRiver: 'Theme River Map',
- sunburst: 'Sunburst'
- }
- },
- aria: {
- general: {
- withTitle: 'This is a chart about "{title}"',
- withoutTitle: 'This is a chart'
- },
- series: {
- single: {
- prefix: '',
- withName: ' with type {seriesType} named {seriesName}.',
- withoutName: ' with type {seriesType}.'
- },
- multiple: {
- prefix: '. It consists of {seriesCount} series count.',
- withName: ' The {seriesId} series is a {seriesType} representing {seriesName}.',
- withoutName: ' The {seriesId} series is a {seriesType}.',
- separator: {
- middle: '',
- end: ''
- }
- }
- },
- data: {
- allData: 'The data is as follows: ',
- partialData: 'The first {displayCnt} items are: ',
- withName: 'the data for {name} is {value}',
- withoutName: '{value}',
- separator: {
- middle: ',',
- end: '.'
- }
- }
- }
- };
-
- echarts.registerLocale('EN', lang);
-});
diff --git a/i18n/langEN.json b/i18n/langEN.json
new file mode 100644
index 0000000..2a41e5a
--- /dev/null
+++ b/i18n/langEN.json
@@ -0,0 +1,109 @@
+{
+ "legend": {
+ "selector": {
+ "all": "All",
+ "inverse": "Inv"
+ }
+ },
+ "toolbox": {
+ "brush": {
+ "title": {
+ "rect": "Box Select",
+ "polygon": "Lasso Select",
+ "lineX": "Horizontally Select",
+ "lineY": "Vertically Select",
+ "keep": "Keep Selections",
+ "clear": "Clear Selections"
+ }
+ },
+ "dataView": {
+ "title": "Data View",
+ "lang": [
+ "Data View",
+ "Close",
+ "Refresh"
+ ]
+ },
+ "dataZoom": {
+ "title": {
+ "zoom": "Zoom",
+ "back": "Zoom Reset"
+ }
+ },
+ "magicType": {
+ "title": {
+ "line": "Switch to Line Chart",
+ "bar": "Switch to Bar Chart",
+ "stack": "Stack",
+ "tiled": "Tile"
+ }
+ },
+ "restore": {
+ "title": "Restore"
+ },
+ "saveAsImage": {
+ "title": "Save as Image",
+ "lang": [
+ "Right Click to Save Image"
+ ]
+ }
+ },
+ "series": {
+ "typeNames": {
+ "pie": "Pie chart",
+ "bar": "Bar chart",
+ "line": "Line chart",
+ "scatter": "Scatter plot",
+ "effectScatter": "Ripple scatter plot",
+ "radar": "Radar chart",
+ "tree": "Tree",
+ "treemap": "Treemap",
+ "boxplot": "Boxplot",
+ "candlestick": "Candlestick",
+ "k": "K line chart",
+ "heatmap": "Heat map",
+ "map": "Map",
+ "parallel": "Parallel coordinate map",
+ "lines": "Line graph",
+ "graph": "Relationship graph",
+ "sankey": "Sankey diagram",
+ "funnel": "Funnel chart",
+ "gauge": "Guage",
+ "pictorialBar": "Pictorial bar",
+ "themeRiver": "Theme River Map",
+ "sunburst": "Sunburst"
+ }
+ },
+ "aria": {
+ "general": {
+ "withTitle": "This is a chart about \"{title}\"",
+ "withoutTitle": "This is a chart"
+ },
+ "series": {
+ "single": {
+ "prefix": "",
+ "withName": " with type {seriesType} named {seriesName}.",
+ "withoutName": " with type {seriesType}."
+ },
+ "multiple": {
+ "prefix": ". It consists of {seriesCount} series count.",
+ "withName": " The {seriesId} series is a {seriesType} representing {seriesName}.",
+ "withoutName": " The {seriesId} series is a {seriesType}.",
+ "separator": {
+ "middle": "",
+ "end": ""
+ }
+ }
+ },
+ "data": {
+ "allData": "The data is as follows: ",
+ "partialData": "The first {displayCnt} items are: ",
+ "withName": "the data for {name} is {value}",
+ "withoutName": "{value}",
+ "separator": {
+ "middle": ",",
+ "end": "."
+ }
+ }
+ }
+}
diff --git a/i18n/langZH.js b/i18n/langZH.js
deleted file mode 100644
index 5b2a4d9..0000000
--- a/i18n/langZH.js
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-(function(root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['exports', 'echarts'], factory);
- } else if (
- typeof exports === 'object' &&
- typeof exports.nodeName !== 'string'
- ) {
- // CommonJS
- factory(exports, require('echarts'));
- } else {
- // Browser globals
- factory({}, root.echarts);
- }
-})(this, function(exports, echarts) {
- let lang = {
-
- legend: {
- selector: {
- all: '全选',
- inverse: '反选'
- }
- },
- toolbox: {
- brush: {
- title: {
- rect: '矩形选择',
- polygon: '圈选',
- lineX: '横向选择',
- lineY: '纵向选择',
- keep: '保持选择',
- clear: '清除选择'
- }
- },
- dataView: {
- title: '数据视图',
- lang: ['数据视图', '关闭', '刷新']
- },
- dataZoom: {
- title: {
- zoom: '区域缩放',
- back: '区域缩放还原'
- }
- },
- magicType: {
- title: {
- line: '切换为折线图',
- bar: '切换为柱状图',
- stack: '切换为堆叠',
- tiled: '切换为平铺'
- }
- },
- restore: {
- title: '还原'
- },
- saveAsImage: {
- title: '保存为图片',
- lang: ['右键另存为图片']
- }
- },
- series: {
- typeNames: {
- pie: '饼图',
- bar: '柱状图',
- line: '折线图',
- scatter: '散点图',
- effectScatter: '涟漪散点图',
- radar: '雷达图',
- tree: '树图',
- treemap: '矩形树图',
- boxplot: '箱型图',
- candlestick: 'K线图',
- k: 'K线图',
- heatmap: '热力图',
- map: '地图',
- parallel: '平行坐标图',
- lines: '线图',
- graph: '关系图',
- sankey: '桑基图',
- funnel: '漏斗图',
- gauge: '仪表盘图',
- pictorialBar: '象形柱图',
- themeRiver: '主题河流图',
- sunburst: '旭日图'
- }
- },
- aria: {
- general: {
- withTitle: '这是一个关于“{title}”的图表。',
- withoutTitle: '这是一个图表,'
- },
- series: {
- single: {
- prefix: '',
- withName: '图表类型是{seriesType},表示{seriesName}。',
- withoutName: '图表类型是{seriesType}。'
- },
- multiple: {
- prefix: '它由{seriesCount}个图表系列组成。',
- withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType},',
- withoutName: '第{seriesId}个系列是一个{seriesType},',
- separator: {
- middle: ';',
- end: '。'
- }
- }
- },
- data: {
- allData: '其数据是——',
- partialData: '其中,前{displayCnt}项是——',
- withName: '{name}的数据是{value}',
- withoutName: '{value}',
- separator: {
- middle: ',',
- end: ''
- }
- }
- }
- };
-
- echarts.registerLocale('ZH', lang);
-});
diff --git a/i18n/langZH.json b/i18n/langZH.json
new file mode 100644
index 0000000..5c64933
--- /dev/null
+++ b/i18n/langZH.json
@@ -0,0 +1,109 @@
+{
+ "legend": {
+ "selector": {
+ "all": "全选",
+ "inverse": "反选"
+ }
+ },
+ "toolbox": {
+ "brush": {
+ "title": {
+ "rect": "矩形选择",
+ "polygon": "圈选",
+ "lineX": "横向选择",
+ "lineY": "纵向选择",
+ "keep": "保持选择",
+ "clear": "清除选择"
+ }
+ },
+ "dataView": {
+ "title": "数据视图",
+ "lang": [
+ "数据视图",
+ "关闭",
+ "刷新"
+ ]
+ },
+ "dataZoom": {
+ "title": {
+ "zoom": "区域缩放",
+ "back": "区域缩放还原"
+ }
+ },
+ "magicType": {
+ "title": {
+ "line": "切换为折线图",
+ "bar": "切换为柱状图",
+ "stack": "切换为堆叠",
+ "tiled": "切换为平铺"
+ }
+ },
+ "restore": {
+ "title": "还原"
+ },
+ "saveAsImage": {
+ "title": "保存为图片",
+ "lang": [
+ "右键另存为图片"
+ ]
+ }
+ },
+ "series": {
+ "typeNames": {
+ "pie": "饼图",
+ "bar": "柱状图",
+ "line": "折线图",
+ "scatter": "散点图",
+ "effectScatter": "涟漪散点图",
+ "radar": "雷达图",
+ "tree": "树图",
+ "treemap": "矩形树图",
+ "boxplot": "箱型图",
+ "candlestick": "K线图",
+ "k": "K线图",
+ "heatmap": "热力图",
+ "map": "地图",
+ "parallel": "平行坐标图",
+ "lines": "线图",
+ "graph": "关系图",
+ "sankey": "桑基图",
+ "funnel": "漏斗图",
+ "gauge": "仪表盘图",
+ "pictorialBar": "象形柱图",
+ "themeRiver": "主题河流图",
+ "sunburst": "旭日图"
+ }
+ },
+ "aria": {
+ "general": {
+ "withTitle": "这是一个关于“{title}”的图表。",
+ "withoutTitle": "这是一个图表,"
+ },
+ "series": {
+ "single": {
+ "prefix": "",
+ "withName": "图表类型是{seriesType},表示{seriesName}。",
+ "withoutName": "图表类型是{seriesType}。"
+ },
+ "multiple": {
+ "prefix": "它由{seriesCount}个图表系列组成。",
+ "withName": "第{seriesId}个系列是一个表示{seriesName}的{seriesType},",
+ "withoutName": "第{seriesId}个系列是一个{seriesType},",
+ "separator": {
+ "middle": ";",
+ "end": "。"
+ }
+ }
+ },
+ "data": {
+ "allData": "其数据是——",
+ "partialData": "其中,前{displayCnt}项是——",
+ "withName": "{name}的数据是{value}",
+ "withoutName": "{value}",
+ "separator": {
+ "middle": ",",
+ "end": ""
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 18b43e4..af19970 100644
--- a/package.json
+++ b/package.json
@@ -13,11 +13,12 @@
"url": "https://github.com/apache/incubator-echarts.git"
},
"scripts": {
+ "buildi18n": "node build/build-i18n.js",
"prepublish": "node build/build.js --prepublish",
"build": "node build/build.js",
"build:full": "node build/build.js --clean",
"watch": "node build/build.js --watch",
- "dev:fast": "node build/dev-fast.js",
+ "dev:fast": "node build/build-i18n.js && node build/dev-fast.js",
"release": "node build/build.js --release",
"help": "node build/build.js --help",
"test:visual": "node test/runTest/server.js",
diff --git a/src/echarts.ts b/src/echarts.ts
index 73c883b..e242e88 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -92,6 +92,9 @@ import { getVisualFromData, getItemVisualFromData } from './visual/helper';
import LabelManager from './label/LabelManager';
import { deprecateLog } from './util/log';
import { handleLegacySelectEvents } from './legacy/dataSelectAction';
+// default import ZH and EN lang
+import langEN from "../i18n/langEN";
+import langZH from "../i18n/langZH";
// At least canvas renderer.
import 'zrender/src/canvas/canvas';
@@ -312,8 +315,8 @@ class ECharts extends Eventful {
dom: HTMLElement,
// Theme name or themeOption.
theme?: string | ThemeOption,
- locale?: string | LocaleOption,
opts?: {
+ locale?: string | LocaleOption,
renderer?: RendererType,
devicePixelRatio?: number,
width?: number,
@@ -352,8 +355,13 @@ class ECharts extends Eventful {
theme && backwardCompat(theme as ECUnitOption, true);
this._theme = theme;
- this._locale = typeof locale === 'string' ? localeStorage[locale || 'ZH'] : zrUtil.clone(locale);
- console.log(this._locale);
+
+ const {locale = 'ZH'} = opts;
+ // set default lang package
+ localeStorage['ZH'] = localeStorage['ZH'] || langZH;
+ localeStorage['EN'] = localeStorage['EN'] || langEN;
+ this._locale = typeof locale === 'string' ? localeStorage[locale] : zrUtil.clone(locale);
+ console.log(this._locale)
this._coordSysMgr = new CoordinateSystemManager();
@@ -2329,12 +2337,12 @@ const DOM_ATTRIBUTE_KEY = '_echarts_instance_';
export function init(
dom: HTMLElement,
theme?: string | object,
- locale?: string | object,
opts?: {
renderer?: RendererType,
devicePixelRatio?: number,
width?: number,
- height?: number
+ height?: number,
+ locale?: string | object,
}
): ECharts {
if (__DEV__) {
@@ -2376,7 +2384,7 @@ export function init(
}
}
- const chart = new ECharts(dom, theme, locale, opts);
+ const chart = new ECharts(dom, theme, opts);
chart.id = 'ec_' + idBase++;
instances[chart.id] = chart;
diff --git a/src/model/Global.ts b/src/model/Global.ts
index d9109d0..1557d20 100644
--- a/src/model/Global.ts
+++ b/src/model/Global.ts
@@ -128,7 +128,6 @@ class GlobalModel extends Model<ECUnitOption> {
theme = theme || {};
this.option = null; // Mark as not initialized.
this._theme = new Model(theme);
- console.log('init', locale)
this._locale = locale;
this._optionManager = optionManager;
}
@@ -428,7 +427,7 @@ class GlobalModel extends Model<ECUnitOption> {
}
getWithLocale(localePosition: Array<string>, optionsPosition?: Array<string>, localeHandlerFn?: (text: string) => string): any {
- console.log(optionsPosition, localePosition);
+ // console.log(optionsPosition, localePosition);
const locale = this.getLocale()
let localeText: string | any;
localePosition.map(t => {
@@ -438,7 +437,7 @@ class GlobalModel extends Model<ECUnitOption> {
if(localeHandlerFn) {
localeText = localeHandlerFn(localeText);
}
- console.log(localeText);
+ // console.log(localeText);
return localeText;
}
diff --git a/test/lang.html b/test/lang.html
index a498ab3..231355f 100644
--- a/test/lang.html
+++ b/test/lang.html
@@ -39,11 +39,13 @@ under the License.
}
</style>
+<div id="main2" class="main"></div>
<div id="main0" class="main"></div>
<div id="main1" class="main"></div>
<script>
- require(['echarts', 'i18n/langZH'], function (echarts) {
+ // auto with default
+ require(['echarts'], function (echarts,) {
var option = {
xAxis: {},
yAxis: {},
@@ -65,13 +67,16 @@ under the License.
animation: false
};
- var chart = echarts.init(document.getElementById('main0'), null, 'ZH');
+ var chart = echarts.init(document.getElementById('main2'), null, {
+ locale: 'EN'
+ });
chart.setOption(option);
});
</script>
<script>
- require(['echarts', 'i18n/langEN'], function (echarts) {
+ // auto register ZH lang
+ require(['echarts', 'i18n/langZH'], function (echarts,) {
var option = {
xAxis: {},
yAxis: {},
@@ -93,7 +98,41 @@ under the License.
animation: false
};
- var chart = echarts.init(document.getElementById('main1'), null, 'EN');
+ var chart = echarts.init(document.getElementById('main0'), null, {
+ locale: 'ZH'
+ });
+ chart.setOption(option);
+ });
+</script>
+
+<script>
+ // register lang
+ require(['echarts', 'i18n/langEN-obj'], function (echarts, {lang}) {
+ echarts.registerLocale('EN', lang);
+ var option = {
+ xAxis: {},
+ yAxis: {},
+ series: {
+ type: 'line',
+ data: [[11, 22], [33, 44]]
+ },
+ toolbox: {
+ show: true,
+ feature: {
+ restore: {},
+ saveAsImage: {},
+ dataZoom: {},
+ dataView: {}
+ },
+ orient: 'vertical',
+ right: 5
+ },
+ animation: false
+ };
+
+ var chart = echarts.init(document.getElementById('main1'), null, {
+ locale: 'EN'
+ });
chart.setOption(option);
});
</script>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org