summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/react/components/about_build_modal.jsx65
-rw-r--r--web/react/components/access_history_modal.jsx311
-rw-r--r--web/react/components/activity_log_modal.jsx99
-rw-r--r--web/react/components/admin_console/analytics.jsx20
-rw-r--r--web/react/components/admin_console/user_item.jsx155
-rw-r--r--web/react/components/change_url_modal.jsx54
-rw-r--r--web/react/components/confirm_modal.jsx6
-rw-r--r--web/react/components/get_link_modal.jsx22
-rw-r--r--web/react/components/get_team_invite_link_modal.jsx27
-rw-r--r--web/react/components/invite_member_modal.jsx126
-rw-r--r--web/react/components/member_list_team_item.jsx50
-rw-r--r--web/react/components/more_channels.jsx45
-rw-r--r--web/react/components/more_direct_channels.jsx71
-rw-r--r--web/react/components/navbar_dropdown.jsx68
-rw-r--r--web/react/components/new_channel_flow.jsx66
-rw-r--r--web/react/components/new_channel_modal.jsx118
-rw-r--r--web/react/components/post_info.jsx41
-rw-r--r--web/react/components/register_app_modal.jsx157
-rw-r--r--web/react/components/search_results_item.jsx2
-rw-r--r--web/react/components/setting_item_max.jsx12
-rw-r--r--web/react/components/setting_item_min.jsx7
-rw-r--r--web/react/components/setting_picture.jsx32
-rw-r--r--web/react/components/setting_upload.jsx21
-rw-r--r--web/react/components/sidebar.jsx109
-rw-r--r--web/react/components/sidebar_header.jsx23
-rw-r--r--web/react/components/team_export_tab.jsx44
-rw-r--r--web/react/components/team_general_tab.jsx176
-rw-r--r--web/react/components/team_import_tab.jsx67
-rw-r--r--web/react/components/team_members_modal.jsx15
-rw-r--r--web/react/components/team_settings_modal.jsx34
-rw-r--r--web/react/components/unread_channel_indicator.jsx2
-rw-r--r--web/react/components/user_settings/custom_theme_chooser.jsx104
-rw-r--r--web/react/components/user_settings/import_theme_modal.jsx44
-rw-r--r--web/react/components/user_settings/manage_incoming_hooks.jsx49
-rw-r--r--web/react/components/user_settings/manage_languages.jsx14
-rw-r--r--web/react/components/user_settings/manage_outgoing_hooks.jsx131
-rw-r--r--web/react/components/user_settings/user_settings_advanced.jsx101
-rw-r--r--web/react/components/user_settings/user_settings_appearance.jsx40
-rw-r--r--web/react/components/user_settings/user_settings_developer.jsx42
-rw-r--r--web/react/components/user_settings/user_settings_display.jsx140
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx254
-rw-r--r--web/react/components/user_settings/user_settings_integrations.jsx51
-rw-r--r--web/react/components/user_settings/user_settings_modal.jsx84
-rw-r--r--web/react/components/user_settings/user_settings_notifications.jsx226
-rw-r--r--web/react/components/user_settings/user_settings_security.jsx153
-rw-r--r--web/sass-files/sass/partials/_post.scss4
-rw-r--r--web/sass-files/sass/partials/_responsive.scss40
-rw-r--r--web/static/i18n/en.json418
-rw-r--r--web/static/i18n/es.json421
49 files changed, 3738 insertions, 623 deletions
diff --git a/web/react/components/about_build_modal.jsx b/web/react/components/about_build_modal.jsx
index f70027498..fe48bb48e 100644
--- a/web/react/components/about_build_modal.jsx
+++ b/web/react/components/about_build_modal.jsx
@@ -3,6 +3,8 @@
var Modal = ReactBootstrap.Modal;
+import {FormattedMessage} from 'mm-intl';
+
export default class AboutBuildModal extends React.Component {
constructor(props) {
super(props);
@@ -17,13 +19,28 @@ export default class AboutBuildModal extends React.Component {
const config = global.window.mm_config;
const license = global.window.mm_license;
- let title = 'Team Edition';
+ let title = (
+ <FormattedMessage
+ id='about.teamEdtion'
+ defaultMessage='Team Edition'
+ />
+ );
let licensee;
if (config.BuildEnterpriseReady === 'true' && license.IsLicensed === 'true') {
- title = 'Enterprise Edition';
+ title = (
+ <FormattedMessage
+ id='about.enterpriseEdition'
+ defaultMessage='Enterprise Edition'
+ />
+ );
licensee = (
<div className='row form-group'>
- <div className='col-sm-3 info__label'>{'Licensed by:'}</div>
+ <div className='col-sm-3 info__label'>
+ <FormattedMessage
+ id='about.licensed'
+ defaultMessage='Licensed by:'
+ />
+ </div>
<div className='col-sm-9'>{license.Company}</div>
</div>
);
@@ -35,25 +52,50 @@ export default class AboutBuildModal extends React.Component {
onHide={this.doHide}
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'About Mattermost'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='about.title'
+ defaultMessage='About Mattermost'
+ />
+ </Modal.Title>
</Modal.Header>
<Modal.Body>
- <h4>{`Mattermost ${title}`}</h4>
+ <h4>{'Mattermost'} {title}</h4>
{licensee}
<div className='row form-group'>
- <div className='col-sm-3 info__label'>{'Version:'}</div>
+ <div className='col-sm-3 info__label'>
+ <FormattedMessage
+ id='about.version'
+ defaultMessage='Version:'
+ />
+ </div>
<div className='col-sm-9'>{config.Version}</div>
</div>
<div className='row form-group'>
- <div className='col-sm-3 info__label'>{'Build Number:'}</div>
+ <div className='col-sm-3 info__label'>
+ <FormattedMessage
+ id='about.number'
+ defaultMessage='Build Number:'
+ />
+ </div>
<div className='col-sm-9'>{config.BuildNumber}</div>
</div>
<div className='row form-group'>
- <div className='col-sm-3 info__label'>{'Build Date:'}</div>
+ <div className='col-sm-3 info__label'>
+ <FormattedMessage
+ id='about.date'
+ defaultMessage='Build Date:'
+ />
+ </div>
<div className='col-sm-9'>{config.BuildDate}</div>
</div>
<div className='row form-group'>
- <div className='col-sm-3 info__label'>{'Build Hash:'}</div>
+ <div className='col-sm-3 info__label'>
+ <FormattedMessage
+ id='about.hash'
+ defaultMessage='Build Hash:'
+ />
+ </div>
<div className='col-sm-9'>{config.BuildHash}</div>
</div>
</Modal.Body>
@@ -63,7 +105,10 @@ export default class AboutBuildModal extends React.Component {
className='btn btn-default'
onClick={this.doHide}
>
- {'Close'}
+ <FormattedMessage
+ id='about.close'
+ defaultMessage='Close'
+ />
</button>
</Modal.Footer>
</Modal>
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx
index 85c28ca5c..6319b5681 100644
--- a/web/react/components/access_history_modal.jsx
+++ b/web/react/components/access_history_modal.jsx
@@ -8,7 +8,188 @@ import * as AsyncClient from '../utils/async_client.jsx';
import LoadingScreen from './loading_screen.jsx';
import * as Utils from '../utils/utils.jsx';
-export default class AccessHistoryModal extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ sessionRevoked: {
+ id: 'access_history.sessionRevoked',
+ defaultMessage: 'The session with id {sessionId} was revoked'
+ },
+ channelCreated: {
+ id: 'access_history.channelCreated',
+ defaultMessage: 'Created the {channelName} channel/group'
+ },
+ establishedDM: {
+ id: 'access_history.establishedDM',
+ defaultMessage: 'Established a direct message channel with {username}'
+ },
+ nameUpdated: {
+ id: 'access_history.nameUpdated',
+ defaultMessage: 'Updated the {channelName} channel/group name'
+ },
+ headerUpdated: {
+ id: 'access_history.headerUpdated',
+ defaultMessage: 'Updated the {channelName} channel/group header'
+ },
+ channelDeleted: {
+ id: 'access_history.channelDeleted',
+ defaultMessage: 'Deleted the channel/group with the URL {url}'
+ },
+ userAdded: {
+ id: 'access_history.userAdded',
+ defaultMessage: 'Added {username} to the {channelName} channel/group'
+ },
+ userRemoved: {
+ id: 'access_history.userRemoved',
+ defaultMessage: 'Removed {username} to the {channelName} channel/group'
+ },
+ attemptedRegisterApp: {
+ id: 'access_history.attemptedRegisterApp',
+ defaultMessage: 'Attempted to register a new OAuth Application with ID {id}'
+ },
+ attemptedAllowOAuthAccess: {
+ id: 'access_history.attemptedAllowOAuthAccess',
+ defaultMessage: 'Attempted to allow a new OAuth service access'
+ },
+ successfullOAuthAccess: {
+ id: 'access_history.successfullOAuthAccess',
+ defaultMessage: 'Successfully gave a new OAuth service access'
+ },
+ failedOAuthAccess: {
+ id: 'access_history.failedOAuthAccess',
+ defaultMessage: 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback'
+ },
+ attemptedOAuthToken: {
+ id: 'access_history.attemptedOAuthToken',
+ defaultMessage: 'Attempted to get an OAuth access token'
+ },
+ successfullOAuthToken: {
+ id: 'access_history.successfullOAuthToken',
+ defaultMessage: 'Successfully added a new OAuth service'
+ },
+ oauthTokenFailed: {
+ id: 'access_history.oauthTokenFailed',
+ defaultMessage: 'Failed to get an OAuth access token - {token}'
+ },
+ attemptedLogin: {
+ id: 'access_history.attemptedLogin',
+ defaultMessage: 'Attempted to login'
+ },
+ successfullLogin: {
+ id: 'access_history.successfullLogin',
+ defaultMessage: 'Successfully logged in'
+ },
+ failedLogin: {
+ id: 'access_history.failedLogin',
+ defaultMessage: 'FAILED login attempt'
+ },
+ updatePicture: {
+ id: 'access_history.updatePicture',
+ defaultMessage: 'Updated your profile picture'
+ },
+ updateGeneral: {
+ id: 'access_history.updateGeneral',
+ defaultMessage: 'Updated the general settings of your account'
+ },
+ attemptedPassword: {
+ id: 'access_history.attemptedPassword',
+ defaultMessage: 'Attempted to change password'
+ },
+ successfullPassword: {
+ id: 'access_history.successfullPassword',
+ defaultMessage: 'Successfully changed password'
+ },
+ failedPassword: {
+ id: 'access_history.failedPassword',
+ defaultMessage: 'Failed to change password - tried to update user password who was logged in through oauth'
+ },
+ updatedRol: {
+ id: 'access_history.updatedRol',
+ defaultMessage: 'Updated user role(s) to '
+ },
+ member: {
+ id: 'access_history.member',
+ defaultMessage: 'member'
+ },
+ accountActive: {
+ id: 'access_history.accountActive',
+ defaultMessage: 'Account made active'
+ },
+ accountInactive: {
+ id: 'access_history.accountInactive',
+ defaultMessage: 'Account made inactive'
+ },
+ by: {
+ id: 'access_history.by',
+ defaultMessage: ' by {username}'
+ },
+ byAdmin: {
+ id: 'access_history.byAdmin',
+ defaultMessage: ' by an admin'
+ },
+ sentEmail: {
+ id: 'access_history.sentEmail',
+ defaultMessage: 'Sent an email to {email} to reset your password'
+ },
+ attemptedReset: {
+ id: 'access_history.attemptedReset',
+ defaultMessage: 'Attempted to reset password'
+ },
+ successfullReset: {
+ id: 'access_history.successfullReset',
+ defaultMessage: 'Successfully reset password'
+ },
+ updateGlobalNotifications: {
+ id: 'access_history.updateGlobalNotifications',
+ defaultMessage: 'Updated your global notification settings'
+ },
+ attemptedWebhookCreate: {
+ id: 'access_history.attemptedWebhookCreate',
+ defaultMessage: 'Attempted to create a webhook'
+ },
+ succcessfullWebhookCreate: {
+ id: 'access_history.successfullWebhookCreate',
+ defaultMessage: 'Successfully created a webhook'
+ },
+ failedWebhookCreate: {
+ id: 'access_history.failedWebhookCreate',
+ defaultMessage: 'Failed to create a webhook - bad channel permissions'
+ },
+ attemptedWebhookDelete: {
+ id: 'access_history.attemptedWebhookDelete',
+ defaultMessage: 'Attempted to delete a webhook'
+ },
+ successfullWebhookDelete: {
+ id: 'access_history.successfullWebhookDelete',
+ defaultMessage: 'Successfully deleted a webhook'
+ },
+ failedWebhookDelete: {
+ id: 'access_history.failedWebhookDelete',
+ defaultMessage: 'Failed to delete a webhook - inappropriate conditions'
+ },
+ logout: {
+ id: 'access_history.logout',
+ defaultMessage: 'Logged out of your account'
+ },
+ verified: {
+ id: 'access_history.verified',
+ defaultMessage: 'Sucessfully verified your email address'
+ },
+ revokedAll: {
+ id: 'access_history.revokedAll',
+ defaultMessage: 'Revoked all current sessions for the team'
+ },
+ loginAttempt: {
+ id: 'access_history.loginAttempt',
+ defaultMessage: ' (Login attempt)'
+ },
+ loginFailure: {
+ id: 'access_history.loginFailure',
+ defaultMessage: ' (Login failure)'
+ }
+});
+
+class AccessHistoryModal extends React.Component {
constructor(props) {
super(props);
@@ -70,11 +251,12 @@ export default class AccessHistoryModal extends React.Component {
this.setState({moreInfo: newMoreInfo});
}
handleRevokedSession(sessionId) {
- return 'The session with id ' + sessionId + ' was revoked';
+ return this.props.intl.formatMessage(holders.sessionRevoked, {sessionId: sessionId});
}
formatAuditInfo(currentAudit) {
const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, '');
+ const {formatMessage} = this.props.intl;
let currentAuditDesc = '';
if (currentActionURL.indexOf('/channels') === 0) {
@@ -96,17 +278,17 @@ export default class AccessHistoryModal extends React.Component {
switch (currentActionURL) {
case '/channels/create':
- currentAuditDesc = 'Created the ' + channelName + ' channel/group';
+ currentAuditDesc = formatMessage(holders.channelCreated, {channelName: channelName});
break;
case '/channels/create_direct':
- currentAuditDesc = 'Established a direct message channel with ' + Utils.getDirectTeammate(channelObj.id).username;
+ currentAuditDesc = formatMessage(holders.establishedDM, {username: Utils.getDirectTeammate(channelObj.id).username});
break;
case '/channels/update':
- currentAuditDesc = 'Updated the ' + channelName + ' channel/group name';
+ currentAuditDesc = formatMessage(holders.nameUpdated, {channelName: channelName});
break;
case '/channels/update_desc': // support the old path
case '/channels/update_header':
- currentAuditDesc = 'Updated the ' + channelName + ' channel/group header';
+ currentAuditDesc = formatMessage(holders.headerUpdated, {channelName: channelName});
break;
default: {
let userIdField = [];
@@ -123,11 +305,11 @@ export default class AccessHistoryModal extends React.Component {
}
if (/\/channels\/[A-Za-z0-9]+\/delete/.test(currentActionURL)) {
- currentAuditDesc = 'Deleted the channel/group with the URL ' + channelURL;
+ currentAuditDesc = formatMessage(holders.channelDeleted, {url: channelURL});
} else if (/\/channels\/[A-Za-z0-9]+\/add/.test(currentActionURL)) {
- currentAuditDesc = 'Added ' + username + ' to the ' + channelName + ' channel/group';
+ currentAuditDesc = formatMessage(holders.userAdded, {username: username, channelName: channelName});
} else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(currentActionURL)) {
- currentAuditDesc = 'Removed ' + username + ' from the ' + channelName + ' channel/group';
+ currentAuditDesc = formatMessage(holders.userRemoved, {username: username, channelName: channelName});
}
break;
@@ -141,31 +323,31 @@ export default class AccessHistoryModal extends React.Component {
const clientIdField = oauthInfo[0].split('=');
if (clientIdField[0] === 'client_id') {
- currentAuditDesc = 'Attempted to register a new OAuth Application with ID ' + clientIdField[1];
+ currentAuditDesc = formatMessage(holders.attemptedRegisterApp, {id: clientIdField[1]});
}
break;
}
case '/oauth/allow':
if (oauthInfo[0] === 'attempt') {
- currentAuditDesc = 'Attempted to allow a new OAuth service access';
+ currentAuditDesc = formatMessage(holders.attemptedAllowOAuthAccess);
} else if (oauthInfo[0] === 'success') {
- currentAuditDesc = 'Successfully gave a new OAuth service access';
+ currentAuditDesc = formatMessage(holders.successfullOAuthAccess);
} else if (oauthInfo[0] === 'fail - redirect_uri did not match registered callback') {
- currentAuditDesc = 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback';
+ currentAuditDesc = formatMessage(holders.failedOAuthAccess);
}
break;
case '/oauth/access_token':
if (oauthInfo[0] === 'attempt') {
- currentAuditDesc = 'Attempted to get an OAuth access token';
+ currentAuditDesc = formatMessage(holders.attemptedOAuthToken);
} else if (oauthInfo[0] === 'success') {
- currentAuditDesc = 'Successfully added a new OAuth service';
+ currentAuditDesc = formatMessage(holders.successfullOAuthToken);
} else {
const oauthTokenFailure = oauthInfo[0].split('-');
if (oauthTokenFailure[0].trim() === 'fail' && oauthTokenFailure[1]) {
- currentAuditDesc = 'Failed to get an OAuth access token - ' + oauthTokenFailure[1].trim();
+ currentAuditDesc = formatMessage(oauthTokenFailure, {token: oauthTokenFailure[1].trim()});
}
}
@@ -179,11 +361,11 @@ export default class AccessHistoryModal extends React.Component {
switch (currentActionURL) {
case '/users/login':
if (userInfo[0] === 'attempt') {
- currentAuditDesc = 'Attempted to login';
+ currentAuditDesc = formatMessage(holders.attemptedLogin);
} else if (userInfo[0] === 'success') {
- currentAuditDesc = 'Successfully logged in';
+ currentAuditDesc = formatMessage(holders.successfullLogin);
} else if (userInfo[0]) {
- currentAuditDesc = 'FAILED login attempt';
+ currentAuditDesc = formatMessage(holders.failedLogin);
}
break;
@@ -191,29 +373,29 @@ export default class AccessHistoryModal extends React.Component {
currentAuditDesc = this.handleRevokedSession(userInfo[0].split('=')[1]);
break;
case '/users/newimage':
- currentAuditDesc = 'Updated your profile picture';
+ currentAuditDesc = formatMessage(holders.updatePicture);
break;
case '/users/update':
- currentAuditDesc = 'Updated the general settings of your account';
+ currentAuditDesc = formatMessage(holders.updateGeneral);
break;
case '/users/newpassword':
if (userInfo[0] === 'attempted') {
- currentAuditDesc = 'Attempted to change password';
+ currentAuditDesc = formatMessage(holders.attemptedPassword);
} else if (userInfo[0] === 'completed') {
- currentAuditDesc = 'Successfully changed password';
+ currentAuditDesc = formatMessage(holders.successfullPassword);
} else if (userInfo[0] === 'failed - tried to update user password who was logged in through oauth') {
- currentAuditDesc = 'Failed to change password - tried to update user password who was logged in through oauth';
+ currentAuditDesc = formatMessage(holders.failedPassword);
}
break;
case '/users/update_roles': {
const userRoles = userInfo[0].split('=')[1];
- currentAuditDesc = 'Updated user role(s) to ';
+ currentAuditDesc = formatMessage(holders.updatedRol);
if (userRoles.trim()) {
currentAuditDesc += userRoles;
} else {
- currentAuditDesc += 'member';
+ currentAuditDesc += formatMessage(holders.member);
}
break;
@@ -225,9 +407,9 @@ export default class AccessHistoryModal extends React.Component {
/* Either describes account activation/deactivation or a revoked session as part of an account deactivation */
if (updateType === 'active') {
if (updateField === 'true') {
- currentAuditDesc = 'Account made active';
+ currentAuditDesc = formatMessage(holders.accountActive);
} else if (updateField === 'false') {
- currentAuditDesc = 'Account made inactive';
+ currentAuditDesc = formatMessage(holders.accountInactive);
}
const actingUserInfo = userInfo[1].split('=');
@@ -235,9 +417,9 @@ export default class AccessHistoryModal extends React.Component {
const actingUser = UserStore.getProfile(actingUserInfo[1]);
const currentUser = UserStore.getCurrentUser();
if (currentUser && actingUser && (Utils.isAdmin(currentUser.roles) || Utils.isSystemAdmin(currentUser.roles))) {
- currentAuditDesc += ' by ' + actingUser.username;
+ currentAuditDesc += formatMessage(holders.by, {username: actingUser.username});
} else if (currentUser && actingUser) {
- currentAuditDesc += ' by an admin';
+ currentAuditDesc += formatMessage(holders.byAdmin);
}
}
} else if (updateType === 'session_id') {
@@ -247,18 +429,18 @@ export default class AccessHistoryModal extends React.Component {
break;
}
case '/users/send_password_reset':
- currentAuditDesc = 'Sent an email to ' + userInfo[0].split('=')[1] + ' to reset your password';
+ currentAuditDesc = formatMessage(holders.sentEmail, {email: userInfo[0].split('=')[1]});
break;
case '/users/reset_password':
if (userInfo[0] === 'attempt') {
- currentAuditDesc = 'Attempted to reset password';
+ currentAuditDesc = formatMessage(holders.attemptedReset);
} else if (userInfo[0] === 'success') {
- currentAuditDesc = 'Successfully reset password';
+ currentAuditDesc = formatMessage(holders.successfullReset);
}
break;
case '/users/update_notify':
- currentAuditDesc = 'Updated your global notification settings';
+ currentAuditDesc = formatMessage(holders.updateGlobalNotifications);
break;
default:
break;
@@ -269,21 +451,21 @@ export default class AccessHistoryModal extends React.Component {
switch (currentActionURL) {
case '/hooks/incoming/create':
if (webhookInfo[0] === 'attempt') {
- currentAuditDesc = 'Attempted to create a webhook';
+ currentAuditDesc = formatMessage(holders.attemptedWebhookCreate);
} else if (webhookInfo[0] === 'success') {
- currentAuditDesc = 'Successfully created a webhook';
+ currentAuditDesc = formatMessage(holders.succcessfullWebhookCreate);
} else if (webhookInfo[0] === 'fail - bad channel permissions') {
- currentAuditDesc = 'Failed to create a webhook - bad channel permissions';
+ currentAuditDesc = formatMessage(holders.failedWebhookCreate);
}
break;
case '/hooks/incoming/delete':
if (webhookInfo[0] === 'attempt') {
- currentAuditDesc = 'Attempted to delete a webhook';
+ currentAuditDesc = formatMessage(holders.attemptedWebhookDelete);
} else if (webhookInfo[0] === 'success') {
- currentAuditDesc = 'Successfully deleted a webhook';
+ currentAuditDesc = formatMessage(holders.successfullWebhookDelete);
} else if (webhookInfo[0] === 'fail - inappropriate conditions') {
- currentAuditDesc = 'Failed to delete a webhook - inappropriate conditions';
+ currentAuditDesc = formatMessage(holders.failedWebhookDelete);
}
break;
@@ -293,10 +475,10 @@ export default class AccessHistoryModal extends React.Component {
} else {
switch (currentActionURL) {
case '/logout':
- currentAuditDesc = 'Logged out of your account';
+ currentAuditDesc = formatMessage(holders.logout);
break;
case '/verify_email':
- currentAuditDesc = 'Sucessfully verified your email address';
+ currentAuditDesc = formatMessage(holders.verified);
break;
default:
break;
@@ -307,7 +489,7 @@ export default class AccessHistoryModal extends React.Component {
if (!currentAuditDesc) {
/* Currently not called anywhere */
if (currentAudit.extra_info.indexOf('revoked_all=') >= 0) {
- currentAuditDesc = 'Revoked all current sessions for the team';
+ currentAuditDesc = formatMessage(holders.revokedAll);
} else {
let currentActionDesc = '';
if (currentActionURL && currentActionURL.lastIndexOf('/') !== -1) {
@@ -328,12 +510,14 @@ export default class AccessHistoryModal extends React.Component {
}
const currentDate = new Date(currentAudit.create_at);
- const currentAuditInfo = currentDate.toDateString() + ' - ' + currentDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc;
+ const currentAuditInfo = currentDate.toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'}) + ' - ' +
+ currentDate.toLocaleTimeString(global.window.mm_locale, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc;
return currentAuditInfo;
}
render() {
var accessList = [];
+ const {formatMessage} = this.props.intl;
for (var i = 0; i < this.state.audits.length; i++) {
const currentAudit = this.state.audits[i];
const currentAuditInfo = this.formatAuditInfo(currentAudit);
@@ -344,7 +528,10 @@ export default class AccessHistoryModal extends React.Component {
className='theme'
onClick={this.handleMoreInfo.bind(this, i)}
>
- {'More info'}
+ <FormattedMessage
+ id='access_history.moreInfo'
+ defaultMessage='More info'
+ />
</a>
);
@@ -354,17 +541,33 @@ export default class AccessHistoryModal extends React.Component {
if (currentAudit.action.search('/users/login') >= 0) {
if (currentAudit.extra_info === 'attempt') {
- currentAudit.session_id += ' (Login attempt)';
+ currentAudit.session_id += formatMessage(holders.loginAttempt);
} else {
- currentAudit.session_id += ' (Login failure)';
+ currentAudit.session_id += formatMessage(holders.loginFailure);
}
}
}
moreInfo = (
<div>
- <div>{'IP: ' + currentAudit.ip_address}</div>
- <div>{'Session ID: ' + currentAudit.session_id}</div>
+ <div>
+ <FormattedMessage
+ id='access_history.ip'
+ defaultMessage='IP: {ip}'
+ values={{
+ ip: currentAudit.ip_address
+ }}
+ />
+ </div>
+ <div>
+ <FormattedMessage
+ id='access_history.session'
+ defaultMessage='Session ID: {id}'
+ values={{
+ id: currentAudit.session_id
+ }}
+ />
+ </div>
</div>
);
}
@@ -404,7 +607,12 @@ export default class AccessHistoryModal extends React.Component {
bsSize='large'
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'Access History'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='access_history.title'
+ defaultMessage='Access History'
+ />
+ </Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
{content}
@@ -415,6 +623,9 @@ export default class AccessHistoryModal extends React.Component {
}
AccessHistoryModal.propTypes = {
+ intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired
};
+
+export default injectIntl(AccessHistoryModal); \ No newline at end of file
diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx
index 2c42f5971..f8a2af571 100644
--- a/web/react/components/activity_log_modal.jsx
+++ b/web/react/components/activity_log_modal.jsx
@@ -8,6 +8,8 @@ const Modal = ReactBootstrap.Modal;
import LoadingScreen from './loading_screen.jsx';
import * as Utils from '../utils/utils.jsx';
+import {FormattedMessage} from 'mm-intl';
+
export default class ActivityLogModal extends React.Component {
constructor(props) {
super(props);
@@ -102,16 +104,31 @@ export default class ActivityLogModal extends React.Component {
devicePicture = 'fa fa-windows';
} else if (currentSession.device_id && currentSession.device_id.indexOf('apple:') === 0) {
devicePicture = 'fa fa-apple';
- devicePlatform = 'iPhone Native App';
+ devicePlatform = (
+ <FormattedMessage
+ id='activity_log_modal.iphoneNativeApp'
+ defaultMessage='iPhone Native App'
+ />
+ );
} else if (currentSession.device_id && currentSession.device_id.indexOf('android:') === 0) {
- devicePlatform = 'Android Native App';
+ devicePlatform = (
+ <FormattedMessage
+ id='activity_log_modal.androidNativeApp'
+ defaultMessage='Android Native App'
+ />
+ );
devicePicture = 'fa fa-android';
} else if (currentSession.props.platform === 'Macintosh' ||
currentSession.props.platform === 'iPhone') {
devicePicture = 'fa fa-apple';
} else if (currentSession.props.platform === 'Linux') {
if (currentSession.props.os.indexOf('Android') >= 0) {
- devicePlatform = 'Android';
+ devicePlatform = (
+ <FormattedMessage
+ id='activity_log_modal.android'
+ defaultMessage='Android'
+ />
+ );
devicePicture = 'fa fa-android';
} else {
devicePicture = 'fa fa-linux';
@@ -122,10 +139,43 @@ export default class ActivityLogModal extends React.Component {
if (this.state.moreInfo[i]) {
moreInfo = (
<div>
- <div>{`First time active: ${firstAccessTime.toDateString()}, ${lastAccessTime.toLocaleTimeString()}`}</div>
- <div>{`OS: ${currentSession.props.os}`}</div>
- <div>{`Browser: ${currentSession.props.browser}`}</div>
- <div>{`Session ID: ${currentSession.id}`}</div>
+ <div>
+ <FormattedMessage
+ id='activity_log.firstTime'
+ defaultMessage='First time active: {date}, {time}'
+ values={{
+ date: firstAccessTime.toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'}),
+ time: lastAccessTime.toLocaleTimeString(global.window.mm_locale, {hour: '2-digit', minute: '2-digit'})
+ }}
+ />
+ </div>
+ <div>
+ <FormattedMessage
+ id='activity_log.os'
+ defaultMessage='OS: {os}'
+ values={{
+ os: currentSession.props.os
+ }}
+ />
+ </div>
+ <div>
+ <FormattedMessage
+ id='activity_log.browser'
+ defaultMessage='Browser: {browser}'
+ values={{
+ browser: currentSession.props.browser
+ }}
+ />
+ </div>
+ <div>
+ <FormattedMessage
+ id='activity_log.sessionId'
+ defaultMessage='Session ID: {id}'
+ values={{
+ id: currentSession.id
+ }}
+ />
+ </div>
</div>
);
} else {
@@ -135,7 +185,10 @@ export default class ActivityLogModal extends React.Component {
href='#'
onClick={this.handleMoreInfo.bind(this, i)}
>
- More info
+ <FormattedMessage
+ id='activity_log.moreInfo'
+ defaultMessage='More info'
+ />
</a>
);
}
@@ -148,7 +201,16 @@ export default class ActivityLogModal extends React.Component {
<div className='activity-log__report'>
<div className='report__platform'><i className={devicePicture} />{devicePlatform}</div>
<div className='report__info'>
- <div>{`Last activity: ${lastAccessTime.toDateString()}, ${lastAccessTime.toLocaleTimeString()}`}</div>
+ <div>
+ <FormattedMessage
+ id='activity_log.lastActivity'
+ defaultMessage='Last activity: {date}, {time}'
+ values={{
+ date: lastAccessTime.toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'}),
+ time: lastAccessTime.toLocaleTimeString(global.window.mm_locale, {hour: '2-digit', minute: '2-digit'})
+ }}
+ />
+ </div>
{moreInfo}
</div>
</div>
@@ -157,7 +219,10 @@ export default class ActivityLogModal extends React.Component {
onClick={this.submitRevoke.bind(this, currentSession.id)}
className='btn btn-primary'
>
- Logout
+ <FormattedMessage
+ id='activity_log.logout'
+ defaultMessage='Logout'
+ />
</button>
</div>
</div>
@@ -178,10 +243,20 @@ export default class ActivityLogModal extends React.Component {
bsSize='large'
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'Active Sessions'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='activity_log.activeSessions'
+ defaultMessage='Active Sessions'
+ />
+ </Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
- <p className='session-help-text'>{'Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the \'Logout\' button below to end a session.'}</p>
+ <p className='session-help-text'>
+ <FormattedMessage
+ id='activity_log.sessionsDescription'
+ defaultMessage="Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the 'Logout' button below to end a session."
+ />
+ </p>
{content}
</Modal.Body>
</Modal>
diff --git a/web/react/components/admin_console/analytics.jsx b/web/react/components/admin_console/analytics.jsx
index ff5903c62..a22c26c34 100644
--- a/web/react/components/admin_console/analytics.jsx
+++ b/web/react/components/admin_console/analytics.jsx
@@ -104,10 +104,12 @@ export default class Analytics extends React.Component {
let content;
if (this.props.postCountsDay.labels.length === 0) {
content = (
- <FormattedMessage
- id='admin.analytics.meaningful'
- defaultMessage='Not enough data for a meaningful representation.'
- />
+ <h5>
+ <FormattedMessage
+ id='admin.analytics.meaningful'
+ defaultMessage='Not enough data for a meaningful representation.'
+ />
+ </h5>
);
} else {
content = (
@@ -153,10 +155,12 @@ export default class Analytics extends React.Component {
let content;
if (this.props.userCountsWithPostsDay.labels.length === 0) {
content = (
- <FormattedMessage
- id='admin.analytics.meaningful'
- defaultMessage='Not enough data for a meaningful representation.'
- />
+ <h5>
+ <FormattedMessage
+ id='admin.analytics.meaningful'
+ defaultMessage='Not enough data for a meaningful representation.'
+ />
+ </h5>
);
} else {
content = (
diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx
index 59b4861e4..02b01b090 100644
--- a/web/react/components/admin_console/user_item.jsx
+++ b/web/react/components/admin_console/user_item.jsx
@@ -3,8 +3,30 @@
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
+import UserStore from '../../stores/user_store.jsx';
+import ConfirmModal from '../confirm_modal.jsx';
+import TeamStore from '../../stores/team_store.jsx';
-import {FormattedMessage} from 'mm-intl';
+import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
+
+var holders = defineMessages({
+ confirmDemoteRoleTitle: {
+ id: 'admin.user_item.confirmDemoteRoleTitle',
+ defaultMessage: 'Confirm demotion from System Admin role'
+ },
+ confirmDemotion: {
+ id: 'admin.user_item.confirmDemotion',
+ defaultMessage: 'Confirm Demotion'
+ },
+ confirmDemoteDescription: {
+ id: 'admin.user_item.confirmDemoteDescription',
+ defaultMessage: 'If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.'
+ },
+ confirmDemotionCmd: {
+ id: 'admin.user_item.confirmDemotionCmd',
+ defaultMessage: 'platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"'
+ }
+});
export default class UserItem extends React.Component {
constructor(props) {
@@ -16,25 +38,38 @@ export default class UserItem extends React.Component {
this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
this.handleMakeSystemAdmin = this.handleMakeSystemAdmin.bind(this);
this.handleResetPassword = this.handleResetPassword.bind(this);
+ this.handleDemote = this.handleDemote.bind(this);
+ this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
+ this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
- this.state = {};
+ this.state = {
+ serverError: null,
+ showDemoteModal: false,
+ user: null,
+ role: null
+ };
}
handleMakeMember(e) {
e.preventDefault();
- const data = {
- user_id: this.props.user.id,
- new_roles: ''
- };
+ const me = UserStore.getCurrentUser();
+ if (this.props.user.id === me.id) {
+ this.handleDemote(this.props.user, '');
+ } else {
+ const data = {
+ user_id: this.props.user.id,
+ new_roles: ''
+ };
- Client.updateRoles(data,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
+ Client.updateRoles(data,
+ () => {
+ this.props.refreshProfiles();
+ },
+ (err) => {
+ this.setState({serverError: err.message});
+ }
+ );
+ }
}
handleMakeActive(e) {
@@ -63,9 +98,31 @@ export default class UserItem extends React.Component {
handleMakeAdmin(e) {
e.preventDefault();
+ const me = UserStore.getCurrentUser();
+ if (this.props.user.id === me.id) {
+ this.handleDemote(this.props.user, 'admin');
+ } else {
+ const data = {
+ user_id: this.props.user.id,
+ new_roles: 'admin'
+ };
+
+ Client.updateRoles(data,
+ () => {
+ this.props.refreshProfiles();
+ },
+ (err) => {
+ this.setState({serverError: err.message});
+ }
+ );
+ }
+ }
+
+ handleMakeSystemAdmin(e) {
+ e.preventDefault();
const data = {
user_id: this.props.user.id,
- new_roles: 'admin'
+ new_roles: 'system_admin'
};
Client.updateRoles(data,
@@ -78,28 +135,59 @@ export default class UserItem extends React.Component {
);
}
- handleMakeSystemAdmin(e) {
+ handleResetPassword(e) {
e.preventDefault();
+ this.props.doPasswordReset(this.props.user);
+ }
+
+ handleDemote(user, role) {
+ this.setState({
+ serverError: this.state.serverError,
+ showDemoteModal: true,
+ user,
+ role
+ });
+ }
+
+ handleDemoteCancel() {
+ this.setState({
+ serverError: null,
+ showDemoteModal: false,
+ user: null,
+ role: null
+ });
+ }
+
+ handleDemoteSubmit() {
const data = {
user_id: this.props.user.id,
- new_roles: 'system_admin'
+ new_roles: this.state.role
};
Client.updateRoles(data,
() => {
- this.props.refreshProfiles();
+ this.setState({
+ serverError: null,
+ showDemoteModal: false,
+ user: null,
+ role: null
+ });
+
+ const teamUrl = TeamStore.getCurrentTeamUrl();
+ if (teamUrl) {
+ window.location.href = teamUrl;
+ } else {
+ window.location.href = '/';
+ }
},
(err) => {
- this.setState({serverError: err.message});
+ this.setState({
+ serverError: err.message
+ });
}
);
}
- handleResetPassword(e) {
- e.preventDefault();
- this.props.doPasswordReset(this.props.user);
- }
-
render() {
let serverError = null;
if (this.state.serverError) {
@@ -247,6 +335,21 @@ export default class UserItem extends React.Component {
</li>
);
}
+ const me = UserStore.getCurrentUser();
+ const {formatMessage} = this.props.intl;
+ let makeDemoteModal = null;
+ if (this.props.user.id === me.id) {
+ makeDemoteModal = (
+ <ConfirmModal
+ show={this.state.showDemoteModal}
+ title={formatMessage(holders.confirmDemoteRoleTitle)}
+ message={[formatMessage(holders.confirmDemoteDescription), React.createElement('br'), React.createElement('br'), formatMessage(holders.confirmDemotionCmd), serverError]}
+ confirm_button={formatMessage(holders.confirmDemotion)}
+ onConfirm={this.handleDemoteSubmit}
+ onCancel={this.handleDemoteCancel}
+ />
+ );
+ }
return (
<tr>
@@ -293,6 +396,7 @@ export default class UserItem extends React.Component {
</li>
</ul>
</div>
+ {makeDemoteModal}
{serverError}
</td>
</tr>
@@ -301,7 +405,10 @@ export default class UserItem extends React.Component {
}
UserItem.propTypes = {
+ intl: intlShape.isRequired,
user: React.PropTypes.object.isRequired,
refreshProfiles: React.PropTypes.func.isRequired,
doPasswordReset: React.PropTypes.func.isRequired
};
+
+export default injectIntl(UserItem);
diff --git a/web/react/components/change_url_modal.jsx b/web/react/components/change_url_modal.jsx
index bbe93f58d..49d1b86b4 100644
--- a/web/react/components/change_url_modal.jsx
+++ b/web/react/components/change_url_modal.jsx
@@ -4,6 +4,8 @@
var Modal = ReactBootstrap.Modal;
import * as Utils from '../utils/utils.jsx';
+import {FormattedMessage} from 'mm-intl';
+
export default class ChangeUrlModal extends React.Component {
constructor(props) {
super(props);
@@ -39,21 +41,58 @@ export default class ChangeUrlModal extends React.Component {
getURLError(url) {
let error = []; //eslint-disable-line prefer-const
if (url.length < 2) {
- error.push(<span key='error1'>{'Must be longer than two characters'}<br/></span>);
+ error.push(
+ <span key='error1'>
+ <FormattedMessage
+ id='change_url.longer'
+ defaultMessage='Must be longer than two characters'
+ />
+ <br/>
+ </span>
+ );
}
if (url.charAt(0) === '-' || url.charAt(0) === '_') {
- error.push(<span key='error2'>{'Must start with a letter or number'}<br/></span>);
+ error.push(
+ <span key='error2'>
+ <FormattedMessage
+ id='change_url.startWithLetter'
+ defaultMessage='Must start with a letter or number'
+ />
+ <br/>
+ </span>
+ );
}
if (url.length > 1 && (url.charAt(url.length - 1) === '-' || url.charAt(url.length - 1) === '_')) {
- error.push(<span key='error3'>{'Must end with a letter or number'}<br/></span>);
+ error.push(
+ <span key='error3'>
+ <FormattedMessage
+ id='change_url.endWithLetter'
+ defaultMessage='Must end with a letter or number'
+ />
+ <br/>
+ </span>);
}
if (url.indexOf('__') > -1) {
- error.push(<span key='error4'>{'Can not contain two underscores in a row.'}<br/></span>);
+ error.push(
+ <span key='error4'>
+ <FormattedMessage
+ id='change_url.noUnderscore'
+ defaultMessage='Can not contain two underscores in a row.'
+ />
+ <br/>
+ </span>);
}
// In case of error we don't detect
if (error.length === 0) {
- error.push(<span key='errorlast'>{'Invalid URL'}<br/></span>);
+ error.push(
+ <span key='errorlast'>
+ <FormattedMessage
+ id='change_url.invalidUrl'
+ defaultMessage='Invalid URL'
+ />
+ <br/>
+ </span>);
}
return error;
}
@@ -137,7 +176,10 @@ export default class ChangeUrlModal extends React.Component {
className='btn btn-default'
onClick={this.doCancel}
>
- {'Close'}
+ <FormattedMessage
+ id='change_url.close'
+ defaultMessage='Close'
+ />
</button>
<button
onClick={this.doSubmit}
diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx
index cdef1c1ea..987649f38 100644
--- a/web/react/components/confirm_modal.jsx
+++ b/web/react/components/confirm_modal.jsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import {FormattedMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class ConfirmModal extends React.Component {
@@ -33,7 +34,10 @@ export default class ConfirmModal extends React.Component {
className='btn btn-default'
onClick={this.props.onCancel}
>
- {'Cancel'}
+ <FormattedMessage
+ id='confirm_modal.cancel'
+ defaultMessage='Cancel'
+ />
</button>
<button
type='button'
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
index fd20834f4..3fc71ff96 100644
--- a/web/react/components/get_link_modal.jsx
+++ b/web/react/components/get_link_modal.jsx
@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import {FormattedMessage} from 'mm-intl';
+
const Modal = ReactBootstrap.Modal;
export default class GetLinkModal extends React.Component {
@@ -59,14 +61,25 @@ export default class GetLinkModal extends React.Component {
className='btn btn-primary pull-left'
onClick={this.copyLink}
>
- {'Copy Link'}
+ <FormattedMessage
+ id='get_link.copy'
+ defaultMessage='Copy Link'
+ />
</button>
);
}
var copyLinkConfirm = null;
if (this.state.copiedLink) {
- copyLinkConfirm = <p className='alert alert-success copy-link-confirm'><i className='fa fa-check'></i>{' Link copied to clipboard.'}</p>;
+ copyLinkConfirm = (
+ <p className='alert alert-success copy-link-confirm'>
+ <i className='fa fa-check'></i>
+ <FormattedMessage
+ id='get_link.clipboard'
+ defaultMessage=' Link copied to clipboard.'
+ />
+ </p>
+ );
}
return (
@@ -92,7 +105,10 @@ export default class GetLinkModal extends React.Component {
className='btn btn-default'
onClick={this.onHide}
>
- {'Close'}
+ <FormattedMessage
+ id='get_link.close'
+ defaultMessage='Close'
+ />
</button>
{copyLink}
{copyLinkConfirm}
diff --git a/web/react/components/get_team_invite_link_modal.jsx b/web/react/components/get_team_invite_link_modal.jsx
index a926c4451..883871267 100644
--- a/web/react/components/get_team_invite_link_modal.jsx
+++ b/web/react/components/get_team_invite_link_modal.jsx
@@ -6,7 +6,20 @@ import GetLinkModal from './get_link_modal.jsx';
import ModalStore from '../stores/modal_store.jsx';
import TeamStore from '../stores/team_store.jsx';
-export default class GetTeamInviteLinkModal extends React.Component {
+import {intlShape, injectIntl, defineMessages} from 'mm-intl';
+
+const holders = defineMessages({
+ title: {
+ id: 'get_team_invite_link_modal.title',
+ defaultMessage: 'Team Invite Link'
+ },
+ help: {
+ id: 'get_team_invite_link_modal.help',
+ defaultMessage: 'Send teammates the link below for them to sign-up to this team site.'
+ }
+});
+
+class GetTeamInviteLinkModal extends React.Component {
constructor(props) {
super(props);
@@ -32,14 +45,22 @@ export default class GetTeamInviteLinkModal extends React.Component {
}
render() {
+ const {formatMessage} = this.props.intl;
+
return (
<GetLinkModal
show={this.state.show}
onHide={() => this.setState({show: false})}
- title='Team Invite Link'
- helpText='Send teammates the link below for them to sign-up to this team site.'
+ title={formatMessage(holders.title)}
+ helpText={formatMessage(holders.help)}
link={TeamStore.getCurrentInviteLink()}
/>
);
}
}
+
+GetTeamInviteLinkModal.propTypes = {
+ intl: intlShape.isRequired
+};
+
+export default injectIntl(GetTeamInviteLinkModal); \ No newline at end of file
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 7e1627555..f2a0a7565 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -12,9 +12,38 @@ import ChannelStore from '../stores/channel_store.jsx';
import TeamStore from '../stores/team_store.jsx';
import ConfirmModal from './confirm_modal.jsx';
+import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
+
const Modal = ReactBootstrap.Modal;
-export default class InviteMemberModal extends React.Component {
+const holders = defineMessages({
+ emailError: {
+ id: 'invite_member.emailError',
+ defaultMessage: 'Please enter a valid email address'
+ },
+ firstname: {
+ id: 'invite_member.firstname',
+ defaultMessage: 'First name'
+ },
+ lastname: {
+ id: 'invite_member.lastname',
+ defaultMessage: 'Last name'
+ },
+ modalTitle: {
+ id: 'invite_member.modalTitle',
+ defaultMessage: 'Discard Invitations?'
+ },
+ modalMessage: {
+ id: 'invite_member.modalMessage',
+ defaultMessage: 'You have unsent invitations, are you sure you want to discard them?'
+ },
+ modalButton: {
+ id: 'invite_member.modalButton',
+ defaultMessage: 'Yes, Discard'
+ }
+});
+
+class InviteMemberModal extends React.Component {
constructor(props) {
super(props);
@@ -72,7 +101,7 @@ export default class InviteMemberModal extends React.Component {
var invite = {};
invite.email = ReactDOM.findDOMNode(this.refs['email' + index]).value.trim();
if (!invite.email || !utils.isEmail(invite.email)) {
- emailErrors[index] = 'Please enter a valid email address';
+ emailErrors[index] = this.props.intl.formatMessage(holders.emailError);
valid = false;
} else {
emailErrors[index] = '';
@@ -103,7 +132,7 @@ export default class InviteMemberModal extends React.Component {
this.setState({isSendingEmails: false});
},
(err) => {
- if (err.message === 'This person is already on your team') {
+ if (err.id === 'api.team.invite_members.already.app_error') {
emailErrors[err.detailed_error] = err.message;
this.setState({emailErrors: emailErrors});
} else {
@@ -199,6 +228,7 @@ export default class InviteMemberModal extends React.Component {
render() {
var currentUser = UserStore.getCurrentUser();
+ const {formatMessage} = this.props.intl;
if (currentUser != null) {
var inviteSections = [];
@@ -252,7 +282,7 @@ export default class InviteMemberModal extends React.Component {
type='text'
className='form-control'
ref={'first_name' + index}
- placeholder='First name'
+ placeholder={formatMessage(holders.firstname)}
maxLength='64'
disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
@@ -266,7 +296,7 @@ export default class InviteMemberModal extends React.Component {
type='text'
className='form-control'
ref={'last_name' + index}
- placeholder='Last name'
+ placeholder={formatMessage(holders.lastname)}
maxLength='64'
disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
@@ -318,20 +348,48 @@ export default class InviteMemberModal extends React.Component {
type='button'
className='btn btn-default'
onClick={this.addInviteFields}
- >{'Add another'}</button>
+ >
+ <FormattedMessage
+ id='invite_member.addAnother'
+ defaultMessage='Add another'
+ />
+ </button>
<br/>
<br/>
- <span>{'People invited automatically join the '}<strong>{defaultChannelName}</strong>{' channel.'}</span>
+ <span>
+ <FormattedHTMLMessage
+ id='invite_member.autoJoin'
+ defaultMessage='People invited automatically join the <strong>{channel}</strong> channel.'
+ values={{
+ channel: defaultChannelName
+ }}
+ />
+ </span>
</div>
);
- var sendButtonLabel = 'Send Invitation';
+ var sendButtonLabel = (
+ <FormattedMessage
+ id='invite_member.send'
+ defaultMessage='Send Invitation'
+ />
+ );
if (this.state.isSendingEmails) {
sendButtonLabel = (
- <span><i className='fa fa-spinner fa-spin' />{' Sending'}</span>
+ <span><i className='fa fa-spinner fa-spin' />
+ <FormattedMessage
+ id='invite_member.sending'
+ defaultMessage=' Sending'
+ />
+ </span>
);
} else if (this.state.inviteIds.length > 1) {
- sendButtonLabel = 'Send Invitations';
+ sendButtonLabel = (
+ <FormattedMessage
+ id='invite_member.send2'
+ defaultMessage='Send Invitations'
+ />
+ );
}
sendButton = (
@@ -352,27 +410,46 @@ export default class InviteMemberModal extends React.Component {
href='#'
onClick={this.showGetTeamInviteLinkModal}
>
- {'Team Invite Link'}
+ <FormattedMessage
+ id='invite_member.inviteLink'
+ defaultMessage='Team Invite Link'
+ />
</a>
);
teamInviteLink = (
<p>
- {'You can also invite people using the '}{link}{'.'}
+ <FormattedMessage
+ id='invite_member.teamInviteLink'
+ defaultMessage='You can also invite people using the {link}.'
+ values={{
+ link: (link)
+ }}
+ />
</p>
);
}
content = (
<div>
- <p>{'Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'}</p>
+ <p>
+ <FormattedMessage
+ id='invite_member.content'
+ defaultMessage='Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'
+ />
+ </p>
{teamInviteLink}
</div>
);
} else {
content = (
<div>
- <p>{'User creation has been disabled for your team. Please ask your team administrator for details.'}</p>
+ <p>
+ <FormattedMessage
+ id='invite_member.disabled'
+ defaultMessage='User creation has been disabled for your team. Please ask your team administrator for details.'
+ />
+ </p>
</div>
);
}
@@ -387,7 +464,12 @@ export default class InviteMemberModal extends React.Component {
backdrop={this.state.isSendingEmails ? 'static' : true}
>
<Modal.Header closeButton={!this.state.isSendingEmails}>
- <Modal.Title>{'Invite New Member'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='invite_member.newMember'
+ defaultMessage='Invite New Member'
+ />
+ </Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<form role='form'>
@@ -402,15 +484,18 @@ export default class InviteMemberModal extends React.Component {
onClick={this.handleHide.bind(this, true)}
disabled={this.state.isSendingEmails}
>
- {'Cancel'}
+ <FormattedMessage
+ id='invite_member.cancel'
+ defaultMessage='Cancel'
+ />
</button>
{sendButton}
</Modal.Footer>
</Modal>
<ConfirmModal
- title='Discard Invitations?'
- message='You have unsent invitations, are you sure you want to discard them?'
- confirm_button='Yes, Discard'
+ title={formatMessage(holders.modalTitle)}
+ message={formatMessage(holders.modalMessage)}
+ confirm_button={formatMessage(holders.modalButton)}
show={this.state.showConfirmModal}
onConfirm={this.handleHide.bind(this, false)}
onCancel={() => this.setState({showConfirmModal: false})}
@@ -424,4 +509,7 @@ export default class InviteMemberModal extends React.Component {
}
InviteMemberModal.propTypes = {
+ intl: intlShape.isRequired
};
+
+export default injectIntl(InviteMemberModal); \ No newline at end of file
diff --git a/web/react/components/member_list_team_item.jsx b/web/react/components/member_list_team_item.jsx
index 7967c410d..6e1006911 100644
--- a/web/react/components/member_list_team_item.jsx
+++ b/web/react/components/member_list_team_item.jsx
@@ -6,6 +6,8 @@ import * as Client from '../utils/client.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
import * as Utils from '../utils/utils.jsx';
+import {FormattedMessage} from 'mm-intl';
+
export default class MemberListTeamItem extends React.Component {
constructor(props) {
super(props);
@@ -78,14 +80,29 @@ export default class MemberListTeamItem extends React.Component {
}
const user = this.props.user;
- let currentRoles = 'Member';
+ let currentRoles = (
+ <FormattedMessage
+ id='member_team_item.member'
+ defaultMessage='Member'
+ />
+ );
const timestamp = UserStore.getCurrentUser().update_at;
if (user.roles.length > 0) {
if (Utils.isSystemAdmin(user.roles)) {
- currentRoles = 'System Admin';
+ currentRoles = (
+ <FormattedMessage
+ id='member_team_item.systemAdmin'
+ defaultMessage='System Admin'
+ />
+ );
} else if (Utils.isAdmin(user.roles)) {
- currentRoles = 'Team Admin';
+ currentRoles = (
+ <FormattedMessage
+ id='member_team_item.teamAdmin'
+ defaultMessage='Team Admin'
+ />
+ );
} else {
currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
}
@@ -98,7 +115,12 @@ export default class MemberListTeamItem extends React.Component {
let showMakeNotActive = user.roles !== 'system_admin';
if (user.delete_at > 0) {
- currentRoles = 'Inactive';
+ currentRoles = (
+ <FormattedMessage
+ id='member_team_item.inactive'
+ defaultMessage='Inactive'
+ />
+ );
showMakeMember = false;
showMakeAdmin = false;
showMakeActive = true;
@@ -114,7 +136,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeAdmin}
>
- {'Make Team Admin'}
+ <FormattedMessage
+ id='member_team_item.makeAdmin'
+ defaultMessage='Make Team Admin'
+ />
</a>
</li>
);
@@ -129,7 +154,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeMember}
>
- {'Make Member'}
+ <FormattedMessage
+ id='member_team_item.makeMember'
+ defaultMessage='Make Member'
+ />
</a>
</li>
);
@@ -144,7 +172,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeActive}
>
- {'Make Active'}
+ <FormattedMessage
+ id='member_team_item.makeActive'
+ defaultMessage='Make Active'
+ />
</a>
</li>
);
@@ -159,7 +190,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeNotActive}
>
- {'Make Inactive'}
+ <FormattedMessage
+ id='member_team_item.makeInactive'
+ defaultMessage='Make Inactive'
+ />
</a>
</li>
);
diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx
index 29512b9b7..d12ea4703 100644
--- a/web/react/components/more_channels.jsx
+++ b/web/react/components/more_channels.jsx
@@ -8,6 +8,8 @@ import ChannelStore from '../stores/channel_store.jsx';
import LoadingScreen from './loading_screen.jsx';
import NewChannelFlow from './new_channel_flow.jsx';
+import {FormattedMessage} from 'mm-intl';
+
function getStateFromStores() {
return {
channels: ChannelStore.getMoreAll(),
@@ -100,7 +102,10 @@ export default class MoreChannels extends React.Component {
onClick={self.handleJoin.bind(self, channel, index)}
className='btn btn-primary'
>
- Join
+ <FormattedMessage
+ id='more_channels.join'
+ defaultMessage='Join'
+ />
</button>
);
}
@@ -123,8 +128,18 @@ export default class MoreChannels extends React.Component {
} else {
moreChannels = (
<div className='no-channel-message'>
- <p className='primary-message'>No more channels to join</p>
- <p className='secondary-message'>Click 'Create New Channel' to make a new one</p>
+ <p className='primary-message'>
+ <FormattedMessage
+ id='more_channels.noMore'
+ defaultMessage='No more channels to join'
+ />
+ </p>
+ <p className='secondary-message'>
+ <FormattedMessage
+ id='more_channels.createClick'
+ defaultMessage="Click 'Create New Channel' to make a new one"
+ />
+ </p>
</div>
);
}
@@ -148,15 +163,28 @@ export default class MoreChannels extends React.Component {
data-dismiss='modal'
>
<span aria-hidden='true'>{'×'}</span>
- <span className='sr-only'>{'Close'}</span>
+ <span className='sr-only'>
+ <FormattedMessage
+ id='more_channels.close'
+ defaultMessage='Close'
+ />
+ </span>
</button>
- <h4 className='modal-title'>{'More Channels'}</h4>
+ <h4 className='modal-title'>
+ <FormattedMessage
+ id='more_channels.title'
+ defaultMessage='More Channels'
+ />
+ </h4>
<button
type='button'
className='btn btn-primary channel-create-btn'
onClick={this.handleNewChannel}
>
- {'Create New Channel'}
+ <FormattedMessage
+ id='more_channels.create'
+ defaultMessage='Create New Channel'
+ />
</button>
<NewChannelFlow
show={this.state.showNewChannelModal}
@@ -174,7 +202,10 @@ export default class MoreChannels extends React.Component {
className='btn btn-default'
data-dismiss='modal'
>
- {'Close'}
+ <FormattedMessage
+ id='more_channels.close'
+ defaultMessage='Close'
+ />
</button>
</div>
</div>
diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx
index 3661b19e6..f8a6884d0 100644
--- a/web/react/components/more_direct_channels.jsx
+++ b/web/react/components/more_direct_channels.jsx
@@ -5,7 +5,20 @@ const Modal = ReactBootstrap.Modal;
import UserStore from '../stores/user_store.jsx';
import * as Utils from '../utils/utils.jsx';
-export default class MoreDirectChannels extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ member: {
+ id: 'more_direct_channels.member',
+ defaultMessage: 'Member'
+ },
+ search: {
+ id: 'more_direct_channels.search',
+ defaultMessage: 'Search members'
+ }
+});
+
+class MoreDirectChannels extends React.Component {
constructor(props) {
super(props);
@@ -148,7 +161,10 @@ export default class MoreDirectChannels extends React.Component {
className='btn btn-primary btn-message'
onClick={this.handleShowDirectChannel.bind(this, user)}
>
- {'Message'}
+ <FormattedMessage
+ id='more_direct_channels.message'
+ defaultMessage='Message'
+ />
</button>
);
}
@@ -180,6 +196,7 @@ export default class MoreDirectChannels extends React.Component {
}
render() {
+ const {formatMessage} = this.props.intl;
if (!this.props.show) {
return null;
}
@@ -199,19 +216,44 @@ export default class MoreDirectChannels extends React.Component {
const userEntries = users.map(this.createRowForUser);
if (userEntries.length === 0) {
- userEntries.push(<tr key='no-users-found'><td>{'No users found :('}</td></tr>);
+ userEntries.push(
+ <tr key='no-users-found'><td>
+ <FormattedMessage
+ id='more_direct_channels.notFound'
+ defaultMessage='No users found :('
+ />
+ </td></tr>);
}
- let memberString = 'Member';
+ let memberString = formatMessage(holders.member);
if (users.length !== 1) {
memberString += 's';
}
let count;
if (users.length === this.state.users.length) {
- count = `${users.length} ${memberString}`;
+ count = (
+ <FormattedMessage
+ id='more_direct_channels.count'
+ defaultMessage='{count} {member}'
+ values={{
+ count: users.length,
+ member: memberString
+ }}
+ />
+ );
} else {
- count = `${users.length} ${memberString} of ${this.state.users.length} Total`;
+ count = (
+ <FormattedMessage
+ id='more_direct_channels.countTotal'
+ defaultMessage='{count} {member} of {total} Total'
+ values={{
+ count: users.length,
+ member: memberString,
+ total: this.state.users.length
+ }}
+ />
+ );
}
return (
@@ -221,7 +263,12 @@ export default class MoreDirectChannels extends React.Component {
onHide={this.handleHide}
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'Direct Messages'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='more_direct_channels.title'
+ defaultMessage='Direct Messages'
+ />
+ </Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<div className='filter-row'>
@@ -229,7 +276,7 @@ export default class MoreDirectChannels extends React.Component {
<input
ref='filter'
className='form-control filter-textbox'
- placeholder='Search members'
+ placeholder={formatMessage(holders.search)}
onInput={this.handleFilterChange}
/>
</div>
@@ -254,7 +301,10 @@ export default class MoreDirectChannels extends React.Component {
className='btn btn-default'
onClick={this.handleHide}
>
- {'Close'}
+ <FormattedMessage
+ id='more_direct_channels.close'
+ defaultMessage='Close'
+ />
</button>
</Modal.Footer>
</Modal>
@@ -263,6 +313,9 @@ export default class MoreDirectChannels extends React.Component {
}
MoreDirectChannels.propTypes = {
+ intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func
};
+
+export default injectIntl(MoreDirectChannels); \ No newline at end of file
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index d4ec5a5f5..e9df03c33 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -14,6 +14,8 @@ import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import Constants from '../utils/constants.jsx';
+import {FormattedMessage} from 'mm-intl';
+
function getStateFromStores() {
const teams = [];
const teamsObject = UserStore.getTeams();
@@ -97,7 +99,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={EventHelpers.showInviteMemberModal}
>
- {'Invite New Member'}
+ <FormattedMessage
+ id='navbar_dropdown.inviteMember'
+ defaultMessage='Invite New Member'
+ />
</a>
</li>
);
@@ -109,7 +114,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={EventHelpers.showGetTeamInviteLinkModal}
>
- {'Get Team Invite Link'}
+ <FormattedMessage
+ id='navbar_dropdown.teamLink'
+ defaultMessage='Get Team Invite Link'
+ />
</a>
</li>
);
@@ -120,7 +128,10 @@ export default class NavbarDropdown extends React.Component {
manageLink = (
<li>
<ToggleModalButton dialogType={TeamMembersModal}>
- {'Manage Members'}
+ <FormattedMessage
+ id='navbar_dropdown.manageMembers'
+ defaultMessage='Manage Members'
+ />
</ToggleModalButton>
</li>
);
@@ -134,7 +145,10 @@ export default class NavbarDropdown extends React.Component {
data-toggle='modal'
data-target='#team_settings'
>
- {'Team Settings'}
+ <FormattedMessage
+ id='navbar_dropdown.teamSettings'
+ defaultMessage='Team Settings'
+ />
</a>
</li>
);
@@ -146,7 +160,10 @@ export default class NavbarDropdown extends React.Component {
<a
href={'/admin_console?' + Utils.getSessionIndex()}
>
- {'System Console'}
+ <FormattedMessage
+ id='navbar_dropdown.console'
+ defaultMessage='System Console'
+ />
</a>
</li>
);
@@ -165,7 +182,16 @@ export default class NavbarDropdown extends React.Component {
this.state.teams.forEach((team) => {
if (team.name !== this.props.teamName) {
- teams.push(<li key={team.name}><a href={Utils.getWindowLocationOrigin() + '/' + team.name}>{'Switch to ' + team.display_name}</a></li>);
+ teams.push(
+ <li key={team.name}><a href={Utils.getWindowLocationOrigin() + '/' + team.name}>
+ <FormattedMessage
+ id='navbar_dropdown.switchTeam'
+ defaultMessage='Switch to {team}'
+ values={{
+ team: team.display_name
+ }}
+ />
+ </a></li>);
}
});
}
@@ -178,7 +204,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={Utils.getWindowLocationOrigin() + '/signup_team'}
>
- {'Create a New Team'}
+ <FormattedMessage
+ id='navbar_dropdown.create'
+ defaultMessage='Create a New Team'
+ />
</a>
</li>
);
@@ -192,7 +221,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={global.window.mm_config.HelpLink}
>
- {'Help'}
+ <FormattedMessage
+ id='navbar_dropdown.help'
+ defaultMessage='Help'
+ />
</a>
</li>
);
@@ -206,7 +238,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={global.window.mm_config.ReportAProblemLink}
>
- {'Report a Problem'}
+ <FormattedMessage
+ id='navbar_dropdown.report'
+ defaultMessage='Report a Problem'
+ />
</a>
</li>
);
@@ -239,7 +274,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={() => this.setState({showUserSettingsModal: true})}
>
- {'Account Settings'}
+ <FormattedMessage
+ id='navbar_dropdown.accountSettings'
+ defaultMessage='Account Settings'
+ />
</a>
</li>
{inviteLink}
@@ -249,7 +287,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={this.handleLogoutClick}
>
- {'Logout'}
+ <FormattedMessage
+ id='navbar_dropdown.logout'
+ defaultMessage='Logout'
+ />
</a>
</li>
{adminDivider}
@@ -265,7 +306,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={this.handleAboutModal}
>
- {'About Mattermost'}
+ <FormattedMessage
+ id='navbar_dropdown.about'
+ defaultMessage='About Mattermost'
+ />
</a>
</li>
<UserSettingsModal
diff --git a/web/react/components/new_channel_flow.jsx b/web/react/components/new_channel_flow.jsx
index 3a114aa19..a0bb14e8f 100644
--- a/web/react/components/new_channel_flow.jsx
+++ b/web/react/components/new_channel_flow.jsx
@@ -9,11 +9,47 @@ import UserStore from '../stores/user_store.jsx';
import NewChannelModal from './new_channel_modal.jsx';
import ChangeURLModal from './change_url_modal.jsx';
+import {intlShape, injectIntl, defineMessages} from 'mm-intl';
+
const SHOW_NEW_CHANNEL = 1;
const SHOW_EDIT_URL = 2;
const SHOW_EDIT_URL_THEN_COMPLETE = 3;
+const messages = defineMessages({
+ invalidName: {
+ id: 'channel_flow.invalidName',
+ defaultMessage: 'Invalid Channel Name'
+ },
+ alreadyExist: {
+ id: 'channel_flow.alreadyExist',
+ defaultMessage: 'A channel with that URL already exists'
+ },
+ channel: {
+ id: 'channel_flow.channel',
+ defaultMessage: 'Channel'
+ },
+ group: {
+ id: 'channel_flow.group',
+ defaultMessage: 'Group'
+ },
+ change: {
+ id: 'channel_flow.changeUrlTitle',
+ defaultMessage: 'Change {term} URL'
+ },
+ set: {
+ id: 'channel_flow.set_url_title',
+ defaultMessage: 'Set {term} URL'
+ },
+ create: {
+ id: 'channel_flow.create',
+ defaultMessage: 'Create {term}'
+ },
+ changeUrlDescription: {
+ id: 'channel_flow.changeUrlDescription',
+ defaultMessage: 'Some characters are not allowed in URLs and may be removed.'
+ }
+});
-export default class NewChannelFlow extends React.Component {
+class NewChannelFlow extends React.Component {
constructor(props) {
super(props);
@@ -51,9 +87,10 @@ export default class NewChannelFlow extends React.Component {
doSubmit() {
var channel = {};
+ const {formatMessage} = this.props.intl;
channel.display_name = this.state.channelDisplayName;
if (!channel.display_name) {
- this.setState({serverError: 'Invalid Channel Name'});
+ this.setState({serverError: formatMessage(messages.invalidName)});
return;
}
@@ -75,11 +112,11 @@ export default class NewChannelFlow extends React.Component {
Utils.switchChannel(data);
},
(err) => {
- if (err.message === 'Name must be 2 or more lowercase alphanumeric characters') {
+ if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
}
- if (err.message === 'A channel with that handle already exists') {
- this.setState({serverError: 'A channel with that URL already exists'});
+ if (err.id === 'store.sql_channel.update.exists.app_error') {
+ this.setState({serverError: formatMessage(messages.alreadyExist)});
return;
}
this.setState({serverError: err.message});
@@ -130,27 +167,29 @@ export default class NewChannelFlow extends React.Component {
let changeURLSubmitButtonText = '';
let channelTerm = '';
+ const {formatMessage} = this.props.intl;
+
// Only listen to flow state if we are being shown
if (this.props.show) {
switch (this.state.flowState) {
case SHOW_NEW_CHANNEL:
if (this.state.channelType === 'O') {
showChannelModal = true;
- channelTerm = 'Channel';
+ channelTerm = formatMessage(messages.channel);
} else {
showGroupModal = true;
- channelTerm = 'Group';
+ channelTerm = formatMessage(messages.group);
}
break;
case SHOW_EDIT_URL:
showChangeURLModal = true;
- changeURLTitle = 'Change ' + channelTerm + ' URL';
- changeURLSubmitButtonText = 'Change ' + channelTerm + ' URL';
+ changeURLTitle = formatMessage(messages.change, {term: channelTerm});
+ changeURLSubmitButtonText = formatMessage(messages.change, {term: channelTerm});
break;
case SHOW_EDIT_URL_THEN_COMPLETE:
showChangeURLModal = true;
- changeURLTitle = 'Set ' + channelTerm + ' URL';
- changeURLSubmitButtonText = 'Create ' + channelTerm;
+ changeURLTitle = formatMessage(messages.set, {term: channelTerm});
+ changeURLSubmitButtonText = formatMessage(messages.create, {term: channelTerm});
break;
}
}
@@ -181,7 +220,7 @@ export default class NewChannelFlow extends React.Component {
<ChangeURLModal
show={showChangeURLModal}
title={changeURLTitle}
- description={'Some characters are not allowed in URLs and may be removed.'}
+ description={formatMessage(messages.changeUrlDescription)}
urlLabel={channelTerm + ' URL'}
submitButtonText={changeURLSubmitButtonText}
currentURL={this.state.channelName}
@@ -200,7 +239,10 @@ NewChannelFlow.defaultProps = {
};
NewChannelFlow.propTypes = {
+ intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
channelType: React.PropTypes.string.isRequired,
onModalDismissed: React.PropTypes.func.isRequired
};
+
+export default injectIntl(NewChannelFlow); \ No newline at end of file
diff --git a/web/react/components/new_channel_modal.jsx b/web/react/components/new_channel_modal.jsx
index 9f733c476..788e6dc1b 100644
--- a/web/react/components/new_channel_modal.jsx
+++ b/web/react/components/new_channel_modal.jsx
@@ -2,9 +2,19 @@
// See License.txt for license information.
import * as Utils from '../utils/utils.jsx';
+
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
var Modal = ReactBootstrap.Modal;
-export default class NewChannelModal extends React.Component {
+const holders = defineMessages({
+ nameEx: {
+ id: 'channel_modal.nameEx',
+ defaultMessage: 'E.g.: "Bugs", "Marketing", "办公室恋情"'
+ }
+});
+
+class NewChannelModal extends React.Component {
constructor(props) {
super(props);
@@ -32,7 +42,7 @@ export default class NewChannelModal extends React.Component {
const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim();
if (displayName.length < 1) {
- this.setState({displayNameError: 'This field is required'});
+ this.setState({displayNameError: true});
return;
}
@@ -51,7 +61,15 @@ export default class NewChannelModal extends React.Component {
var displayNameClass = 'form-group';
if (this.state.displayNameError) {
- displayNameError = <p className='input__help error'>{this.state.displayNameError}</p>;
+ displayNameError = (
+ <p className='input__help error'>
+ <FormattedMessage
+ id='channel_modal.displayNameError'
+ defaultMessage='This field is required'
+ />
+ {this.state.displayNameError}
+ </p>
+ );
displayNameClass += ' has-error';
}
@@ -63,29 +81,51 @@ export default class NewChannelModal extends React.Component {
var channelSwitchText = '';
switch (this.props.channelType) {
case 'P':
- channelTerm = 'Group';
+ channelTerm = (
+ <FormattedMessage
+ id='channel_modal.group'
+ defaultMessage='Group'
+ />
+ );
channelSwitchText = (
<div className='modal-intro'>
- {'Create a new private group with restricted membership. '}
+ <FormattedMessage
+ id='channel_modal.privateGroup1'
+ defaultMessage='Create a new private group with restricted membership. '
+ />
<a
href='#'
onClick={this.props.onTypeSwitched}
>
- {'Create a public channel'}
+ <FormattedMessage
+ id='channel_modal.publicChannel1'
+ defaultMessage='Create a public channel'
+ />
</a>
</div>
);
break;
case 'O':
- channelTerm = 'Channel';
+ channelTerm = (
+ <FormattedMessage
+ id='channel_modal.channel'
+ defaultMessage='Channel'
+ />
+ );
channelSwitchText = (
<div className='modal-intro'>
- {'Create a new public channel anyone can join. '}
+ <FormattedMessage
+ id='channel_modal.publicChannel2'
+ defaultMessage='Create a new public channel anyone can join. '
+ />
<a
href='#'
onClick={this.props.onTypeSwitched}
>
- {'Create a private group'}
+ <FormattedMessage
+ id='channel_modal.privateGroup2'
+ defaultMessage='Create a private group'
+ />
</a>
</div>
);
@@ -102,7 +142,13 @@ export default class NewChannelModal extends React.Component {
onHide={this.props.onModalDismissed}
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'New ' + channelTerm}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='channel_modal.modalTitle'
+ defaultMessage='New '
+ />
+ {channelTerm}
+ </Modal.Title>
</Modal.Header>
<form
role='form'
@@ -113,14 +159,19 @@ export default class NewChannelModal extends React.Component {
{channelSwitchText}
</div>
<div className={displayNameClass}>
- <label className='col-sm-3 form__label control-label'>{'Name'}</label>
+ <label className='col-sm-3 form__label control-label'>
+ <FormattedMessage
+ id='channel_modal.name'
+ defaultMessage='Name'
+ />
+ </label>
<div className='col-sm-9'>
<input
onChange={this.handleChange}
type='text'
ref='display_name'
className='form-control'
- placeholder='E.g.: "Bugs", "Marketing", "办公室恋情"'
+ placeholder={this.props.intl.formatMessage(holders.nameEx)}
maxLength='22'
value={this.props.channelData.displayName}
autoFocus={true}
@@ -133,7 +184,10 @@ export default class NewChannelModal extends React.Component {
href='#'
onClick={this.props.onChangeURLPressed}
>
- {'Edit'}
+ <FormattedMessage
+ id='channel_modal.edit'
+ defaultMessage='Edit'
+ />
</a>
{')'}
</p>
@@ -141,22 +195,38 @@ export default class NewChannelModal extends React.Component {
</div>
<div className='form-group less'>
<div className='col-sm-3'>
- <label className='form__label control-label'>{'Purpose'}</label>
- <label className='form__label light'>{'(optional)'}</label>
+ <label className='form__label control-label'>
+ <FormattedMessage
+ id='channel_modal.purpose'
+ defaultMessage='Purpose'
+ />
+ </label>
+ <label className='form__label light'>
+ <FormattedMessage
+ id='channel_modal.optional'
+ defaultMessage='(optional)'
+ />
+ </label>
</div>
<div className='col-sm-9'>
<textarea
className='form-control no-resize'
ref='channel_purpose'
rows='4'
- placeholder='Purpose'
+ placeholder={this.props.intl.formatMessage({id: 'channel_modal.purpose'})}
maxLength='128'
value={this.props.channelData.purpose}
onChange={this.handleChange}
tabIndex='2'
/>
<p className='input__help'>
- {`Describe how this ${channelTerm} should be used.`}
+ <FormattedMessage
+ id='channel_modal.descriptionHelp'
+ defaultMessage='Describe how this {term} should be used.'
+ values={{
+ term: (channelTerm)
+ }}
+ />
</p>
{serverError}
</div>
@@ -168,7 +238,10 @@ export default class NewChannelModal extends React.Component {
className='btn btn-default'
onClick={this.props.onModalDismissed}
>
- {'Cancel'}
+ <FormattedMessage
+ id='channel_modal.cancel'
+ defaultMessage='Cancel'
+ />
</button>
<button
onClick={this.handleSubmit}
@@ -176,7 +249,11 @@ export default class NewChannelModal extends React.Component {
className='btn btn-primary'
tabIndex='3'
>
- {'Create New ' + channelTerm}
+ <FormattedMessage
+ id='channel_modal.createNew'
+ defaultMessage='Create New '
+ />
+ {channelTerm}
</button>
</Modal.Footer>
</form>
@@ -192,6 +269,7 @@ NewChannelModal.defaultProps = {
serverError: ''
};
NewChannelModal.propTypes = {
+ intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
channelType: React.PropTypes.string.isRequired,
channelData: React.PropTypes.object.isRequired,
@@ -202,3 +280,5 @@ NewChannelModal.propTypes = {
onChangeURLPressed: React.PropTypes.func.isRequired,
onDataChanged: React.PropTypes.func.isRequired
};
+
+export default injectIntl(NewChannelModal); \ No newline at end of file
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index 73b47024c..2bff675a9 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -22,26 +22,6 @@ export default class PostInfo extends React.Component {
this.handlePermalinkCopy = this.handlePermalinkCopy.bind(this);
}
- createReplyLink() {
- if (this.props.allowReply === 'true') {
- var hideReply = '';
-
- if (this.props.commentCount >= 1) {
- hideReply = ' post__reply--hide';
- }
-
- return (
- <div className={'post__reply' + hideReply}>
- <a
- onClick={this.props.handleCommentClick}
- href='#'
- >
- <span dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}/>
- </a>
- </div>
- );
- }
- }
createDropdown() {
var post = this.props.post;
var isOwner = UserStore.getCurrentId() === post.user_id;
@@ -62,6 +42,23 @@ export default class PostInfo extends React.Component {
dataComments = this.props.commentCount;
}
+ if (this.props.allowReply === 'true') {
+ dropdownContents.push(
+ <li
+ key='replyLink'
+ role='presentation'
+ >
+ <a
+ className='link__reply theme'
+ href='#'
+ onClick={this.props.handleCommentClick}
+ >
+ {'Reply'}
+ </a>
+ </li>
+ );
+ }
+
dropdownContents.push(
<li
key='copyLink'
@@ -176,7 +173,7 @@ export default class PostInfo extends React.Component {
>
<span
className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.COMMENT_ICON}}
+ dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
/>
{commentCountText}
</a>
@@ -184,7 +181,6 @@ export default class PostInfo extends React.Component {
}
var dropdown = this.createDropdown();
- var replyLink = this.createReplyLink();
const permalink = TeamStore.getCurrentTeamUrl() + '/pl/' + post.id;
const copyButtonText = this.state.copiedLink ? (<div>{'Copy '}<i className='fa fa-check'/></div>) : 'Copy';
@@ -227,7 +223,6 @@ export default class PostInfo extends React.Component {
/>
</li>
<li className='col col__reply'>
- {replyLink}
<div
className='dropdown'
ref='dotMenu'
diff --git a/web/react/components/register_app_modal.jsx b/web/react/components/register_app_modal.jsx
index f49b33f73..e6d13863b 100644
--- a/web/react/components/register_app_modal.jsx
+++ b/web/react/components/register_app_modal.jsx
@@ -7,9 +7,22 @@ import ModalStore from '../stores/modal_store.jsx';
const Modal = ReactBootstrap.Modal;
import Constants from '../utils/constants.jsx';
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
const ActionTypes = Constants.ActionTypes;
-export default class RegisterAppModal extends React.Component {
+const holders = defineMessages({
+ required: {
+ id: 'register_app.required',
+ defaultMessage: 'Required'
+ },
+ optional: {
+ id: 'register_app.optional',
+ defaultMessage: 'Optional'
+ }
+});
+
+class RegisterAppModal extends React.Component {
constructor() {
super();
@@ -60,7 +73,7 @@ export default class RegisterAppModal extends React.Component {
var name = this.refs.name.value;
if (!name || name.length === 0) {
- state.nameError = 'Application name must be filled in.';
+ state.nameError = true;
this.setState(state);
return;
}
@@ -69,7 +82,7 @@ export default class RegisterAppModal extends React.Component {
var homepage = this.refs.homepage.value;
if (!homepage || homepage.length === 0) {
- state.homepageError = 'Homepage must be filled in.';
+ state.homepageError = true;
this.setState(state);
return;
}
@@ -81,7 +94,7 @@ export default class RegisterAppModal extends React.Component {
var rawCallbacks = this.refs.callback.value.trim();
if (!rawCallbacks || rawCallbacks.length === 0) {
- state.callbackError = 'At least one callback URL must be filled in.';
+ state.callbackError = true;
this.setState(state);
return;
}
@@ -112,17 +125,45 @@ export default class RegisterAppModal extends React.Component {
this.setState({saved: this.refs.save.checked});
}
render() {
+ const {formatMessage} = this.props.intl;
var nameError;
if (this.state.nameError) {
- nameError = <div className='form-group has-error'><label className='control-label'>{this.state.nameError}</label></div>;
+ nameError = (
+ <div className='form-group has-error'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='register_app.nameError'
+ defaultMessage='Application name must be filled in.'
+ />
+ </label>
+ </div>
+ );
}
var homepageError;
if (this.state.homepageError) {
- homepageError = <div className='form-group has-error'><label className='control-label'>{this.state.homepageError}</label></div>;
+ homepageError = (
+ <div className='form-group has-error'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='register_app.homepageError'
+ defaultMessage='Homepage must be filled in.'
+ />
+ </label>
+ </div>
+ );
}
var callbackError;
if (this.state.callbackError) {
- callbackError = <div className='form-group has-error'><label className='control-label'>{this.state.callbackError}</label></div>;
+ callbackError = (
+ <div className='form-group has-error'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='register_app.callbackError'
+ defaultMessage='At least one callback URL must be filled in.'
+ />
+ </label>
+ </div>
+ );
}
var serverError;
if (this.state.serverError) {
@@ -135,50 +176,75 @@ export default class RegisterAppModal extends React.Component {
body = (
<div className='settings-modal'>
<div className='form-horizontal user-settings'>
- <h4 className='padding-bottom x3'>{'Register a New Application'}</h4>
+ <h4 className='padding-bottom x3'>
+ <FormattedMessage
+ id='register_app.title'
+ defaultMessage='Register a New Application'
+ />
+ </h4>
<div className='row'>
- <label className='col-sm-4 control-label'>{'Application Name'}</label>
+ <label className='col-sm-4 control-label'>
+ <FormattedMessage
+ id='register_app.name'
+ defaultMessage='Application Name'
+ />
+ </label>
<div className='col-sm-7'>
<input
ref='name'
className='form-control'
type='text'
- placeholder='Required'
+ placeholder={formatMessage(holders.required)}
/>
{nameError}
</div>
</div>
<div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>{'Homepage URL'}</label>
+ <label className='col-sm-4 control-label'>
+ <FormattedMessage
+ id='register_app.homepage'
+ defaultMessage='Homepage URL'
+ />
+ </label>
<div className='col-sm-7'>
<input
ref='homepage'
className='form-control'
type='text'
- placeholder='Required'
+ placeholder={formatMessage(holders.required)}
/>
{homepageError}
</div>
</div>
<div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>{'Description'}</label>
+ <label className='col-sm-4 control-label'>
+ <FormattedMessage
+ id='register_app.description'
+ defaultMessage='Description'
+ />
+ </label>
<div className='col-sm-7'>
<input
ref='desc'
className='form-control'
type='text'
- placeholder='Optional'
+ placeholder={formatMessage(holders.optional)}
/>
</div>
</div>
<div className='row padding-top padding-bottom x2'>
- <label className='col-sm-4 control-label'>{'Callback URL'}</label>
+ <label className='col-sm-4 control-label'>
+ <FormattedMessage
+ id='register_app.callback'
+ defaultMessage='Callback URL'
+ />
+ </label>
<div className='col-sm-7'>
<textarea
ref='callback'
className='form-control'
type='text'
- placeholder='Required'
+ placeholder={formatMessage(holders.required)}
rows='5'
/>
{callbackError}
@@ -196,7 +262,10 @@ export default class RegisterAppModal extends React.Component {
className='btn btn-default'
onClick={() => this.updateShow(false)}
>
- {'Cancel'}
+ <FormattedMessage
+ id='register_app.cancel'
+ defaultMessage='Cancel'
+ />
</button>
<button
onClick={this.handleSubmit}
@@ -204,7 +273,10 @@ export default class RegisterAppModal extends React.Component {
className='btn btn-primary'
tabIndex='3'
>
- {'Register'}
+ <FormattedMessage
+ id='register_app.register'
+ defaultMessage='Register'
+ />
</button>
</div>
);
@@ -216,10 +288,20 @@ export default class RegisterAppModal extends React.Component {
body = (
<div className='form-horizontal user-settings'>
- <h4 className='padding-bottom x3'>{'Your Application Credentials'}</h4>
+ <h4 className='padding-bottom x3'>
+ <FormattedMessage
+ id='register_app.credentialsTitle'
+ defaultMessage='Your Application Credentials'
+ />
+ </h4>
<br/>
<div className='row'>
- <label className='col-sm-4 control-label'>{'Client ID'}</label>
+ <label className='col-sm-4 control-label'>
+ <FormattedMessage
+ id='register_app.clientId'
+ defaultMessage='Client ID'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -231,7 +313,11 @@ export default class RegisterAppModal extends React.Component {
</div>
<br/>
<div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>{'Client Secret'}</label>
+ <label className='col-sm-4 control-label'>
+ <FormattedMessage
+ id='register_app.clientSecret'
+ defaultMessage='Client Secret'
+ /></label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -243,7 +329,12 @@ export default class RegisterAppModal extends React.Component {
</div>
<br/>
<br/>
- <strong>{'Save these somewhere SAFE and SECURE. Treat your Client ID as your app\'s username and your Client Secret as the app\'s password.'}</strong>
+ <strong>
+ <FormattedMessage
+ id='register_app.credentialsDescription'
+ defaultMessage="Save these somewhere SAFE and SECURE. Treat your Client ID as your app's username and your Client Secret as the app's password."
+ />
+ </strong>
<br/>
<br/>
<div className='checkbox'>
@@ -254,7 +345,10 @@ export default class RegisterAppModal extends React.Component {
checked={this.state.saved}
onChange={this.save}
/>
- {'I have saved both my Client Id and Client Secret somewhere safe'}
+ <FormattedMessage
+ id='register_app.credentialsSave'
+ defaultMessage='I have saved both my Client Id and Client Secret somewhere safe'
+ />
</label>
</div>
</div>
@@ -269,7 +363,10 @@ export default class RegisterAppModal extends React.Component {
this.updateShow(false);
}}
>
- {'Close'}
+ <FormattedMessage
+ id='register_app.close'
+ defaultMessage='Close'
+ />
</a>
);
}
@@ -281,7 +378,12 @@ export default class RegisterAppModal extends React.Component {
onHide={() => this.updateShow(false)}
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'Developer Applications'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='register_app.dev'
+ defaultMessage='Developer Applications'
+ />
+ </Modal.Title>
</Modal.Header>
<form
role='form'
@@ -300,3 +402,8 @@ export default class RegisterAppModal extends React.Component {
}
}
+RegisterAppModal.propTypes = {
+ intl: intlShape.isRequired
+};
+
+export default injectIntl(RegisterAppModal);
diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx
index f235cac0a..e4f83eb1c 100644
--- a/web/react/components/search_results_item.jsx
+++ b/web/react/components/search_results_item.jsx
@@ -89,7 +89,7 @@ export default class SearchResultsItem extends React.Component {
>
<span
className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.COMMENT_ICON}}
+ dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
/>
</a>
</li>
diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx
index d6c4b0d4b..52f1906c3 100644
--- a/web/react/components/setting_item_max.jsx
+++ b/web/react/components/setting_item_max.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 SettingItemMax extends React.Component {
render() {
var clientError = null;
@@ -26,7 +28,10 @@ export default class SettingItemMax extends React.Component {
href='#'
onClick={this.props.submit}
>
- Save
+ <FormattedMessage
+ id='setting_item_max.save'
+ defaultMessage='Save'
+ />
</a>
);
}
@@ -60,7 +65,10 @@ export default class SettingItemMax extends React.Component {
href='#'
onClick={this.props.updateSection}
>
- Cancel
+ <FormattedMessage
+ id='setting_item_max.cancel'
+ defaultMessage='Cancel'
+ />
</a>
</li>
</ul>
diff --git a/web/react/components/setting_item_min.jsx b/web/react/components/setting_item_min.jsx
index ffd2061fe..db5513b14 100644
--- a/web/react/components/setting_item_min.jsx
+++ b/web/react/components/setting_item_min.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 SettingItemMin extends React.Component {
render() {
let editButton = null;
@@ -13,7 +15,10 @@ export default class SettingItemMin extends React.Component {
onClick={this.props.updateSection}
>
<i className='fa fa-pencil'/>
- {'Edit'}
+ <FormattedMessage
+ id='setting_item_min.edit'
+ defaultMessage='Edit'
+ />
</a>
</li>
);
diff --git a/web/react/components/setting_picture.jsx b/web/react/components/setting_picture.jsx
index e69412cca..70e0e6755 100644
--- a/web/react/components/setting_picture.jsx
+++ b/web/react/components/setting_picture.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 SettingPicture extends React.Component {
constructor(props) {
super(props);
@@ -76,10 +78,24 @@ export default class SettingPicture extends React.Component {
<a
className={confirmButtonClass}
onClick={this.props.submit}
- >Save</a>
+ >
+ <FormattedMessage
+ id='setting_picture.save'
+ defaultMessage='Save'
+ />
+ </a>
);
}
- var helpText = 'Upload a profile picture in either JPG or PNG format, at least ' + global.window.mm_config.ProfileWidth + 'px in width and ' + global.window.mm_config.ProfileHeight + 'px height.';
+ var helpText = (
+ <FormattedMessage
+ id='setting_picture.help'
+ defaultMessage='Upload a profile picture in either JPG or PNG format, at least {width}px in width and {height}px height.'
+ values={{
+ width: global.window.mm_config.ProfileWidth,
+ height: global.window.mm_config.ProfileHeight
+ }}
+ />
+ );
var self = this;
return (
@@ -97,7 +113,10 @@ export default class SettingPicture extends React.Component {
{serverError}
{clientError}
<span className='btn btn-sm btn-primary btn-file sel-btn'>
- Select
+ <FormattedMessage
+ id='setting_picture.select'
+ defaultMessage='Select'
+ />
<input
ref='input'
accept='.jpg,.png,.bmp'
@@ -110,7 +129,12 @@ export default class SettingPicture extends React.Component {
className='btn btn-sm theme'
href='#'
onClick={self.props.updateSection}
- >Cancel</a>
+ >
+ <FormattedMessage
+ id='setting_picture.cancel'
+ defaultMessage='Cancel'
+ />
+ </a>
</li>
</ul>
</li>
diff --git a/web/react/components/setting_upload.jsx b/web/react/components/setting_upload.jsx
index a25789dff..5d5cdfdf7 100644
--- a/web/react/components/setting_upload.jsx
+++ b/web/react/components/setting_upload.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 SettingsUpload extends React.Component {
constructor(props) {
super(props);
@@ -41,7 +43,7 @@ export default class SettingsUpload extends React.Component {
if (inputnode.files && inputnode.files[0]) {
this.props.submit(inputnode.files[0]);
} else {
- this.setState({clientError: 'No file selected.'});
+ this.setState({clientError: true});
}
}
@@ -49,7 +51,12 @@ export default class SettingsUpload extends React.Component {
let clientError = null;
if (this.state.clientError) {
clientError = (
- <div className='file-status'>{this.state.clientError}</div>
+ <div className='file-status'>
+ <FormattedMessage
+ id='setting_upload.noFile'
+ defaultMessage='No file selected.'
+ />
+ </div>
);
}
let serverError = null;
@@ -75,7 +82,10 @@ export default class SettingsUpload extends React.Component {
<ul className='setting-list'>
<li className='setting-list-item'>
<span className='btn btn-sm btn-primary btn-file sel-btn'>
- {'Select file'}
+ <FormattedMessage
+ id='setting_upload.select'
+ defaultMessage='Select file'
+ />
<input
ref='uploadinput'
accept={this.props.fileTypesAccepted}
@@ -87,7 +97,10 @@ export default class SettingsUpload extends React.Component {
className={submitButtonClass}
onClick={this.doSubmit}
>
- {'Import'}
+ <FormattedMessage
+ id='setting_upload.import'
+ defaultMessage='Import'
+ />
</a>
{fileNameText}
{serverError}
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index fdd069bb2..14790fbec 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -17,6 +17,9 @@ import * as Client from '../utils/client.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
+
+import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
+
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
@@ -278,34 +281,33 @@ export default class Sidebar extends React.Component {
screens.push(
<div>
- <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>
+ <FormattedHTMLMessage
+ id='sidebar.tutorialScreen1'
+ defaultMessage='<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>'
+ />
</div>
);
screens.push(
<div>
- <h4>{'"Town Square" and "Off-Topic" channels'}</h4>
- <p>{'Here are two public channels to start:'}</p>
- <p>
- <strong>{'Town Square'}</strong>{' is a place for team-wide communication. Everyone in your team is a member of this channel.'}
- </p>
- <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>
+ <FormattedHTMLMessage
+ id='sidebar.tutorialScreen2'
+ defaultMessage='<h4>"Town Square" and "Off-Topic" channels</h4>
+ <p>Here are two public channels to start:</p>
+ <p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>
+ <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>'
+ />
</div>
);
screens.push(
<div>
- <h4>{'Creating and Joining Channels'}</h4>
- <p>
- {'Click '}<strong>{'"More..."'}</strong>{' to create a new channel or join an existing one.'}
- </p>
- <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>
+ <FormattedHTMLMessage
+ id='sidebar.tutorialScreen3'
+ defaultMessage='<h4>Creating and Joining Channels</h4>
+ <p>Click <strong>"More..."</strong> to create a new channel or join an existing one.</p>
+ <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>'
+ />
</div>
);
@@ -440,7 +442,12 @@ export default class Sidebar extends React.Component {
let closeButton = null;
const removeTooltip = (
- <Tooltip id='remove-dm-tooltip'>{'Remove from list'}</Tooltip>
+ <Tooltip id='remove-dm-tooltip'>
+ <FormattedMessage
+ id='sidebar.removeList'
+ defaultMessage='Remove from list'
+ />
+ </Tooltip>
);
if (handleClose && !badge) {
closeButton = (
@@ -528,7 +535,13 @@ export default class Sidebar extends React.Component {
href='#'
onClick={this.showMoreDirectChannelsModal}
>
- {'More (' + this.state.hiddenDirectChannelCount + ')'}
+ <FormattedMessage
+ id='sidebar.more'
+ defaultMessage='More ({count})'
+ values={{
+ count: this.state.hiddenDirectChannelCount
+ }}
+ />
</a>
</li>
);
@@ -540,10 +553,34 @@ export default class Sidebar extends React.Component {
}
const createChannelTootlip = (
- <Tooltip id='new-channel-tooltip' >{'Create new channel'}</Tooltip>
+ <Tooltip id='new-channel-tooltip' >
+ <FormattedMessage
+ id='sidebar.createChannel'
+ defaultMessage='Create new channel'
+ />
+ </Tooltip>
);
const createGroupTootlip = (
- <Tooltip id='new-group-tooltip'>{'Create new group'}</Tooltip>
+ <Tooltip id='new-group-tooltip'>
+ <FormattedMessage
+ id='sidebar.createGroup'
+ defaultMessage='Create new group'
+ />
+ </Tooltip>
+ );
+
+ const above = (
+ <FormattedMessage
+ id='sidebar.unreadAbove'
+ defaultMessage='Unread post(s) above'
+ />
+ );
+
+ const below = (
+ <FormattedMessage
+ id='sidebar.unreadBelow'
+ defaultMessage='Unread post(s) below'
+ />
);
return (
@@ -567,12 +604,12 @@ export default class Sidebar extends React.Component {
<UnreadChannelIndicator
show={this.state.showTopUnread}
extraClass='nav-pills__unread-indicator-top'
- text={'Unread post(s) above'}
+ text={above}
/>
<UnreadChannelIndicator
show={this.state.showBottomUnread}
extraClass='nav-pills__unread-indicator-bottom'
- text={'Unread post(s) below'}
+ text={below}
/>
<div
@@ -583,7 +620,10 @@ export default class Sidebar extends React.Component {
<ul className='nav nav-pills nav-stacked'>
<li>
<h4>
- {'Channels'}
+ <FormattedMessage
+ id='sidebar.channels'
+ defaultMessage='Channels'
+ />
<OverlayTrigger
delayShow={500}
placement='top'
@@ -606,7 +646,10 @@ export default class Sidebar extends React.Component {
className='nav-more'
onClick={this.showMoreChannelsModal}
>
- {'More...'}
+ <FormattedMessage
+ id='sidebar.moreElips'
+ defaultMessage='More...'
+ />
</a>
</li>
</ul>
@@ -614,7 +657,10 @@ export default class Sidebar extends React.Component {
<ul className='nav nav-pills nav-stacked'>
<li>
<h4>
- {'Private Groups'}
+ <FormattedMessage
+ id='sidebar.pg'
+ defaultMessage='Private Groups'
+ />
<OverlayTrigger
delayShow={500}
placement='top'
@@ -633,7 +679,14 @@ export default class Sidebar extends React.Component {
{privateChannelItems}
</ul>
<ul className='nav nav-pills nav-stacked'>
- <li><h4>{'Direct Messages'}</h4></li>
+ <li>
+ <h4>
+ <FormattedMessage
+ id='sidebar.direct'
+ defaultMessage='Direct Messages'
+ />
+ </h4>
+ </li>
{directMessageItems}
{directMessageMore}
</ul>
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index a9616cfc3..45b0a5fc4 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -9,6 +9,9 @@ import PreferenceStore from '../stores/preference_store.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
+
+import {FormattedHTMLMessage} from 'mm-intl';
+
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
@@ -51,20 +54,12 @@ export default class SidebarHeader extends React.Component {
screens.push(
<div>
- <h4>{'Main Menu'}</h4>
- <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>
- <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>
+ <FormattedHTMLMessage
+ id='sidebar_header.tutorial'
+ defaultMessage='<h4>Main Menu</h4>
+ <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>
+ <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>'
+ />
</div>
);
diff --git a/web/react/components/team_export_tab.jsx b/web/react/components/team_export_tab.jsx
index 14df7fffc..8330637d8 100644
--- a/web/react/components/team_export_tab.jsx
+++ b/web/react/components/team_export_tab.jsx
@@ -3,6 +3,8 @@
import * as Client from '../utils/client.jsx';
+import {FormattedMessage} from 'mm-intl';
+
export default class TeamExportTab extends React.Component {
constructor(props) {
super(props);
@@ -35,7 +37,10 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-spinner fa-pulse' />
- {' Exporting...'}
+ <FormattedMessage
+ id='team_export_tab.exporting'
+ defaultMessage=' Exporting...'
+ />
</p>
);
break;
@@ -43,12 +48,18 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-success'>
<i className='fa fa-check' />
- {' Ready for '}
+ <FormattedMessage
+ id='team_export_tab.ready'
+ defaultMessage=' Ready for '
+ />
<a
href={this.state.link}
download={true}
>
- {'download'}
+ <FormattedMessage
+ id='team_export_tab.download'
+ defaultMessage='download'
+ />
</a>
</p>
);
@@ -57,7 +68,13 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-warning' />
- {' Unable to export: ' + this.state.err}
+ <FormattedMessage
+ id='team_export_tab.unable'
+ defaultMessage=' Unable to export: {error}'
+ values={{
+ error: this.state.err
+ }}
+ />
</p>
);
break;
@@ -68,10 +85,20 @@ export default class TeamExportTab extends React.Component {
ref='wrapper'
className='user-settings'
>
- <h3 className='tab-header'>{'Export'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='team_export_tab.export'
+ defaultMessage='Export'
+ />
+ </h3>
<div className='divider-dark first'/>
<ul className='section-max'>
- <li className='col-xs-12 section-title'>{'Export your team'}</li>
+ <li className='col-xs-12 section-title'>
+ <FormattedMessage
+ id='team_export_tab.exportTeam'
+ defaultMessage='Export your team'
+ />
+ </li>
<li className='col-xs-offset-3 col-xs-8'>
<ul className='setting-list'>
<li className='setting-list-item'>
@@ -80,7 +107,10 @@ export default class TeamExportTab extends React.Component {
href='#'
onClick={this.doExport}
>
- {'Export'}
+ <FormattedMessage
+ id='team_export_tab.export'
+ defaultMessage='Export'
+ />
</a>
</li>
</ul>
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index b6fb3389f..0656d3b03 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -8,7 +8,56 @@ import * as Client from '../utils/client.jsx';
import * as Utils from '../utils/utils.jsx';
import TeamStore from '../stores/team_store.jsx';
-export default class GeneralTab extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ dirDisabled: {
+ id: 'general_tab.dirDisabled',
+ defaultMessage: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'
+ },
+ required: {
+ id: 'general_tab.required',
+ defaultMessage: 'This field is required'
+ },
+ chooseName: {
+ id: 'general_tab.chooseName',
+ defaultMessage: 'Please choose a new name for your team'
+ },
+ includeDirTitle: {
+ id: 'general_tab.includeDirTitle',
+ defaultMessage: 'Include this team in the Team Directory'
+ },
+ yes: {
+ id: 'general_tab.yes',
+ defaultMessage: 'Yes'
+ },
+ no: {
+ id: 'general_tab.no',
+ defaultMessage: 'No'
+ },
+ dirOff: {
+ id: 'general_tab.dirOff',
+ defaultMessage: 'Team directory is turned off for this system.'
+ },
+ openInviteTitle: {
+ id: 'general_tab.openInviteTitle',
+ defaultMessage: 'Allow anyone to sign-up from login page'
+ },
+ codeTitle: {
+ id: 'general_tab.codeTitle',
+ defaultMessage: 'Invite Code'
+ },
+ codeDesc: {
+ id: 'general_tab.codeDesc',
+ defaultMessage: "Click 'Edit' to regenerate Invite Code."
+ },
+ teamNameInfo: {
+ id: 'general_tab.teamNameInfo',
+ defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
+ }
+});
+
+class GeneralTab extends React.Component {
constructor(props) {
super(props);
@@ -66,7 +115,7 @@ export default class GeneralTab extends React.Component {
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
- this.setState({clientError: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'});
+ this.setState({clientError: this.props.intl.formatMessage(holders.dirDisabled)});
} else {
this.setState({allow_team_listing: listing});
}
@@ -118,12 +167,13 @@ export default class GeneralTab extends React.Component {
var state = {serverError: '', clientError: ''};
let valid = true;
+ const {formatMessage} = this.props.intl;
const name = this.state.name.trim();
if (!name) {
- state.clientError = 'This field is required';
+ state.clientError = formatMessage(holders.required);
valid = false;
} else if (name === this.props.team.display_name) {
- state.clientError = 'Please choose a new name for your team';
+ state.clientError = formatMessage(holders.chooseName);
valid = false;
} else {
state.clientError = '';
@@ -160,7 +210,7 @@ export default class GeneralTab extends React.Component {
if (inviteId) {
state.clientError = '';
} else {
- state.clientError = 'This field is required';
+ state.clientError = this.props.intl.fromatMessage(holders.required);
valid = false;
}
@@ -260,6 +310,7 @@ export default class GeneralTab extends React.Component {
}
const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
+ const {formatMessage} = this.props.intl;
let teamListingSection;
if (this.props.activeSection === 'team_listing') {
@@ -279,7 +330,10 @@ export default class GeneralTab extends React.Component {
defaultChecked={this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, true)}
/>
- {'Yes'}
+ <FormattedMessage
+ id='general_tab.yes'
+ defaultMessage='Yes'
+ />
</label>
<br/>
</div>
@@ -292,24 +346,39 @@ export default class GeneralTab extends React.Component {
defaultChecked={!this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, false)}
/>
- {'No'}
+ <FormattedMessage
+ id='general_tab.no'
+ defaultMessage='No'
+ />
</label>
<br/>
</div>
- <div><br/>{'Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'}</div>
+ <div>
+ <br/>
+ <FormattedMessage
+ id='general_tab.includeDirDesc'
+ defaultMessage='Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'
+ />
+ </div>
</div>
);
} else {
inputs.push(
<div key='userTeamListingOptions'>
- <div><br/>{'Contact your system administrator to turn on the team directory on the system home page.'}</div>
+ <div>
+ <br/>
+ <FormattedMessage
+ id='general_tab.dirContact'
+ defaultMessage='Contact your system administrator to turn on the team directory on the system home page.'
+ />
+ </div>
</div>
);
}
teamListingSection = (
<SettingItemMax
- title='Include this team in the Team Directory'
+ title={formatMessage(holders.includeDirTitle)}
inputs={inputs}
submit={submitHandle}
server_error={serverError}
@@ -322,17 +391,17 @@ export default class GeneralTab extends React.Component {
if (enableTeamListing) {
if (this.state.allow_team_listing === true) {
- describe = 'Yes';
+ describe = formatMessage(holders.yes);
} else {
- describe = 'No';
+ describe = formatMessage(holders.no);
}
} else {
- describe = 'Team directory is turned off for this system.';
+ describe = formatMessage(holders.dirOff);
}
teamListingSection = (
<SettingItemMin
- title='Include this team in the Team Directory'
+ title={formatMessage(holders.includeDirTitle)}
describe={describe}
updateSection={this.onUpdateTeamListingSection}
/>
@@ -351,7 +420,10 @@ export default class GeneralTab extends React.Component {
defaultChecked={this.state.allow_open_invite}
onChange={this.handleOpenInviteRadio.bind(this, true)}
/>
- {'Yes'}
+ <FormattedMessage
+ id='general_tab.yes'
+ defaultMessage='Yes'
+ />
</label>
<br/>
</div>
@@ -363,17 +435,26 @@ export default class GeneralTab extends React.Component {
defaultChecked={!this.state.allow_open_invite}
onChange={this.handleOpenInviteRadio.bind(this, false)}
/>
- {'No'}
+ <FormattedMessage
+ id='general_tab.no'
+ defaultMessage='No'
+ />
</label>
<br/>
</div>
- <div><br/>{'When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'}</div>
+ <div>
+ <br/>
+ <FormattedMessage
+ id='general_tab.openInviteDesc'
+ defaultMessage='When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'
+ />
+ </div>
</div>
];
openInviteSection = (
<SettingItemMax
- title='Allow anyone to sign-up from login page'
+ title={formatMessage(holders.openInviteTitle)}
inputs={inputs}
submit={this.handleOpenInviteSubmit}
server_error={serverError}
@@ -383,14 +464,14 @@ export default class GeneralTab extends React.Component {
} else {
let describe = '';
if (this.state.allow_open_invite === true) {
- describe = 'Yes';
+ describe = formatMessage(holders.yes);
} else {
- describe = 'No';
+ describe = formatMessage(holders.no);
}
openInviteSection = (
<SettingItemMin
- title='Allow anyone to sign-up from login page'
+ title={formatMessage(holders.openInviteTitle)}
describe={describe}
updateSection={this.onUpdateOpenInviteSection}
/>
@@ -405,7 +486,12 @@ export default class GeneralTab extends React.Component {
inputs.push(
<div key='teamInviteSetting'>
<div className='row'>
- <label className='col-sm-5 control-label'>{'Invite Code'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='general_tab.codeTitle'
+ defaultMessage='Invite Code'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -419,18 +505,26 @@ export default class GeneralTab extends React.Component {
href='#'
onClick={this.handleGenerateInviteId}
>
- {'Re-Generate'}
+ <FormattedMessage
+ id='general_tab.regenerate'
+ defaultMessage='Re-Generate'
+ />
</a>
</div>
</div>
</div>
- <div className='setting-list__hint'>{'The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'}</div>
+ <div className='setting-list__hint'>
+ <FormattedMessage
+ id='general_tab.codeLongDesc'
+ defaultMessage='The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'
+ />
+ </div>
</div>
);
inviteSection = (
<SettingItemMax
- title={`Invite Code`}
+ title={formatMessage(holders.codeTitle)}
inputs={inputs}
submit={this.handleInviteIdSubmit}
server_error={serverError}
@@ -441,8 +535,8 @@ export default class GeneralTab extends React.Component {
} else {
inviteSection = (
<SettingItemMin
- title={`Invite Code`}
- describe={`Click 'Edit' to regenerate Invite Code.`}
+ title={formatMessage(holders.codeTitle)}
+ describe={formatMessage(holders.codeDesc)}
updateSection={this.onUpdateInviteIdSection}
/>
);
@@ -453,7 +547,12 @@ export default class GeneralTab extends React.Component {
if (this.props.activeSection === 'name') {
const inputs = [];
- let teamNameLabel = 'Team Name';
+ let teamNameLabel = (
+ <FormattedMessage
+ id='general_tab.teamName'
+ defaultMessage='Team Name'
+ />
+ );
if (Utils.isMobile()) {
teamNameLabel = '';
}
@@ -478,13 +577,13 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMax
- title={`Team Name`}
+ title={formatMessage({id: 'general_tab.teamName'})}
inputs={inputs}
submit={this.handleNameSubmit}
server_error={serverError}
client_error={clientError}
updateSection={this.onUpdateNameSection}
- extraInfo='Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
+ extraInfo={formatMessage(holders.teamNameInfo)}
/>
);
} else {
@@ -492,7 +591,7 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMin
- title={`Team Name`}
+ title={formatMessage({id: 'general_tab.teamName'})}
describe={describe}
updateSection={this.onUpdateNameSection}
/>
@@ -515,14 +614,22 @@ export default class GeneralTab extends React.Component {
ref='title'
>
<i className='modal-back'></i>
- {'General Settings'}
+ <FormattedMessage
+ id='general_tab.title'
+ defaultMessage='General Settings'
+ />
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
- <h3 className='tab-header'>{'General Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='general_tab.title'
+ defaultMessage='General Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{nameSection}
<div className='divider-light'/>
@@ -539,7 +646,10 @@ export default class GeneralTab extends React.Component {
}
GeneralTab.propTypes = {
+ intl: intlShape.isRequired,
updateSection: React.PropTypes.func.isRequired,
team: React.PropTypes.object.isRequired,
activeSection: React.PropTypes.string.isRequired
};
+
+export default injectIntl(GeneralTab); \ No newline at end of file
diff --git a/web/react/components/team_import_tab.jsx b/web/react/components/team_import_tab.jsx
index 37f8746d7..adf990672 100644
--- a/web/react/components/team_import_tab.jsx
+++ b/web/react/components/team_import_tab.jsx
@@ -4,7 +4,16 @@
import * as utils from '../utils/utils.jsx';
import SettingUpload from './setting_upload.jsx';
-export default class TeamImportTab extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ importSlack: {
+ id: 'team_import_tab.importSlack',
+ defaultMessage: 'Import from Slack (Beta)'
+ }
+});
+
+class TeamImportTab extends React.Component {
constructor(props) {
super(props);
@@ -32,16 +41,19 @@ export default class TeamImportTab extends React.Component {
}
render() {
+ const {formatMessage} = this.props.intl;
var uploadHelpText = (
<div>
- <p>{'To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team\'\s public channels.'}</p>
- <p>{'The Slack import to Mattermost is in "Beta". Slack bot posts do not yet import and Slack @mentions are not currently supported.'}</p>
+ <FormattedHTMLMessage
+ id='team_import_tab.importHelp'
+ defaultMessage="<p>To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.</p><p>The Slack import to Mattermost is in 'Beta'. Slack bot posts do not yet import and Slack @mentions are not currently supported.</p>"
+ />
</div>
);
var uploadSection = (
<SettingUpload
- title='Import from Slack (Beta)'
+ title={formatMessage(holders.importSlack)}
submit={this.doImportSlack}
helpText={uploadHelpText}
fileTypesAccepted='.zip'
@@ -56,19 +68,30 @@ export default class TeamImportTab extends React.Component {
break;
case 'in-progress':
messageSection = (
- <p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>{' Importing...'}</p>
+ <p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>
+ <FormattedMessage
+ id='team_import_tab.importing'
+ defaultMessage=' Importing...'
+ />
+ </p>
);
break;
case 'done':
messageSection = (
<p className='confirm-import alert alert-success'>
<i className='fa fa-check' />
- {' Import successful: '}
+ <FormattedMessage
+ id='team_import_tab.successful'
+ defaultMessage=' Import successful: '
+ />
<a
href={this.state.link}
download='MattermostImportSummary.txt'
>
- {'View Summary'}
+ <FormattedMessage
+ id='team_import_tab.summary'
+ defaultMessage='View Summary'
+ />
</a>
</p>
);
@@ -77,12 +100,18 @@ export default class TeamImportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-warning' />
- {' Import failure: '}
+ <FormattedMessage
+ id='team_import_tab.failure'
+ defaultMessage=' Import failure: '
+ />
<a
href={this.state.link}
download='MattermostImportSummary.txt'
>
- {'View Summary'}
+ <FormattedMessage
+ id='team_import_tab.summary'
+ defaultMessage='View Summary'
+ />
</a>
</p>
);
@@ -102,13 +131,23 @@ export default class TeamImportTab extends React.Component {
<h4
className='modal-title'
ref='title'
- ><i className='modal-back'></i>{'Import'}</h4>
+ ><i className='modal-back'></i>
+ <FormattedMessage
+ id='team_import_tab.import'
+ defaultMessage='Import'
+ />
+ </h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
- <h3 className='tab-header'>{'Import'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='team_import_tab.import'
+ defaultMessage='Import'
+ />
+ </h3>
<div className='divider-dark first'/>
{uploadSection}
<div className='divider-dark'/>
@@ -118,3 +157,9 @@ export default class TeamImportTab extends React.Component {
);
}
}
+
+TeamImportTab.propTypes = {
+ intl: intlShape.isRequired
+};
+
+export default injectIntl(TeamImportTab); \ No newline at end of file
diff --git a/web/react/components/team_members_modal.jsx b/web/react/components/team_members_modal.jsx
index 27224c283..92adb6e2a 100644
--- a/web/react/components/team_members_modal.jsx
+++ b/web/react/components/team_members_modal.jsx
@@ -4,6 +4,8 @@
import MemberListTeam from './member_list_team.jsx';
import TeamStore from '../stores/team_store.jsx';
+import {FormattedMessage} from 'mm-intl';
+
const Modal = ReactBootstrap.Modal;
export default class TeamMembersModal extends React.Component {
@@ -44,7 +46,13 @@ export default class TeamMembersModal extends React.Component {
onHide={this.props.onHide}
>
<Modal.Header closeButton={true}>
- {team.display_name + ' Members'}
+ <FormattedMessage
+ id='team_member_modal.members'
+ defaultMessage='{team} Members'
+ values={{
+ team: team.display_name
+ }}
+ />
</Modal.Header>
<Modal.Body ref='modalBody'>
<div className='team-member-list'>
@@ -57,7 +65,10 @@ export default class TeamMembersModal extends React.Component {
className='btn btn-default'
onClick={this.props.onHide}
>
- {'Close'}
+ <FormattedMessage
+ id='team_member_modal.close'
+ defaultMessage='Close'
+ />
</button>
</Modal.Footer>
</Modal>
diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx
index dbdbde958..d517f92fb 100644
--- a/web/react/components/team_settings_modal.jsx
+++ b/web/react/components/team_settings_modal.jsx
@@ -4,7 +4,24 @@
import SettingsSidebar from './settings_sidebar.jsx';
import TeamSettings from './team_settings.jsx';
-export default class TeamSettingsModal extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ generalTab: {
+ id: 'team_settings_modal.generalTab',
+ defaultMessage: 'General'
+ },
+ importTab: {
+ id: 'team_settings_modal.importTab',
+ defaultMessage: 'Import'
+ },
+ exportTab: {
+ id: 'team_settings_modal.exportTab',
+ defaultMessage: 'Export'
+ }
+});
+
+class TeamSettingsModal extends React.Component {
constructor(props) {
super(props);
@@ -36,12 +53,13 @@ export default class TeamSettingsModal extends React.Component {
this.setState({activeSection: section});
}
render() {
+ const {formatMessage} = this.props.intl;
const tabs = [];
- tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
- tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'});
+ tabs.push({name: 'general', uiName: formatMessage(holders.generalTab), icon: 'glyphicon glyphicon-cog'});
+ tabs.push({name: 'import', uiName: formatMessage(holders.importTab), icon: 'glyphicon glyphicon-upload'});
// To enable export uncomment this line
- //tabs.push({name: 'export', uiName: 'Export', icon: 'glyphicon glyphicon-download'});
+ //tabs.push({name: 'export', uiName: formatMessage(holders.exportTab), icon: 'glyphicon glyphicon-download'});
return (
<div
@@ -67,7 +85,10 @@ export default class TeamSettingsModal extends React.Component {
className='modal-title'
ref='title'
>
- {'Team Settings'}
+ <FormattedMessage
+ id='team_settings_modal.title'
+ defaultMessage='Team Settings'
+ />
</h4>
</div>
<div className='modal-body'>
@@ -96,4 +117,7 @@ export default class TeamSettingsModal extends React.Component {
}
TeamSettingsModal.propTypes = {
+ intl: intlShape.isRequired
};
+
+export default injectIntl(TeamSettingsModal); \ No newline at end of file
diff --git a/web/react/components/unread_channel_indicator.jsx b/web/react/components/unread_channel_indicator.jsx
index c0c34584f..509ac9e4d 100644
--- a/web/react/components/unread_channel_indicator.jsx
+++ b/web/react/components/unread_channel_indicator.jsx
@@ -31,5 +31,5 @@ UnreadChannelIndicator.defaultProps = {
UnreadChannelIndicator.propTypes = {
show: React.PropTypes.bool,
extraClass: React.PropTypes.string,
- text: React.PropTypes.string
+ text: React.PropTypes.object
};
diff --git a/web/react/components/user_settings/custom_theme_chooser.jsx b/web/react/components/user_settings/custom_theme_chooser.jsx
index 8ec3863f3..9116dd938 100644
--- a/web/react/components/user_settings/custom_theme_chooser.jsx
+++ b/web/react/components/user_settings/custom_theme_chooser.jsx
@@ -6,7 +6,96 @@ import Constants from '../../utils/constants.jsx';
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
const Popover = ReactBootstrap.Popover;
-export default class CustomThemeChooser extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const messages = defineMessages({
+ sidebarBg: {
+ id: 'user.settings.custom_theme.sidebarBg',
+ defaultMessage: 'Sidebar BG'
+ },
+ sidebarText: {
+ id: 'user.settings.custom_theme.sidebarText',
+ defaultMessage: 'Sidebar Text'
+ },
+ sidebarHeaderBg: {
+ id: 'user.settings.custom_theme.sidebarHeaderBg',
+ defaultMessage: 'Sidebar Header BG'
+ },
+ sidebarHeaderTextColor: {
+ id: 'user.settings.custom_theme.sidebarHeaderTextColor',
+ defaultMessage: 'Sidebar Header Text'
+ },
+ sidebarUnreadText: {
+ id: 'user.settings.custom_theme.sidebarUnreadText',
+ defaultMessage: 'Sidebar Unread Text'
+ },
+ sidebarTextHoverBg: {
+ id: 'user.settings.custom_theme.sidebarTextHoverBg',
+ defaultMessage: 'Sidebar Text Hover BG'
+ },
+ sidebarTextActiveBorder: {
+ id: 'user.settings.custom_theme.sidebarTextActiveBorder',
+ defaultMessage: 'Sidebar Text Active Border'
+ },
+ sidebarTextActiveColor: {
+ id: 'user.settings.custom_theme.sidebarTextActiveColor',
+ defaultMessage: 'Sidebar Text Active Color'
+ },
+ onlineIndicator: {
+ id: 'user.settings.custom_theme.onlineIndicator',
+ defaultMessage: 'Online Indicator'
+ },
+ awayIndicator: {
+ id: 'user.settings.custom_theme.awayIndicator',
+ defaultMessage: 'Away Indicator'
+ },
+ mentionBj: {
+ id: 'user.settings.custom_theme.mentionBj',
+ defaultMessage: 'Mention Jewel BG'
+ },
+ mentionColor: {
+ id: 'user.settings.custom_theme.mentionColor',
+ defaultMessage: 'Mention Jewel Text'
+ },
+ centerChannelBg: {
+ id: 'user.settings.custom_theme.centerChannelBg',
+ defaultMessage: 'Center Channel BG'
+ },
+ centerChannelColor: {
+ id: 'user.settings.custom_theme.centerChannelColor',
+ defaultMessage: 'Center Channel Text'
+ },
+ newMessageSeparator: {
+ id: 'user.settings.custom_theme.newMessageSeparator',
+ defaultMessage: 'New Message Separator'
+ },
+ linkColor: {
+ id: 'user.settings.custom_theme.linkColor',
+ defaultMessage: 'Link Color'
+ },
+ buttonBg: {
+ id: 'user.settings.custom_theme.buttonBg',
+ defaultMessage: 'Button BG'
+ },
+ buttonColor: {
+ id: 'user.settings.custom_theme.buttonColor',
+ defaultMessage: 'Button Text'
+ },
+ mentionHighlightBg: {
+ id: 'user.settings.custom_theme.mentionHighlightBg',
+ defaultMessage: 'Mention Highlight BG'
+ },
+ mentionHighlightLink: {
+ id: 'user.settings.custom_theme.mentionHighlightLink',
+ defaultMessage: 'Mention Highlight Link'
+ },
+ codeTheme: {
+ id: 'user.settings.custom_theme.codeTheme',
+ defaultMessage: 'Code Theme'
+ }
+});
+
+class CustomThemeChooser extends React.Component {
constructor(props) {
super(props);
@@ -65,6 +154,7 @@ export default class CustomThemeChooser extends React.Component {
this.props.updateTheme(theme);
}
render() {
+ const {formatMessage} = this.props.intl;
const theme = this.props.theme;
const elements = [];
@@ -102,7 +192,7 @@ export default class CustomThemeChooser extends React.Component {
className='col-sm-4 form-group'
key={'custom-theme-key' + index}
>
- <label className='custom-label'>{element.uiName}</label>
+ <label className='custom-label'>{formatMessage(messages[element.id])}</label>
<div
className='input-group theme-group group--code dropdown'
id={element.id}
@@ -135,7 +225,7 @@ export default class CustomThemeChooser extends React.Component {
className='col-sm-4 form-group'
key={'custom-theme-key' + index}
>
- <label className='custom-label'>{element.uiName}</label>
+ <label className='custom-label'>{formatMessage(messages[element.id])}</label>
<div
className='input-group color-picker'
id={element.id}
@@ -160,7 +250,10 @@ export default class CustomThemeChooser extends React.Component {
const pasteBox = (
<div className='col-sm-12'>
<label className='custom-label'>
- {'Copy and paste to share theme colors:'}
+ <FormattedMessage
+ id='user.settings.custom_theme.copyPaste'
+ defaultMessage='Copy and paste to share theme colors:'
+ />
</label>
<input
type='text'
@@ -185,6 +278,9 @@ export default class CustomThemeChooser extends React.Component {
}
CustomThemeChooser.propTypes = {
+ intl: intlShape.isRequired,
theme: React.PropTypes.object.isRequired,
updateTheme: React.PropTypes.func.isRequired
};
+
+export default injectIntl(CustomThemeChooser); \ No newline at end of file
diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx
index 45b05f19b..66bed0b0b 100644
--- a/web/react/components/user_settings/import_theme_modal.jsx
+++ b/web/react/components/user_settings/import_theme_modal.jsx
@@ -9,9 +9,19 @@ const Modal = ReactBootstrap.Modal;
import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
import Constants from '../../utils/constants.jsx';
+
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ submitError: {
+ id: 'user.settings.import_theme.submitError',
+ defaultMessage: 'Invalid format, please try copying and pasting in again.'
+ }
+});
+
const ActionTypes = Constants.ActionTypes;
-export default class ImportThemeModal extends React.Component {
+class ImportThemeModal extends React.Component {
constructor(props) {
super(props);
@@ -39,7 +49,7 @@ export default class ImportThemeModal extends React.Component {
const text = ReactDOM.findDOMNode(this.refs.input).value;
if (!this.isInputValid(text)) {
- this.setState({inputError: 'Invalid format, please try copying and pasting in again.'});
+ this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
return;
}
@@ -125,7 +135,7 @@ export default class ImportThemeModal extends React.Component {
if (this.isInputValid(e.target.value)) {
this.setState({inputError: null});
} else {
- this.setState({inputError: 'Invalid format, please try copying and pasting in again.'});
+ this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
}
}
render() {
@@ -136,7 +146,12 @@ export default class ImportThemeModal extends React.Component {
onHide={() => this.setState({show: false})}
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'Import Slack Theme'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='user.settings.import_theme.importHeader'
+ defaultMessage='Import Slack Theme'
+ />
+ </Modal.Title>
</Modal.Header>
<form
role='form'
@@ -144,7 +159,10 @@ export default class ImportThemeModal extends React.Component {
>
<Modal.Body>
<p>
- {'To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'}
+ <FormattedMessage
+ id='user.settings.import_theme.importBody'
+ defaultMessage='To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'
+ />
</p>
<div className='form-group less'>
<div className='col-sm-9'>
@@ -166,7 +184,10 @@ export default class ImportThemeModal extends React.Component {
className='btn btn-default'
onClick={() => this.setState({show: false})}
>
- {'Cancel'}
+ <FormattedMessage
+ id='user.settings.import_theme.cancel'
+ defaultMessage='Cancel'
+ />
</button>
<button
onClick={this.handleSubmit}
@@ -174,7 +195,10 @@ export default class ImportThemeModal extends React.Component {
className='btn btn-primary'
tabIndex='3'
>
- {'Submit'}
+ <FormattedMessage
+ id='user.settings.import_theme.submit'
+ defaultMessage='Submit'
+ />
</button>
</Modal.Footer>
</form>
@@ -183,3 +207,9 @@ export default class ImportThemeModal extends React.Component {
);
}
}
+
+ImportThemeModal.propTypes = {
+ intl: intlShape.isRequired
+};
+
+export default injectIntl(ImportThemeModal); \ No newline at end of file
diff --git a/web/react/components/user_settings/manage_incoming_hooks.jsx b/web/react/components/user_settings/manage_incoming_hooks.jsx
index 1506e3c98..c6532b018 100644
--- a/web/react/components/user_settings/manage_incoming_hooks.jsx
+++ b/web/react/components/user_settings/manage_incoming_hooks.jsx
@@ -7,6 +7,8 @@ import Constants from '../../utils/constants.jsx';
import ChannelStore from '../../stores/channel_store.jsx';
import LoadingScreen from '../loading_screen.jsx';
+import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
+
export default class ManageIncomingHooks extends React.Component {
constructor() {
super();
@@ -126,7 +128,12 @@ export default class ManageIncomingHooks extends React.Component {
<span className='word-break--all'>{Utils.getWindowLocationOrigin() + '/hooks/' + hook.id}</span>
</div>
<div className='padding-top'>
- <strong>{'Channel: '}</strong>{c.display_name}
+ <strong>
+ <FormattedMessage
+ id='user.settings.hooks_in.channel'
+ defaultMessage='Channel: '
+ />
+ </strong>{c.display_name}
</div>
<a
className={'webhook__remove'}
@@ -147,12 +154,24 @@ export default class ManageIncomingHooks extends React.Component {
} else if (hooks.length > 0) {
displayHooks = hooks;
} else {
- displayHooks = <div className='padding-top x2'>{'None'}</div>;
+ displayHooks = (
+ <div className='padding-top x2'>
+ <FormattedMessage
+ id='user.settings.hooks_in.none'
+ defaultMessage='None'
+ />
+ </div>
+ );
}
const existingHooks = (
<div className='webhooks__container'>
- <label className='control-label padding-top x2'>{'Existing incoming webhooks'}</label>
+ <label className='control-label padding-top x2'>
+ <FormattedMessage
+ id='user.settings.hooks_in.existing'
+ defaultMessage='Existing incoming webhooks'
+ />
+ </label>
<div className='padding-top divider-light'></div>
<div className='webhooks__list'>
{displayHooks}
@@ -162,15 +181,16 @@ export default class ManageIncomingHooks extends React.Component {
return (
<div key='addIncomingHook'>
- {'Create webhook URLs for use in external integrations. Please see '}
- <a
- href='http://mattermost.org/webhooks'
- target='_blank'
- >
- {'http://mattermost.org/webhooks'}
- </a>
- {' to learn more.'}
- <div><label className='control-label padding-top x2'>{'Add a new incoming webhook'}</label></div>
+ <FormattedHTMLMessage
+ id='user.settings.hooks_in.description'
+ defaultMessage='Create webhook URLs for use in external integrations. Please see<a href="http://mattermost.org/webhooks" target="_blank">http://mattermost.org/webhooks</a> to learn more.'
+ />
+ <div><label className='control-label padding-top x2'>
+ <FormattedMessage
+ id='user.settings.hooks_in.addTitle'
+ defaultMessage='Add a new incoming webhook'
+ />
+ </label></div>
<div className='row padding-top'>
<div className='col-sm-10 padding-bottom'>
<select
@@ -189,7 +209,10 @@ export default class ManageIncomingHooks extends React.Component {
href='#'
onClick={this.addNewHook}
>
- {'Add'}
+ <FormattedMessage
+ id='user.settings.hooks_in.add'
+ defaultMessage='Add'
+ />
</a>
</div>
</div>
diff --git a/web/react/components/user_settings/manage_languages.jsx b/web/react/components/user_settings/manage_languages.jsx
index 123165b76..fee6d9da2 100644
--- a/web/react/components/user_settings/manage_languages.jsx
+++ b/web/react/components/user_settings/manage_languages.jsx
@@ -4,6 +4,8 @@
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
+import {FormattedMessage} from 'mm-intl';
+
export default class ManageLanguage extends React.Component {
constructor(props) {
super(props);
@@ -70,7 +72,12 @@ export default class ManageLanguage extends React.Component {
return (
<div key='changeLanguage'>
<br/>
- <label className='control-label'>{'Change interface language'}</label>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.languages.change'
+ defaultMessage='Change interface language'
+ />
+ </label>
<div className='padding-top'>
<select
ref='language'
@@ -87,7 +94,10 @@ export default class ManageLanguage extends React.Component {
href='#'
onClick={this.changeLanguage}
>
- {'Set language'}
+ <FormattedMessage
+ id='user.settings.languages'
+ defaultMessage='Set language'
+ />
</a>
</div>
</div>
diff --git a/web/react/components/user_settings/manage_outgoing_hooks.jsx b/web/react/components/user_settings/manage_outgoing_hooks.jsx
index 17acf0f10..3f88e9f41 100644
--- a/web/react/components/user_settings/manage_outgoing_hooks.jsx
+++ b/web/react/components/user_settings/manage_outgoing_hooks.jsx
@@ -8,7 +8,20 @@ import ChannelStore from '../../stores/channel_store.jsx';
import * as Client from '../../utils/client.jsx';
import Constants from '../../utils/constants.jsx';
-export default class ManageOutgoingHooks extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ optional: {
+ id: 'user.settings.hooks_out.optional',
+ defaultMessage: 'Optional if channel selected'
+ },
+ callbackHolder: {
+ id: 'user.settings.hooks_out.callbackHolder',
+ defaultMessage: 'Each URL must start with http:// or https://'
+ }
+});
+
+class ManageOutgoingHooks extends React.Component {
constructor() {
super();
@@ -140,7 +153,10 @@ export default class ManageOutgoingHooks extends React.Component {
key='select-channel'
value=''
>
- {'--- Select a channel ---'}
+ <FormattedMessage
+ id='user.settings.hooks_out.select'
+ defaultMessage='--- Select a channel ---'
+ />
</option>
);
@@ -169,7 +185,12 @@ export default class ManageOutgoingHooks extends React.Component {
if (c) {
channelDiv = (
<div className='padding-top'>
- <strong>{'Channel: '}</strong>{c.display_name}
+ <strong>
+ <FormattedMessage
+ id='user.settings.hooks_out.channel'
+ defaultMessage='Channel: '
+ />
+ </strong>{c.display_name}
</div>
);
}
@@ -178,7 +199,12 @@ export default class ManageOutgoingHooks extends React.Component {
if (hook.trigger_words && hook.trigger_words.length !== 0) {
triggerDiv = (
<div className='padding-top'>
- <strong>{'Trigger Words: '}</strong>{hook.trigger_words.join(', ')}
+ <strong>
+ <FormattedMessage
+ id='user.settings.hooks_out.trigger'
+ defaultMessage='Trigger Words: '
+ />
+ </strong>{hook.trigger_words.join(', ')}
</div>
);
}
@@ -202,7 +228,10 @@ export default class ManageOutgoingHooks extends React.Component {
href='#'
onClick={this.regenToken.bind(this, hook.id)}
>
- {'Regen Token'}
+ <FormattedMessage
+ id='user.settings.hooks_out.regen'
+ defaultMessage='Regen Token'
+ />
</a>
<a
className='webhook__remove'
@@ -223,12 +252,24 @@ export default class ManageOutgoingHooks extends React.Component {
} else if (hooks.length > 0) {
displayHooks = hooks;
} else {
- displayHooks = <div className='padding-top x2'>{'None'}</div>;
+ displayHooks = (
+ <div className='padding-top x2'>
+ <FormattedMessage
+ id='user.settings.hooks_out.none'
+ defaultMessage='None'
+ />
+ </div>
+ );
}
const existingHooks = (
<div className='webhooks__container'>
- <label className='control-label padding-top x2'>{'Existing outgoing webhooks'}</label>
+ <label className='control-label padding-top x2'>
+ <FormattedMessage
+ id='user.settings.hooks_out.existing'
+ defaultMessage='Existing outgoing webhooks'
+ />
+ </label>
<div className='padding-top divider-light'></div>
<div className='webhooks__list'>
{displayHooks}
@@ -240,19 +281,25 @@ export default class ManageOutgoingHooks extends React.Component {
return (
<div key='addOutgoingHook'>
- {'Create webhooks to send new message events to an external integration. Please see '}
- <a
- href='http://mattermost.org/webhooks'
- target='_blank'
- >
- {'http://mattermost.org/webhooks'}
- </a>
- {' to learn more.'}
- <div><label className='control-label padding-top x2'>{'Add a new outgoing webhook'}</label></div>
+ <FormattedHTMLMessage
+ id='user.settings.hooks_out.addDescription'
+ defaultMessage='Create webhooks to send new message events to an external integration. Please see <a href="http://mattermost.org/webhooks">http://mattermost.org/webhooks</a> to learn more.'
+ />
+ <div><label className='control-label padding-top x2'>
+ <FormattedMessage
+ id='user.settings.hooks_out.addTitle'
+ defaultMessage='Add a new outgoing webhook'
+ />
+ </label></div>
<div className='padding-top divider-light'></div>
<div className='padding-top'>
<div>
- <label className='control-label'>{'Channel'}</label>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.hooks_out.channel'
+ defaultMessage='Channel: '
+ />
+ </label>
<div className='padding-top'>
<select
ref='channelName'
@@ -263,23 +310,43 @@ export default class ManageOutgoingHooks extends React.Component {
{options}
</select>
</div>
- <div className='padding-top'>{'Only public channels can be used'}</div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.hooks_out.only'
+ defaultMessage='Only public channels can be used'
+ />
+ </div>
</div>
<div className='padding-top x2'>
- <label className='control-label'>{'Trigger Words:'}</label>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.hooks_out.trigger'
+ defaultMessage='Trigger Words: '
+ />
+ </label>
<div className='padding-top'>
<input
ref='triggerWords'
className='form-control'
value={this.state.triggerWords}
onChange={this.updateTriggerWords}
- placeholder='Optional if channel selected'
+ placeholder={this.props.intl.formatMessage(holders.optional)}
+ />
+ </div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.hooks_out.comma'
+ defaultMessage='Comma separated words to trigger on'
/>
</div>
- <div className='padding-top'>{'Comma separated words to trigger on'}</div>
</div>
<div className='padding-top x2'>
- <label className='control-label'>{'Callback URLs:'}</label>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.hooks_out.callback'
+ defaultMessage='Callback URLs: '
+ />
+ </label>
<div className='padding-top'>
<textarea
ref='callbackURLs'
@@ -288,10 +355,15 @@ export default class ManageOutgoingHooks extends React.Component {
resize={false}
rows={3}
onChange={this.updateCallbackURLs}
- placeholder='Each URL must start with http:// or https://'
+ placeholder={this.props.intl.formatMessage(holders.callbackHolder)}
/>
</div>
- <div className='padding-top'>{'New line separated URLs that will receive the HTTP POST event'}</div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.hooks_out.callbackDesc'
+ defaultMessage='New line separated URLs that will receive the HTTP POST event'
+ />
+ </div>
{addError}
</div>
<div className='padding-top padding-bottom'>
@@ -301,7 +373,10 @@ export default class ManageOutgoingHooks extends React.Component {
disabled={disableButton}
onClick={this.addNewHook}
>
- {'Add'}
+ <FormattedMessage
+ id='user.settings.hooks_out.add'
+ defaultMessage='Add'
+ />
</a>
</div>
</div>
@@ -311,3 +386,9 @@ export default class ManageOutgoingHooks extends React.Component {
);
}
}
+
+ManageOutgoingHooks.propTypes = {
+ intl: intlShape.isRequired
+};
+
+export default injectIntl(ManageOutgoingHooks); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_advanced.jsx b/web/react/components/user_settings/user_settings_advanced.jsx
index d4bd00bb5..5c0757589 100644
--- a/web/react/components/user_settings/user_settings_advanced.jsx
+++ b/web/react/components/user_settings/user_settings_advanced.jsx
@@ -6,9 +6,55 @@ import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import Constants from '../../utils/constants.jsx';
import PreferenceStore from '../../stores/preference_store.jsx';
+
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
-export default class AdvancedSettingsDisplay extends React.Component {
+const holders = defineMessages({
+ sendTitle: {
+ id: 'user.settings.advance.sendTitle',
+ defaultMessage: 'Send messages on Ctrl + Enter'
+ },
+ on: {
+ id: 'user.settings.advance.on',
+ defaultMessage: 'On'
+ },
+ off: {
+ id: 'user.settings.advance.off',
+ defaultMessage: 'Off'
+ },
+ preReleaseTitle: {
+ id: 'user.settings.advance.preReleaseTitle',
+ defaultMessage: 'Preview pre-release features'
+ },
+ feature: {
+ id: 'user.settings.advance.feature',
+ defaultMessage: ' Feature '
+ },
+ features: {
+ id: 'user.settings.advance.features',
+ defaultMessage: ' Features '
+ },
+ enabled: {
+ id: 'user.settings.advance.enabled',
+ defaultMessage: 'enabled'
+ },
+ MARKDOWN_PREVIEW: {
+ id: 'user.settings.advance.markdown_preview',
+ defaultMessage: 'Show markdown preview option in message input box'
+ },
+ EMBED_PREVIEW: {
+ id: 'user.settings.advance.embed_preview',
+ defaultMessage: 'Show preview snippet of links below message'
+ },
+ LOC_PREVIEW: {
+ id: 'user.settings.advance.loc_preview',
+ defaultMessage: 'Show user language in display settings'
+ }
+});
+
+class AdvancedSettingsDisplay extends React.Component {
constructor(props) {
super(props);
@@ -104,6 +150,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
render() {
const serverError = this.state.serverError || null;
+ const {formatMessage} = this.props.intl;
let ctrlSendSection;
if (this.props.activeSection === 'advancedCtrlSend') {
@@ -121,7 +168,10 @@ export default class AdvancedSettingsDisplay extends React.Component {
checked={ctrlSendActive[0]}
onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'true')}
/>
- {'On'}
+ <FormattedMessage
+ id='user.settings.advance.on'
+ defaultMessage='On'
+ />
</label>
<br/>
</div>
@@ -132,17 +182,26 @@ export default class AdvancedSettingsDisplay extends React.Component {
checked={ctrlSendActive[1]}
onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'false')}
/>
- {'Off'}
+ <FormattedMessage
+ id='user.settings.advance.off'
+ defaultMessage='Off'
+ />
</label>
<br/>
</div>
- <div><br/>{'If enabled \'Enter\' inserts a new line and \'Ctrl + Enter\' submits the message.'}</div>
+ <div>
+ <br/>
+ <FormattedMessage
+ id='user.settings.advance.sendDesc'
+ defaultMessage="If enabled 'Enter' inserts a new line and 'Ctrl + Enter' submits the message."
+ />
+ </div>
</div>
];
ctrlSendSection = (
<SettingItemMax
- title='Send messages on Ctrl + Enter'
+ title={formatMessage(holders.sendTitle)}
inputs={inputs}
submit={() => this.handleSubmit('send_on_ctrl_enter')}
server_error={serverError}
@@ -155,8 +214,8 @@ export default class AdvancedSettingsDisplay extends React.Component {
} else {
ctrlSendSection = (
<SettingItemMin
- title='Send messages on Ctrl + Enter'
- describe={this.state.settings.send_on_ctrl_enter === 'true' ? 'On' : 'Off'}
+ title={formatMessage(holders.sendTitle)}
+ describe={this.state.settings.send_on_ctrl_enter === 'true' ? formatMessage(holders.on) : formatMessage(holders.off)}
updateSection={() => this.props.updateSection('advancedCtrlSend')}
/>
);
@@ -185,7 +244,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
this.toggleFeature(feature.label, e.target.checked);
}}
/>
- {feature.description}
+ {formatMessage({id: 'user.settings.advance.' + feature.label})}
</label>
</div>
</div>
@@ -195,13 +254,16 @@ export default class AdvancedSettingsDisplay extends React.Component {
inputs.push(
<div key='advancedPreviewFeatures_helptext'>
<br/>
- {'Check any pre-released features you\'d like to preview. You may also need to refresh the page before the setting will take effect.'}
+ <FormattedMessage
+ id='user.settings.advance.preReleaseDesc'
+ defaultMessage="Check any pre-released features you'd like to preview. You may also need to refresh the page before the setting will take effect."
+ />
</div>
);
previewFeaturesSection = (
<SettingItemMax
- title='Preview pre-release features'
+ title={formatMessage(holders.preReleaseTitle)}
inputs={inputs}
submit={this.saveEnabledFeatures}
server_error={serverError}
@@ -214,8 +276,8 @@ export default class AdvancedSettingsDisplay extends React.Component {
} else {
previewFeaturesSection = (
<SettingItemMin
- title='Preview pre-release features'
- describe={this.state.enabledFeatures + (this.state.enabledFeatures === 1 ? ' Feature ' : ' Features ') + 'enabled'}
+ title={formatMessage(holders.preReleaseTitle)}
+ describe={this.state.enabledFeatures + (this.state.enabledFeatures === 1 ? formatMessage(holders.feature) : formatMessage(holders.features)) + formatMessage(holders.enabled)}
updateSection={() => this.props.updateSection('advancedPreviewFeatures')}
/>
);
@@ -242,11 +304,19 @@ export default class AdvancedSettingsDisplay extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'Advanced Settings'}
+ <FormattedMessage
+ id='user.settings.advance.title'
+ defaultMessage='Advanced Settings'
+ />
</h4>
</div>
<div className='user-settings'>
- <h3 className='tab-header'>{'Advanced Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.advance.title'
+ defaultMessage='Advanced Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{ctrlSendSection}
{previewFeaturesSectionDivider}
@@ -259,6 +329,7 @@ export default class AdvancedSettingsDisplay extends React.Component {
}
AdvancedSettingsDisplay.propTypes = {
+ intl: intlShape.isRequired,
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
@@ -266,3 +337,5 @@ AdvancedSettingsDisplay.propTypes = {
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
+
+export default injectIntl(AdvancedSettingsDisplay); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx
index 7bfc9fdbd..fb11dc81b 100644
--- a/web/react/components/user_settings/user_settings_appearance.jsx
+++ b/web/react/components/user_settings/user_settings_appearance.jsx
@@ -11,6 +11,9 @@ import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import Constants from '../../utils/constants.jsx';
+
+import {FormattedMessage} from 'mm-intl';
+
const ActionTypes = Constants.ActionTypes;
export default class UserSettingsAppearance extends React.Component {
@@ -180,7 +183,10 @@ export default class UserSettingsAppearance extends React.Component {
checked={!displayCustom}
onChange={this.updateType.bind(this, 'premade')}
/>
- {'Theme Colors'}
+ <FormattedMessage
+ id='user.settings.appearance.themeColors'
+ defaultMessage='Theme Colors'
+ />
</label>
<br/>
</div>
@@ -191,7 +197,10 @@ export default class UserSettingsAppearance extends React.Component {
checked={displayCustom}
onChange={this.updateType.bind(this, 'custom')}
/>
- {'Custom Theme'}
+ <FormattedMessage
+ id='user.settings.appearance.customTheme'
+ defaultMessage='Custom Theme'
+ />
</label>
<br/>
</div>
@@ -203,14 +212,20 @@ export default class UserSettingsAppearance extends React.Component {
href='#'
onClick={this.submitTheme}
>
- {'Save'}
+ <FormattedMessage
+ id='user.settings.appearance.save'
+ defaultMessage='Save'
+ />
</a>
<a
className='btn btn-sm theme'
href='#'
onClick={this.resetFields}
>
- {'Cancel'}
+ <FormattedMessage
+ id='user.settings.appearance.cancel'
+ defaultMessage='Cancel'
+ />
</a>
</div>
</div>
@@ -235,11 +250,19 @@ export default class UserSettingsAppearance extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'Appearance Settings'}
+ <FormattedMessage
+ id='user.settings.appearance.title'
+ defaultMessage='Appearance Settings'
+ />
</h4>
</div>
<div className='user-settings'>
- <h3 className='tab-header'>{'Appearance Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.appearance.title'
+ defaultMessage='Appearance Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{themeUI}
<div className='divider-dark'/>
@@ -248,7 +271,10 @@ export default class UserSettingsAppearance extends React.Component {
className='theme'
onClick={this.handleImportModal}
>
- {'Import theme colors from Slack'}
+ <FormattedMessage
+ id='user.settings.appearance.import'
+ defaultMessage='Import theme colors from Slack'
+ />
</a>
</div>
</div>
diff --git a/web/react/components/user_settings/user_settings_developer.jsx b/web/react/components/user_settings/user_settings_developer.jsx
index 01e13be57..5868e0ad3 100644
--- a/web/react/components/user_settings/user_settings_developer.jsx
+++ b/web/react/components/user_settings/user_settings_developer.jsx
@@ -5,7 +5,20 @@ import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import * as EventHelpers from '../../dispatcher/event_helpers.jsx';
-export default class DeveloperTab extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ applicationsPreview: {
+ id: 'user.settings.developer.applicationsPreview',
+ defaultMessage: 'Applications (Preview)'
+ },
+ thirdParty: {
+ id: 'user.settings.developer.thirdParty',
+ defaultMessage: 'Open to register a new third-party application'
+ }
+});
+
+class DeveloperTab extends React.Component {
constructor(props) {
super(props);
@@ -20,6 +33,7 @@ export default class DeveloperTab extends React.Component {
render() {
var appSection;
var self = this;
+ const {formatMessage} = this.props.intl;
if (this.props.activeSection === 'app') {
var inputs = [];
@@ -33,7 +47,10 @@ export default class DeveloperTab extends React.Component {
className='btn btn-sm btn-primary'
onClick={this.register}
>
- {'Register New Application'}
+ <FormattedMessage
+ id='user.settings.developer.register'
+ defaultMessage='Register New Application'
+ />
</a>
</div>
</div>
@@ -41,7 +58,7 @@ export default class DeveloperTab extends React.Component {
appSection = (
<SettingItemMax
- title='Applications (Preview)'
+ title={formatMessage(holders.applicationsPreview)}
inputs={inputs}
updateSection={function updateSection(e) {
self.props.updateSection('');
@@ -52,8 +69,8 @@ export default class DeveloperTab extends React.Component {
} else {
appSection = (
<SettingItemMin
- title='Applications (Preview)'
- describe='Open to register a new third-party application'
+ title={formatMessage(holders.applicationsPreview)}
+ describe={formatMessage(holders.thirdParty)}
updateSection={function updateSection() {
self.props.updateSection('app');
}}
@@ -81,11 +98,19 @@ export default class DeveloperTab extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'Developer Settings'}
+ <FormattedMessage
+ id='user.settings.developer.title'
+ defaultMessage='Developer Settings'
+ />
</h4>
</div>
<div className='user-settings'>
- <h3 className='tab-header'>{'Developer Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.developer.title'
+ defaultMessage='Developer Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{appSection}
<div className='divider-dark'/>
@@ -99,8 +124,11 @@ DeveloperTab.defaultProps = {
activeSection: ''
};
DeveloperTab.propTypes = {
+ intl: intlShape.isRequired,
activeSection: React.PropTypes.string,
updateSection: React.PropTypes.func,
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
+
+export default injectIntl(DeveloperTab); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx
index f2c2502fb..3b2a2065b 100644
--- a/web/react/components/user_settings/user_settings_display.jsx
+++ b/web/react/components/user_settings/user_settings_display.jsx
@@ -10,6 +10,47 @@ import PreferenceStore from '../../stores/preference_store.jsx';
import ManageLanguages from './manage_languages.jsx';
import * as Utils from '../../utils/utils.jsx';
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ normalClock: {
+ id: 'user.settings.display.normalClock',
+ defaultMessage: '12-hour clock (example: 4:00 PM)'
+ },
+ militaryClock: {
+ id: 'user.settings.display.militaryClock',
+ defaultMessage: '24-hour clock (example: 16:00)'
+ },
+ clockDisplay: {
+ id: 'user.settings.display.clockDisplay',
+ defaultMessage: 'Clock Display'
+ },
+ teammateDisplay: {
+ id: 'user.settings.display.teammateDisplay',
+ defaultMessage: 'Teammate Name Display'
+ },
+ showNickname: {
+ id: 'user.settings.display.showNickname',
+ defaultMessage: 'Show nickname if one exists, otherwise show first and last name'
+ },
+ showUsername: {
+ id: 'user.settings.display.showUsername',
+ defaultMessage: 'Show username (team default)'
+ },
+ showFullname: {
+ id: 'user.settings.display.showFullname',
+ defaultMessage: 'Show first and last name'
+ },
+ fontTitle: {
+ id: 'user.settings.display.fontTitle',
+ defaultMessage: 'Display Font'
+ },
+ language: {
+ id: 'user.settings.display.language',
+ defaultMessage: 'Language'
+ }
+});
+
function getDisplayStateFromStores() {
const militaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'});
const nameFormat = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', {value: 'username'});
@@ -22,7 +63,7 @@ function getDisplayStateFromStores() {
};
}
-export default class UserSettingsDisplay extends React.Component {
+class UserSettingsDisplay extends React.Component {
constructor(props) {
super(props);
@@ -76,6 +117,7 @@ export default class UserSettingsDisplay extends React.Component {
this.updateState();
}
render() {
+ const {formatMessage} = this.props.intl;
const serverError = this.state.serverError || null;
let clockSection;
let nameFormatSection;
@@ -104,7 +146,10 @@ export default class UserSettingsDisplay extends React.Component {
checked={clockFormat[0]}
onChange={this.handleClockRadio.bind(this, 'false')}
/>
- {'12-hour clock (example: 4:00 PM)'}
+ <FormattedMessage
+ id='user.settings.display.normalClock'
+ defaultMessage='12-hour clock (example: 4:00 PM)'
+ />
</label>
<br/>
</div>
@@ -115,17 +160,26 @@ export default class UserSettingsDisplay extends React.Component {
checked={clockFormat[1]}
onChange={this.handleClockRadio.bind(this, 'true')}
/>
- {'24-hour clock (example: 16:00)'}
+ <FormattedMessage
+ id='user.settings.display.militaryClock'
+ defaultMessage='24-hour clock (example: 16:00)'
+ />
</label>
<br/>
</div>
- <div><br/>{'Select how you prefer time displayed.'}</div>
+ <div>
+ <br/>
+ <FormattedMessage
+ id='user.settings.display.preferTime'
+ defaultMessage='Select how you prefer time displayed.'
+ />
+ </div>
</div>
];
clockSection = (
<SettingItemMax
- title='Clock Display'
+ title={formatMessage(holders.clockDisplay)}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
@@ -135,9 +189,9 @@ export default class UserSettingsDisplay extends React.Component {
} else {
let describe = '';
if (this.state.militaryTime === 'true') {
- describe = '24-hour clock (example: 16:00)';
+ describe = formatMessage(holders.militaryClock);
} else {
- describe = '12-hour clock (example: 4:00 PM)';
+ describe = formatMessage(holders.normalClock);
}
const handleUpdateClockSection = () => {
@@ -146,16 +200,31 @@ export default class UserSettingsDisplay extends React.Component {
clockSection = (
<SettingItemMin
- title='Clock Display'
+ title={formatMessage(holders.clockDisplay)}
describe={describe}
updateSection={handleUpdateClockSection}
/>
);
}
- const showUsername = 'Show username (team default)';
- const showNickname = 'Show nickname if one exists, otherwise show first and last name';
- const showFullName = 'Show first and last name';
+ const showUsername = (
+ <FormattedMessage
+ id='user.settings.display.showUsername'
+ defaultMessage='Show username (team default)'
+ />
+ );
+ const showNickname = (
+ <FormattedMessage
+ id='user.settings.display.showNickname'
+ defaultMessage='Show nickname if one exists, otherwise show first and last name'
+ />
+ );
+ const showFullName = (
+ <FormattedMessage
+ id='user.settings.display.showFullname'
+ defaultMessage='Show first and last name'
+ />
+ );
if (this.props.activeSection === 'name_format') {
const nameFormat = [false, false, false];
if (this.state.nameFormat === 'nickname_full_name') {
@@ -201,13 +270,19 @@ export default class UserSettingsDisplay extends React.Component {
</label>
<br/>
</div>
- <div><br/>{'Set how to display other user\'s names in posts and the Direct Messages list.'}</div>
+ <div>
+ <br/>
+ <FormattedMessage
+ id='user.settings.display.nameOptsDesc'
+ defaultMessage="Set how to display other user's names in posts and the Direct Messages list."
+ />
+ </div>
</div>
];
nameFormatSection = (
<SettingItemMax
- title='Teammate Name Display'
+ title={formatMessage(holders.teammateDisplay)}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
@@ -220,16 +295,16 @@ export default class UserSettingsDisplay extends React.Component {
} else {
let describe = '';
if (this.state.nameFormat === 'username') {
- describe = showUsername;
+ describe = formatMessage(holders.showUsername);
} else if (this.state.nameFormat === 'full_name') {
- describe = showFullName;
+ describe = formatMessage(holders.showFullName);
} else {
- describe = showNickname;
+ describe = formatMessage(holders.showNickname);
}
nameFormatSection = (
<SettingItemMin
- title='Teammate Name Display'
+ title={formatMessage(holders.teammateDisplay)}
describe={describe}
updateSection={() => {
this.props.updateSection('name_format');
@@ -267,13 +342,19 @@ export default class UserSettingsDisplay extends React.Component {
{options}
</select>
</div>
- <div><br/>{'Select the font displayed in the Mattermost user interface.'}</div>
+ <div>
+ <br/>
+ <FormattedMessage
+ id='user.settings.display.fontDesc'
+ defaultMessage='Select the font displayed in the Mattermost user interface.'
+ />
+ </div>
</div>
];
fontSection = (
<SettingItemMax
- title='Display Font'
+ title={formatMessage(holders.fontTitle)}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
@@ -286,7 +367,7 @@ export default class UserSettingsDisplay extends React.Component {
} else {
fontSection = (
<SettingItemMin
- title='Display Font'
+ title={formatMessage(holders.fontTitle)}
describe={this.state.selectedFont}
updateSection={() => {
this.props.updateSection('font');
@@ -307,7 +388,7 @@ export default class UserSettingsDisplay extends React.Component {
languagesSection = (
<SettingItemMax
- title={'Language'}
+ title={formatMessage(holders.language)}
width='medium'
inputs={inputs}
updateSection={(e) => {
@@ -326,7 +407,7 @@ export default class UserSettingsDisplay extends React.Component {
languagesSection = (
<SettingItemMin
- title={'Language'}
+ title={formatMessage(holders.language)}
width='medium'
describe={locale}
updateSection={() => {
@@ -357,11 +438,19 @@ export default class UserSettingsDisplay extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'Display Settings'}
+ <FormattedMessage
+ id='user.settings.display.title'
+ defaultMessage='Display Settings'
+ />
</h4>
</div>
<div className='user-settings'>
- <h3 className='tab-header'>{'Display Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.display.title'
+ defaultMessage='Display Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{fontSection}
<div className='divider-dark'/>
@@ -377,6 +466,7 @@ export default class UserSettingsDisplay extends React.Component {
}
UserSettingsDisplay.propTypes = {
+ intl: intlShape.isRequired,
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
@@ -384,3 +474,5 @@ UserSettingsDisplay.propTypes = {
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
+
+export default injectIntl(UserSettingsDisplay); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index df7ae4a25..f20b4b807 100644
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ b/web/react/components/user_settings/user_settings_general.jsx
@@ -13,7 +13,84 @@ import Constants from '../../utils/constants.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import * as Utils from '../../utils/utils.jsx';
-export default class UserSettingsGeneralTab extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ usernameReserved: {
+ id: 'user.settings.general.usernameReserved',
+ defaultMessage: 'This username is reserved, please choose a new one.'
+ },
+ usernameRestrictions: {
+ id: 'user.settings.general.usernameRestrictions',
+ defaultMessage: "'Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."
+ },
+ validEmail: {
+ id: 'user.settings.general.validEmail',
+ defaultMessage: 'Please enter a valid email address'
+ },
+ emailMatch: {
+ id: 'user.settings.general.emailMatch',
+ defaultMessage: 'The new emails you entered do not match.'
+ },
+ checkEmail: {
+ id: 'user.settings.general.checkEmail',
+ defaultMessage: 'Check your email at {email} to verify the address.'
+ },
+ newAddress: {
+ id: 'user.settings.general.newAddress',
+ defaultMessage: 'New Address: {email}<br />Check your email to verify the above address.'
+ },
+ checkEmailNoAddress: {
+ id: 'user.settings.general.checkEmailNoAddress',
+ defaultMessage: 'Check your email to verify your new address'
+ },
+ loginGitlab: {
+ id: 'user.settings.general.loginGitlab',
+ defaultMessage: 'Log in done through GitLab'
+ },
+ validImage: {
+ id: 'user.settings.general.validImage',
+ defaultMessage: 'Only JPG or PNG images may be used for profile pictures'
+ },
+ imageTooLarge: {
+ id: 'user.settings.general.imageTooLarge',
+ defaultMessage: 'Unable to upload profile image. File is too large.'
+ },
+ uploadImage: {
+ id: 'user.settings.general.uploadImage',
+ defaultMessage: "Click 'Edit' to upload an image."
+ },
+ imageUpdated: {
+ id: 'user.settings.general.imageUpdated',
+ defaultMessage: 'Image last updated {date}'
+ },
+ fullName: {
+ id: 'user.settings.general.fullName',
+ defaultMessage: 'Full Name'
+ },
+ nickname: {
+ id: 'user.settings.general.nickname',
+ defaultMessage: 'Nickname'
+ },
+ username: {
+ id: 'user.settings.general.username',
+ defaultMessage: 'Username'
+ },
+ email: {
+ id: 'user.settings.general.email',
+ defaultMessage: 'Email'
+ },
+ profilePicture: {
+ id: 'user.settings.general.profilePicture',
+ defaultMessage: 'Profile Picture'
+ },
+ close: {
+ id: 'user.settings.general.close',
+ defaultMessage: 'Close'
+ }
+});
+
+class UserSettingsGeneralTab extends React.Component {
constructor(props) {
super(props);
this.submitActive = false;
@@ -42,12 +119,13 @@ export default class UserSettingsGeneralTab extends React.Component {
const user = Object.assign({}, this.props.user);
const username = this.state.username.trim().toLowerCase();
+ const {formatMessage} = this.props.intl;
const usernameError = Utils.isValidUsername(username);
if (usernameError === 'Cannot use a reserved word as a username.') {
- this.setState({clientError: 'This username is reserved, please choose a new one.'});
+ this.setState({clientError: formatMessage(holders.usernameReserved)});
return;
} else if (usernameError) {
- this.setState({clientError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."});
+ this.setState({clientError: formatMessage(holders.usernameRestrictions, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
return;
}
@@ -99,13 +177,14 @@ export default class UserSettingsGeneralTab extends React.Component {
const email = this.state.email.trim().toLowerCase();
const confirmEmail = this.state.confirmEmail.trim().toLowerCase();
+ const {formatMessage} = this.props.intl;
if (email === '' || !Utils.isEmail(email)) {
- this.setState({emailError: 'Please enter a valid email address.', clientError: '', serverError: ''});
+ this.setState({emailError: formatMessage(holders.validEmail), clientError: '', serverError: ''});
return;
}
if (email !== confirmEmail) {
- this.setState({emailError: 'The new emails you entered do not match.', clientError: '', serverError: ''});
+ this.setState({emailError: formatMessage(holders.emailMatch), clientError: '', serverError: ''});
return;
}
@@ -125,7 +204,7 @@ export default class UserSettingsGeneralTab extends React.Component {
const verificationEnabled = global.window.mm_config.SendEmailNotifications === 'true' && global.window.mm_config.RequireEmailVerification === 'true' && emailUpdated;
if (verificationEnabled) {
- ErrorStore.storeLastError({message: 'Check your email at ' + user.email + ' to verify the address.'});
+ ErrorStore.storeLastError({message: this.props.intl.formatMessage(holders.checkEmail, {email: user.email})});
ErrorStore.emitChange();
this.setState({emailChangeInProgress: true});
}
@@ -152,13 +231,14 @@ export default class UserSettingsGeneralTab extends React.Component {
return;
}
+ const {formatMessage} = this.props.intl;
const picture = this.state.picture;
if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') {
- this.setState({clientError: 'Only JPG or PNG images may be used for profile pictures.'});
+ this.setState({clientError: formatMessage(holders.validImage)});
return;
} else if (picture.size > Constants.MAX_FILE_SIZE) {
- this.setState({clientError: 'Unable to upload profile image. File is too large.'});
+ this.setState({clientError: formatMessage(holders.imageTooLarge)});
return;
}
@@ -221,6 +301,7 @@ export default class UserSettingsGeneralTab extends React.Component {
}
render() {
const user = this.props.user;
+ const {formatMessage, formatHTMLMessage} = this.props.intl;
let clientError = null;
if (this.state.clientError) {
@@ -244,7 +325,12 @@ export default class UserSettingsGeneralTab extends React.Component {
key='firstNameSetting'
className='form-group'
>
- <label className='col-sm-5 control-label'>{'First Name'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='user.settings.general.firstName'
+ defaultMessage='First Name'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -261,7 +347,12 @@ export default class UserSettingsGeneralTab extends React.Component {
key='lastNameSetting'
className='form-group'
>
- <label className='col-sm-5 control-label'>{'Last Name'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='user.settings.general.lastName'
+ defaultMessage='Last Name'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -284,20 +375,28 @@ export default class UserSettingsGeneralTab extends React.Component {
href='#'
onClick={notifClick.bind(this)}
>
- {'Notifications'}
+ <FormattedMessage
+ id='user.settings.general.notificationsLink'
+ defaultMessage='Notifications'
+ />
</a>
);
const extraInfo = (
<span>
- {'By default, you will receive mention notifications when someone types your first name. '}
- {'Go to '} {notifLink} {'settings to change this default.'}
+ <FormattedMessage
+ id='user.settings.general.notificationsExtra'
+ defaultMessage='By default, you will receive mention notifications when someone types your first name. Go to {notify} settings to change this default.'
+ values={{
+ notify: (notifLink)
+ }}
+ />
</span>
);
nameSection = (
<SettingItemMax
- title='Full Name'
+ title={formatMessage(holders.fullName)}
inputs={inputs}
submit={this.submitName}
server_error={serverError}
@@ -322,7 +421,7 @@ export default class UserSettingsGeneralTab extends React.Component {
nameSection = (
<SettingItemMin
- title='Full Name'
+ title={formatMessage(holders.fullName)}
describe={fullName}
updateSection={() => {
this.updateSection('name');
@@ -333,7 +432,12 @@ export default class UserSettingsGeneralTab extends React.Component {
let nicknameSection;
if (this.props.activeSection === 'nickname') {
- let nicknameLabel = 'Nickname';
+ let nicknameLabel = (
+ <FormattedMessage
+ id='user.settings.general.nickname'
+ defaultMessage='Nickname'
+ />
+ );
if (Utils.isMobile()) {
nicknameLabel = '';
}
@@ -357,13 +461,16 @@ export default class UserSettingsGeneralTab extends React.Component {
const extraInfo = (
<span>
- {'Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'}
+ <FormattedMessage
+ id='user.settings.general.nicknameExtra'
+ defaultMessage='Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'
+ />
</span>
);
nicknameSection = (
<SettingItemMax
- title='Nickname'
+ title={formatMessage(holders.nickname)}
inputs={inputs}
submit={this.submitNickname}
server_error={serverError}
@@ -378,7 +485,7 @@ export default class UserSettingsGeneralTab extends React.Component {
} else {
nicknameSection = (
<SettingItemMin
- title='Nickname'
+ title={formatMessage(holders.nickname)}
describe={UserStore.getCurrentUser().nickname}
updateSection={() => {
this.updateSection('nickname');
@@ -389,7 +496,12 @@ export default class UserSettingsGeneralTab extends React.Component {
let usernameSection;
if (this.props.activeSection === 'username') {
- let usernameLabel = 'Username';
+ let usernameLabel = (
+ <FormattedMessage
+ id='user.settings.general.username'
+ defaultMessage='Username'
+ />
+ );
if (Utils.isMobile()) {
usernameLabel = '';
}
@@ -411,11 +523,18 @@ export default class UserSettingsGeneralTab extends React.Component {
</div>
);
- const extraInfo = (<span>{'Pick something easy for teammates to recognize and recall.'}</span>);
+ const extraInfo = (
+ <span>
+ <FormattedMessage
+ id='user.settings.general.usernameInfo'
+ defaultMessage='Pick something easy for teammates to recognize and recall.'
+ />
+ </span>
+ );
usernameSection = (
<SettingItemMax
- title='Username'
+ title={formatMessage(holders.username)}
inputs={inputs}
submit={this.submitUsername}
server_error={serverError}
@@ -430,7 +549,7 @@ export default class UserSettingsGeneralTab extends React.Component {
} else {
usernameSection = (
<SettingItemMin
- title='Username'
+ title={formatMessage(holders.username)}
describe={UserStore.getCurrentUser().username}
updateSection={() => {
this.updateSection('username');
@@ -443,16 +562,41 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.props.activeSection === 'email') {
const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
- let helpText = 'Email is used for sign-in, notifications, and password reset. Email requires verification if changed.';
+ let helpText = (
+ <FormattedMessage
+ id='user.settings.general.emailHelp1'
+ defaultMessage='Email is used for sign-in, notifications, and password reset. Email requires verification if changed.'
+ />
+ );
if (!emailEnabled) {
- helpText = <div className='setting-list__hint text-danger'>{'Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'}</div>;
+ helpText = (
+ <div className='setting-list__hint text-danger'>
+ <FormattedMessage
+ id='user.settings.general.emailHelp2'
+ defaultMessage='Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'
+ />
+ </div>
+ );
} else if (!emailVerificationEnabled) {
- helpText = 'Email is used for sign-in, notifications, and password reset.';
+ helpText = (
+ <FormattedMessage
+ id='user.settings.general.emailHelp3'
+ defaultMessage='Email is used for sign-in, notifications, and password reset.'
+ />
+ );
} else if (this.state.emailChangeInProgress) {
const newEmail = UserStore.getCurrentUser().email;
if (newEmail) {
- helpText = 'A verification email was sent to ' + newEmail + '.';
+ helpText = (
+ <FormattedMessage
+ id='user.settings.general.emailHelp4'
+ defaultMessage='A verification email was sent to {email}.'
+ values={{
+ email: newEmail
+ }}
+ />
+ );
}
}
@@ -462,7 +606,12 @@ export default class UserSettingsGeneralTab extends React.Component {
inputs.push(
<div key='emailSetting'>
<div className='form-group'>
- <label className='col-sm-5 control-label'>{'Primary Email'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='user.settings.general.primaryEmail'
+ defaultMessage='Primary Email'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -478,7 +627,12 @@ export default class UserSettingsGeneralTab extends React.Component {
inputs.push(
<div key='confirmEmailSetting'>
<div className='form-group'>
- <label className='col-sm-5 control-label'>{'Confirm Email'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='user.settings.general.confirmEmail'
+ defaultMessage='Confirm Email'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -499,7 +653,12 @@ export default class UserSettingsGeneralTab extends React.Component {
key='oauthEmailInfo'
className='form-group'
>
- <div className='setting-list__hint'>{'Log in occurs through GitLab. Email cannot be updated.'}</div>
+ <div className='setting-list__hint'>
+ <FormattedMessage
+ id='user.settings.general.emailCantUpdate'
+ defaultMessage='Log in occurs through GitLab. Email cannot be updated.'
+ />
+ </div>
{helpText}
</div>
);
@@ -524,20 +683,20 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.state.emailChangeInProgress) {
const newEmail = UserStore.getCurrentUser().email;
if (newEmail) {
- describe = 'New Address: ' + newEmail + '\nCheck your email to verify the above address.';
+ describe = formatHTMLMessage(holders.newAddress, {email: newEmail});
} else {
- describe = 'Check your email to verify your new address';
+ describe = formatMessage(holders.checkEmailNoAddress);
}
} else {
describe = UserStore.getCurrentUser().email;
}
} else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
- describe = 'Log in done through GitLab';
+ describe = formatMessage(holders.loginGitlab);
}
emailSection = (
<SettingItemMin
- title='Email'
+ title={formatMessage(holders.email)}
describe={describe}
updateSection={() => {
this.updateSection('email');
@@ -550,7 +709,7 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.props.activeSection === 'picture') {
pictureSection = (
<SettingPicture
- title='Profile Picture'
+ title={formatMessage(holders.profilePicture)}
submit={this.submitPicture}
src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update + '&' + Utils.getSessionIndex()}
server_error={serverError}
@@ -566,13 +725,15 @@ export default class UserSettingsGeneralTab extends React.Component {
/>
);
} else {
- let minMessage = 'Click \'Edit\' to upload an image.';
+ let minMessage = formatMessage(holders.uploadImage);
if (user.last_picture_update) {
- minMessage = 'Image last updated ' + Utils.displayDate(user.last_picture_update);
+ minMessage = formatMessage(holders.imageUpdated, {
+ date: new Date(user.last_picture_update).toLocaleDateString(global.window.mm_locale, {month: 'short', day: '2-digit', year: 'numeric'})
+ });
}
pictureSection = (
<SettingItemMin
- title='Profile Picture'
+ title={formatMessage(holders.profilePicture)}
describe={minMessage}
updateSection={() => {
this.updateSection('picture');
@@ -588,7 +749,7 @@ export default class UserSettingsGeneralTab extends React.Component {
type='button'
className='close'
data-dismiss='modal'
- aria-label='Close'
+ aria-label={formatMessage(holders.close)}
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
@@ -601,11 +762,19 @@ export default class UserSettingsGeneralTab extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'General Settings'}
+ <FormattedMessage
+ id='user.settings.general.title'
+ defaultMessage='General Settings'
+ />
</h4>
</div>
<div className='user-settings'>
- <h3 className='tab-header'>{'General Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.general.title'
+ defaultMessage='General Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{nameSection}
<div className='divider-light'/>
@@ -624,6 +793,7 @@ export default class UserSettingsGeneralTab extends React.Component {
}
UserSettingsGeneralTab.propTypes = {
+ intl: intlShape.isRequired,
user: React.PropTypes.object.isRequired,
updateSection: React.PropTypes.func.isRequired,
updateTab: React.PropTypes.func.isRequired,
@@ -631,3 +801,5 @@ UserSettingsGeneralTab.propTypes = {
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
+
+export default injectIntl(UserSettingsGeneralTab); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_integrations.jsx b/web/react/components/user_settings/user_settings_integrations.jsx
index a86510eb3..abd04a301 100644
--- a/web/react/components/user_settings/user_settings_integrations.jsx
+++ b/web/react/components/user_settings/user_settings_integrations.jsx
@@ -6,7 +6,28 @@ import SettingItemMax from '../setting_item_max.jsx';
import ManageIncomingHooks from './manage_incoming_hooks.jsx';
import ManageOutgoingHooks from './manage_outgoing_hooks.jsx';
-export default class UserSettingsIntegrationsTab extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ inName: {
+ id: 'user.settings.integrations.incomingWebhooks',
+ defaultMessage: 'Incoming Webhooks'
+ },
+ inDesc: {
+ id: 'user.settings.integrations.incomingWebhooksDescription',
+ defaultMessage: 'Manage your incoming webhooks'
+ },
+ outName: {
+ id: 'user.settings.integrations.outWebhooks',
+ defaultMessage: 'Outgoing Webhooks'
+ },
+ outDesc: {
+ id: 'user.settings.integrations.outWebhooksDescription',
+ defaultMessage: 'Manage your outgoing webhooks'
+ }
+});
+
+class UserSettingsIntegrationsTab extends React.Component {
constructor(props) {
super(props);
@@ -21,6 +42,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
let incomingHooksSection;
let outgoingHooksSection;
var inputs = [];
+ const {formatMessage} = this.props.intl;
if (global.window.mm_config.EnableIncomingWebhooks === 'true') {
if (this.props.activeSection === 'incoming-hooks') {
@@ -30,7 +52,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
incomingHooksSection = (
<SettingItemMax
- title='Incoming Webhooks'
+ title={formatMessage(holders.inName)}
width='medium'
inputs={inputs}
updateSection={(e) => {
@@ -42,9 +64,9 @@ export default class UserSettingsIntegrationsTab extends React.Component {
} else {
incomingHooksSection = (
<SettingItemMin
- title='Incoming Webhooks'
+ title={formatMessage(holders.inName)}
width='medium'
- describe='Manage your incoming webhooks'
+ describe={formatMessage(holders.inDesc)}
updateSection={() => {
this.updateSection('incoming-hooks');
}}
@@ -61,7 +83,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
outgoingHooksSection = (
<SettingItemMax
- title='Outgoing Webhooks'
+ title={formatMessage(holders.outName)}
width='medium'
inputs={inputs}
updateSection={(e) => {
@@ -73,9 +95,9 @@ export default class UserSettingsIntegrationsTab extends React.Component {
} else {
outgoingHooksSection = (
<SettingItemMin
- title='Outgoing Webhooks'
+ title={formatMessage(holders.outName)}
width='medium'
- describe='Manage your outgoing webhooks'
+ describe={formatMessage(holders.outDesc)}
updateSection={() => {
this.updateSection('outgoing-hooks');
}}
@@ -104,11 +126,19 @@ export default class UserSettingsIntegrationsTab extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'Integration Settings'}
+ <FormattedMessage
+ id='user.settings.integrations.title'
+ defaultMessage='Integration Settings'
+ />
</h4>
</div>
<div className='user-settings'>
- <h3 className='tab-header'>{'Integration Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.integrations.title'
+ defaultMessage='Integration Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{incomingHooksSection}
<div className='divider-light'/>
@@ -121,6 +151,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
}
UserSettingsIntegrationsTab.propTypes = {
+ intl: intlShape.isRequired,
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
@@ -128,3 +159,5 @@ UserSettingsIntegrationsTab.propTypes = {
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
+
+export default injectIntl(UserSettingsIntegrationsTab); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx
index 36e1aa217..2a0a90cf5 100644
--- a/web/react/components/user_settings/user_settings_modal.jsx
+++ b/web/react/components/user_settings/user_settings_modal.jsx
@@ -6,7 +6,56 @@ const Modal = ReactBootstrap.Modal;
import SettingsSidebar from '../settings_sidebar.jsx';
import UserSettings from './user_settings.jsx';
-export default class UserSettingsModal extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ general: {
+ id: 'user.settings.modal.general',
+ defaultMessage: 'General'
+ },
+ security: {
+ id: 'user.settings.modal.security',
+ defaultMessage: 'Security'
+ },
+ notifications: {
+ id: 'user.settings.modal.notifications',
+ defaultMessage: 'Notifications'
+ },
+ appearance: {
+ id: 'user.settings.modal.appearance',
+ defaultMessage: 'Appearance'
+ },
+ developer: {
+ id: 'user.settings.modal.developer',
+ defaultMessage: 'Developer'
+ },
+ integrations: {
+ id: 'user.settings.modal.integrations',
+ defaultMessage: 'Integrations'
+ },
+ display: {
+ id: 'user.settings.modal.display',
+ defaultMessage: 'Display'
+ },
+ advanced: {
+ id: 'user.settings.modal.advanced',
+ defaultMessage: 'Advanced'
+ },
+ confirmTitle: {
+ id: 'user.settings.modal.confirmTitle',
+ defaultMessage: 'Discard Changes?'
+ },
+ confirmMsg: {
+ id: 'user.settings.modal.confirmMsg',
+ defaultMessage: 'You have unsaved changes, are you sure you want to discard them?'
+ },
+ confirmBtns: {
+ id: 'user.settings.modal.confirmBtns',
+ defaultMessage: 'Yes, Discard'
+ }
+});
+
+class UserSettingsModal extends React.Component {
constructor(props) {
super(props);
@@ -170,20 +219,21 @@ export default class UserSettingsModal extends React.Component {
}
render() {
+ const {formatMessage} = this.props.intl;
var tabs = [];
- tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
- tabs.push({name: 'security', uiName: 'Security', icon: 'glyphicon glyphicon-lock'});
- tabs.push({name: 'notifications', uiName: 'Notifications', icon: 'glyphicon glyphicon-exclamation-sign'});
- tabs.push({name: 'appearance', uiName: 'Appearance', icon: 'glyphicon glyphicon-wrench'});
+ tabs.push({name: 'general', uiName: formatMessage(holders.general), icon: 'glyphicon glyphicon-cog'});
+ tabs.push({name: 'security', uiName: formatMessage(holders.security), icon: 'glyphicon glyphicon-lock'});
+ tabs.push({name: 'notifications', uiName: formatMessage(holders.notifications), icon: 'glyphicon glyphicon-exclamation-sign'});
+ tabs.push({name: 'appearance', uiName: formatMessage(holders.appearance), icon: 'glyphicon glyphicon-wrench'});
if (global.window.mm_config.EnableOAuthServiceProvider === 'true') {
- tabs.push({name: 'developer', uiName: 'Developer', icon: 'glyphicon glyphicon-th'});
+ tabs.push({name: 'developer', uiName: formatMessage(holders.developer), icon: 'glyphicon glyphicon-th'});
}
if (global.window.mm_config.EnableIncomingWebhooks === 'true' || global.window.mm_config.EnableOutgoingWebhooks === 'true') {
- tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'});
+ tabs.push({name: 'integrations', uiName: formatMessage(holders.integrations), icon: 'glyphicon glyphicon-transfer'});
}
- tabs.push({name: 'display', uiName: 'Display', icon: 'glyphicon glyphicon-eye-open'});
- tabs.push({name: 'advanced', uiName: 'Advanced', icon: 'glyphicon glyphicon-list-alt'});
+ tabs.push({name: 'display', uiName: formatMessage(holders.display), icon: 'glyphicon glyphicon-eye-open'});
+ tabs.push({name: 'advanced', uiName: formatMessage(holders.advanced), icon: 'glyphicon glyphicon-list-alt'});
return (
<Modal
@@ -194,7 +244,12 @@ export default class UserSettingsModal extends React.Component {
enforceFocus={this.state.enforceFocus}
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'Account Settings'}</Modal.Title>
+ <Modal.Title>
+ <FormattedMessage
+ id='user.settings.modal.title'
+ defaultMessage='Account Settings'
+ />
+ </Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<div className='settings-table'>
@@ -221,9 +276,9 @@ export default class UserSettingsModal extends React.Component {
</div>
</Modal.Body>
<ConfirmModal
- title='Discard Changes?'
- message='You have unsaved changes, are you sure you want to discard them?'
- confirm_button='Yes, Discard'
+ title={formatMessage(holders.confirmTitle)}
+ message={formatMessage(holders.confirmMsg)}
+ confirm_button={formatMessage(holders.confirmBtns)}
show={this.state.showConfirmModal}
onConfirm={this.handleConfirm}
onCancel={this.handleCancelConfirmation}
@@ -234,6 +289,9 @@ export default class UserSettingsModal extends React.Component {
}
UserSettingsModal.propTypes = {
+ intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func.isRequired
};
+
+export default injectIntl(UserSettingsModal); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx
index f762405af..91a03eb70 100644
--- a/web/react/components/user_settings/user_settings_notifications.jsx
+++ b/web/react/components/user_settings/user_settings_notifications.jsx
@@ -10,6 +10,8 @@ import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import * as Utils from '../../utils/utils.jsx';
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
function getNotificationsStateFromStores() {
var user = UserStore.getCurrentUser();
var soundNeeded = !Utils.isBrowserFirefox();
@@ -73,7 +75,30 @@ function getNotificationsStateFromStores() {
firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey};
}
-export default class NotificationsTab extends React.Component {
+const holders = defineMessages({
+ desktop: {
+ id: 'user.settings.notifications.desktop',
+ defaultMessage: 'Send desktop notifications'
+ },
+ desktopSounds: {
+ id: 'user.settings.notifications.desktopSounds',
+ defaultMessage: 'Desktop notification sounds'
+ },
+ emailNotifications: {
+ id: 'user.settings.notifications.emailNotifications',
+ defaultMessage: 'Email notifications'
+ },
+ wordsTrigger: {
+ id: 'user.settings.notifications.wordsTrigger',
+ defaultMessage: 'Words that trigger mentions'
+ },
+ close: {
+ id: 'user.settings.notifications.close',
+ defaultMessage: 'Close'
+ }
+});
+
+class NotificationsTab extends React.Component {
constructor(props) {
super(props);
@@ -198,6 +223,7 @@ export default class NotificationsTab extends React.Component {
this.updateCustomMentionKeys();
}
render() {
+ const {formatMessage} = this.props.intl;
var serverError = null;
if (this.state.serverError) {
serverError = this.state.serverError;
@@ -227,7 +253,10 @@ export default class NotificationsTab extends React.Component {
checked={notifyActive[0]}
onChange={this.handleNotifyRadio.bind(this, 'all')}
/>
- {'For all activity'}
+ <FormattedMessage
+ id='user.settings.notification.allActivity'
+ defaultMessage='For all activity'
+ />
</label>
<br/>
</div>
@@ -238,7 +267,10 @@ export default class NotificationsTab extends React.Component {
checked={notifyActive[1]}
onChange={this.handleNotifyRadio.bind(this, 'mention')}
/>
- {'Only for mentions and direct messages'}
+ <FormattedMessage
+ id='user.settings.notifications.onlyMentions'
+ defaultMessage='Only for mentions and direct messages'
+ />
</label>
<br/>
</div>
@@ -249,17 +281,27 @@ export default class NotificationsTab extends React.Component {
checked={notifyActive[2]}
onChange={this.handleNotifyRadio.bind(this, 'none')}
/>
- {'Never'}
+ <FormattedMessage
+ id='user.settings.notifications.never'
+ defaultMessage='Never'
+ />
</label>
</div>
</div>
);
- const extraInfo = <span>{'Desktop notifications are available on Firefox, Safari, and Chrome.'}</span>;
+ const extraInfo = (
+ <span>
+ <FormattedMessage
+ id='user.settings.notifications.info'
+ defaultMessage='Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
+ />
+ </span>
+ );
desktopSection = (
<SettingItemMax
- title='Send desktop notifications'
+ title={formatMessage(holders.desktop)}
extraInfo={extraInfo}
inputs={inputs}
submit={this.handleSubmit}
@@ -270,11 +312,26 @@ export default class NotificationsTab extends React.Component {
} else {
let describe = '';
if (this.state.notifyLevel === 'mention') {
- describe = 'Only for mentions and direct messages';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notifications.onlyMentions'
+ defaultMessage='Only for mentions and direct messages'
+ />
+ );
} else if (this.state.notifyLevel === 'none') {
- describe = 'Never';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notifications.never'
+ defaultMessage='Never'
+ />
+ );
} else {
- describe = 'For all activity';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notification.allActivity'
+ defaultMessage='For all activity'
+ />
+ );
}
handleUpdateDesktopSection = function updateDesktopSection() {
@@ -283,7 +340,7 @@ export default class NotificationsTab extends React.Component {
desktopSection = (
<SettingItemMin
- title='Send desktop notifications'
+ title={formatMessage(holders.desktop)}
describe={describe}
updateSection={handleUpdateDesktopSection}
/>
@@ -311,7 +368,10 @@ export default class NotificationsTab extends React.Component {
checked={soundActive[0]}
onChange={this.handleSoundRadio.bind(this, 'true')}
/>
- {'On'}
+ <FormattedMessage
+ id='user.settings.notifications.on'
+ defaultMessage='On'
+ />
</label>
<br/>
</div>
@@ -322,18 +382,28 @@ export default class NotificationsTab extends React.Component {
checked={soundActive[1]}
onChange={this.handleSoundRadio.bind(this, 'false')}
/>
- {'Off'}
+ <FormattedMessage
+ id='user.settings.notifications.off'
+ defaultMessage='Off'
+ />
</label>
<br/>
</div>
</div>
);
- const extraInfo = <span>{'Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'}</span>;
+ const extraInfo = (
+ <span>
+ <FormattedMessage
+ id='user.settings.notifications.info'
+ defaultMessage='Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
+ />
+ </span>
+ );
soundSection = (
<SettingItemMax
- title='Desktop notification sounds'
+ title={formatMessage(holders.desktopSounds)}
extraInfo={extraInfo}
inputs={inputs}
submit={this.handleSubmit}
@@ -344,11 +414,26 @@ export default class NotificationsTab extends React.Component {
} else {
let describe = '';
if (!this.state.soundNeeded) {
- describe = 'Please configure notification sounds in your browser settings';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notification.soundConfig'
+ defaultMessage='Please configure notification sounds in your browser settings'
+ />
+ );
} else if (this.state.enableSound === 'false') {
- describe = 'Off';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notifications.off'
+ defaultMessage='Off'
+ />
+ );
} else {
- describe = 'On';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notifications.on'
+ defaultMessage='On'
+ />
+ );
}
handleUpdateSoundSection = function updateSoundSection() {
@@ -357,7 +442,7 @@ export default class NotificationsTab extends React.Component {
soundSection = (
<SettingItemMin
- title='Desktop notification sounds'
+ title={formatMessage(holders.desktopSounds)}
describe={describe}
updateSection={handleUpdateSoundSection}
disableOpen = {!this.state.soundNeeded}
@@ -386,7 +471,10 @@ export default class NotificationsTab extends React.Component {
checked={emailActive[0]}
onChange={this.handleEmailRadio.bind(this, 'true')}
/>
- {'On'}
+ <FormattedMessage
+ id='user.settings.notifications.on'
+ defaultMessage='On'
+ />
</label>
<br/>
</div>
@@ -397,17 +485,28 @@ export default class NotificationsTab extends React.Component {
checked={emailActive[1]}
onChange={this.handleEmailRadio.bind(this, 'false')}
/>
- {'Off'}
+ <FormattedMessage
+ id='user.settings.notifications.off'
+ defaultMessage='Off'
+ />
</label>
<br/>
</div>
- <div><br/>{'Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from ' + global.window.mm_config.SiteName + ' for more than 5 minutes.'}</div>
+ <div><br/>
+ <FormattedMessage
+ id='user.settings.notifications.emailInfo'
+ defaultMessage='Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from {siteName} for more than 5 minutes.'
+ values={{
+ siteName: global.window.mm_config.SiteName
+ }}
+ />
+ </div>
</div>
);
emailSection = (
<SettingItemMax
- title='Email notifications'
+ title={formatMessage(holders.emailNotifications)}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
@@ -417,9 +516,19 @@ export default class NotificationsTab extends React.Component {
} else {
let describe = '';
if (this.state.enableEmail === 'false') {
- describe = 'Off';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notifications.off'
+ defaultMessage='Off'
+ />
+ );
} else {
- describe = 'On';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notifications.on'
+ defaultMessage='On'
+ />
+ );
}
handleUpdateEmailSection = function updateEmailSection() {
@@ -428,7 +537,7 @@ export default class NotificationsTab extends React.Component {
emailSection = (
<SettingItemMin
- title='Email notifications'
+ title={formatMessage(holders.emailNotifications)}
describe={describe}
updateSection={handleUpdateEmailSection}
/>
@@ -459,7 +568,13 @@ export default class NotificationsTab extends React.Component {
checked={this.state.firstNameKey}
onChange={handleUpdateFirstNameKey}
/>
- {'Your case sensitive first name "' + user.first_name + '"'}
+ <FormattedMessage
+ id='user.settings.notifications.sensitiveName'
+ defaultMessage='Your case sensitive first name "{first_name}"'
+ values={{
+ first_name: user.first_name
+ }}
+ />
</label>
</div>
</div>
@@ -478,7 +593,13 @@ export default class NotificationsTab extends React.Component {
checked={this.state.usernameKey}
onChange={handleUpdateUsernameKey}
/>
- {'Your non-case sensitive username "' + user.username + '"'}
+ <FormattedMessage
+ id='user.settings.notifications.sensitiveUsername'
+ defaultMessage='Your non-case sensitive username "{username}"'
+ values={{
+ username: user.username
+ }}
+ />
</label>
</div>
</div>
@@ -496,7 +617,13 @@ export default class NotificationsTab extends React.Component {
checked={this.state.mentionKey}
onChange={handleUpdateMentionKey}
/>
- {'Your username mentioned "@' + user.username + '"'}
+ <FormattedMessage
+ id='user.settings.notifications.usernameMention'
+ defaultMessage='Your username mentioned "@{username}"'
+ values={{
+ username: user.username
+ }}
+ />
</label>
</div>
</div>
@@ -514,7 +641,10 @@ export default class NotificationsTab extends React.Component {
checked={this.state.allKey}
onChange={handleUpdateAllKey}
/>
- {'Team-wide mentions "@all"'}
+ <FormattedMessage
+ id='user.settings.notifications.teamWide'
+ defaultMessage='Team-wide mentions "@all"'
+ />
</label>
</div>
</div>
@@ -532,7 +662,10 @@ export default class NotificationsTab extends React.Component {
checked={this.state.channelKey}
onChange={handleUpdateChannelKey}
/>
- {'Channel-wide mentions "@channel"'}
+ <FormattedMessage
+ id='user.settings.notifications.channelWide'
+ defaultMessage='Channel-wide mentions "@channel"'
+ />
</label>
</div>
</div>
@@ -548,7 +681,10 @@ export default class NotificationsTab extends React.Component {
checked={this.state.customKeysChecked}
onChange={this.updateCustomMentionKeys}
/>
- {'Other non-case sensitive words, separated by commas:'}
+ <FormattedMessage
+ id='user.settings.notifications.sensitiveWords'
+ defaultMessage='Other non-case sensitive words, separated by commas:'
+ />
</label>
</div>
<input
@@ -563,7 +699,7 @@ export default class NotificationsTab extends React.Component {
keysSection = (
<SettingItemMax
- title='Words that trigger mentions'
+ title={formatMessage(holders.wordsTrigger)}
inputs={inputs}
submit={this.handleSubmit}
server_error={serverError}
@@ -601,7 +737,12 @@ export default class NotificationsTab extends React.Component {
if (describe.length > 0) {
describe = describe.substring(0, describe.length - 2);
} else {
- describe = 'No words configured';
+ describe = (
+ <FormattedMessage
+ id='user.settings.notifications.noWords'
+ defaultMessage='No words configured'
+ />
+ );
}
handleUpdateKeysSection = function updateKeysSection() {
@@ -610,7 +751,7 @@ export default class NotificationsTab extends React.Component {
keysSection = (
<SettingItemMin
- title='Words that trigger mentions'
+ title={formatMessage(holders.wordsTrigger)}
describe={describe}
updateSection={handleUpdateKeysSection}
/>
@@ -624,7 +765,7 @@ export default class NotificationsTab extends React.Component {
type='button'
className='close'
data-dismiss='modal'
- aria-label='Close'
+ aria-label={formatMessage(holders.close)}
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
@@ -637,14 +778,22 @@ export default class NotificationsTab extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'Notification Settings'}
+ <FormattedMessage
+ id='user.settings.notifications.title'
+ defaultMessage='Notification Settings'
+ />
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
- <h3 className='tab-header'>{'Notifications'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.notifications.header'
+ defaultMessage='Notifications'
+ />
+ </h3>
<div className='divider-dark first'/>
{desktopSection}
<div className='divider-light'/>
@@ -667,6 +816,7 @@ NotificationsTab.defaultProps = {
activeTab: ''
};
NotificationsTab.propTypes = {
+ intl: intlShape.isRequired,
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
@@ -675,3 +825,5 @@ NotificationsTab.propTypes = {
closeModal: React.PropTypes.func.isRequired,
collapseModal: React.PropTypes.func.isRequired
};
+
+export default injectIntl(NotificationsTab); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx
index 5a21abd19..0c9e722de 100644
--- a/web/react/components/user_settings/user_settings_security.jsx
+++ b/web/react/components/user_settings/user_settings_security.jsx
@@ -13,7 +13,40 @@ import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import Constants from '../../utils/constants.jsx';
-export default class SecurityTab extends React.Component {
+import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
+
+const holders = defineMessages({
+ currentPasswordError: {
+ id: 'user.settings.security.currentPasswordError',
+ defaultMessage: 'Please enter your current password'
+ },
+ passwordLengthError: {
+ id: 'user.settings.security.passwordLengthError',
+ defaultMessage: 'New passwords must be at least {chars} characters'
+ },
+ passwordMatchError: {
+ id: 'user.settings.security.passwordMatchError',
+ defaultMessage: 'The new passwords you entered do not match'
+ },
+ password: {
+ id: 'user.settings.security.password',
+ defaultMessage: 'Password'
+ },
+ lastUpdated: {
+ id: 'user.settings.security.lastUpdated',
+ defaultMessage: 'Last updated {date} at {time}'
+ },
+ method: {
+ id: 'user.settings.security.method',
+ defaultMessage: 'Sign-in Method'
+ },
+ close: {
+ id: 'user.settings.security.close',
+ defaultMessage: 'Close'
+ }
+});
+
+class SecurityTab extends React.Component {
constructor(props) {
super(props);
@@ -43,18 +76,19 @@ export default class SecurityTab extends React.Component {
var newPassword = this.state.newPassword;
var confirmPassword = this.state.confirmPassword;
+ const {formatMessage} = this.props.intl;
if (currentPassword === '') {
- this.setState({passwordError: 'Please enter your current password', serverError: ''});
+ this.setState({passwordError: formatMessage(holders.currentPasswordError), serverError: ''});
return;
}
if (newPassword.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({passwordError: 'New passwords must be at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters', serverError: ''});
+ this.setState({passwordError: formatMessage(holders.passwordLengthError, {chars: Constants.MIN_PASSWORD_LENGTH}), serverError: ''});
return;
}
if (newPassword !== confirmPassword) {
- this.setState({passwordError: 'The new passwords you entered do not match', serverError: ''});
+ this.setState({passwordError: formatMessage(holders.passwordMatchError), serverError: ''});
return;
}
@@ -92,6 +126,7 @@ export default class SecurityTab extends React.Component {
}
createPasswordSection() {
let updateSectionStatus;
+ const {formatMessage} = this.props.intl;
if (this.props.activeSection === 'password' && this.props.user.auth_service === '') {
const inputs = [];
@@ -101,7 +136,12 @@ export default class SecurityTab extends React.Component {
key='currentPasswordUpdateForm'
className='form-group'
>
- <label className='col-sm-5 control-label'>{'Current Password'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='user.settings.security.currentPassword'
+ defaultMessage='Current Password'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -117,7 +157,12 @@ export default class SecurityTab extends React.Component {
key='newPasswordUpdateForm'
className='form-group'
>
- <label className='col-sm-5 control-label'>{'New Password'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='user.settings.security.newPassword'
+ defaultMessage='New Password'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -133,7 +178,12 @@ export default class SecurityTab extends React.Component {
key='retypeNewPasswordUpdateForm'
className='form-group'
>
- <label className='col-sm-5 control-label'>{'Retype New Password'}</label>
+ <label className='col-sm-5 control-label'>
+ <FormattedMessage
+ id='user.settings.security.retypePassword'
+ defaultMessage='Retype New Password'
+ />
+ </label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -153,7 +203,7 @@ export default class SecurityTab extends React.Component {
return (
<SettingItemMax
- title='Password'
+ title={formatMessage(holders.password)}
inputs={inputs}
submit={this.submitPassword}
server_error={this.state.serverError}
@@ -165,20 +215,16 @@ export default class SecurityTab extends React.Component {
var describe;
var d = new Date(this.props.user.last_password_update);
- var hour = '12';
- if (d.getHours() % 12) {
- hour = String(d.getHours() % 12);
- }
- var min = String(d.getMinutes());
- if (d.getMinutes() < 10) {
- min = '0' + d.getMinutes();
- }
var timeOfDay = ' am';
if (d.getHours() >= 12) {
timeOfDay = ' pm';
}
- describe = 'Last updated ' + Constants.MONTHS[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear() + ' at ' + hour + ':' + min + timeOfDay;
+ const locale = global.window.mm_locale;
+ describe = formatMessage(holders.lastUpdated, {
+ date: d.toLocaleDateString(locale, {month: 'short', day: '2-digit', year: 'numeric'}),
+ time: d.toLocaleTimeString(locale, {hours12: true, hour: '2-digit', minute: '2-digit'}) + timeOfDay
+ });
updateSectionStatus = function updateSection() {
this.props.updateSection('password');
@@ -186,7 +232,7 @@ export default class SecurityTab extends React.Component {
return (
<SettingItemMin
- title='Password'
+ title={formatMessage(holders.password)}
describe={describe}
updateSection={updateSectionStatus}
/>
@@ -208,7 +254,10 @@ export default class SecurityTab extends React.Component {
className='btn btn-primary'
href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email)}
>
- {'Switch to using email and password'}
+ <FormattedMessage
+ id='user.settings.security.switchEmail'
+ defaultMessage='Switch to using email and password'
+ />
</a>
<br/>
</div>
@@ -223,7 +272,10 @@ export default class SecurityTab extends React.Component {
className='btn btn-primary'
href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&new_type=' + Constants.GITLAB_SERVICE}
>
- {'Switch to using GitLab SSO'}
+ <FormattedMessage
+ id='user.settings.security.switchGitlab'
+ defaultMessage='Switch to using GitLab SSO'
+ />
</a>
<br/>
</div>
@@ -238,7 +290,10 @@ export default class SecurityTab extends React.Component {
className='btn btn-primary'
href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&new_type=' + Constants.GOOGLE_SERVICE}
>
- {'Switch to using Google SSO'}
+ <FormattedMessage
+ id='user.settings.security.switchGoogle'
+ defaultMessage='Switch to using Google SSO'
+ />
</a>
<br/>
</div>
@@ -260,11 +315,18 @@ export default class SecurityTab extends React.Component {
e.preventDefault();
}.bind(this);
- const extraInfo = <span>{'You may only have one sign-in method at a time. Switching sign-in method will send an email notifying you if the change was successful.'}</span>;
+ const extraInfo = (
+ <span>
+ <FormattedMessage
+ id='user.settings.security.oneSignin'
+ defaultMessage='You may only have one sign-in method at a time. Switching sign-in method will send an email notifying you if the change was successful.'
+ />
+ </span>
+ );
return (
<SettingItemMax
- title='Sign-in Method'
+ title={this.props.intl.formatMessage(holders.method)}
extraInfo={extraInfo}
inputs={inputs}
server_error={this.state.serverError}
@@ -277,14 +339,24 @@ export default class SecurityTab extends React.Component {
this.props.updateSection('signin');
}.bind(this);
- let describe = 'Email and Password';
+ let describe = (
+ <FormattedMessage
+ id='user.settings.security.emailPwd'
+ defaultMessage='Email and Password'
+ />
+ );
if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
- describe = 'GitLab SSO';
+ describe = (
+ <FormattedMessage
+ id='user.settings.security.gitlab'
+ defaultMessage='GitLab SSO'
+ />
+ );
}
return (
<SettingItemMin
- title='Sign-in Method'
+ title={this.props.intl.formatMessage(holders.method)}
describe={describe}
updateSection={updateSectionStatus}
/>
@@ -309,7 +381,7 @@ export default class SecurityTab extends React.Component {
type='button'
className='close'
data-dismiss='modal'
- aria-label='Close'
+ aria-label={this.props.intl.formatMessage(holders.close)}
onClick={this.props.closeModal}
>
<span aria-hidden='true'>{'×'}</span>
@@ -322,11 +394,19 @@ export default class SecurityTab extends React.Component {
className='modal-back'
onClick={this.props.collapseModal}
/>
- {'Security Settings'}
+ <FormattedMessage
+ id='user.settings.security.title'
+ defaultMessage='Security Settings'
+ />
</h4>
</div>
<div className='user-settings'>
- <h3 className='tab-header'>{'Security Settings'}</h3>
+ <h3 className='tab-header'>
+ <FormattedMessage
+ id='user.settings.security.title'
+ defaultMessage='Security Settings'
+ />
+ </h3>
<div className='divider-dark first'/>
{passwordSection}
<div className='divider-light'/>
@@ -337,14 +417,22 @@ export default class SecurityTab extends React.Component {
className='security-links theme'
dialogType={AccessHistoryModal}
>
- <i className='fa fa-clock-o'></i>{'View Access History'}
+ <i className='fa fa-clock-o'></i>
+ <FormattedMessage
+ id='user.settings.security.viewHistory'
+ defaultMessage='View Access History'
+ />
</ToggleModalButton>
<b> </b>
<ToggleModalButton
className='security-links theme'
dialogType={ActivityLogModal}
>
- <i className='fa fa-clock-o'></i>{'View and Logout of Active Sessions'}
+ <i className='fa fa-clock-o'></i>
+ <FormattedMessage
+ id='user.settings.security.logoutActiveSessions'
+ defaultMessage='View and Logout of Active Sessions'
+ />
</ToggleModalButton>
</div>
</div>
@@ -357,6 +445,7 @@ SecurityTab.defaultProps = {
activeSection: ''
};
SecurityTab.propTypes = {
+ intl: intlShape.isRequired,
user: React.PropTypes.object,
activeSection: React.PropTypes.string,
updateSection: React.PropTypes.func,
@@ -365,3 +454,5 @@ SecurityTab.propTypes = {
collapseModal: React.PropTypes.func.isRequired,
setEnforceFocus: React.PropTypes.func.isRequired
};
+
+export default injectIntl(SecurityTab); \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index 1f7a55cd0..be85ef07b 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -597,7 +597,7 @@ body.ios {
position: absolute;
right: 0;
top: 30px;
- width: 85px;
+ width: 65px;
white-space: nowrap;
}
@@ -666,7 +666,7 @@ body.ios {
word-wrap: break-word;
padding: 0.2em 0.5em 0em;
@include legacy-pie-clearfix;
- width: calc(100% - 95px);
+ width: calc(100% - 75px);
p {
margin: 0 0 0.4em;
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index a37974c7a..832481cc5 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -72,23 +72,19 @@
}
- .post {
-
- .post-list__content & {
+}
- &:hover {
- background: transparent;
- }
+@media screen and (max-width: 768px) {
- .comment-icon__container {
- visibility: hidden;
- }
+ .signup-team__container {
+ font-size: 1em;
+ }
- }
+ .date-separator.hovered--after:before, .new-separator.hovered--after:before {
+ background: none !important;
+ }
- .dropdown, .post__reply {
- visibility: visible;
- }
+ .post {
.post__dropdown {
line-height: 9px;
@@ -106,17 +102,21 @@
}
- }
+ .post-list__content & {
-}
+ &:hover {
+ background: transparent;
+ }
-@media screen and (max-width: 768px) {
+ .comment-icon__container {
+ visibility: visible;
+ }
- .signup-team__container {
- font-size: 1em;
- }
+ }
- .post {
+ .dropdown, .post__reply {
+ visibility: visible;
+ }
.post__body {
width: calc(100% - 75px);
diff --git a/web/static/i18n/en.json b/web/static/i18n/en.json
index 45000a890..62608b85a 100644
--- a/web/static/i18n/en.json
+++ b/web/static/i18n/en.json
@@ -1,4 +1,73 @@
{
+ "about.teamEdtion": "Team Edition",
+ "about.enterpriseEdition": "Enterprise Edition",
+ "about.licensed": "Licensed by:",
+ "about.title": "About Mattermost",
+ "about.version": "Version:",
+ "about.number": "Build Number:",
+ "about.date": "Build Date:",
+ "about.hash": "Build Hash:",
+ "about.close": "Close",
+ "access_history.sessionRevoked": "The session with id {sessionId} was revoked",
+ "access_history.channelCreated": "Created the {channelName} channel/group",
+ "access_history.establishedDM": "Established a direct message channel with {username}",
+ "access_history.nameUpdated": "Updated the {channelName} channel/group name",
+ "access_history.headerUpdated": "Updated the {channelName} channel/group header",
+ "access_history.channelDeleted": "Deleted the channel/group with the URL {url}",
+ "access_history.userAdded": "Added {username} to the {channelName} channel/group",
+ "access_history.userRemoved": "Removed {username} to the {channelName} channel/group",
+ "access_history.attemptedRegisterApp": "Attempted to register a new OAuth Application with ID {id}",
+ "access_history.attemptedAllowOAuthAccess": "Attempted to allow a new OAuth service access",
+ "access_history.successfullOAuthAccess": "Successfully gave a new OAuth service access",
+ "access_history.failedOAuthAccess": "Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback",
+ "access_history.attemptedOAuthToken": "Attempted to get an OAuth access token",
+ "access_history.successfullOAuthToken": "Successfully added a new OAuth service",
+ "access_history.oauthTokenFailed": "Failed to get an OAuth access token - {token}",
+ "access_history.attemptedLogin": "Attempted to login",
+ "access_history.successfullLogin": "Successfully logged in",
+ "access_history.failedLogin": "FAILED login attempt",
+ "access_history.updatePicture": "Updated your profile picture",
+ "access_history.updateGeneral": "Updated the general settings of your account",
+ "access_history.attemptedPassword": "Attempted to change password",
+ "access_history.successfullPassword": "Successfully changed password",
+ "access_history.failedPassword": "Failed to change password - tried to update user password who was logged in through oauth",
+ "access_history.updatedRol": "Updated user role(s) to ",
+ "access_history.member": "member",
+ "access_history.accountActive": "Account made active",
+ "access_history.accountInactive": "Account made inactive",
+ "access_history.by": " by {username}",
+ "access_history.byAdmin": " by an admin",
+ "access_history.sentEmail": "Sent an email to {email} to reset your password",
+ "access_history.attemptedReset": "Attempted to reset password",
+ "access_history.successfullReset": "Successfully reset password",
+ "access_history.updateGlobalNotifications": "Updated your global notification settings",
+ "access_history.attemptedWebhookCreate": "Attempted to create a webhook",
+ "access_history.successfullWebhookCreate": "Successfully created a webhook",
+ "access_history.failedWebhookCreate": "Failed to create a webhook - bad channel permissions",
+ "access_history.attemptedWebhookDelete": "Attempted to delete a webhook",
+ "access_history.successfullWebhookDelete": "Successfully deleted a webhook",
+ "access_history.failedWebhookDelete": "Failed to delete a webhook - inappropriate conditions",
+ "access_history.logout": "Logged out of your account",
+ "access_history.verified": "Sucessfully verified your email address",
+ "access_history.revokedAll": "Revoked all current sessions for the team",
+ "access_history.loginAttempt": " (Login attempt)",
+ "access_history.loginFailure": " (Login failure)",
+ "access_history.moreInfo": "More info",
+ "access_history.ip": "IP: {ip}",
+ "access_history.session": "Session ID: {id}",
+ "access_history.title": "Access History",
+ "activity_log_modal.iphoneNativeApp": "iPhone Native App",
+ "activity_log_modal.androidNativeApp": "Android Native App",
+ "activity_log_modal.android": "Android",
+ "activity_log.firstTime": "First time active: {date}, {time}",
+ "activity_log.os": "OS: {os}",
+ "activity_log.browser": "Browser: {browser}",
+ "activity_log.sessionId": "Session ID: {id}",
+ "activity_log.moreInfo": "More info",
+ "activity_log.lastActivity": "Last activity: {date}, {time}",
+ "activity_log.logout": "Logout",
+ "activity_log.activeSessions": "Active Sessions",
+ "activity_log.sessionsDescription": "Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the 'Logout' button below to end a session.",
"admin.nav.switch": "Switch to {display_name}",
"admin.nav.logout": "Logout",
"admin.nav.help": "Help",
@@ -386,6 +455,10 @@
"admin.team.save": "Save",
"admin.userList.title": "Users for {team}",
"admin.userList.title2": "Users for {team} ({count})",
+ "admin.user_item.confirmDemoteRoleTitle": "Confirm demotion from System Admin role",
+ "admin.user_item.confirmDemotion": "Confirm Demotion",
+ "admin.user_item.confirmDemoteDescription": "If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.",
+ "admin.user_item.confirmDemotionCmd": "platform -assign_role -team_name=\"yourteam\" -email=\"name@yourcompany.com\" -role=\"system_admin\"",
"admin.user_item.member": "Member",
"admin.user_item.sysAdmin": "System Admin",
"admin.user_item.teamAdmin": "Team Admin",
@@ -401,6 +474,12 @@
"authorize.access": "Allow <strong>{appName}</strong> access?",
"authorize.deny": "Deny",
"authorize.allow": "Allow",
+ "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",
+ "change_url.noUnderscore": "Can not contain two underscores in a row.",
+ "change_url.invalidUrl": "Invalid URL",
+ "change_url.close": "Close",
"claim.account.noEmail": "No email specified",
"claim.email_to_sso.pwdError": "Please enter your password.",
"claim.email_to_sso.pwd": "Password",
@@ -416,6 +495,7 @@
"claim.sso_to_email.description": "Upon changing your account type, you will only be able to login with your email and password.",
"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",
"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",
@@ -430,6 +510,28 @@
"find_team.getLinks": "Get an email with links to any teams to which you are a member.",
"find_team.email": "Email",
"find_team.send": "Send",
+ "get_link.copy": "Copy Link",
+ "get_link.clipboard": " Link copied to clipboard.",
+ "get_link.close": "Close",
+ "get_team_invite_link_modal.title": "Team Invite Link",
+ "get_team_invite_link_modal.help": "Send teammates the link below for them to sign-up to this team site.",
+ "invite_member.emailError": "Please enter a valid email address",
+ "invite_member.firstname": "First name",
+ "invite_member.lastname": "Last name",
+ "invite_member.modalTitle": "Discard Invitations?",
+ "invite_member.modalMessage": "You have unsent invitations, are you sure you want to discard them?",
+ "invite_member.modalButton": "Yes, Discard",
+ "invite_member.addAnother": "Add another",
+ "invite_member.autoJoin": "People invited automatically join the <strong>{channel}</strong> channel.",
+ "invite_member.send": "Send Invitation",
+ "invite_member.sending": " Sending",
+ "invite_member.send2": "Send Invitations",
+ "invite_member.inviteLink": "Team Invite Link",
+ "invite_member.teamInviteLink": "You can also invite people using the {link}.",
+ "invite_member.content": "Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.",
+ "invite_member.disabled": "User creation has been disabled for your team. Please ask your team administrator for details.",
+ "invite_member.newMember": "Invite New Member",
+ "invite_member.cancel": "Cancel",
"loading_screen.loading": "Loading",
"login_email.badTeam": "Bad team name",
"login_email.emailReq": "An email is required",
@@ -455,6 +557,64 @@
"login.find": "Find your other teams",
"login.signTo": "Sign in to:",
"login.on": "on {siteName}",
+ "member_team_item.member": "Member",
+ "member_team_item.systemAdmin": "System Admin",
+ "member_team_item.teamAdmin": "Team Admin",
+ "member_team_item.inactive": "Inactive",
+ "member_team_item.makeAdmin": "Make Team Admin",
+ "member_team_item.makeMember": "Make Member",
+ "member_team_item.makeActive": "Make Active",
+ "member_team_item.makeInactive": "Make Inactive",
+ "more_channels.join": "Join",
+ "more_channels.noMore": "No more channels to join",
+ "more_channels.createClick": "Click 'Create New Channel' to make a new one",
+ "more_channels.close": "Close",
+ "more_channels.title": "More Channels",
+ "more_channels.create": "Create New Channel",
+ "more_direct_channels.member": "Member",
+ "more_direct_channels.search": "Search members",
+ "more_direct_channels.message": "Message",
+ "more_direct_channels.notFound": "No users found :(",
+ "more_direct_channels.count": "{count} {member}",
+ "more_direct_channels.countTotal": "{count} {member} of {total} Total",
+ "more_direct_channels.title": "Direct Messages",
+ "more_direct_channels.close": "Close",
+ "navbar_dropdown.inviteMember": "Invite New Member",
+ "navbar_dropdown.teamLink": "Get Team Invite Link",
+ "navbar_dropdown.manageMembers": "Manage Members",
+ "navbar_dropdown.teamSettings": "Team Settings",
+ "navbar_dropdown.console": "System Console",
+ "navbar_dropdown.switchTeam": "Switch to {team}",
+ "navbar_dropdown.create": "Create a New Team",
+ "navbar_dropdown.help": "Help",
+ "navbar_dropdown.report": "Report a Problem",
+ "navbar_dropdown.accountSettings": "Account Settings",
+ "navbar_dropdown.logout": "Logout",
+ "navbar_dropdown.about": "About Mattermost",
+ "channel_flow.invalidName": "Invalid Channel Name",
+ "channel_flow.alreadyExist": "A channel with that URL already exists",
+ "channel_flow.channel": "Channel",
+ "channel_flow.group": "Group",
+ "channel_flow.changeUrlTitle": "Change {term} URL",
+ "channel_flow.set_url_title": "Set {term} URL",
+ "channel_flow.create": "Create {term}",
+ "channel_flow.changeUrlDescription": "Some characters are not allowed in URLs and may be removed.",
+ "channel_modal.nameEx": "E.g.: \"Bugs\", \"Marketing\", \"办公室恋情\"",
+ "channel_modal.displayNameError": "This field is required",
+ "channel_modal.group": "Group",
+ "channel_modal.privateGroup1": "Create a new private group with restricted membership. ",
+ "channel_modal.publicChannel1": "Create a public channel",
+ "channel_modal.channel": "Channel",
+ "channel_modal.publicChannel2": "Create a new public channel anyone can join. ",
+ "channel_modal.privateGroup2": "Create a private group",
+ "channel_modal.modalTitle": "New ",
+ "channel_modal.name": "Name",
+ "channel_modal.edit": "Edit",
+ "channel_modal.purpose": "Purpose",
+ "channel_modal.optional": "(optional)",
+ "channel_modal.descriptionHelp": "Describe how this {term} should be used.",
+ "channel_modal.cancel": "Cancel",
+ "channel_modal.createNew": "Create New ",
"password_form.error": "Please enter at least {chars} characters.",
"password_form.update": "Your password has been updated successfully.",
"password_form.pwd": "Password",
@@ -469,6 +629,49 @@
"password_send.title": "Password Reset",
"password_send.description": "To reset your password, enter the email address you used to sign up for {teamName}.",
"password_send.reset": "Reset my password",
+ "register_app.required": "Required",
+ "register_app.optional": "Optional",
+ "register_app.nameError": "Application name must be filled in.",
+ "register_app.homepageError": "Homepage must be filled in.",
+ "register_app.callbackError": "At least one callback URL must be filled in.",
+ "register_app.title": "Register a New Application",
+ "register_app.name": "Application Name",
+ "register_app.homepage": "Homepage URL",
+ "register_app.description": "Description",
+ "register_app.callback": "Callback URL",
+ "register_app.cancel": "Cancel",
+ "register_app.register": "Register",
+ "register_app.credentialsTitle": "Your Application Credentials",
+ "register_app.clientId": "Client ID",
+ "register_app.clientSecret": "Client Secret",
+ "register_app.credentialsDescription": "Save these somewhere SAFE and SECURE. Treat your Client ID as your app's username and your Client Secret as the app's password.",
+ "register_app.credentialsSave": "I have saved both my Client Id and Client Secret somewhere safe",
+ "register_app.close": "Close",
+ "register_app.dev": "Developer Applications",
+ "setting_item_max.save": "Save",
+ "setting_item_max.cancel": "Cancel",
+ "setting_item_min.edit": "Edit",
+ "setting_picture.save": "Save",
+ "setting_picture.help": "Upload a profile picture in either JPG or PNG format, at least {width}px in width and {height}px height.",
+ "setting_picture.select": "Select",
+ "setting_picture.cancel": "Cancel",
+ "setting_upload.noFile": "No file selected.",
+ "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.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>",
+ "sidebar.removeList": "Remove from list",
+ "sidebar.more": "More ({count})",
+ "sidebar.createChannel": "Create new channel",
+ "sidebar.createGroup": "Create new group",
+ "sidebar.unreadAbove": "Unread post(s) above",
+ "sidebar.unreadBelow": "Unread post(s) below",
+ "sidebar.channels": "Channels",
+ "sidebar.moreElips": "More...",
+ "sidebar.pg": "Private Groups",
+ "sidebar.direct": "Direct Messages",
"signup_team_complete.completed": "You've already completed the signup process for this invitation or this invitation has expired.",
"signup_team_confirm.title": "Sign up Complete",
"signup_team_confirm.checkEmail": "Please check your email: <strong>{email}</strong><br />Your email contains a link to set up your team",
@@ -499,6 +702,43 @@
"suggestion.mention.channel": "Notifies everyone in the channel",
"suggestion.search.public": "Public Channels",
"suggestion.search.private": "Public Groups",
+ "team_export_tab.exporting": " Exporting...",
+ "team_export_tab.ready": " Ready for ",
+ "team_export_tab.download": "download",
+ "team_export_tab.unable": " Unable to export: {error}",
+ "team_export_tab.export": "Export",
+ "team_export_tab.exportTeam": "Export your team",
+ "general_tab.dirDisabled": "Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.",
+ "general_tab.required": "This field is required",
+ "general_tab.chooseName": "Please choose a new name for your team",
+ "general_tab.includeDirTitle": "Include this team in the Team Directory",
+ "general_tab.yes": "Yes",
+ "general_tab.no": "No",
+ "general_tab.dirOff": "Team directory is turned off for this system.",
+ "general_tab.openInviteTitle": "Allow anyone to sign-up from login page",
+ "general_tab.codeTitle": "Invite Code",
+ "general_tab.codeDesc": "Click 'Edit' to regenerate Invite Code.",
+ "general_tab.teamNameInfo": "Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.",
+ "general_tab.includeDirDesc": "Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.",
+ "general_tab.dirContact": "Contact your system administrator to turn on the team directory on the system home page.",
+ "general_tab.openInviteDesc": "When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.",
+ "general_tab.regenerate": "Re-Generate",
+ "general_tab.codeLongDesc": "The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.",
+ "general_tab.teamName": "Team Name",
+ "general_tab.title": "General Settings",
+ "team_import_tab.importSlack": "Import from Slack (Beta)",
+ "team_import_tab.importHelp": "<p>To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.</p><p>The Slack import to Mattermost is in 'Beta'. Slack bot posts do not yet import and Slack @mentions are not currently supported.</p>",
+ "team_import_tab.importing": " Importing...",
+ "team_import_tab.successful": " Import successful: ",
+ "team_import_tab.summary": "View Summary",
+ "team_import_tab.failure": " Import failure: ",
+ "team_import_tab.import": "Import",
+ "team_member_modal.members": "{team} Members",
+ "team_member_modal.close": "Close",
+ "team_settings_modal.generalTab": "General",
+ "team_settings_modal.importTab": "Import",
+ "team_settings_modal.exportTab": "Export",
+ "team_settings_modal.title": "Team Settings",
"choose_auth_page.gitlabCreate": "Create new team with GitLab Account",
"choose_auth_page.googleCreate": "Create new team with Google Apps Account",
"choose_auth_page.emailCreate": "Create new team with email address",
@@ -582,5 +822,181 @@
"tutorial_tip.ok": "Okay",
"tutorial_tip.next": "Next",
"tutorial_tip.seen": "Seen this before? ",
- "tutorial_tip.out": "Opt out of these tips."
+ "tutorial_tip.out": "Opt out of these tips.",
+ "user.settings.custom_theme.sidebarBg": "Sidebar BG",
+ "user.settings.custom_theme.sidebarText": "Sidebar Text",
+ "user.settings.custom_theme.sidebarHeaderBg": "Sidebar Header BG",
+ "user.settings.custom_theme.sidebarHeaderTextColor": "Sidebar Header Text",
+ "user.settings.custom_theme.sidebarUnreadText": "Sidebar Unread Text",
+ "user.settings.custom_theme.sidebarTextHoverBg": "Sidebar Text Hover BG",
+ "user.settings.custom_theme.sidebarTextActiveBorder": "Sidebar Text Active Border",
+ "user.settings.custom_theme.sidebarTextActiveColor": "Sidebar Text Active Color",
+ "user.settings.custom_theme.onlineIndicator": "Online Indicator",
+ "user.settings.custom_theme.awayIndicator": "Away Indicator",
+ "user.settings.custom_theme.mentionBj": "Mention Jewel BG",
+ "user.settings.custom_theme.mentionColor": "Mention Jewel Text",
+ "user.settings.custom_theme.centerChannelBg": "Center Channel BG",
+ "user.settings.custom_theme.centerChannelColor": "Center Channel Text",
+ "user.settings.custom_theme.newMessageSeparator": "New Message Separator",
+ "user.settings.custom_theme.linkColor": "Link Color",
+ "user.settings.custom_theme.buttonBg": "Button BG",
+ "user.settings.custom_theme.buttonColor": "Button Text",
+ "user.settings.custom_theme.mentionHighlightBg": "Mention Highlight BG",
+ "user.settings.custom_theme.mentionHighlightLink": "Mention Highlight Link",
+ "user.settings.custom_theme.codeTheme": "Code Theme",
+ "user.settings.custom_theme.copyPaste": "Copy and paste to share theme colors:",
+ "user.settings.import_theme.submitError": "Invalid format, please try copying and pasting in again.",
+ "user.settings.import_theme.importHeader": "Import Slack Theme",
+ "user.settings.import_theme.importBody": "To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:",
+ "user.settings.import_theme.cancel": "Cancel",
+ "user.settings.import_theme.submit": "Submit",
+ "user.settings.hooks_in.channel": "Channel: ",
+ "user.settings.hooks_in.none": "None",
+ "user.settings.hooks_in.existing": "Existing incoming webhooks",
+ "user.settings.hooks_in.description": "Create webhook URLs for use in external integrations. Please see<a href=\"http://mattermost.org/webhooks\" target=\"_blank\">http://mattermost.org/webhooks</a> to learn more.",
+ "user.settings.hooks_in.addTitle": "Add a new incoming webhook",
+ "user.settings.hooks_in.add": "Add",
+ "user.settings.languages.change": "Change interface language",
+ "user.settings.languages": "Set language",
+ "user.settings.hooks_out.optional": "Optional if channel selected",
+ "user.settings.hooks_out.callbackHolder": "Each URL must start with http:// or https://",
+ "user.settings.hooks_out.select": "--- Select a channel ---",
+ "user.settings.hooks_out.channel": "Channel: ",
+ "user.settings.hooks_out.trigger": "Trigger Words: ",
+ "user.settings.hooks_out.regen": "Regen Token",
+ "user.settings.hooks_out.none": "None",
+ "user.settings.hooks_out.existing": "Existing outgoing webhooks",
+ "user.settings.hooks_out.addDescription": "Create webhooks to send new message events to an external integration. Please see <a href=\"http://mattermost.org/webhooks\">http://mattermost.org/webhooks</a> to learn more.",
+ "user.settings.hooks_out.addTitle": "Add a new outgoing webhook",
+ "user.settings.hooks_out.only": "Only public channels can be used",
+ "user.settings.hooks_out.comma": "Comma separated words to trigger on",
+ "user.settings.hooks_out.callback": "Callback URLs: ",
+ "user.settings.hooks_out.callbackDesc": "New line separated URLs that will receive the HTTP POST event",
+ "user.settings.hooks_out.add": "Add",
+ "user.settings.advance.sendTitle": "Send messages on Ctrl + Enter",
+ "user.settings.advance.on": "On",
+ "user.settings.advance.off": "Off",
+ "user.settings.advance.preReleaseTitle": "Preview pre-release features",
+ "user.settings.advance.feature": " Feature ",
+ "user.settings.advance.features": " Features ",
+ "user.settings.advance.enabled": "enabled",
+ "user.settings.advance.markdown_preview": "Show markdown preview option in message input box",
+ "user.settings.advance.embed_preview": "Show preview snippet of links below message",
+ "user.settings.advance.loc_preview": "Show user language in display settings",
+ "user.settings.advance.sendDesc": "If enabled 'Enter' inserts a new line and 'Ctrl + Enter' submits the message.",
+ "user.settings.advance.preReleaseDesc": "Check any pre-released features you'd like to preview. You may also need to refresh the page before the setting will take effect.",
+ "user.settings.advance.title": "Advanced Settings",
+ "user.settings.appearance.themeColors": "Theme Colors",
+ "user.settings.appearance.customTheme": "Custom Theme",
+ "user.settings.appearance.save": "Save",
+ "user.settings.appearance.cancel": "Cancel",
+ "user.settings.appearance.title": "Appearance Settings",
+ "user.settings.appearance.import": "Import theme colors from Slack",
+ "user.settings.developer.applicationsPreview": "Applications (Preview)",
+ "user.settings.developer.thirdParty": "Open to register a new third-party application",
+ "user.settings.developer.register": "Register New Application",
+ "user.settings.developer.title": "Developer Settings",
+ "user.settings.display.normalClock": "12-hour clock (example: 4:00 PM)",
+ "user.settings.display.militaryClock": "24-hour clock (example: 16:00)",
+ "user.settings.display.clockDisplay": "Clock Display",
+ "user.settings.display.teammateDisplay": "Teammate Name Display",
+ "user.settings.display.showNickname": "Show nickname if one exists, otherwise show first and last name",
+ "user.settings.display.showUsername": "Show username (team default)",
+ "user.settings.display.showFullname": "Show first and last name",
+ "user.settings.display.fontTitle": "Display Font",
+ "user.settings.display.language": "Language",
+ "user.settings.display.preferTime": "Select how you prefer time displayed.",
+ "user.settings.display.nameOptsDesc": "Set how to display other user's names in posts and the Direct Messages list.",
+ "user.settings.display.fontDesc": "Select the font displayed in the Mattermost user interface.",
+ "user.settings.display.title": "Display Settings",
+ "user.settings.general.usernameReserved": "This username is reserved, please choose a new one.",
+ "user.settings.general.usernameRestrictions": "'Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'.",
+ "user.settings.general.validEmail": "Please enter a valid email address",
+ "user.settings.general.emailMatch": "The new emails you entered do not match.",
+ "user.settings.general.checkEmail": "Check your email at {email} to verify the address.",
+ "user.settings.general.newAddress": "New Address: {email}<br />Check your email to verify the above address.",
+ "user.settings.general.checkEmailNoAddress": "Check your email to verify your new address",
+ "user.settings.general.loginGitlab": "Log in done through GitLab",
+ "user.settings.general.validImage": "Only JPG or PNG images may be used for profile pictures",
+ "user.settings.general.imageTooLarge": "Unable to upload profile image. File is too large.",
+ "user.settings.general.uploadImage": "Click 'Edit' to upload an image.",
+ "user.settings.general.imageUpdated": "Image last updated {date}",
+ "user.settings.general.fullName": "Full Name",
+ "user.settings.general.nickname": "Nickname",
+ "user.settings.general.username": "Username",
+ "user.settings.general.email": "Email",
+ "user.settings.general.profilePicture": "Profile Picture",
+ "user.settings.general.close": "Close",
+ "user.settings.general.firstName": "First Name",
+ "user.settings.general.lastName": "Last Name",
+ "user.settings.general.notificationsLink": "Notifications",
+ "user.settings.general.notificationsExtra": "By default, you will receive mention notifications when someone types your first name. Go to {notify} settings to change this default.",
+ "user.settings.general.nicknameExtra": "Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.",
+ "user.settings.general.usernameInfo": "Pick something easy for teammates to recognize and recall.",
+ "user.settings.general.emailHelp1": "Email is used for sign-in, notifications, and password reset. Email requires verification if changed.",
+ "user.settings.general.emailHelp2": "Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.",
+ "user.settings.general.emailHelp3": "Email is used for sign-in, notifications, and password reset.",
+ "user.settings.general.emailHelp4": "A verification email was sent to {email}.",
+ "user.settings.general.primaryEmail": "Primary Email",
+ "user.settings.general.confirmEmail": "Confirm Email",
+ "user.settings.general.emailCantUpdate": "Log in occurs through GitLab. Email cannot be updated.",
+ "user.settings.general.title": "General Settings",
+ "user.settings.integrations.incomingWebhooks": "Incoming Webhooks",
+ "user.settings.integrations.incomingWebhooksDescription": "Manage your incoming webhooks",
+ "user.settings.integrations.outWebhooks": "Outgoing Webhooks",
+ "user.settings.integrations.outWebhooksDescription": "Manage your outgoing webhooks",
+ "user.settings.integrations.title": "Integration Settings",
+ "user.settings.modal.general": "General",
+ "user.settings.modal.security": "Security",
+ "user.settings.modal.notifications": "Notifications",
+ "user.settings.modal.appearance": "Appearance",
+ "user.settings.modal.developer": "Developer",
+ "user.settings.modal.integrations": "Integrations",
+ "user.settings.modal.display": "Display",
+ "user.settings.modal.advanced": "Advanced",
+ "user.settings.modal.confirmTitle": "Discard Changes?",
+ "user.settings.modal.confirmMsg": "You have unsaved changes, are you sure you want to discard them?",
+ "user.settings.modal.confirmBtns": "Yes, Discard",
+ "user.settings.modal.title": "Account Settings",
+ "user.settings.notifications.desktop": "Send desktop notifications",
+ "user.settings.notifications.desktopSounds": "Desktop notification sounds",
+ "user.settings.notifications.emailNotifications": "Email notifications",
+ "user.settings.notifications.wordsTrigger": "Words that trigger mentions",
+ "user.settings.notifications.close": "Close",
+ "user.settings.notification.allActivity": "For all activity",
+ "user.settings.notifications.onlyMentions": "Only for mentions and direct messages",
+ "user.settings.notifications.never": "Never",
+ "user.settings.notifications.info": "Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.",
+ "user.settings.notifications.on": "On",
+ "user.settings.notifications.off": "Off",
+ "user.settings.notification.soundConfig": "Please configure notification sounds in your browser settings",
+ "user.settings.notifications.emailInfo": "Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from {siteName} for more than 5 minutes.",
+ "user.settings.notifications.sensitiveName": "Your case sensitive first name \"{first_name}\"",
+ "user.settings.notifications.sensitiveUsername": "Your non-case sensitive username \"{username}\"",
+ "user.settings.notifications.usernameMention": "Your username mentioned \"@{username}\"",
+ "user.settings.notifications.teamWide": "Team-wide mentions \"@all\"",
+ "user.settings.notifications.channelWide": "Channel-wide mentions \"@channel\"",
+ "user.settings.notifications.sensitiveWords": "Other non-case sensitive words, separated by commas:",
+ "user.settings.notifications.noWords": "No words configured",
+ "user.settings.notifications.title": "Notification Settings",
+ "user.settings.notifications.header": "Notifications",
+ "user.settings.security.currentPasswordError": "Please enter your current password",
+ "user.settings.security.passwordLengthError": "New passwords must be at least {chars} characters",
+ "user.settings.security.passwordMatchError": "The new passwords you entered do not match",
+ "user.settings.security.password": "Password",
+ "user.settings.security.lastUpdated": "Last updated {date} at {time}",
+ "user.settings.security.method": "Sign-in Method",
+ "user.settings.security.close": "Close",
+ "user.settings.security.currentPassword": "Current Password",
+ "user.settings.security.newPassword": "New Password",
+ "user.settings.security.retypePassword": "Retype New Password",
+ "user.settings.security.switchEmail": "Switch to using email and password",
+ "user.settings.security.switchGitlab": "Switch to using GitLab SSO",
+ "user.settings.security.switchGoogle": "Switch to using Google SSO",
+ "user.settings.security.oneSignin": "You may only have one sign-in method at a time. Switching sign-in method will send an email notifying you if the change was successful.",
+ "user.settings.security.emailPwd": "Email and Password",
+ "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"
} \ No newline at end of file
diff --git a/web/static/i18n/es.json b/web/static/i18n/es.json
index 48412510d..ee24df1b3 100644
--- a/web/static/i18n/es.json
+++ b/web/static/i18n/es.json
@@ -1,4 +1,73 @@
{
+ "about.close": "Cerrar",
+ "about.date": "Fecha de compilación:",
+ "about.enterpriseEdition": "Edición Enterprise",
+ "about.hash": "Hash de compilación:",
+ "about.licensed": "Licenciado por:",
+ "about.number": "Número de compilación:",
+ "about.teamEdtion": "Edición Team",
+ "about.title": "Acerca de Mattermost",
+ "about.version": "Versión:",
+ "access_history.accountActive": "La cuenta se ha activado",
+ "access_history.accountInactive": "La cuenta se ha desactivado",
+ "access_history.attemptedAllowOAuthAccess": "Intento para permitir acceso a un nuevo servicio de OAuth",
+ "access_history.attemptedLogin": "Intento de inicio de sesión",
+ "access_history.attemptedOAuthToken": "Intento de obtener un token de acceso de OAuth",
+ "access_history.attemptedPassword": "Intento de cambio de contraseña",
+ "access_history.attemptedRegisterApp": "Intento de registrar una nueva aplicación OAuth con el ID {id}",
+ "access_history.attemptedReset": "Intento de reestablecer al contraseña",
+ "access_history.attemptedWebhookCreate": "Intento de creación de un webhook",
+ "access_history.attemptedWebhookDelete": "Intento de eliminación de un webhook",
+ "access_history.by": " por {username}",
+ "access_history.byAdmin": " por un admin",
+ "access_history.channelCreated": "Creado el canal/grupo {channelName}",
+ "access_history.channelDeleted": "Eliminado el canal/grupo con el URL {url}",
+ "access_history.establishedDM": "Establecido un canal de mensajes directos con {username}",
+ "access_history.failedLogin": "Intento de inicio de sesión FALLIDO",
+ "access_history.failedOAuthAccess": "Fallo al permitir acceso a un nuevo servicio de OAuth - la URI de redirección no concuerda con la previamente registrada",
+ "access_history.failedPassword": "Fallo al cambiar la contraseña - se trató de actualizar la contraseña de un usuario que ingresó por medio de un servicio de OAuth",
+ "access_history.failedWebhookCreate": "Falló la creación del webhook - permisos inadecuados del canal",
+ "access_history.failedWebhookDelete": "Falló la eliminación del webhook - condiciones inapropiadas",
+ "access_history.headerUpdated": "Actualizado el cancabezado del canal/grupo {channelName}",
+ "access_history.ip": "IP: {ip}",
+ "access_history.loginAttempt": " (intento de inicio de sesión)",
+ "access_history.loginFailure": " (Fallo de inicio de sesión)",
+ "access_history.logout": "Cerrada la sesión de tu cuenta",
+ "access_history.member": "miembro",
+ "access_history.moreInfo": "Más información",
+ "access_history.nameUpdated": "Actualizado el nombre del canal/grupo {channelName}",
+ "access_history.oauthTokenFailed": "Fallo al obtener un token de acceso de OAuth - {token}",
+ "access_history.revokedAll": "Revocadas todas las sesiones actuales para el equipo",
+ "access_history.sentEmail": "Enviado un correo electrónico a {email} para restablecer tu contraseña",
+ "access_history.session": "Sesión ID: {id}",
+ "access_history.sessionRevoked": "La sesión con el id {sessionId} fue revocada",
+ "access_history.successfullLogin": "Inicio de sesión con éxito",
+ "access_history.successfullOAuthAccess": "Se asignó un nuevo servicio de OAuth con éxito",
+ "access_history.successfullOAuthToken": "Se agregó un nuevo servicio de OAuth con éxito",
+ "access_history.successfullPassword": "La contraseña se cambió satisfactoriamente",
+ "access_history.successfullReset": "La contraseña fue reestablecida con éxito",
+ "access_history.successfullWebhookCreate": "Creación del webhook con éxito",
+ "access_history.successfullWebhookDelete": "Eliminación del webhook con éxito",
+ "access_history.title": "Historial de Acceso",
+ "access_history.updateGeneral": "Actualizada la configuración general de tu cuenta",
+ "access_history.updateGlobalNotifications": "Actualizada la configuración global de tus notificaciones",
+ "access_history.updatePicture": "Actualizada tu imagén de perfil",
+ "access_history.updatedRol": "Actualizado rol(es) de usuario a ",
+ "access_history.userAdded": "Agregado {username} al canal/grupo {channelName}",
+ "access_history.userRemoved": "Removido {username} del canal/grupo {channelName}",
+ "access_history.verified": "Se verificó tu dirección de correo electrónico con éxito",
+ "activity_log.activeSessions": "Sesiones Activas",
+ "activity_log.browser": "Navegador: {browser}",
+ "activity_log.firstTime": "Primera actividad: {date}, {time}",
+ "activity_log.lastActivity": "Última actividad: {date}, {time}",
+ "activity_log.logout": "Cerrar sesión",
+ "activity_log.moreInfo": "Mas información",
+ "activity_log.os": "Sistema Operativo: {os}",
+ "activity_log.sessionId": "Sesión ID: {id}",
+ "activity_log.sessionsDescription": "Las sesiones son creadas cuando inicias sesión con tus credenciales en un nuevo navegador desde cualquier dispositivo. Las sesiones te permiten utilizar Mattermost por un período de hasta 30 días sin tener que iniciar sesión nuevamente. Si quieres cerrar tu sesión antes de ese tiempo, utiliza el botón de 'Cerrar Sesión' en la parte de abajo.",
+ "activity_log_modal.android": "Android",
+ "activity_log_modal.androidNativeApp": "Android App Nativa",
+ "activity_log_modal.iphoneNativeApp": "iPhone App Nativa",
"admin.analytics.activeUsers": "Usuarios Activos con Mensajes",
"admin.analytics.loading": "Cargando...",
"admin.analytics.meaningful": "No hay suficiente data para tener una representación significativa.",
@@ -386,13 +455,17 @@
"admin.team_analytics.totalPosts": "Total de Mensajes",
"admin.userList.title": "Usuarios para ",
"admin.userList.title2": "Usuarios para {team} ({count})",
+ "admin.user_item.confirmDemoteDescription": "Si te degradas a ti mismo de la función de Administrador de Sistema y no hay otro usuario con privilegios de Administrador de Sistema, tendrás que volver a asignar un Administrador del Sistema accediendo al servidor de Mattermost a través de un terminal y ejecutar el siguiente comando.",
+ "admin.user_item.confirmDemoteRoleTitle": "Confirmar el decenso del rol de Administrador de Sistema",
+ "admin.user_item.confirmDemotion": "Confirmar decenso",
+ "admin.user_item.confirmDemotionCmd": "platform -assign_role -team_name=\"tuequipo\" -email=\"nombre@tuempresa.com\" -role=\"system_admin\"",
"admin.user_item.inactive": "Inactivo",
"admin.user_item.makeActive": "Activar",
"admin.user_item.makeInactive": "Inactivar",
"admin.user_item.makeMember": "Hacer Miembro",
"admin.user_item.makeSysAdmin": "Hacer Admin del Sistema",
"admin.user_item.makeTeamAdmin": "Hacer Admin de Equipo",
- "admin.user_item.member": "Meiembro",
+ "admin.user_item.member": "Miembro",
"admin.user_item.resetPwd": "Reiniciar Contraseña",
"admin.user_item.sysAdmin": "Admin de Sistema",
"admin.user_item.teamAdmin": "Admin de Equipo",
@@ -401,6 +474,36 @@
"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}",
+ "change_url.close": "Cerrar",
+ "change_url.endWithLetter": "Debe terminar con una letra o número",
+ "change_url.invalidUrl": "URL Inválida",
+ "change_url.longer": "Debe ser mayor a 2 caracteres",
+ "change_url.noUnderscore": "No puede tener dos guíones bajos seguidos",
+ "change_url.startWithLetter": "Debe comenzar con una letra o número",
+ "channel_flow.alreadyExist": "Un canal con este identificador ya existe",
+ "channel_flow.changeUrlDescription": "Algunos caracteres no están permitidos en las URLs y pueden ser removidos.",
+ "channel_flow.changeUrlTitle": "Cambiar URL de {term}",
+ "channel_flow.channel": "Canal",
+ "channel_flow.create": "Crear {term}",
+ "channel_flow.group": "Grupo",
+ "channel_flow.invalidName": "Nombre de Canal Inválido",
+ "channel_flow.set_url_title": "Asignar URL de {term}",
+ "channel_modal.cancel": "Cancelar",
+ "channel_modal.channel": "Canal",
+ "channel_modal.createNew": "Crear Nuevo ",
+ "channel_modal.descriptionHelp": "Describe como este {term} debería ser utilizado.",
+ "channel_modal.displayNameError": "Este campo es obligatorio",
+ "channel_modal.edit": "Editar",
+ "channel_modal.group": "Grupo",
+ "channel_modal.modalTitle": "Nuevo ",
+ "channel_modal.name": "Nombre",
+ "channel_modal.nameEx": "Ej: \"Errores\", \"Mercadeo\", \"办公室恋情\"",
+ "channel_modal.optional": "(opcional)",
+ "channel_modal.privateGroup1": "Crear un grupo privado con acceso restringido. ",
+ "channel_modal.privateGroup2": "Crear un grupo privado",
+ "channel_modal.publicChannel1": "Crear un canal público",
+ "channel_modal.publicChannel2": "Crear un canal público al que cualquiera puede unirse. ",
+ "channel_modal.purpose": "Propósito",
"choose_auth_page.emailCreate": "Crea un nuevo equipo con tu cuenta de correo",
"choose_auth_page.find": "Encontrar mi equipo",
"choose_auth_page.gitlabCreate": "Crear un nuevo equipo con una cuenta de GitLab",
@@ -421,6 +524,7 @@
"claim.sso_to_email.switchTo": "Cambiar {type} a correo electrónico y contraseña",
"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",
"email_signup.address": "Correo electrónico",
"email_signup.createTeam": "Crear Equipo",
"email_signup.emailError": "Por favor ingresa una dirección de correos válida",
@@ -439,6 +543,47 @@
"find_team.placeholder": "tu@ejemplo.com",
"find_team.send": "Enviar",
"find_team.submitError": "Por favor ingresa una dirección válida",
+ "general_tab.chooseName": "Por favor escoge otro nombre para tu equipo",
+ "general_tab.codeDesc": "Pincha 'Editar' para regenerar el código de Invitación.",
+ "general_tab.codeLongDesc": "El Código de Invitación es utilizado como parte del URL del enlace creado por la opción **Obtener Enlace de invitación** en el menú principal. Regenerar este código crea un nuevo enlace e invalida los enlaces anteriores.",
+ "general_tab.codeTitle": "Código de Invitación",
+ "general_tab.dirContact": "Contácta a un administrador del sistema para habilitar el directorio de equipos en la página principal.",
+ "general_tab.dirDisabled": "El directorio de Equipos ha sido deshabilitado. Por favor solicita a un Administrador de Sistema que habilite la opción de Directorio de Equipos en la Consola del Sistema.",
+ "general_tab.dirOff": "El directorio de Equipos ha sido deshabilitado para este sistema.",
+ "general_tab.includeDirDesc": "Incluir este equipo mostrará el nombre del equipo en la sección de Directorio de Equipos en la página de inicio, y proveerá un enlace para la página de inicio de sesión.",
+ "general_tab.includeDirTitle": "Incluir este Equipo en el Directorio de Equipos",
+ "general_tab.no": "No",
+ "general_tab.openInviteDesc": "Cuando está permitido, un enlace para la creación de cuentas será incluido en la página de registro de este equipo y permitirá a cualquier visitante registrarse.",
+ "general_tab.openInviteTitle": "Permitir a cualquiera a inscribirse desde la página de inicio de sesión",
+ "general_tab.regenerate": "Regenerar",
+ "general_tab.required": "Este campo es obligatorio",
+ "general_tab.teamName": "Nombre del Equipo",
+ "general_tab.teamNameInfo": "Asigna el nombre del equipo como aparecerá en la página de inicio de sesión y en la parte superior izquierda de la barra lateral.",
+ "general_tab.title": "Configuración General",
+ "general_tab.yes": "Sí",
+ "get_link.clipboard": " Enlace copiado al portapapeles.",
+ "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.title": "Enlace de Invitación al Equipo",
+ "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",
+ "invite_member.content": "El envio de correos está actualmente desactivado para tu equipo, por lo que no puedes enviar invitaciones.",
+ "invite_member.disabled": "La creación de usuarios ha sido deshabilitada para tu equipo. Por favor consulta con un administrador de tu equipo.",
+ "invite_member.emailError": "Por favor ingresa un correo electrónico válido",
+ "invite_member.firstname": "Nombre",
+ "invite_member.inviteLink": "Enlace de Invitación al Equipo",
+ "invite_member.lastname": "Apellido",
+ "invite_member.modalButton": "Sí, Borrar",
+ "invite_member.modalMessage": "Tienes invitaciones sin usar, estás seguro que quieres borrarlas?",
+ "invite_member.modalTitle": "¿Descartar Invitaciones?",
+ "invite_member.newMember": "Invitar nuevo Miembro",
+ "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",
"login.create": "Crea una ahora",
@@ -464,6 +609,40 @@
"login_ldap.pwdReq": "La contraseña LDAP es obligatoria",
"login_ldap.signin": "Entrar",
"login_ldap.username": "Usuario LDAP",
+ "member_team_item.inactive": "Inactivo",
+ "member_team_item.makeActive": "Activar",
+ "member_team_item.makeAdmin": "Convertir a Admin de Equipo",
+ "member_team_item.makeInactive": "Desactivar",
+ "member_team_item.makeMember": "Convertir en Miembro",
+ "member_team_item.member": "Miembro",
+ "member_team_item.systemAdmin": "Administrador de Sistema",
+ "member_team_item.teamAdmin": "Admin de Equipo",
+ "more_channels.close": "Cerrar",
+ "more_channels.create": "Crear Nuevo Canal",
+ "more_channels.createClick": "Pincha 'Crear Nuevo Canal' para crear uno nuevo",
+ "more_channels.join": "Unirme",
+ "more_channels.noMore": "No hay más canales para unirse",
+ "more_channels.title": "Más Canales",
+ "more_direct_channels.close": "Cerrar",
+ "more_direct_channels.count": "{count} {member}",
+ "more_direct_channels.countTotal": "{count} {member} de {total} Total",
+ "more_direct_channels.member": "Miembro",
+ "more_direct_channels.message": "Mensaje",
+ "more_direct_channels.notFound": "No se encontraron usuarios :(",
+ "more_direct_channels.search": "Buscar miembros",
+ "more_direct_channels.title": "Mensajes Directos",
+ "navbar_dropdown.about": "Acerca de Mattermost",
+ "navbar_dropdown.accountSettings": "Configurar Cuenta",
+ "navbar_dropdown.console": "Consola de Sistema",
+ "navbar_dropdown.create": "Crear nuevo Equipo",
+ "navbar_dropdown.help": "Ayuda",
+ "navbar_dropdown.inviteMember": "Invitar Nuevo Miembro",
+ "navbar_dropdown.logout": "Cerrar sesión",
+ "navbar_dropdown.manageMembers": "Administrar Miembros",
+ "navbar_dropdown.report": "Reportar un Problema",
+ "navbar_dropdown.switchTeam": "Cambiar a {team}",
+ "navbar_dropdown.teamLink": "Enlace invitación al equipo",
+ "navbar_dropdown.teamSettings": "Configurar Equipo",
"password_form.change": "Cambiar mi contraseña",
"password_form.click": " Pincha <a href={url}>aquí</a> para iniciar sesión.",
"password_form.enter": "Ingresa una nueva contraseña para tu cuenta en {teamDisplayName} {SiteName}.",
@@ -478,6 +657,49 @@
"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",
+ "register_app.callback": "Callback URL",
+ "register_app.callbackError": "Al menos un callback URL debe ser ingresado.",
+ "register_app.cancel": "Cancelar",
+ "register_app.clientId": "ID del Cliente",
+ "register_app.clientSecret": "Clave secreta del Cliente",
+ "register_app.close": "Cerrar",
+ "register_app.credentialsDescription": "Guarde estos datos en un lugar SEGURO. Siempre podemos obtener el ID si lo pierdes, pero el SECRET se perderá por siempre si lo llegas a perder.",
+ "register_app.credentialsSave": "Ya guarde tanto mi ID como el SECRET en un lugar seguro",
+ "register_app.credentialsTitle": "Las Credenciales de tu Aplicación",
+ "register_app.description": "Descripción",
+ "register_app.dev": "Aplicaciones de Desarrollador",
+ "register_app.homepage": "URL de tu página",
+ "register_app.homepageError": "Se debe ingresar la Página de Inicio.",
+ "register_app.name": "Nombre de la Aplicación",
+ "register_app.nameError": "Se debe ingresar el Nombre de la Aplicación.",
+ "register_app.optional": "Opcional",
+ "register_app.register": "Registrar",
+ "register_app.required": "Requerido",
+ "register_app.title": "Registra una Nueva Aplicación",
+ "setting_item_max.cancel": "Cancelar",
+ "setting_item_max.save": "Guardar",
+ "setting_item_min.edit": "Editar",
+ "setting_picture.cancel": "Cancelar",
+ "setting_picture.help": "Sube una imagen de tu perfil en formato JPG o PNG de al menos {width}px de ancho y {height}px de alto.",
+ "setting_picture.save": "Guardar",
+ "setting_picture.select": "Selecciona",
+ "setting_upload.import": "Importar",
+ "setting_upload.noFile": "No ha seleccionado un archivo",
+ "setting_upload.select": "Selecciona un archivo",
+ "sidebar.channels": "Canales",
+ "sidebar.createChannel": "Crear un nuevo canal",
+ "sidebar.createGroup": "Crear un nuevo grupo",
+ "sidebar.direct": "Mensajes Directos",
+ "sidebar.more": "Más ({count})",
+ "sidebar.moreElips": "Más...",
+ "sidebar.pg": "Grupos Privados",
+ "sidebar.removeList": "Remover de la lista",
+ "sidebar.tutorialScreen1": "<h4>Canales</h4><p><strong>Canales</strong> organizan las conversaciones en diferentes tópicos. Son abiertos para cualquier persona de tu equipo. Para enviar comunicaciones privadas con una sola persona utiliza <strong>Mensajes Directos</strong> o con multiples personas utilizando <strong>Grupos Privados</strong>.</p>",
+ "sidebar.tutorialScreen2": "<h4>Canal \"General\"</h4><p>Este es un canal para comenzar:</p><p><strong>General</strong> es el lugar para tener comunicación con todo el equipo. Todos los integrantes de tu equipo son miembros de este canal.</p>",
+ "sidebar.tutorialScreen3": "<h4>Creando y Uniendose a Canales</h4><p>Pincha en <strong>\"Más...\"</strong> para crear un nuevo canal o unirte a uno existente.</p><p>También puedes crear un nuevo canal o grupo privado al pinchar el simbolo de <strong>\"+\"</strong> que se encuentra al lado del encabezado de Canales o Grupos Privados.</p>",
+ "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>",
"signup_team.choose": "Selecciona un Equipo",
"signup_team.createTeam": "O Crea un Equipo",
"signup_team.disabled": "La creación de Equipos ha sido deshabilitada.",
@@ -514,6 +736,25 @@
"suggestion.mention.channel": "Notifica a todas las personas en el canal",
"suggestion.search.private": "Grupos Privados",
"suggestion.search.public": "Canales Públicos",
+ "team_export_tab.download": "descargar",
+ "team_export_tab.export": "Exportar",
+ "team_export_tab.exportTeam": "Exportar tu equipo",
+ "team_export_tab.exporting": " Exportando...",
+ "team_export_tab.ready": " Listo para ",
+ "team_export_tab.unable": " No se pudo exportar: {error}",
+ "team_import_tab.failure": " Fallo al importar: ",
+ "team_import_tab.import": "Importar",
+ "team_import_tab.importHelp": "<p>Para importar un equipo desde Slack dirigete a Slack > Team Settings > Import/Export Data > Export > Start Export. Slack no permite exportar archivos, imágenes, grupos privados o mensajes directos almacenados en Slack. Por ende, La importación desde Slack hacia Mattermost sólo soporta la importación de los mensajes de texto de los canales públicos de tu equipo.</p><p>La importación desde Slack hacia Mattermost está en fase \"Beta\". Los mensajes enviados por Bots en y las @menciones actualmente no son soportados.</p>",
+ "team_import_tab.importSlack": "Importar desde Slack (Beta)",
+ "team_import_tab.importing": " Importando...",
+ "team_import_tab.successful": " Importado con éxito: ",
+ "team_import_tab.summary": "Ver Resumen",
+ "team_member_modal.close": "Cerrar",
+ "team_member_modal.members": "{team} Miembros",
+ "team_settings_modal.exportTab": "Exportar",
+ "team_settings_modal.generalTab": "General",
+ "team_settings_modal.importTab": "Importar",
+ "team_settings_modal.title": "Configuración del Equipo",
"team_signup_display_name.back": "Volver al paso previo",
"team_signup_display_name.charLength": "El nombre debe tener 4 o más caracteres hasta un máximo de 15",
"team_signup_display_name.nameHelp": "Nombre tu equipo en cualquier idioma. El nombre de tu equipo aparecerá en menús y en inicios",
@@ -582,5 +823,181 @@
"tutorial_tip.next": "Siguiente",
"tutorial_tip.ok": "Aceptar",
"tutorial_tip.out": "No optar por estos consejos.",
- "tutorial_tip.seen": "¿Haz visto esto antes? "
+ "tutorial_tip.seen": "¿Haz visto esto antes? ",
+ "user.settings.advance.embed_preview": "Mostrar la previsualización de enlaces",
+ "user.settings.advance.enabled": "habilitada(s)",
+ "user.settings.advance.feature": " Característica ",
+ "user.settings.advance.features": " Características ",
+ "user.settings.advance.loc_preview": "Mostrar el idioma del usuario en la configuración de visualización",
+ "user.settings.advance.markdown_preview": "Mostrar la previsualización de mensajes escritos con markdown",
+ "user.settings.advance.off": "Encendido",
+ "user.settings.advance.on": "Apagado",
+ "user.settings.advance.preReleaseDesc": "Marca las características de pre-lanzamiento que quieras previsualizar. Es posible que necesites refrescar la página antes de que los cambios se vean reflejados.",
+ "user.settings.advance.preReleaseTitle": "Previsualizar características de pre-lanzamiento",
+ "user.settings.advance.sendDesc": "Si está habilitado 'Retorno' inserta una nueva linea y 'Ctrl + Retorno' envía el mensaje.",
+ "user.settings.advance.sendTitle": "Enviar mensajes con Ctrl + Retorno",
+ "user.settings.advance.title": "Configuración Avanzada",
+ "user.settings.appearance.cancel": "Cancelar",
+ "user.settings.appearance.customTheme": "Tema personalizado",
+ "user.settings.appearance.import": "Importar los colores de tema de Slack",
+ "user.settings.appearance.save": "Guardar",
+ "user.settings.appearance.themeColors": "Selecciona un Tema",
+ "user.settings.appearance.title": "Configuraciones de Apariencia",
+ "user.settings.custom_theme.awayIndicator": "Indicador Ausente",
+ "user.settings.custom_theme.buttonBg": "Fondo Botón",
+ "user.settings.custom_theme.buttonColor": "Texto Botón",
+ "user.settings.custom_theme.centerChannelBg": "Fondo Centro Canal",
+ "user.settings.custom_theme.centerChannelColor": "Texto Centro Canal",
+ "user.settings.custom_theme.codeTheme": "Tema para Código",
+ "user.settings.custom_theme.copyPaste": "Copia y pega para compartir los colores del tema:",
+ "user.settings.custom_theme.linkColor": "Color Enclaces",
+ "user.settings.custom_theme.mentionBj": "Fondo Joya Mención",
+ "user.settings.custom_theme.mentionColor": "Texto Joya Mención",
+ "user.settings.custom_theme.mentionHighlightBg": "Fondo resaltado Mención",
+ "user.settings.custom_theme.mentionHighlightLink": "Enlace resaltado Mención",
+ "user.settings.custom_theme.newMessageSeparator": "Sep. Nuevos Mensajes",
+ "user.settings.custom_theme.onlineIndicator": "Inicador En Linea",
+ "user.settings.custom_theme.sidebarBg": "Fondo Barra lateral",
+ "user.settings.custom_theme.sidebarHeaderBg": "Fondo Encab. Barra lateral",
+ "user.settings.custom_theme.sidebarHeaderTextColor": "Texto Encabezado Barra lateral",
+ "user.settings.custom_theme.sidebarText": "Texto Barra lateral",
+ "user.settings.custom_theme.sidebarTextActiveBorder": "Borde Activo Barra lateral",
+ "user.settings.custom_theme.sidebarTextActiveColor": "Color Activo Barra lateral",
+ "user.settings.custom_theme.sidebarTextHoverBg": "Fondo Texto pasada Barra lateral",
+ "user.settings.custom_theme.sidebarUnreadText": "Texto No Leidos Barra Lateral",
+ "user.settings.developer.applicationsPreview": "Applicaciones (Vista previa)",
+ "user.settings.developer.register": "Registrar una Nueva Aplicación",
+ "user.settings.developer.thirdParty": "Abrir para registrar una nueva aplicación externa",
+ "user.settings.developer.title": "Configuraciones de Desarrollo",
+ "user.settings.display.clockDisplay": "Visualización del Reloj",
+ "user.settings.display.fontDesc": "Selecciona la fuente con la que quieres ver la interfaz de Mattermost.",
+ "user.settings.display.fontTitle": "Fuente de Visualización",
+ "user.settings.display.language": "Idioma",
+ "user.settings.display.militaryClock": "Reloj de 24 horas (ejemplo: 16:00)",
+ "user.settings.display.nameOptsDesc": "Asigna como mostrar los nombres de los otros usuarios en los mensajes y en la lista de Mensajes Directos.",
+ "user.settings.display.normalClock": "Reloj de 12 horas (ejemplo: 4:00 pm)",
+ "user.settings.display.preferTime": "Selecciona como prefieres mostrar la hora.",
+ "user.settings.display.showFullname": "Mostrar nombre y apellido",
+ "user.settings.display.showNickname": "Mostrar el sobrenombre si existe, de lo contrario mostrar el nombre y apellido",
+ "user.settings.display.showUsername": "Mostrar el nombre de usuario (predeterminado)",
+ "user.settings.display.teammateDisplay": "Visualización del nombre de los integrantes",
+ "user.settings.display.title": "Configuración de Visualización",
+ "user.settings.general.checkEmail": "Revisa tu correo electrónico {email} para verificar la dirección.",
+ "user.settings.general.checkEmailNoAddress": "Revisa tu correo electrónico para verificar la dirección",
+ "user.settings.general.close": "Cerrar",
+ "user.settings.general.confirmEmail": "Confirmar Correo electrónico",
+ "user.settings.general.email": "Correo electrónico",
+ "user.settings.general.emailCantUpdate": "El inicio de sesión ocurre a través de GitLab. El correo electrónico no puede ser cambiado.",
+ "user.settings.general.emailHelp1": "El correo electrónico es utilizado para iniciar sesión, recibir notificaciones y para restablecer la contraseña. Si se cambia el correo electrónico deberás verificarlo nuevamente.",
+ "user.settings.general.emailHelp2": "El correo ha sido deshabilitado por el administrador de sistemas. No llegarán correos de notificación hasta que se vuelva a habilitar.",
+ "user.settings.general.emailHelp3": "El correo electrónico es utilizado para iniciar sesión, recibir notificaciones y para restablecer la contraseña.",
+ "user.settings.general.emailHelp4": "Un correo de verificación ha sido enviado a {email}.",
+ "user.settings.general.emailMatch": "El nuevo correo electrónico introducido no coincide.",
+ "user.settings.general.firstName": "Nombre",
+ "user.settings.general.fullName": "Nombre completo",
+ "user.settings.general.imageTooLarge": "No se puede subir la imagen del perfil. El archivo es muy grande.",
+ "user.settings.general.imageUpdated": "Última actualizacón de la imagen {date}",
+ "user.settings.general.lastName": "Apellido",
+ "user.settings.general.loginGitlab": "Inicio de sesión realizado a través de GitLab",
+ "user.settings.general.newAddress": "Nueva dirección: {email}<br />Revisa tu correo electrónico para verificar tu nueva dirección.",
+ "user.settings.general.nickname": "Sobrenombre",
+ "user.settings.general.nicknameExtra": "Utiliza un Sobrenombre por el cual te conocen que sea diferente de tu nombre y del nombre de tu usuario. Esto se utiliza con mayor frecuencia cuando dos o más personas tienen nombres y nombres de usuario que suenan similares.",
+ "user.settings.general.notificationsExtra": "De forma predeterminada, recibirás notificaciones cuando alguien escribe tu nombre. Ve a la configuración de {notify} para cambiar este comportamiento.",
+ "user.settings.general.notificationsLink": "Notificaciones",
+ "user.settings.general.primaryEmail": "Correo Principal",
+ "user.settings.general.profilePicture": "Foto del Perfil",
+ "user.settings.general.title": "Configuración General",
+ "user.settings.general.uploadImage": "Pinchar 'Editar' para subir una imagen.",
+ "user.settings.general.username": "Nombre de usuario",
+ "user.settings.general.usernameInfo": "Escoje algo fácil para que tus compañeros de equipo puedan reconocerte y recordar.",
+ "user.settings.general.usernameReserved": "Este nombre de usuario está reservado, por favor escoge otro",
+ "user.settings.general.usernameRestrictions": "El nombre de usuario debe empezar con una letra, y contener entre {min} a {max} caracteres en minúscula con números, lettras, y los símbolos '.', '-' y '_'.",
+ "user.settings.general.validEmail": "Por favor ingresa una dirección de correo electrónico válida",
+ "user.settings.general.validImage": "Sólo pueden ser utilizadas imágenes JPG o PNG en el perfil",
+ "user.settings.hooks_in.add": "Agregar",
+ "user.settings.hooks_in.addTitle": "Agregar un nuevo webhook de entrada",
+ "user.settings.hooks_in.channel": "Canal: ",
+ "user.settings.hooks_in.description": "Crea webhook URLs para ser utilizados por integraciones externas. Por favor revisa <a href=\"http://mattermost.org/webhooks\">http://mattermost.org/webhooks</a> para conocer más.",
+ "user.settings.hooks_in.existing": "Webhooks de entrada existentes",
+ "user.settings.hooks_in.none": "Ninguno",
+ "user.settings.hooks_out.add": "Agregar",
+ "user.settings.hooks_out.addDescription": "Crea webhooks para enviar los nuevos mensajes a una integración externa. Por favor revisa <a href=\"http://mattermost.org/webhooks\">http://mattermost.org/webhooks</a> para conocer más.",
+ "user.settings.hooks_out.addTitle": "Agregar un nuevo webhook de salida",
+ "user.settings.hooks_out.callback": "Callback URLs:",
+ "user.settings.hooks_out.callbackDesc": "Separa por una nueva linea cada URL donde quieres recibir el evento de HTTP POST",
+ "user.settings.hooks_out.callbackHolder": "Cada URL debe comenzar con http:// o https://",
+ "user.settings.hooks_out.channel": "Canal: ",
+ "user.settings.hooks_out.comma": "Escribe las palabras de activación que ejecutan el evento separadas por coma",
+ "user.settings.hooks_out.existing": "Webhooks de salida existentes",
+ "user.settings.hooks_out.none": "Ninguno",
+ "user.settings.hooks_out.only": "Sólo se pueden utilizar Canales",
+ "user.settings.hooks_out.optional": "Opcional si se selecciona un canal",
+ "user.settings.hooks_out.regen": "Regenerar Token",
+ "user.settings.hooks_out.select": "--- Selecciona un canal ---",
+ "user.settings.hooks_out.trigger": "Palabras de activación: ",
+ "user.settings.import_theme.cancel": "Cancelar",
+ "user.settings.import_theme.importBody": "Para importar un tema, anda al equipo Slack y busca en [Preferences -> Sidebar Theme]. Abre las opciones del tema, copia los valores de color del tema y pégalo aquí:",
+ "user.settings.import_theme.importHeader": "Importar Tema de Slack",
+ "user.settings.import_theme.submit": "Enviar",
+ "user.settings.import_theme.submitError": "Formato inválido, por favor intenta copiando y pegando nuevamente.",
+ "user.settings.integrations.incomingWebhooks": "Webhooks de entrada",
+ "user.settings.integrations.incomingWebhooksDescription": "Administra tus webhooks de entrada",
+ "user.settings.integrations.outWebhooks": "Webhooks de salida",
+ "user.settings.integrations.outWebhooksDescription": "Administra tus webhooks de salida",
+ "user.settings.integrations.title": "Configuraciones de Integración",
+ "user.settings.languages": "Cambiar Idioma",
+ "user.settings.languages.change": "Cambia el idioma con el que se muestra la intefaz de usuario",
+ "user.settings.modal.advanced": "Avanzada",
+ "user.settings.modal.appearance": "Apariencia",
+ "user.settings.modal.confirmBtns": "Sí, Descartar",
+ "user.settings.modal.confirmMsg": "Tienes cambios sin guardar, ¿Estás seguro que los quieres descartar?",
+ "user.settings.modal.confirmTitle": "¿Descartar Cambios?",
+ "user.settings.modal.developer": "Desarrollo",
+ "user.settings.modal.display": "Visualización",
+ "user.settings.modal.general": "General",
+ "user.settings.modal.integrations": "Integraciones",
+ "user.settings.modal.notifications": "Notificaciones",
+ "user.settings.modal.security": "Seguridad",
+ "user.settings.modal.title": "Configuración de la Cuenta",
+ "user.settings.notification.allActivity": "Para toda actividad",
+ "user.settings.notification.soundConfig": "Por favor configura los sonidos de notificación en las configuraciones de tu navegador",
+ "user.settings.notifications.channelWide": "Menciones a todo el canal \"@channel\"",
+ "user.settings.notifications.close": "Cerrar",
+ "user.settings.notifications.desktop": "Enviar notifiaciones de escritorio",
+ "user.settings.notifications.desktopSounds": "Sonidos de notificación de escritorio",
+ "user.settings.notifications.emailInfo": "Las notificaciones por correo son enviadas para menciones y mensajes directos después de que estés fuera de línea por más de 60 segundos o sin participar en {siteName} por más de 5 minutos.",
+ "user.settings.notifications.emailNotifications": "Notificaciones de correo",
+ "user.settings.notifications.header": "Notificaciones",
+ "user.settings.notifications.info": "Las notificaciones de escritorio están disponibles en Firefox, Safari, Chrome, Internet Explorer, y Edge.",
+ "user.settings.notifications.never": "Nunca",
+ "user.settings.notifications.noWords": "No hay palabras configuradas",
+ "user.settings.notifications.off": "Apagado",
+ "user.settings.notifications.on": "Encendido",
+ "user.settings.notifications.onlyMentions": "Sólo para menciones y mensajes directos",
+ "user.settings.notifications.sensitiveName": "Tu nombre con distinción de mayúsculas \"{first_name}\"",
+ "user.settings.notifications.sensitiveUsername": "Tu nombre de usuario sin distinción de mayúsculas \"{username}\"",
+ "user.settings.notifications.sensitiveWords": "Otras palabras sin distinción de mayúsculas, separadas por comas:",
+ "user.settings.notifications.teamWide": "Menciones para todo el equipo \"@all\"",
+ "user.settings.notifications.title": "Configuracón de Notificaciones",
+ "user.settings.notifications.usernameMention": "Tu nombre de usuario mencionado \"@{username}\"",
+ "user.settings.notifications.wordsTrigger": "Palabras que gatillan menciones",
+ "user.settings.security.close": "Cerrar",
+ "user.settings.security.currentPassword": "Contraseña Actual",
+ "user.settings.security.currentPasswordError": "Por favor ingresa tu contraseña actual",
+ "user.settings.security.emailPwd": "Correo electrónico y Contraseña",
+ "user.settings.security.gitlab": "GitLab SSO",
+ "user.settings.security.lastUpdated": "Última actualización {date} a las {time}",
+ "user.settings.security.logoutActiveSessions": "Visualizar y cerrar las sesiones activas",
+ "user.settings.security.method": "Método de inicio de sesión",
+ "user.settings.security.newPassword": "Nueva Contraseña",
+ "user.settings.security.oneSignin": "Sólo puedes tener un método de inicio de sesión a la vez. El cambio del método de inicio de sesión te enviará un correo notificandote que el cambio se realizó con éxito.",
+ "user.settings.security.password": "Contraseña",
+ "user.settings.security.passwordLengthError": "La nueva contraseña debe contener al menos {chars} carácteres",
+ "user.settings.security.passwordMatchError": "La nueva contraseña que ingresaste no coincide",
+ "user.settings.security.retypePassword": "Reescribe la Nueva Contraseña",
+ "user.settings.security.switchEmail": "Cambiar para utilizar correo electrónico y contraseña",
+ "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"
} \ No newline at end of file