summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-02-01 08:58:16 -0500
committerChristopher Speller <crspeller@gmail.com>2016-02-01 08:58:16 -0500
commit4a214cdaa2912f48dec4361722522c424384af49 (patch)
tree498f477c6782778062c60c1fe831150480c94ecf /web
parent681134912c057bcd9c6ca14260a17b1edf040303 (diff)
parent5be57bc970ebe72ecae3d421e75e3e418c479bc7 (diff)
downloadchat-4a214cdaa2912f48dec4361722522c424384af49.tar.gz
chat-4a214cdaa2912f48dec4361722522c424384af49.tar.bz2
chat-4a214cdaa2912f48dec4361722522c424384af49.zip
Merge pull request #2029 from ZBoxApp/PLT-7-user
PLT-7: Refactoring frontend (chunk 7)
Diffstat (limited to 'web')
-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/confirm_modal.jsx6
-rw-r--r--web/react/components/register_app_modal.jsx157
-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/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/static/i18n/en.json265
-rw-r--r--web/static/i18n/es.json265
23 files changed, 2256 insertions, 331 deletions
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/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/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/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/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/static/i18n/en.json b/web/static/i18n/en.json
index ed481a7f3..00c2c67f6 100644
--- a/web/static/i18n/en.json
+++ b/web/static/i18n/en.json
@@ -1,4 +1,64 @@
{
+ "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",
@@ -419,6 +479,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",
@@ -472,6 +533,32 @@
"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",
"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",
@@ -585,5 +672,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 02aab1dd2..dd7d22fdd 100644
--- a/web/static/i18n/es.json
+++ b/web/static/i18n/es.json
@@ -1,4 +1,64 @@
{
+ "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.",
@@ -424,6 +484,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",
@@ -481,6 +542,32 @@
"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",
"signup_team.choose": "Selecciona un Equipo",
"signup_team.createTeam": "O Crea un Equipo",
"signup_team.disabled": "La creación de Equipos ha sido deshabilitada.",
@@ -585,5 +672,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