Merge pull request #2038 from ZBoxApp/PLT-7-sidebar

PLT-7: Refactoring frontend (chunk 8)
This commit is contained in:
Joram Wilander
2016-02-01 09:52:01 -05:00
23 changed files with 1307 additions and 224 deletions

View File

@@ -3,6 +3,8 @@
var Modal = ReactBootstrap.Modal;
import {FormattedMessage} from 'mm-intl';
export default class AboutBuildModal extends React.Component {
constructor(props) {
super(props);
@@ -17,13 +19,28 @@ export default class AboutBuildModal extends React.Component {
const config = global.window.mm_config;
const license = global.window.mm_license;
let title = 'Team Edition';
let title = (
<FormattedMessage
id='about.teamEdtion'
defaultMessage='Team Edition'
/>
);
let licensee;
if (config.BuildEnterpriseReady === 'true' && license.IsLicensed === 'true') {
title = 'Enterprise Edition';
title = (
<FormattedMessage
id='about.enterpriseEdition'
defaultMessage='Enterprise Edition'
/>
);
licensee = (
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Licensed by:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.licensed'
defaultMessage='Licensed by:'
/>
</div>
<div className='col-sm-9'>{license.Company}</div>
</div>
);
@@ -35,25 +52,50 @@ export default class AboutBuildModal extends React.Component {
onHide={this.doHide}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'About Mattermost'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='about.title'
defaultMessage='About Mattermost'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>{`Mattermost ${title}`}</h4>
<h4>{'Mattermost'} {title}</h4>
{licensee}
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Version:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.version'
defaultMessage='Version:'
/>
</div>
<div className='col-sm-9'>{config.Version}</div>
</div>
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Build Number:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.number'
defaultMessage='Build Number:'
/>
</div>
<div className='col-sm-9'>{config.BuildNumber}</div>
</div>
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Build Date:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.date'
defaultMessage='Build Date:'
/>
</div>
<div className='col-sm-9'>{config.BuildDate}</div>
</div>
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Build Hash:'}</div>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='about.hash'
defaultMessage='Build Hash:'
/>
</div>
<div className='col-sm-9'>{config.BuildHash}</div>
</div>
</Modal.Body>
@@ -63,7 +105,10 @@ export default class AboutBuildModal extends React.Component {
className='btn btn-default'
onClick={this.doHide}
>
{'Close'}
<FormattedMessage
id='about.close'
defaultMessage='Close'
/>
</button>
</Modal.Footer>
</Modal>

View File

@@ -9,7 +9,7 @@ import TeamStore from '../../stores/team_store.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
var messages = defineMessages({
var holders = defineMessages({
confirmDemoteRoleTitle: {
id: 'admin.user_item.confirmDemoteRoleTitle',
defaultMessage: 'Confirm demotion from System Admin role'
@@ -21,6 +21,10 @@ var messages = defineMessages({
confirmDemoteDescription: {
id: 'admin.user_item.confirmDemoteDescription',
defaultMessage: 'If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.'
},
confirmDemotionCmd: {
id: 'admin.user_item.confirmDemotionCmd',
defaultMessage: 'platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"'
}
});
@@ -332,14 +336,15 @@ export default class UserItem extends React.Component {
);
}
const me = UserStore.getCurrentUser();
const {formatMessage} = this.props.intl;
let makeDemoteModal = null;
if (this.props.user.id === me.id) {
makeDemoteModal = (
<ConfirmModal
show={this.state.showDemoteModal}
title={this.props.intl.formatMessage(messages.confirmDemoteRoleTitle)}
message={[this.props.intl.formatMessage(messages.confirmDemoteDescription), React.createElement('br'), React.createElement('br'), './platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"', serverError]}
confirm_button={this.props.intl.formatMessage(messages.confirmDemotion)}
title={formatMessage(holders.confirmDemoteRoleTitle)}
message={[formatMessage(holders.confirmDemoteDescription), React.createElement('br'), React.createElement('br'), formatMessage(holders.confirmDemotionCmd), serverError]}
confirm_button={formatMessage(holders.confirmDemotion)}
onConfirm={this.handleDemoteSubmit}
onCancel={this.handleDemoteCancel}
/>

View File

@@ -4,6 +4,8 @@
var Modal = ReactBootstrap.Modal;
import * as Utils from '../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class ChangeUrlModal extends React.Component {
constructor(props) {
super(props);
@@ -39,21 +41,58 @@ export default class ChangeUrlModal extends React.Component {
getURLError(url) {
let error = []; //eslint-disable-line prefer-const
if (url.length < 2) {
error.push(<span key='error1'>{'Must be longer than two characters'}<br/></span>);
error.push(
<span key='error1'>
<FormattedMessage
id='change_url.longer'
defaultMessage='Must be longer than two characters'
/>
<br/>
</span>
);
}
if (url.charAt(0) === '-' || url.charAt(0) === '_') {
error.push(<span key='error2'>{'Must start with a letter or number'}<br/></span>);
error.push(
<span key='error2'>
<FormattedMessage
id='change_url.startWithLetter'
defaultMessage='Must start with a letter or number'
/>
<br/>
</span>
);
}
if (url.length > 1 && (url.charAt(url.length - 1) === '-' || url.charAt(url.length - 1) === '_')) {
error.push(<span key='error3'>{'Must end with a letter or number'}<br/></span>);
error.push(
<span key='error3'>
<FormattedMessage
id='change_url.endWithLetter'
defaultMessage='Must end with a letter or number'
/>
<br/>
</span>);
}
if (url.indexOf('__') > -1) {
error.push(<span key='error4'>{'Can not contain two underscores in a row.'}<br/></span>);
error.push(
<span key='error4'>
<FormattedMessage
id='change_url.noUnderscore'
defaultMessage='Can not contain two underscores in a row.'
/>
<br/>
</span>);
}
// In case of error we don't detect
if (error.length === 0) {
error.push(<span key='errorlast'>{'Invalid URL'}<br/></span>);
error.push(
<span key='errorlast'>
<FormattedMessage
id='change_url.invalidUrl'
defaultMessage='Invalid URL'
/>
<br/>
</span>);
}
return error;
}
@@ -137,7 +176,10 @@ export default class ChangeUrlModal extends React.Component {
className='btn btn-default'
onClick={this.doCancel}
>
{'Close'}
<FormattedMessage
id='change_url.close'
defaultMessage='Close'
/>
</button>
<button
onClick={this.doSubmit}

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class GetLinkModal extends React.Component {
@@ -59,14 +61,25 @@ export default class GetLinkModal extends React.Component {
className='btn btn-primary pull-left'
onClick={this.copyLink}
>
{'Copy Link'}
<FormattedMessage
id='get_link.copy'
defaultMessage='Copy Link'
/>
</button>
);
}
var copyLinkConfirm = null;
if (this.state.copiedLink) {
copyLinkConfirm = <p className='alert alert-success copy-link-confirm'><i className='fa fa-check'></i>{' Link copied to clipboard.'}</p>;
copyLinkConfirm = (
<p className='alert alert-success copy-link-confirm'>
<i className='fa fa-check'></i>
<FormattedMessage
id='get_link.clipboard'
defaultMessage=' Link copied to clipboard.'
/>
</p>
);
}
return (
@@ -92,7 +105,10 @@ export default class GetLinkModal extends React.Component {
className='btn btn-default'
onClick={this.onHide}
>
{'Close'}
<FormattedMessage
id='get_link.close'
defaultMessage='Close'
/>
</button>
{copyLink}
{copyLinkConfirm}

View File

@@ -6,7 +6,20 @@ import GetLinkModal from './get_link_modal.jsx';
import ModalStore from '../stores/modal_store.jsx';
import TeamStore from '../stores/team_store.jsx';
export default class GetTeamInviteLinkModal extends React.Component {
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const holders = defineMessages({
title: {
id: 'get_team_invite_link_modal.title',
defaultMessage: 'Team Invite Link'
},
help: {
id: 'get_team_invite_link_modal.help',
defaultMessage: 'Send teammates the link below for them to sign-up to this team site.'
}
});
class GetTeamInviteLinkModal extends React.Component {
constructor(props) {
super(props);
@@ -32,14 +45,22 @@ export default class GetTeamInviteLinkModal extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
return (
<GetLinkModal
show={this.state.show}
onHide={() => this.setState({show: false})}
title='Team Invite Link'
helpText='Send teammates the link below for them to sign-up to this team site.'
title={formatMessage(holders.title)}
helpText={formatMessage(holders.help)}
link={TeamStore.getCurrentInviteLink()}
/>
);
}
}
GetTeamInviteLinkModal.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(GetTeamInviteLinkModal);

View File

@@ -12,9 +12,38 @@ import ChannelStore from '../stores/channel_store.jsx';
import TeamStore from '../stores/team_store.jsx';
import ConfirmModal from './confirm_modal.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class InviteMemberModal extends React.Component {
const holders = defineMessages({
emailError: {
id: 'invite_member.emailError',
defaultMessage: 'Please enter a valid email address'
},
firstname: {
id: 'invite_member.firstname',
defaultMessage: 'First name'
},
lastname: {
id: 'invite_member.lastname',
defaultMessage: 'Last name'
},
modalTitle: {
id: 'invite_member.modalTitle',
defaultMessage: 'Discard Invitations?'
},
modalMessage: {
id: 'invite_member.modalMessage',
defaultMessage: 'You have unsent invitations, are you sure you want to discard them?'
},
modalButton: {
id: 'invite_member.modalButton',
defaultMessage: 'Yes, Discard'
}
});
class InviteMemberModal extends React.Component {
constructor(props) {
super(props);
@@ -72,7 +101,7 @@ export default class InviteMemberModal extends React.Component {
var invite = {};
invite.email = ReactDOM.findDOMNode(this.refs['email' + index]).value.trim();
if (!invite.email || !utils.isEmail(invite.email)) {
emailErrors[index] = 'Please enter a valid email address';
emailErrors[index] = this.props.intl.formatMessage(holders.emailError);
valid = false;
} else {
emailErrors[index] = '';
@@ -103,7 +132,7 @@ export default class InviteMemberModal extends React.Component {
this.setState({isSendingEmails: false});
},
(err) => {
if (err.message === 'This person is already on your team') {
if (err.id === 'api.team.invite_members.already.app_error') {
emailErrors[err.detailed_error] = err.message;
this.setState({emailErrors: emailErrors});
} else {
@@ -199,6 +228,7 @@ export default class InviteMemberModal extends React.Component {
render() {
var currentUser = UserStore.getCurrentUser();
const {formatMessage} = this.props.intl;
if (currentUser != null) {
var inviteSections = [];
@@ -252,7 +282,7 @@ export default class InviteMemberModal extends React.Component {
type='text'
className='form-control'
ref={'first_name' + index}
placeholder='First name'
placeholder={formatMessage(holders.firstname)}
maxLength='64'
disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
@@ -266,7 +296,7 @@ export default class InviteMemberModal extends React.Component {
type='text'
className='form-control'
ref={'last_name' + index}
placeholder='Last name'
placeholder={formatMessage(holders.lastname)}
maxLength='64'
disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
@@ -318,20 +348,48 @@ export default class InviteMemberModal extends React.Component {
type='button'
className='btn btn-default'
onClick={this.addInviteFields}
>{'Add another'}</button>
>
<FormattedMessage
id='invite_member.addAnother'
defaultMessage='Add another'
/>
</button>
<br/>
<br/>
<span>{'People invited automatically join the '}<strong>{defaultChannelName}</strong>{' channel.'}</span>
<span>
<FormattedHTMLMessage
id='invite_member.autoJoin'
defaultMessage='People invited automatically join the <strong>{channel}</strong> channel.'
values={{
channel: defaultChannelName
}}
/>
</span>
</div>
);
var sendButtonLabel = 'Send Invitation';
var sendButtonLabel = (
<FormattedMessage
id='invite_member.send'
defaultMessage='Send Invitation'
/>
);
if (this.state.isSendingEmails) {
sendButtonLabel = (
<span><i className='fa fa-spinner fa-spin' />{' Sending'}</span>
<span><i className='fa fa-spinner fa-spin' />
<FormattedMessage
id='invite_member.sending'
defaultMessage=' Sending'
/>
</span>
);
} else if (this.state.inviteIds.length > 1) {
sendButtonLabel = 'Send Invitations';
sendButtonLabel = (
<FormattedMessage
id='invite_member.send2'
defaultMessage='Send Invitations'
/>
);
}
sendButton = (
@@ -352,27 +410,46 @@ export default class InviteMemberModal extends React.Component {
href='#'
onClick={this.showGetTeamInviteLinkModal}
>
{'Team Invite Link'}
<FormattedMessage
id='invite_member.inviteLink'
defaultMessage='Team Invite Link'
/>
</a>
);
teamInviteLink = (
<p>
{'You can also invite people using the '}{link}{'.'}
<FormattedMessage
id='invite_member.teamInviteLink'
defaultMessage='You can also invite people using the {link}.'
values={{
link: (link)
}}
/>
</p>
);
}
content = (
<div>
<p>{'Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'}</p>
<p>
<FormattedMessage
id='invite_member.content'
defaultMessage='Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'
/>
</p>
{teamInviteLink}
</div>
);
} else {
content = (
<div>
<p>{'User creation has been disabled for your team. Please ask your team administrator for details.'}</p>
<p>
<FormattedMessage
id='invite_member.disabled'
defaultMessage='User creation has been disabled for your team. Please ask your team administrator for details.'
/>
</p>
</div>
);
}
@@ -387,7 +464,12 @@ export default class InviteMemberModal extends React.Component {
backdrop={this.state.isSendingEmails ? 'static' : true}
>
<Modal.Header closeButton={!this.state.isSendingEmails}>
<Modal.Title>{'Invite New Member'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='invite_member.newMember'
defaultMessage='Invite New Member'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<form role='form'>
@@ -402,15 +484,18 @@ export default class InviteMemberModal extends React.Component {
onClick={this.handleHide.bind(this, true)}
disabled={this.state.isSendingEmails}
>
{'Cancel'}
<FormattedMessage
id='invite_member.cancel'
defaultMessage='Cancel'
/>
</button>
{sendButton}
</Modal.Footer>
</Modal>
<ConfirmModal
title='Discard Invitations?'
message='You have unsent invitations, are you sure you want to discard them?'
confirm_button='Yes, Discard'
title={formatMessage(holders.modalTitle)}
message={formatMessage(holders.modalMessage)}
confirm_button={formatMessage(holders.modalButton)}
show={this.state.showConfirmModal}
onConfirm={this.handleHide.bind(this, false)}
onCancel={() => this.setState({showConfirmModal: false})}
@@ -424,4 +509,7 @@ export default class InviteMemberModal extends React.Component {
}
InviteMemberModal.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(InviteMemberModal);

View File

@@ -6,6 +6,8 @@ import * as Client from '../utils/client.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
import * as Utils from '../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
export default class MemberListTeamItem extends React.Component {
constructor(props) {
super(props);
@@ -78,14 +80,29 @@ export default class MemberListTeamItem extends React.Component {
}
const user = this.props.user;
let currentRoles = 'Member';
let currentRoles = (
<FormattedMessage
id='member_team_item.member'
defaultMessage='Member'
/>
);
const timestamp = UserStore.getCurrentUser().update_at;
if (user.roles.length > 0) {
if (Utils.isSystemAdmin(user.roles)) {
currentRoles = 'System Admin';
currentRoles = (
<FormattedMessage
id='member_team_item.systemAdmin'
defaultMessage='System Admin'
/>
);
} else if (Utils.isAdmin(user.roles)) {
currentRoles = 'Team Admin';
currentRoles = (
<FormattedMessage
id='member_team_item.teamAdmin'
defaultMessage='Team Admin'
/>
);
} else {
currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
}
@@ -98,7 +115,12 @@ export default class MemberListTeamItem extends React.Component {
let showMakeNotActive = user.roles !== 'system_admin';
if (user.delete_at > 0) {
currentRoles = 'Inactive';
currentRoles = (
<FormattedMessage
id='member_team_item.inactive'
defaultMessage='Inactive'
/>
);
showMakeMember = false;
showMakeAdmin = false;
showMakeActive = true;
@@ -114,7 +136,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeAdmin}
>
{'Make Team Admin'}
<FormattedMessage
id='member_team_item.makeAdmin'
defaultMessage='Make Team Admin'
/>
</a>
</li>
);
@@ -129,7 +154,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeMember}
>
{'Make Member'}
<FormattedMessage
id='member_team_item.makeMember'
defaultMessage='Make Member'
/>
</a>
</li>
);
@@ -144,7 +172,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeActive}
>
{'Make Active'}
<FormattedMessage
id='member_team_item.makeActive'
defaultMessage='Make Active'
/>
</a>
</li>
);
@@ -159,7 +190,10 @@ export default class MemberListTeamItem extends React.Component {
href='#'
onClick={this.handleMakeNotActive}
>
{'Make Inactive'}
<FormattedMessage
id='member_team_item.makeInactive'
defaultMessage='Make Inactive'
/>
</a>
</li>
);

View File

@@ -8,6 +8,8 @@ import ChannelStore from '../stores/channel_store.jsx';
import LoadingScreen from './loading_screen.jsx';
import NewChannelFlow from './new_channel_flow.jsx';
import {FormattedMessage} from 'mm-intl';
function getStateFromStores() {
return {
channels: ChannelStore.getMoreAll(),
@@ -100,7 +102,10 @@ export default class MoreChannels extends React.Component {
onClick={self.handleJoin.bind(self, channel, index)}
className='btn btn-primary'
>
Join
<FormattedMessage
id='more_channels.join'
defaultMessage='Join'
/>
</button>
);
}
@@ -123,8 +128,18 @@ export default class MoreChannels extends React.Component {
} else {
moreChannels = (
<div className='no-channel-message'>
<p className='primary-message'>No more channels to join</p>
<p className='secondary-message'>Click 'Create New Channel' to make a new one</p>
<p className='primary-message'>
<FormattedMessage
id='more_channels.noMore'
defaultMessage='No more channels to join'
/>
</p>
<p className='secondary-message'>
<FormattedMessage
id='more_channels.createClick'
defaultMessage="Click 'Create New Channel' to make a new one"
/>
</p>
</div>
);
}
@@ -148,15 +163,28 @@ export default class MoreChannels extends React.Component {
data-dismiss='modal'
>
<span aria-hidden='true'>{'×'}</span>
<span className='sr-only'>{'Close'}</span>
<span className='sr-only'>
<FormattedMessage
id='more_channels.close'
defaultMessage='Close'
/>
</span>
</button>
<h4 className='modal-title'>{'More Channels'}</h4>
<h4 className='modal-title'>
<FormattedMessage
id='more_channels.title'
defaultMessage='More Channels'
/>
</h4>
<button
type='button'
className='btn btn-primary channel-create-btn'
onClick={this.handleNewChannel}
>
{'Create New Channel'}
<FormattedMessage
id='more_channels.create'
defaultMessage='Create New Channel'
/>
</button>
<NewChannelFlow
show={this.state.showNewChannelModal}
@@ -174,7 +202,10 @@ export default class MoreChannels extends React.Component {
className='btn btn-default'
data-dismiss='modal'
>
{'Close'}
<FormattedMessage
id='more_channels.close'
defaultMessage='Close'
/>
</button>
</div>
</div>

View File

@@ -5,7 +5,20 @@ const Modal = ReactBootstrap.Modal;
import UserStore from '../stores/user_store.jsx';
import * as Utils from '../utils/utils.jsx';
export default class MoreDirectChannels extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
member: {
id: 'more_direct_channels.member',
defaultMessage: 'Member'
},
search: {
id: 'more_direct_channels.search',
defaultMessage: 'Search members'
}
});
class MoreDirectChannels extends React.Component {
constructor(props) {
super(props);
@@ -148,7 +161,10 @@ export default class MoreDirectChannels extends React.Component {
className='btn btn-primary btn-message'
onClick={this.handleShowDirectChannel.bind(this, user)}
>
{'Message'}
<FormattedMessage
id='more_direct_channels.message'
defaultMessage='Message'
/>
</button>
);
}
@@ -180,6 +196,7 @@ export default class MoreDirectChannels extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
if (!this.props.show) {
return null;
}
@@ -199,19 +216,44 @@ export default class MoreDirectChannels extends React.Component {
const userEntries = users.map(this.createRowForUser);
if (userEntries.length === 0) {
userEntries.push(<tr key='no-users-found'><td>{'No users found :('}</td></tr>);
userEntries.push(
<tr key='no-users-found'><td>
<FormattedMessage
id='more_direct_channels.notFound'
defaultMessage='No users found :('
/>
</td></tr>);
}
let memberString = 'Member';
let memberString = formatMessage(holders.member);
if (users.length !== 1) {
memberString += 's';
}
let count;
if (users.length === this.state.users.length) {
count = `${users.length} ${memberString}`;
count = (
<FormattedMessage
id='more_direct_channels.count'
defaultMessage='{count} {member}'
values={{
count: users.length,
member: memberString
}}
/>
);
} else {
count = `${users.length} ${memberString} of ${this.state.users.length} Total`;
count = (
<FormattedMessage
id='more_direct_channels.countTotal'
defaultMessage='{count} {member} of {total} Total'
values={{
count: users.length,
member: memberString,
total: this.state.users.length
}}
/>
);
}
return (
@@ -221,7 +263,12 @@ export default class MoreDirectChannels extends React.Component {
onHide={this.handleHide}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'Direct Messages'}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='more_direct_channels.title'
defaultMessage='Direct Messages'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body ref='modalBody'>
<div className='filter-row'>
@@ -229,7 +276,7 @@ export default class MoreDirectChannels extends React.Component {
<input
ref='filter'
className='form-control filter-textbox'
placeholder='Search members'
placeholder={formatMessage(holders.search)}
onInput={this.handleFilterChange}
/>
</div>
@@ -254,7 +301,10 @@ export default class MoreDirectChannels extends React.Component {
className='btn btn-default'
onClick={this.handleHide}
>
{'Close'}
<FormattedMessage
id='more_direct_channels.close'
defaultMessage='Close'
/>
</button>
</Modal.Footer>
</Modal>
@@ -263,6 +313,9 @@ export default class MoreDirectChannels extends React.Component {
}
MoreDirectChannels.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func
};
export default injectIntl(MoreDirectChannels);

View File

@@ -14,6 +14,8 @@ import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
function getStateFromStores() {
const teams = [];
const teamsObject = UserStore.getTeams();
@@ -97,7 +99,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={EventHelpers.showInviteMemberModal}
>
{'Invite New Member'}
<FormattedMessage
id='navbar_dropdown.inviteMember'
defaultMessage='Invite New Member'
/>
</a>
</li>
);
@@ -109,7 +114,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={EventHelpers.showGetTeamInviteLinkModal}
>
{'Get Team Invite Link'}
<FormattedMessage
id='navbar_dropdown.teamLink'
defaultMessage='Get Team Invite Link'
/>
</a>
</li>
);
@@ -120,7 +128,10 @@ export default class NavbarDropdown extends React.Component {
manageLink = (
<li>
<ToggleModalButton dialogType={TeamMembersModal}>
{'Manage Members'}
<FormattedMessage
id='navbar_dropdown.manageMembers'
defaultMessage='Manage Members'
/>
</ToggleModalButton>
</li>
);
@@ -134,7 +145,10 @@ export default class NavbarDropdown extends React.Component {
data-toggle='modal'
data-target='#team_settings'
>
{'Team Settings'}
<FormattedMessage
id='navbar_dropdown.teamSettings'
defaultMessage='Team Settings'
/>
</a>
</li>
);
@@ -146,7 +160,10 @@ export default class NavbarDropdown extends React.Component {
<a
href={'/admin_console?' + Utils.getSessionIndex()}
>
{'System Console'}
<FormattedMessage
id='navbar_dropdown.console'
defaultMessage='System Console'
/>
</a>
</li>
);
@@ -165,7 +182,16 @@ export default class NavbarDropdown extends React.Component {
this.state.teams.forEach((team) => {
if (team.name !== this.props.teamName) {
teams.push(<li key={team.name}><a href={Utils.getWindowLocationOrigin() + '/' + team.name}>{'Switch to ' + team.display_name}</a></li>);
teams.push(
<li key={team.name}><a href={Utils.getWindowLocationOrigin() + '/' + team.name}>
<FormattedMessage
id='navbar_dropdown.switchTeam'
defaultMessage='Switch to {team}'
values={{
team: team.display_name
}}
/>
</a></li>);
}
});
}
@@ -178,7 +204,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={Utils.getWindowLocationOrigin() + '/signup_team'}
>
{'Create a New Team'}
<FormattedMessage
id='navbar_dropdown.create'
defaultMessage='Create a New Team'
/>
</a>
</li>
);
@@ -192,7 +221,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={global.window.mm_config.HelpLink}
>
{'Help'}
<FormattedMessage
id='navbar_dropdown.help'
defaultMessage='Help'
/>
</a>
</li>
);
@@ -206,7 +238,10 @@ export default class NavbarDropdown extends React.Component {
target='_blank'
href={global.window.mm_config.ReportAProblemLink}
>
{'Report a Problem'}
<FormattedMessage
id='navbar_dropdown.report'
defaultMessage='Report a Problem'
/>
</a>
</li>
);
@@ -239,7 +274,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={() => this.setState({showUserSettingsModal: true})}
>
{'Account Settings'}
<FormattedMessage
id='navbar_dropdown.accountSettings'
defaultMessage='Account Settings'
/>
</a>
</li>
{inviteLink}
@@ -249,7 +287,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={this.handleLogoutClick}
>
{'Logout'}
<FormattedMessage
id='navbar_dropdown.logout'
defaultMessage='Logout'
/>
</a>
</li>
{adminDivider}
@@ -265,7 +306,10 @@ export default class NavbarDropdown extends React.Component {
href='#'
onClick={this.handleAboutModal}
>
{'About Mattermost'}
<FormattedMessage
id='navbar_dropdown.about'
defaultMessage='About Mattermost'
/>
</a>
</li>
<UserSettingsModal

View File

@@ -9,11 +9,47 @@ import UserStore from '../stores/user_store.jsx';
import NewChannelModal from './new_channel_modal.jsx';
import ChangeURLModal from './change_url_modal.jsx';
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const SHOW_NEW_CHANNEL = 1;
const SHOW_EDIT_URL = 2;
const SHOW_EDIT_URL_THEN_COMPLETE = 3;
const messages = defineMessages({
invalidName: {
id: 'channel_flow.invalidName',
defaultMessage: 'Invalid Channel Name'
},
alreadyExist: {
id: 'channel_flow.alreadyExist',
defaultMessage: 'A channel with that URL already exists'
},
channel: {
id: 'channel_flow.channel',
defaultMessage: 'Channel'
},
group: {
id: 'channel_flow.group',
defaultMessage: 'Group'
},
change: {
id: 'channel_flow.changeUrlTitle',
defaultMessage: 'Change {term} URL'
},
set: {
id: 'channel_flow.set_url_title',
defaultMessage: 'Set {term} URL'
},
create: {
id: 'channel_flow.create',
defaultMessage: 'Create {term}'
},
changeUrlDescription: {
id: 'channel_flow.changeUrlDescription',
defaultMessage: 'Some characters are not allowed in URLs and may be removed.'
}
});
export default class NewChannelFlow extends React.Component {
class NewChannelFlow extends React.Component {
constructor(props) {
super(props);
@@ -51,9 +87,10 @@ export default class NewChannelFlow extends React.Component {
doSubmit() {
var channel = {};
const {formatMessage} = this.props.intl;
channel.display_name = this.state.channelDisplayName;
if (!channel.display_name) {
this.setState({serverError: 'Invalid Channel Name'});
this.setState({serverError: formatMessage(messages.invalidName)});
return;
}
@@ -75,11 +112,11 @@ export default class NewChannelFlow extends React.Component {
Utils.switchChannel(data);
},
(err) => {
if (err.message === 'Name must be 2 or more lowercase alphanumeric characters') {
if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
}
if (err.message === 'A channel with that handle already exists') {
this.setState({serverError: 'A channel with that URL already exists'});
if (err.id === 'store.sql_channel.update.exists.app_error') {
this.setState({serverError: formatMessage(messages.alreadyExist)});
return;
}
this.setState({serverError: err.message});
@@ -130,27 +167,29 @@ export default class NewChannelFlow extends React.Component {
let changeURLSubmitButtonText = '';
let channelTerm = '';
const {formatMessage} = this.props.intl;
// Only listen to flow state if we are being shown
if (this.props.show) {
switch (this.state.flowState) {
case SHOW_NEW_CHANNEL:
if (this.state.channelType === 'O') {
showChannelModal = true;
channelTerm = 'Channel';
channelTerm = formatMessage(messages.channel);
} else {
showGroupModal = true;
channelTerm = 'Group';
channelTerm = formatMessage(messages.group);
}
break;
case SHOW_EDIT_URL:
showChangeURLModal = true;
changeURLTitle = 'Change ' + channelTerm + ' URL';
changeURLSubmitButtonText = 'Change ' + channelTerm + ' URL';
changeURLTitle = formatMessage(messages.change, {term: channelTerm});
changeURLSubmitButtonText = formatMessage(messages.change, {term: channelTerm});
break;
case SHOW_EDIT_URL_THEN_COMPLETE:
showChangeURLModal = true;
changeURLTitle = 'Set ' + channelTerm + ' URL';
changeURLSubmitButtonText = 'Create ' + channelTerm;
changeURLTitle = formatMessage(messages.set, {term: channelTerm});
changeURLSubmitButtonText = formatMessage(messages.create, {term: channelTerm});
break;
}
}
@@ -181,7 +220,7 @@ export default class NewChannelFlow extends React.Component {
<ChangeURLModal
show={showChangeURLModal}
title={changeURLTitle}
description={'Some characters are not allowed in URLs and may be removed.'}
description={formatMessage(messages.changeUrlDescription)}
urlLabel={channelTerm + ' URL'}
submitButtonText={changeURLSubmitButtonText}
currentURL={this.state.channelName}
@@ -200,7 +239,10 @@ NewChannelFlow.defaultProps = {
};
NewChannelFlow.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
channelType: React.PropTypes.string.isRequired,
onModalDismissed: React.PropTypes.func.isRequired
};
export default injectIntl(NewChannelFlow);

View File

@@ -2,9 +2,19 @@
// See License.txt for license information.
import * as Utils from '../utils/utils.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
var Modal = ReactBootstrap.Modal;
export default class NewChannelModal extends React.Component {
const holders = defineMessages({
nameEx: {
id: 'channel_modal.nameEx',
defaultMessage: 'E.g.: "Bugs", "Marketing", "办公室恋情"'
}
});
class NewChannelModal extends React.Component {
constructor(props) {
super(props);
@@ -32,7 +42,7 @@ export default class NewChannelModal extends React.Component {
const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim();
if (displayName.length < 1) {
this.setState({displayNameError: 'This field is required'});
this.setState({displayNameError: true});
return;
}
@@ -51,7 +61,15 @@ export default class NewChannelModal extends React.Component {
var displayNameClass = 'form-group';
if (this.state.displayNameError) {
displayNameError = <p className='input__help error'>{this.state.displayNameError}</p>;
displayNameError = (
<p className='input__help error'>
<FormattedMessage
id='channel_modal.displayNameError'
defaultMessage='This field is required'
/>
{this.state.displayNameError}
</p>
);
displayNameClass += ' has-error';
}
@@ -63,29 +81,51 @@ export default class NewChannelModal extends React.Component {
var channelSwitchText = '';
switch (this.props.channelType) {
case 'P':
channelTerm = 'Group';
channelTerm = (
<FormattedMessage
id='channel_modal.group'
defaultMessage='Group'
/>
);
channelSwitchText = (
<div className='modal-intro'>
{'Create a new private group with restricted membership. '}
<FormattedMessage
id='channel_modal.privateGroup1'
defaultMessage='Create a new private group with restricted membership. '
/>
<a
href='#'
onClick={this.props.onTypeSwitched}
>
{'Create a public channel'}
<FormattedMessage
id='channel_modal.publicChannel1'
defaultMessage='Create a public channel'
/>
</a>
</div>
);
break;
case 'O':
channelTerm = 'Channel';
channelTerm = (
<FormattedMessage
id='channel_modal.channel'
defaultMessage='Channel'
/>
);
channelSwitchText = (
<div className='modal-intro'>
{'Create a new public channel anyone can join. '}
<FormattedMessage
id='channel_modal.publicChannel2'
defaultMessage='Create a new public channel anyone can join. '
/>
<a
href='#'
onClick={this.props.onTypeSwitched}
>
{'Create a private group'}
<FormattedMessage
id='channel_modal.privateGroup2'
defaultMessage='Create a private group'
/>
</a>
</div>
);
@@ -102,7 +142,13 @@ export default class NewChannelModal extends React.Component {
onHide={this.props.onModalDismissed}
>
<Modal.Header closeButton={true}>
<Modal.Title>{'New ' + channelTerm}</Modal.Title>
<Modal.Title>
<FormattedMessage
id='channel_modal.modalTitle'
defaultMessage='New '
/>
{channelTerm}
</Modal.Title>
</Modal.Header>
<form
role='form'
@@ -113,14 +159,19 @@ export default class NewChannelModal extends React.Component {
{channelSwitchText}
</div>
<div className={displayNameClass}>
<label className='col-sm-3 form__label control-label'>{'Name'}</label>
<label className='col-sm-3 form__label control-label'>
<FormattedMessage
id='channel_modal.name'
defaultMessage='Name'
/>
</label>
<div className='col-sm-9'>
<input
onChange={this.handleChange}
type='text'
ref='display_name'
className='form-control'
placeholder='E.g.: "Bugs", "Marketing", "办公室恋情"'
placeholder={this.props.intl.formatMessage(holders.nameEx)}
maxLength='22'
value={this.props.channelData.displayName}
autoFocus={true}
@@ -133,7 +184,10 @@ export default class NewChannelModal extends React.Component {
href='#'
onClick={this.props.onChangeURLPressed}
>
{'Edit'}
<FormattedMessage
id='channel_modal.edit'
defaultMessage='Edit'
/>
</a>
{')'}
</p>
@@ -141,22 +195,38 @@ export default class NewChannelModal extends React.Component {
</div>
<div className='form-group less'>
<div className='col-sm-3'>
<label className='form__label control-label'>{'Purpose'}</label>
<label className='form__label light'>{'(optional)'}</label>
<label className='form__label control-label'>
<FormattedMessage
id='channel_modal.purpose'
defaultMessage='Purpose'
/>
</label>
<label className='form__label light'>
<FormattedMessage
id='channel_modal.optional'
defaultMessage='(optional)'
/>
</label>
</div>
<div className='col-sm-9'>
<textarea
className='form-control no-resize'
ref='channel_purpose'
rows='4'
placeholder='Purpose'
placeholder={this.props.intl.formatMessage({id: 'channel_modal.purpose'})}
maxLength='128'
value={this.props.channelData.purpose}
onChange={this.handleChange}
tabIndex='2'
/>
<p className='input__help'>
{`Describe how this ${channelTerm} should be used.`}
<FormattedMessage
id='channel_modal.descriptionHelp'
defaultMessage='Describe how this {term} should be used.'
values={{
term: (channelTerm)
}}
/>
</p>
{serverError}
</div>
@@ -168,7 +238,10 @@ export default class NewChannelModal extends React.Component {
className='btn btn-default'
onClick={this.props.onModalDismissed}
>
{'Cancel'}
<FormattedMessage
id='channel_modal.cancel'
defaultMessage='Cancel'
/>
</button>
<button
onClick={this.handleSubmit}
@@ -176,7 +249,11 @@ export default class NewChannelModal extends React.Component {
className='btn btn-primary'
tabIndex='3'
>
{'Create New ' + channelTerm}
<FormattedMessage
id='channel_modal.createNew'
defaultMessage='Create New '
/>
{channelTerm}
</button>
</Modal.Footer>
</form>
@@ -192,6 +269,7 @@ NewChannelModal.defaultProps = {
serverError: ''
};
NewChannelModal.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
channelType: React.PropTypes.string.isRequired,
channelData: React.PropTypes.object.isRequired,
@@ -202,3 +280,5 @@ NewChannelModal.propTypes = {
onChangeURLPressed: React.PropTypes.func.isRequired,
onDataChanged: React.PropTypes.func.isRequired
};
export default injectIntl(NewChannelModal);

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'mm-intl';
export default class SettingsUpload extends React.Component {
constructor(props) {
super(props);
@@ -41,7 +43,7 @@ export default class SettingsUpload extends React.Component {
if (inputnode.files && inputnode.files[0]) {
this.props.submit(inputnode.files[0]);
} else {
this.setState({clientError: 'No file selected.'});
this.setState({clientError: true});
}
}
@@ -49,7 +51,12 @@ export default class SettingsUpload extends React.Component {
let clientError = null;
if (this.state.clientError) {
clientError = (
<div className='file-status'>{this.state.clientError}</div>
<div className='file-status'>
<FormattedMessage
id='setting_upload.noFile'
defaultMessage='No file selected.'
/>
</div>
);
}
let serverError = null;
@@ -75,7 +82,10 @@ export default class SettingsUpload extends React.Component {
<ul className='setting-list'>
<li className='setting-list-item'>
<span className='btn btn-sm btn-primary btn-file sel-btn'>
{'Select file'}
<FormattedMessage
id='setting_upload.select'
defaultMessage='Select file'
/>
<input
ref='uploadinput'
accept={this.props.fileTypesAccepted}
@@ -87,7 +97,10 @@ export default class SettingsUpload extends React.Component {
className={submitButtonClass}
onClick={this.doSubmit}
>
{'Import'}
<FormattedMessage
id='setting_upload.import'
defaultMessage='Import'
/>
</a>
{fileNameText}
{serverError}

View File

@@ -17,6 +17,9 @@ import * as Client from '../utils/client.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
@@ -275,34 +278,33 @@ export default class Sidebar extends React.Component {
screens.push(
<div>
<h4>{'Channels'}</h4>
<p><strong>{'Channels'}</strong>{' organize conversations across different topics. Theyre open to everyone on your team. To send private communications use '}<strong>{'Direct Messages'}</strong>{' for a single person or '}<strong>{'Private Groups'}</strong>{' for multiple people.'}
</p>
<FormattedHTMLMessage
id='sidebar.tutorialScreen1'
defaultMessage='<h4>Channels</h4><p><strong>Channels</strong> organize conversations across different topics. Theyre open to everyone on your team. To send private communications use <strong>Direct Messages</strong> for a single person or <strong>Private Groups</strong> for multiple people.</p>'
/>
</div>
);
screens.push(
<div>
<h4>{'"Town Square" and "Off-Topic" channels'}</h4>
<p>{'Here are two public channels to start:'}</p>
<p>
<strong>{'Town Square'}</strong>{' is a place for team-wide communication. Everyone in your team is a member of this channel.'}
</p>
<p>
<strong>{'Off-Topic'}</strong>{' is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.'}
</p>
<FormattedHTMLMessage
id='sidebar.tutorialScreen2'
defaultMessage='<h4>"Town Square" and "Off-Topic" channels</h4>
<p>Here are two public channels to start:</p>
<p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>
<p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>'
/>
</div>
);
screens.push(
<div>
<h4>{'Creating and Joining Channels'}</h4>
<p>
{'Click '}<strong>{'"More..."'}</strong>{' to create a new channel or join an existing one.'}
</p>
<p>
{'You can also create a new channel or private group by clicking the '}<strong>{'"+" symbol'}</strong>{' next to the channel or private group header.'}
</p>
<FormattedHTMLMessage
id='sidebar.tutorialScreen3'
defaultMessage='<h4>Creating and Joining Channels</h4>
<p>Click <strong>"More..."</strong> to create a new channel or join an existing one.</p>
<p>You can also create a new channel or private group by clicking the <strong>"+" symbol</strong> next to the channel or private group header.</p>'
/>
</div>
);
@@ -437,7 +439,12 @@ export default class Sidebar extends React.Component {
let closeButton = null;
const removeTooltip = (
<Tooltip id='remove-dm-tooltip'>{'Remove from list'}</Tooltip>
<Tooltip id='remove-dm-tooltip'>
<FormattedMessage
id='sidebar.removeList'
defaultMessage='Remove from list'
/>
</Tooltip>
);
if (handleClose && !badge) {
closeButton = (
@@ -525,7 +532,13 @@ export default class Sidebar extends React.Component {
href='#'
onClick={this.showMoreDirectChannelsModal}
>
{'More (' + this.state.hiddenDirectChannelCount + ')'}
<FormattedMessage
id='sidebar.more'
defaultMessage='More ({count})'
values={{
count: this.state.hiddenDirectChannelCount
}}
/>
</a>
</li>
);
@@ -537,10 +550,34 @@ export default class Sidebar extends React.Component {
}
const createChannelTootlip = (
<Tooltip id='new-channel-tooltip' >{'Create new channel'}</Tooltip>
<Tooltip id='new-channel-tooltip' >
<FormattedMessage
id='sidebar.createChannel'
defaultMessage='Create new channel'
/>
</Tooltip>
);
const createGroupTootlip = (
<Tooltip id='new-group-tooltip'>{'Create new group'}</Tooltip>
<Tooltip id='new-group-tooltip'>
<FormattedMessage
id='sidebar.createGroup'
defaultMessage='Create new group'
/>
</Tooltip>
);
const above = (
<FormattedMessage
id='sidebar.unreadAbove'
defaultMessage='Unread post(s) above'
/>
);
const below = (
<FormattedMessage
id='sidebar.unreadBelow'
defaultMessage='Unread post(s) below'
/>
);
return (
@@ -564,12 +601,12 @@ export default class Sidebar extends React.Component {
<UnreadChannelIndicator
show={this.state.showTopUnread}
extraClass='nav-pills__unread-indicator-top'
text={'Unread post(s) above'}
text={above}
/>
<UnreadChannelIndicator
show={this.state.showBottomUnread}
extraClass='nav-pills__unread-indicator-bottom'
text={'Unread post(s) below'}
text={below}
/>
<div
@@ -580,7 +617,10 @@ export default class Sidebar extends React.Component {
<ul className='nav nav-pills nav-stacked'>
<li>
<h4>
{'Channels'}
<FormattedMessage
id='sidebar.channels'
defaultMessage='Channels'
/>
<OverlayTrigger
delayShow={500}
placement='top'
@@ -603,7 +643,10 @@ export default class Sidebar extends React.Component {
className='nav-more'
onClick={this.showMoreChannelsModal}
>
{'More...'}
<FormattedMessage
id='sidebar.moreElips'
defaultMessage='More...'
/>
</a>
</li>
</ul>
@@ -611,7 +654,10 @@ export default class Sidebar extends React.Component {
<ul className='nav nav-pills nav-stacked'>
<li>
<h4>
{'Private Groups'}
<FormattedMessage
id='sidebar.pg'
defaultMessage='Private Groups'
/>
<OverlayTrigger
delayShow={500}
placement='top'
@@ -630,7 +676,14 @@ export default class Sidebar extends React.Component {
{privateChannelItems}
</ul>
<ul className='nav nav-pills nav-stacked'>
<li><h4>{'Direct Messages'}</h4></li>
<li>
<h4>
<FormattedMessage
id='sidebar.direct'
defaultMessage='Direct Messages'
/>
</h4>
</li>
{directMessageItems}
{directMessageMore}
</ul>

View File

@@ -9,6 +9,9 @@ import PreferenceStore from '../stores/preference_store.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedHTMLMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
@@ -51,20 +54,12 @@ export default class SidebarHeader extends React.Component {
screens.push(
<div>
<h4>{'Main Menu'}</h4>
<p>
{'The '}<strong>{'Main Menu'}</strong>{' is where you can '}
<strong>{'Invite New Members'}</strong>
{', access your '}
<strong>{'Account Settings'}</strong>
{' and set your '}<strong>{'Theme Color'}</strong>{'.'}
</p>
<p>
{'Team administrators can also access their '}<strong>{'Team Settings'}</strong>{' from this menu.'}
</p>
<p>
{'System administrators will find a '}<strong>{'System Console'}</strong>{' option to administrate the entire system.'}
</p>
<FormattedHTMLMessage
id='sidebar_header.tutorial'
defaultMessage='<h4>Main Menu</h4>
<p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p>
<p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>'
/>
</div>
);

View File

@@ -3,6 +3,8 @@
import * as Client from '../utils/client.jsx';
import {FormattedMessage} from 'mm-intl';
export default class TeamExportTab extends React.Component {
constructor(props) {
super(props);
@@ -35,7 +37,10 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-spinner fa-pulse' />
{' Exporting...'}
<FormattedMessage
id='team_export_tab.exporting'
defaultMessage=' Exporting...'
/>
</p>
);
break;
@@ -43,12 +48,18 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-success'>
<i className='fa fa-check' />
{' Ready for '}
<FormattedMessage
id='team_export_tab.ready'
defaultMessage=' Ready for '
/>
<a
href={this.state.link}
download={true}
>
{'download'}
<FormattedMessage
id='team_export_tab.download'
defaultMessage='download'
/>
</a>
</p>
);
@@ -57,7 +68,13 @@ export default class TeamExportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-warning' />
{' Unable to export: ' + this.state.err}
<FormattedMessage
id='team_export_tab.unable'
defaultMessage=' Unable to export: {error}'
values={{
error: this.state.err
}}
/>
</p>
);
break;
@@ -68,10 +85,20 @@ export default class TeamExportTab extends React.Component {
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>{'Export'}</h3>
<h3 className='tab-header'>
<FormattedMessage
id='team_export_tab.export'
defaultMessage='Export'
/>
</h3>
<div className='divider-dark first'/>
<ul className='section-max'>
<li className='col-xs-12 section-title'>{'Export your team'}</li>
<li className='col-xs-12 section-title'>
<FormattedMessage
id='team_export_tab.exportTeam'
defaultMessage='Export your team'
/>
</li>
<li className='col-xs-offset-3 col-xs-8'>
<ul className='setting-list'>
<li className='setting-list-item'>
@@ -80,7 +107,10 @@ export default class TeamExportTab extends React.Component {
href='#'
onClick={this.doExport}
>
{'Export'}
<FormattedMessage
id='team_export_tab.export'
defaultMessage='Export'
/>
</a>
</li>
</ul>

View File

@@ -8,7 +8,56 @@ import * as Client from '../utils/client.jsx';
import * as Utils from '../utils/utils.jsx';
import TeamStore from '../stores/team_store.jsx';
export default class GeneralTab extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
dirDisabled: {
id: 'general_tab.dirDisabled',
defaultMessage: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'
},
required: {
id: 'general_tab.required',
defaultMessage: 'This field is required'
},
chooseName: {
id: 'general_tab.chooseName',
defaultMessage: 'Please choose a new name for your team'
},
includeDirTitle: {
id: 'general_tab.includeDirTitle',
defaultMessage: 'Include this team in the Team Directory'
},
yes: {
id: 'general_tab.yes',
defaultMessage: 'Yes'
},
no: {
id: 'general_tab.no',
defaultMessage: 'No'
},
dirOff: {
id: 'general_tab.dirOff',
defaultMessage: 'Team directory is turned off for this system.'
},
openInviteTitle: {
id: 'general_tab.openInviteTitle',
defaultMessage: 'Allow anyone to sign-up from login page'
},
codeTitle: {
id: 'general_tab.codeTitle',
defaultMessage: 'Invite Code'
},
codeDesc: {
id: 'general_tab.codeDesc',
defaultMessage: "Click 'Edit' to regenerate Invite Code."
},
teamNameInfo: {
id: 'general_tab.teamNameInfo',
defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
}
});
class GeneralTab extends React.Component {
constructor(props) {
super(props);
@@ -66,7 +115,7 @@ export default class GeneralTab extends React.Component {
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
this.setState({clientError: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'});
this.setState({clientError: this.props.intl.formatMessage(holders.dirDisabled)});
} else {
this.setState({allow_team_listing: listing});
}
@@ -118,12 +167,13 @@ export default class GeneralTab extends React.Component {
var state = {serverError: '', clientError: ''};
let valid = true;
const {formatMessage} = this.props.intl;
const name = this.state.name.trim();
if (!name) {
state.clientError = 'This field is required';
state.clientError = formatMessage(holders.required);
valid = false;
} else if (name === this.props.team.display_name) {
state.clientError = 'Please choose a new name for your team';
state.clientError = formatMessage(holders.chooseName);
valid = false;
} else {
state.clientError = '';
@@ -160,7 +210,7 @@ export default class GeneralTab extends React.Component {
if (inviteId) {
state.clientError = '';
} else {
state.clientError = 'This field is required';
state.clientError = this.props.intl.fromatMessage(holders.required);
valid = false;
}
@@ -260,6 +310,7 @@ export default class GeneralTab extends React.Component {
}
const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
const {formatMessage} = this.props.intl;
let teamListingSection;
if (this.props.activeSection === 'team_listing') {
@@ -279,7 +330,10 @@ export default class GeneralTab extends React.Component {
defaultChecked={this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, true)}
/>
{'Yes'}
<FormattedMessage
id='general_tab.yes'
defaultMessage='Yes'
/>
</label>
<br/>
</div>
@@ -292,24 +346,39 @@ export default class GeneralTab extends React.Component {
defaultChecked={!this.state.allow_team_listing}
onChange={this.handleTeamListingRadio.bind(this, false)}
/>
{'No'}
<FormattedMessage
id='general_tab.no'
defaultMessage='No'
/>
</label>
<br/>
</div>
<div><br/>{'Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'}</div>
<div>
<br/>
<FormattedMessage
id='general_tab.includeDirDesc'
defaultMessage='Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'
/>
</div>
</div>
);
} else {
inputs.push(
<div key='userTeamListingOptions'>
<div><br/>{'Contact your system administrator to turn on the team directory on the system home page.'}</div>
<div>
<br/>
<FormattedMessage
id='general_tab.dirContact'
defaultMessage='Contact your system administrator to turn on the team directory on the system home page.'
/>
</div>
</div>
);
}
teamListingSection = (
<SettingItemMax
title='Include this team in the Team Directory'
title={formatMessage(holders.includeDirTitle)}
inputs={inputs}
submit={submitHandle}
server_error={serverError}
@@ -322,17 +391,17 @@ export default class GeneralTab extends React.Component {
if (enableTeamListing) {
if (this.state.allow_team_listing === true) {
describe = 'Yes';
describe = formatMessage(holders.yes);
} else {
describe = 'No';
describe = formatMessage(holders.no);
}
} else {
describe = 'Team directory is turned off for this system.';
describe = formatMessage(holders.dirOff);
}
teamListingSection = (
<SettingItemMin
title='Include this team in the Team Directory'
title={formatMessage(holders.includeDirTitle)}
describe={describe}
updateSection={this.onUpdateTeamListingSection}
/>
@@ -351,7 +420,10 @@ export default class GeneralTab extends React.Component {
defaultChecked={this.state.allow_open_invite}
onChange={this.handleOpenInviteRadio.bind(this, true)}
/>
{'Yes'}
<FormattedMessage
id='general_tab.yes'
defaultMessage='Yes'
/>
</label>
<br/>
</div>
@@ -363,17 +435,26 @@ export default class GeneralTab extends React.Component {
defaultChecked={!this.state.allow_open_invite}
onChange={this.handleOpenInviteRadio.bind(this, false)}
/>
{'No'}
<FormattedMessage
id='general_tab.no'
defaultMessage='No'
/>
</label>
<br/>
</div>
<div><br/>{'When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'}</div>
<div>
<br/>
<FormattedMessage
id='general_tab.openInviteDesc'
defaultMessage='When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'
/>
</div>
</div>
];
openInviteSection = (
<SettingItemMax
title='Allow anyone to sign-up from login page'
title={formatMessage(holders.openInviteTitle)}
inputs={inputs}
submit={this.handleOpenInviteSubmit}
server_error={serverError}
@@ -383,14 +464,14 @@ export default class GeneralTab extends React.Component {
} else {
let describe = '';
if (this.state.allow_open_invite === true) {
describe = 'Yes';
describe = formatMessage(holders.yes);
} else {
describe = 'No';
describe = formatMessage(holders.no);
}
openInviteSection = (
<SettingItemMin
title='Allow anyone to sign-up from login page'
title={formatMessage(holders.openInviteTitle)}
describe={describe}
updateSection={this.onUpdateOpenInviteSection}
/>
@@ -405,7 +486,12 @@ export default class GeneralTab extends React.Component {
inputs.push(
<div key='teamInviteSetting'>
<div className='row'>
<label className='col-sm-5 control-label'>{'Invite Code'}</label>
<label className='col-sm-5 control-label'>
<FormattedMessage
id='general_tab.codeTitle'
defaultMessage='Invite Code'
/>
</label>
<div className='col-sm-7'>
<input
className='form-control'
@@ -419,18 +505,26 @@ export default class GeneralTab extends React.Component {
href='#'
onClick={this.handleGenerateInviteId}
>
{'Re-Generate'}
<FormattedMessage
id='general_tab.regenerate'
defaultMessage='Re-Generate'
/>
</a>
</div>
</div>
</div>
<div className='setting-list__hint'>{'The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'}</div>
<div className='setting-list__hint'>
<FormattedMessage
id='general_tab.codeLongDesc'
defaultMessage='The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'
/>
</div>
</div>
);
inviteSection = (
<SettingItemMax
title={`Invite Code`}
title={formatMessage(holders.codeTitle)}
inputs={inputs}
submit={this.handleInviteIdSubmit}
server_error={serverError}
@@ -441,8 +535,8 @@ export default class GeneralTab extends React.Component {
} else {
inviteSection = (
<SettingItemMin
title={`Invite Code`}
describe={`Click 'Edit' to regenerate Invite Code.`}
title={formatMessage(holders.codeTitle)}
describe={formatMessage(holders.codeDesc)}
updateSection={this.onUpdateInviteIdSection}
/>
);
@@ -453,7 +547,12 @@ export default class GeneralTab extends React.Component {
if (this.props.activeSection === 'name') {
const inputs = [];
let teamNameLabel = 'Team Name';
let teamNameLabel = (
<FormattedMessage
id='general_tab.teamName'
defaultMessage='Team Name'
/>
);
if (Utils.isMobile()) {
teamNameLabel = '';
}
@@ -478,13 +577,13 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMax
title={`Team Name`}
title={formatMessage({id: 'general_tab.teamName'})}
inputs={inputs}
submit={this.handleNameSubmit}
server_error={serverError}
client_error={clientError}
updateSection={this.onUpdateNameSection}
extraInfo='Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
extraInfo={formatMessage(holders.teamNameInfo)}
/>
);
} else {
@@ -492,7 +591,7 @@ export default class GeneralTab extends React.Component {
nameSection = (
<SettingItemMin
title={`Team Name`}
title={formatMessage({id: 'general_tab.teamName'})}
describe={describe}
updateSection={this.onUpdateNameSection}
/>
@@ -515,14 +614,22 @@ export default class GeneralTab extends React.Component {
ref='title'
>
<i className='modal-back'></i>
{'General Settings'}
<FormattedMessage
id='general_tab.title'
defaultMessage='General Settings'
/>
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>{'General Settings'}</h3>
<h3 className='tab-header'>
<FormattedMessage
id='general_tab.title'
defaultMessage='General Settings'
/>
</h3>
<div className='divider-dark first'/>
{nameSection}
<div className='divider-light'/>
@@ -539,7 +646,10 @@ export default class GeneralTab extends React.Component {
}
GeneralTab.propTypes = {
intl: intlShape.isRequired,
updateSection: React.PropTypes.func.isRequired,
team: React.PropTypes.object.isRequired,
activeSection: React.PropTypes.string.isRequired
};
export default injectIntl(GeneralTab);

View File

@@ -4,7 +4,16 @@
import * as utils from '../utils/utils.jsx';
import SettingUpload from './setting_upload.jsx';
export default class TeamImportTab extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
const holders = defineMessages({
importSlack: {
id: 'team_import_tab.importSlack',
defaultMessage: 'Import from Slack (Beta)'
}
});
class TeamImportTab extends React.Component {
constructor(props) {
super(props);
@@ -32,16 +41,19 @@ export default class TeamImportTab extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
var uploadHelpText = (
<div>
<p>{'To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team\'\s public channels.'}</p>
<p>{'The Slack import to Mattermost is in "Beta". Slack bot posts do not yet import and Slack @mentions are not currently supported.'}</p>
<FormattedHTMLMessage
id='team_import_tab.importHelp'
defaultMessage="<p>To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.</p><p>The Slack import to Mattermost is in 'Beta'. Slack bot posts do not yet import and Slack @mentions are not currently supported.</p>"
/>
</div>
);
var uploadSection = (
<SettingUpload
title='Import from Slack (Beta)'
title={formatMessage(holders.importSlack)}
submit={this.doImportSlack}
helpText={uploadHelpText}
fileTypesAccepted='.zip'
@@ -56,19 +68,30 @@ export default class TeamImportTab extends React.Component {
break;
case 'in-progress':
messageSection = (
<p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>{' Importing...'}</p>
<p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>
<FormattedMessage
id='team_import_tab.importing'
defaultMessage=' Importing...'
/>
</p>
);
break;
case 'done':
messageSection = (
<p className='confirm-import alert alert-success'>
<i className='fa fa-check' />
{' Import successful: '}
<FormattedMessage
id='team_import_tab.successful'
defaultMessage=' Import successful: '
/>
<a
href={this.state.link}
download='MattermostImportSummary.txt'
>
{'View Summary'}
<FormattedMessage
id='team_import_tab.summary'
defaultMessage='View Summary'
/>
</a>
</p>
);
@@ -77,12 +100,18 @@ export default class TeamImportTab extends React.Component {
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-warning' />
{' Import failure: '}
<FormattedMessage
id='team_import_tab.failure'
defaultMessage=' Import failure: '
/>
<a
href={this.state.link}
download='MattermostImportSummary.txt'
>
{'View Summary'}
<FormattedMessage
id='team_import_tab.summary'
defaultMessage='View Summary'
/>
</a>
</p>
);
@@ -102,13 +131,23 @@ export default class TeamImportTab extends React.Component {
<h4
className='modal-title'
ref='title'
><i className='modal-back'></i>{'Import'}</h4>
><i className='modal-back'></i>
<FormattedMessage
id='team_import_tab.import'
defaultMessage='Import'
/>
</h4>
</div>
<div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>{'Import'}</h3>
<h3 className='tab-header'>
<FormattedMessage
id='team_import_tab.import'
defaultMessage='Import'
/>
</h3>
<div className='divider-dark first'/>
{uploadSection}
<div className='divider-dark'/>
@@ -118,3 +157,9 @@ export default class TeamImportTab extends React.Component {
);
}
}
TeamImportTab.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(TeamImportTab);

View File

@@ -4,6 +4,8 @@
import MemberListTeam from './member_list_team.jsx';
import TeamStore from '../stores/team_store.jsx';
import {FormattedMessage} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
export default class TeamMembersModal extends React.Component {
@@ -44,7 +46,13 @@ export default class TeamMembersModal extends React.Component {
onHide={this.props.onHide}
>
<Modal.Header closeButton={true}>
{team.display_name + ' Members'}
<FormattedMessage
id='team_member_modal.members'
defaultMessage='{team} Members'
values={{
team: team.display_name
}}
/>
</Modal.Header>
<Modal.Body ref='modalBody'>
<div className='team-member-list'>
@@ -57,7 +65,10 @@ export default class TeamMembersModal extends React.Component {
className='btn btn-default'
onClick={this.props.onHide}
>
{'Close'}
<FormattedMessage
id='team_member_modal.close'
defaultMessage='Close'
/>
</button>
</Modal.Footer>
</Modal>

View File

@@ -4,7 +4,24 @@
import SettingsSidebar from './settings_sidebar.jsx';
import TeamSettings from './team_settings.jsx';
export default class TeamSettingsModal extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
generalTab: {
id: 'team_settings_modal.generalTab',
defaultMessage: 'General'
},
importTab: {
id: 'team_settings_modal.importTab',
defaultMessage: 'Import'
},
exportTab: {
id: 'team_settings_modal.exportTab',
defaultMessage: 'Export'
}
});
class TeamSettingsModal extends React.Component {
constructor(props) {
super(props);
@@ -36,12 +53,13 @@ export default class TeamSettingsModal extends React.Component {
this.setState({activeSection: section});
}
render() {
const {formatMessage} = this.props.intl;
const tabs = [];
tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'});
tabs.push({name: 'general', uiName: formatMessage(holders.generalTab), icon: 'glyphicon glyphicon-cog'});
tabs.push({name: 'import', uiName: formatMessage(holders.importTab), icon: 'glyphicon glyphicon-upload'});
// To enable export uncomment this line
//tabs.push({name: 'export', uiName: 'Export', icon: 'glyphicon glyphicon-download'});
//tabs.push({name: 'export', uiName: formatMessage(holders.exportTab), icon: 'glyphicon glyphicon-download'});
return (
<div
@@ -67,7 +85,10 @@ export default class TeamSettingsModal extends React.Component {
className='modal-title'
ref='title'
>
{'Team Settings'}
<FormattedMessage
id='team_settings_modal.title'
defaultMessage='Team Settings'
/>
</h4>
</div>
<div className='modal-body'>
@@ -96,4 +117,7 @@ export default class TeamSettingsModal extends React.Component {
}
TeamSettingsModal.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(TeamSettingsModal);

View File

@@ -31,5 +31,5 @@ UnreadChannelIndicator.defaultProps = {
UnreadChannelIndicator.propTypes = {
show: React.PropTypes.bool,
extraClass: React.PropTypes.string,
text: React.PropTypes.string
text: React.PropTypes.object
};

View File

@@ -1,4 +1,13 @@
{
"about.teamEdtion": "Team Edition",
"about.enterpriseEdition": "Enterprise Edition",
"about.licensed": "Licensed by:",
"about.title": "About Mattermost",
"about.version": "Version:",
"about.number": "Build Number:",
"about.date": "Build Date:",
"about.hash": "Build Hash:",
"about.close": "Close",
"access_history.sessionRevoked": "The session with id {sessionId} was revoked",
"access_history.channelCreated": "Created the {channelName} channel/group",
"access_history.establishedDM": "Established a direct message channel with {username}",
@@ -446,6 +455,10 @@
"admin.team.save": "Save",
"admin.userList.title": "Users for {team}",
"admin.userList.title2": "Users for {team} ({count})",
"admin.user_item.confirmDemoteRoleTitle": "Confirm demotion from System Admin role",
"admin.user_item.confirmDemotion": "Confirm Demotion",
"admin.user_item.confirmDemoteDescription": "If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.",
"admin.user_item.confirmDemotionCmd": "platform -assign_role -team_name=\"yourteam\" -email=\"name@yourcompany.com\" -role=\"system_admin\"",
"admin.user_item.member": "Member",
"admin.user_item.sysAdmin": "System Admin",
"admin.user_item.teamAdmin": "Team Admin",
@@ -456,14 +469,17 @@
"admin.user_item.makeActive": "Make Active",
"admin.user_item.makeInactive": "Make Inactive",
"admin.user_item.resetPwd": "Reset Password",
"admin.user_item.confirmDemoteRoleTitle": "Confirm demotion from System Admin role",
"admin.user_item.confirmDemotion": "Confirm Demotion",
"admin.user_item.confirmDemoteDescription": "If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.",
"authorize.title": "An application would like to connect to your {teamName} account",
"authorize.app": "The app <strong>{appName}</strong> would like the ability to access and modify your basic information.",
"authorize.access": "Allow <strong>{appName}</strong> access?",
"authorize.deny": "Deny",
"authorize.allow": "Allow",
"change_url.longer": "Must be longer than two characters",
"change_url.startWithLetter": "Must start with a letter or number",
"change_url.endWithLetter": "Must end with a letter or number",
"change_url.noUnderscore": "Can not contain two underscores in a row.",
"change_url.invalidUrl": "Invalid URL",
"change_url.close": "Close",
"claim.account.noEmail": "No email specified",
"claim.email_to_sso.pwdError": "Please enter your password.",
"claim.email_to_sso.pwd": "Password",
@@ -494,6 +510,28 @@
"find_team.getLinks": "Get an email with links to any teams to which you are a member.",
"find_team.email": "Email",
"find_team.send": "Send",
"get_link.copy": "Copy Link",
"get_link.clipboard": " Link copied to clipboard.",
"get_link.close": "Close",
"get_team_invite_link_modal.title": "Team Invite Link",
"get_team_invite_link_modal.help": "Send teammates the link below for them to sign-up to this team site.",
"invite_member.emailError": "Please enter a valid email address",
"invite_member.firstname": "First name",
"invite_member.lastname": "Last name",
"invite_member.modalTitle": "Discard Invitations?",
"invite_member.modalMessage": "You have unsent invitations, are you sure you want to discard them?",
"invite_member.modalButton": "Yes, Discard",
"invite_member.addAnother": "Add another",
"invite_member.autoJoin": "People invited automatically join the <strong>{channel}</strong> channel.",
"invite_member.send": "Send Invitation",
"invite_member.sending": " Sending",
"invite_member.send2": "Send Invitations",
"invite_member.inviteLink": "Team Invite Link",
"invite_member.teamInviteLink": "You can also invite people using the {link}.",
"invite_member.content": "Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.",
"invite_member.disabled": "User creation has been disabled for your team. Please ask your team administrator for details.",
"invite_member.newMember": "Invite New Member",
"invite_member.cancel": "Cancel",
"loading_screen.loading": "Loading",
"login_email.badTeam": "Bad team name",
"login_email.emailReq": "An email is required",
@@ -519,6 +557,64 @@
"login.find": "Find your other teams",
"login.signTo": "Sign in to:",
"login.on": "on {siteName}",
"member_team_item.member": "Member",
"member_team_item.systemAdmin": "System Admin",
"member_team_item.teamAdmin": "Team Admin",
"member_team_item.inactive": "Inactive",
"member_team_item.makeAdmin": "Make Team Admin",
"member_team_item.makeMember": "Make Member",
"member_team_item.makeActive": "Make Active",
"member_team_item.makeInactive": "Make Inactive",
"more_channels.join": "Join",
"more_channels.noMore": "No more channels to join",
"more_channels.createClick": "Click 'Create New Channel' to make a new one",
"more_channels.close": "Close",
"more_channels.title": "More Channels",
"more_channels.create": "Create New Channel",
"more_direct_channels.member": "Member",
"more_direct_channels.search": "Search members",
"more_direct_channels.message": "Message",
"more_direct_channels.notFound": "No users found :(",
"more_direct_channels.count": "{count} {member}",
"more_direct_channels.countTotal": "{count} {member} of {total} Total",
"more_direct_channels.title": "Direct Messages",
"more_direct_channels.close": "Close",
"navbar_dropdown.inviteMember": "Invite New Member",
"navbar_dropdown.teamLink": "Get Team Invite Link",
"navbar_dropdown.manageMembers": "Manage Members",
"navbar_dropdown.teamSettings": "Team Settings",
"navbar_dropdown.console": "System Console",
"navbar_dropdown.switchTeam": "Switch to {team}",
"navbar_dropdown.create": "Create a New Team",
"navbar_dropdown.help": "Help",
"navbar_dropdown.report": "Report a Problem",
"navbar_dropdown.accountSettings": "Account Settings",
"navbar_dropdown.logout": "Logout",
"navbar_dropdown.about": "About Mattermost",
"channel_flow.invalidName": "Invalid Channel Name",
"channel_flow.alreadyExist": "A channel with that URL already exists",
"channel_flow.channel": "Channel",
"channel_flow.group": "Group",
"channel_flow.changeUrlTitle": "Change {term} URL",
"channel_flow.set_url_title": "Set {term} URL",
"channel_flow.create": "Create {term}",
"channel_flow.changeUrlDescription": "Some characters are not allowed in URLs and may be removed.",
"channel_modal.nameEx": "E.g.: \"Bugs\", \"Marketing\", \"办公室恋情\"",
"channel_modal.displayNameError": "This field is required",
"channel_modal.group": "Group",
"channel_modal.privateGroup1": "Create a new private group with restricted membership. ",
"channel_modal.publicChannel1": "Create a public channel",
"channel_modal.channel": "Channel",
"channel_modal.publicChannel2": "Create a new public channel anyone can join. ",
"channel_modal.privateGroup2": "Create a private group",
"channel_modal.modalTitle": "New ",
"channel_modal.name": "Name",
"channel_modal.edit": "Edit",
"channel_modal.purpose": "Purpose",
"channel_modal.optional": "(optional)",
"channel_modal.descriptionHelp": "Describe how this {term} should be used.",
"channel_modal.cancel": "Cancel",
"channel_modal.createNew": "Create New ",
"password_form.error": "Please enter at least {chars} characters.",
"password_form.update": "Your password has been updated successfully.",
"password_form.pwd": "Password",
@@ -559,6 +655,23 @@
"setting_picture.help": "Upload a profile picture in either JPG or PNG format, at least {width}px in width and {height}px height.",
"setting_picture.select": "Select",
"setting_picture.cancel": "Cancel",
"setting_upload.noFile": "No file selected.",
"setting_upload.select": "Select file",
"setting_upload.import": "Import",
"sidebar_header.tutorial": "<h4>Main Menu</h4>\n <p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p>\n <p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>",
"sidebar.tutorialScreen1": "<h4>Channels</h4><p><strong>Channels</strong> organize conversations across different topics. Theyre open to everyone on your team. To send private communications use <strong>Direct Messages</strong> for a single person or <strong>Private Groups</strong> for multiple people.</p>",
"sidebar.tutorialScreen2": "<h4>\"Town Square\" and \"Off-Topic\" channels</h4>\n <p>Here are two public channels to start:</p>\n <p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p>\n <p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>",
"sidebar.tutorialScreen3": "<h4>Creating and Joining Channels</h4>\n <p>Click <strong>\"More...\"</strong> to create a new channel or join an existing one.</p>\n <p>You can also create a new channel or private group by clicking the <strong>\"+\" symbol</strong> next to the channel or private group header.</p>",
"sidebar.removeList": "Remove from list",
"sidebar.more": "More ({count})",
"sidebar.createChannel": "Create new channel",
"sidebar.createGroup": "Create new group",
"sidebar.unreadAbove": "Unread post(s) above",
"sidebar.unreadBelow": "Unread post(s) below",
"sidebar.channels": "Channels",
"sidebar.moreElips": "More...",
"sidebar.pg": "Private Groups",
"sidebar.direct": "Direct Messages",
"signup_team_complete.completed": "You've already completed the signup process for this invitation or this invitation has expired.",
"signup_team_confirm.title": "Sign up Complete",
"signup_team_confirm.checkEmail": "Please check your email: <strong>{email}</strong><br />Your email contains a link to set up your team",
@@ -589,6 +702,43 @@
"suggestion.mention.channel": "Notifies everyone in the channel",
"suggestion.search.public": "Public Channels",
"suggestion.search.private": "Public Groups",
"team_export_tab.exporting": " Exporting...",
"team_export_tab.ready": " Ready for ",
"team_export_tab.download": "download",
"team_export_tab.unable": " Unable to export: {error}",
"team_export_tab.export": "Export",
"team_export_tab.exportTeam": "Export your team",
"general_tab.dirDisabled": "Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.",
"general_tab.required": "This field is required",
"general_tab.chooseName": "Please choose a new name for your team",
"general_tab.includeDirTitle": "Include this team in the Team Directory",
"general_tab.yes": "Yes",
"general_tab.no": "No",
"general_tab.dirOff": "Team directory is turned off for this system.",
"general_tab.openInviteTitle": "Allow anyone to sign-up from login page",
"general_tab.codeTitle": "Invite Code",
"general_tab.codeDesc": "Click 'Edit' to regenerate Invite Code.",
"general_tab.teamNameInfo": "Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.",
"general_tab.includeDirDesc": "Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.",
"general_tab.dirContact": "Contact your system administrator to turn on the team directory on the system home page.",
"general_tab.openInviteDesc": "When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.",
"general_tab.regenerate": "Re-Generate",
"general_tab.codeLongDesc": "The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.",
"general_tab.teamName": "Team Name",
"general_tab.title": "General Settings",
"team_import_tab.importSlack": "Import from Slack (Beta)",
"team_import_tab.importHelp": "<p>To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.</p><p>The Slack import to Mattermost is in 'Beta'. Slack bot posts do not yet import and Slack @mentions are not currently supported.</p>",
"team_import_tab.importing": " Importing...",
"team_import_tab.successful": " Import successful: ",
"team_import_tab.summary": "View Summary",
"team_import_tab.failure": " Import failure: ",
"team_import_tab.import": "Import",
"team_member_modal.members": "{team} Members",
"team_member_modal.close": "Close",
"team_settings_modal.generalTab": "General",
"team_settings_modal.importTab": "Import",
"team_settings_modal.exportTab": "Export",
"team_settings_modal.title": "Team Settings",
"choose_auth_page.gitlabCreate": "Create new team with GitLab Account",
"choose_auth_page.googleCreate": "Create new team with Google Apps Account",
"choose_auth_page.emailCreate": "Create new team with email address",

View File

@@ -1,4 +1,13 @@
{
"about.close": "Cerrar",
"about.date": "Fecha de compilación:",
"about.enterpriseEdition": "Edición Enterprise",
"about.hash": "Hash de compilación:",
"about.licensed": "Licenciado por:",
"about.number": "Número de compilación:",
"about.teamEdtion": "Edición Team",
"about.title": "Acerca de Mattermost",
"about.version": "Versión:",
"access_history.accountActive": "La cuenta se ha activado",
"access_history.accountInactive": "La cuenta se ha desactivado",
"access_history.attemptedAllowOAuthAccess": "Intento para permitir acceso a un nuevo servicio de OAuth",
@@ -446,24 +455,55 @@
"admin.team_analytics.totalPosts": "Total de Mensajes",
"admin.userList.title": "Usuarios para ",
"admin.userList.title2": "Usuarios para {team} ({count})",
"admin.user_item.confirmDemoteDescription": "Si te degradas a ti mismo de la función de Administrador de Sistema y no hay otro usuario con privilegios de Administrador de Sistema, tendrás que volver a asignar un Administrador del Sistema accediendo al servidor de Mattermost a través de un terminal y ejecutar el siguiente comando.",
"admin.user_item.confirmDemoteRoleTitle": "Confirmar el decenso del rol de Administrador de Sistema",
"admin.user_item.confirmDemotion": "Confirmar decenso",
"admin.user_item.confirmDemotionCmd": "platform -assign_role -team_name=\"tuequipo\" -email=\"nombre@tuempresa.com\" -role=\"system_admin\"",
"admin.user_item.inactive": "Inactivo",
"admin.user_item.makeActive": "Activar",
"admin.user_item.makeInactive": "Inactivar",
"admin.user_item.makeMember": "Hacer Miembro",
"admin.user_item.makeSysAdmin": "Hacer Admin del Sistema",
"admin.user_item.makeTeamAdmin": "Hacer Admin de Equipo",
"admin.user_item.member": "Meiembro",
"admin.user_item.member": "Miembro",
"admin.user_item.resetPwd": "Reiniciar Contraseña",
"admin.user_item.sysAdmin": "Admin de Sistema",
"admin.user_item.teamAdmin": "Admin de Equipo",
"admin.user_item.confirmDemoteRoleTitle": "Confirm demotion from System Admin role",
"admin.user_item.confirmDemotion": "Confirm Demotion",
"admin.user_item.confirmDemoteDescription": "If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.",
"authorize.access": "¿Permitir acceso a {appName}?",
"authorize.allow": "Permitir",
"authorize.app": "La app {appName} quiere tener la abilidad de accesar y modificar tu información básica.",
"authorize.deny": "Denegar",
"authorize.title": "Una aplicación quiere conectarse con tu cuenta de {teamName}",
"change_url.close": "Cerrar",
"change_url.endWithLetter": "Debe terminar con una letra o número",
"change_url.invalidUrl": "URL Inválida",
"change_url.longer": "Debe ser mayor a 2 caracteres",
"change_url.noUnderscore": "No puede tener dos guíones bajos seguidos",
"change_url.startWithLetter": "Debe comenzar con una letra o número",
"channel_flow.alreadyExist": "Un canal con este identificador ya existe",
"channel_flow.changeUrlDescription": "Algunos caracteres no están permitidos en las URLs y pueden ser removidos.",
"channel_flow.changeUrlTitle": "Cambiar URL de {term}",
"channel_flow.channel": "Canal",
"channel_flow.create": "Crear {term}",
"channel_flow.group": "Grupo",
"channel_flow.invalidName": "Nombre de Canal Inválido",
"channel_flow.set_url_title": "Asignar URL de {term}",
"channel_modal.cancel": "Cancelar",
"channel_modal.channel": "Canal",
"channel_modal.createNew": "Crear Nuevo ",
"channel_modal.descriptionHelp": "Describe como este {term} debería ser utilizado.",
"channel_modal.displayNameError": "Este campo es obligatorio",
"channel_modal.edit": "Editar",
"channel_modal.group": "Grupo",
"channel_modal.modalTitle": "Nuevo ",
"channel_modal.name": "Nombre",
"channel_modal.nameEx": "Ej: \"Errores\", \"Mercadeo\", \"办公室恋情\"",
"channel_modal.optional": "(opcional)",
"channel_modal.privateGroup1": "Crear un grupo privado con acceso restringido. ",
"channel_modal.privateGroup2": "Crear un grupo privado",
"channel_modal.publicChannel1": "Crear un canal público",
"channel_modal.publicChannel2": "Crear un canal público al que cualquiera puede unirse. ",
"channel_modal.purpose": "Propósito",
"choose_auth_page.emailCreate": "Crea un nuevo equipo con tu cuenta de correo",
"choose_auth_page.find": "Encontrar mi equipo",
"choose_auth_page.gitlabCreate": "Crear un nuevo equipo con una cuenta de GitLab",
@@ -503,6 +543,47 @@
"find_team.placeholder": "tu@ejemplo.com",
"find_team.send": "Enviar",
"find_team.submitError": "Por favor ingresa una dirección válida",
"general_tab.chooseName": "Por favor escoge otro nombre para tu equipo",
"general_tab.codeDesc": "Pincha 'Editar' para regenerar el código de Invitación.",
"general_tab.codeLongDesc": "El Código de Invitación es utilizado como parte del URL del enlace creado por la opción **Obtener Enlace de invitación** en el menú principal. Regenerar este código crea un nuevo enlace e invalida los enlaces anteriores.",
"general_tab.codeTitle": "Código de Invitación",
"general_tab.dirContact": "Contácta a un administrador del sistema para habilitar el directorio de equipos en la página principal.",
"general_tab.dirDisabled": "El directorio de Equipos ha sido deshabilitado. Por favor solicita a un Administrador de Sistema que habilite la opción de Directorio de Equipos en la Consola del Sistema.",
"general_tab.dirOff": "El directorio de Equipos ha sido deshabilitado para este sistema.",
"general_tab.includeDirDesc": "Incluir este equipo mostrará el nombre del equipo en la sección de Directorio de Equipos en la página de inicio, y proveerá un enlace para la página de inicio de sesión.",
"general_tab.includeDirTitle": "Incluir este Equipo en el Directorio de Equipos",
"general_tab.no": "No",
"general_tab.openInviteDesc": "Cuando está permitido, un enlace para la creación de cuentas será incluido en la página de registro de este equipo y permitirá a cualquier visitante registrarse.",
"general_tab.openInviteTitle": "Permitir a cualquiera a inscribirse desde la página de inicio de sesión",
"general_tab.regenerate": "Regenerar",
"general_tab.required": "Este campo es obligatorio",
"general_tab.teamName": "Nombre del Equipo",
"general_tab.teamNameInfo": "Asigna el nombre del equipo como aparecerá en la página de inicio de sesión y en la parte superior izquierda de la barra lateral.",
"general_tab.title": "Configuración General",
"general_tab.yes": "Sí",
"get_link.clipboard": " Enlace copiado al portapapeles.",
"get_link.close": "Cerrar",
"get_link.copy": "Copiar Enlace",
"get_team_invite_link_modal.help": "Enviar a los compañeros de equipo el enlace que se muestra a continuación para permitirles registrarse a este equipo.",
"get_team_invite_link_modal.title": "Enlace de Invitación al Equipo",
"invite_member.addAnother": "Agregar otro",
"invite_member.autoJoin": "Las personas invitadas se unirán automáticamente al canal <strong>{channel}</strong>.",
"invite_member.cancel": "Cancelar",
"invite_member.content": "El envio de correos está actualmente desactivado para tu equipo, por lo que no puedes enviar invitaciones.",
"invite_member.disabled": "La creación de usuarios ha sido deshabilitada para tu equipo. Por favor consulta con un administrador de tu equipo.",
"invite_member.emailError": "Por favor ingresa un correo electrónico válido",
"invite_member.firstname": "Nombre",
"invite_member.inviteLink": "Enlace de Invitación al Equipo",
"invite_member.lastname": "Apellido",
"invite_member.modalButton": "Sí, Borrar",
"invite_member.modalMessage": "Tienes invitaciones sin usar, estás seguro que quieres borrarlas?",
"invite_member.modalTitle": "¿Descartar Invitaciones?",
"invite_member.newMember": "Invitar nuevo Miembro",
"invite_member.send": "Enviar Invitaciones",
"invite_member.send2": "Enviar Invitaciones",
"invite_member.sending": " Enviando",
"invite_member.teamInvite": "Invitación de Equipo",
"invite_member.teamInviteLink": "También puedes invitar personas usando el {link}.",
"loading_screen.loading": "Cargando",
"login.changed": " Cambiado el método de inicio de sesión satisfactoriamente",
"login.create": "Crea una ahora",
@@ -528,6 +609,40 @@
"login_ldap.pwdReq": "La contraseña LDAP es obligatoria",
"login_ldap.signin": "Entrar",
"login_ldap.username": "Usuario LDAP",
"member_team_item.inactive": "Inactivo",
"member_team_item.makeActive": "Activar",
"member_team_item.makeAdmin": "Convertir a Admin de Equipo",
"member_team_item.makeInactive": "Desactivar",
"member_team_item.makeMember": "Convertir en Miembro",
"member_team_item.member": "Miembro",
"member_team_item.systemAdmin": "Administrador de Sistema",
"member_team_item.teamAdmin": "Admin de Equipo",
"more_channels.close": "Cerrar",
"more_channels.create": "Crear Nuevo Canal",
"more_channels.createClick": "Pincha 'Crear Nuevo Canal' para crear uno nuevo",
"more_channels.join": "Unirme",
"more_channels.noMore": "No hay más canales para unirse",
"more_channels.title": "Más Canales",
"more_direct_channels.close": "Cerrar",
"more_direct_channels.count": "{count} {member}",
"more_direct_channels.countTotal": "{count} {member} de {total} Total",
"more_direct_channels.member": "Miembro",
"more_direct_channels.message": "Mensaje",
"more_direct_channels.notFound": "No se encontraron usuarios :(",
"more_direct_channels.search": "Buscar miembros",
"more_direct_channels.title": "Mensajes Directos",
"navbar_dropdown.about": "Acerca de Mattermost",
"navbar_dropdown.accountSettings": "Configurar Cuenta",
"navbar_dropdown.console": "Consola de Sistema",
"navbar_dropdown.create": "Crear nuevo Equipo",
"navbar_dropdown.help": "Ayuda",
"navbar_dropdown.inviteMember": "Invitar Nuevo Miembro",
"navbar_dropdown.logout": "Cerrar sesión",
"navbar_dropdown.manageMembers": "Administrar Miembros",
"navbar_dropdown.report": "Reportar un Problema",
"navbar_dropdown.switchTeam": "Cambiar a {team}",
"navbar_dropdown.teamLink": "Enlace invitación al equipo",
"navbar_dropdown.teamSettings": "Configurar Equipo",
"password_form.change": "Cambiar mi contraseña",
"password_form.click": " Pincha <a href={url}>aquí</a> para iniciar sesión.",
"password_form.enter": "Ingresa una nueva contraseña para tu cuenta en {teamDisplayName} {SiteName}.",
@@ -568,6 +683,23 @@
"setting_picture.help": "Sube una imagen de tu perfil en formato JPG o PNG de al menos {width}px de ancho y {height}px de alto.",
"setting_picture.save": "Guardar",
"setting_picture.select": "Selecciona",
"setting_upload.import": "Importar",
"setting_upload.noFile": "No ha seleccionado un archivo",
"setting_upload.select": "Selecciona un archivo",
"sidebar.channels": "Canales",
"sidebar.createChannel": "Crear un nuevo canal",
"sidebar.createGroup": "Crear un nuevo grupo",
"sidebar.direct": "Mensajes Directos",
"sidebar.more": "Más ({count})",
"sidebar.moreElips": "Más...",
"sidebar.pg": "Grupos Privados",
"sidebar.removeList": "Remover de la lista",
"sidebar.tutorialScreen1": "<h4>Canales</h4><p><strong>Canales</strong> organizan las conversaciones en diferentes tópicos. Son abiertos para cualquier persona de tu equipo. Para enviar comunicaciones privadas con una sola persona utiliza <strong>Mensajes Directos</strong> o con multiples personas utilizando <strong>Grupos Privados</strong>.</p>",
"sidebar.tutorialScreen2": "<h4>Canal \"General\"</h4><p>Este es un canal para comenzar:</p><p><strong>General</strong> es el lugar para tener comunicación con todo el equipo. Todos los integrantes de tu equipo son miembros de este canal.</p>",
"sidebar.tutorialScreen3": "<h4>Creando y Uniendose a Canales</h4><p>Pincha en <strong>\"Más...\"</strong> para crear un nuevo canal o unirte a uno existente.</p><p>También puedes crear un nuevo canal o grupo privado al pinchar el simbolo de <strong>\"+\"</strong> que se encuentra al lado del encabezado de Canales o Grupos Privados.</p>",
"sidebar.unreadAbove": "Mensaje(s) sin leer arriba",
"sidebar.unreadBelow": "Mensaje(s) sin leer abajo",
"sidebar_header.tutorial": "<h4>Menú Principal</h4><p>El <strong>Menú Principal</strong> es donde puedes <strong>Invitar a nuevos miembros</strong>, podrás <strong>Configurar tu Cuenta</strong> y seleccionar un <strong>Tema</strong> para personalizar la apariencia.</p><p>Los administradores del Equipo podrán <strong>Configurar el Equipo</strong> desde este menú.</p><p>Los administradores del Sistema encontrarán una opción para ir a la <strong>Consola de Sistema</strong> para administrar el sistema completo.</p>",
"signup_team.choose": "Selecciona un Equipo",
"signup_team.createTeam": "O Crea un Equipo",
"signup_team.disabled": "La creación de Equipos ha sido deshabilitada.",
@@ -604,6 +736,25 @@
"suggestion.mention.channel": "Notifica a todas las personas en el canal",
"suggestion.search.private": "Grupos Privados",
"suggestion.search.public": "Canales Públicos",
"team_export_tab.download": "descargar",
"team_export_tab.export": "Exportar",
"team_export_tab.exportTeam": "Exportar tu equipo",
"team_export_tab.exporting": " Exportando...",
"team_export_tab.ready": " Listo para ",
"team_export_tab.unable": " No se pudo exportar: {error}",
"team_import_tab.failure": " Fallo al importar: ",
"team_import_tab.import": "Importar",
"team_import_tab.importHelp": "<p>Para importar un equipo desde Slack dirigete a Slack > Team Settings > Import/Export Data > Export > Start Export. Slack no permite exportar archivos, imágenes, grupos privados o mensajes directos almacenados en Slack. Por ende, La importación desde Slack hacia Mattermost sólo soporta la importación de los mensajes de texto de los canales públicos de tu equipo.</p><p>La importación desde Slack hacia Mattermost está en fase \"Beta\". Los mensajes enviados por Bots en y las @menciones actualmente no son soportados.</p>",
"team_import_tab.importSlack": "Importar desde Slack (Beta)",
"team_import_tab.importing": " Importando...",
"team_import_tab.successful": " Importado con éxito: ",
"team_import_tab.summary": "Ver Resumen",
"team_member_modal.close": "Cerrar",
"team_member_modal.members": "{team} Miembros",
"team_settings_modal.exportTab": "Exportar",
"team_settings_modal.generalTab": "General",
"team_settings_modal.importTab": "Importar",
"team_settings_modal.title": "Configuración del Equipo",
"team_signup_display_name.back": "Volver al paso previo",
"team_signup_display_name.charLength": "El nombre debe tener 4 o más caracteres hasta un máximo de 15",
"team_signup_display_name.nameHelp": "Nombre tu equipo en cualquier idioma. El nombre de tu equipo aparecerá en menús y en inicios",