diff options
-rw-r--r-- | web/react/components/create_comment.jsx | 52 | ||||
-rw-r--r-- | web/react/components/file_upload.jsx | 38 | ||||
-rw-r--r-- | web/react/components/file_upload_overlay.jsx | 9 | ||||
-rw-r--r-- | web/react/components/msg_typing.jsx | 42 | ||||
-rw-r--r-- | web/react/components/rhs_comment.jsx | 41 | ||||
-rw-r--r-- | web/react/components/rhs_header_post.jsx | 11 | ||||
-rw-r--r-- | web/react/components/rhs_root_post.jsx | 29 | ||||
-rw-r--r-- | web/react/components/search_bar.jsx | 39 | ||||
-rw-r--r-- | web/react/components/search_results.jsx | 32 | ||||
-rw-r--r-- | web/react/components/search_results_header.jsx | 17 | ||||
-rw-r--r-- | web/react/components/search_results_item.jsx | 24 | ||||
-rw-r--r-- | web/react/components/sidebar_right_menu.jsx | 68 | ||||
-rw-r--r-- | web/react/components/textbox.jsx | 20 | ||||
-rw-r--r-- | web/react/components/user_profile.jsx | 8 | ||||
-rw-r--r-- | web/static/i18n/en.json | 45 | ||||
-rw-r--r-- | web/static/i18n/es.json | 47 |
16 files changed, 446 insertions, 76 deletions
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index aa7ab6a7b..1b552838a 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -16,10 +16,32 @@ import FilePreview from './file_preview.jsx'; import * as Utils from '../utils/utils.jsx'; import Constants from '../utils/constants.jsx'; + +import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; + const ActionTypes = Constants.ActionTypes; const KeyCodes = Constants.KeyCodes; -export default class CreateComment extends React.Component { +const holders = defineMessages({ + commentLength: { + id: 'create_comment.commentLength', + defaultMessage: 'Comment length must be less than {max} characters.' + }, + comment: { + id: 'create_comment.comment', + defaultMessage: 'Add Comment' + }, + addComment: { + id: 'create_comment.addComment', + defaultMessage: 'Add a comment...' + }, + commentTitle: { + id: 'create_comment.commentTitle', + defaultMessage: 'Comment' + } +}); + +class CreateComment extends React.Component { constructor(props) { super(props); @@ -93,7 +115,7 @@ export default class CreateComment extends React.Component { } if (post.message.length > Constants.CHARACTER_LIMIT) { - this.setState({postError: `Comment length must be less than ${Constants.CHARACTER_LIMIT} characters.`}); + this.setState({postError: this.props.intl.formatMessage(holders.commentLength, {max: Constants.CHARACTER_LIMIT})}); return; } @@ -189,7 +211,7 @@ export default class CreateComment extends React.Component { AppDispatcher.handleViewAction({ type: ActionTypes.RECIEVED_EDIT_POST, refocusId: '#reply_textbox', - title: 'Comment', + title: this.props.intl.formatMessage(holders.commentTitle), message: lastPost.message, postId: lastPost.id, channelId: lastPost.channel_id, @@ -305,14 +327,23 @@ export default class CreateComment extends React.Component { let uploadsInProgressText = null; if (this.state.uploadsInProgress.length > 0) { uploadsInProgressText = ( - <span - className='pull-right post-right-comments-upload-in-progress' - > - {this.state.uploadsInProgress.length === 1 ? 'File uploading' : 'Files uploading'} + <span className='pull-right post-right-comments-upload-in-progress'> + {this.state.uploadsInProgress.length === 1 ? ( + <FormattedMessage + id='create_comment.file' + defaultMessage='File uploading' + /> + ) : ( + <FormattedMessage + id='create_comment.files' + defaultMessage='Files uploading' + /> + )} </span> ); } + const {formatMessage} = this.props.intl; return ( <form onSubmit={this.handleSubmit}> <div className='post-create'> @@ -326,7 +357,7 @@ export default class CreateComment extends React.Component { onKeyPress={this.commentMsgKeyPress} onKeyDown={this.handleKeyDown} messageText={this.state.messageText} - createMessage='Add a comment...' + createMessage={formatMessage(holders.addComment)} initialText='' supportsCommands={false} id='reply_textbox' @@ -351,7 +382,7 @@ export default class CreateComment extends React.Component { <input type='button' className='btn btn-primary comment-btn pull-right' - value='Add Comment' + value={formatMessage(holders.comment)} onClick={this.handleSubmit} /> {uploadsInProgressText} @@ -366,6 +397,9 @@ export default class CreateComment extends React.Component { } CreateComment.propTypes = { + intl: intlShape.isRequired, channelId: React.PropTypes.string.isRequired, rootId: React.PropTypes.string.isRequired }; + +export default injectIntl(CreateComment);
\ No newline at end of file diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx index 7e6cc2942..626dbc5b3 100644 --- a/web/react/components/file_upload.jsx +++ b/web/react/components/file_upload.jsx @@ -6,7 +6,28 @@ import Constants from '../utils/constants.jsx'; import ChannelStore from '../stores/channel_store.jsx'; import * as Utils from '../utils/utils.jsx'; -export default class FileUpload extends React.Component { +import {intlShape, injectIntl, defineMessages} from 'mm-intl'; + +const holders = defineMessages({ + limited: { + id: 'file_upload.limited', + defaultMessage: 'Uploads limited to {count} files maximum. Please use additional posts for more files.' + }, + filesAbove: { + id: 'file_upload.filesAbove', + defaultMessage: 'Files above {max}MB could not be uploaded: {filenames}' + }, + fileAbove: { + id: 'file_upload.fileAbove', + defaultMessage: 'File above {max}MB could not be uploaded: {filename}' + }, + pasted: { + id: 'file_upload.pasted', + defaultMessage: 'Image Pasted at ' + } +}); + +class FileUpload extends React.Component { constructor(props) { super(props); @@ -74,14 +95,15 @@ export default class FileUpload extends React.Component { numUploads += 1; } + const {formatMessage} = this.props.intl; if (files.length > uploadsRemaining) { - this.props.onUploadError(`Uploads limited to ${Constants.MAX_UPLOAD_FILES} files maximum. Please use additional posts for more files.`); + this.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES})); } else if (tooLargeFiles.length > 1) { var tooLargeFilenames = tooLargeFiles.map((file) => file.name).join(', '); - this.props.onUploadError(`Files above ${Constants.MAX_FILE_SIZE / 1000000}MB could not be uploaded: ${tooLargeFilenames}`); + this.props.onUploadError(formatMessage(holders.filesAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), files: tooLargeFilenames})); } else if (tooLargeFiles.length > 0) { - this.props.onUploadError(`File above ${Constants.MAX_FILE_SIZE / 1000000}MB could not be uploaded: ${tooLargeFiles[0].name}`); + this.props.onUploadError(formatMessage(holders.fileAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), file: tooLargeFiles[0].name})); } } @@ -106,6 +128,7 @@ export default class FileUpload extends React.Component { componentDidMount() { var inputDiv = ReactDOM.findDOMNode(this.refs.input); var self = this; + const {formatMessage} = this.props.intl; if (this.props.postType === 'post') { $('.row.main').dragster({ @@ -184,7 +207,7 @@ export default class FileUpload extends React.Component { var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - self.props.getFileCount(ChannelStore.getCurrentId()), numItems); if (numItems > numToUpload) { - self.props.onUploadError('Uploads limited to ' + Constants.MAX_UPLOAD_FILES + ' files maximum. Please use additional posts for more files.'); + self.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES})); } for (var i = 0; i < items.length && i < numToUpload; i++) { @@ -218,7 +241,7 @@ export default class FileUpload extends React.Component { min = String(d.getMinutes()); } - var name = 'Image Pasted at ' + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext; + var name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext; formData.append('files', file, name); formData.append('client_ids', clientId); @@ -296,6 +319,7 @@ export default class FileUpload extends React.Component { } FileUpload.propTypes = { + intl: intlShape.isRequired, onUploadError: React.PropTypes.func, getFileCount: React.PropTypes.func, onFileUpload: React.PropTypes.func, @@ -304,3 +328,5 @@ FileUpload.propTypes = { channelId: React.PropTypes.string, postType: React.PropTypes.string }; + +export default injectIntl(FileUpload);
\ No newline at end of file diff --git a/web/react/components/file_upload_overlay.jsx b/web/react/components/file_upload_overlay.jsx index dbba00022..497d5aee2 100644 --- a/web/react/components/file_upload_overlay.jsx +++ b/web/react/components/file_upload_overlay.jsx @@ -1,6 +1,8 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import {FormattedMessage} from 'mm-intl'; + export default class FileUploadOverlay extends React.Component { render() { var overlayClass = 'file-overlay hidden'; @@ -19,7 +21,12 @@ export default class FileUploadOverlay extends React.Component { src='/static/images/filesOverlay.png' alt='Files' /> - <span><i className='fa fa-upload'></i>{'Drop a file to upload it.'}</span> + <span><i className='fa fa-upload'></i> + <FormattedMessage + id='upload_overlay.info' + defaultMessage='Drop a file to upload it.' + /> + </span> <img className='overlay__logo' src='/static/images/logoWhite.png' diff --git a/web/react/components/msg_typing.jsx b/web/react/components/msg_typing.jsx index 35a832875..b95b06260 100644 --- a/web/react/components/msg_typing.jsx +++ b/web/react/components/msg_typing.jsx @@ -5,9 +5,19 @@ import SocketStore from '../stores/socket_store.jsx'; import UserStore from '../stores/user_store.jsx'; import Constants from '../utils/constants.jsx'; + +import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; + const SocketEvents = Constants.SocketEvents; -export default class MsgTyping extends React.Component { +const holders = defineMessages({ + someone: { + id: 'msg_typing.someone', + defaultMessage: 'Someone' + } +}); + +class MsgTyping extends React.Component { constructor(props) { super(props); @@ -44,10 +54,10 @@ export default class MsgTyping extends React.Component { } onChange(msg) { - let username = 'Someone'; + let username = this.props.intl.formatMessage(holders.someone); if (msg.action === SocketEvents.TYPING && - this.props.channelId === msg.channel_id && - this.props.parentId === msg.props.parent_id) { + this.props.channelId === msg.channel_id && + this.props.parentId === msg.props.parent_id) { if (UserStore.hasProfile(msg.user_id)) { username = UserStore.getProfile(msg.user_id).username; } @@ -80,11 +90,28 @@ export default class MsgTyping extends React.Component { text = ''; break; case 1: - text = users[0] + ' is typing...'; + text = ( + <FormattedMessage + id='msg_typing.isTyping' + defaultMessage='{user} is typing...' + values={{ + user: users[0] + }} + /> + ); break; default: { const last = users.pop(); - text = users.join(', ') + ' and ' + last + ' are typing...'; + text = ( + <FormattedMessage + id='msg_typing.areTyping' + defaultMessage='{users} and {last} are typing...' + vaues={{ + users: users.join(', '), + last: last + }} + /> + ); break; } } @@ -100,6 +127,9 @@ export default class MsgTyping extends React.Component { } MsgTyping.propTypes = { + intl: intlShape.isRequired, channelId: React.PropTypes.string, parentId: React.PropTypes.string }; + +export default injectIntl(MsgTyping);
\ No newline at end of file diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx index 7aae5177e..1addebbe4 100644 --- a/web/react/components/rhs_comment.jsx +++ b/web/react/components/rhs_comment.jsx @@ -16,7 +16,16 @@ import * as TextFormatting from '../utils/text_formatting.jsx'; import twemoji from 'twemoji'; import * as EventHelpers from '../dispatcher/event_helpers.jsx'; -export default class RhsComment extends React.Component { +import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate} from 'mm-intl'; + +const holders = defineMessages({ + comment: { + id: 'rhs_comment.comment', + defaultMessage: 'Comment' + } +}); + +class RhsComment extends React.Component { constructor(props) { super(props); @@ -95,12 +104,15 @@ export default class RhsComment extends React.Component { data-toggle='modal' data-target='#edit_post' data-refocusid='#reply_textbox' - data-title='Comment' + data-title={this.props.intl.formatMessage(holders.comment)} data-message={post.message} data-postid={post.id} data-channelid={post.channel_id} > - {'Edit'} + <FormattedMessage + id='rhs_comment.edit' + defaultMessage='Edit' + /> </a> </li> ); @@ -117,7 +129,10 @@ export default class RhsComment extends React.Component { role='menuitem' onClick={() => EventHelpers.showDeletePostModal(post, 0)} > - {'Delete'} + <FormattedMessage + id='rhs_comment.del' + defaultMessage='Delete' + /> </a> </li> ); @@ -165,7 +180,10 @@ export default class RhsComment extends React.Component { href='#' onClick={this.retryComment} > - {'Retry'} + <FormattedMessage + id='rhs_comment.retry' + defaultMessage='Retry' + /> </a> ); } else if (post.state === Constants.POST_LOADING) { @@ -208,7 +226,15 @@ export default class RhsComment extends React.Component { </li> <li className='col'> <time className='post__time'> - {Utils.displayCommentDateTime(post.create_at)} + <FormattedDate + value={post.create_at} + day='numeric' + month='long' + year='numeric' + hour12={true} + hour='2-digit' + minute='2-digit' + /> </time> </li> <li className='col col__reply'> @@ -237,5 +263,8 @@ RhsComment.defaultProps = { post: null }; RhsComment.propTypes = { + intl: intlShape.isRequired, post: React.PropTypes.object }; + +export default injectIntl(RhsComment);
\ No newline at end of file diff --git a/web/react/components/rhs_header_post.jsx b/web/react/components/rhs_header_post.jsx index 990b33eb5..d56ba76f8 100644 --- a/web/react/components/rhs_header_post.jsx +++ b/web/react/components/rhs_header_post.jsx @@ -3,6 +3,9 @@ import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; import Constants from '../utils/constants.jsx'; + +import {FormattedMessage} from 'mm-intl'; + const ActionTypes = Constants.ActionTypes; export default class RhsHeaderPost extends React.Component { @@ -58,7 +61,13 @@ export default class RhsHeaderPost extends React.Component { return ( <div className='sidebar--right__header'> - <span className='sidebar--right__title'>{back}Message Details</span> + <span className='sidebar--right__title'> + {back} + <FormattedMessage + id='rhs_header.details' + defaultMessage='Message Details' + /> + </span> <button type='button' className='sidebar--right__close' diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx index cd7f6766c..f9f7f8f81 100644 --- a/web/react/components/rhs_root_post.jsx +++ b/web/react/components/rhs_root_post.jsx @@ -14,6 +14,8 @@ import * as EventHelpers from '../dispatcher/event_helpers.jsx'; import Constants from '../utils/constants.jsx'; +import {FormattedMessage, FormattedDate} from 'mm-intl'; + export default class RhsRootPost extends React.Component { constructor(props) { super(props); @@ -68,7 +70,12 @@ export default class RhsRootPost extends React.Component { var channelName; if (channel) { if (channel.type === 'D') { - channelName = 'Direct Message'; + channelName = ( + <FormattedMessage + id='rhs_root.direct' + defaultMessage='Direct Message' + /> + ); } else { channelName = channel.display_name; } @@ -93,7 +100,10 @@ export default class RhsRootPost extends React.Component { data-postid={post.id} data-channelid={post.channel_id} > - {'Edit'} + <FormattedMessage + id='rhs_root.edit' + defaultMessage='Edit' + /> </a> </li> ); @@ -110,7 +120,10 @@ export default class RhsRootPost extends React.Component { role='menuitem' onClick={() => EventHelpers.showDeletePostModal(post, this.props.commentCount)} > - {'Delete'} + <FormattedMessage + id='rhs_root.del' + defaultMessage='Delete' + /> </a> </li> ); @@ -205,7 +218,15 @@ export default class RhsRootPost extends React.Component { {botIndicator} <li className='col'> <time className='post__time'> - {utils.displayCommentDateTime(post.create_at)} + <FormattedDate + value={post.create_at} + day='numeric' + month='long' + year='numeric' + hour12={true} + hour='2-digit' + minute='2-digit' + /> </time> </li> <li className='col col__reply'> diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx index 77c9e39b9..35d7e9514 100644 --- a/web/react/components/search_bar.jsx +++ b/web/react/components/search_bar.jsx @@ -11,10 +11,20 @@ import SearchSuggestionList from './suggestion/search_suggestion_list.jsx'; import SearchUserProvider from './suggestion/search_user_provider.jsx'; import * as utils from '../utils/utils.jsx'; import Constants from '../utils/constants.jsx'; + +import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl'; + var ActionTypes = Constants.ActionTypes; var Popover = ReactBootstrap.Popover; -export default class SearchBar extends React.Component { +const holders = defineMessages({ + search: { + id: 'search_bar.search', + defaultMessage: 'Search' + } +}); + +class SearchBar extends React.Component { constructor() { super(); this.mounted = false; @@ -147,7 +157,10 @@ export default class SearchBar extends React.Component { className='search__clear' onClick={this.clearFocus} > - {'Cancel'} + <FormattedMessage + id='search_bar.cancel' + defaultMessage='Cancel' + /> </span> <form role='form' @@ -160,7 +173,7 @@ export default class SearchBar extends React.Component { <SuggestionBox ref='search' className='form-control search-bar' - placeholder='Search' + placeholder={this.props.intl.formatMessage(holders.search)} value={this.state.searchTerm} onFocus={this.handleUserFocus} onBlur={this.handleUserBlur} @@ -174,18 +187,20 @@ export default class SearchBar extends React.Component { placement='bottom' className={helpClass} > - <h4>{'Search Options'}</h4> - <ul> - <li> - <span>{'Use '}</span><b>{'"quotation marks"'}</b><span>{' to search for phrases'}</span> - </li> - <li> - <span>{'Use '}</span><b>{'from:'}</b><span>{' to find posts from specific users and '}</span><b>{'in:'}</b><span>{' to find posts in specific channels'}</span> - </li> - </ul> + <FormattedHTMLMessage + id='search_bar.usage' + defaultMessage='<h4>Search Options</h4><ul><li><span>Use </span><b>"quotation marks"</b><span> to search for phrases</span></li><li><span>Use </span><b>from:</b><span> to find posts from specific users and </span><b>in:</b><span> to find posts in specific channels</span></li></ul>' + /> </Popover> </form> </div> ); } } + +SearchBar.propTypes = { + intl: intlShape.isRequired +}; + +export default injectIntl(SearchBar); + diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 141181701..9dcc99061 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -8,6 +8,8 @@ import * as Utils from '../utils/utils.jsx'; import SearchResultsHeader from './search_results_header.jsx'; import SearchResultsItem from './search_results_item.jsx'; +import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl'; + function getStateFromStores() { return {results: SearchStore.getSearchResults()}; } @@ -83,25 +85,29 @@ export default class SearchResults extends React.Component { if (!searchTerm && noResults) { ctls = ( <div className='sidebar--right__subheader'> - <ul> - <li> - {'Use '}<b>{'"quotation marks"'}</b>{' to search for phrases'} - </li> - <li> - {'Use '}<b>{'from:'}</b>{' to find posts from specific users and '}<b>{'in:'}</b>{' to find posts in specific channels'} - </li> - </ul> + <FormattedHTMLMessage + id='search_results.usage' + defaultMessage='<ul><li>Use <b>"quotation marks"</b> to search for phrases</li><li>Use <b>from:</b> to find posts from specific users and <b>in:</b> to find posts in specific channels</li></ul>' + /> </div> ); } else if (noResults) { ctls = ( <div className='sidebar--right__subheader'> - <h4>{'NO RESULTS'}</h4> - <ul> - <li>{'If you\'re searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term'}</li> - <li>{'Due to the volume of results, two letter searches and common words like "this", "a" and "is" won\'t appear in search results'}</li> - </ul> + <h4> + <FormattedMessage + id='search_results.noResults' + defaultMessage='NO RESULTS' + /> + </h4> + <FormattedHTMLMessage + id='search_results.because' + defaultMessage='<ul> + <li>If you're searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term</li> + <li>Due to the volume of results, two letter searches and common words like "this", "a" and "is" won't appear in search results</li> + </ul>' + /> </div> ); } else { diff --git a/web/react/components/search_results_header.jsx b/web/react/components/search_results_header.jsx index 581976494..45f56f65a 100644 --- a/web/react/components/search_results_header.jsx +++ b/web/react/components/search_results_header.jsx @@ -3,6 +3,9 @@ import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; import Constants from '../utils/constants.jsx'; + +import {FormattedMessage} from 'mm-intl'; + var ActionTypes = Constants.ActionTypes; export default class SearchResultsHeader extends React.Component { @@ -34,10 +37,20 @@ export default class SearchResultsHeader extends React.Component { } render() { - var title = 'Search Results'; + var title = ( + <FormattedMessage + id='search_header.results' + defaultMessage='Search Results' + /> + ); if (this.props.isMentionSearch) { - title = 'Recent Mentions'; + title = ( + <FormattedMessage + id='search_header.title2' + defaultMessage='Recent Mentions' + /> + ); } return ( diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx index e4f83eb1c..0ad091d5b 100644 --- a/web/react/components/search_results_item.jsx +++ b/web/react/components/search_results_item.jsx @@ -10,6 +10,8 @@ import * as TextFormatting from '../utils/text_formatting.jsx'; import Constants from '../utils/constants.jsx'; +import {FormattedMessage, FormattedDate} from 'mm-intl'; + export default class SearchResultsItem extends React.Component { constructor(props) { super(props); @@ -42,7 +44,12 @@ export default class SearchResultsItem extends React.Component { if (channel) { channelName = channel.display_name; if (channel.type === 'D') { - channelName = 'Direct Message'; + channelName = ( + <FormattedMessage + id='search_item.direct' + defaultMessage='Direct Message' + /> + ); } } @@ -69,7 +76,15 @@ export default class SearchResultsItem extends React.Component { <li className='col__name'><strong><UserProfile userId={this.props.post.user_id} /></strong></li> <li className='col'> <time className='search-item-time'> - {utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at)} + <FormattedDate + value={this.props.post.create_at} + day='numeric' + month='long' + year='numeric' + hour12={true} + hour='2-digit' + minute='2-digit' + /> </time> </li> <li> @@ -78,7 +93,10 @@ export default class SearchResultsItem extends React.Component { className='search-item__jump' onClick={this.handleClick} > - {'Jump'} + <FormattedMessage + id='search_item.jump' + defaultMessage='Jump' + /> </a> </li> <li> diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx index 20c2bf696..4d714e9f1 100644 --- a/web/react/components/sidebar_right_menu.jsx +++ b/web/react/components/sidebar_right_menu.jsx @@ -9,6 +9,8 @@ import * as client from '../utils/client.jsx'; import * as EventHelpers from '../dispatcher/event_helpers.jsx'; import * as utils from '../utils/utils.jsx'; +import {FormattedMessage} from 'mm-intl'; + export default class SidebarRightMenu extends React.Component { componentDidMount() { $('.sidebar--left .dropdown-menu').perfectScrollbar(); @@ -49,7 +51,11 @@ export default class SidebarRightMenu extends React.Component { href='#' onClick={EventHelpers.showInviteMemberModal} > - <i className='fa fa-user'></i>{'Invite New Member'} + <i className='fa fa-user'></i> + <FormattedMessage + id='sidebar_right_menu.inviteNew' + defaultMessage='Invite New Member' + /> </a> </li> ); @@ -61,7 +67,11 @@ export default class SidebarRightMenu extends React.Component { href='#' onClick={EventHelpers.showGetTeamInviteLinkModal} > - <i className='glyphicon glyphicon-link'></i>{'Get Team Invite Link'} + <i className='glyphicon glyphicon-link'></i> + <FormattedMessage + id='sidebar_right_menu.teamLink' + defaultMessage='Get Team Invite Link' + /> </a> </li> ); @@ -75,13 +85,23 @@ export default class SidebarRightMenu extends React.Component { href='#' data-toggle='modal' data-target='#team_settings' - ><i className='fa fa-globe'></i>{'Team Settings'}</a> + > + <i className='fa fa-globe'></i> + <FormattedMessage + id='sidebar_right_menu.teamSettings' + defaultMessage='Team Settings' + /> + </a> </li> ); manageLink = ( <li> <ToggleModalButton dialogType={TeamMembersModal}> - <i className='fa fa-users'></i>{'Manage Members'} + <i className='fa fa-users'></i> + <FormattedMessage + id='sidebar_right_menu.manageMembers' + defaultMessage='Manage Members' + /> </ToggleModalButton> </li> ); @@ -93,7 +113,12 @@ export default class SidebarRightMenu extends React.Component { <a href={'/admin_console?' + utils.getSessionIndex()} > - <i className='fa fa-wrench'></i>{'System Console'}</a> + <i className='fa fa-wrench'></i> + <FormattedMessage + id='sidebar_right_menu.console' + defaultMessage='System Console' + /> + </a> </li> ); } @@ -114,7 +139,14 @@ export default class SidebarRightMenu extends React.Component { <a target='_blank' href={global.window.mm_config.HelpLink} - ><i className='fa fa-question'></i>{'Help'}</a></li> + > + <i className='fa fa-question'></i> + <FormattedMessage + id='sidebar_right_menu.help' + defaultMessage='Help' + /> + </a> + </li> ); } @@ -125,7 +157,14 @@ export default class SidebarRightMenu extends React.Component { <a target='_blank' href={global.window.mm_config.ReportAProblemLink} - ><i className='fa fa-phone'></i>{'Report a Problem'}</a></li> + > + <i className='fa fa-phone'></i> + <FormattedMessage + id='sidebar_right_menu.report' + defaultMessage='Report a Problem' + /> + </a> + </li> ); } return ( @@ -144,7 +183,11 @@ export default class SidebarRightMenu extends React.Component { href='#' onClick={() => this.setState({showUserSettingsModal: true})} > - <i className='fa fa-cog'></i>{'Account Settings'} + <i className='fa fa-cog'></i> + <FormattedMessage + id='sidebar_right_menu.accountSettings' + defaultMessage='Account Settings' + /> </a> </li> {teamSettingsLink} @@ -156,7 +199,14 @@ export default class SidebarRightMenu extends React.Component { <a href='#' onClick={this.handleLogoutClick} - ><i className='fa fa-sign-out'></i>{'Logout'}</a></li> + > + <i className='fa fa-sign-out'></i> + <FormattedMessage + id='sidebar_right_menu.logout' + defaultMessage='Logout' + /> + </a> + </li> <li className='divider'></li> {helpLink} {reportLink} diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx index 62c0d5218..bb383aca1 100644 --- a/web/react/components/textbox.jsx +++ b/web/react/components/textbox.jsx @@ -11,6 +11,9 @@ import ErrorStore from '../stores/error_store.jsx'; import * as TextFormatting from '../utils/text_formatting.jsx'; import * as Utils from '../utils/utils.jsx'; import Constants from '../utils/constants.jsx'; + +import {FormattedMessage} from 'mm-intl'; + const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES; export default class Textbox extends React.Component { @@ -143,7 +146,17 @@ export default class Textbox extends React.Component { onClick={this.showPreview} className='textbox-preview-link' > - {this.state.preview ? 'Edit message' : 'Preview'} + {this.state.preview ? ( + <FormattedMessage + id='textbox.edit' + defaultMessage='Edit message' + /> + ) : ( + <FormattedMessage + id='textbox.preview' + defaultMessage='Preview' + /> + )} </a> ); } @@ -184,7 +197,10 @@ export default class Textbox extends React.Component { onClick={this.showHelp} className='textbox-help-link' > - {'Help'} + <FormattedMessage + id='textbox.help' + defaultMessage='Help' + /> </a> </div> ); diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index 385cd0f52..1e393cfe9 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -3,6 +3,9 @@ import * as Utils from '../utils/utils.jsx'; import UserStore from '../stores/user_store.jsx'; + +import {FormattedMessage} from 'mm-intl'; + var Popover = ReactBootstrap.Popover; var OverlayTrigger = ReactBootstrap.OverlayTrigger; @@ -87,7 +90,10 @@ export default class UserProfile extends React.Component { className='text-nowrap' key='user-popover-no-email' > - {'Email not shared'} + <FormattedMessage + id='user_profile.notShared' + defaultMessage='Email not shared' + /> </div> ); } else { diff --git a/web/static/i18n/en.json b/web/static/i18n/en.json index 62608b85a..d6401ab6e 100644 --- a/web/static/i18n/en.json +++ b/web/static/i18n/en.json @@ -496,6 +496,12 @@ "claim.sso_to_email_newPwd": "Enter a new password for your {team} {site} account", "claim.sso_to_email.switchTo": "Switch {type} to email and password", "confirm_modal.cancel": "Cancel", + "create_comment.commentLength": "Comment length must be less than {max} characters.", + "create_comment.comment": "Add Comment", + "create_comment.addComment": "Add a comment...", + "create_comment.commentTitle": "Comment", + "create_comment.file": "File uploading", + "create_comment.files": "Files uploading", "email_verify.verified": "{siteName} Email Verified", "email_verify.verifiedBody": "<p>Your email has been verified! Click <a href={url}>here</a> to log in.</p>", "email_verify.almost": "{siteName}: You are almost done", @@ -503,6 +509,11 @@ "email_verify.resend": "Resend Email", "email_verify.sent": " Verification email sent.", "error_bar.preview_mode": "Preview Mode: Email notifications have not been configured", + "upload_overlay.info": "Drop a file to upload it.", + "file_upload.limited": "Uploads limited to {count} files maximum. Please use additional posts for more files.", + "file_upload.filesAbove": "Files above {max}MB could not be uploaded: {filenames}", + "file_upload.fileAbove": "File above {max}MB could not be uploaded: {filename}", + "file_upload.pasted": "Image Pasted at ", "find_team.submitError": "Please enter a valid email address", "find_team.placeholder": "you@domain.com", "find_team.findTitle": "Find Your Team", @@ -579,6 +590,9 @@ "more_direct_channels.countTotal": "{count} {member} of {total} Total", "more_direct_channels.title": "Direct Messages", "more_direct_channels.close": "Close", + "msg_typing.someone": "Someone", + "msg_typing.isTyping": "{user} is typing...", + "msg_typing.areTyping": "{users} and {last} are typing...", "navbar_dropdown.inviteMember": "Invite New Member", "navbar_dropdown.teamLink": "Get Team Invite Link", "navbar_dropdown.manageMembers": "Manage Members", @@ -648,6 +662,24 @@ "register_app.credentialsSave": "I have saved both my Client Id and Client Secret somewhere safe", "register_app.close": "Close", "register_app.dev": "Developer Applications", + "rhs_comment.comment": "Comment", + "rhs_comment.edit": "Edit", + "rhs_comment.del": "Delete", + "rhs_comment.retry": "Retry", + "rhs_header.details": "Message Details", + "rhs_root.direct": "Direct Message", + "rhs_root.edit": "Edit", + "rhs_root.del": "Delete", + "search_bar.search": "Search", + "search_bar.cancel": "Cancel", + "search_bar.usage": "<h4>Search Options</h4><ul><li><span>Use </span><b>\"quotation marks\"</b><span> to search for phrases</span></li><li><span>Use </span><b>from:</b><span> to find posts from specific users and </span><b>in:</b><span> to find posts in specific channels</span></li></ul>", + "search_header.results": "Search Results", + "search_header.title2": "Recent Mentions", + "search_item.direct": "Direct Message", + "search_item.jump": "Jump", + "search_results.usage": "<ul><li>Use <b>\"quotation marks\"</b> to search for phrases</li><li>Use <b>from:</b> to find posts from specific users and <b>in:</b> to find posts in specific channels</li></ul>", + "search_results.noResults": "NO RESULTS", + "search_results.because": "<ul>\n <li>If you're searching a partial phrase (ex. searching \"rea\", looking for \"reach\" or \"reaction\"), append a * to your search term</li>\n <li>Due to the volume of results, two letter searches and common words like \"this\", \"a\" and \"is\" won't appear in search results</li>\n </ul>", "setting_item_max.save": "Save", "setting_item_max.cancel": "Cancel", "setting_item_min.edit": "Edit", @@ -659,6 +691,15 @@ "setting_upload.select": "Select file", "setting_upload.import": "Import", "sidebar_header.tutorial": "<h4>Main Menu</h4>\n <p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p>\n <p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>", + "sidebar_right_menu.inviteNew": "Invite New Member", + "sidebar_right_menu.teamLink": "Get Team Invite Link", + "sidebar_right_menu.teamSettings": "Team Settings", + "sidebar_right_menu.manageMembers": "Manage Members", + "sidebar_right_menu.console": "System Console", + "sidebar_right_menu.help": "Help", + "sidebar_right_menu.report": "Report a Problem", + "sidebar_right_menu.accountSettings": "Account Settings", + "sidebar_right_menu.logout": "Logout", "sidebar.tutorialScreen1": "<h4>Channels</h4><p><strong>Channels</strong> organize conversations across different topics. They’re open to everyone on your team. To send private communications use <strong>Direct Messages</strong> for a single person or <strong>Private Groups</strong> for multiple people.</p>", "sidebar.tutorialScreen2": "<h4>\"Town Square\" and \"Off-Topic\" channels</h4>\n <p>Here are two public channels to start:</p>\n <p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>\n <p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>", "sidebar.tutorialScreen3": "<h4>Creating and Joining Channels</h4>\n <p>Click <strong>\"More...\"</strong> to create a new channel or join an existing one.</p>\n <p>You can also create a new channel or private group by clicking the <strong>\"+\" symbol</strong> next to the channel or private group header.</p>", @@ -809,6 +850,9 @@ "sso_signup.gitlab": "Create team with GitLab Account", "sso_signup.google": "Create team with Google Apps Account", "sso_signup.find": "Find my teams", + "textbox.edit": "Edit message", + "textbox.preview": "Preview", + "textbox.help": "Help", "tutorial_intro.screenOne": "<h3>Welcome to:</h3>\n <h1>Mattermost</h1>\n <p>Your team communication all in one place, instantly searchable and available anywhere</p>\n <p>Keep your team connected to help them achieve what matters most.</p>", "tutorial_intro.screenTwo": "<h3>How Mattermost works:</h3>\n <p>Communication happens in public discussion channels, private groups and direct messages.</p>\n <p>Everything is archived and searchable from any web-enabled desktop, laptop or phone.</p>", "tutorial_intro.invite": "Invite teammates", @@ -823,6 +867,7 @@ "tutorial_tip.next": "Next", "tutorial_tip.seen": "Seen this before? ", "tutorial_tip.out": "Opt out of these tips.", + "user_profile.notShared": "Email not shared", "user.settings.custom_theme.sidebarBg": "Sidebar BG", "user.settings.custom_theme.sidebarText": "Sidebar Text", "user.settings.custom_theme.sidebarHeaderBg": "Sidebar Header BG", diff --git a/web/static/i18n/es.json b/web/static/i18n/es.json index ee24df1b3..cb3e8a199 100644 --- a/web/static/i18n/es.json +++ b/web/static/i18n/es.json @@ -525,6 +525,12 @@ "claim.sso_to_email.title": "Cambiar la cuenta de {type} a Correo Electrónico", "claim.sso_to_email_newPwd": "Ingresa una nueva contraseña para tu cuenta de {team} {site}", "confirm_modal.cancel": "Cancelar", + "create_comment.addComment": "Agregar un comentario...", + "create_comment.comment": "Agregar Comentario", + "create_comment.commentLength": "El largo del Comentario debe ser menor de {max} caracteres.", + "create_comment.commentTitle": "Comentario", + "create_comment.file": "Subiendo archivo", + "create_comment.files": "Subiendo archivos", "email_signup.address": "Correo electrónico", "email_signup.createTeam": "Crear Equipo", "email_signup.emailError": "Por favor ingresa una dirección de correos válida", @@ -536,6 +542,10 @@ "email_verify.verified": "{siteName} Correo electrónico verificado", "email_verify.verifiedBody": "<p>Tu correo electrónico ha sido verificado!! Pincha <a href={url}>aquí</a> para iniciar sesión.</p>", "error_bar.preview_mode": "Modo de prueba: Las notificaciones por correo electrónico no han sido configuradas", + "file_upload.fileAbove": "No se puede subir un archivo que pesa más de {max}MB: {filename}", + "file_upload.filesAbove": "No se pueden subir archivos de más de {max}MB: {filenames}", + "file_upload.limited": "Se pueden subir un máximo de {count} archivos. Por favor envía otros mensajes para adjuntar más archivos.", + "file_upload.pasted": "Imagen Pegada el ", "find_team.email": "Correo electrónico", "find_team.findDescription": "Enviamos un correo electrónico con los equipos a los que perteneces.", "find_team.findTitle": "Encuentra tu equipo", @@ -631,6 +641,9 @@ "more_direct_channels.notFound": "No se encontraron usuarios :(", "more_direct_channels.search": "Buscar miembros", "more_direct_channels.title": "Mensajes Directos", + "msg_typing.areTyping": "{users} y {last} están escribiendo...", + "msg_typing.isTyping": "{user} está escribiendo...", + "msg_typing.someone": "Alguien", "navbar_dropdown.about": "Acerca de Mattermost", "navbar_dropdown.accountSettings": "Configurar Cuenta", "navbar_dropdown.console": "Consola de Sistema", @@ -676,6 +689,24 @@ "register_app.register": "Registrar", "register_app.required": "Requerido", "register_app.title": "Registra una Nueva Aplicación", + "rhs_comment.comment": "Comentario", + "rhs_comment.del": "Borrar", + "rhs_comment.edit": "Editar", + "rhs_comment.retry": "Reintentar", + "rhs_header.details": "Detalles del Mensaje", + "rhs_root.del": "Borrar", + "rhs_root.direct": "Mensaje Directo", + "rhs_root.edit": "Editar", + "search_bar.cancel": "Cancelar", + "search_bar.search": "Buscar", + "search_bar.usage": "<h4>Opciones de Búsqueda</h4><ul><li><span>Utiliza </span><b>\"comillas\"</b><span> para buscar frases</span></li><li><span>Utiliza </span><b>from:</b><span> para encontrar mensajes de usuarios en específico e </span><b>in:</b><span> para encontrar mensajes de canales específicos</span></li></ul>", + "search_header.results": "Resultados de la Búsqueda", + "search_header.title2": "Menciones Recientes", + "search_item.direct": "Mensjae Directo", + "search_item.jump": "Ir ", + "search_results.because": "<ul><li>Si estás buscando un frase parcial (ej. buscando \"aud\", tratando de encontrar \"audible\" o \"audifonos\"), agrega un * a la palabra que estás buscando</li><li>Debido a la cantidad de resultados, la búsqueda con dos letras y palabras comunes como \"this\", \"a\" and \"es\" no aparecerán en los resultados</li>/ul>", + "search_results.noResults": "SIN RESULTADOS", + "search_results.usage": "<ul><li>Utiliza <b>\"comillas\"</b> para buscar frases</li><li>Utiliza <b>from:</b> para encontrar mensajes de usuarios en específico e <b>in:</b> para encontrar mensajes de canales específicos</li></ul>", "setting_item_max.cancel": "Cancelar", "setting_item_max.save": "Guardar", "setting_item_min.edit": "Editar", @@ -700,6 +731,15 @@ "sidebar.unreadAbove": "Mensaje(s) sin leer arriba", "sidebar.unreadBelow": "Mensaje(s) sin leer abajo", "sidebar_header.tutorial": "<h4>Menú Principal</h4><p>El <strong>Menú Principal</strong> es donde puedes <strong>Invitar a nuevos miembros</strong>, podrás <strong>Configurar tu Cuenta</strong> y seleccionar un <strong>Tema</strong> para personalizar la apariencia.</p><p>Los administradores del Equipo podrán <strong>Configurar el Equipo</strong> desde este menú.</p><p>Los administradores del Sistema encontrarán una opción para ir a la <strong>Consola de Sistema</strong> para administrar el sistema completo.</p>", + "sidebar_right_menu.accountSettings": "Configurar tu Cuenta", + "sidebar_right_menu.console": "Consola del Sistema", + "sidebar_right_menu.help": "Ayuda", + "sidebar_right_menu.inviteNew": "Invitar Nuevo Miembro", + "sidebar_right_menu.logout": "Cerrar sesión", + "sidebar_right_menu.manageMembers": "Adminisrar Miembros", + "sidebar_right_menu.report": "Reporta un Problema", + "sidebar_right_menu.teamLink": "Enlace Invitación al Equipo", + "sidebar_right_menu.teamSettings": "Configurar Equipo", "signup_team.choose": "Selecciona un Equipo", "signup_team.createTeam": "O Crea un Equipo", "signup_team.disabled": "La creación de Equipos ha sido deshabilitada.", @@ -810,6 +850,9 @@ "team_signup_welcome.validEmailError": "Por favor ingresa una dirección de correo electrónico válida", "team_signup_welcome.welcome": "Bienvenido a:", "team_signup_welcome.yes": "Sí, esta dirección es correcta", + "textbox.edit": "Editar mensaje", + "textbox.help": "Ayuda", + "textbox.preview": "Previsualizar", "tutorial_intro.allSet": "Ya estás listo para comenzar", "tutorial_intro.end": "Pincha “Siguiente” para entrar al Canal General. Este es el primer canal que ven tus compañeros cuando ingresan. Utilizalo para mandar mensajes que todos deben leer.", "tutorial_intro.invite": "Invitar compañeros", @@ -824,6 +867,7 @@ "tutorial_tip.ok": "Aceptar", "tutorial_tip.out": "No optar por estos consejos.", "tutorial_tip.seen": "¿Haz visto esto antes? ", + "upload_overlay.info": "Arrastra un archivo para subirlo.", "user.settings.advance.embed_preview": "Mostrar la previsualización de enlaces", "user.settings.advance.enabled": "habilitada(s)", "user.settings.advance.feature": " Característica ", @@ -999,5 +1043,6 @@ "user.settings.security.switchGitlab": "Cambiar para utilizar GitLab SSO", "user.settings.security.switchGoogle": "Cambiar para utilizar Google SSO", "user.settings.security.title": "Configuración de Seguridad", - "user.settings.security.viewHistory": "Visualizar historial de acceso" + "user.settings.security.viewHistory": "Visualizar historial de acceso", + "user_profile.notShared": "Correo no compartido" }
\ No newline at end of file |