feat: support Korean i18n

Support Korean i18n

Change-Id: Idf5efce1ad02743f0ba8734009e1bb661038c087
This commit is contained in:
Jingwei.Zhang 2023-03-23 10:23:18 +08:00
parent decd2bfec7
commit 235ee19da6
9 changed files with 3077 additions and 51 deletions

View File

@ -19,7 +19,7 @@ module.exports = function (grunt) {
src: ['src/**/*.{jsx,js}'],
dest: 'src',
options: {
lngs: ['en', 'zh'],
lngs: ['en', 'zh', 'ko-kr'],
removeUnusedKeys: true,
sort: true,
keySeparator: false,
@ -39,10 +39,10 @@ module.exports = function (grunt) {
extensions: ['.js', '.jsx'],
},
defaultValue: (lng, ns, key) => {
if (lng === 'zh') {
return '';
if (lng === 'en') {
return key;
}
return key;
return '';
},
},
},

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

View File

@ -14,7 +14,7 @@
import React from '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 i18n from 'core/i18n';
import ItemActionButtons from 'components/Tables/Base/ItemActionButtons';
@ -24,7 +24,7 @@ import OpenRc from './OpenRc';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
const { getLocale, setLocale } = i18n;
const { getLocale, setLocale, SUPPORT_LOCALES } = i18n;
export class AvatarDropdown extends React.Component {
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() {
if (!this.user) {
return (
@ -71,7 +116,6 @@ export class AvatarDropdown extends React.Component {
);
}
const { name: username } = this.user.user;
const selectedLang = getLocale();
// const { selectedLang } = this.state.selectedLang;
const menuHeaderDropdown = (
<Menu className={styles.menu} onClick={this.onMenuClick}>
@ -97,28 +141,7 @@ export class AvatarDropdown extends React.Component {
className={`${styles['no-hover']} ${styles['menu-item']}`}
>
<span>{t('Switch Language')}</span>
<span style={{ float: 'right' }}>
<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>
{this.renderLanguageSwitch()}
</Menu.Item>
<Menu.Item key="password" className={styles['menu-item']}>
<ItemActionButtons

View File

@ -19,7 +19,7 @@ import classNames from 'classnames';
import { GlobalOutlined } from '@ant-design/icons';
import styles from './index.less';
const { getLocale, setLocale } = i18n;
const { getLocale, setLocale, SUPPORT_LOCALES } = i18n;
const SelectLang = (props) => {
const { className } = props;
@ -29,27 +29,21 @@ const SelectLang = (props) => {
setLocale(key, false);
};
const locales = ['zh-cn', 'en'];
const languageLabels = {
'zh-cn': '简体中文',
en: 'English',
};
const languageIcons = {
'zh-cn': '🇨🇳',
en: '🇺🇸',
};
const locales = SUPPORT_LOCALES.map((it) => it.value);
const languageLabels = SUPPORT_LOCALES.map((it) => it.name);
const languageIcons = SUPPORT_LOCALES.map((it) => it.icon);
const langMenu = (
<Menu
className={styles.menu}
selectedKeys={[selectedLang]}
onClick={changeLang}
>
{locales.map((locale) => (
{locales.map((locale, index) => (
<Menu.Item key={locale}>
<span role="img" aria-label={languageLabels[locale]}>
{languageIcons[locale]}
<span role="img" aria-label={languageLabels[index]}>
{languageIcons[index]}
</span>{' '}
{languageLabels[locale]}
{languageLabels[index]}
</Menu.Item>
))}
</Menu>

View File

@ -24,10 +24,20 @@ const SUPPORT_LOCALES = [
{
name: 'English',
value: 'en',
shortName: 'en',
icon: 'us',
},
{
name: '简体中文',
value: 'zh-cn',
shortName: 'zh',
icon: 'cn',
},
{
name: '한글',
value: 'ko-kr',
shortName: 'kr',
icon: 'kr',
},
];
@ -63,6 +73,12 @@ const getLocale = () => {
return currentLocale;
};
const getLocaleShortName = () => {
const fullName = getLocale();
const item = SUPPORT_LOCALES.find((it) => it.value === fullName);
return item ? item.shortName : fullName;
};
const loadLocales = () => {
const currentLocale = getLocale();
return intl.init({
@ -131,4 +147,6 @@ export default {
init,
t,
isLocaleZh,
getLocaleShortName,
SUPPORT_LOCALES,
};

View File

@ -36,9 +36,10 @@ export class BlankLayout extends Component {
}
get title() {
const { title: { zh = t('Cloud'), en = 'Cloud' } = {} } = this.info;
const { isLocaleZh } = i18n;
return isLocaleZh ? zh : en;
const { title = { zh: t('Cloud'), en: 'Cloud' } } = this.info;
const { getLocaleShortName } = i18n;
const language = getLocaleShortName();
return title[language] || t('Cloud') || 'Cloud';
}
render() {

View File

@ -14,8 +14,10 @@
import zhData from './zh.json';
import enData from './en.json';
import krData from './ko-kr.json';
export default {
'zh-cn': zhData,
en: enData,
'ko-kr': krData,
};

2976
src/locales/ko-kr.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -69,11 +69,13 @@ export class Login extends Component {
}
get productName() {
const {
product_name: { zh = t('Cloud Platform'), en = 'Cloud Platform' } = {},
} = this.info;
const { isLocaleZh } = i18n;
return t('Welcome, {name}', { name: isLocaleZh ? zh : en });
const { product_name = { zh: t('Cloud Platform'), en: 'Cloud Platform' } } =
this.info;
const { getLocaleShortName } = i18n;
const language = getLocaleShortName();
const name =
product_name[language] || t('Cloud Platform') || 'Cloud Platform';
return t('Welcome, {name}', { name });
}
get domains() {