feat: support custom locale language
support custom locale language and custom support languages. Change-Id: Ia235cb83b65c8530449f52d9ea575c3d6f603f04
This commit is contained in:
parent
dd00305998
commit
b36d5593e6
@ -139,6 +139,7 @@
|
|||||||
"globals": true,
|
"globals": true,
|
||||||
"request": true,
|
"request": true,
|
||||||
"METRICDICT": true,
|
"METRICDICT": true,
|
||||||
"globalCSS": true
|
"globalCSS": true,
|
||||||
|
"GLOBAL_VARIABLES": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
host: 0.0.0.0
|
host: 0.0.0.0
|
||||||
port: 8088
|
port: 8088
|
||||||
server: http://localhost
|
server: http://localhost
|
||||||
|
|
||||||
|
globalVariables:
|
||||||
|
defaultLanguage: en
|
||||||
|
supportLanguages: # use value in i18n.js
|
||||||
|
- en
|
||||||
|
- zh-hans
|
||||||
|
- ko-kr
|
||||||
|
@ -2,7 +2,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
const { merge } = require('lodash');
|
const { merge, extend, has } = require('lodash');
|
||||||
|
|
||||||
const root = (dir) =>
|
const root = (dir) =>
|
||||||
`${path.resolve(__dirname, './')}/${dir}`.replace(/(\/+)/g, '/');
|
`${path.resolve(__dirname, './')}/${dir}`.replace(/(\/+)/g, '/');
|
||||||
@ -22,16 +22,43 @@ const getServerConfig = (key) => {
|
|||||||
const tryFile = root('./local_config.yaml');
|
const tryFile = root('./local_config.yaml');
|
||||||
if (fs.existsSync(tryFile)) {
|
if (fs.existsSync(tryFile)) {
|
||||||
// merge local_config
|
// merge local_config
|
||||||
const local_config = loadYaml(tryFile);
|
const localConfig = loadYaml(tryFile);
|
||||||
if (typeof local_config === 'object') {
|
if (typeof localConfig === 'object') {
|
||||||
merge(config, local_config);
|
merge(config, localConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return key ? config[key] : config;
|
return key ? config[key] : config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getObjectConfig = (key) => {
|
||||||
|
// parse config yaml
|
||||||
|
const config = loadYaml(root('./config.yaml')) || {};
|
||||||
|
if (!has(config, key)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const defaultConfig = config[key];
|
||||||
|
|
||||||
|
const result = defaultConfig;
|
||||||
|
const tryFile = root('./local_config.yaml');
|
||||||
|
if (fs.existsSync(tryFile)) {
|
||||||
|
// merge local_config
|
||||||
|
const localConfig = loadYaml(tryFile);
|
||||||
|
extend(result, localConfig[key] || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGlobalVariables = () => {
|
||||||
|
const variables = getObjectConfig('globalVariables') || {};
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('globalVariables', variables, JSON.stringify(variables));
|
||||||
|
return JSON.stringify(variables);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getServerConfig,
|
getServerConfig,
|
||||||
root,
|
root,
|
||||||
|
getGlobalVariables,
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ const { normalize, resolve } = require('path');
|
|||||||
// const path = require("path");
|
// const path = require("path");
|
||||||
// const CleanWebpackPlugin = require('clean-webpack-plugin');
|
// const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
const { getGlobalVariables } = require('./utils');
|
||||||
|
|
||||||
const root = (path) => resolve(__dirname, `../${path}`);
|
const root = (path) => resolve(__dirname, `../${path}`);
|
||||||
const version = moment().unix();
|
const version = moment().unix();
|
||||||
@ -109,7 +110,12 @@ module.exports = {
|
|||||||
client: root('src/client'),
|
client: root('src/client'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)],
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
GLOBAL_VARIABLES: getGlobalVariables(),
|
||||||
|
}),
|
||||||
|
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.version = version;
|
module.exports.version = version;
|
||||||
|
@ -32,4 +32,10 @@ module.exports = {
|
|||||||
moduleDirectories: ['node_modules', 'src'],
|
moduleDirectories: ['node_modules', 'src'],
|
||||||
testPathIgnorePatterns: ['node_modules', '.cache', 'test/e2e', 'config'],
|
testPathIgnorePatterns: ['node_modules', '.cache', 'test/e2e', 'config'],
|
||||||
setupFiles: ['<rootDir>/test/unit/setup-tests.js'],
|
setupFiles: ['<rootDir>/test/unit/setup-tests.js'],
|
||||||
|
globals: {
|
||||||
|
GLOBAL_VARIABLES: {
|
||||||
|
defaultLanguage: 'en',
|
||||||
|
supportLanguages: ['en', 'zh-hans'],
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support custom local language:
|
||||||
|
|
||||||
|
* The globalVariables in the config.yaml support default language and all support languages.
|
||||||
|
|
||||||
|
* Use globalVariables in the local_config.yaml to support custom local languages and custom support languages.
|
||||||
|
|
||||||
|
* If only one support language, the switch language icon will been auto hidden.
|
@ -103,6 +103,21 @@ export class AvatarDropdown extends React.Component {
|
|||||||
return <span style={{ float: 'right' }}>{btns}</span>;
|
return <span style={{ float: 'right' }}>{btns}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLanguageMenuItem() {
|
||||||
|
if (SUPPORT_LOCALES.length <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Menu.Item
|
||||||
|
key="language"
|
||||||
|
className={`${styles['no-hover']} ${styles['menu-item']}`}
|
||||||
|
>
|
||||||
|
<span>{t('Switch Language')}</span>
|
||||||
|
{this.renderLanguageSwitch()}
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.user) {
|
if (!this.user) {
|
||||||
return (
|
return (
|
||||||
@ -136,13 +151,7 @@ export class AvatarDropdown extends React.Component {
|
|||||||
</Button>
|
</Button>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
<Menu.Item
|
{this.renderLanguageMenuItem()}
|
||||||
key="language"
|
|
||||||
className={`${styles['no-hover']} ${styles['menu-item']}`}
|
|
||||||
>
|
|
||||||
<span>{t('Switch Language')}</span>
|
|
||||||
{this.renderLanguageSwitch()}
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item key="password" className={styles['menu-item']}>
|
<Menu.Item key="password" className={styles['menu-item']}>
|
||||||
<ItemActionButtons
|
<ItemActionButtons
|
||||||
actions={{ moreActions: [{ action: Password }] }}
|
actions={{ moreActions: [{ action: Password }] }}
|
||||||
|
@ -22,6 +22,10 @@ import styles from './index.less';
|
|||||||
const { getLocale, setLocale, SUPPORT_LOCALES } = i18n;
|
const { getLocale, setLocale, SUPPORT_LOCALES } = i18n;
|
||||||
|
|
||||||
const SelectLang = (props) => {
|
const SelectLang = (props) => {
|
||||||
|
if (SUPPORT_LOCALES.length <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const { className } = props;
|
const { className } = props;
|
||||||
const selectedLang = getLocale();
|
const selectedLang = getLocale();
|
||||||
|
|
||||||
|
@ -14,35 +14,69 @@
|
|||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import 'moment/locale/zh-cn';
|
import 'moment/locale/zh-cn';
|
||||||
import _ from 'lodash';
|
import _, { isArray } from 'lodash';
|
||||||
import cookie from 'utils/cookie';
|
import cookie from 'utils/cookie';
|
||||||
import SLI18n from 'utils/translate';
|
import SLI18n from 'utils/translate';
|
||||||
import { setLocalStorageItem } from 'utils/local-storage';
|
import { setLocalStorageItem } from 'utils/local-storage';
|
||||||
|
|
||||||
import locales from '../locales';
|
import locales from '../locales';
|
||||||
|
|
||||||
// shortName: the i18n name in the api
|
// shortName: the i18n name in the api
|
||||||
// icon: the icon of switch language in ui
|
// icon: the icon of switch language in ui
|
||||||
const SUPPORT_LOCALES = [
|
const SUPPORT_LOCALES_ALL = [
|
||||||
{
|
{
|
||||||
name: 'English',
|
name: 'English',
|
||||||
value: 'en',
|
value: 'en',
|
||||||
shortName: 'en',
|
shortName: 'en',
|
||||||
icon: 'en',
|
icon: 'en',
|
||||||
|
momentName: 'en',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '简体中文',
|
name: '简体中文',
|
||||||
value: 'zh-hans',
|
value: 'zh-hans',
|
||||||
shortName: 'zh',
|
shortName: 'zh',
|
||||||
icon: 'zh',
|
icon: 'zh',
|
||||||
|
momentName: 'zhCN',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '한글',
|
name: '한글',
|
||||||
value: 'ko-kr',
|
value: 'ko-kr',
|
||||||
shortName: 'ko',
|
shortName: 'ko',
|
||||||
icon: 'ko',
|
icon: 'ko',
|
||||||
|
momentName: 'ko',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const getDefaultLanguageInConfig = () => {
|
||||||
|
const { defaultLanguage } = GLOBAL_VARIABLES;
|
||||||
|
const defaultLang = defaultLanguage || 'en';
|
||||||
|
const inSupport = SUPPORT_LOCALES_ALL.find((it) => it.value === defaultLang);
|
||||||
|
return inSupport ? defaultLang : 'en';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSupportLanguagesInConfig = () => {
|
||||||
|
const { supportLanguages } = GLOBAL_VARIABLES;
|
||||||
|
const defaultLang = getDefaultLanguageInConfig();
|
||||||
|
const defaultSupportLanguages = [defaultLang];
|
||||||
|
if (!supportLanguages || !isArray(supportLanguages)) {
|
||||||
|
return defaultSupportLanguages;
|
||||||
|
}
|
||||||
|
if (!supportLanguages.includes(defaultLang)) {
|
||||||
|
return [...supportLanguages, defaultLang];
|
||||||
|
}
|
||||||
|
return supportLanguages;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SUPPORT_LOCALES = SUPPORT_LOCALES_ALL.filter((it) => {
|
||||||
|
const supportLanguages = getSupportLanguagesInConfig();
|
||||||
|
return supportLanguages.includes(it.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getMomentName = (locale) => {
|
||||||
|
const item = SUPPORT_LOCALES_ALL.find((it) => it.value === locale);
|
||||||
|
return (item || {}).momentName || 'en';
|
||||||
|
};
|
||||||
|
|
||||||
const intl = new SLI18n();
|
const intl = new SLI18n();
|
||||||
|
|
||||||
let currentLocals = null;
|
let currentLocals = null;
|
||||||
@ -63,9 +97,11 @@ const getLocale = () => {
|
|||||||
localStorageLocaleKey: 'lang',
|
localStorageLocaleKey: 'lang',
|
||||||
});
|
});
|
||||||
|
|
||||||
// If not found, the default is English
|
const { defaultLanguage } = GLOBAL_VARIABLES;
|
||||||
|
|
||||||
|
// If not found, the default language is set in config.yaml
|
||||||
if (!_.find(SUPPORT_LOCALES, { value: currentLocale })) {
|
if (!_.find(SUPPORT_LOCALES, { value: currentLocale })) {
|
||||||
currentLocale = 'en';
|
currentLocale = defaultLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentLocals) {
|
if (!currentLocals) {
|
||||||
@ -93,7 +129,7 @@ const loadLocales = () => {
|
|||||||
const setLocale = (lang) => {
|
const setLocale = (lang) => {
|
||||||
setLocaleToStorage(lang);
|
setLocaleToStorage(lang);
|
||||||
cookie('lang', lang);
|
cookie('lang', lang);
|
||||||
moment.locale(lang);
|
moment.locale(getMomentName(lang));
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
return lang;
|
return lang;
|
||||||
};
|
};
|
||||||
@ -104,7 +140,7 @@ const init = () => {
|
|||||||
const lang = getLocale();
|
const lang = getLocale();
|
||||||
|
|
||||||
if (lang === 'zh-hans') {
|
if (lang === 'zh-hans') {
|
||||||
moment.locale('zh', {
|
moment.locale('zh-cn', {
|
||||||
relativeTime: {
|
relativeTime: {
|
||||||
s: '1秒',
|
s: '1秒',
|
||||||
ss: '%d秒',
|
ss: '%d秒',
|
||||||
@ -140,6 +176,7 @@ const t = (key, options) => intl.get(key, options);
|
|||||||
t.html = (key, options) => intl.getHTML(key, options);
|
t.html = (key, options) => intl.getHTML(key, options);
|
||||||
|
|
||||||
loadLocales();
|
loadLocales();
|
||||||
|
init();
|
||||||
window.t = t;
|
window.t = t;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -17,12 +17,13 @@ import ReactDOM from 'react-dom';
|
|||||||
import { createBrowserHistory } from 'history';
|
import { createBrowserHistory } from 'history';
|
||||||
import { syncHistoryWithStore } from 'mobx-react-router';
|
import { syncHistoryWithStore } from 'mobx-react-router';
|
||||||
import { ConfigProvider } from 'antd';
|
import { ConfigProvider } from 'antd';
|
||||||
import zhCN from 'antd/es/locale/zh_CN';
|
|
||||||
import enUS from 'antd/es/locale/en_US';
|
|
||||||
import globalRootStore from 'stores/root';
|
import globalRootStore from 'stores/root';
|
||||||
import PageLoading from 'components/PageLoading';
|
import PageLoading from 'components/PageLoading';
|
||||||
import metricDict from 'resources/prometheus/metricDict';
|
import metricDict from 'resources/prometheus/metricDict';
|
||||||
import variables from 'styles/variables.less';
|
import variables from 'styles/variables.less';
|
||||||
|
import zhCN from 'antd/es/locale/zh_CN';
|
||||||
|
import enUS from 'antd/es/locale/en_US';
|
||||||
|
import koKR from 'antd/es/locale/ko_KR';
|
||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
@ -33,8 +34,19 @@ window.globalCSS = variables;
|
|||||||
const store = globalRootStore;
|
const store = globalRootStore;
|
||||||
const browserHistory = createBrowserHistory();
|
const browserHistory = createBrowserHistory();
|
||||||
const history = syncHistoryWithStore(browserHistory, store.routing);
|
const history = syncHistoryWithStore(browserHistory, store.routing);
|
||||||
const lang = i18n.getLocale();
|
|
||||||
const localeProvider = lang === 'en' ? enUS : zhCN;
|
const antdLanguageMap = {
|
||||||
|
en: enUS,
|
||||||
|
'zh-hans': zhCN,
|
||||||
|
'ko-kr': koKR,
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAntdLocale = (locale) => {
|
||||||
|
const lang = locale || i18n.getLocale();
|
||||||
|
return antdLanguageMap[lang] || enUS;
|
||||||
|
};
|
||||||
|
|
||||||
|
const localeProvider = getAntdLocale(i18n.getLocale());
|
||||||
|
|
||||||
const render = (component) => {
|
const render = (component) => {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
|
Loading…
Reference in New Issue
Block a user