Skip to content

Commit

Permalink
feat: i18n
Browse files Browse the repository at this point in the history
  • Loading branch information
56 committed Jul 22, 2020
1 parent db3e40f commit 473b893
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 41 deletions.
19 changes: 11 additions & 8 deletions dashboard/src/components/Editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { ForwardRefRenderFunction, forwardRef } from 'react';
import { TFunction } from 'i18next';
import { Button, Input, message } from 'antd';
import CodeMirror from 'codemirror';
import { CodeEditorProps } from '@src/components/Editor/Editor';
Expand All @@ -9,15 +10,16 @@ import YamlEditor from './YamlEditor';
import TomlEditor from './TomlEditor';

import styles from './index.module.scss';
import { useTranslation } from 'react-i18next';

window.CodeMirror = CodeMirror;

export { default as JsonEditor } from './JsonEditor';
export { default as YamlEditor } from './YamlEditor';
export { default as TomlEditor } from './TomlEditor';

export const validateFormat = (value: string, format: NamespaceFormat): [boolean, string?] => {
if (!value) return [false, '配置不能为空'];
export const validateFormat = (value: string, format: NamespaceFormat, t: TFunction): [boolean, string?] => {
if (!value) return [false, t('form.creation.configuration.validation')];
if (format === NamespaceFormat.CUSTOM) return [true];
let validate;
if (format === NamespaceFormat.JSON) validate = window.jsonlint.parse;
Expand Down Expand Up @@ -46,6 +48,7 @@ const Editor: ForwardRefRenderFunction<any, EditorProps> = (
{ canControl, format, value, released, onSave, onRelease, ...props },
ref,
) => {
const { t } = useTranslation();
const renderEditor = () => {
if (format === NamespaceFormat.CUSTOM)
return (
Expand Down Expand Up @@ -74,24 +77,24 @@ const Editor: ForwardRefRenderFunction<any, EditorProps> = (
type="primary"
onClick={() => {
const v = value || '';
const [result, msg] = validateFormat(v, format);
const [result, msg] = validateFormat(v, format, t);
if (result) onSave && onSave(v, format);
else message.error(`格式错误: ${msg}`);
else message.error(t('form.creation.format.validation.failure') + `: ${msg}`);
}}
>
保存
{t('form.creation.button.save')}
</Button>
<Button
danger
disabled={released || props.initialValue !== value}
onClick={() => {
const v = value || '';
const [result, msg] = validateFormat(v, format);
const [result, msg] = validateFormat(v, format, t);
if (result) onRelease && onRelease(v, format);
else message.error(`格式错误: ${msg}`);
else message.error(t('form.creation.format.validation.failure') + `: ${msg}`);
}}
>
发布
{t('form.creation.button.release')}
</Button>
</div>
)}
Expand Down
10 changes: 6 additions & 4 deletions dashboard/src/components/ITable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react';
import { TableProps } from 'antd/lib/table';
import { useTranslation } from 'react-i18next';
import { Button, Input, Table } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { TableProps } from 'antd/lib/table';

import { usePropsValue } from '@src/hooks/usePropsValue';

Expand All @@ -22,6 +23,7 @@ export interface ITableProps<T = any> extends TableProps<T> {
}

const ITable = <T extends object = any>({ showCreate, showSearch, ...props }: ITableProps<T>): JSX.Element => {
const { t } = useTranslation();
const [key, setKey] = usePropsValue<string>({ value: showSearch?.value, onChange: showSearch?.onChange });
const [data, setData] = useState<T[]>([]);

Expand All @@ -41,15 +43,15 @@ const ITable = <T extends object = any>({ showCreate, showSearch, ...props }: IT
<div className="clear-float">
{!!showSearch && (
<div style={{ float: 'left', marginBottom: 16, display: 'flex', alignItems: 'center' }}>
<label>关键字过滤: </label>
<label>{t('table.filter')}: </label>
<Input
value={key}
onChange={(e) => setKey(e.target.value)}
style={{ marginLeft: 12, width: 300 }}
placeholder="输入关键字过滤"
placeholder={t('table.filter.placeholder')}
addonAfter={
<span style={{ cursor: 'pointer' }} onClick={() => setKey('')}>
清除
{t('table.filter_clear_button')}
</span>
}
/>
Expand Down
4 changes: 3 additions & 1 deletion dashboard/src/components/Loading/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { Spin } from 'antd';

export interface LoadingProps {}

const Loading: FC<LoadingProps> = () => {
const { t } = useTranslation();
return (
<div style={{ minHeight: 300, lineHeight: 300, width: '100%', textAlign: 'center' }}>
<Spin spinning tip="努力加载中捏. 💪" />
<Spin spinning tip={t('label.loading') + ' 💪'} />
</div>
);
};
Expand Down
92 changes: 91 additions & 1 deletion dashboard/src/locales/en_us.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,97 @@ const locale: Locale = {
langs: ['en', 'en_us', 'en_US'],
languageLabel: 'English',
antLocale: enUS,
value: {},
value: {
'layout.title': '分布式配置中心',
'layout.subTit': 'XConf',
'layout.ownership': 'Micro China open source technology',

'label.released': 'released',
'label.unreleased': 'unreleased',
'label.loading': 'Try to load.',

'menus.apps': 'Applications',
'menus.clusters': 'Clusters',
'menus.github': 'Source',

'empty.desc': 'No description',

'card.app': 'My app',
'card.app.create': 'Create App',
'card.app.title': 'App',
'card.cluster.create': 'Create Cluster',
'card.cluster.title': 'Cluster',
'card.namespace.title': 'Namespace',
'card.namespace.create': 'Create Namespace',

'table.filter': 'Keyword',
'table.filter.placeholder': 'Keyword filtering',
'table.filter_clear_button': 'Clear',
'table.columns.app': 'App',
'table.columns.cluster': 'Cluster',
'table.columns.namespace': 'Namespace',
'table.columns.comment': 'Explain',
'table.columns.desc': 'Description',
'table.columns.status': 'Status',
'table.columns.createdAt': 'CreatedAt',
'table.columns.updatedAt': 'updatedAt',
'table.columns.control': 'Control',
'table.columns.control.view': 'View',
'table.columns.control.history': 'History',
'table.columns.control.import': 'Import',
'table.columns.control.import.success': 'Import configuration success',
'table.columns.control.import.failure': 'Import configuration failure',
'table.columns.control.export': 'Export',
'table.columns.control.rollback': 'Rollback',
'table.columns.control.rollback.confirm': 'Confirm rollback',
'table.columns.control.rollback.success': 'Rollback success',
'table.columns.control.rollback.failure': 'Rollback failure',
'table.columns.control.remove': 'Delete',
'table.columns.control.remove.success': 'Delete success',
'table.columns.control.remove.failure': 'Delete failure',
'table.columns.control.remove.confirm.app': 'Do you want to delete this application?',
'table.columns.control.remove.confirm.cluster': 'Do you want to delete this cluster?',
'table.columns.control.remove.confirm.namespace': 'Do you want to delete this namespace?',

'form.creation.appName': 'App name',
'form.creation.appName.validation': 'App name cannot be empty',
'form.creation.appName.placeholder': 'Please enter the app name',
'form.creation.clusterName': 'Cluster name',
'form.creation.clusterName.validation': 'Cluster name cannot be empty',
'form.creation.clusterName.placeholder': 'Please enter the cluster name',
'form.creation.namespace': 'Namespace name',
'form.creation.namespace.validation': 'Namespace name cannot be empty',
'form.creation.namespace.placeholder': 'Please enter the namespace name',
'form.creation.format': 'Format',
'form.creation.format.validation': 'Format cannot be empty',
'form.creation.format.validation.failure': 'Format error',
'form.creation.status': 'Status',
'form.creation.status.validation': 'Status cannot be empty',
'form.creation.configuration': 'Configuration',
'form.creation.configuration.validation': 'Configuration cannot be empty',
'form.creation.tag': 'tag',
'form.creation.tag.validation': 'Tag is required',
'form.creation.tag.placeholder': 'Please enter the tag',
'form.creation.comment': 'Note',
'form.creation.comment.validation': 'Note cannot be empty',
'form.creation.comment.placeholder': 'Please enter the note',

'form.creation.desc': 'Description',
'form.creation.desc.placeholder': 'Please enter the description',

'form.creation.button.sure': 'Create',
'form.creation.button.cancel': 'Cancel',
'form.creation.button.save': 'Save',
'form.creation.button.release': 'Release',
'form.creation.app.success': 'Create application success',
'form.creation.app.failure': 'Create application failure',
'form.creation.cluster.success': 'Create cluster success',
'form.creation.cluster.failure': 'Create cluster failure',
'form.creation.namespace.failure': 'Create namespace failure',
'form.creation.namespace.save.success': 'Save configuration success',
'form.creation.namespace.save.failure': 'Save configuration failure',
'form.creation.namespace.release.success': 'Release configuration success',
},
};

export default locale;
27 changes: 22 additions & 5 deletions dashboard/src/locales/zh_cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const locale: Locale = {
'layout.subTit': '分布式配置中心',
'layout.ownership': 'Micro China开源技术出品',

'label.released': '已发布',
'label.unreleased': '未发布',
'label.loading': '努力加载中捏.',

'menus.apps': '应用列表',
'menus.clusters': '集群列表',
'menus.github': '项目地址',
Expand All @@ -26,6 +30,7 @@ const locale: Locale = {
'card.namespace.create': '创建新配置',

'table.filter': '关键字过滤',
'table.filter.placeholder': '输入关键字过滤',
'table.filter_clear_button': '清除',
'table.columns.app': '应用',
'table.columns.cluster': '集群',
Expand All @@ -50,35 +55,47 @@ const locale: Locale = {
'table.columns.control.remove.success': '删除成功',
'table.columns.control.remove.failure': '删除失败',
'table.columns.control.remove.confirm.app': '确认删除该应用?',
'table.columns.control.remove.confirm.cluster': '确认删除该应用?',
'table.columns.control.remove.confirm.cluster': '确认删除该集群?',
'table.columns.control.remove.confirm.namespace': '确认删除该空间?',

'form.creation.appName': '应用名',
'form.creation.appName.validation': '应用名不能为空',
'form.creation.appName.placeholder': '请输入应用名',

'form.creation.clusterName': '集群名',
'form.creation.clusterName.validation': '集群名不能为空',
'form.creation.clusterName.placeholder': '请输入集群名',

'form.creation.namespace': '配置名',
'form.creation.namespace.validation': '配置名不能为空',
'form.creation.namespace.placeholder': '请输入配置名',

'form.creation.format': '格式',
'form.creation.format.validation': '格式不能为空',
'form.creation.format.validation.failure': '格式错误',
'form.creation.status': '状态',
'form.creation.status.validation': '状态不能为空',
'form.creation.configuration': '配置',
'form.creation.configuration.validation': '配置不能为空',
'form.creation.tag': 'tag',
'form.creation.tag.validation': '必须填写tag',
'form.creation.tag.placeholder': '请输入Tag',
'form.creation.comment': '备注',
'form.creation.comment.validation': '发布备注',
'form.creation.comment.placeholder': '请输入发布备注',

'form.creation.desc': '描述',
'form.creation.desc.placeholder': '请输入描述内容',

'form.creation.button.sure': '创建',
'form.creation.button.cancel': '取消',
'form.creation.button.save': '保存',
'form.creation.button.release': '发布',
'form.creation.app.success': '创建应用成功',
'form.creation.app.failure': '创建应用失败',
'form.creation.cluster.success': '创建集群成功',
'form.creation.cluster.failure': '创建集群失败',
'form.creation.namespace.success': '创建配置成功',
'form.creation.namespace.failure': '创建配置失败',
'form.creation.namespace.save.success': '配置保存成功',
'form.creation.namespace.save.failure': '配置保存失败',
'form.creation.namespace.release.success': '配置发布成功',
},
};

Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/pages/App/Apps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const Apps: FC<AppsProps> = () => {
{
title: t('table.columns.control'),
key: 'control',
width: 120,
width: 130,
render: (_, app) => (
<div>
<Link to={`/apps/${app.appName}`}>{t('table.columns.control.view')}</Link>
Expand Down
12 changes: 7 additions & 5 deletions dashboard/src/pages/App/Cluster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const getFunction = (namespace: Namespace, t: TFunction, callback: () => void) =

const getContent = (file: File) => {
readFile(file, (value) => {
const [res, msg] = validateFormat(value, namespace.format);
const [res, msg] = validateFormat(value, namespace.format, t);
if (res) uploadConfig(value);
else message.error(t('table.columns.control.import.failure') + `: ${msg}`);
});
Expand Down Expand Up @@ -80,17 +80,15 @@ const Cluster: FC<ClusterProps> = ({ appName, clusterName }) => {
title: t('table.columns.status'),
key: 'release',
width: 100,
render: (_, namespace) => renderNamespaceRelease(namespace),
render: (_, namespace) => renderNamespaceRelease(namespace, t),
},
{
title: t('table.columns.control'),
key: 'control',
width: 230,
width: 256,
render: (_, namespace) => {
return (
<div>
<Link to={`/apps/${appName}/${clusterName}/${namespace.namespaceName}/histories`}>历史版本</Link>
<Divider type="vertical" />
<button
className="link-button"
onClick={getFunction(namespace, t, () => getNamespaces((state) => ({ ...state })))}
Expand All @@ -105,6 +103,10 @@ const Cluster: FC<ClusterProps> = ({ appName, clusterName }) => {
{t('table.columns.control.export')}
</button>
<Divider type="vertical" />
<Link to={`/apps/${appName}/${clusterName}/${namespace.namespaceName}/histories`}>
{t('table.columns.control.history')}
</Link>
<Divider type="vertical" />
{renderDeleteWithLinkButton({
label: t('table.columns.control.remove'),
popLabel: t('table.columns.control.remove.confirm.namespace'),
Expand Down
22 changes: 15 additions & 7 deletions dashboard/src/pages/App/NamespaceInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { FC, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Col, Form, Row, Tag, message } from 'antd';

import Editor from '@src/components/Editor';
Expand All @@ -17,21 +18,28 @@ const NamespaceInfo: FC<NamespaceInfoProps> = ({ namespace, canControl, callback
const callbackRef = useRef(callback);
callbackRef.current = callback;

const { t } = useTranslation();

return (
<Form>
<Row>
<Col span={6}>
<Form.Item label="格式" labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}>
<Form.Item label={t('form.creation.format')} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}>
<Tag color="green">{namespace.format}</Tag>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item label="状态" labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}>
{renderNamespaceRelease(namespace)}
<Form.Item label={t('form.creation.status')} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}>
{renderNamespaceRelease(namespace, t)}
</Form.Item>
</Col>
</Row>
<Form.Item name="configuration" label="配置" labelCol={{ span: 2 }} wrapperCol={{ span: 22 }}>
<Form.Item
name="configuration"
label={t('form.creation.configuration')}
labelCol={{ span: 2 }}
wrapperCol={{ span: 22 }}
>
<Editor
canControl={canControl}
format={namespace.format}
Expand All @@ -48,17 +56,17 @@ const NamespaceInfo: FC<NamespaceInfoProps> = ({ namespace, canControl, callback
.then(() => {
const callback = callbackRef.current;
callback && callback();
message.success('配置保存成功');
message.success(t('form.creation.namespace.save.success'));
})
.catch((e) => message.error('保存失败:', e.message));
.catch((e) => message.error(t('form.creation.namespace.save.failure') + ': ' + e.message));
}}
onRelease={() => {
ReleaseModel({
appName: namespace.appName,
clusterName: namespace.clusterName,
namespaceName: namespace.namespaceName,
onOk: () => {
message.success('配置发布成功');
message.success(t('form.creation.namespace.release.success'));
const callback = callbackRef.current;
callback && callback();
},
Expand Down
Loading

0 comments on commit 473b893

Please sign in to comment.