summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/team.go2
-rw-r--r--web/react/components/admin_console/ldap_settings.jsx2
-rw-r--r--web/react/components/audio_video_preview.jsx4
-rw-r--r--web/react/components/center_panel.jsx9
-rw-r--r--web/react/components/create_post.jsx42
-rw-r--r--web/react/components/file_attachment.jsx16
-rw-r--r--web/react/components/file_info_preview.jsx19
-rw-r--r--web/react/components/navbar.jsx89
-rw-r--r--web/react/components/post_attachment.jsx22
-rw-r--r--web/react/components/post_body.jsx47
-rw-r--r--web/react/components/post_focus_view.jsx9
-rw-r--r--web/react/components/post_info.jsx32
-rw-r--r--web/react/components/posts_view.jsx27
-rw-r--r--web/react/components/time_since.jsx20
-rw-r--r--web/react/components/view_image.jsx28
-rw-r--r--web/react/components/view_image_popover_bar.jsx23
-rw-r--r--web/react/stores/post_store.jsx5
-rw-r--r--web/react/utils/channel_intro_messages.jsx146
-rw-r--r--web/static/i18n/en.json73
-rw-r--r--web/static/i18n/es.json95
20 files changed, 583 insertions, 127 deletions
diff --git a/api/team.go b/api/team.go
index 8b25e3316..6d59e94e9 100644
--- a/api/team.go
+++ b/api/team.go
@@ -66,7 +66,7 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
bodyPage.Props["SiteURL"] = c.GetSiteURL()
bodyPage.Props["Title"] = c.T("api.templates.signup_team_body.title")
bodyPage.Props["Button"] = c.T("api.templates.signup_team_body.button")
- bodyPage.Html["Info"] = template.HTML(c.T("api.templates.signup_team_body.button",
+ bodyPage.Html["Info"] = template.HTML(c.T("api.templates.signup_team_body.info",
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]}))
props := make(map[string]string)
diff --git a/web/react/components/admin_console/ldap_settings.jsx b/web/react/components/admin_console/ldap_settings.jsx
index bc13b3bcd..535c264dd 100644
--- a/web/react/components/admin_console/ldap_settings.jsx
+++ b/web/react/components/admin_console/ldap_settings.jsx
@@ -164,7 +164,7 @@ class LdapSettings extends React.Component {
<div className='banner__content'>
<FormattedHTMLMessage
id='admin.ldap.noLicense'
- defaultMessage='<h4 className="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
+ defaultMessage='<h4 class="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
/>
</div>
</div>
diff --git a/web/react/components/audio_video_preview.jsx b/web/react/components/audio_video_preview.jsx
index 7d00fbdaa..739c8c95e 100644
--- a/web/react/components/audio_video_preview.jsx
+++ b/web/react/components/audio_video_preview.jsx
@@ -75,6 +75,7 @@ export default class AudioVideoPreview extends React.Component {
filename={this.props.filename}
fileUrl={this.props.fileUrl}
fileInfo={this.props.fileInfo}
+ formatMessage={this.props.formatMessage}
/>
);
}
@@ -110,5 +111,6 @@ AudioVideoPreview.propTypes = {
filename: React.PropTypes.string.isRequired,
fileUrl: React.PropTypes.string.isRequired,
fileInfo: React.PropTypes.object.isRequired,
- maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired
+ maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired,
+ formatMessage: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/center_panel.jsx b/web/react/components/center_panel.jsx
index 443ecefde..7d2be04d6 100644
--- a/web/react/components/center_panel.jsx
+++ b/web/react/components/center_panel.jsx
@@ -15,6 +15,8 @@ import UserStore from '../stores/user_store.jsx';
import * as Utils from '../utils/utils.jsx';
+import {FormattedMessage} from 'mm-intl';
+
import Constants from '../utils/constants.jsx';
const TutorialSteps = Constants.TutorialSteps;
const Preferences = Constants.Preferences;
@@ -69,8 +71,11 @@ export default class CenterPanel extends React.Component {
onClick={handleClick}
>
<a href=''>
- {'Click here to jump to recent messages. '}
- {<i className='fa fa-arrow-down'></i>}
+ <FormattedMessage
+ id='center_panel.recent'
+ defaultMessage='Click here to jump to recent messages. '
+ />
+ <i className='fa fa-arrow-down'></i>
</a>
</div>
);
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index de971c43f..ed672cd34 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -21,12 +21,33 @@ import SocketStore from '../stores/socket_store.jsx';
import Constants from '../utils/constants.jsx';
+import {intlShape, injectIntl, defineMessages, FormattedHTMLMessage} from 'mm-intl';
+
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
const ActionTypes = Constants.ActionTypes;
const KeyCodes = Constants.KeyCodes;
-export default class CreatePost extends React.Component {
+const holders = defineMessages({
+ comment: {
+ id: 'create_post.comment',
+ defaultMessage: 'Comment'
+ },
+ post: {
+ id: 'create_post.post',
+ defaultMessage: 'Post'
+ },
+ write: {
+ id: 'create_post.write',
+ defaultMessage: 'Write a message...'
+ },
+ deleteMsg: {
+ id: 'create_post.deleteMsg',
+ defaultMessage: '(message deleted)'
+ }
+});
+
+class CreatePost extends React.Component {
constructor(props) {
super(props);
@@ -49,6 +70,7 @@ export default class CreatePost extends React.Component {
this.sendMessage = this.sendMessage.bind(this);
PostStore.clearDraftUploads();
+ PostStore.deleteMessage(this.props.intl.formatMessage(holders.deleteMsg));
const draft = this.getCurrentDraft();
@@ -361,7 +383,8 @@ export default class CreatePost extends React.Component {
if (!lastPost) {
return;
}
- var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post';
+ const {formatMessage} = this.props.intl;
+ var type = (lastPost.root_id && lastPost.root_id.length > 0) ? formatMessage(holders.comment) : formatMessage(holders.post);
AppDispatcher.handleViewAction({
type: ActionTypes.RECIEVED_EDIT_POST,
@@ -379,9 +402,10 @@ export default class CreatePost extends React.Component {
screens.push(
<div>
- <h4>{'Sending Messages'}</h4>
- <p>{'Type here to write a message and press '}<strong>{'Enter'}</strong>{' to post it.'}</p>
- <p>{'Click the '}<strong>{'Attachment'}</strong>{' button to upload an image or a file.'}</p>
+ <FormattedHTMLMessage
+ id='create_post.tutorialTip'
+ defaultMessage='<h4>Sending Messages</h4><p>Type here to write a message and press <strong>Enter</strong> to post it.</p><p>Click the <strong>Attachment</strong> button to upload an image or a file.</p>'
+ />
</div>
);
@@ -445,7 +469,7 @@ export default class CreatePost extends React.Component {
onKeyDown={this.handleKeyDown}
onHeightChange={this.resizePostHolder}
messageText={this.state.messageText}
- createMessage='Write a message...'
+ createMessage={this.props.intl.formatMessage(holders.write)}
channelId={this.state.channelId}
id='post_textbox'
ref='textbox'
@@ -482,3 +506,9 @@ export default class CreatePost extends React.Component {
);
}
}
+
+CreatePost.propTypes = {
+ intl: intlShape.isRequired
+};
+
+export default injectIntl(CreatePost); \ No newline at end of file
diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx
index eeb218bfe..776394828 100644
--- a/web/react/components/file_attachment.jsx
+++ b/web/react/components/file_attachment.jsx
@@ -5,7 +5,16 @@ import * as utils from '../utils/utils.jsx';
import * as Client from '../utils/client.jsx';
import Constants from '../utils/constants.jsx';
-export default class FileAttachment extends React.Component {
+import {intlShape, injectIntl, defineMessages} from 'mm-intl';
+
+const holders = defineMessages({
+ download: {
+ id: 'file_attachment.download',
+ defaultMessage: 'Download'
+ }
+});
+
+class FileAttachment extends React.Component {
constructor(props) {
super(props);
@@ -266,7 +275,7 @@ export default class FileAttachment extends React.Component {
href={fileUrl}
download={filenameString}
data-toggle='tooltip'
- title={'Download \"' + filenameString + '\"'}
+ title={this.props.intl.formatMessage(holders.download) + ' \"' + filenameString + '\"'}
className='post-image__name'
>
{trimmedFilename}
@@ -291,6 +300,7 @@ export default class FileAttachment extends React.Component {
}
FileAttachment.propTypes = {
+ intl: intlShape.isRequired,
// a list of file pathes displayed by the parent FileAttachmentList
filename: React.PropTypes.string.isRequired,
@@ -301,3 +311,5 @@ FileAttachment.propTypes = {
// handler for when the thumbnail is clicked passed the index above
handleImageClick: React.PropTypes.func
};
+
+export default injectIntl(FileAttachment); \ No newline at end of file
diff --git a/web/react/components/file_info_preview.jsx b/web/react/components/file_info_preview.jsx
index 45d89007f..1dac140c9 100644
--- a/web/react/components/file_info_preview.jsx
+++ b/web/react/components/file_info_preview.jsx
@@ -3,15 +3,28 @@
import * as Utils from '../utils/utils.jsx';
-export default function FileInfoPreview({filename, fileUrl, fileInfo}) {
+import {defineMessages} from 'mm-intl';
+
+const holders = defineMessages({
+ type: {
+ id: 'file_info_preview.type',
+ defaultMessage: 'File type '
+ },
+ size: {
+ id: 'file_info_preview.size',
+ defaultMessage: 'Size '
+ }
+});
+
+export default function FileInfoPreview({filename, fileUrl, fileInfo, formatMessage}) {
// non-image files include a section providing details about the file
const infoParts = [];
if (fileInfo.extension !== '') {
- infoParts.push('File type ' + fileInfo.extension.toUpperCase());
+ infoParts.push(formatMessage(holders.type) + fileInfo.extension.toUpperCase());
}
- infoParts.push('Size ' + Utils.fileSizeToString(fileInfo.size));
+ infoParts.push(formatMessage(holders.size) + Utils.fileSizeToString(fileInfo.size));
const infoString = infoParts.join(', ');
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 7326a9ef8..8005678a2 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -24,6 +24,8 @@ import Constants from '../utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import {FormattedMessage} from 'mm-intl';
+
const Popover = ReactBootstrap.Popover;
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -133,7 +135,10 @@ export default class Navbar extends React.Component {
dialogType={ChannelInfoModal}
dialogProps={{channel}}
>
- {'View Info'}
+ <FormattedMessage
+ id='navbar.viewInfo'
+ defaultMessage='View Info'
+ />
</ToggleModalButton>
</li>
);
@@ -145,7 +150,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={this.showEditChannelHeaderModal}
>
- {'Set Channel Header...'}
+ <FormattedMessage
+ id='navbar.setHeader'
+ defaultMessage='Set Channel Header...'
+ />
</a>
</li>
);
@@ -159,7 +167,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={() => this.setState({showEditChannelPurposeModal: true})}
>
- {'Set Channel Purpose...'}
+ <FormattedMessage
+ id='navbar.setPurpose'
+ defaultMessage='Set Channel Purpose...'
+ />
</a>
</li>
);
@@ -175,7 +186,10 @@ export default class Navbar extends React.Component {
dialogType={ChannelInviteModal}
dialogProps={{channel}}
>
- {'Add Members'}
+ <FormattedMessage
+ id='navbar.addMembers'
+ defaultMessage='Add Members'
+ />
</ToggleModalButton>
</li>
);
@@ -187,7 +201,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={this.handleLeave}
>
- {'Leave Channel'}
+ <FormattedMessage
+ id='navbar.leave'
+ defaultMessage='Leave Channel'
+ />
</a>
</li>
);
@@ -205,7 +222,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={() => this.setState({showMembersModal: true})}
>
- {'Manage Members'}
+ <FormattedMessage
+ id='navbar.manageMembers'
+ defaultMessage='Manage Members'
+ />
</a>
</li>
);
@@ -217,7 +237,10 @@ export default class Navbar extends React.Component {
dialogType={DeleteChannelModal}
dialogProps={{channel}}
>
- {'Delete Channel...'}
+ <FormattedMessage
+ id='navbar.delete'
+ defaultMessage='Delete Channel...'
+ />
</ToggleModalButton>
</li>
);
@@ -234,7 +257,10 @@ export default class Navbar extends React.Component {
data-name={channel.name}
data-channelid={channel.id}
>
- {'Rename Channel...'}
+ <FormattedMessage
+ id='navbar.rename'
+ defaultMessage='Rename Channel...'
+ />
</a>
</li>
);
@@ -249,7 +275,10 @@ export default class Navbar extends React.Component {
dialogType={ChannelNotificationsModal}
dialogProps={{channel}}
>
- {'Notification Preferences'}
+ <FormattedMessage
+ id='navbar.preferences'
+ defaultMessage='Notification Preferences'
+ />
</ToggleModalButton>
</li>
);
@@ -319,7 +348,12 @@ export default class Navbar extends React.Component {
data-toggle='collapse'
data-target='#navbar-collapse-1'
>
- <span className='sr-only'>{'Toggle sidebar'}</span>
+ <span className='sr-only'>
+ <FormattedMessage
+ id='navbar.toggle1'
+ defaultMessage='Toggle sidebar'
+ />
+ </span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
@@ -335,7 +369,12 @@ export default class Navbar extends React.Component {
data-target='#sidebar-nav'
onClick={this.toggleLeftSidebar}
>
- <span className='sr-only'>{'Toggle sidebar'}</span>
+ <span className='sr-only'>
+ <FormattedMessage
+ id='navbar.toggle2'
+ defaultMessage='Toggle sidebar'
+ />
+ </span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
@@ -405,6 +444,17 @@ export default class Navbar extends React.Component {
}
if (channel.header.length === 0) {
+ const link = (
+ <a
+ href='#'
+ onClick={this.showEditChannelHeaderModal}
+ >
+ <FormattedMessage
+ id='navbar.click'
+ defaultMessage='Click here'
+ />
+ </a>
+ );
popoverContent = (
<Popover
bsStyle='info'
@@ -412,15 +462,14 @@ export default class Navbar extends React.Component {
id='header-popover'
>
<div>
- {'No channel header yet.'}
- <br/>
- <a
- href='#'
- onClick={this.showEditChannelHeaderModal}
- >
- {'Click here'}
- </a>
- {' to add one.'}
+ <FormattedMessage
+ id='navbar.noHeader'
+ defaultMessage='No channel header yet.{newline}{link} to add one.'
+ values={{
+ newline: (<br/>),
+ link: (link)
+ }}
+ />
</div>
</Popover>
);
diff --git a/web/react/components/post_attachment.jsx b/web/react/components/post_attachment.jsx
index 676bc91af..2eedfb7c1 100644
--- a/web/react/components/post_attachment.jsx
+++ b/web/react/components/post_attachment.jsx
@@ -3,7 +3,20 @@
import * as TextFormatting from '../utils/text_formatting.jsx';
-export default class PostAttachment extends React.Component {
+import {intlShape, injectIntl, defineMessages} from 'mm-intl';
+
+const holders = defineMessages({
+ collapse: {
+ id: 'post_attachment.collapse',
+ defaultMessage: '▲ collapse text'
+ },
+ more: {
+ id: 'post_attachment.more',
+ defaultMessage: '▼ read more'
+ }
+});
+
+class PostAttachment extends React.Component {
constructor(props) {
super(props);
@@ -28,7 +41,7 @@ export default class PostAttachment extends React.Component {
getInitState() {
const shouldCollapse = this.shouldCollapse();
const text = TextFormatting.formatText(this.props.attachment.text || '');
- const uncollapsedText = text + (shouldCollapse ? '<a class="attachment-link-more" href="#">▲ collapse text</a>' : '');
+ const uncollapsedText = text + (shouldCollapse ? `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.collapse)}</a>` : '');
const collapsedText = shouldCollapse ? this.getCollapsedText() : text;
return {
@@ -62,7 +75,7 @@ export default class PostAttachment extends React.Component {
text = text.substr(0, 700);
}
- return TextFormatting.formatText(text) + '<a class="attachment-link-more" href="#">▼ read more</a>';
+ return TextFormatting.formatText(text) + `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.more)}</a>`;
}
getFieldsTable() {
@@ -292,5 +305,8 @@ export default class PostAttachment extends React.Component {
}
PostAttachment.propTypes = {
+ intl: intlShape.isRequired,
attachment: React.PropTypes.object.isRequired
};
+
+export default injectIntl(PostAttachment);
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index b1657f0eb..16f8528b2 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -14,7 +14,20 @@ import YoutubeVideo from './youtube_video.jsx';
import providers from './providers.json';
-export default class PostBody extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ plusOne: {
+ id: 'post_body.plusOne',
+ defaultMessage: ' plus 1 other file'
+ },
+ plusMore: {
+ id: 'post_body.plusMore',
+ defaultMessage: ' plus {count} other files'
+ }
+});
+
+class PostBody extends React.Component {
constructor(props) {
super(props);
@@ -187,6 +200,7 @@ export default class PostBody extends React.Component {
}
render() {
+ const {formatMessage} = this.props.intl;
const post = this.props.post;
const filenames = this.props.post.filenames;
const parentPost = this.props.parentPost;
@@ -208,10 +222,12 @@ export default class PostBody extends React.Component {
username = parentPost.props.override_username;
}
- if (username.slice(-1) === 's') {
- apostrophe = '\'';
- } else {
- apostrophe = '\'s';
+ if (global.window.mm_locale === 'en') {
+ if (username.slice(-1) === 's') {
+ apostrophe = '\'';
+ } else {
+ apostrophe = '\'s';
+ }
}
name = (
<a
@@ -230,16 +246,23 @@ export default class PostBody extends React.Component {
message = parentPost.filenames[0].split('/').pop();
if (parentPost.filenames.length === 2) {
- message += ' plus 1 other file';
+ message += formatMessage(holders.plusOne);
} else if (parentPost.filenames.length > 2) {
- message += ` plus ${parentPost.filenames.length - 1} other files`;
+ message += formatMessage(holders.plusMore, {count: (parentPost.filenames.length - 1)});
}
}
comment = (
<div className='post__link'>
<span>
- {'Commented on '}{name}{apostrophe}{' message: '}
+ <FormattedMessage
+ id='post_body.commentedOn'
+ defaultMessage='Commented on {name}{apostrophe} message: '
+ values={{
+ name: (name),
+ apostrophe: apostrophe
+ }}
+ />
<a
className='theme'
onClick={this.props.handleCommentClick}
@@ -260,7 +283,10 @@ export default class PostBody extends React.Component {
href='#'
onClick={this.props.retryPost}
>
- {'Retry'}
+ <FormattedMessage
+ id='post_body.retry'
+ defaultMessage='Retry'
+ />
</a>
);
} else if (post.state === Constants.POST_LOADING) {
@@ -313,8 +339,11 @@ export default class PostBody extends React.Component {
}
PostBody.propTypes = {
+ intl: intlShape.isRequired,
post: React.PropTypes.object.isRequired,
parentPost: React.PropTypes.object,
retryPost: React.PropTypes.func.isRequired,
handleCommentClick: React.PropTypes.func.isRequired
};
+
+export default injectIntl(PostBody); \ No newline at end of file
diff --git a/web/react/components/post_focus_view.jsx b/web/react/components/post_focus_view.jsx
index adcd78839..b9b6acd5f 100644
--- a/web/react/components/post_focus_view.jsx
+++ b/web/react/components/post_focus_view.jsx
@@ -7,6 +7,8 @@ import PostStore from '../stores/post_store.jsx';
import ChannelStore from '../stores/channel_store.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
+import {FormattedMessage} from 'mm-intl';
+
export default class PostFocusView extends React.Component {
constructor(props) {
super(props);
@@ -73,7 +75,12 @@ export default class PostFocusView extends React.Component {
getIntroMessage() {
return (
<div className='channel-intro'>
- <h4 className='channel-intro__title'>{'Beginning of Channel Archives'}</h4>
+ <h4 className='channel-intro__title'>
+ <FormattedMessage
+ id='post_focus_view.beginning'
+ defaultMessage='Beginning of Channel Archives'
+ />
+ </h4>
</div>
);
}
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index 0fb9d7f4a..ddb393520 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -9,6 +9,8 @@ import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Constants from '../utils/constants.jsx';
+import {FormattedMessage} from 'mm-intl';
+
const Overlay = ReactBootstrap.Overlay;
const Popover = ReactBootstrap.Popover;
@@ -53,7 +55,10 @@ export default class PostInfo extends React.Component {
href='#'
onClick={this.props.handleCommentClick}
>
- {'Reply'}
+ <FormattedMessage
+ id='post_info.reply'
+ defaultMessage='Reply'
+ />
</a>
</li>
);
@@ -68,7 +73,10 @@ export default class PostInfo extends React.Component {
href='#'
onClick={(e) => this.setState({target: e.target, show: !this.state.show})}
>
- {'Permalink'}
+ <FormattedMessage
+ id='post_info.permalink'
+ defaultMessage='Permalink'
+ />
</a>
</li>
);
@@ -84,7 +92,10 @@ export default class PostInfo extends React.Component {
role='menuitem'
onClick={() => EventHelpers.showDeletePostModal(post, dataComments)}
>
- {'Delete'}
+ <FormattedMessage
+ id='post_info.del'
+ defaultMessage='Delete'
+ />
</a>
</li>
);
@@ -108,7 +119,10 @@ export default class PostInfo extends React.Component {
data-channelid={post.channel_id}
data-comments={dataComments}
>
- {'Edit'}
+ <FormattedMessage
+ id='post_info.edit'
+ defaultMessage='Edit'
+ />
</a>
</li>
);
@@ -183,7 +197,15 @@ export default class PostInfo extends React.Component {
var dropdown = this.createDropdown();
const permalink = TeamStore.getCurrentTeamUrl() + '/pl/' + post.id;
- const copyButtonText = this.state.copiedLink ? (<div>{'Copy '}<i className='fa fa-check'/></div>) : 'Copy';
+ const copyButtonText = this.state.copiedLink ? (
+ <div>
+ <FormattedMessage
+ id='post_info.copy'
+ defaultMessage='Copy '
+ />
+ <i className='fa fa-check'/></div>
+ ) : (<FormattedMessage id='post_info.copy' />);
+
const permalinkOverlay = (
<Popover
id='permalink-overlay'
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
index 856403af5..f108ace2e 100644
--- a/web/react/components/posts_view.jsx
+++ b/web/react/components/posts_view.jsx
@@ -8,6 +8,9 @@ import * as Utils from '../utils/utils.jsx';
import Post from './post.jsx';
import Constants from '../utils/constants.jsx';
import DelayedAction from '../utils/delayed_action.jsx';
+
+import {FormattedDate, FormattedMessage} from 'mm-intl';
+
const Preferences = Constants.Preferences;
export default class PostsView extends React.Component {
@@ -250,7 +253,15 @@ export default class PostsView extends React.Component {
className='date-separator'
>
<hr className='separator__hr' />
- <div className='separator__text'>{currentPostDay.toDateString()}</div>
+ <div className='separator__text'>
+ <FormattedDate
+ value={currentPostDay}
+ weekday='short'
+ month='short'
+ day='2-digit'
+ year='numeric'
+ />
+ </div>
</div>
);
}
@@ -276,7 +287,12 @@ export default class PostsView extends React.Component {
<hr
className='separator__hr'
/>
- <div className='separator__text'>{'New Messages'}</div>
+ <div className='separator__text'>
+ <FormattedMessage
+ id='posts_view.newMsg'
+ defaultMessage='New Messages'
+ />
+ </div>
</div>
);
}
@@ -420,7 +436,10 @@ export default class PostsView extends React.Component {
href='#'
onClick={this.loadMorePostsTop}
>
- {'Load more messages'}
+ <FormattedMessage
+ id='posts_view.loadMore'
+ defaultMessage='Load more messages'
+ />
</a>
);
} else {
@@ -436,7 +455,7 @@ export default class PostsView extends React.Component {
href='#'
onClick={this.loadMorePostsBottom}
>
- {'Load more messages'}
+ <FormattedMessage id='posts_view.loadMore' />
</a>
);
} else {
diff --git a/web/react/components/time_since.jsx b/web/react/components/time_since.jsx
index 0b549b1e6..ba8dbffcc 100644
--- a/web/react/components/time_since.jsx
+++ b/web/react/components/time_since.jsx
@@ -2,7 +2,8 @@
// See License.txt for license information.
import Constants from '../utils/constants.jsx';
-import * as Utils from '../utils/utils.jsx';
+
+import {FormattedRelative, FormattedDate} from 'mm-intl';
var Tooltip = ReactBootstrap.Tooltip;
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -20,20 +21,25 @@ export default class TimeSince extends React.Component {
clearInterval(this.intervalId);
}
render() {
- const displayDate = Utils.displayDate(this.props.eventTime);
- const displayTime = Utils.displayTime(this.props.eventTime);
-
if (this.props.sameUser) {
return (
<time className='post__time'>
- {Utils.displayTime(this.props.eventTime)}
+ <FormattedRelative value={this.props.eventTime} />
</time>
);
}
const tooltip = (
<Tooltip id={'time-since-tooltip-' + this.props.eventTime}>
- {displayDate + ' at ' + displayTime}
+ <FormattedDate
+ value={this.props.eventTime}
+ month='long'
+ day='numeric'
+ year='numeric'
+ hour12={true}
+ hour='numeric'
+ minute='2-digit'
+ />
</Tooltip>
);
@@ -44,7 +50,7 @@ export default class TimeSince extends React.Component {
overlay={tooltip}
>
<time className='post__time'>
- {Utils.displayDateTime(this.props.eventTime)}
+ <FormattedRelative value={this.props.eventTime} />
</time>
</OverlayTrigger>
);
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index d11f8a21c..90885e495 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -9,10 +9,20 @@ import Constants from '../utils/constants.jsx';
import FileInfoPreview from './file_info_preview.jsx';
import FileStore from '../stores/file_store.jsx';
import ViewImagePopoverBar from './view_image_popover_bar.jsx';
+
+import {intlShape, injectIntl, defineMessages} from 'mm-intl';
+
const Modal = ReactBootstrap.Modal;
const KeyCodes = Constants.KeyCodes;
-export default class ViewImageModal extends React.Component {
+const holders = defineMessages({
+ loading: {
+ id: 'view_image.loading',
+ defaultMessage: 'Loading '
+ }
+});
+
+class ViewImageModal extends React.Component {
constructor(props) {
super(props);
@@ -235,6 +245,7 @@ export default class ViewImageModal extends React.Component {
fileUrl={fileUrl}
fileInfo={this.state.fileInfo}
maxHeight={this.state.imgHeight}
+ formatMessage={this.props.intl.formatMessage}
/>
);
} else {
@@ -243,6 +254,7 @@ export default class ViewImageModal extends React.Component {
filename={filename}
fileUrl={fileUrl}
fileInfo={fileInfo}
+ formatMessage={this.props.intl.formatMessage}
/>
);
}
@@ -250,7 +262,12 @@ export default class ViewImageModal extends React.Component {
// display a progress indicator when the preview for an image is still loading
const progress = Math.floor(this.state.progress[this.state.imgId]);
- content = <LoadingImagePreview progress={progress} />;
+ content = (
+ <LoadingImagePreview
+ progress={progress}
+ loading={this.props.intl.formatMessage(holders.loading)}
+ />
+ );
}
let leftArrow = null;
@@ -335,6 +352,7 @@ ViewImageModal.defaultProps = {
startId: 0
};
ViewImageModal.propTypes = {
+ intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func.isRequired,
filenames: React.PropTypes.array,
@@ -344,12 +362,12 @@ ViewImageModal.propTypes = {
startId: React.PropTypes.number
};
-function LoadingImagePreview({progress}) {
+function LoadingImagePreview({progress, loading}) {
let progressView = null;
if (progress) {
progressView = (
<span className='loader-percent'>
- {'Loading ' + progress + '%'}
+ {loading + progress + '%'}
</span>
);
}
@@ -386,3 +404,5 @@ function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) {
</a>
);
}
+
+export default injectIntl(ViewImageModal); \ No newline at end of file
diff --git a/web/react/components/view_image_popover_bar.jsx b/web/react/components/view_image_popover_bar.jsx
index 1287f4fba..97671b845 100644
--- a/web/react/components/view_image_popover_bar.jsx
+++ b/web/react/components/view_image_popover_bar.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 ViewImagePopoverBar extends React.Component {
constructor(props) {
super(props);
@@ -16,7 +18,10 @@ export default class ViewImagePopoverBar extends React.Component {
data-title='Public Image'
onClick={this.props.getPublicLink}
>
- {'Get Public Link'}
+ <FormattedMessage
+ id='view_image_popover.publicLink'
+ defaultMessage='Get Public Link'
+ />
</a>
<span className='text'>{' | '}</span>
</div>
@@ -33,7 +38,16 @@ export default class ViewImagePopoverBar extends React.Component {
ref='imageFooter'
className={footerClass}
>
- <span className='pull-left text'>{'File ' + (this.props.fileId + 1) + ' of ' + this.props.totalFiles}</span>
+ <span className='pull-left text'>
+ <FormattedMessage
+ id='view_image_popover.file'
+ defaultMessage='File {count} of {total}'
+ values={{
+ count: (this.props.fileId + 1),
+ total: this.props.totalFiles
+ }}
+ />
+ </span>
<div className='image-links'>
{publicLink}
<a
@@ -41,7 +55,10 @@ export default class ViewImagePopoverBar extends React.Component {
download={this.props.filename}
className='text'
>
- {'Download'}
+ <FormattedMessage
+ id='view_image_popover.download'
+ defaultMessage='Download'
+ />
</a>
</div>
</div>
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
index 7abadf2b1..08ffef822 100644
--- a/web/react/stores/post_store.jsx
+++ b/web/react/stores/post_store.jsx
@@ -446,7 +446,7 @@ class PostStoreClass extends EventEmitter {
posts = {};
}
- post.message = '(message deleted)';
+ post.message = this.delete_message;
post.state = Constants.POST_DELETED;
post.filenames = [];
@@ -581,6 +581,9 @@ class PostStoreClass extends EventEmitter {
return commentCount;
}
+ deleteMessage(msg) {
+ this.delete_message = msg;
+ }
}
var PostStore = new PostStoreClass();
diff --git a/web/react/utils/channel_intro_messages.jsx b/web/react/utils/channel_intro_messages.jsx
index 9685f94b0..69e08f143 100644
--- a/web/react/utils/channel_intro_messages.jsx
+++ b/web/react/utils/channel_intro_messages.jsx
@@ -11,6 +11,8 @@ import Constants from '../utils/constants.jsx';
import TeamStore from '../stores/team_store.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
+import {FormattedMessage, FormattedHTMLMessage, FormattedDate} from 'mm-intl';
+
export function createChannelIntroMessage(channel) {
if (channel.type === 'D') {
return createDMIntroMessage(channel);
@@ -48,8 +50,13 @@ export function createDMIntroMessage(channel) {
</strong>
</div>
<p className='channel-intro-text'>
- {'This is the start of your direct message history with ' + teammateName + '.'}<br/>
- {'Direct messages and files shared here are not shown to people outside this area.'}
+ <FormattedHTMLMessage
+ id='intro_messages.DM'
+ defaultMessage='This is the start of your direct message history with {teammate}.<br />Direct messages and files shared here are not shown to people outside this area.'
+ values={{
+ teammate: teammateName
+ }}
+ />
</p>
{createSetHeaderButton(channel)}
</div>
@@ -58,7 +65,12 @@ export function createDMIntroMessage(channel) {
return (
<div className='channel-intro'>
- <p className='channel-intro-text'>{'This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.'}</p>
+ <p className='channel-intro-text'>
+ <FormattedMessage
+ id='intro_messages.teammate'
+ defaultMessage='This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.'
+ />
+ </p>
</div>
);
}
@@ -66,11 +78,13 @@ export function createDMIntroMessage(channel) {
export function createOffTopicIntroMessage(channel) {
return (
<div className='channel-intro'>
- <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
- <p className='channel-intro__content'>
- {'This is the start of ' + channel.display_name + ', a channel for non-work-related conversations.'}
- <br/>
- </p>
+ <FormattedHTMLMessage
+ id='intro_messages.offTopic'
+ defaultMessage='<h4 class="channel-intro__title">Beginning of {display_name}</h4><p class="channel-intro__content">This is the start of {display_name}, a channel for non-work-related conversations.<br/></p>'
+ values={{
+ display_name: channel.display_name
+ }}
+ />
{createSetHeaderButton(channel)}
{createInviteChannelMemberButton(channel, 'channel')}
</div>
@@ -87,7 +101,11 @@ export function createDefaultIntroMessage(channel) {
href='#'
onClick={EventHelpers.showInviteMemberModal}
>
- <i className='fa fa-user-plus'></i>{'Invite others to this team'}
+ <i className='fa fa-user-plus'></i>
+ <FormattedMessage
+ id='intro_messages.inviteOthers'
+ defaultMessage='Invite others to this team'
+ />
</a>
);
} else {
@@ -97,19 +115,24 @@ export function createDefaultIntroMessage(channel) {
href='#'
onClick={EventHelpers.showGetTeamInviteLinkModal}
>
- <i className='fa fa-user-plus'></i>{'Invite others to this team'}
+ <i className='fa fa-user-plus'></i>
+ <FormattedMessage
+ id='intro_messages.inviteOthers'
+ defaultMessage='Invite others to this team'
+ />
</a>
);
}
return (
<div className='channel-intro'>
- <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
- <p className='channel-intro__content'>
- <strong>{'Welcome to ' + channel.display_name + '!'}</strong>
- <br/><br/>
- {'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'}
- </p>
+ <FormattedHTMLMessage
+ id='intro_messages.default'
+ defaultMessage="<h4 class='channel-intro__title'>Beginning of {display_name}</h4><p class='channel-intro__content'><strong>Welcome to {display_name}!'</strong><br/><br/>This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.</p>"
+ values={{
+ display_name: channel.display_name
+ }}
+ />
{inviteModalLink}
{createSetHeaderButton(channel)}
<br/>
@@ -124,33 +147,83 @@ export function createStandardIntroMessage(channel) {
var uiType;
var memberMessage;
if (channel.type === 'P') {
- uiType = 'private group';
- memberMessage = ' Only invited members can see this private group.';
+ uiType = (
+ <FormattedMessage
+ id='intro_messages.group'
+ defaultMessage='private group'
+ />
+ );
+ memberMessage = (
+ <FormattedMessage
+ id='intro_messages.onlyInvited'
+ defaultMessage=' Only invited members can see this private group.'
+ />
+ );
} else {
- uiType = 'channel';
- memberMessage = ' Any member can join and read this channel.';
+ uiType = (
+ <FormattedMessage
+ id='intro_messages.channel'
+ defaultMessage='channel'
+ />
+ );
+ memberMessage = (
+ <FormattedMessage
+ id='intro_messages.anyMember'
+ defaultMessage=' Any member can join and read this channel.'
+ />
+ );
}
+ const date = (
+ <FormattedDate
+ value={channel.create_at}
+ month='long'
+ day='2-digit'
+ year='numeric'
+ />
+ );
+
var createMessage;
if (creatorName === '') {
- createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created on ' + Utils.displayDate(channel.create_at) + '.';
+ createMessage = (
+ <FormattedMessage
+ id='intro_messages.noCreator'
+ defaultMessage='This is the start of the {name} {type}, created on {date}.'
+ values={{
+ name: (uiName),
+ type: (uiType),
+ date: (date)
+ }}
+ />
+ );
} else {
createMessage = (
<span>
- {'This is the start of the '}
- <strong>{uiName}</strong>
- {' '}
- {uiType}{', created by '}
- <strong>{creatorName}</strong>
- {' on '}
- <strong>{Utils.displayDate(channel.create_at)}</strong>
+ <FormattedHTMLMessage
+ id='intro_messages.creator'
+ defaultMessage='This is the start of the <strong>{name}</strong> {type}, created by <strong>{creator}</strong> on <strong>{date}</strong>'
+ values={{
+ name: (uiName),
+ type: (uiType),
+ date: (date),
+ creator: creatorName
+ }}
+ />
</span>
);
}
return (
<div className='channel-intro'>
- <h4 className='channel-intro__title'>{'Beginning of ' + uiName}</h4>
+ <h4 className='channel-intro__title'>
+ <FormattedMessage
+ id='intro_messages.beginning'
+ defaultMessage='Beginning of {name}'
+ values={{
+ name: (uiName)
+ }}
+ />
+ </h4>
<p className='channel-intro__content'>
{createMessage}
{memberMessage}
@@ -169,7 +242,14 @@ function createInviteChannelMemberButton(channel, uiType) {
dialogType={ChannelInviteModal}
dialogProps={{channel}}
>
- <i className='fa fa-user-plus'></i>{'Invite others to this ' + uiType}
+ <i className='fa fa-user-plus'></i>
+ <FormattedMessage
+ id='intro_messages.invite'
+ defaultMessage='Invite others to this {type}'
+ values={{
+ type: (uiType)
+ }}
+ />
</ToggleModalButton>
);
}
@@ -181,7 +261,11 @@ function createSetHeaderButton(channel) {
dialogType={EditChannelHeaderModal}
dialogProps={{channel}}
>
- <i className='fa fa-pencil'></i>{'Set a header'}
+ <i className='fa fa-pencil'></i>
+ <FormattedMessage
+ id='intro_messages.setHeader'
+ defaultMessage='Set a Header'
+ />
</ToggleModalButton>
);
}
diff --git a/web/static/i18n/en.json b/web/static/i18n/en.json
index 890e7188d..b8958e76b 100644
--- a/web/static/i18n/en.json
+++ b/web/static/i18n/en.json
@@ -254,7 +254,7 @@
"admin.ldap.saving": "Saving Config...",
"admin.ldap.bannerHeading": "Note:",
"admin.ldap.bannerDesc": "If a user attribute changes on the LDAP server it will be updated the next time the user enters their credentials to log in to Mattermost. This includes if a user is made inactive or removed from an LDAP server. Synchronization with LDAP servers is planned in a future release.",
- "admin.ldap.noLicense": "<h4 className=\"banner__heading\">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href=\"http://mattermost.com\"target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>",
+ "admin.ldap.noLicense": "<h4 class=\"banner__heading\">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href=\"http://mattermost.com\"target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>",
"admin.ldap.title": "LDAP Settings",
"admin.ldap.enableTitle": "Enable Login With LDAP:",
"admin.ldap.true": "true",
@@ -489,6 +489,7 @@
"authorize.access": "Allow <strong>{appName}</strong> access?",
"authorize.deny": "Deny",
"authorize.allow": "Allow",
+ "center_panel.recent": "Click here to jump to recent messages. ",
"change_url.longer": "Must be longer than two characters",
"change_url.startWithLetter": "Must start with a letter or number",
"change_url.endWithLetter": "Must end with a letter or number",
@@ -551,6 +552,11 @@
"create_comment.commentTitle": "Comment",
"create_comment.file": "File uploading",
"create_comment.files": "Files uploading",
+ "create_post.comment": "Comment",
+ "create_post.post": "Post",
+ "create_post.write": "Write a message...",
+ "create_post.deleteMsg": "(message deleted)",
+ "create_post.tutorialTip": "<h4>Sending Messages</h4><p>Type here to write a message and press <strong>Enter</strong> to post it.</p><p>Click the <strong>Attachment</strong> button to upload an image or a file.</p>",
"delete_channel.channel": "channel",
"delete_channel.group": "group",
"delete_channel.confirm": "Confirm DELETE Channel",
@@ -588,6 +594,9 @@
"email_verify.resend": "Resend Email",
"email_verify.sent": " Verification email sent.",
"error_bar.preview_mode": "Preview Mode: Email notifications have not been configured",
+ "file_attachment.download": "Download",
+ "file_info_preview.type": "File type ",
+ "file_info_preview.size": "Size ",
"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}",
@@ -630,6 +639,12 @@
"login_email.email": "Email",
"login_email.pwd": "Password",
"login_email.signin": "Sign in",
+ "login_ldap.badTeam": "Bad team name",
+ "login_ldap.idlReq": "An LDAP ID is required",
+ "login_ldap.pwdReq": "An LDAP password is required",
+ "login_ldap.username": "LDAP Username",
+ "login_ldap.pwd": "LDAP Password",
+ "login_ldap.signin": "Sign in",
"login_username.badTeam": "Bad team name",
"login_username.usernameReq": "A username is required",
"login_username.pwdReq": "A password is required",
@@ -638,12 +653,6 @@
"login_username.username": "Username",
"login_username.pwd": "Password",
"login_username.signin": "Sign in",
- "login_ldap.badTeam": "Bad team name",
- "login_ldap.idlReq": "An LDAP ID is required",
- "login_ldap.pwdReq": "An LDAP password is required",
- "login_ldap.username": "LDAP Username",
- "login_ldap.pwd": "LDAP Password",
- "login_ldap.signin": "Sign in",
"login.gitlab": "with GitLab",
"login.google": "with Google Apps",
"login.changed": " Sign-in method changed successfully",
@@ -697,6 +706,19 @@
"navbar_dropdown.accountSettings": "Account Settings",
"navbar_dropdown.logout": "Logout",
"navbar_dropdown.about": "About Mattermost",
+ "navbar.viewInfo": "View Info",
+ "navbar.setHeader": "Set Channel Header...",
+ "navbar.setPurpose": "Set Channel Purpose...",
+ "navbar.addMembers": "Add Members",
+ "navbar.leave": "Leave Channel",
+ "navbar.manageMembers": "Manage Members",
+ "navbar.delete": "Delete Channel...",
+ "navbar.rename": "Rename Channel...",
+ "navbar.preferences": "Notification Preferences",
+ "navbar.toggle1": "Toggle sidebar",
+ "navbar.toggle2": "Toggle sidebar",
+ "navbar.click": "Click here",
+ "navbar.noHeader": "No channel header yet.{newline}{link} to add one.",
"channel_flow.invalidName": "Invalid Channel Name",
"channel_flow.alreadyExist": "A channel with that URL already exists",
"channel_flow.channel": "Channel",
@@ -737,9 +759,23 @@
"password_send.reset": "Reset my password",
"members_popover.msg": "Message",
"members_popover.title": "Members",
+ "post_attachment.collapse": "▲ collapse text",
+ "post_attachment.more": "▼ read more",
+ "post_body.plusOne": " plus 1 other file",
+ "post_body.plusMore": " plus {count} other files",
+ "post_body.commentedOn": "Commented on {name}{apostrophe} message: ",
+ "post_body.retry": "Retry",
"post_delete.notPosted": "Comment could not be posted",
"post_delete.someone": "Someone deleted the message on which you tried to post a comment.",
"post_delete.okay": "Okay",
+ "post_focus_view.beginning": "Beginning of Channel Archives",
+ "post_info.reply": "Reply",
+ "post_info.permalink": "Permalink",
+ "post_info.del": "Delete",
+ "post_info.edit": "Edit",
+ "post_info.copy": "Copy ",
+ "posts_view.newMsg": "New Messages",
+ "posts_view.loadMore": "Load more messages",
"register_app.required": "Required",
"register_app.optional": "Optional",
"register_app.nameError": "Application name must be filled in.",
@@ -841,6 +877,7 @@
"signup_user_completed.usernameLength": "Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'.",
"signup_user_completed.passwordLength": "Please enter at least {min} characters",
"signup_user_completed.expired": "You've already completed the signup process for this invitation or this invitation has expired.",
+ "signup_user_completed.emailHelp": "Valid email required for sign-up",
"signup_user_completed.userHelp": "Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'",
"signup_user_completed.emailIs": "Your email address is <strong>{email}</strong>. You'll use this address to sign in to {siteName}.",
"signup_user_completed.whatis": "What's your email address?",
@@ -1157,5 +1194,23 @@
"user.settings.security.gitlab": "GitLab SSO",
"user.settings.security.title": "Security Settings",
"user.settings.security.viewHistory": "View Access History",
- "user.settings.security.logoutActiveSessions": "View and Logout of Active Sessions"
-}
+ "user.settings.security.logoutActiveSessions": "View and Logout of Active Sessions",
+ "view_image_popover.publicLink": "Get Public Link",
+ "view_image_popover.file": "File {count} of {total}",
+ "view_image_popover.download": "Download",
+ "view_image.loading": "Loading ",
+ "intro_messages.DM": "This is the start of your direct message history with {teammate}.<br />Direct messages and files shared here are not shown to people outside this area.",
+ "intro_messages.teammate": "This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.",
+ "intro_messages.offTopic": "<h4 class=\"channel-intro__title\">Beginning of {display_name}</h4><p class=\"channel-intro__content\">This is the start of {display_name}, a channel for non-work-related conversations.<br/></p>",
+ "intro_messages.inviteOthers": "Invite others to this team",
+ "intro_messages.default": "<h4 class='channel-intro__title'>Beginning of {display_name}</h4><p class='channel-intro__content'><strong>Welcome to {display_name}!'</strong><br/><br/>This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.</p>",
+ "intro_messages.group": "private group",
+ "intro_messages.onlyInvited": " Only invited members can see this private group.",
+ "intro_messages.channel": "channel",
+ "intro_messages.anyMember": " Any member can join and read this channel.",
+ "intro_messages.noCreator": "This is the start of the {name} {type}, created on {date}.",
+ "intro_messages.creator": "This is the start of the <strong>{name}</strong> {type}, created by <strong>{creator}</strong> on <strong>{date}</strong>",
+ "intro_messages.beginning": "Beginning of {name}",
+ "intro_messages.invite": "Invite others to this {type}",
+ "intro_messages.setHeader": "Set a Header"
+} \ No newline at end of file
diff --git a/web/static/i18n/es.json b/web/static/i18n/es.json
index 92f3ba2ea..bfdde9cfd 100644
--- a/web/static/i18n/es.json
+++ b/web/static/i18n/es.json
@@ -78,8 +78,12 @@
"admin.analytics.title": "Estadísticas para {title}",
"admin.analytics.totalPosts": "Total de Mensajes",
"admin.analytics.totalUsers": "Total de Usuarios",
+ "admin.email.allowEmailSignInDescription": "Cuando es verdadero, Mattermost permite a los usuarios iniciar sesión utilizando el correo electrónico y contraseña.",
+ "admin.email.allowEmailSignInTitle": "Permitir inicio de sesión con Correo electrónico: ",
"admin.email.allowSignupDescription": "Cuando está en verdadero, Mattermost permite la creación de equipos y cuentas utilizando el correo electrónico y contraseña. Este valor debe estar en falso sólo cuando quieres limitar el inicio de sesión a través de servicios tipo OAuth o LDAP.",
"admin.email.allowSignupTitle": "Permitir inicio de sesión con correo:",
+ "admin.email.allowUsernameSignInDescription": "Cuando es verdadero, Mattermost permite a los usuarios iniciar sesión con el nombre de usuario y contraseña. Esta opción normalmente se utiliza cuando la verificación de correo electrónico está deshabilitada.",
+ "admin.email.allowUsernameSignInTitle": "Permitir inicio de sesión con Nombre de usuario: ",
"admin.email.connectionSecurityNone": "Ninguno",
"admin.email.connectionSecurityNoneDescription": "Mattermost enviará los correos electrónicos sobre conexiones no seguras.",
"admin.email.connectionSecurityStart": "STARTTLS",
@@ -474,6 +478,7 @@
"authorize.app": "La app {appName} quiere tener la abilidad de accesar y modificar tu información básica.",
"authorize.deny": "Denegar",
"authorize.title": "Una aplicación quiere conectarse con tu cuenta de {teamName}",
+ "center_panel.recent": "Pincha aquí para ir a los mensajes más recientes. ",
"chanel_header.addMembers": "Agregar Miembros",
"change_url.close": "Cerrar",
"change_url.endWithLetter": "Debe terminar con una letra o número",
@@ -490,14 +495,14 @@
"channel_flow.invalidName": "Nombre de Canal Inválido",
"channel_flow.set_url_title": "Asignar URL de {term}",
"channel_header.channel": "Canal",
- "channel_header.channelHeader": "Encabezado del Canal...",
- "channel_header.delete": "Eliminar {term}...",
+ "channel_header.channelHeader": "Asignar Encabezado del Canal...",
+ "channel_header.delete": "Borrar {term}...",
"channel_header.group": "Grupo",
- "channel_header.leave": "Abondanar {term}",
+ "channel_header.leave": "Abandonar ",
"channel_header.manageMembers": "Administrar Miembros",
"channel_header.notificationPreferences": "Preferencias de Notificación",
"channel_header.recentMentions": "Menciones recientes",
- "channel_header.rename": "Renombrar {term}...",
+ "channel_header.rename": "Renombrar ",
"channel_header.setHeader": "Encabezado del {term}...",
"channel_header.setPurpose": "Propósito del {term}...",
"channel_header.viewInfo": "Ver Info",
@@ -530,12 +535,12 @@
"channel_modal.purpose": "Propósito",
"channel_notifications.allActivity": "Para toda actividad",
"channel_notifications.allUnread": "Para todos los mensajes sin leer",
- "channel_notifications.globalDefault": "Predeterminado global ({notifyLevel})",
+ "channel_notifications.globalDefault": "Predeterminada",
"channel_notifications.markUnread": "Marcar Canal como No Leido",
"channel_notifications.never": "Nunca",
"channel_notifications.onlyMentions": "Sólo para menciones",
"channel_notifications.override": "Seleccionar una opción diferente a \"Predeterminada\" anulará las configuraciones globales de notificación. Las notificaciones de Escritorio están disponibles para Firefox, Safari, y Chrome.",
- "channel_notifications.preferences": "Preferencias de Notificación de ",
+ "channel_notifications.preferences": "Preferencias de Notificación para ",
"channel_notifications.sendDesktop": "Enviar notificaciones de escritorio",
"channel_notifications.unreadInfo": "El nombre del canal está en negritas en la barra lateral cuando hay mensajes sin leer. Al elegir \"Sólo para menciones\" sólo lo dejará en negritas cuando seas mencionado.",
"choose_auth_page.emailCreate": "Crea un nuevo equipo con tu cuenta de correo",
@@ -565,18 +570,23 @@
"create_comment.commentTitle": "Comentario",
"create_comment.file": "Subiendo archivo",
"create_comment.files": "Subiendo archivos",
+ "create_post.comment": "Comentario",
+ "create_post.deleteMsg": "(mensaje eliminado)",
+ "create_post.post": "Mensaje",
+ "create_post.tutorialTip": "<h4>Enviar Mensajes</h4> <p>Escribe aquí para redactar un mensaje y presiona <strong>Retorno</strong> para enviarlo.</p><p>Pincha el botón de <strong>Adjuntar</strong> para subir una imagen o archivo.</p>",
+ "create_post.write": "Escribe un mensaje...",
"delete_channel.cancel": "Cancelar",
"delete_channel.channel": "canal",
- "delete_channel.confirm": "Confirmar la ELIMINACIÓN del Canal",
- "delete_channel.del": "Eliminar",
+ "delete_channel.confirm": "Confirmar BORRAR Canal",
+ "delete_channel.del": "Borrar",
"delete_channel.group": "grupo",
- "delete_channel.question": "¿Estás seguro de querer eliminar el {term} {display_name}?",
+ "delete_channel.question": "¿Estás seguro de querer borrar el ",
"delete_post.cancel": "Cancelar",
"delete_post.comment": "Comentario",
"delete_post.confirm": "Confirmar Eliminación del {term}",
- "delete_post.del": "Eliminar",
+ "delete_post.del": "Borrar",
"delete_post.post": "Mensaje",
- "delete_post.question": "¿Estás seguro(a) de querer eliminar este {term}?",
+ "delete_post.question": "¿Estás seguro(a) de querer borrar este {term}?",
"delete_post.warning": "Este mensaje tiene {count} comentario(s).",
"edit_channel_header_modal.cancel": "Cancelar",
"edit_channel_header_modal.description": "Edita el texto que aparece al lado del nombre del canal en el encabezado del canal.",
@@ -606,6 +616,9 @@
"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_attachment.download": "Descargar",
+ "file_info_preview.size": "Tamaño ",
+ "file_info_preview.type": "Tipo de archivo ",
"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.",
@@ -639,7 +652,22 @@
"get_link.close": "Cerrar",
"get_link.copy": "Copiar Enlace",
"get_team_invite_link_modal.help": "Enviar a los compañeros de equipo el enlace que se muestra a continuación para permitirles registrarse a este equipo.",
+ "get_team_invite_link_modal.helpDisabled": "La creación de usuario ha sido deshabilitada para tu equipo. Por favor solicita más detalles a tu administrador de equipo.",
"get_team_invite_link_modal.title": "Enlace de Invitación al Equipo",
+ "intro_messages.DM": "Este es el inicio de tu historial de mensajes directos con {teammate}.<br />Los mensajes directos y archivos que se comparten aquí no son mostrados a personas fuera de esta área.",
+ "intro_messages.anyMember": " Cualquier miembro se puede unir y leer este canal.",
+ "intro_messages.beginning": "Inicio de {name}",
+ "intro_messages.channel": "canal",
+ "intro_messages.creator": "Este es el inicio del {type} <strong>{name}</strong>, creado por <strong>{creator}</strong> el <strong>{date}</strong>",
+ "intro_messages.default": "<h4 class='channel-intro__title'>Inicio de {display_name}</h4><p class='channel-intro__content'><strong>¡Bienvenido a {display_name}!</strong><br/><br/>Este es el primer canal que ven tus compañeros cuando se registran - utilizalo para colocar mensajes que todos deberían leer.</p>",
+ "intro_messages.group": "grupo privado",
+ "intro_messages.invite": "Invita a otros a este {type}",
+ "intro_messages.inviteOthers": "Invita a otros a este equipo",
+ "intro_messages.noCreator": "Este es el inicio del {type} {name}, creado el {date}.",
+ "intro_messages.offTopic": "<h4 class=\"channel-intro__title\">Inicio de {display_name}</h4><p class=\"channel-intro__content\">Este es el inicio de {display_name}, un canal para tener conversaciones no relacionadas trabajo.<br/></p>",
+ "intro_messages.onlyInvited": " Sólo miembros invitados pueden ver este grupo privado.",
+ "intro_messages.setHeader": "Asignar un Encabezado",
+ "intro_messages.teammate": "Este es el inicio de tu historial de mensajes directos con este compañero. Los mensajes directos y archivos que se comparten aquí no son mostrados a personas fuera de esta área.",
"invite_member.addAnother": "Agregar otro",
"invite_member.autoJoin": "Las personas invitadas se unirán automáticamente al canal <strong>{channel}</strong>.",
"invite_member.cancel": "Cancelar",
@@ -656,7 +684,6 @@
"invite_member.send": "Enviar Invitaciones",
"invite_member.send2": "Enviar Invitaciones",
"invite_member.sending": " Enviando",
- "invite_member.teamInvite": "Invitación de Equipo",
"invite_member.teamInviteLink": "También puedes invitar personas usando el {link}.",
"loading_screen.loading": "Cargando",
"login.changed": " Cambiado el método de inicio de sesión satisfactoriamente",
@@ -683,6 +710,14 @@
"login_ldap.pwdReq": "La contraseña LDAP es obligatoria",
"login_ldap.signin": "Entrar",
"login_ldap.username": "Usuario LDAP",
+ "login_username.badTeam": "Mal nombre de equipo",
+ "login_username.pwd": "Contraseña",
+ "login_username.pwdReq": "La contraseña es obligatoria",
+ "login_username.signin": "Ingresar",
+ "login_username.userNotFoundError": "No encontramos una cuenta existente que coincida con tu nombre de usuario en este equipo.",
+ "login_username.username": "Nombre de usuario",
+ "login_username.usernameReq": "El nombre de usuario es obligatorio",
+ "login_username.verifyEmailError": "Por favor válida tu dirección de correo electrónico. Te hemos enviado un correo, revisa tu bandeja de entrada.",
"member_item.add": " Agregar",
"member_item.makeAdmin": "Convertir en Admin de Equipo",
"member_item.member": "Miembro",
@@ -714,6 +749,19 @@
"msg_typing.areTyping": "{users} y {last} están escribiendo...",
"msg_typing.isTyping": "{user} está escribiendo...",
"msg_typing.someone": "Alguien",
+ "navbar.addMembers": "Agregar Miembros",
+ "navbar.click": "Pincha aquí",
+ "navbar.delete": "Borrar Canal...",
+ "navbar.leave": "Abandonar Canal",
+ "navbar.manageMembers": "Administrar Miembros",
+ "navbar.noHeader": "Todavía no hay un encabezado.{newline}{link} para agregar uno.",
+ "navbar.preferences": "Preferencias de Notificación",
+ "navbar.rename": "Renombrar Canal...",
+ "navbar.setHeader": "Asignar Encabezado del Canal...",
+ "navbar.setPurpose": "Asignar Propósito del Canal...",
+ "navbar.toggle1": "Mostrar Barra",
+ "navbar.toggle2": "Esconder Barra",
+ "navbar.viewInfo": "Ver Info",
"navbar_dropdown.about": "Acerca de Mattermost",
"navbar_dropdown.accountSettings": "Configurar Cuenta",
"navbar_dropdown.console": "Consola de Sistema",
@@ -740,9 +788,23 @@
"password_send.link": "<p>Se ha enviado un enlace para restablecer la contraseña a <b>{email}</b> para tu equipo <b>{teamDisplayName}</b> en {hostname}.</p>",
"password_send.reset": "Restablecer mi contraseña",
"password_send.title": "Restablecer Contraseña",
+ "post_attachment.collapse": "▲ colapsar texto",
+ "post_attachment.more": "▼ leer más",
+ "post_body.commentedOn": "Comentó el mensaje de {name}{apostrophe}: ",
+ "post_body.plusMore": " más {count} otros archivos",
+ "post_body.plusOne": " más 1 archivo",
+ "post_body.retry": "Reintentar",
"post_delete.notPosted": "No se pudo enviar el comentario",
"post_delete.okay": "Ok",
"post_delete.someone": "Alguien borró el mensaje que querías comentar.",
+ "post_focus_view.beginning": "Inicio de los Archivos del Canal",
+ "post_info.copy": "Copiar ",
+ "post_info.del": "Borrar",
+ "post_info.edit": "Editar",
+ "post_info.permalink": "Enlace permanente",
+ "post_info.reply": "Responder",
+ "posts_view.loadMore": "Cargar más mensajes",
+ "posts_view.newMsg": "Nuevos Mensajes",
"register_app.callback": "Callback URL",
"register_app.callbackError": "Al menos un callback URL debe ser ingresado.",
"register_app.cancel": "Cancelar",
@@ -841,6 +903,7 @@
"signup_user_completed.choosePwd": "Escoge tu contraseña",
"signup_user_completed.chooseUser": "Escoge tu nombre de usuario",
"signup_user_completed.create": "Crea una Cuenta",
+ "signup_user_completed.emailHelp": "Para registrarte es necesario un correo electrónico válido",
"signup_user_completed.emailIs": "Tu dirección de correo electrónico es <strong>{email}</strong>. Utiliza está dirección para ingresar a {siteName}.",
"signup_user_completed.expired": "Ya haz completado el proceso de registro para esta invitación, o esta invitación ya ha expirado.",
"signup_user_completed.gitlab": "con GitLab",
@@ -1134,5 +1197,9 @@
"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_profile.notShared": "Correo no compartido"
-}
+ "user_profile.notShared": "Correo no compartido",
+ "view_image.loading": "Cargando ",
+ "view_image_popover.download": "Descargar",
+ "view_image_popover.file": "Archivo {count} de {total}",
+ "view_image_popover.publicLink": "Obtener Enlace Público"
+} \ No newline at end of file