diff --git a/components/date-picker/PickerMixin.jsx b/components/date-picker/PickerMixin.jsx index 837fba5e01..f936dabe69 100644 --- a/components/date-picker/PickerMixin.jsx +++ b/components/date-picker/PickerMixin.jsx @@ -1,14 +1,23 @@ +import React from 'react'; import objectAssign from 'object-assign'; import defaultLocale from './locale/zh_CN'; import DateTimeFormat from 'gregorian-calendar-format'; import GregorianCalendar from 'gregorian-calendar'; export default { + contextTypes: { + antLocale: React.PropTypes.object, + }, + getLocale() { + let locale = defaultLocale; + if (this.context.antLocale && this.context.antLocale.DatePicker) { + locale = this.context.antLocale.DatePicker; + } // 统一合并为完整的 Locale - let locale = objectAssign({}, defaultLocale, this.props.locale); - locale.lang = objectAssign({}, defaultLocale.lang, this.props.locale.lang); - return locale; + const result = objectAssign({}, locale, this.props.locale); + result.lang = objectAssign({}, locale.lang, this.props.locale.lang); + return result; }, getFormatter() { diff --git a/components/locale-provider/demo/all.md b/components/locale-provider/demo/all.md new file mode 100644 index 0000000000..6a5e5c6233 --- /dev/null +++ b/components/locale-provider/demo/all.md @@ -0,0 +1,131 @@ +# 所有组件 + +- order: 2 + +此处列出 Ant Design 中需要国际化支持的组件,你可以在演示里切换语言。 + +--- + +````jsx +import { LocaleProvider, Pagination, DatePicker, TimePicker, + Popconfirm, Table, Modal, Button, Select, Transfer } from 'antd'; +import enUS from 'antd/lib/locale-provider/en_US'; +const Option = Select.Option; + +const columns = [{ + title: 'Name', + dataIndex: 'name', + filters: [{ + text: 'filter1', + value: 'filter1', + }], +}, { + title: 'Age', + dataIndex: 'age', +}]; + +const Page = React.createClass({ + getInitialState() { + return { + visible: false, + }; + }, + showModal() { + this.setState({ visible: true }); + }, + hideModal() { + this.setState({ visible: false }); + }, + render() { + const info = () => { + Modal.info({ + title: 'some info', + content: 'some info', + }); + }; + const confirm = () => { + Modal.confirm({ + title: 'some info', + content: 'some info', + }); + }; + return ( +
+
+ +
+
+ + + + + + + Click to confirm + +
+
+ item.title} /> +
+
+ + + +

Locale Modal

+
+ + ); + } +}); + +const App = React.createClass({ + getInitialState() { + return { + locale: enUS, + }; + }, + changeLocale(locale) { + this.setState({ locale }); + }, + render() { + return ( +
+
+ Change locale of components: + +
+ +
+ ); + } +}); + +ReactDOM.render(, mountNode); +```` + +````css +.locale-components { + border-top: 1px solid #d9d9d9; + padding-top: 16px; +} + +.example { + margin: 16px 0; +} + +.example > * { + margin-right: 8px; +} + +.change-locale { + margin-bottom: 16px; +} +```` diff --git a/components/locale-provider/demo/basic.md b/components/locale-provider/demo/basic.md new file mode 100644 index 0000000000..8137c42259 --- /dev/null +++ b/components/locale-provider/demo/basic.md @@ -0,0 +1,28 @@ +# 国际化 + +- order: 1 + +用 `LocaleProvider` 包裹你的应用,并引用对应的语言包。 + +--- + +````jsx +import { Pagination, LocaleProvider } from 'antd'; +import enUS from 'antd/lib/locale-provider/en_US'; + +const App = React.createClass({ + render() { + return ( +
+ +
+ ); + } +}); + +ReactDOM.render( + + + +, mountNode); +```` diff --git a/components/locale-provider/en_US.js b/components/locale-provider/en_US.js new file mode 100644 index 0000000000..9e5b243e76 --- /dev/null +++ b/components/locale-provider/en_US.js @@ -0,0 +1,26 @@ +module.exports = { + Pagination: require('rc-pagination/lib/locale/en_US'), + DatePicker: require('../date-picker/locale/en_US'), + TimePicker: require('../time-picker/locale/en_US'), + Table: { + filterTitle: 'Filter Menu', + filterConfirm: 'OK', + filterReset: 'Reset', + emptyText: 'No Data', + }, + Modal: { + okText: 'OK', + cancelText: 'Cancel', + justOkText: 'OK', + }, + Popconfirm: { + okText: 'OK', + cancelText: 'Cancel', + }, + Transfer: { + notFoundContent: 'Not Found', + searchPlaceholder: 'Search here', + itemUnit: 'item', + itemsUnit: 'items', + }, +}; diff --git a/components/locale-provider/index.jsx b/components/locale-provider/index.jsx new file mode 100644 index 0000000000..9588eb5eec --- /dev/null +++ b/components/locale-provider/index.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { changeConfirmLocale } from '../modal/confirm'; + +export default class LocaleProvider extends React.Component { + getChildContext() { + return { + antLocale: this.props.locale, + }; + } + componentDidMount() { + this.componentDidUpdate(); + } + componentDidUpdate() { + const { locale } = this.props; + changeConfirmLocale(locale && locale.Modal); + } + componentWillUnMount() { + changeConfirmLocale(); + } + render() { + return React.Children.only(this.props.children); + } +} + +LocaleProvider.childContextTypes = { + antLocale: React.PropTypes.object, +}; + +LocaleProvider.propTypes = { + locale: React.PropTypes.object, +}; diff --git a/components/locale-provider/index.md b/components/locale-provider/index.md new file mode 100644 index 0000000000..dfe6ee1c59 --- /dev/null +++ b/components/locale-provider/index.md @@ -0,0 +1,36 @@ +# LocaleProvider + +- category: Components +- chinese: 国际化 +- cols: 1 + +--- + +为组件内建文案提供统一的国际化支持。 + +## 使用 + +LocaleProvider 使用 React 的 [context](https://facebook.github.io/react/docs/context.html) 特性,只需在应用外围包裹一次即可全局生效。 + + +```jsx +import enUS from 'antd/lib/locale-provider/en_US'; + +... + +return ; +``` + +### Add a language + +We supply an English locale package by now. Other language users can custumize your locale package as [en_US](https://github.com/ant-design/ant-design/blob/26b1f37392a278285aec6c573b99c6feea09e218/components/locale-provider/en_US.js) and ask a pull request to us. + +### 其他国际化需求 + +本模块仅用于组件的内建文案,若有业务文案的国际化需求,建议使用 [react-intl](https://github.com/yahoo/react-intl),可参考示例:[Intl demo 1](http://github.com/ant-design/intl-example) 和 [Intl demo 2](http://yiminghe.me/learning-react/examples/react-intl.html?locale=en-US)。 + +## API + +| 参数 | 说明 | 类型 | 默认值 | +|--------|----------------|------------------|--------------| +| locale | 语言包配置,语言包可到 `antd/lib/locale-provider/` 目录下寻找 | Object | - | diff --git a/components/modal/Modal.jsx b/components/modal/Modal.jsx index 7bf8f2a311..bd2274306b 100644 --- a/components/modal/Modal.jsx +++ b/components/modal/Modal.jsx @@ -14,8 +14,6 @@ let AntModal = React.createClass({ prefixCls: 'ant-modal', onOk: noop, onCancel: noop, - okText: '确定', - cancelText: '取消', width: 520, transitionName: 'zoom', maskAnimation: 'fade', @@ -24,6 +22,10 @@ let AntModal = React.createClass({ }; }, + contextTypes: { + antLocale: React.PropTypes.object, + }, + handleCancel(e) { this.props.onCancel(e); }, @@ -52,19 +54,26 @@ let AntModal = React.createClass({ render() { let props = this.props; + + let { okText, cancelText } = props; + if (this.context.antLocale && this.context.antLocale.Modal) { + okText = okText || this.context.antLocale.Modal.okText; + cancelText = cancelText || this.context.antLocale.Modal.cancelText; + } + let defaultFooter = [ , ]; let footer = props.footer || defaultFooter; diff --git a/components/modal/confirm.jsx b/components/modal/confirm.jsx index 28f95931db..10a1c2f0c4 100644 --- a/components/modal/confirm.jsx +++ b/components/modal/confirm.jsx @@ -5,8 +5,24 @@ import Icon from '../icon'; import Button from '../button'; import objectAssign from 'object-assign'; -export default function (config) { - const props = objectAssign({}, config || {}); +const defaultLocale = { + okText: '确定', + cancelText: '取消', + justOkText: '知道了', +}; + +let runtimeLocale = { ...defaultLocale }; + +export function changeConfirmLocale(newLocale) { + if (newLocale) { + objectAssign(runtimeLocale, newLocale); + } else { + runtimeLocale = { ...defaultLocale }; + } +} + +export default function confirm(config) { + const props = objectAssign({}, config); let div = document.createElement('div'); document.body.appendChild(div); @@ -22,12 +38,13 @@ export default function (config) { props.okCancel = true; } - props.okText = props.okText || (props.okCancel ? '确定' : '知道了'); - props.cancelText = props.cancelText || '取消'; + props.okText = props.okText || + (props.okCancel ? runtimeLocale.okText : runtimeLocale.justOkText); + props.cancelText = props.cancelText || runtimeLocale.cancelText; function close() { d.setState({ - visible: false + visible: false, }); ReactDOM.unmountComponentAtNode(div); div.parentNode.removeChild(div); diff --git a/components/pagination/demo/locale.md b/components/pagination/demo/locale.md deleted file mode 100644 index d617906b86..0000000000 --- a/components/pagination/demo/locale.md +++ /dev/null @@ -1,16 +0,0 @@ -# 国际化 - -- order: 7 - -通过 `locale` 配置时区、语言等, 默认支持 en_US, zh_CN - ---- - -````jsx -import { Pagination } from 'antd'; -import enUS from 'antd/lib/pagination/locale/en_US'; - -ReactDOM.render( - , - mountNode); -```` diff --git a/components/pagination/index.jsx b/components/pagination/index.jsx index 1bbf3ac0cf..725d20a0a5 100644 --- a/components/pagination/index.jsx +++ b/components/pagination/index.jsx @@ -16,6 +16,13 @@ class AntPagination extends React.Component { let className = this.props.className; let selectComponentClass = Select; + let locale; + if (this.context.antLocale && this.context.antLocale.Pagination) { + locale = this.context.antLocale.Pagination; + } else { + locale = this.props.locale; + } + if (this.props.size === 'small') { className += ' mini'; selectComponentClass = MiniSelect; @@ -25,6 +32,7 @@ class AntPagination extends React.Component { ); } @@ -36,4 +44,8 @@ AntPagination.defaultProps = { prefixCls: 'ant-pagination', }; +AntPagination.contextTypes = { + antLocale: React.PropTypes.object, +}; + export default AntPagination; diff --git a/components/popconfirm/index.jsx b/components/popconfirm/index.jsx index 79e21f0747..887ea73d59 100644 --- a/components/popconfirm/index.jsx +++ b/components/popconfirm/index.jsx @@ -34,11 +34,12 @@ export default React.createClass({ overlayStyle: {}, onConfirm: noop, onCancel: noop, - okText: '确定', - cancelText: '取消', onVisibleChange() {}, }; }, + contextTypes: { + antLocale: React.PropTypes.object, + }, componentWillReceiveProps(nextProps) { if ('visible' in nextProps) { this.setState({ visible: nextProps.visible }); @@ -62,7 +63,12 @@ export default React.createClass({ } }, render() { - const { title, okText, cancelText, placement, overlayStyle, trigger, ...restProps } = this.props; + const { title, placement, overlayStyle, trigger, ...restProps } = this.props; + let { okText, cancelText } = this.props; + if (this.context.antLocale && this.context.antLocale.Popconfirm) { + okText = okText || this.context.antLocale.Popconfirm.okText; + cancelText = cancelText || this.context.antLocale.Popconfirm.cancelText; + } const overlay = (
@@ -71,8 +77,8 @@ export default React.createClass({ {title}

- - + +
diff --git a/components/table/index.jsx b/components/table/index.jsx index 32ffc4d205..dc82598d12 100644 --- a/components/table/index.jsx +++ b/components/table/index.jsx @@ -74,6 +74,10 @@ let AntTable = React.createClass({ locale: React.PropTypes.object, }, + contextTypes: { + antLocale: React.PropTypes.object, + }, + getDefaultSelection() { if (!this.props.rowSelection || !this.props.rowSelection.getCheckboxProps) { return []; @@ -83,6 +87,14 @@ let AntTable = React.createClass({ .map((record, rowIndex) => this.getRecordKey(record, rowIndex)); }, + getLocale() { + let locale = {}; + if (this.context.antLocale && this.context.antLocale.Table) { + locale = this.context.antLocale.Table; + } + return objectAssign({}, defaultLocale, locale, this.props.locale); + }, + componentWillReceiveProps(nextProps) { if (('pagination' in nextProps) && nextProps.pagination !== false) { this.setState({ @@ -405,7 +417,7 @@ let AntTable = React.createClass({ }, renderColumnsDropdown(columns) { - let locale = objectAssign({}, defaultLocale, this.props.locale); + const locale = this.getLocale(); return columns.map((originColumn, i) => { let column = objectAssign({}, originColumn); let key = this.getColumnKey(column, i); @@ -563,7 +575,7 @@ let AntTable = React.createClass({ const data = this.getCurrentPageData(); let columns = this.renderRowSelection(); const expandIconAsCell = this.props.expandedRowRender && this.props.expandIconAsCell !== false; - const locale = objectAssign({}, defaultLocale, this.props.locale); + const locale = this.getLocale(); const classString = classNames({ [`ant-table-${this.props.size}`]: true, diff --git a/components/time-picker/index.jsx b/components/time-picker/index.jsx index aecc2e727f..5240390c4d 100644 --- a/components/time-picker/index.jsx +++ b/components/time-picker/index.jsx @@ -27,6 +27,10 @@ const AntTimePicker = React.createClass({ }; }, + contextTypes: { + antLocale: React.PropTypes.object, + }, + getFormatter() { return new DateTimeFormat(this.props.format); }, @@ -68,14 +72,19 @@ const AntTimePicker = React.createClass({ }, getLocale() { + let locale = defaultLocale; + if (this.context.antLocale && this.context.antLocale.TimePicker) { + locale = this.context.antLocale.TimePicker; + } // 统一合并为完整的 Locale - return objectAssign({}, defaultLocale, this.props.locale); + return objectAssign({}, locale, this.props.locale); }, render() { + const locale = this.getLocale(); const props = objectAssign({}, this.props); props.placeholder = ('placeholder' in this.props) - ? props.placeholder : this.getLocale().placeholder; + ? props.placeholder : locale.placeholder; if (props.defaultValue) { props.defaultValue = this.parseTimeFromValue(props.defaultValue); } else { @@ -99,7 +108,7 @@ const AntTimePicker = React.createClass({ diff --git a/components/transfer/index.jsx b/components/transfer/index.jsx index eef2f2b9e6..f57bfaf72c 100644 --- a/components/transfer/index.jsx +++ b/components/transfer/index.jsx @@ -223,8 +223,6 @@ Transfer.defaultProps = { titles: ['源列表', '目的列表'], operations: [], showSearch: false, - searchPlaceholder: '请输入搜索内容', - notFoundContent: 'Not Found', body: noop, footer: noop, }; diff --git a/components/transfer/index.md b/components/transfer/index.md index 51326c12f9..4f28a8e81e 100644 --- a/components/transfer/index.md +++ b/components/transfer/index.md @@ -27,6 +27,6 @@ | titles | 标题集合,顺序从左至右 | Array | ['源列表', '目的列表'] | | operations | 操作文案集合,顺序从上至下 | Array | [] | | showSearch | 是否显示搜索框 | Boolean | false | -| searchPlaceholder | 搜索框的默认值 | String | 请输入搜索的内容 | -| notFoundContent | 当列表为空时显示的内容 | React.node | 'Not Found' | +| searchPlaceholder | 搜索框的默认值 | String | '请输入搜索内容' | +| notFoundContent | 当列表为空时显示的内容 | React.node | '列表为空' | | footer | 底部渲染函数 | Function(props) | | | diff --git a/components/transfer/list.jsx b/components/transfer/list.jsx index 0b34cc2913..e22c0f8056 100644 --- a/components/transfer/list.jsx +++ b/components/transfer/list.jsx @@ -69,8 +69,10 @@ class TransferList extends Component { } render() { - const { prefixCls, dataSource, titleText, filter, checkedKeys, notFoundContent, - checkStatus, body, footer, showSearch, searchPlaceholder } = this.props; + const { prefixCls, dataSource, titleText, filter, checkedKeys, + checkStatus, body, footer, showSearch } = this.props; + + let { searchPlaceholder, notFoundContent } = this.props; // Custom Layout const footerDom = footer({ ...this.props }); @@ -95,6 +97,18 @@ class TransferList extends Component { ); }); + let unit = '条'; + if (this.context.antLocale && + this.context.antLocale.Transfer) { + unit = dataSource.length > 1 + ? this.context.antLocale.Transfer.itemsUnit + : this.context.antLocale.Transfer.itemUnit; + searchPlaceholder = searchPlaceholder + || this.context.antLocale.Transfer.searchPlaceholder; + notFoundContent = notFoundContent + || this.context.antLocale.Transfer.notFoundContent; + } + return (
@@ -103,8 +117,15 @@ class TransferList extends Component { checked: checkStatus === 'all', checkPart: checkStatus === 'part', checkable: - })}{(checkedKeys.length > 0 ? `${checkedKeys.length}/` : '') + dataSource.length} 条 - {titleText} + })} + + + {(checkedKeys.length > 0 ? `${checkedKeys.length}/` : '') + dataSource.length} {unit} + + + {titleText} + +
{ bodyDom ||
@@ -112,13 +133,15 @@ class TransferList extends Component {
: null } - {showItems.length > 0 ? showItems :
{notFoundContent}
} + {showItems.length > 0 + ? showItems + :
{notFoundContent || '列表为空'}
}
} { footerDom ?
@@ -133,7 +156,6 @@ TransferList.defaultProps = { dataSource: [], titleText: '', showSearch: false, - searchPlaceholder: '', handleFilter: noop, handleSelect: noop, handleSelectAll: noop, @@ -158,4 +180,8 @@ TransferList.propTypes = { footer: PropTypes.func, }; +TransferList.contextTypes = { + antLocale: React.PropTypes.object, +}; + export default TransferList; diff --git a/index.js b/index.js index 8d81517e75..695ace85ac 100644 --- a/index.js +++ b/index.js @@ -46,6 +46,7 @@ const antd = { Transfer: require('./components/transfer'), Cascader: require('./components/cascader'), Card: require('./components/card'), + LocaleProvider: require('./components/locale-provider'), }; module.exports = antd; diff --git a/scripts/demo.js b/scripts/demo.js index 62278e0bd6..9aad398c00 100644 --- a/scripts/demo.js +++ b/scripts/demo.js @@ -61,6 +61,8 @@ antd.Pagination.locale = { zh_CN: require('../components/pagination/locale/zh_CN'), }; +antd.LocaleProvider['en_US'] = require('../components/locale-provider/en_US'), + InstantClickChangeFns.push(function () { // auto complete for components var Select = antd.Select;