feat: support Korean i18n
Support Korean i18n Change-Id: Idf5efce1ad02743f0ba8734009e1bb661038c087
This commit is contained in:
parent
decd2bfec7
commit
235ee19da6
@ -19,7 +19,7 @@ module.exports = function (grunt) {
|
|||||||
src: ['src/**/*.{jsx,js}'],
|
src: ['src/**/*.{jsx,js}'],
|
||||||
dest: 'src',
|
dest: 'src',
|
||||||
options: {
|
options: {
|
||||||
lngs: ['en', 'zh'],
|
lngs: ['en', 'zh', 'ko-kr'],
|
||||||
removeUnusedKeys: true,
|
removeUnusedKeys: true,
|
||||||
sort: true,
|
sort: true,
|
||||||
keySeparator: false,
|
keySeparator: false,
|
||||||
@ -39,10 +39,10 @@ module.exports = function (grunt) {
|
|||||||
extensions: ['.js', '.jsx'],
|
extensions: ['.js', '.jsx'],
|
||||||
},
|
},
|
||||||
defaultValue: (lng, ns, key) => {
|
defaultValue: (lng, ns, key) => {
|
||||||
if (lng === 'zh') {
|
if (lng === 'en') {
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return key;
|
return key;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
10
releasenotes/notes/Support-Korean-I18n-6f258836f7b30db9.yaml
Normal file
10
releasenotes/notes/Support-Korean-I18n-6f258836f7b30db9.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support Korean I18n:
|
||||||
|
|
||||||
|
* Korean language switching is supported on the login page.
|
||||||
|
|
||||||
|
* Support switch Korean in the avatar hover box on the upper right of the page after logged in.
|
||||||
|
|
||||||
|
* Support automatic collection of Korean i18n file.
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { inject, observer } from 'mobx-react';
|
import { inject, observer } from 'mobx-react';
|
||||||
import { Menu, Spin, Button } from 'antd';
|
import { Menu, Spin, Button, Select } from 'antd';
|
||||||
import { UserOutlined } from '@ant-design/icons';
|
import { UserOutlined } from '@ant-design/icons';
|
||||||
import i18n from 'core/i18n';
|
import i18n from 'core/i18n';
|
||||||
import ItemActionButtons from 'components/Tables/Base/ItemActionButtons';
|
import ItemActionButtons from 'components/Tables/Base/ItemActionButtons';
|
||||||
@ -24,7 +24,7 @@ import OpenRc from './OpenRc';
|
|||||||
import HeaderDropdown from '../HeaderDropdown';
|
import HeaderDropdown from '../HeaderDropdown';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { getLocale, setLocale } = i18n;
|
const { getLocale, setLocale, SUPPORT_LOCALES } = i18n;
|
||||||
|
|
||||||
export class AvatarDropdown extends React.Component {
|
export class AvatarDropdown extends React.Component {
|
||||||
get rootStore() {
|
get rootStore() {
|
||||||
@ -58,6 +58,51 @@ export class AvatarDropdown extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onClickSelectLanguage = (e) => {
|
||||||
|
e && e.preventDefault();
|
||||||
|
e && e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
renderLanguageSwitch() {
|
||||||
|
const selectedLang = getLocale();
|
||||||
|
const { length } = SUPPORT_LOCALES;
|
||||||
|
if (length > 3) {
|
||||||
|
const options = SUPPORT_LOCALES.map((it) => ({
|
||||||
|
label: it.shortName.toLocaleUpperCase(),
|
||||||
|
value: it.value,
|
||||||
|
}));
|
||||||
|
return (
|
||||||
|
<div style={{ float: 'right' }}>
|
||||||
|
<Select
|
||||||
|
options={options}
|
||||||
|
value={selectedLang}
|
||||||
|
onChange={this.changeLang}
|
||||||
|
onClick={this.onClickSelectLanguage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const btns = SUPPORT_LOCALES.map((item, index) => {
|
||||||
|
const { value, shortName } = item;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
className={index === 0 ? styles['no-padding-top'] : ''}
|
||||||
|
type="link"
|
||||||
|
disabled={selectedLang === value}
|
||||||
|
onClick={() => {
|
||||||
|
this.changeLang(value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{shortName.toUpperCase()}
|
||||||
|
</Button>
|
||||||
|
{index !== length - 1 && <span>/</span>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return <span style={{ float: 'right' }}>{btns}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.user) {
|
if (!this.user) {
|
||||||
return (
|
return (
|
||||||
@ -71,7 +116,6 @@ export class AvatarDropdown extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { name: username } = this.user.user;
|
const { name: username } = this.user.user;
|
||||||
const selectedLang = getLocale();
|
|
||||||
// const { selectedLang } = this.state.selectedLang;
|
// const { selectedLang } = this.state.selectedLang;
|
||||||
const menuHeaderDropdown = (
|
const menuHeaderDropdown = (
|
||||||
<Menu className={styles.menu} onClick={this.onMenuClick}>
|
<Menu className={styles.menu} onClick={this.onMenuClick}>
|
||||||
@ -97,28 +141,7 @@ export class AvatarDropdown extends React.Component {
|
|||||||
className={`${styles['no-hover']} ${styles['menu-item']}`}
|
className={`${styles['no-hover']} ${styles['menu-item']}`}
|
||||||
>
|
>
|
||||||
<span>{t('Switch Language')}</span>
|
<span>{t('Switch Language')}</span>
|
||||||
<span style={{ float: 'right' }}>
|
{this.renderLanguageSwitch()}
|
||||||
<Button
|
|
||||||
className={styles['no-padding-top']}
|
|
||||||
type="link"
|
|
||||||
disabled={selectedLang === 'zh-cn'}
|
|
||||||
onClick={() => {
|
|
||||||
this.changeLang('zh-cn');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
CN
|
|
||||||
</Button>
|
|
||||||
<span>/</span>
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
disabled={selectedLang === 'en'}
|
|
||||||
onClick={() => {
|
|
||||||
this.changeLang('en');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
EN
|
|
||||||
</Button>
|
|
||||||
</span>
|
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="password" className={styles['menu-item']}>
|
<Menu.Item key="password" className={styles['menu-item']}>
|
||||||
<ItemActionButtons
|
<ItemActionButtons
|
||||||
|
@ -19,7 +19,7 @@ import classNames from 'classnames';
|
|||||||
import { GlobalOutlined } from '@ant-design/icons';
|
import { GlobalOutlined } from '@ant-design/icons';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { getLocale, setLocale } = i18n;
|
const { getLocale, setLocale, SUPPORT_LOCALES } = i18n;
|
||||||
|
|
||||||
const SelectLang = (props) => {
|
const SelectLang = (props) => {
|
||||||
const { className } = props;
|
const { className } = props;
|
||||||
@ -29,27 +29,21 @@ const SelectLang = (props) => {
|
|||||||
setLocale(key, false);
|
setLocale(key, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const locales = ['zh-cn', 'en'];
|
const locales = SUPPORT_LOCALES.map((it) => it.value);
|
||||||
const languageLabels = {
|
const languageLabels = SUPPORT_LOCALES.map((it) => it.name);
|
||||||
'zh-cn': '简体中文',
|
const languageIcons = SUPPORT_LOCALES.map((it) => it.icon);
|
||||||
en: 'English',
|
|
||||||
};
|
|
||||||
const languageIcons = {
|
|
||||||
'zh-cn': '🇨🇳',
|
|
||||||
en: '🇺🇸',
|
|
||||||
};
|
|
||||||
const langMenu = (
|
const langMenu = (
|
||||||
<Menu
|
<Menu
|
||||||
className={styles.menu}
|
className={styles.menu}
|
||||||
selectedKeys={[selectedLang]}
|
selectedKeys={[selectedLang]}
|
||||||
onClick={changeLang}
|
onClick={changeLang}
|
||||||
>
|
>
|
||||||
{locales.map((locale) => (
|
{locales.map((locale, index) => (
|
||||||
<Menu.Item key={locale}>
|
<Menu.Item key={locale}>
|
||||||
<span role="img" aria-label={languageLabels[locale]}>
|
<span role="img" aria-label={languageLabels[index]}>
|
||||||
{languageIcons[locale]}
|
{languageIcons[index]}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
{languageLabels[locale]}
|
{languageLabels[index]}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
@ -24,10 +24,20 @@ const SUPPORT_LOCALES = [
|
|||||||
{
|
{
|
||||||
name: 'English',
|
name: 'English',
|
||||||
value: 'en',
|
value: 'en',
|
||||||
|
shortName: 'en',
|
||||||
|
icon: 'us',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '简体中文',
|
name: '简体中文',
|
||||||
value: 'zh-cn',
|
value: 'zh-cn',
|
||||||
|
shortName: 'zh',
|
||||||
|
icon: 'cn',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '한글',
|
||||||
|
value: 'ko-kr',
|
||||||
|
shortName: 'kr',
|
||||||
|
icon: 'kr',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -63,6 +73,12 @@ const getLocale = () => {
|
|||||||
return currentLocale;
|
return currentLocale;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getLocaleShortName = () => {
|
||||||
|
const fullName = getLocale();
|
||||||
|
const item = SUPPORT_LOCALES.find((it) => it.value === fullName);
|
||||||
|
return item ? item.shortName : fullName;
|
||||||
|
};
|
||||||
|
|
||||||
const loadLocales = () => {
|
const loadLocales = () => {
|
||||||
const currentLocale = getLocale();
|
const currentLocale = getLocale();
|
||||||
return intl.init({
|
return intl.init({
|
||||||
@ -131,4 +147,6 @@ export default {
|
|||||||
init,
|
init,
|
||||||
t,
|
t,
|
||||||
isLocaleZh,
|
isLocaleZh,
|
||||||
|
getLocaleShortName,
|
||||||
|
SUPPORT_LOCALES,
|
||||||
};
|
};
|
||||||
|
@ -36,9 +36,10 @@ export class BlankLayout extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
const { title: { zh = t('Cloud'), en = 'Cloud' } = {} } = this.info;
|
const { title = { zh: t('Cloud'), en: 'Cloud' } } = this.info;
|
||||||
const { isLocaleZh } = i18n;
|
const { getLocaleShortName } = i18n;
|
||||||
return isLocaleZh ? zh : en;
|
const language = getLocaleShortName();
|
||||||
|
return title[language] || t('Cloud') || 'Cloud';
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -14,8 +14,10 @@
|
|||||||
|
|
||||||
import zhData from './zh.json';
|
import zhData from './zh.json';
|
||||||
import enData from './en.json';
|
import enData from './en.json';
|
||||||
|
import krData from './ko-kr.json';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'zh-cn': zhData,
|
'zh-cn': zhData,
|
||||||
en: enData,
|
en: enData,
|
||||||
|
'ko-kr': krData,
|
||||||
};
|
};
|
||||||
|
2976
src/locales/ko-kr.json
Normal file
2976
src/locales/ko-kr.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -69,11 +69,13 @@ export class Login extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get productName() {
|
get productName() {
|
||||||
const {
|
const { product_name = { zh: t('Cloud Platform'), en: 'Cloud Platform' } } =
|
||||||
product_name: { zh = t('Cloud Platform'), en = 'Cloud Platform' } = {},
|
this.info;
|
||||||
} = this.info;
|
const { getLocaleShortName } = i18n;
|
||||||
const { isLocaleZh } = i18n;
|
const language = getLocaleShortName();
|
||||||
return t('Welcome, {name}', { name: isLocaleZh ? zh : en });
|
const name =
|
||||||
|
product_name[language] || t('Cloud Platform') || 'Cloud Platform';
|
||||||
|
return t('Welcome, {name}', { name });
|
||||||
}
|
}
|
||||||
|
|
||||||
get domains() {
|
get domains() {
|
||||||
|
Loading…
Reference in New Issue
Block a user