diff --git a/.yarnrc b/.yarnrc index cbdd124a4..57f8c2eb2 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,2 @@ registry "https://registry.yarnpkg.com" -sass_binary_site "https://npm.taobao.org/mirrors/node-sass/" diff --git a/jest.config.js b/jest.config.js index 0391a1d18..ef0f4228d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,7 +5,7 @@ module.exports = { transformIgnorePatterns: ['/node_modules/', 'lib', 'dist'], testPathIgnorePatterns: ['/node_modules/'], transform: { - '^.+\\.[jt]s?(x)$': 'babel-jest', + '^.+\\.(ts|tsx|js|jsx)$': 'babel-jest', }, testMatch: [ '**/__tests__/**/(*.)+(spec|test).[jt]s?(x)', diff --git a/src/components/blockHeader/index.tsx b/src/components/blockHeader/index.tsx index d23fac43b..ac41e5fda 100644 --- a/src/components/blockHeader/index.tsx +++ b/src/components/blockHeader/index.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { QuestionCircleOutlined, UpOutlined } from '@ant-design/icons'; import { Tooltip } from 'antd'; +import useLocale from '../locale/useLocale'; export interface BlockHeaderProps { // 标题 @@ -60,6 +61,9 @@ const BlockHeader: React.FC = function (props) { let bottomStyle; if (hasBottom) bottomStyle = { marginBottom: 16 }; if (spaceBottom) bottomStyle = { marginBottom: spaceBottom }; + + const locale = useLocale('BlockHeader'); + const [expand, setExpand] = useState(defaultExpand); const handleExpand = (expand) => { @@ -87,7 +91,7 @@ const BlockHeader: React.FC = function (props) { {addonAfter &&
{addonAfter}
} {children && (
-
{expand ? '收起' : '展开'}
+
{expand ? locale.collapse : locale.expand}
)} diff --git a/src/components/chromeDownload/index.tsx b/src/components/chromeDownload/index.tsx index 66e23dc28..015236e9a 100644 --- a/src/components/chromeDownload/index.tsx +++ b/src/components/chromeDownload/index.tsx @@ -2,14 +2,17 @@ import React from 'react'; import classNames from 'classnames'; import utils from '../utils'; +import useLocale, { Locale } from '../locale/useLocale'; + const prefixCls = 'dtc-chrome'; interface ChromeDownloadProps { downloadChrome?: (chromeOfOsType?: 'macChrome' | 'windowsChrome' | 'others') => void; className?: string; style?: React.CSSProperties; + locale?: Locale['ChromeDownload']; } -export default class ChromeDownload extends React.Component { +class ChromeDownload extends React.Component { constructor(props: ChromeDownloadProps) { super(props); } @@ -29,6 +32,7 @@ export default class ChromeDownload extends React.Component -
- 本产品当前可兼容Chrome66以及以上版本,请您更换至最新的谷歌(Chrome)浏览器 -
-
- {this.renderDivide('点击即可下载,更新即刻搞定')} -
+
{locale.description}
+
{this.renderDivide(locale.download)}
) => { + const locale = useLocale('ChromeDownload'); + return ; +}; + +export default ChromeDownloadWrapper; diff --git a/src/components/configProvider/index.tsx b/src/components/configProvider/index.tsx new file mode 100644 index 000000000..649f2f513 --- /dev/null +++ b/src/components/configProvider/index.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { LocaleContext } from '../locale/useLocale'; + +const ConfigProvider = ({ locale, children }) => { + return {children}; +}; + +export default ConfigProvider; diff --git a/src/components/copyIcon/index.tsx b/src/components/copyIcon/index.tsx index a86a5a1ef..6fc624fb9 100644 --- a/src/components/copyIcon/index.tsx +++ b/src/components/copyIcon/index.tsx @@ -1,15 +1,17 @@ import * as React from 'react'; import { Tooltip, message } from 'antd'; import { CopyOutlined } from '@ant-design/icons'; +import useLocale, { Locale } from '../locale/useLocale'; export interface CopyIconProps { text: string; style?: React.CSSProperties; title?: string; customRender?: React.ReactNode; + locale?: Locale['CopyIcon']; } -export default class CopyIcon extends React.Component { +class CopyIcon extends React.Component { fakeHandlerCallback: () => void; fakeHandler: EventListener | void; fakeElem: HTMLTextAreaElement; @@ -82,15 +84,16 @@ export default class CopyIcon extends React.Component { } handleResult(succeeded: any) { + const { locale } = this.props; if (succeeded) { - message.success('复制成功'); + message.success(locale.copied); } else { - message.error('不支持'); + message.error(locale.notSupport); } } render() { - let { customRender, text, style, title, ...rest } = this.props; + let { customRender, text, style, title, locale, ...rest } = this.props; style = { cursor: 'pointer', @@ -101,7 +104,7 @@ export default class CopyIcon extends React.Component { return customRender ? ( {customRender} ) : ( - + { ); } } + +const CopyIconWrapper = (props: Omit) => { + const locale = useLocale('CopyIcon'); + return ; +}; + +export default CopyIconWrapper; diff --git a/src/components/editCell/index.tsx b/src/components/editCell/index.tsx index 2ff2f8687..e2b7ffd27 100644 --- a/src/components/editCell/index.tsx +++ b/src/components/editCell/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Input } from 'antd'; import EllipsisText from '../ellipsisText'; import classNames from 'classnames'; +import useLocale, { Locale } from '../locale/useLocale'; type EditType = string | number; export interface EditCellProps { @@ -9,6 +10,7 @@ export interface EditCellProps { value: string; keyField: string; isView?: boolean; + locale?: Locale['EditCell']; onHandleEdit: (keyField: string, editValue: EditType) => void; } export interface EditCellStates { @@ -16,7 +18,7 @@ export interface EditCellStates { editValue: EditType; } -export default class EditCell extends React.PureComponent { +class EditCell extends React.PureComponent { state: EditCellStates = { isEdit: false, editValue: '', @@ -47,7 +49,7 @@ export default class EditCell extends React.PureComponent {isEdit ? ( @@ -57,16 +59,23 @@ export default class EditCell extends React.PureComponent - 完成 - 取消 + {locale.complete} + {locale.cancel}
) : ( <> - {!isView && 修改} + {!isView && {locale.modify}} )} ); } } + +const EditCellWrapper = (props: Omit) => { + const locale = useLocale('EditCell'); + return ; +}; + +export default EditCellWrapper; diff --git a/src/components/editInput/__tests__/editInput.test.tsx b/src/components/editInput/__tests__/editInput.test.tsx index 65178ef9f..42eaa6cd1 100644 --- a/src/components/editInput/__tests__/editInput.test.tsx +++ b/src/components/editInput/__tests__/editInput.test.tsx @@ -33,7 +33,7 @@ describe('test edit input', () => { }); test('should render message when length more then max', () => { fireEvent.change(element, { target: { value: '12345678910' } }); - expect(wrapper.getByText('字符长度不可超过10')).toBeInTheDocument(); + expect(wrapper.getByText('字符长度不可超过${max}')).toBeInTheDocument(); expect(element.value).toEqual('1234567891'); }); diff --git a/src/components/editInput/index.tsx b/src/components/editInput/index.tsx index 4c1384103..58afb3afc 100644 --- a/src/components/editInput/index.tsx +++ b/src/components/editInput/index.tsx @@ -1,16 +1,18 @@ import React from 'react'; import { Input, message } from 'antd'; +import useLocale, { Locale } from '../locale/useLocale'; export interface EditInputProps { value?: string | number; onChange?: (e) => void; max?: number; + locale?: Locale['EditInput']; [propName: string]: any; } export interface EditInputPropsStates { value: string | number; } -export default class EditInput extends React.PureComponent { +class EditInput extends React.PureComponent { constructor(props: EditInputProps) { super(props); this.state = { @@ -34,10 +36,11 @@ export default class EditInput extends React.PureComponent) => { + const { locale } = this.props; const value = e.target.value; const { max = 64 } = this.props; if (value && max && value.length > max) { - message.warning(`字符长度不可超过${max}`); + message.warning(locale.description); this.setState({ value: value.substring(0, max), }); @@ -61,3 +64,10 @@ export default class EditInput extends React.PureComponent) => { + const locale = useLocale('EditInput'); + return ; +}; + +export default EditInputWrapper; diff --git a/src/components/fullscreen/index.tsx b/src/components/fullscreen/index.tsx index 68d52fd2a..4e6a2a34d 100644 --- a/src/components/fullscreen/index.tsx +++ b/src/components/fullscreen/index.tsx @@ -3,6 +3,7 @@ import { Button } from 'antd'; import MyIcon from './icon'; import KeyEventListener from '../keyEventListener'; +import useLocale, { Locale } from '../locale/useLocale'; const { KeyCombiner } = KeyEventListener; declare let document: any; @@ -14,12 +15,13 @@ export interface FullscreenProps { iconStyle?: object; fullIcon?: React.ReactNode; exitFullIcon?: React.ReactNode; + locale?: Locale['Fullscreen']; [propName: string]: any; } export interface FullscreenState { isFullScreen: boolean; } -export default class Fullscreen extends React.Component { +class Fullscreen extends React.Component { state: FullscreenState = { isFullScreen: false, }; @@ -136,8 +138,8 @@ export default class Fullscreen extends React.Component) => { + const locale = useLocale('Fullscreen'); + return ; +}; + +export default FullscreenWrapper; diff --git a/src/components/globalLoading/index.tsx b/src/components/globalLoading/index.tsx index e18faa65c..eb5d085a1 100644 --- a/src/components/globalLoading/index.tsx +++ b/src/components/globalLoading/index.tsx @@ -1,5 +1,6 @@ import classNames from 'classnames'; import React from 'react'; +import useLocale, { Locale } from '../locale/useLocale'; export interface GlobalLoadingProps { className?: string; @@ -8,19 +9,23 @@ export interface GlobalLoadingProps { mainBackground?: string; circleBackground?: string; titleColor?: string; + locale?: Locale['GlobalLoading']; } -export default class GlobalLoading extends React.Component { +class GlobalLoading extends React.Component { render() { const { prefix = '', - loadingTitle = '应用加载中,请等候~', + loadingTitle, mainBackground = '#F2F7FA', circleBackground = '#1D78FF', titleColor = '#3D446E', className = '', + locale, } = this.props; + const newLoadingTitle = loadingTitle || locale.loading; + return (
{`${prefix} ${loadingTitle}`}
+ >{`${prefix} ${newLoadingTitle}`}
@@ -48,3 +53,10 @@ export default class GlobalLoading extends React.Component) => { + const locale = useLocale('GlobalLoading'); + return ; +}; + +export default GlobalLoadingWrapper; diff --git a/src/components/goBack/goBackButton.tsx b/src/components/goBack/goBackButton.tsx index 7b4dc93e4..09017df5d 100644 --- a/src/components/goBack/goBackButton.tsx +++ b/src/components/goBack/goBackButton.tsx @@ -2,8 +2,9 @@ import * as React from 'react'; import { Button } from 'antd'; import { browserHistory, hashHistory } from 'react-router'; import { GoBackButtonProps } from './index'; +import useLocale from '../locale/useLocale'; -export default class GoBackButton extends React.Component { +class GoBackButton extends React.Component { go = () => { const { url, history, autoClose } = this.props; @@ -25,11 +26,18 @@ export default class GoBackButton extends React.Component - {title || '返回'} + {title || locale.back} ); } } + +const GoBackButtonWrapper = (props: Omit) => { + const locale = useLocale('GoBack'); + return ; +}; + +export default GoBackButtonWrapper; diff --git a/src/components/goBack/index.tsx b/src/components/goBack/index.tsx index 3faf3f58c..add1228dc 100644 --- a/src/components/goBack/index.tsx +++ b/src/components/goBack/index.tsx @@ -1,3 +1,4 @@ +import { Locale } from '../locale/useLocale'; import GoBack from './goBack'; import GoBackButton from './goBackButton'; @@ -9,6 +10,7 @@ export interface GoBackProps { } export interface GoBackButtonProps extends GoBackProps { title?: string; + locale?: Locale['GoBack']; } GoBack.GoBackButton = GoBackButton; diff --git a/src/components/index.tsx b/src/components/index.tsx index 6c2d6d68b..4c53105a1 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -34,3 +34,6 @@ export { default as TextMark } from './textMark'; export { default as ToolModal } from './toolModal'; export { default as ProgressLine } from './progressLine'; export { default as Empty } from './empty'; +export { default as ConfigProvider } from './configProvider'; +export { default as zhCN } from './locale/zh-CN'; +export { default as enUS } from './locale/en-US'; diff --git a/src/components/loadError/index.tsx b/src/components/loadError/index.tsx index 4768004c6..fd43bf13f 100644 --- a/src/components/loadError/index.tsx +++ b/src/components/loadError/index.tsx @@ -1,22 +1,23 @@ import React from 'react'; +import useLocale from '../locale/useLocale'; const LoadError: React.FC = function () { + const locale = useLocale('LoadError'); return (
- 

- 发现新版本,请 + {locale.please} { location.reload(); }} > - 刷新 + {locale.refresh} - 获取新版本。 + {locale.get}

-

若该提示长时间存在,请联系管理员。

+

{locale.title}

); diff --git a/src/components/locale/en-US.ts b/src/components/locale/en-US.ts new file mode 100644 index 000000000..8efc76687 --- /dev/null +++ b/src/components/locale/en-US.ts @@ -0,0 +1,82 @@ +import { Locale } from './useLocale'; + +const localeValues: Locale = { + locale: 'zh-CN', + BlockHeader: { + expand: 'Expand', + collapse: 'Collapse', + }, + ChromeDownload: { + description: + 'This product is currently compatible with Chrome 66 and above. Please switch to the latest Google (Chrome) browser.', + download: 'Click to download, update instantly.', + }, + CopyIcon: { + copied: 'Copied', + notSupport: 'Not Support', + copy: 'Copy', + }, + EditCell: { + complete: 'Complete', + cancel: 'Cancel', + modify: 'Modify', + }, + EditInput: { + description: 'The character length cannot exceed ${max}', + }, + Fullscreen: { + exitFull: 'Exit Full Screen', + full: 'Full Screen', + }, + GlobalLoading: { + loading: 'The application is loading, please wait~', + }, + GoBack: { + back: 'GoBack', + }, + LoadError: { + please: 'A new version has been found. Please', + get: 'to get the new version.', + refresh: ' refresh ', + title: 'If this prompt persists for a long time, please contact the administrator.', + }, + ModalWithForm: { + okText: 'Ok', + cancelText: 'Cancel', + }, + MulSelectDropdown: { + open: 'Open', + cancelText: 'Cancel', + okText: 'Ok', + selectAll: 'Select All', + }, + MultiSearchInput: { + case: 'Case-sensitive match', + precise: 'Exact match', + front: 'Head match', + tail: 'Tail match', + }, + MxGraph: { + newNode: 'New node', + }, + NotFound: { + description: 'Dear, did you go to the wrong place?', + }, + ProgressLine: { + description: 'No description yet', + }, + RenderFormItem: { + description: '${label} is empty', + }, + SearchModal: { + title: 'Search and open', + placeholder: 'Please enter', + }, + SpreadSheet: { + description: 'No Data', + copy: 'Copy', + copyAll: 'Copy values ​​and column names', + }, +}; + +export default localeValues; diff --git a/src/components/locale/useLocale.tsx b/src/components/locale/useLocale.tsx new file mode 100644 index 000000000..b5efa56df --- /dev/null +++ b/src/components/locale/useLocale.tsx @@ -0,0 +1,87 @@ +import { createContext, useContext, useMemo } from 'react'; + +import defaultLocaleData from './zh-CN'; + +export interface Locale { + locale: string; + BlockHeader?: { expand: string; collapse: string }; + ChromeDownload?: { description: string; download: string }; + CopyIcon?: { copied: string; notSupport: string; copy: string }; + EditCell?: { complete: string; cancel: string; modify: string }; + EditInput?: { description: string }; + Fullscreen?: { exitFull: string; full: string }; + GlobalLoading?: { + loading: string; + }; + GoBack?: { + back: string; + }; + LoadError?: { + please: string; + get: string; + refresh: string; + title: string; + }; + ModalWithForm?: { + okText: string; + cancelText: string; + }; + MulSelectDropdown?: { + open: string; + cancelText: string; + okText: string; + selectAll: string; + }; + MultiSearchInput?: { + case: string; + precise: string; + front: string; + tail: string; + }; + MxGraph?: { newNode: string }; + NotFound?: { + description: string; + }; + ProgressLine?: { + description: string; + }; + RenderFormItem?: { + description: string; + }; + SearchModal?: { + title: string; + placeholder: string; + }; + SpreadSheet?: { + description: string; + copy: string; + copyAll: string; + }; +} + +export interface LocaleContextProps { + locale: Locale; +} + +export const LocaleContext = createContext(undefined); + +export type LocaleComponentName = keyof Locale; + +const useLocale = ( + componentName: C +): NonNullable => { + const fullLocale = useContext(LocaleContext); + + const getLocale = useMemo(() => { + const locale = defaultLocaleData[componentName] ?? {}; + const localeFromContext = fullLocale?.[componentName as keyof LocaleContextProps] ?? {}; + return { + ...locale, + ...localeFromContext, + } as NonNullable; + }, [componentName, fullLocale]); + + return getLocale; +}; + +export default useLocale; diff --git a/src/components/locale/zh-CN.ts b/src/components/locale/zh-CN.ts new file mode 100644 index 000000000..5967d76ec --- /dev/null +++ b/src/components/locale/zh-CN.ts @@ -0,0 +1,81 @@ +import { Locale } from './useLocale'; + +const localeValues: Locale = { + locale: 'zh-CN', + BlockHeader: { + expand: '展开', + collapse: '收起', + }, + ChromeDownload: { + description: '本产品当前可兼容Chrome66以及以上版本,请您更换至最新的谷歌(Chrome)浏览器', + download: '点击即可下载,更新即刻搞定', + }, + CopyIcon: { + copied: '复制成功', + notSupport: '不支持', + copy: '复制', + }, + EditCell: { + complete: '完成', + cancel: '取消', + modify: '修改', + }, + EditInput: { + description: '字符长度不可超过${max}', + }, + Fullscreen: { + exitFull: '退出全屏', + full: '全屏', + }, + GlobalLoading: { + loading: '应用加载中,请等候~', + }, + GoBack: { + back: '返回', + }, + LoadError: { + please: '发现新版本,请', + get: '获取新版本。', + refresh: '刷新', + title: '若该提示长时间存在,请联系管理员。', + }, + ModalWithForm: { + okText: '确定', + cancelText: '取消', + }, + MulSelectDropdown: { + open: '打开', + cancelText: '取消', + okText: '确定', + selectAll: '全选', + }, + MultiSearchInput: { + case: '区分大小写匹配', + precise: '精确匹配', + front: '头部匹配', + tail: '尾部匹配', + }, + MxGraph: { + newNode: '新节点', + }, + NotFound: { + description: '亲,是不是走错地方了?', + }, + ProgressLine: { + description: '暂无描述', + }, + RenderFormItem: { + description: '${label}为空', + }, + SearchModal: { + title: '搜索并打开', + placeholder: '请输入', + }, + SpreadSheet: { + description: '暂无数据', + copy: '复制', + copyAll: '复制值以及列名', + }, +}; + +export default localeValues; diff --git a/src/components/modalWithForm/index.tsx b/src/components/modalWithForm/index.tsx index ae0941176..95bc98081 100644 --- a/src/components/modalWithForm/index.tsx +++ b/src/components/modalWithForm/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Form, FormProps, Modal } from 'antd'; import { ButtonProps, ButtonType } from 'antd/es/button'; +import useLocale from '../locale/useLocale'; export interface IProps { confirmLoading?: boolean; @@ -63,12 +64,14 @@ export const useFilterFormProps = (props = {}) => { }; const ModalForm = (props: ModalProps) => { + const locale = useLocale('ModalWithForm'); + const { title, visible, record, - okText = '确定', - cancelText = '取消', + okText = locale.okText, + cancelText = locale.cancelText, modelClass, okType, width, diff --git a/src/components/mulSelectDropdown/index.tsx b/src/components/mulSelectDropdown/index.tsx index 1e4aace28..275e90b0b 100644 --- a/src/components/mulSelectDropdown/index.tsx +++ b/src/components/mulSelectDropdown/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Checkbox, Dropdown, Button, Divider } from 'antd'; import { isEqual } from 'lodash'; import classNames from 'classnames'; +import useLocale, { Locale } from '../locale/useLocale'; interface Opts { label?: string; @@ -18,6 +19,7 @@ interface MulSelectDropdownProps { renderNode: Function; cancelText?: string; okText?: string; + locale?: Locale['MulSelectDropdown']; } interface MulSelectDropdownStates { @@ -28,7 +30,7 @@ interface MulSelectDropdownStates { indeterminate: boolean; } -export default class MulSelectDropdown extends React.Component< +class MulSelectDropdown extends React.Component< MulSelectDropdownProps, MulSelectDropdownStates > { @@ -108,11 +110,15 @@ export default class MulSelectDropdown extends React.Component< popupContainer = () => document.body, options = [], className = '', - renderNode = (openFun) => 打开, - cancelText = '取消', - okText = '确定', + locale, + renderNode:propRenderNode, + cancelText, + okText, } = this.props; const { visible, selectVal, indeterminate, allKeys } = this.state; + const defaultRenderNode = (openFun) => {locale.open}; + const renderNode = propRenderNode || defaultRenderNode; + const overlay = (
@@ -133,7 +139,7 @@ export default class MulSelectDropdown extends React.Component< checked={selectVal.length === allKeys.length} indeterminate={indeterminate} > - 全选 + {locale.selectAll}
@@ -172,3 +178,11 @@ export default class MulSelectDropdown extends React.Component< ); } } + + +const MulSelectDropdownWrapper = (props: Omit) => { + const locale = useLocale("MulSelectDropdown"); + return ; +}; + +export default MulSelectDropdownWrapper; diff --git a/src/components/multiSearchInput/index.tsx b/src/components/multiSearchInput/index.tsx index df16a6b84..22dfd263b 100644 --- a/src/components/multiSearchInput/index.tsx +++ b/src/components/multiSearchInput/index.tsx @@ -1,29 +1,7 @@ import _ from 'lodash'; import { Input, Tooltip } from 'antd'; import React from 'react'; - -const searchTypeList: any = [ - { - key: 'caseSensitive', - imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAABhElEQVRYhe2VPUvDQByHf5cmrRolQyHtYJ18gy4O4qSDg64i2II4SAvFNoWgX8ShQ64RBxFd6hdwc1QXQRCEirgo7WAXhxJJ079LC+LiRQWH3jPey8PD3cEBEolEMugw0YWc8zqAqRDuh1KpNB0+adAQvoI+nPM9APuMsblisXj72wD1B3vyRHQOIAdg90ucGQTBsGmaz9lsNhCRhToBzvkCgFNVVVc7nc617/sp27bfe3MHAJaJqMkYM3zfX7Rt++07pxImAEAewHGhUHgCcK9p2hoA1Gq1KBE9xuPxtGVZS4yxV03TVkSEwgGu644A2AQwX61WXQB6LwiZTMYHYLRarUvHca6IKA1gTMQr/AaIaANAnYhOiAiMsQsiOnRdd8JxnHFFUbYURZlsNBpBIpG4EfWGCcgTkWtZ1ll/jHO+3u12twEcARgNgoAnk8kUAJWI/jZAVdWcYRgvn8c8z9uJRqND5XK5WalUZmOx2Ey73b6LRCKaruueqPtfkX+BRCKRfAAM043+Rp32BgAAAABJRU5ErkJggg==', - tip: '区分大小写匹配', - }, - { - key: 'precise', - imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAwFBMVEUAAAD///+AgICioqKSkpKcnJyUlJSbm5uVlZWXl5eWlpaWlpaYmJiZmZmWlpaVlZWVlZWWlpaVlZWYmJiWlpaYmJiWlpaXl5eYmJiXl5eXl5eWlpaYmJiYmJiXl5eYmJiYmJiWlpaXl5eXl5eWlpaWlpaXl5eWlpaWlpaYmJiXl5eXl5eYmJiYmJiXl5eYmJiXl5eWlpaXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eIXQHmAAAAP3RSTlMAAQILDhITFxggIi4vMjM1Oj9BQ0RISVFUXWBmam9zdHmBhIWGjZaXoaOkp6qvvL7EyMzR2t3g4eTn6erz+PmloSGXAAAAAWJLR0QB/wIt3gAAAJlJREFUOMtjYBgF1AUqdvYowE4ZTYEdK4QW1oPQbLZoCuyhNJ82mgCKAh1TRgwdyHw2M31BIMUtxo5DgayqpAYDg5yBsjknFgX2JgzGPMxWLAxcAuL6kpb2WEzgtzEyspZmUNOVMZDCaoW6PAODiCGDqSi/BXYFChwMDExKzEJaihJ8OLwJBLyaOLxpx0YgJJXR40JpNH2SBgAKqha54oD4rgAAAABJRU5ErkJggg==', - tip: '精确匹配', - }, - { - key: 'front', - imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAB20lEQVRYhe2QPYgTURSFz33zJtkogrI6hcUqIm5pobiVVlsq6SwEmyQwZMCfThYRXiXYSZjMzdOwYVER0gsWay2ynYiFICgoGIw2oilm8q7NBpYVMkEN28xX3nM553CAgoKCgoIpMPPLOI4X55mhcvSjvu/v38sCc2emAsYY1ev1Fnbfmfk1M2/NtYCIlIMgeDYaja7tCj8HoCQii51O5/ROrd/ve0mSnLDWHv4fBZ4qpcae5/WSJDlkjNHbUo2INgA8EpHa5D+O42PD4fAtgA3n3CtmNv9UAMAZEbngnHtHRO+DILhnrd0H4DIRPVFKPQZwpdVqlQFAa30EwK0ois5rrVcB3JxmrqeJACAiz5VSZSK6GIbhLwBg5qsAlIjc3n5b8H2/CqCfZdlXz/PuMvNalmUZgAPT/HMXIKImgA8isrbjXCeiB865TefcJoB1IqoDgFLqDoCtZrO5QkQ38vxzF1BKuTAMa8YYBQDtdvukiJwtlUqX6vX6DwDodrsv0jT9ZK1dGo/Hb0TkOjMfFJHjANxU/7wCE4wxE6MvRLQ8CQeARqPx3Tm3XKlUvkVRdN/zvKpz7uFgMKhqrU/NmvEHzPzRWrv01wYzkLfA5zRNf86zQEFBQcGe8xvNVbTHyF7uqwAAAABJRU5ErkJggg==', - tip: '头部匹配', - }, - { - key: 'tail', - imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAB9klEQVRYhe2UP4gTQRjF37fJJqcRDw0kEUTwDygIIgh2NoIoWFwjKU5QDIRjJhxoddptY32wxcyuWEi4QnK1eJ1a2HqNIAh3V6jkApcyISCzz0YhitmcSqz2B9PM+2bem2+GATIyMjIyZkwURctRFC1P0vOzDkCynKZ7sw4wjakBrLUPrLU0xlz4k43b7XZpP3X7uYIGyQ0RaQC4/0u4inPuQKVS+Vyv192P+TiOjw0Gg01jzM1pm6d2wFp7GcCc7/sawGIYhsUx7QmAt57nrfX7/c0wDA8DgDHmiHNuRHJFRDYAXPzrAAAaANrNZnMHwAff9xcAoNPpFEhulcvl81rrKyKy5/v+NWPMoohsfR+rAI4CWEgzkElCHMcHkyTpAngtIrskLwHYU0rdICnW2scicp3kVxE5CWBFKfVsrENLAB6RfCMiO0qp4Hc+E98AyVsAPpJcIwkReUXyaRzHJ4wxxz3Pu+153plut+uq1eq78bXGmFMAHpK8KiJ30jow8QpINkjGWut1rfW6Uuq5iLxIkuQugE8ADjnnbK1WeykiPx1Ea73d6/VOa62308xTO5DP5+/Nz89/GZ8bjUZLhUJhrtVq7YZheK5YLJ4dDofvc7mcXyqVRuO1QRAk08z/C9bawFobTNJn/hWLSH/WHhkZGRn/xDfA/cJ9mTtA3QAAAABJRU5ErkJggg==', - tip: '尾部匹配', - }, -]; +import useLocale, { Locale } from '../locale/useLocale'; export type SearchType = 'fuzzy' | 'precise' | 'front' | 'tail'; @@ -37,6 +15,7 @@ export interface MultiSearchInputProps { searchType?: SearchType; // input框中选中的筛选方式 filterOptions?: SearchType[]; // 数组 className?: string; + locale?: Locale['MultiSearchInput']; [propName: string]: any; } @@ -74,9 +53,36 @@ class MultiSearchInput extends React.Component< render() { const { placeholder, style, value, onChange, onSearch, onTypeChange, filterOptions } = this.state; - const { className = '', value: propsValue, searchType: propsSearchType } = this.props; + const { + className = '', + value: propsValue, + searchType: propsSearchType, + locale, + } = this.props; let searchType = this.state.searchType; searchType = propsSearchType != null ? propsSearchType : searchType; + const searchTypeList: any = [ + { + key: 'caseSensitive', + imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAABhElEQVRYhe2VPUvDQByHf5cmrRolQyHtYJ18gy4O4qSDg64i2II4SAvFNoWgX8ShQ64RBxFd6hdwc1QXQRCEirgo7WAXhxJJ079LC+LiRQWH3jPey8PD3cEBEolEMugw0YWc8zqAqRDuh1KpNB0+adAQvoI+nPM9APuMsblisXj72wD1B3vyRHQOIAdg90ucGQTBsGmaz9lsNhCRhToBzvkCgFNVVVc7nc617/sp27bfe3MHAJaJqMkYM3zfX7Rt++07pxImAEAewHGhUHgCcK9p2hoA1Gq1KBE9xuPxtGVZS4yxV03TVkSEwgGu644A2AQwX61WXQB6LwiZTMYHYLRarUvHca6IKA1gTMQr/AaIaANAnYhOiAiMsQsiOnRdd8JxnHFFUbYURZlsNBpBIpG4EfWGCcgTkWtZ1ll/jHO+3u12twEcARgNgoAnk8kUAJWI/jZAVdWcYRgvn8c8z9uJRqND5XK5WalUZmOx2Ey73b6LRCKaruueqPtfkX+BRCKRfAAM043+Rp32BgAAAABJRU5ErkJggg==', + tip: locale.case, + }, + { + key: 'precise', + imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAwFBMVEUAAAD///+AgICioqKSkpKcnJyUlJSbm5uVlZWXl5eWlpaWlpaYmJiZmZmWlpaVlZWVlZWWlpaVlZWYmJiWlpaYmJiWlpaXl5eYmJiXl5eXl5eWlpaYmJiYmJiXl5eYmJiYmJiWlpaXl5eXl5eWlpaWlpaXl5eWlpaWlpaYmJiXl5eXl5eYmJiYmJiXl5eYmJiXl5eWlpaXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eIXQHmAAAAP3RSTlMAAQILDhITFxggIi4vMjM1Oj9BQ0RISVFUXWBmam9zdHmBhIWGjZaXoaOkp6qvvL7EyMzR2t3g4eTn6erz+PmloSGXAAAAAWJLR0QB/wIt3gAAAJlJREFUOMtjYBgF1AUqdvYowE4ZTYEdK4QW1oPQbLZoCuyhNJ82mgCKAh1TRgwdyHw2M31BIMUtxo5DgayqpAYDg5yBsjknFgX2JgzGPMxWLAxcAuL6kpb2WEzgtzEyspZmUNOVMZDCaoW6PAODiCGDqSi/BXYFChwMDExKzEJaihJ8OLwJBLyaOLxpx0YgJJXR40JpNH2SBgAKqha54oD4rgAAAABJRU5ErkJggg==', + tip: locale.precise, + }, + { + key: 'front', + imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAB20lEQVRYhe2QPYgTURSFz33zJtkogrI6hcUqIm5pobiVVlsq6SwEmyQwZMCfThYRXiXYSZjMzdOwYVER0gsWay2ynYiFICgoGIw2oilm8q7NBpYVMkEN28xX3nM553CAgoKCgoIpMPPLOI4X55mhcvSjvu/v38sCc2emAsYY1ev1Fnbfmfk1M2/NtYCIlIMgeDYaja7tCj8HoCQii51O5/ROrd/ve0mSnLDWHv4fBZ4qpcae5/WSJDlkjNHbUo2INgA8EpHa5D+O42PD4fAtgA3n3CtmNv9UAMAZEbngnHtHRO+DILhnrd0H4DIRPVFKPQZwpdVqlQFAa30EwK0ois5rrVcB3JxmrqeJACAiz5VSZSK6GIbhLwBg5qsAlIjc3n5b8H2/CqCfZdlXz/PuMvNalmUZgAPT/HMXIKImgA8isrbjXCeiB865TefcJoB1IqoDgFLqDoCtZrO5QkQ38vxzF1BKuTAMa8YYBQDtdvukiJwtlUqX6vX6DwDodrsv0jT9ZK1dGo/Hb0TkOjMfFJHjANxU/7wCE4wxE6MvRLQ8CQeARqPx3Tm3XKlUvkVRdN/zvKpz7uFgMKhqrU/NmvEHzPzRWrv01wYzkLfA5zRNf86zQEFBQcGe8xvNVbTHyF7uqwAAAABJRU5ErkJggg==', + tip: locale.front, + }, + { + key: 'tail', + imgSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAB9klEQVRYhe2UP4gTQRjF37fJJqcRDw0kEUTwDygIIgh2NoIoWFwjKU5QDIRjJhxoddptY32wxcyuWEi4QnK1eJ1a2HqNIAh3V6jkApcyISCzz0YhitmcSqz2B9PM+2bem2+GATIyMjIyZkwURctRFC1P0vOzDkCynKZ7sw4wjakBrLUPrLU0xlz4k43b7XZpP3X7uYIGyQ0RaQC4/0u4inPuQKVS+Vyv192P+TiOjw0Gg01jzM1pm6d2wFp7GcCc7/sawGIYhsUx7QmAt57nrfX7/c0wDA8DgDHmiHNuRHJFRDYAXPzrAAAaANrNZnMHwAff9xcAoNPpFEhulcvl81rrKyKy5/v+NWPMoohsfR+rAI4CWEgzkElCHMcHkyTpAngtIrskLwHYU0rdICnW2scicp3kVxE5CWBFKfVsrENLAB6RfCMiO0qp4Hc+E98AyVsAPpJcIwkReUXyaRzHJ4wxxz3Pu+153plut+uq1eq78bXGmFMAHpK8KiJ30jow8QpINkjGWut1rfW6Uuq5iLxIkuQugE8ADjnnbK1WeykiPx1Ea73d6/VOa62308xTO5DP5+/Nz89/GZ8bjUZLhUJhrtVq7YZheK5YLJ4dDofvc7mcXyqVRuO1QRAk08z/C9bawFobTNJn/hWLSH/WHhkZGRn/xDfA/cJ9mTtA3QAAAABJRU5ErkJggg==', + tip: locale.tail, + }, + ]; const filterList = _.filter(searchTypeList, (item: any) => { return _.includes(filterOptions, item.key); }); @@ -157,4 +163,9 @@ class MultiSearchInput extends React.Component< } } -export default MultiSearchInput; +const MultiSearchInputWrapper = (props) => { + const locale = useLocale('MultiSearchInput'); + return ; +}; + +export default MultiSearchInputWrapper; diff --git a/src/components/mxGraph/index.tsx b/src/components/mxGraph/index.tsx index bf021dc41..84911a91b 100644 --- a/src/components/mxGraph/index.tsx +++ b/src/components/mxGraph/index.tsx @@ -21,6 +21,7 @@ import { mxPopupMenuHandler, } from 'mxgraph'; import MxFactory from './factory'; +import useLocale from '../locale/useLocale'; const Mx = new MxFactory(); @@ -292,6 +293,8 @@ function MxGraphContainer( const keybindingsRef = useRef([]); const [current, setCurrent] = useState(null); + const locale = useLocale('MxGraph'); + useImperativeHandle(ref, () => ({ /** * 在某一位置插入节点 @@ -426,7 +429,7 @@ function MxGraphContainer( onGetPreview?.(node) || (() => { const dom = document.createElement('div'); - dom.innerHTML = `新节点`; + dom.innerHTML = `${locale.newNode}`; return dom; })(); diff --git a/src/components/notFound/index.tsx b/src/components/notFound/index.tsx index 97e9b9dc1..d920b7c12 100644 --- a/src/components/notFound/index.tsx +++ b/src/components/notFound/index.tsx @@ -1,15 +1,17 @@ import React from 'react'; import { FrownOutlined } from '@ant-design/icons'; +import useLocale from '../locale/useLocale'; -export default class NotFound extends React.Component { - render() { - return ( -
-

- 亲,是不是走错地方了? -

-
- ); - } -} +const NotFound = () => { + const locale = useLocale('NotFound'); + return ( +
+

+ {locale.description} +

+
+ ); +}; + +export default NotFound; diff --git a/src/components/progressLine/index.tsx b/src/components/progressLine/index.tsx index f2318d554..736967fdd 100644 --- a/src/components/progressLine/index.tsx +++ b/src/components/progressLine/index.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { Tooltip } from 'antd'; +import useLocale from '../locale/useLocale'; interface IProps { title?: string; @@ -12,7 +13,7 @@ interface IProps { } const ProgressLine = ({ - title = '暂无描述', + title, num = 0, percent = '0%', color = '#3BCEFF', @@ -21,7 +22,8 @@ const ProgressLine = ({ width = '280px', }: IProps) => { const slidePrefixCls = 'dtc-progress-line'; - const label = `${title}: ${num}`; + const locale = useLocale('ProgressLine'); + const label = `${title || locale.description}: ${num}`; return (
{needTitle && ( diff --git a/src/components/renderFormItem/index.tsx b/src/components/renderFormItem/index.tsx index 02e8c26a8..1bdb48e1d 100644 --- a/src/components/renderFormItem/index.tsx +++ b/src/components/renderFormItem/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Form, Input } from 'antd'; import { Rule } from 'antd/lib/form'; +import useLocale from '../locale/useLocale'; const FormItem = Form.Item; @@ -43,6 +44,8 @@ export default function RenderFormItem({ item, layout }: ItemType) { valuePropName, normalize, } = options; + const locale = useLocale('RenderFormItem'); + return ( void; onSelect?: (value: string, option: Object) => void; onCancel?: () => void; @@ -30,15 +32,22 @@ class SearchModal extends React.Component { render() { const { visible, - title = '搜索并打开', + title, prefixRender, dataSource = [], // bodyStyle, - placeholder = '请输入', + placeholder, + locale, ...rest } = this.props; return ( - + {prefixRender && ( @@ -52,7 +61,7 @@ class SearchModal extends React.Component { onSelect={this.onSelect} onSearch={this.onChange} > - + @@ -60,4 +69,11 @@ class SearchModal extends React.Component { ); } } -export default SearchModal; + +const SearchModalWrapper = (props: Omit) => { + const locale = useLocale('SearchModal'); + const { visible, dataSource, ...reset } = props; + return ; +}; + +export default SearchModalWrapper; diff --git a/src/components/spreadSheet/index.tsx b/src/components/spreadSheet/index.tsx index ac8569b03..cdb5b1128 100644 --- a/src/components/spreadSheet/index.tsx +++ b/src/components/spreadSheet/index.tsx @@ -6,6 +6,7 @@ import type { HotTableProps } from '@handsontable/react'; import classNames from 'classnames'; import 'handsontable/dist/handsontable.full.css'; import 'handsontable/languages/zh-CN.js'; +import useLocale, { Locale } from '../locale/useLocale'; type IOptions = HotTableProps & { /** 是否展示复制值以及列名 */ @@ -24,6 +25,7 @@ export interface SpreadSheetProps { /** 字段类型 */ type: string; }>; + locale?: Locale['SpreadSheet']; hotTableInstanceRef?: (instance: any) => void; } @@ -55,11 +57,11 @@ class SpreadSheet extends React.PureComponent { this.removeRenderClock(); } getData() { - const { data, columns = [] } = this.props; + const { data, columns = [], locale } = this.props; let showData = data; if (!showData || !showData.length) { const emptyArr = new Array(columns.length).fill('', 0, columns.length); - emptyArr[0] = '暂无数据'; + emptyArr[0] = locale.description; showData = [emptyArr]; } return showData; @@ -92,10 +94,10 @@ class SpreadSheet extends React.PureComponent { } getContextMenu() { const that = this; - const { columns = [], options } = this.props; + const { columns = [], options, locale } = this.props; const items = { copy: { - name: '复制', + name: locale.copy, callback: function (_key) { const indexArr = this.getSelected(); // eslint-disable-next-line prefer-spread @@ -106,7 +108,7 @@ class SpreadSheet extends React.PureComponent { }; if (options?.showCopyWithHeader) { const copyWithHeaderItem = { - name: '复制值以及列名', + name: locale.copyAll, callback: function (_key, selection) { const indexArr = this.getSelected(); // eslint-disable-next-line prefer-spread @@ -153,7 +155,9 @@ class SpreadSheet extends React.PureComponent { if (!isShowColHeaders) return false; // handsontable 不支持 renderCustomHeader,所以只能用 html string 实现 tooltip const fieldTypeStr = columnTypes?.[index]?.type; - const title = fieldTypeStr ? `${columns?.[index]}: ${fieldTypeStr}` : columns?.[index]; + const title = fieldTypeStr + ? `${columns?.[index]}: ${fieldTypeStr}` + : columns?.[index]; return `${title}`; }} data={showData} @@ -176,4 +180,10 @@ class SpreadSheet extends React.PureComponent { ); } } -export default SpreadSheet; + +const SpreadSheetWrapper = (props: Omit) => { + const locale = useLocale('SpreadSheet'); + return ; +}; + +export default SpreadSheetWrapper;