summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorElias Nahum <nahumhbl@gmail.com>2016-02-01 17:23:45 -0300
committerElias Nahum <nahumhbl@gmail.com>2016-02-01 17:23:45 -0300
commit3f91bc2df2c6f9d8437f258233609e9801b7f8f6 (patch)
tree3721db61cb0c052fba9c100c000ab64297c81f51 /web
parent9fe4c0603e8b98570d4a9ab1c6bd3d69b321c36c (diff)
downloadchat-3f91bc2df2c6f9d8437f258233609e9801b7f8f6.tar.gz
chat-3f91bc2df2c6f9d8437f258233609e9801b7f8f6.tar.bz2
chat-3f91bc2df2c6f9d8437f258233609e9801b7f8f6.zip
PLT-7: Refactoring frontend (chunk 9)
- sidebar right & rhs
Diffstat (limited to 'web')
-rw-r--r--web/react/components/create_comment.jsx52
-rw-r--r--web/react/components/file_upload.jsx32
-rw-r--r--web/react/components/file_upload_overlay.jsx9
-rw-r--r--web/react/components/msg_typing.jsx42
-rw-r--r--web/react/components/rhs_comment.jsx41
-rw-r--r--web/react/components/rhs_header_post.jsx11
-rw-r--r--web/react/components/rhs_root_post.jsx29
-rw-r--r--web/react/components/search_bar.jsx39
-rw-r--r--web/react/components/search_results.jsx32
-rw-r--r--web/react/components/search_results_header.jsx17
-rw-r--r--web/react/components/search_results_item.jsx24
-rw-r--r--web/react/components/sidebar_right_menu.jsx68
-rw-r--r--web/react/components/textbox.jsx20
-rw-r--r--web/react/components/user_profile.jsx8
-rw-r--r--web/static/i18n/en.json44
-rw-r--r--web/static/i18n/es.json47
16 files changed, 440 insertions, 75 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..2f9146193 100644
--- a/web/react/components/file_upload.jsx
+++ b/web/react/components/file_upload.jsx
@@ -6,7 +6,24 @@ 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}'
+ }
+});
+
+class FileUpload extends React.Component {
constructor(props) {
super(props);
@@ -74,14 +91,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 +124,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 +203,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++) {
@@ -296,6 +315,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 +324,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..1dd8354d7 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..bc772aaa5 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&#39;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&#39;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..a0b252d25 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..78e4c625d 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,10 @@
"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}",
"find_team.submitError": "Please enter a valid email address",
"find_team.placeholder": "you@domain.com",
"find_team.findTitle": "Find Your Team",
@@ -579,6 +589,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 +661,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 +690,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 +849,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 +866,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..6b5eee94a 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 en ",
"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