diff --git a/babel/messages.pot b/babel/messages.pot
index 198b467626b7..41851e382ad0 100755
--- a/babel/messages.pot
+++ b/babel/messages.pot
@@ -916,3 +916,55 @@ msgstr ""
msgid "Saved Queries"
msgstr ""
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:73
+msgid "Please enter a slice name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:88
+msgid "Please select a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:96
+msgid "Please enter a dashboard name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:134
+msgid "Save A Slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:155
+msgid "Overwrite slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:164
+msgid "Save as"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:168
+msgid "[slice name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:181
+msgid "Do not add to a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:189
+msgid "Add slice to existing dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:203
+msgid "Add to new dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:208
+msgid "[dashboard name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:220
+msgid "Save"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:229
+msgid "Save & go to dashboard"
+msgstr ""
+
diff --git a/superset/.config.py.swp b/superset/.config.py.swp
new file mode 100644
index 000000000000..23cdfdb4edb5
Binary files /dev/null and b/superset/.config.py.swp differ
diff --git a/superset/assets/javascripts/explore/components/SaveModal.jsx b/superset/assets/javascripts/explore/components/SaveModal.jsx
index 4dbff36acfea..a6b645e85586 100644
--- a/superset/assets/javascripts/explore/components/SaveModal.jsx
+++ b/superset/assets/javascripts/explore/components/SaveModal.jsx
@@ -5,6 +5,7 @@ import $ from 'jquery';
import { Modal, Alert, Button, Radio } from 'react-bootstrap';
import Select from 'react-select';
import { connect } from 'react-redux';
+import { t } from '../../locale';
const propTypes = {
can_overwrite: PropTypes.bool,
@@ -69,7 +70,7 @@ class SaveModal extends React.Component {
if (sliceParams.action === 'saveas') {
sliceName = this.state.newSliceName;
if (sliceName === '') {
- this.setState({ alert: 'Please enter a slice name' });
+ this.setState({ alert: t('Please enter a slice name') });
return;
}
sliceParams.slice_name = sliceName;
@@ -84,7 +85,7 @@ class SaveModal extends React.Component {
case ('existing'):
dashboard = this.state.saveToDashboardId;
if (!dashboard) {
- this.setState({ alert: 'Please select a dashboard' });
+ this.setState({ alert: t('Please select a dashboard') });
return;
}
sliceParams.save_to_dashboard_id = dashboard;
@@ -92,7 +93,7 @@ class SaveModal extends React.Component {
case ('new'):
dashboard = this.state.newDashboardName;
if (dashboard === '') {
- this.setState({ alert: 'Please enter a dashboard name' });
+ this.setState({ alert: t('Please enter a dashboard name') });
return;
}
sliceParams.new_dashboard_name = dashboard;
@@ -130,7 +131,7 @@ class SaveModal extends React.Component {
>
- Save A Slice
+ {t('Save A Slice')}
@@ -151,7 +152,7 @@ class SaveModal extends React.Component {
checked={this.state.action === 'overwrite'}
onChange={this.changeAction.bind(this, 'overwrite')}
>
- {`Overwrite slice ${this.props.slice.slice_name}`}
+ {`${t('Overwrite slice')} ${this.props.slice.slice_name}`}
}
@@ -160,11 +161,11 @@ class SaveModal extends React.Component {
inline
checked={this.state.action === 'saveas'}
onChange={this.changeAction.bind(this, 'saveas')}
- > Save as
+ > {t('Save as')}
@@ -177,7 +178,7 @@ class SaveModal extends React.Component {
checked={this.state.addToDash === 'noSave'}
onChange={this.changeDash.bind(this, 'noSave')}
>
- Do not add to a dashboard
+ {t('Do not add to a dashboard')}
- Add slice to existing dashboard
+ {t('Add slice to existing dashboard')}
@@ -215,7 +217,7 @@ class SaveModal extends React.Component {
className="btn pull-left"
onClick={this.saveOrOverwrite.bind(this, false)}
>
- Save
+ {t('Save')}
diff --git a/superset/assets/javascripts/explore/stores/language.js b/superset/assets/javascripts/explore/stores/language.js
new file mode 100644
index 000000000000..23898a8d1843
--- /dev/null
+++ b/superset/assets/javascripts/explore/stores/language.js
@@ -0,0 +1,34 @@
+
+const $ = window.$ = require('jquery');
+
+export function getLanguage() {
+ function getLocale() {
+ const locale = $.ajax({
+ url: '/superset/rest/api/getLocale',
+ async: false,
+ });
+ return locale.responseText;
+ }
+ switch (getLocale()) {
+ case 'en':
+ return 'es';
+ case 'fr':
+ return 'fr';
+ case 'it':
+ return 'it';
+ case 'zh':
+ return 'zh';
+ default:
+ return 'es';
+ }
+}
+
+export function getTranslate() {
+ const translate = $.ajax({
+ url: '/superset/rest/api/getTranslate',
+ async: false,
+ });
+ console.log(translate);
+ console.log(translate.responseText);
+ return translate.responseText;
+}
diff --git a/superset/assets/javascripts/locale.jsx b/superset/assets/javascripts/locale.jsx
new file mode 100644
index 000000000000..01054480d4ce
--- /dev/null
+++ b/superset/assets/javascripts/locale.jsx
@@ -0,0 +1,193 @@
+import Jed from 'jed';
+import React from 'react';
+import { getTranslations } from './translations';
+import { sprintf } from 'sprintf-js';
+import { getLanguage } from './explore/stores/language';
+
+let LOCALE_DEBUG = false;
+
+if (sessionStorage && sessionStorage.getItem('localeDebug') == '1') {
+ LOCALE_DEBUG = true;
+}
+
+export function setLocaleDebug(value) {
+ sessionStorage.setItem('localeDebug', value ? '1' : '0');
+}
+
+let i18n = null;
+
+export function setLocale(locale) {
+ let translations = getTranslations(locale);
+ i18n = new Jed({
+ 'domain' : 'superset',
+ 'missing_key_callback' : function(key) {
+ // console.error(key)
+ },
+ 'locale_data': {
+ 'superset': translations
+ }
+ });
+}
+
+setLocale(getLanguage());
+// setLocale('zh');
+
+function formatForReact(formatString, args) {
+ let rv = [];
+ let cursor = 0;
+ sprintf.parse(formatString).forEach((match, idx) => {
+ if (typeof match === 'string') {
+ rv.push(match);
+ } else {
+ let arg = null;
+ if (match[2]) {
+ arg = args[0][match[2][0]];
+ } else if (match[1]) {
+ arg = args[parseInt(match[1], 10) - 1];
+ } else {
+ arg = args[cursor++];
+ }
+ if (React.isValidElement(arg)) {
+ rv.push(React.cloneElement(arg, {key: idx}));
+ } else {
+ match[2] = null;
+ match[1] = 1;
+ rv.push(
+ {sprintf.format([match], [null, arg])}
+ );
+ }
+ }
+ });
+ return rv;
+}
+
+function argsInvolveReact(args) {
+ if (args.some(React.isValidElement)) {
+ return true;
+ }
+ if (args.length == 1 && typeof args[0] === 'object') {
+ return Object.keys(args[0]).some((key) => {
+ return React.isValidElement(args[0][key]);
+ });
+ }
+ return false;
+}
+
+export function parseComponentTemplate(string) {
+ let rv = {};
+ function process(startPos, group, inGroup) {
+ let regex = /\[(.*?)(:|\])|\]/g;
+ let match;
+ let buf = [];
+ let satisfied = false;
+ let pos = regex.lastIndex = startPos;
+ while ((match = regex.exec(string)) !== null) {
+ let substr = string.substr(pos, match.index - pos);
+ if (substr !== '') {
+ buf.push(substr);
+ }
+ if (match[0] == ']') {
+ if (inGroup) {
+ satisfied = true;
+ break;
+ } else {
+ pos = regex.lastIndex;
+ continue;
+ }
+ }
+ if (match[2] == ']') {
+ pos = regex.lastIndex;
+ } else {
+ pos = regex.lastIndex = process(regex.lastIndex, match[1], true);
+ }
+ buf.push({group: match[1]});
+ }
+ let endPos = regex.lastIndex;
+ if (!satisfied) {
+ let rest = string.substr(pos);
+ if (rest) {
+ buf.push(rest);
+ }
+ endPos = string.length;
+ }
+ rv[group] = buf;
+ return endPos;
+ }
+ process(0, 'root', false);
+ return rv;
+}
+
+export function renderComponentTemplate(template, components) {
+ let idx = 0;
+ function renderGroup(group) {
+ let children = [];
+ (template[group] || []).forEach((item) => {
+ if (typeof item === 'string') {
+ children.push({item});
+ } else {
+ children.push(renderGroup(item.group));
+ }
+ });
+ let reference = components[group] || ;
+ if (!React.isValidElement(reference)) {
+ reference = {reference};
+ }
+ if (children.length > 0) {
+ return React.cloneElement(reference, {key: idx++}, children);
+ } else {
+ return React.cloneElement(reference, {key: idx++});
+ }
+ }
+ return renderGroup('root');
+}
+
+function mark(rv) {
+ if (!LOCALE_DEBUG) {
+ return rv;
+ }
+ let proxy = {
+ $$typeof: Symbol.for('react.element'),
+ type: 'span',
+ key: null,
+ ref: null,
+ props: {
+ className: 'translation-wrapper',
+ children: typeof rv === 'array' ? rv : [rv]
+ },
+ _owner: null,
+ _store: {}
+ };
+ proxy.toString = function() {
+ return '🇦🇹' + rv + '🇦🇹';
+ };
+ return proxy;
+}
+
+export function format(formatString, args) {
+ if (argsInvolveReact(args)) {
+ return formatForReact(formatString, args);
+ } else {
+ return sprintf(formatString, ...args);
+ }
+}
+
+export function gettext(string, ...args) {
+ let rv = i18n.gettext(string);
+ if (args.length > 0) {
+ rv = format(rv, args);
+ }
+ return mark(rv);
+}
+
+export function ngettext(singular, plural, ...args) {
+ return mark(format(i18n.ngettext(singular, plural, args[0] || 0), args));
+}
+
+export function gettextComponentTemplate(template, components) {
+ let tmpl = parseComponentTemplate(i18n.gettext(template));
+ return mark(renderComponentTemplate(tmpl, components));
+}
+
+export const t = gettext;
+export const tn = ngettext;
+export const tct = gettextComponentTemplate;
diff --git a/superset/assets/javascripts/translations.jsx b/superset/assets/javascripts/translations.jsx
new file mode 100644
index 000000000000..8853cb49ff17
--- /dev/null
+++ b/superset/assets/javascripts/translations.jsx
@@ -0,0 +1,34 @@
+import {_} from 'underscore';
+
+const catalogs = (function() {
+ let info = require('../../translations/catalogs.json');
+ return info.supported_locales;
+})();
+
+export const translations = (function() {
+ let ctx = require.context('../../translations/', true, /\.po$/);
+ let rv = {};
+ ctx.keys().forEach((translation) => {
+ let langCode = translation.match(/([a-zA-Z_]+)/)[1];
+ if (_.contains(catalogs, langCode)) {
+ rv[dirname_to_locale(langCode)] = ctx(translation);
+ }
+ });
+ return rv;
+})();
+
+export function getTranslations(language) {
+ return translations[language] || translations['zh'];
+}
+
+export function translationsExist(language) {
+ return translations[language] !== undefined;
+}
+
+function dirname_to_locale(dir_name) {
+ if (dir_name.indexOf('_') >= 0) {
+ let locale_array = dir_name.split('_');
+ dir_name = locale_array[0] + '-' + locale_array[1].toLowerCase();
+ }
+ return dir_name
+}
diff --git a/superset/assets/package.json b/superset/assets/package.json
index 5d5022def10b..c5136b7c6c42 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -53,6 +53,7 @@
"datatables.net": "^1.10.13",
"datatables.net-bs": "^1.10.12",
"immutable": "^3.8.1",
+ "jed": "^1.1.1",
"jquery": "^3.2.1",
"jsdom": "9.12.0",
"lodash.throttle": "^4.1.1",
@@ -60,6 +61,7 @@
"moment": "^2.14.1",
"mustache": "^2.2.1",
"nvd3": "1.8.5",
+ "po-catalog-loader": "^1.2.0",
"prop-types": "^15.5.8",
"react": "^15.5.1",
"react-ace": "^5.0.1",
@@ -84,7 +86,9 @@
"redux-localstorage": "^0.4.1",
"redux-thunk": "^2.1.0",
"shortid": "^2.2.6",
+ "sprintfjs": "^1.2.6",
"supercluster": "https://github.com/georgeke/supercluster/tarball/ac3492737e7ce98e07af679623aad452373bbc40",
+ "underscore": "^1.8.3",
"urijs": "^1.18.10",
"viewport-mercator-project": "^2.1.0"
},
diff --git a/superset/assets/webpack.config.js b/superset/assets/webpack.config.js
index be8c8c983b9c..e2f33bc71afb 100644
--- a/superset/assets/webpack.config.js
+++ b/superset/assets/webpack.config.js
@@ -107,6 +107,14 @@ const config = {
include: APP_DIR + '/node_modules/mapbox-gl/js/render/painter/use_program.js',
loader: 'transform/cacheable?brfs',
},
+ {
+ test: /\.po$/,
+ loader: 'po-catalog-loader',
+ query: {
+ referenceExtensions: ['.js', '.jsx'],
+ domain: 'superset'
+ }
+ },
],
},
externals: {
diff --git a/superset/config.py b/superset/config.py
index 6c38fa2f76b5..394c7f215e01 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -144,8 +144,8 @@
LANGUAGES = {
'en': {'flag': 'us', 'name': 'English'},
'it': {'flag': 'it', 'name': 'Italian'},
- # 'fr': {'flag': 'fr', 'name': 'French'},
- # 'zh': {'flag': 'cn', 'name': 'Chinese'},
+ 'fr': {'flag': 'fr', 'name': 'French'},
+ 'zh': {'flag': 'cn', 'name': 'Chinese'},
}
# ---------------------------------------------------
# Image and file configuration
diff --git a/superset/translations/catalogs.json b/superset/translations/catalogs.json
new file mode 100644
index 000000000000..b4fe4dd395c5
--- /dev/null
+++ b/superset/translations/catalogs.json
@@ -0,0 +1,8 @@
+{
+ "supported_locales": [
+ "es",
+ "fr",
+ "it",
+ "zh"
+ ]
+}
diff --git a/superset/translations/es/LC_MESSAGES/messages.mo b/superset/translations/es/LC_MESSAGES/messages.mo
index 3da6a8e16b57..580a31fcab3e 100644
Binary files a/superset/translations/es/LC_MESSAGES/messages.mo and b/superset/translations/es/LC_MESSAGES/messages.mo differ
diff --git a/superset/translations/es/LC_MESSAGES/messages.po b/superset/translations/es/LC_MESSAGES/messages.po
index 16e4d98dad98..569c5b9a16bd 100644
--- a/superset/translations/es/LC_MESSAGES/messages.po
+++ b/superset/translations/es/LC_MESSAGES/messages.po
@@ -917,6 +917,58 @@ msgstr ""
msgid "Saved Queries"
msgstr ""
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:73
+msgid "Please enter a slice name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:88
+msgid "Please select a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:96
+msgid "Please enter a dashboard name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:134
+msgid "Save A Slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:155
+msgid "Overwrite slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:164
+msgid "Save as"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:168
+msgid "[slice name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:181
+msgid "Do not add to a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:189
+msgid "Add slice to existing dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:203
+msgid "Add to new dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:208
+msgid "[dashboard name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:220
+msgid "Save"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:229
+msgid "Save & go to dashboard"
+msgstr ""
+
#~ msgid "Databases"
#~ msgstr ""
diff --git a/superset/translations/fr/LC_MESSAGES/messages.mo b/superset/translations/fr/LC_MESSAGES/messages.mo
index ce19085d4ae0..4fa36d625378 100644
Binary files a/superset/translations/fr/LC_MESSAGES/messages.mo and b/superset/translations/fr/LC_MESSAGES/messages.mo differ
diff --git a/superset/translations/fr/LC_MESSAGES/messages.po b/superset/translations/fr/LC_MESSAGES/messages.po
index 15b8fc40321f..a45d5bacd198 100644
--- a/superset/translations/fr/LC_MESSAGES/messages.po
+++ b/superset/translations/fr/LC_MESSAGES/messages.po
@@ -917,6 +917,58 @@ msgstr ""
msgid "Saved Queries"
msgstr ""
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:73
+msgid "Please enter a slice name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:88
+msgid "Please select a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:96
+msgid "Please enter a dashboard name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:134
+msgid "Save A Slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:155
+msgid "Overwrite slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:164
+msgid "Save as"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:168
+msgid "[slice name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:181
+msgid "Do not add to a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:189
+msgid "Add slice to existing dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:203
+msgid "Add to new dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:208
+msgid "[dashboard name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:220
+msgid "Save"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:229
+msgid "Save & go to dashboard"
+msgstr ""
+
#~ msgid "Databases"
#~ msgstr ""
diff --git a/superset/translations/it/LC_MESSAGES/messages.mo b/superset/translations/it/LC_MESSAGES/messages.mo
index c3583a7db5a8..b38fc85fcb5d 100644
Binary files a/superset/translations/it/LC_MESSAGES/messages.mo and b/superset/translations/it/LC_MESSAGES/messages.mo differ
diff --git a/superset/translations/it/LC_MESSAGES/messages.po b/superset/translations/it/LC_MESSAGES/messages.po
index ea03d006fa5b..2ae9574b2b91 100644
--- a/superset/translations/it/LC_MESSAGES/messages.po
+++ b/superset/translations/it/LC_MESSAGES/messages.po
@@ -981,3 +981,55 @@ msgstr ""
msgid "Saved Queries"
msgstr ""
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:73
+msgid "Please enter a slice name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:88
+msgid "Please select a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:96
+msgid "Please enter a dashboard name"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:134
+msgid "Save A Slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:155
+msgid "Overwrite slice"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:164
+msgid "Save as"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:168
+msgid "[slice name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:181
+msgid "Do not add to a dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:189
+msgid "Add slice to existing dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:203
+msgid "Add to new dashboard"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:208
+msgid "[dashboard name]"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:220
+msgid "Save"
+msgstr ""
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:229
+msgid "Save & go to dashboard"
+msgstr ""
+
diff --git a/superset/translations/zh/LC_MESSAGES/messages.mo b/superset/translations/zh/LC_MESSAGES/messages.mo
index d35a0d6341fe..a23a0d28bf58 100644
Binary files a/superset/translations/zh/LC_MESSAGES/messages.mo and b/superset/translations/zh/LC_MESSAGES/messages.mo differ
diff --git a/superset/translations/zh/LC_MESSAGES/messages.po b/superset/translations/zh/LC_MESSAGES/messages.po
index 4c72d20b2f97..6dbc3202a69b 100644
--- a/superset/translations/zh/LC_MESSAGES/messages.po
+++ b/superset/translations/zh/LC_MESSAGES/messages.po
@@ -917,3 +917,55 @@ msgstr "查询"
msgid "Saved Queries"
msgstr "已保存查询"
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:73
+msgid "Please enter a slice name"
+msgstr "请输入切片名称"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:88
+msgid "Please select a dashboard"
+msgstr "请选择一个仪表盘"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:96
+msgid "Please enter a dashboard name"
+msgstr "请输入仪表盘名称"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:134
+msgid "Save A Slice"
+msgstr "保存一个切片"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:155
+msgid "Overwrite slice"
+msgstr "覆盖切片"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:164
+msgid "Save as"
+msgstr "另存为"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:168
+msgid "[slice name]"
+msgstr "[切片名称]"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:181
+msgid "Do not add to a dashboard"
+msgstr "不要添加到仪表盘"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:189
+msgid "Add slice to existing dashboard"
+msgstr "将切片添加到现有仪表盘"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:203
+msgid "Add to new dashboard"
+msgstr "添加到新的仪表盘"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:208
+msgid "[dashboard name]"
+msgstr "[仪表盘名称]"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:220
+msgid "Save"
+msgstr "保存"
+
+#: superset/assets/javascripts/explore/components/SaveModal.jsx:229
+msgid "Save & go to dashboard"
+msgstr "保存并转到仪表盘"
+
diff --git a/superset/views/core.py b/superset/views/core.py
index dec49edd23a3..008a2685494c 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -26,6 +26,7 @@
from flask_babel import gettext as __
from flask_babel import lazy_gettext as _
+from flask.ext import babel
from sqlalchemy import create_engine
from werkzeug.routing import BaseConverter
@@ -2260,6 +2261,11 @@ def sqllab(self):
'superset/sqllab.html',
bootstrap_data=json.dumps(d, default=utils.json_iso_dttm_ser)
)
+
+ @has_access
+ @expose("/rest/api/getLocale", methods=['GET', 'POST'])
+ def getLocale(self):
+ return str(babel.get_locale())
appbuilder.add_view_no_menu(Superset)