diff --git a/source/components/Config/Locale/Consumer.tsx b/source/components/Config/Locale/Consumer.tsx index f335e5d01..88ba505b5 100644 --- a/source/components/Config/Locale/Consumer.tsx +++ b/source/components/Config/Locale/Consumer.tsx @@ -2,16 +2,23 @@ import React from 'react'; import { Consumer } from './Context'; import LocaleList from '../../Locale/index'; +export function getLocale(Locale: string, componentName?: string) { + let currentLocale = LocaleList[Locale] && LocaleList[Locale]; + if (componentName) { + currentLocale = currentLocale[componentName]; + } + return currentLocale || {}; +} + export default function LocaleConsumer({ componentName, children }: { componentName: string, - children: (params: object) => React.ReactNode + children: (params: object) => React.ReactNode }) { return { (value) => { const { Locale } = value; - const ComponentLocal = LocaleList[Locale] && LocaleList[Locale][componentName] - // console.log(Locale, ComponentLocal); + const ComponentLocal = getLocale(Locale, componentName); return children(ComponentLocal || {}); } } diff --git a/source/components/Config/Locale/Provider.tsx b/source/components/Config/Locale/Provider.tsx index eaa4b4f42..0ef2dc108 100644 --- a/source/components/Config/Locale/Provider.tsx +++ b/source/components/Config/Locale/Provider.tsx @@ -1,6 +1,18 @@ import React from 'react' import { Provider } from './Context'; +import { getLocale } from './Consumer'; + +let runtimeLocale = {}; + +export function getRuntimeLocale(componentName) { + return runtimeLocale[componentName] || {}; +} + +// 部分组件如Modal 可以通过函数式调用 所以需要runtimeLocale提供支撑 +export function changeRuntimeLocale(LocaleValue = 'en_US') { + runtimeLocale = getLocale(LocaleValue); +}; export default (props: { value: { @@ -8,7 +20,13 @@ export default (props: { } children?: React.ReactNode }) => { + const renderProvider = () => { + changeRuntimeLocale(props.value && props.value.Locale); + return props.children + } return - {props.children} + { + renderProvider() + } }; \ No newline at end of file diff --git a/source/components/Locale/en_US.ts b/source/components/Locale/en_US.ts index a26848df8..be40455d6 100644 --- a/source/components/Locale/en_US.ts +++ b/source/components/Locale/en_US.ts @@ -14,6 +14,30 @@ const LocaleValue: LocaleProperties = { }, Table: { filterTitle: 'filters', + }, + Modal: { + okText: 'ok', + cancelText: 'cancel', + }, + Pagination: { + items_per_page: '/ page', + jump_to: 'Go to', + jump_to_confirm: 'confirm', + page: '', + + // Pagination.jsx + prev_page: 'Previous Page', + next_page: 'Next Page', + prev_5: 'Previous 5 Pages', + next_5: 'Next 5 Pages', + prev_3: 'Previous 3 Pages', + next_3: 'Next 3 Pages' + }, + VideoViewer: { + fullScreen: 'fullScreen', + cancelFullScreen: 'cancelFullScreen', + download: 'download', + retry: 'retry' } } diff --git a/source/components/Locale/index.ts b/source/components/Locale/index.ts index 6ccc3175c..3db588d9b 100644 --- a/source/components/Locale/index.ts +++ b/source/components/Locale/index.ts @@ -2,22 +2,22 @@ import zh_CN from "./zh_CN"; import en_US from './en_US'; -type keyStringObj = { - [propName: string]: string | object, +type keyString = { + [propName: string]: string, } export interface LocaleProperties { locale: string, - AudioPlayer?: keyStringObj, - AutoComplete?: keyStringObj, - Cascader?: keyStringObj, - global?: keyStringObj, - Table?: keyStringObj, - Modal?: keyStringObj, - Form?: keyStringObj & { - defaultValidateMessages: keyStringObj, - } + AudioPlayer?: keyString, + AutoComplete?: keyString, + Cascader?: keyString, + global?: keyString, + Table?: keyString, + Modal?: keyString, + RichEditor?: keyString, + Pagination?: keyString, + VideoViewer?: keyString, }; const Locale = { diff --git a/source/components/Locale/zh_CN.ts b/source/components/Locale/zh_CN.ts index 3e60dcdc2..e3b2dd928 100644 --- a/source/components/Locale/zh_CN.ts +++ b/source/components/Locale/zh_CN.ts @@ -33,17 +33,85 @@ const localeValues: LocaleProperties = { Modal: { okText: '确定', cancelText: '取消', - justOkText: '知道了', + // justOkText: '知道了', }, - Form: { - optional: '(可选)', - defaultValidateMessages: { - default: '字段验证错误${label}', - required: '请输入${label}', - enum: '${label}必须是其中一个[${enum}]', - whitespace: '${label}不能为空字符', - }, + Pagination: { + items_per_page: '条/页', + jump_to: '跳至', + jump_to_confirm: '确定', + page: '页', + + // Pagination.js + prev_page: '上一页', + next_page: '下一页', + prev_5: '向前 5 页', + next_5: '向后 5 页', + prev_3: '向前 3 页', + next_3: '向后 3 页', + }, + RichEditor: { + // editor + insertImageTip: '支持jpg、jpeg、png、gif、bmp格式的图片,最佳显示高度不超过400px,宽度不超过270px。', + rule1: '1、单个视频不超过10M,支持MP4、3GP格式视频。', + rule2: '2、最佳显示高度不超过400px', + PleaseEnterTheVideolinkURL: "请输入视频链接URL", + selectLocalVideo: '选择本地视频', + videoLink: '视频链接', + localVideo: "本地视频", + insertVideo: '插入视频', + selectLocalImage: "选择本地图片", + insertAttachment: "插入附件", + selectLocalFile: "选择本地文件", + HyperlinkAddress: '超链接地址', + insertPicture: '插入图片', + noVideoLinkErrorTip: '请设置视频源地址', + VideoCantPlayTip: '视频无法播放', + file: '[文件]', + noFileInfoTip: '文件信息读取失败', + noPicSrcTip: '请设置图片源地址', + noVideoUrlErrorTip: '视频链接URL不得为空', + videoUrlFormattingError: '视频链接URL格式错误', + videoLinkTooLongError: '视频链接不得超过1000个字', + linkEmptyTip: '链接地址不得为空', + linkToolongError: '链接地址不得超过1000个字', + noSelectionText: '没有选中文本', + insertLink: '插入超链接', + editLink: '编辑超链接', + + //toolbar + backgroundColor: '背景色', + textDirection: '文字方向', + increaseIndent: '增加缩进', + decreaseIndent: '减少缩进', + subScript: '下脚标', + superSciprt: '上脚标', + codeBlock: '代码块', + blockquote: '块引用', + strike: '删除线', + brushFormat: '格式刷', + clearFormat: '清除格式', + fontSize: '文字大小', + insertEmoji: '插入表情', + defaultEmoji: '默认表情', + orderedList: '有序列表', + unOrderedList: '无序列表', + alignJustified: '两端对齐', + alignCenter: '居中', + alignRight: '居右', + alignLeft: '居左', + fontColor: '文字颜色', + underline: '下划线', + italic: '斜体', + bold: '粗体', + temporarilyNoData: '暂无数据', + enterKeyWordPlease: '请输入关键字', }, + VideoViewer: { + fullScreen: '全屏', + cancelFullScreen: '取消全屏', + download: '下载', + retry: '重试' + } }; export default localeValues; diff --git a/source/components/Modal/Modal.tsx b/source/components/Modal/Modal.tsx index d95eead22..cff4ebaba 100644 --- a/source/components/Modal/Modal.tsx +++ b/source/components/Modal/Modal.tsx @@ -4,6 +4,8 @@ import * as PropTypes from 'prop-types'; import { addEventListener } from '../../utils/index'; import Button from '../Button'; import { ButtonType, NativeButtonProps } from '../Button/Button'; +import ConfigConsumer from '../Config/Consumer'; +import { LocaleProperties } from '../Locale'; let mousePosition: { x: number; y: number } | null; let mousePositionEventBinded: boolean; @@ -108,9 +110,9 @@ export default class Modal extends React.Component { draggable: false, maskClosable: false, esc: false, - okText: '确定', - cancelText: '取消', - justOkText: '知道了' + // okText: '确定', + // cancelText: '取消', + // justOkText: '知道了' }; static propTypes = { @@ -174,30 +176,36 @@ export default class Modal extends React.Component { cancelButtonProps, okButtonProps } = this.props; - const defaultFooter = ( -
- - -
- ); return ( - + + { + (Locale: LocaleProperties['Modal']) => { + const defaultFooter = ( +
+ + +
); + + return + } + } +
); } } diff --git a/source/components/Modal/confirm.tsx b/source/components/Modal/confirm.tsx index ee8beaf9f..1188b4c47 100755 --- a/source/components/Modal/confirm.tsx +++ b/source/components/Modal/confirm.tsx @@ -4,6 +4,9 @@ import classNames from 'classnames'; import Icon from '../Icon/index'; import Dialog, { ModalFuncProps } from './Modal'; import ActionButton from './ActionButton'; +import { getRuntimeLocale } from '../Config/Locale/Provider'; +import { LocaleProperties } from '../Locale'; + interface ConfirmDialogProps extends ModalFuncProps { afterClose?: () => void; @@ -13,6 +16,8 @@ interface ConfirmDialogProps extends ModalFuncProps { const IS_REACT_16 = !!ReactDOM.createPortal; const ConfirmDialog = (props: ConfirmDialogProps) => { + const runtimeLocale: LocaleProperties['Modal'] = getRuntimeLocale('Modal'); + const { onCancel, onOk, close, zIndex, afterClose, visible, esc } = props; const iconType = props.iconType || 'hints-descriptions'; const okType = props.okType || 'primary'; @@ -23,8 +28,8 @@ const ConfirmDialog = (props: ConfirmDialogProps) => { const style = props.style || {}; // 默认为 false,保持旧版默认行为 const maskClosable = props.maskClosable === undefined ? false : props.maskClosable; - const okText = props.okText || '确定'; - const cancelText = props.cancelText || '取消'; + const okText = props.okText || runtimeLocale.okText; + const cancelText = props.cancelText || runtimeLocale.cancelText; const classString = classNames(prefixCls, `${prefixCls}-${props.type}`, props.className); @@ -33,7 +38,6 @@ const ConfirmDialog = (props: ConfirmDialogProps) => { {cancelText} ); - return ( {
{cancelButton} - {okText} + {okText || runtimeLocale.okText}
diff --git a/source/components/Pagination/src/Pagination.js b/source/components/Pagination/src/Pagination.js index 9055b0c81..941f04322 100755 --- a/source/components/Pagination/src/Pagination.js +++ b/source/components/Pagination/src/Pagination.js @@ -3,8 +3,8 @@ import PropTypes from 'prop-types'; import Pager from './Pager'; import Options from './Options'; import KEYCODE from './KeyCode'; -import LOCALE from './locale/zh_CN'; -import {polyfill} from 'react-lifecycles-compat'; +import ConfigComsumer from '../../Config/Consumer'; +import { polyfill } from 'react-lifecycles-compat'; function noop() { } @@ -67,7 +67,6 @@ class Pagination extends React.Component { showLessItems: false, showTitle: true, onShowSizeChange: noop, - locale: LOCALE, style: {}, itemRender: defaultItemRender, }; @@ -127,7 +126,7 @@ class Pagination extends React.Component { componentDidUpdate(prevProps, prevState) { // When current page change, fix focused style of prev item // A hacky solution of https://github.com/ant-design/ant-design/issues/8948 - const {prefixCls} = this.props; + const { prefixCls } = this.props; if (prevState.current !== this.state.current && this.paginationNode) { const lastCurrentNode = this.paginationNode.querySelector( `.${prefixCls}-item-${prevState.current}` @@ -296,302 +295,308 @@ class Pagination extends React.Component { }; render() { - // When hideOnSinglePage is true and there is only 1 page, hide the pager - if (this.props.hideOnSinglePage === true && this.props.total <= this.state.pageSize) { - return null; - } - - const props = this.props; - const locale = props.locale; - - const prefixCls = props.prefixCls; - const allPages = calculatePage(undefined, this.state, this.props); - const pagerList = []; - let jumpPrev = null; - let jumpNext = null; - let firstPager = null; - let lastPager = null; - let gotoButton = null; - - const goButton = (props.showQuickJumper && props.showQuickJumper.goButton); - const pageBufferSize = props.showLessItems ? 1 : 2; - const {current, pageSize} = this.state; - - const prevPage = current - 1 > 0 ? current - 1 : 0; - const nextPage = current + 1 < allPages ? current + 1 : allPages; - - const dataOrAriaAttributeProps = Object.keys(props).reduce((prev, key) => { - if ((key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-' || key === 'role')) { - prev[key] = props[key]; - } - return prev; - }, {}); - - if (props.simple) { - if (goButton) { - if (typeof goButton === 'boolean') { - gotoButton = ( - + ); + } else { + gotoButton = ( + {goButton} + ); + } + gotoButton = ( +
  • + {gotoButton} +
  • + ); + } + + return ( + + ); + } + + if (allPages <= 5 + pageBufferSize * 2) { + for (let i = 1; i <= allPages; i++) { + const active = this.state.current === i; + pagerList.push( + + ); + } + } else { + const prevItemTitle = props.showLessItems ? locale.prev_3 : locale.prev_5; + const nextItemTitle = props.showLessItems ? locale.next_3 : locale.next_5; + if (props.showPrevNextJumpers) { + jumpPrev = ( +
  • + {props.itemRender( + this.getJumpPrevPage(), 'jump-prev', + )} +
  • + ); + jumpNext = ( +
  • + {props.itemRender( + this.getJumpNextPage(), 'jump-next', + )} +
  • + ); + } + lastPager = ( + + ); + firstPager = ( + + ); + + let left = Math.max(1, current - pageBufferSize); + let right = Math.min(current + pageBufferSize, allPages); + + if (current - 1 <= pageBufferSize) { + right = 1 + pageBufferSize * 2; + } + + if (allPages - current <= pageBufferSize) { + left = allPages - pageBufferSize * 2; + } + + for (let i = left; i <= right; i++) { + const active = current === i; + pagerList.push( + + ); + } + + if (current - 1 >= pageBufferSize * 2 && current !== 1 + 2) { + pagerList[0] = React.cloneElement(pagerList[0], { + className: `${prefixCls}-item-after-jump-prev`, + }); + pagerList.unshift(jumpPrev); + } + if (allPages - current >= pageBufferSize * 2 && current !== allPages - 2) { + pagerList[pagerList.length - 1] = React.cloneElement(pagerList[pagerList.length - 1], { + className: `${prefixCls}-item-before-jump-next`, + }); + pagerList.push(jumpNext); + } + + if (left !== 1) { + pagerList.unshift(firstPager); + } + if (right !== allPages) { + pagerList.push(lastPager); + } + } + + let totalText = null; + + if (props.showTotal) { + totalText = ( +
  • + {props.showTotal( + props.total, + [ + (current - 1) * pageSize + 1, + current * pageSize > props.total ? props.total : current * pageSize, + ] + )} +
  • + ); + } + const prevDisabled = !this.hasPrev(); + const nextDisabled = !this.hasNext(); + return ( +
    ); } - gotoButton = ( -
  • - {gotoButton} -
  • - ); - } - - return ( -
    - ); - } - - if (allPages <= 5 + pageBufferSize * 2) { - for (let i = 1; i <= allPages; i++) { - const active = this.state.current === i; - pagerList.push( - - ); - } - } else { - const prevItemTitle = props.showLessItems ? locale.prev_3 : locale.prev_5; - const nextItemTitle = props.showLessItems ? locale.next_3 : locale.next_5; - if (props.showPrevNextJumpers) { - jumpPrev = ( -
  • - {props.itemRender( - this.getJumpPrevPage(), 'jump-prev', - )} -
  • - ); - jumpNext = ( -
  • - {props.itemRender( - this.getJumpNextPage(), 'jump-next', - )} -
  • - ); - } - lastPager = ( - - ); - firstPager = ( - - ); - - let left = Math.max(1, current - pageBufferSize); - let right = Math.min(current + pageBufferSize, allPages); - - if (current - 1 <= pageBufferSize) { - right = 1 + pageBufferSize * 2; - } - - if (allPages - current <= pageBufferSize) { - left = allPages - pageBufferSize * 2; - } - - for (let i = left; i <= right; i++) { - const active = current === i; - pagerList.push( - - ); - } - - if (current - 1 >= pageBufferSize * 2 && current !== 1 + 2) { - pagerList[0] = React.cloneElement(pagerList[0], { - className: `${prefixCls}-item-after-jump-prev`, - }); - pagerList.unshift(jumpPrev); } - if (allPages - current >= pageBufferSize * 2 && current !== allPages - 2) { - pagerList[pagerList.length - 1] = React.cloneElement(pagerList[pagerList.length - 1], { - className: `${prefixCls}-item-before-jump-next`, - }); - pagerList.push(jumpNext); - } - - if (left !== 1) { - pagerList.unshift(firstPager); - } - if (right !== allPages) { - pagerList.push(lastPager); - } - } - - let totalText = null; - - if (props.showTotal) { - totalText = ( -
  • - {props.showTotal( - props.total, - [ - (current - 1) * pageSize + 1, - current * pageSize > props.total ? props.total : current * pageSize, - ] - )} -
  • - ); - } - const prevDisabled = !this.hasPrev(); - const nextDisabled = !this.hasNext(); - return ( -
    - ); + ); } } diff --git a/source/components/RichEditor/src/editor.tsx b/source/components/RichEditor/src/editor.tsx index 36a212fbc..ce35f8f65 100644 --- a/source/components/RichEditor/src/editor.tsx +++ b/source/components/RichEditor/src/editor.tsx @@ -21,7 +21,9 @@ import PlainClipboard from "./modules/plainClipboard"; import ImageDrop from "./modules/imageDrop"; import FileDrop from "./modules/fileDrop"; -import {RichEditorProps, RichEditorState} from './interface' +import { RichEditorProps, RichEditorState } from './interface' +import ConfigConsumer from '../../Config/Consumer'; +import { LocaleProperties } from '../../Locale'; Quill.register(EmojiBlot); Quill.register(LinkBlot); @@ -33,12 +35,12 @@ Quill.register("modules/fileDrop", FileDrop, true); Quill.register(Quill.import('attributors/style/align'), true); Quill.register(Quill.import('attributors/style/direction'), true); -const getImageSize = function( +const getImageSize = function ( url: string, - callback: (width: number|string, height: number|string) => void + callback: (width: number | string, height: number | string) => void ) { let newImage = document.createElement("img"); - newImage.onload = function(this: GlobalEventHandlers & {width?: number|string, height?: number|string}) { + newImage.onload = function (this: GlobalEventHandlers & { width?: number | string, height?: number | string }) { // callback(this.width, this.height); callback(this.width, this.height); }; @@ -48,7 +50,7 @@ const getImageSize = function( class Range { index: number; length: number; - constructor(index: number, length = 0) { + constructor (index: number, length = 0) { this.index = index; this.length = length; } @@ -57,46 +59,40 @@ class Range { class RichEditor extends Component { - reactQuillNode: Element | Text - defaultFontSize: string - - defaultVideoType: string - isSupportCustomInsertVideo: boolean - prevSelectionFormat: any - handlers: { - link: Function, - video: Function, - emoji: Function, - image: Function, - attachment: Function, - clean: Function, - customInsertValue: Function - } - editorCtner: HTMLDivElement - linkModalInputRef: any - videoModalInputRef: any - toolbarRef: React.ReactInstance - reactQuillRef: ReactQuill - onClickRemoveHandler: any - onClickActionHandler: any - onBlurHandler: any - linkRange: any + reactQuillNode: Element | Text + defaultFontSize: string + Locale: LocaleProperties['RichEditor'] + + defaultVideoType: string + isSupportCustomInsertVideo: boolean + prevSelectionFormat: any + handlers: { + link: Function, + video: Function, + emoji: Function, + image: Function, + attachment: Function, + clean: Function, + customInsertValue: Function + } + editorCtner: HTMLDivElement + linkModalInputRef: any + videoModalInputRef: any + toolbarRef: React.ReactInstance + reactQuillRef: ReactQuill + onClickRemoveHandler: any + onClickActionHandler: any + onBlurHandler: any + linkRange: any static defaultProps = { customEmoji: [], customLink: {}, customInsertValue: {}, - insertImageTip: - "支持jpg、jpeg、png、gif、bmp格式的图片,最佳显示高度不超过400px,宽度不超过270px。", - insertVideoTip: ( - - 1、单个视频不超过10M,支持MP4、3GP格式视频。 -
    - 2、最佳显示高度不超过400px, 宽度不超过270px。 -
    - ), - placeholder: "请输入内容", + insertImageTip: true, + insertVideoTip: true, + placeholder: true, prefixCls: "fishd-richeditor", popoverPlacement: "top", tooltipPlacement: "bottom", @@ -132,10 +128,11 @@ class RichEditor extends Component { return newState; } - constructor(props: RichEditorProps) { + constructor (props: RichEditorProps) { super(props); this.reactQuillNode = document.body; this.defaultFontSize = "14px"; + this.Locale = {}; let { value, @@ -177,7 +174,7 @@ class RichEditor extends Component { curRange: null, curVideoType: this.defaultVideoType, defaultInputLink: "http://", - linkModalTitle: "插入超链接", + linkModalTitle: "", formatPainterActive: false }; this.handlers = { @@ -210,14 +207,14 @@ class RichEditor extends Component { // 点击编辑链接触发 if (fromAction) { newState["defaultInputLink"] = value; - newState["linkModalTitle"] = "编辑超链接"; + newState["linkModalTitle"] = this.Locale.editLink; } else { - newState["linkModalTitle"] = "插入超链接"; + newState["linkModalTitle"] = this.Locale.insertLink; } this.setState(newState); } else { - message.error("没有选中文本"); + message.error(this.Locale.noSelectionText); } }, video: value => { @@ -330,7 +327,8 @@ class RichEditor extends Component { // 处理定制的超链接 Object.keys(customLink).forEach(moduleName => { - this.handlers[`${moduleName}Entry`] = function() { + const that = this; + this.handlers[`${moduleName}Entry`] = function () { let range = this.quill.getSelection(), url = customLink[moduleName].url; if (range.length !== 0) { @@ -363,7 +361,7 @@ class RichEditor extends Component { } } } else { - message.error("没有选中文本"); + message.error(that.Locale.noSelectionText); } }; }); @@ -489,14 +487,14 @@ class RichEditor extends Component { case "size": { // font标签size属性的value是数字类型,取值范围是[1,7]。 let size2pxMap = { - "1": "12px", - "2": "13px", - "3": "16px", - "4": "18px", - "5": "24px", - "6": "32px", - "7": "48px" - }, + "1": "12px", + "2": "13px", + "3": "16px", + "4": "18px", + "5": "24px", + "6": "32px", + "7": "48px" + }, sizeWithUnit = this.defaultFontSize, val = value && value.trim(); @@ -539,7 +537,7 @@ class RichEditor extends Component { this.reactQuillRef.blur(); }; - getEditor = ():ReactQuill|undefined => { + getEditor = (): ReactQuill | undefined => { if (!this.reactQuillRef) return; return this.reactQuillRef.getEditor() as ReactQuill; }; @@ -550,7 +548,7 @@ class RichEditor extends Component { if (val) { if (val.length > 1000) { - message.error("链接地址不得超过1000个字"); + message.error(this.Locale.linkToolongError); return; } @@ -572,7 +570,7 @@ class RichEditor extends Component { defaultInputLink: "http://" }); } else { - message.error("链接地址不得为空"); + message.error(this.Locale.linkEmptyTip); } }; @@ -592,12 +590,12 @@ class RichEditor extends Component { if (val) { if (val.length > 1000) { - message.error("视频链接不得超过1000个字"); + message.error(this.Locale.videoLinkTooLongError); return; } if (val.indexOf("//") < 0) { - message.error("视频链接URL格式错误"); + message.error(this.Locale.videoUrlFormattingError); return; } @@ -620,7 +618,7 @@ class RichEditor extends Component { curRange: null }); } else { - message.error("视频链接URL不得为空"); + message.error(this.Locale.noVideoUrlErrorTip); } }; @@ -658,7 +656,7 @@ class RichEditor extends Component { const handleInsertImage = info => { if (info.src == undefined) { - message.error("请设置图片源地址"); + message.error(this.Locale.noPicSrcTip); return; } @@ -741,7 +739,7 @@ class RichEditor extends Component { const handleInsertFile = file => { if (!file || !file.url || !file.name) { - message.error("文件信息读取失败"); + message.error(this.Locale.noFileInfoTip); return; } @@ -752,13 +750,13 @@ class RichEditor extends Component { // 继承列表的样式 let curFormat = quill.getFormat(range), - listFormat: {list?:any} = {}; + listFormat: { list?: any } = {}; if (curFormat && curFormat.list) { listFormat.list = curFormat.list; } - let displayFileName = "[文件] " + file.name, + let displayFileName = (this.Locale.file) + file.name, contentsDelta: any[] = [ { insert: displayFileName, @@ -832,7 +830,7 @@ class RichEditor extends Component { videoNode = document.createElement("video"); videoNode.onerror = () => { - message.error("视频无法播放"); + message.error(this.Locale.VideoCantPlayTip); }; videoNode.src = attrs.src && attrs.src.trim(); videoNode = null; @@ -862,7 +860,7 @@ class RichEditor extends Component { const handleVideoInsert = info => { if (info.src == undefined) { - message.error("请设置视频源地址"); + message.error(this.Locale.noVideoLinkErrorTip); return; } @@ -1294,128 +1292,145 @@ class RichEditor extends Component { } return ( -
    (this.editorCtner = el)}> - - 超链接地址 - (this.linkModalInputRef = el)} - style={{ width: "434px" }} - defaultValue={defaultInputLink} - /> - {insertLinkTip ?
    {insertLinkTip}
    : null} -
    - - - {insertImageTip ?
    {insertImageTip}
    : null} -
    - - - {insertAttachmentTip ? ( -
    {insertAttachmentTip}
    - ) : null} -
    - - - {this.isSupportCustomInsertVideo ? ( - 本地视频 - ) : null} - 视频链接 - - {curVideoType == "video_local" ? ( - - - {insertVideoTip ? ( -
    {insertVideoTip}
    - ) : null} -
    - ) : ( - (this.videoModalInputRef = el)} - style={{ width: "434px" }} - placeholder="请输入视频链接URL" - /> - )} -
    - (this.toolbarRef = el)} - className={"editor-head"} - toolbar={toolbar} - customEmoji={customEmoji} - customLink={customLink} - customInsertValue={customInsertValue} - handleInsertEmoji={this.handleInsertEmoji} - handleFormatColor={this.handleFormatColor} - handleFormatBackground={this.handleFormatBackground} - handleFormatSize={this.handleFormatSize} - handleInsertValue={this.handleInsertValue} - popoverPlacement={popoverPlacement} - tooltipPlacement={tooltipPlacement} - getPopupContainer={getPopupContainer} - getCurrentSize={this.getCurrentSize} - formatPainterActive={this.state.formatPainterActive} - saveSelectionFormat={this.handleSaveSelectionFormat} - unsaveSelectionFormat={this.handleUnsaveSelectionFormat} - /> - (this.reactQuillRef = el)} - bounds={this.editorCtner} - className={"editor-body"} - modules={moduleOpts} - placeholder={placeholder} - onChange={this.handleChange} - onSelectionChange={this.handleSelectionChange} - /> - {loading ? ( - - ) : null} -
    + + { + (Locale: LocaleProperties['RichEditor']) => { + this.Locale = Locale; + return ( +
    (this.editorCtner = el)}> + + {Locale.HyperlinkAddress} + (this.linkModalInputRef = el)} + style={{ width: "434px" }} + defaultValue={defaultInputLink} + /> + {insertLinkTip ?
    {insertLinkTip}
    : null} +
    + + + {insertImageTip ?
    {Locale.inserImageTip || insertImageTip}
    : null} +
    + + + {insertAttachmentTip ? ( +
    {insertAttachmentTip}
    + ) : null} +
    + + + {this.isSupportCustomInsertVideo ? ( + {Locale.localVideo} + ) : null} + {Locale.videoLink} + + {curVideoType == "video_local" ? ( + + + {insertVideoTip ? ( + insertVideoTip === true + ?
    { + + {Locale.rule1} +
    + {Locale.rule2} +
    + }
    + : insertVideoTip + ) : null} +
    + ) : ( + (this.videoModalInputRef = el)} + style={{ width: "434px" }} + placeholder={Locale.PleaseEnterTheVideolinkURL} + /> + )} +
    + (this.toolbarRef = el)} + className={"editor-head"} + toolbar={toolbar} + customEmoji={customEmoji} + customLink={customLink} + customInsertValue={customInsertValue} + handleInsertEmoji={this.handleInsertEmoji} + handleFormatColor={this.handleFormatColor} + handleFormatBackground={this.handleFormatBackground} + handleFormatSize={this.handleFormatSize} + handleInsertValue={this.handleInsertValue} + popoverPlacement={popoverPlacement} + tooltipPlacement={tooltipPlacement} + getPopupContainer={getPopupContainer} + getCurrentSize={this.getCurrentSize} + formatPainterActive={this.state.formatPainterActive} + saveSelectionFormat={this.handleSaveSelectionFormat} + unsaveSelectionFormat={this.handleUnsaveSelectionFormat} + /> + (this.reactQuillRef = el)} + bounds={this.editorCtner} + className={"editor-body"} + modules={moduleOpts} + placeholder={Locale.placeholder} + onChange={this.handleChange} + onSelectionChange={this.handleSelectionChange} + /> + {loading ? ( + + ) : null} +
    + ) + } + } +
    ); } } diff --git a/source/components/RichEditor/src/interface.ts b/source/components/RichEditor/src/interface.ts index 2c5add758..1bca81d61 100644 --- a/source/components/RichEditor/src/interface.ts +++ b/source/components/RichEditor/src/interface.ts @@ -35,7 +35,7 @@ export interface RichEditorProps { value?: string; insertImageTip?: string | Element; insertAttachmentTip?: string | Element; - insertVideoTip?: string | Element; + insertVideoTip?: boolean | string | Element; insertLinkTip?: string | Element; popoverPlacement?: TooltipPlacement; tooltipPlacement?: TooltipPlacement; diff --git a/source/components/RichEditor/src/toolbar.tsx b/source/components/RichEditor/src/toolbar.tsx index 08d1cbb57..b13e1a75d 100644 --- a/source/components/RichEditor/src/toolbar.tsx +++ b/source/components/RichEditor/src/toolbar.tsx @@ -17,6 +17,8 @@ declare module 'react' { } import { CustomToolbarProps, EmojiInferface, CustomToolbarState } from './interface' +import ConfigConsumer from '../../Config/Consumer'; +import { LocaleProperties } from '../../Locale'; const TabPane = Tabs.TabPane; const COLORS: string[] = [ @@ -156,9 +158,10 @@ class CustomToolbar extends PureComponent private curSizeList: Array private curInsertValueList: Array + private Locale: LocaleProperties['RichEditor'] public toolbarCtner: HTMLDivElement - constructor(props: CustomToolbarProps) { + constructor (props: CustomToolbarProps) { super(props); this.defaultSizes = ['32px', '24px', '20px', '18px', '16px', '14px', '13px', '12px']; @@ -206,6 +209,7 @@ class CustomToolbar extends PureComponent ); - }) :
    暂无数据
    + }) :
    {Locale.temporarilyNoData}
    } ); @@ -337,7 +341,7 @@ class CustomToolbar extends PureComponent; - tooltip = '超链接'; + tooltip = Locale.hyperlinks || 'link'; break; } case 'bold': { @@ -346,7 +350,7 @@ class CustomToolbar extends PureComponent; - tooltip = '粗体'; + tooltip = Locale.bold; break; } case 'italic': { @@ -355,7 +359,7 @@ class CustomToolbar extends PureComponent; - tooltip = '斜体'; + tooltip = Locale.italic; break; } case 'underline': { @@ -364,7 +368,7 @@ class CustomToolbar extends PureComponent; - tooltip = '下划线'; + tooltip = Locale.underline; break; } case 'color': { @@ -405,7 +409,7 @@ class CustomToolbar extends PureComponent
    @@ -430,23 +434,23 @@ class CustomToolbar extends PureComponent //
    // ); - tooltip = '文字颜色'; + tooltip = Locale.fontColor; break; } case 'align': { if (typeof mValue === 'string') { let alignIconType = 'richeditor-align-lef'; - tooltip = '居左'; + tooltip = Locale.alignLeft; if (mValue == 'right') { alignIconType = 'richeditor-align-rig'; - tooltip = '居右'; + tooltip = Locale.alignRight; } else if (mValue == 'center') { alignIconType = 'richeditor-align-mid'; - tooltip = '居中'; + tooltip = Locale.alignCenter; } else if (mValue == 'justify') { alignIconType = 'richeditor-align-all'; - tooltip = '两端对齐'; + tooltip = Locale.alignJustified; } const alignCls = classNames('action ql-align', { @@ -477,11 +481,11 @@ class CustomToolbar extends PureComponent +
    {defaultEmojis} @@ -548,7 +552,7 @@ class CustomToolbar extends PureComponent
    @@ -559,7 +563,7 @@ class CustomToolbar extends PureComponent ); - tooltip = '插入表情'; + tooltip = Locale.insertEmoji; break; } case 'image': { @@ -568,7 +572,7 @@ class CustomToolbar extends PureComponent; - tooltip = '插入图片'; + tooltip = Locale.insertPicture; break; } case 'attachment': { @@ -577,7 +581,7 @@ class CustomToolbar extends PureComponent; - tooltip = '插入附件'; + tooltip = Locale.insertAttachment; break; } case 'size': { @@ -629,7 +633,7 @@ class CustomToolbar extends PureComponent
    @@ -639,7 +643,7 @@ class CustomToolbar extends PureComponent ); - tooltip = '文字大小'; + tooltip = Locale.fontSize; break; } @@ -650,7 +654,7 @@ class CustomToolbar extends PureComponent; - tooltip = '清除格式'; + tooltip = Locale.clearFormat; break; } case 'formatPainter': { @@ -661,30 +665,30 @@ class CustomToolbar extends PureComponent; - tooltip = '格式刷'; + tooltip = Locale.brushFormat; break; } case 'strike': { value =
    + + { + (Locale: LocaleProperties['RichEditor']) => { + this.Locale = Locale; + return ( +
    this.toolbarCtner = node} style={style}> + {this.genToolbar(toolbar)} +
    + ) + } + } + +
    ); } } diff --git a/source/components/VideoViewer/component/DownLoad.js b/source/components/VideoViewer/component/DownLoad.js index ffaf7da62..a2c30a73e 100644 --- a/source/components/VideoViewer/component/DownLoad.js +++ b/source/components/VideoViewer/component/DownLoad.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import Icon from '../../Icon/index.tsx'; import Tooltip from '../../Tooltip/index.tsx'; +import ConfigConsumer from '../../Config/Consumer'; export default class DownLoad extends Component { static propTypes = { @@ -15,16 +16,28 @@ export default class DownLoad extends Component { } render() { - const { vjsComponent,prefixCls } = this.props; + const { vjsComponent, prefixCls } = this.props; const src = vjsComponent.options_.playerOptions.downloadSrc; return ( -
    + + { + (Locale) => { + return ( +
    + + {Locale.download}} + getPopupContainer={(e) => e.parentNode}> + + + + +
    + ); + } + } + +
    ); } } diff --git a/source/components/VideoViewer/component/ErrorDisplay.js b/source/components/VideoViewer/component/ErrorDisplay.js index 360663ba4..47f06ebb2 100644 --- a/source/components/VideoViewer/component/ErrorDisplay.js +++ b/source/components/VideoViewer/component/ErrorDisplay.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Icon from '../../Icon/index.tsx'; import { MEDIA_ERROR } from '../constant'; +import ConfigConsumer from '../../Config/Consumer'; export default class ErrorDisplay extends Component { static propTypes = { @@ -56,22 +57,31 @@ export default class ErrorDisplay extends Component { const errorMessage = mediaError !== null ? MEDIA_ERROR[mediaError.code] : ''; return ( -
    + { - mediaError ? -
    - {errorMessage} - { - mediaError.code === 2 ? - - 重试 - - : null - } -
    - : null + (Locale) => { + return ( +
    + { + mediaError ? +
    + {errorMessage} + { + mediaError.code === 2 ? + + + {Locale.retry} + + : null + } +
    + : null + } +
    + ); + } } -
    + ); } } diff --git a/source/components/VideoViewer/component/FullScreen.js b/source/components/VideoViewer/component/FullScreen.js index 6130b92bc..f6d686b47 100644 --- a/source/components/VideoViewer/component/FullScreen.js +++ b/source/components/VideoViewer/component/FullScreen.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import Icon from '../../Icon/index.tsx'; import Tooltip from '../../Tooltip/index.tsx'; +import ConfigConsumer from '../../Config/Consumer'; export default class FullScreen extends Component { static propTypes = { @@ -42,10 +43,10 @@ export default class FullScreen extends Component { handleClick = () => { const { isFullScreen } = this.state; - if(!isFullScreen) { + if (!isFullScreen) { this.player.requestFullscreen(); - }else{ + } else { this.player.exitFullscreen(); } @@ -57,18 +58,31 @@ export default class FullScreen extends Component { render() { const { prefixCls } = this.props; const { isFullScreen } = this.state; - const title = !isFullScreen ? '全屏' : '取消全屏'; return ( -
    this.handleClick()}> - {title}} getPopupContainer={(e) => e.parentNode} > - - { - !isFullScreen ? : - } - - -
    + + { + (Locale) => { + const title = !isFullScreen ? Locale.fullScreen : Locale.cancelFullScreen; + return ( +
    this.handleClick()} + > + {title}} + getPopupContainer={(e) => e.parentNode} > + + { + !isFullScreen ? : + } + + +
    + ); + } + } +
    ); } } diff --git a/source/components/index.js b/source/components/index.js index 25af74589..60395aabf 100644 --- a/source/components/index.js +++ b/source/components/index.js @@ -62,3 +62,4 @@ export { default as Trend } from './Trend/index.tsx'; export { default as Upload } from './Upload/index.tsx'; export { default as VideoViewer } from './VideoViewer/index.tsx'; export { default as Guide } from './Guide/index.tsx'; +export { default as Config } from './Config/index.ts';