feat: support custom locale language

support custom locale language and custom support languages.

Change-Id: Ia235cb83b65c8530449f52d9ea575c3d6f603f04
This commit is contained in:
Jingwei.Zhang 2023-08-08 13:17:29 +08:00
parent dd00305998
commit b36d5593e6
10 changed files with 142 additions and 23 deletions

View File

@ -139,6 +139,7 @@
"globals": true,
"request": true,
"METRICDICT": true,
"globalCSS": true
"globalCSS": true,
"GLOBAL_VARIABLES": true
}
}

View File

@ -1,3 +1,10 @@
host: 0.0.0.0
port: 8088
server: http://localhost
globalVariables:
defaultLanguage: en
supportLanguages: # use value in i18n.js
- en
- zh-hans
- ko-kr

View File

@ -2,7 +2,7 @@ const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
const { merge } = require('lodash');
const { merge, extend, has } = require('lodash');
const root = (dir) =>
`${path.resolve(__dirname, './')}/${dir}`.replace(/(\/+)/g, '/');
@ -22,16 +22,43 @@ const getServerConfig = (key) => {
const tryFile = root('./local_config.yaml');
if (fs.existsSync(tryFile)) {
// merge local_config
const local_config = loadYaml(tryFile);
if (typeof local_config === 'object') {
merge(config, local_config);
const localConfig = loadYaml(tryFile);
if (typeof localConfig === 'object') {
merge(config, localConfig);
}
}
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 = {
getServerConfig,
root,
getGlobalVariables,
};

View File

@ -17,6 +17,7 @@ const { normalize, resolve } = require('path');
// const path = require("path");
// const CleanWebpackPlugin = require('clean-webpack-plugin');
const moment = require('moment');
const { getGlobalVariables } = require('./utils');
const root = (path) => resolve(__dirname, `../${path}`);
const version = moment().unix();
@ -109,7 +110,12 @@ module.exports = {
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;

View File

@ -32,4 +32,10 @@ module.exports = {
moduleDirectories: ['node_modules', 'src'],
testPathIgnorePatterns: ['node_modules', '.cache', 'test/e2e', 'config'],
setupFiles: ['<rootDir>/test/unit/setup-tests.js'],
globals: {
GLOBAL_VARIABLES: {
defaultLanguage: 'en',
supportLanguages: ['en', 'zh-hans'],
},
},
};

View File

@ -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.

View File

@ -103,6 +103,21 @@ export class AvatarDropdown extends React.Component {
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() {
if (!this.user) {
return (
@ -136,13 +151,7 @@ export class AvatarDropdown extends React.Component {
</Button>
</Menu.Item>
<Menu.Divider />
<Menu.Item
key="language"
className={`${styles['no-hover']} ${styles['menu-item']}`}
>
<span>{t('Switch Language')}</span>
{this.renderLanguageSwitch()}
</Menu.Item>
{this.renderLanguageMenuItem()}
<Menu.Item key="password" className={styles['menu-item']}>
<ItemActionButtons
actions={{ moreActions: [{ action: Password }] }}

View File

@ -22,6 +22,10 @@ import styles from './index.less';
const { getLocale, setLocale, SUPPORT_LOCALES } = i18n;
const SelectLang = (props) => {
if (SUPPORT_LOCALES.length <= 1) {
return null;
}
const { className } = props;
const selectedLang = getLocale();

View File

@ -14,35 +14,69 @@
import moment from 'moment';
import 'moment/locale/zh-cn';
import _ from 'lodash';
import _, { isArray } from 'lodash';
import cookie from 'utils/cookie';
import SLI18n from 'utils/translate';
import { setLocalStorageItem } from 'utils/local-storage';
import locales from '../locales';
// shortName: the i18n name in the api
// icon: the icon of switch language in ui
const SUPPORT_LOCALES = [
const SUPPORT_LOCALES_ALL = [
{
name: 'English',
value: 'en',
shortName: 'en',
icon: 'en',
momentName: 'en',
},
{
name: '简体中文',
value: 'zh-hans',
shortName: 'zh',
icon: 'zh',
momentName: 'zhCN',
},
{
name: '한글',
value: 'ko-kr',
shortName: '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();
let currentLocals = null;
@ -63,9 +97,11 @@ const getLocale = () => {
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 })) {
currentLocale = 'en';
currentLocale = defaultLanguage;
}
if (!currentLocals) {
@ -93,7 +129,7 @@ const loadLocales = () => {
const setLocale = (lang) => {
setLocaleToStorage(lang);
cookie('lang', lang);
moment.locale(lang);
moment.locale(getMomentName(lang));
window.location.reload();
return lang;
};
@ -104,7 +140,7 @@ const init = () => {
const lang = getLocale();
if (lang === 'zh-hans') {
moment.locale('zh', {
moment.locale('zh-cn', {
relativeTime: {
s: '1秒',
ss: '%d秒',
@ -140,6 +176,7 @@ const t = (key, options) => intl.get(key, options);
t.html = (key, options) => intl.getHTML(key, options);
loadLocales();
init();
window.t = t;
export default {

View File

@ -17,12 +17,13 @@ import ReactDOM from 'react-dom';
import { createBrowserHistory } from 'history';
import { syncHistoryWithStore } from 'mobx-react-router';
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 PageLoading from 'components/PageLoading';
import metricDict from 'resources/prometheus/metricDict';
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 App from './App';
@ -33,8 +34,19 @@ window.globalCSS = variables;
const store = globalRootStore;
const browserHistory = createBrowserHistory();
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) => {
ReactDOM.render(