Merge branch 'master' into PLT-1429

Этот коммит содержится в:
=Corey Hulen
2016-02-03 12:11:02 -08:00
родитель bdfa4715d6 2b4af8d8c7
Коммит d3a965a362
28 изменённых файлов: 798 добавлений и 329 удалений

Просмотреть файл

@@ -339,7 +339,10 @@ class EmailSettings extends React.Component {
defaultChecked={this.props.config.EmailSettings.EnableSignInWithEmail}
onChange={this.handleChange.bind(this, 'allowSignInWithEmail_true')}
/>
{'true'}
<FormattedMessage
id='admin.email.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -349,7 +352,10 @@ class EmailSettings extends React.Component {
defaultChecked={!this.props.config.EmailSettings.EnableSignInWithEmail}
onChange={this.handleChange.bind(this, 'allowSignInWithEmail_false')}
/>
{'false'}
<FormattedMessage
id='admin.email.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>
<FormattedMessage
@@ -380,7 +386,10 @@ class EmailSettings extends React.Component {
defaultChecked={this.props.config.EmailSettings.EnableSignInWithUsername}
onChange={this.handleChange.bind(this, 'allowSignInWithUsername_true')}
/>
{'true'}
<FormattedMessage
id='admin.email.true'
defaultMessage='true'
/>
</label>
<label className='radio-inline'>
<input
@@ -390,7 +399,10 @@ class EmailSettings extends React.Component {
defaultChecked={!this.props.config.EmailSettings.EnableSignInWithUsername}
onChange={this.handleChange.bind(this, 'allowSignInWithUsername_false')}
/>
{'false'}
<FormattedMessage
id='admin.email.false'
defaultMessage='false'
/>
</label>
<p className='help-text'>
<FormattedMessage

Просмотреть файл

@@ -164,7 +164,7 @@ class LdapSettings extends React.Component {
<div className='banner__content'>
<FormattedHTMLMessage
id='admin.ldap.noLicense'
defaultMessage='<h4 className="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
defaultMessage='<h4 class="banner__heading">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
/>
</div>
</div>

Просмотреть файл

@@ -75,6 +75,7 @@ export default class AudioVideoPreview extends React.Component {
filename={this.props.filename}
fileUrl={this.props.fileUrl}
fileInfo={this.props.fileInfo}
formatMessage={this.props.formatMessage}
/>
);
}
@@ -110,5 +111,6 @@ AudioVideoPreview.propTypes = {
filename: React.PropTypes.string.isRequired,
fileUrl: React.PropTypes.string.isRequired,
fileInfo: React.PropTypes.object.isRequired,
maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired
maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired,
formatMessage: React.PropTypes.func.isRequired
};

Просмотреть файл

@@ -15,6 +15,8 @@ import UserStore from '../stores/user_store.jsx';
import * as Utils from '../utils/utils.jsx';
import {FormattedMessage} from 'mm-intl';
import Constants from '../utils/constants.jsx';
const TutorialSteps = Constants.TutorialSteps;
const Preferences = Constants.Preferences;
@@ -69,8 +71,11 @@ export default class CenterPanel extends React.Component {
onClick={handleClick}
>
<a href=''>
{'Click here to jump to recent messages. '}
{<i className='fa fa-arrow-down'></i>}
<FormattedMessage
id='center_panel.recent'
defaultMessage='Click here to jump to recent messages. '
/>
<i className='fa fa-arrow-down'></i>
</a>
</div>
);

Просмотреть файл

@@ -15,7 +15,40 @@ import PreferenceStore from '../stores/preference_store.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
export default class ChannelLoader extends React.Component {
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const holders = defineMessages({
socketError: {
id: 'channel_loader.socketError',
defaultMessage: 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.'
},
someone: {
id: 'channel_loader.someone',
defaultMessage: 'Someone'
},
posted: {
id: 'channel_loader.posted',
defaultMessage: 'Posted'
},
uploadedImage: {
id: 'channel_loader.uploadedImage',
defaultMessage: ' uploaded an image'
},
uploadedFile: {
id: 'channel_loader.uploadedFile',
defaultMessage: ' uploaded a file'
},
something: {
id: 'channel_loader.something',
defaultMessage: ' did something new'
},
wrote: {
id: 'channel_loader.wrote',
defaultMessage: ' wrote: '
}
});
class ChannelLoader extends React.Component {
constructor(props) {
super(props);
@@ -23,6 +56,17 @@ export default class ChannelLoader extends React.Component {
this.onSocketChange = this.onSocketChange.bind(this);
const {formatMessage} = this.props.intl;
SocketStore.setTranslations({
socketError: formatMessage(holders.socketError),
someone: formatMessage(holders.someone),
posted: formatMessage(holders.posted),
uploadedImage: formatMessage(holders.uploadedImage),
uploadedFile: formatMessage(holders.uploadedFile),
something: formatMessage(holders.something),
wrote: formatMessage(holders.wrote)
});
this.state = {};
}
componentDidMount() {
@@ -126,3 +170,9 @@ export default class ChannelLoader extends React.Component {
return <div/>;
}
}
ChannelLoader.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(ChannelLoader);

Просмотреть файл

@@ -21,12 +21,33 @@ import SocketStore from '../stores/socket_store.jsx';
import Constants from '../utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedHTMLMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
const ActionTypes = Constants.ActionTypes;
const KeyCodes = Constants.KeyCodes;
export default class CreatePost extends React.Component {
const holders = defineMessages({
comment: {
id: 'create_post.comment',
defaultMessage: 'Comment'
},
post: {
id: 'create_post.post',
defaultMessage: 'Post'
},
write: {
id: 'create_post.write',
defaultMessage: 'Write a message...'
},
deleteMsg: {
id: 'create_post.deleteMsg',
defaultMessage: '(message deleted)'
}
});
class CreatePost extends React.Component {
constructor(props) {
super(props);
@@ -49,6 +70,7 @@ export default class CreatePost extends React.Component {
this.sendMessage = this.sendMessage.bind(this);
PostStore.clearDraftUploads();
PostStore.deleteMessage(this.props.intl.formatMessage(holders.deleteMsg));
const draft = this.getCurrentDraft();
@@ -356,7 +378,8 @@ export default class CreatePost extends React.Component {
if (!lastPost) {
return;
}
var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post';
const {formatMessage} = this.props.intl;
var type = (lastPost.root_id && lastPost.root_id.length > 0) ? formatMessage(holders.comment) : formatMessage(holders.post);
AppDispatcher.handleViewAction({
type: ActionTypes.RECIEVED_EDIT_POST,
@@ -374,9 +397,10 @@ export default class CreatePost extends React.Component {
screens.push(
<div>
<h4>{'Sending Messages'}</h4>
<p>{'Type here to write a message and press '}<strong>{'Enter'}</strong>{' to post it.'}</p>
<p>{'Click the '}<strong>{'Attachment'}</strong>{' button to upload an image or a file.'}</p>
<FormattedHTMLMessage
id='create_post.tutorialTip'
defaultMessage='<h4>Sending Messages</h4><p>Type here to write a message and press <strong>Enter</strong> to post it.</p><p>Click the <strong>Attachment</strong> button to upload an image or a file.</p>'
/>
</div>
);
@@ -440,7 +464,7 @@ export default class CreatePost extends React.Component {
onKeyDown={this.handleKeyDown}
onHeightChange={this.resizePostHolder}
messageText={this.state.messageText}
createMessage='Write a message...'
createMessage={this.props.intl.formatMessage(holders.write)}
channelId={this.state.channelId}
id='post_textbox'
ref='textbox'
@@ -477,3 +501,9 @@ export default class CreatePost extends React.Component {
);
}
}
CreatePost.propTypes = {
intl: intlShape.isRequired
};
export default injectIntl(CreatePost);

Просмотреть файл

@@ -5,7 +5,16 @@ import * as utils from '../utils/utils.jsx';
import * as Client from '../utils/client.jsx';
import Constants from '../utils/constants.jsx';
export default class FileAttachment extends React.Component {
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const holders = defineMessages({
download: {
id: 'file_attachment.download',
defaultMessage: 'Download'
}
});
class FileAttachment extends React.Component {
constructor(props) {
super(props);
@@ -266,7 +275,7 @@ export default class FileAttachment extends React.Component {
href={fileUrl}
download={filenameString}
data-toggle='tooltip'
title={'Download \"' + filenameString + '\"'}
title={this.props.intl.formatMessage(holders.download) + ' \"' + filenameString + '\"'}
className='post-image__name'
>
{trimmedFilename}
@@ -291,6 +300,7 @@ export default class FileAttachment extends React.Component {
}
FileAttachment.propTypes = {
intl: intlShape.isRequired,
// a list of file pathes displayed by the parent FileAttachmentList
filename: React.PropTypes.string.isRequired,
@@ -301,3 +311,5 @@ FileAttachment.propTypes = {
// handler for when the thumbnail is clicked passed the index above
handleImageClick: React.PropTypes.func
};
export default injectIntl(FileAttachment);

Просмотреть файл

@@ -3,15 +3,28 @@
import * as Utils from '../utils/utils.jsx';
export default function FileInfoPreview({filename, fileUrl, fileInfo}) {
import {defineMessages} from 'mm-intl';
const holders = defineMessages({
type: {
id: 'file_info_preview.type',
defaultMessage: 'File type '
},
size: {
id: 'file_info_preview.size',
defaultMessage: 'Size '
}
});
export default function FileInfoPreview({filename, fileUrl, fileInfo, formatMessage}) {
// non-image files include a section providing details about the file
const infoParts = [];
if (fileInfo.extension !== '') {
infoParts.push('File type ' + fileInfo.extension.toUpperCase());
infoParts.push(formatMessage(holders.type) + fileInfo.extension.toUpperCase());
}
infoParts.push('Size ' + Utils.fileSizeToString(fileInfo.size));
infoParts.push(formatMessage(holders.size) + Utils.fileSizeToString(fileInfo.size));
const infoString = infoParts.join(', ');

Просмотреть файл

@@ -89,9 +89,9 @@ export default class LoginUsername extends React.Component {
}
},
(err) => {
if (err.message === 'api.user.login.not_verified.app_error') {
if (err.id === 'api.user.login.not_verified.app_error') {
state.serverError = formatMessage(holders.verifyEmailError);
} else if (err.message === 'store.sql_user.get_by_username.app_error') {
} else if (err.id === 'store.sql_user.get_by_username.app_error') {
state.serverError = formatMessage(holders.userNotFoundError);
} else {
state.serverError = err.message;

Просмотреть файл

@@ -24,6 +24,8 @@ import Constants from '../utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import {FormattedMessage} from 'mm-intl';
const Popover = ReactBootstrap.Popover;
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -133,7 +135,10 @@ export default class Navbar extends React.Component {
dialogType={ChannelInfoModal}
dialogProps={{channel}}
>
{'View Info'}
<FormattedMessage
id='navbar.viewInfo'
defaultMessage='View Info'
/>
</ToggleModalButton>
</li>
);
@@ -145,7 +150,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={this.showEditChannelHeaderModal}
>
{'Set Channel Header...'}
<FormattedMessage
id='navbar.setHeader'
defaultMessage='Set Channel Header...'
/>
</a>
</li>
);
@@ -159,7 +167,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={() => this.setState({showEditChannelPurposeModal: true})}
>
{'Set Channel Purpose...'}
<FormattedMessage
id='navbar.setPurpose'
defaultMessage='Set Channel Purpose...'
/>
</a>
</li>
);
@@ -175,7 +186,10 @@ export default class Navbar extends React.Component {
dialogType={ChannelInviteModal}
dialogProps={{channel}}
>
{'Add Members'}
<FormattedMessage
id='navbar.addMembers'
defaultMessage='Add Members'
/>
</ToggleModalButton>
</li>
);
@@ -187,7 +201,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={this.handleLeave}
>
{'Leave Channel'}
<FormattedMessage
id='navbar.leave'
defaultMessage='Leave Channel'
/>
</a>
</li>
);
@@ -205,7 +222,10 @@ export default class Navbar extends React.Component {
href='#'
onClick={() => this.setState({showMembersModal: true})}
>
{'Manage Members'}
<FormattedMessage
id='navbar.manageMembers'
defaultMessage='Manage Members'
/>
</a>
</li>
);
@@ -217,7 +237,10 @@ export default class Navbar extends React.Component {
dialogType={DeleteChannelModal}
dialogProps={{channel}}
>
{'Delete Channel...'}
<FormattedMessage
id='navbar.delete'
defaultMessage='Delete Channel...'
/>
</ToggleModalButton>
</li>
);
@@ -234,7 +257,10 @@ export default class Navbar extends React.Component {
data-name={channel.name}
data-channelid={channel.id}
>
{'Rename Channel...'}
<FormattedMessage
id='navbar.rename'
defaultMessage='Rename Channel...'
/>
</a>
</li>
);
@@ -249,7 +275,10 @@ export default class Navbar extends React.Component {
dialogType={ChannelNotificationsModal}
dialogProps={{channel}}
>
{'Notification Preferences'}
<FormattedMessage
id='navbar.preferences'
defaultMessage='Notification Preferences'
/>
</ToggleModalButton>
</li>
);
@@ -319,7 +348,12 @@ export default class Navbar extends React.Component {
data-toggle='collapse'
data-target='#navbar-collapse-1'
>
<span className='sr-only'>{'Toggle sidebar'}</span>
<span className='sr-only'>
<FormattedMessage
id='navbar.toggle1'
defaultMessage='Toggle sidebar'
/>
</span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
@@ -335,7 +369,12 @@ export default class Navbar extends React.Component {
data-target='#sidebar-nav'
onClick={this.toggleLeftSidebar}
>
<span className='sr-only'>{'Toggle sidebar'}</span>
<span className='sr-only'>
<FormattedMessage
id='navbar.toggle2'
defaultMessage='Toggle sidebar'
/>
</span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
@@ -405,6 +444,17 @@ export default class Navbar extends React.Component {
}
if (channel.header.length === 0) {
const link = (
<a
href='#'
onClick={this.showEditChannelHeaderModal}
>
<FormattedMessage
id='navbar.click'
defaultMessage='Click here'
/>
</a>
);
popoverContent = (
<Popover
bsStyle='info'
@@ -412,15 +462,14 @@ export default class Navbar extends React.Component {
id='header-popover'
>
<div>
{'No channel header yet.'}
<br/>
<a
href='#'
onClick={this.showEditChannelHeaderModal}
>
{'Click here'}
</a>
{' to add one.'}
<FormattedMessage
id='navbar.noHeader'
defaultMessage='No channel header yet.{newline}{link} to add one.'
values={{
newline: (<br/>),
link: (link)
}}
/>
</div>
</Popover>
);

Просмотреть файл

@@ -3,7 +3,20 @@
import * as TextFormatting from '../utils/text_formatting.jsx';
export default class PostAttachment extends React.Component {
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const holders = defineMessages({
collapse: {
id: 'post_attachment.collapse',
defaultMessage: '▲ collapse text'
},
more: {
id: 'post_attachment.more',
defaultMessage: '▼ read more'
}
});
class PostAttachment extends React.Component {
constructor(props) {
super(props);
@@ -28,7 +41,7 @@ export default class PostAttachment extends React.Component {
getInitState() {
const shouldCollapse = this.shouldCollapse();
const text = TextFormatting.formatText(this.props.attachment.text || '');
const uncollapsedText = text + (shouldCollapse ? '<a class="attachment-link-more" href="#">▲ collapse text</a>' : '');
const uncollapsedText = text + (shouldCollapse ? `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.collapse)}</a>` : '');
const collapsedText = shouldCollapse ? this.getCollapsedText() : text;
return {
@@ -62,7 +75,7 @@ export default class PostAttachment extends React.Component {
text = text.substr(0, 700);
}
return TextFormatting.formatText(text) + '<a class="attachment-link-more" href="#">▼ read more</a>';
return TextFormatting.formatText(text) + `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.more)}</a>`;
}
getFieldsTable() {
@@ -292,5 +305,8 @@ export default class PostAttachment extends React.Component {
}
PostAttachment.propTypes = {
intl: intlShape.isRequired,
attachment: React.PropTypes.object.isRequired
};
export default injectIntl(PostAttachment);

Просмотреть файл

@@ -14,7 +14,20 @@ import YoutubeVideo from './youtube_video.jsx';
import providers from './providers.json';
export default class PostBody extends React.Component {
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
const holders = defineMessages({
plusOne: {
id: 'post_body.plusOne',
defaultMessage: ' plus 1 other file'
},
plusMore: {
id: 'post_body.plusMore',
defaultMessage: ' plus {count} other files'
}
});
class PostBody extends React.Component {
constructor(props) {
super(props);
@@ -187,6 +200,7 @@ export default class PostBody extends React.Component {
}
render() {
const {formatMessage} = this.props.intl;
const post = this.props.post;
const filenames = this.props.post.filenames;
const parentPost = this.props.parentPost;
@@ -208,10 +222,12 @@ export default class PostBody extends React.Component {
username = parentPost.props.override_username;
}
if (username.slice(-1) === 's') {
apostrophe = '\'';
} else {
apostrophe = '\'s';
if (global.window.mm_locale === 'en') {
if (username.slice(-1) === 's') {
apostrophe = '\'';
} else {
apostrophe = '\'s';
}
}
name = (
<a
@@ -230,16 +246,23 @@ export default class PostBody extends React.Component {
message = parentPost.filenames[0].split('/').pop();
if (parentPost.filenames.length === 2) {
message += ' plus 1 other file';
message += formatMessage(holders.plusOne);
} else if (parentPost.filenames.length > 2) {
message += ` plus ${parentPost.filenames.length - 1} other files`;
message += formatMessage(holders.plusMore, {count: (parentPost.filenames.length - 1)});
}
}
comment = (
<div className='post__link'>
<span>
{'Commented on '}{name}{apostrophe}{' message: '}
<FormattedMessage
id='post_body.commentedOn'
defaultMessage='Commented on {name}{apostrophe} message: '
values={{
name: (name),
apostrophe: apostrophe
}}
/>
<a
className='theme'
onClick={this.props.handleCommentClick}
@@ -260,7 +283,10 @@ export default class PostBody extends React.Component {
href='#'
onClick={this.props.retryPost}
>
{'Retry'}
<FormattedMessage
id='post_body.retry'
defaultMessage='Retry'
/>
</a>
);
} else if (post.state === Constants.POST_LOADING) {
@@ -313,8 +339,11 @@ export default class PostBody extends React.Component {
}
PostBody.propTypes = {
intl: intlShape.isRequired,
post: React.PropTypes.object.isRequired,
parentPost: React.PropTypes.object,
retryPost: React.PropTypes.func.isRequired,
handleCommentClick: React.PropTypes.func.isRequired
};
export default injectIntl(PostBody);

Просмотреть файл

@@ -7,6 +7,8 @@ import PostStore from '../stores/post_store.jsx';
import ChannelStore from '../stores/channel_store.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import {FormattedMessage} from 'mm-intl';
export default class PostFocusView extends React.Component {
constructor(props) {
super(props);
@@ -73,7 +75,12 @@ export default class PostFocusView extends React.Component {
getIntroMessage() {
return (
<div className='channel-intro'>
<h4 className='channel-intro__title'>{'Beginning of Channel Archives'}</h4>
<h4 className='channel-intro__title'>
<FormattedMessage
id='post_focus_view.beginning'
defaultMessage='Beginning of Channel Archives'
/>
</h4>
</div>
);
}

Просмотреть файл

@@ -9,6 +9,8 @@ import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import Constants from '../utils/constants.jsx';
import {FormattedMessage} from 'mm-intl';
const Overlay = ReactBootstrap.Overlay;
const Popover = ReactBootstrap.Popover;
@@ -53,7 +55,10 @@ export default class PostInfo extends React.Component {
href='#'
onClick={this.props.handleCommentClick}
>
{'Reply'}
<FormattedMessage
id='post_info.reply'
defaultMessage='Reply'
/>
</a>
</li>
);
@@ -68,7 +73,10 @@ export default class PostInfo extends React.Component {
href='#'
onClick={(e) => this.setState({target: e.target, show: !this.state.show})}
>
{'Permalink'}
<FormattedMessage
id='post_info.permalink'
defaultMessage='Permalink'
/>
</a>
</li>
);
@@ -84,7 +92,10 @@ export default class PostInfo extends React.Component {
role='menuitem'
onClick={() => EventHelpers.showDeletePostModal(post, dataComments)}
>
{'Delete'}
<FormattedMessage
id='post_info.del'
defaultMessage='Delete'
/>
</a>
</li>
);
@@ -108,7 +119,10 @@ export default class PostInfo extends React.Component {
data-channelid={post.channel_id}
data-comments={dataComments}
>
{'Edit'}
<FormattedMessage
id='post_info.edit'
defaultMessage='Edit'
/>
</a>
</li>
);
@@ -183,7 +197,15 @@ export default class PostInfo extends React.Component {
var dropdown = this.createDropdown();
const permalink = TeamStore.getCurrentTeamUrl() + '/pl/' + post.id;
const copyButtonText = this.state.copiedLink ? (<div>{'Copy '}<i className='fa fa-check'/></div>) : 'Copy';
const copyButtonText = this.state.copiedLink ? (
<div>
<FormattedMessage
id='post_info.copy'
defaultMessage='Copy '
/>
<i className='fa fa-check'/></div>
) : (<FormattedMessage id='post_info.copy' />);
const permalinkOverlay = (
<Popover
id='permalink-overlay'

Просмотреть файл

@@ -8,6 +8,9 @@ import * as Utils from '../utils/utils.jsx';
import Post from './post.jsx';
import Constants from '../utils/constants.jsx';
import DelayedAction from '../utils/delayed_action.jsx';
import {FormattedDate, FormattedMessage} from 'mm-intl';
const Preferences = Constants.Preferences;
export default class PostsView extends React.Component {
@@ -250,7 +253,15 @@ export default class PostsView extends React.Component {
className='date-separator'
>
<hr className='separator__hr' />
<div className='separator__text'>{currentPostDay.toDateString()}</div>
<div className='separator__text'>
<FormattedDate
value={currentPostDay}
weekday='short'
month='short'
day='2-digit'
year='numeric'
/>
</div>
</div>
);
}
@@ -276,7 +287,12 @@ export default class PostsView extends React.Component {
<hr
className='separator__hr'
/>
<div className='separator__text'>{'New Messages'}</div>
<div className='separator__text'>
<FormattedMessage
id='posts_view.newMsg'
defaultMessage='New Messages'
/>
</div>
</div>
);
}
@@ -420,7 +436,10 @@ export default class PostsView extends React.Component {
href='#'
onClick={this.loadMorePostsTop}
>
{'Load more messages'}
<FormattedMessage
id='posts_view.loadMore'
defaultMessage='Load more messages'
/>
</a>
);
} else {
@@ -436,7 +455,7 @@ export default class PostsView extends React.Component {
href='#'
onClick={this.loadMorePostsBottom}
>
{'Load more messages'}
<FormattedMessage id='posts_view.loadMore' />
</a>
);
} else {

Просмотреть файл

@@ -2,7 +2,8 @@
// See License.txt for license information.
import Constants from '../utils/constants.jsx';
import * as Utils from '../utils/utils.jsx';
import {FormattedRelative, FormattedDate} from 'mm-intl';
var Tooltip = ReactBootstrap.Tooltip;
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -20,20 +21,25 @@ export default class TimeSince extends React.Component {
clearInterval(this.intervalId);
}
render() {
const displayDate = Utils.displayDate(this.props.eventTime);
const displayTime = Utils.displayTime(this.props.eventTime);
if (this.props.sameUser) {
return (
<time className='post__time'>
{Utils.displayTime(this.props.eventTime)}
<FormattedRelative value={this.props.eventTime} />
</time>
);
}
const tooltip = (
<Tooltip id={'time-since-tooltip-' + this.props.eventTime}>
{displayDate + ' at ' + displayTime}
<FormattedDate
value={this.props.eventTime}
month='long'
day='numeric'
year='numeric'
hour12={true}
hour='numeric'
minute='2-digit'
/>
</Tooltip>
);
@@ -44,7 +50,7 @@ export default class TimeSince extends React.Component {
overlay={tooltip}
>
<time className='post__time'>
{Utils.displayDateTime(this.props.eventTime)}
<FormattedRelative value={this.props.eventTime} />
</time>
</OverlayTrigger>
);

Просмотреть файл

@@ -9,10 +9,20 @@ import Constants from '../utils/constants.jsx';
import FileInfoPreview from './file_info_preview.jsx';
import FileStore from '../stores/file_store.jsx';
import ViewImagePopoverBar from './view_image_popover_bar.jsx';
import {intlShape, injectIntl, defineMessages} from 'mm-intl';
const Modal = ReactBootstrap.Modal;
const KeyCodes = Constants.KeyCodes;
export default class ViewImageModal extends React.Component {
const holders = defineMessages({
loading: {
id: 'view_image.loading',
defaultMessage: 'Loading '
}
});
class ViewImageModal extends React.Component {
constructor(props) {
super(props);
@@ -235,6 +245,7 @@ export default class ViewImageModal extends React.Component {
fileUrl={fileUrl}
fileInfo={this.state.fileInfo}
maxHeight={this.state.imgHeight}
formatMessage={this.props.intl.formatMessage}
/>
);
} else {
@@ -243,6 +254,7 @@ export default class ViewImageModal extends React.Component {
filename={filename}
fileUrl={fileUrl}
fileInfo={fileInfo}
formatMessage={this.props.intl.formatMessage}
/>
);
}
@@ -250,7 +262,12 @@ export default class ViewImageModal extends React.Component {
// display a progress indicator when the preview for an image is still loading
const progress = Math.floor(this.state.progress[this.state.imgId]);
content = <LoadingImagePreview progress={progress} />;
content = (
<LoadingImagePreview
progress={progress}
loading={this.props.intl.formatMessage(holders.loading)}
/>
);
}
let leftArrow = null;
@@ -335,6 +352,7 @@ ViewImageModal.defaultProps = {
startId: 0
};
ViewImageModal.propTypes = {
intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func.isRequired,
filenames: React.PropTypes.array,
@@ -344,12 +362,12 @@ ViewImageModal.propTypes = {
startId: React.PropTypes.number
};
function LoadingImagePreview({progress}) {
function LoadingImagePreview({progress, loading}) {
let progressView = null;
if (progress) {
progressView = (
<span className='loader-percent'>
{'Loading ' + progress + '%'}
{loading + progress + '%'}
</span>
);
}
@@ -386,3 +404,5 @@ function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) {
</a>
);
}
export default injectIntl(ViewImageModal);

Просмотреть файл

@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {FormattedMessage} from 'mm-intl';
export default class ViewImagePopoverBar extends React.Component {
constructor(props) {
super(props);
@@ -16,7 +18,10 @@ export default class ViewImagePopoverBar extends React.Component {
data-title='Public Image'
onClick={this.props.getPublicLink}
>
{'Get Public Link'}
<FormattedMessage
id='view_image_popover.publicLink'
defaultMessage='Get Public Link'
/>
</a>
<span className='text'>{' | '}</span>
</div>
@@ -33,7 +38,16 @@ export default class ViewImagePopoverBar extends React.Component {
ref='imageFooter'
className={footerClass}
>
<span className='pull-left text'>{'File ' + (this.props.fileId + 1) + ' of ' + this.props.totalFiles}</span>
<span className='pull-left text'>
<FormattedMessage
id='view_image_popover.file'
defaultMessage='File {count} of {total}'
values={{
count: (this.props.fileId + 1),
total: this.props.totalFiles
}}
/>
</span>
<div className='image-links'>
{publicLink}
<a
@@ -41,7 +55,10 @@ export default class ViewImagePopoverBar extends React.Component {
download={this.props.filename}
className='text'
>
{'Download'}
<FormattedMessage
id='view_image_popover.download'
defaultMessage='Download'
/>
</a>
</div>
</div>

Просмотреть файл

@@ -446,7 +446,7 @@ class PostStoreClass extends EventEmitter {
posts = {};
}
post.message = '(message deleted)';
post.message = this.delete_message;
post.state = Constants.POST_DELETED;
post.filenames = [];
@@ -581,6 +581,9 @@ class PostStoreClass extends EventEmitter {
return commentCount;
}
deleteMessage(msg) {
this.delete_message = msg;
}
}
var PostStore = new PostStoreClass();

Просмотреть файл

@@ -86,7 +86,7 @@ class SocketStoreClass extends EventEmitter {
this.failCount = this.failCount + 1;
ErrorStore.storeLastError({connErrorCount: this.failCount, message: 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.'});
ErrorStore.storeLastError({connErrorCount: this.failCount, message: this.translations.socketError});
ErrorStore.emitChange();
};
@@ -109,7 +109,7 @@ class SocketStoreClass extends EventEmitter {
handleMessage(msg) {
switch (msg.action) {
case SocketEvents.POSTED:
handleNewPostEvent(msg);
handleNewPostEvent(msg, this.translations);
break;
case SocketEvents.POST_EDITED:
@@ -151,9 +151,12 @@ class SocketStoreClass extends EventEmitter {
this.initialize();
}
}
setTranslations(messages) {
this.translations = messages;
}
}
function handleNewPostEvent(msg) {
function handleNewPostEvent(msg, translations) {
// Store post
const post = JSON.parse(msg.props.post);
EventHelpers.emitPostRecievedEvent(post);
@@ -192,14 +195,14 @@ function handleNewPostEvent(msg) {
return;
}
let username = 'Someone';
let username = translations.someone;
if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
username = post.props.override_username;
} else if (UserStore.hasProfile(msg.user_id)) {
username = UserStore.getProfile(msg.user_id).username;
}
let title = 'Posted';
let title = translations.posted;
if (channel) {
title = channel.display_name;
}
@@ -211,14 +214,14 @@ function handleNewPostEvent(msg) {
if (notifyText.length === 0) {
if (msgProps.image) {
Utils.notifyMe(title, username + ' uploaded an image', channel);
Utils.notifyMe(title, username + translations.uploadedImage, channel);
} else if (msgProps.otherFile) {
Utils.notifyMe(title, username + ' uploaded a file', channel);
Utils.notifyMe(title, username + translations.uploadedFile, channel);
} else {
Utils.notifyMe(title, username + ' did something new', channel);
Utils.notifyMe(title, username + translations.something, channel);
}
} else {
Utils.notifyMe(title, username + ' wrote: ' + notifyText, channel);
Utils.notifyMe(title, username + translations.wrote + notifyText, channel);
}
if (!user.notify_props || user.notify_props.desktop_sound === 'true') {
Utils.ding();

Просмотреть файл

@@ -11,6 +11,8 @@ import Constants from '../utils/constants.jsx';
import TeamStore from '../stores/team_store.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import {FormattedMessage, FormattedHTMLMessage, FormattedDate} from 'mm-intl';
export function createChannelIntroMessage(channel) {
if (channel.type === 'D') {
return createDMIntroMessage(channel);
@@ -48,8 +50,13 @@ export function createDMIntroMessage(channel) {
</strong>
</div>
<p className='channel-intro-text'>
{'This is the start of your direct message history with ' + teammateName + '.'}<br/>
{'Direct messages and files shared here are not shown to people outside this area.'}
<FormattedHTMLMessage
id='intro_messages.DM'
defaultMessage='This is the start of your direct message history with {teammate}.<br />Direct messages and files shared here are not shown to people outside this area.'
values={{
teammate: teammateName
}}
/>
</p>
{createSetHeaderButton(channel)}
</div>
@@ -58,7 +65,12 @@ export function createDMIntroMessage(channel) {
return (
<div className='channel-intro'>
<p className='channel-intro-text'>{'This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.'}</p>
<p className='channel-intro-text'>
<FormattedMessage
id='intro_messages.teammate'
defaultMessage='This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.'
/>
</p>
</div>
);
}
@@ -66,11 +78,13 @@ export function createDMIntroMessage(channel) {
export function createOffTopicIntroMessage(channel) {
return (
<div className='channel-intro'>
<h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
<p className='channel-intro__content'>
{'This is the start of ' + channel.display_name + ', a channel for non-work-related conversations.'}
<br/>
</p>
<FormattedHTMLMessage
id='intro_messages.offTopic'
defaultMessage='<h4 class="channel-intro__title">Beginning of {display_name}</h4><p class="channel-intro__content">This is the start of {display_name}, a channel for non-work-related conversations.<br/></p>'
values={{
display_name: channel.display_name
}}
/>
{createSetHeaderButton(channel)}
{createInviteChannelMemberButton(channel, 'channel')}
</div>
@@ -87,7 +101,11 @@ export function createDefaultIntroMessage(channel) {
href='#'
onClick={EventHelpers.showInviteMemberModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this team'}
<i className='fa fa-user-plus'></i>
<FormattedMessage
id='intro_messages.inviteOthers'
defaultMessage='Invite others to this team'
/>
</a>
);
} else {
@@ -97,19 +115,24 @@ export function createDefaultIntroMessage(channel) {
href='#'
onClick={EventHelpers.showGetTeamInviteLinkModal}
>
<i className='fa fa-user-plus'></i>{'Invite others to this team'}
<i className='fa fa-user-plus'></i>
<FormattedMessage
id='intro_messages.inviteOthers'
defaultMessage='Invite others to this team'
/>
</a>
);
}
return (
<div className='channel-intro'>
<h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
<p className='channel-intro__content'>
<strong>{'Welcome to ' + channel.display_name + '!'}</strong>
<br/><br/>
{'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'}
</p>
<FormattedHTMLMessage
id='intro_messages.default'
defaultMessage="<h4 class='channel-intro__title'>Beginning of {display_name}</h4><p class='channel-intro__content'><strong>Welcome to {display_name}!'</strong><br/><br/>This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.</p>"
values={{
display_name: channel.display_name
}}
/>
{inviteModalLink}
{createSetHeaderButton(channel)}
<br/>
@@ -124,33 +147,83 @@ export function createStandardIntroMessage(channel) {
var uiType;
var memberMessage;
if (channel.type === 'P') {
uiType = 'private group';
memberMessage = ' Only invited members can see this private group.';
uiType = (
<FormattedMessage
id='intro_messages.group'
defaultMessage='private group'
/>
);
memberMessage = (
<FormattedMessage
id='intro_messages.onlyInvited'
defaultMessage=' Only invited members can see this private group.'
/>
);
} else {
uiType = 'channel';
memberMessage = ' Any member can join and read this channel.';
uiType = (
<FormattedMessage
id='intro_messages.channel'
defaultMessage='channel'
/>
);
memberMessage = (
<FormattedMessage
id='intro_messages.anyMember'
defaultMessage=' Any member can join and read this channel.'
/>
);
}
const date = (
<FormattedDate
value={channel.create_at}
month='long'
day='2-digit'
year='numeric'
/>
);
var createMessage;
if (creatorName === '') {
createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created on ' + Utils.displayDate(channel.create_at) + '.';
createMessage = (
<FormattedMessage
id='intro_messages.noCreator'
defaultMessage='This is the start of the {name} {type}, created on {date}.'
values={{
name: (uiName),
type: (uiType),
date: (date)
}}
/>
);
} else {
createMessage = (
<span>
{'This is the start of the '}
<strong>{uiName}</strong>
{' '}
{uiType}{', created by '}
<strong>{creatorName}</strong>
{' on '}
<strong>{Utils.displayDate(channel.create_at)}</strong>
<FormattedHTMLMessage
id='intro_messages.creator'
defaultMessage='This is the start of the <strong>{name}</strong> {type}, created by <strong>{creator}</strong> on <strong>{date}</strong>'
values={{
name: (uiName),
type: (uiType),
date: (date),
creator: creatorName
}}
/>
</span>
);
}
return (
<div className='channel-intro'>
<h4 className='channel-intro__title'>{'Beginning of ' + uiName}</h4>
<h4 className='channel-intro__title'>
<FormattedMessage
id='intro_messages.beginning'
defaultMessage='Beginning of {name}'
values={{
name: (uiName)
}}
/>
</h4>
<p className='channel-intro__content'>
{createMessage}
{memberMessage}
@@ -169,7 +242,14 @@ function createInviteChannelMemberButton(channel, uiType) {
dialogType={ChannelInviteModal}
dialogProps={{channel}}
>
<i className='fa fa-user-plus'></i>{'Invite others to this ' + uiType}
<i className='fa fa-user-plus'></i>
<FormattedMessage
id='intro_messages.invite'
defaultMessage='Invite others to this {type}'
values={{
type: (uiType)
}}
/>
</ToggleModalButton>
);
}
@@ -181,7 +261,11 @@ function createSetHeaderButton(channel) {
dialogType={EditChannelHeaderModal}
dialogProps={{channel}}
>
<i className='fa fa-pencil'></i>{'Set a header'}
<i className='fa fa-pencil'></i>
<FormattedMessage
id='intro_messages.setHeader'
defaultMessage='Set a Header'
/>
</ToggleModalButton>
);
}

Просмотреть файл

@@ -1,16 +0,0 @@
// GENERATED FILE
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = [{ "locale": "en", "pluralRuleFunction": function pluralRuleFunction(n, ord) {
var s = String(n).split("."),
v0 = !s[1],
t0 = Number(s[0]) == n,
n10 = t0 && s[0].slice(-1),
n100 = t0 && s[0].slice(-2);if (ord) return n10 == 1 && n100 != 11 ? "one" : n10 == 2 && n100 != 12 ? "two" : n10 == 3 && n100 != 13 ? "few" : "other";return n == 1 && v0 ? "one" : "other";
}, "fields": { "year": { "displayName": "Year", "relative": { "0": "this year", "1": "next year", "-1": "last year" }, "relativeTime": { "future": { "one": "in {0} year", "other": "in {0} years" }, "past": { "one": "{0} year ago", "other": "{0} years ago" } } }, "month": { "displayName": "Month", "relative": { "0": "this month", "1": "next month", "-1": "last month" }, "relativeTime": { "future": { "one": "in {0} month", "other": "in {0} months" }, "past": { "one": "{0} month ago", "other": "{0} months ago" } } }, "day": { "displayName": "Day", "relative": { "0": "today", "1": "tomorrow", "-1": "yesterday" }, "relativeTime": { "future": { "one": "in {0} day", "other": "in {0} days" }, "past": { "one": "{0} day ago", "other": "{0} days ago" } } }, "hour": { "displayName": "Hour", "relativeTime": { "future": { "one": "in {0} hour", "other": "in {0} hours" }, "past": { "one": "{0} hour ago", "other": "{0} hours ago" } } }, "minute": { "displayName": "Minute", "relativeTime": { "future": { "one": "in {0} minute", "other": "in {0} minutes" }, "past": { "one": "{0} minute ago", "other": "{0} minutes ago" } } }, "second": { "displayName": "Second", "relative": { "0": "now" }, "relativeTime": { "future": { "one": "in {0} second", "other": "in {0} seconds" }, "past": { "one": "{0} second ago", "other": "{0} seconds ago" } } } } }, { "locale": "en-001", "parentLocale": "en" }, { "locale": "en-150", "parentLocale": "en-GB" }, { "locale": "en-GB", "parentLocale": "en-001" }, { "locale": "en-AG", "parentLocale": "en-001" }, { "locale": "en-AI", "parentLocale": "en-001" }, { "locale": "en-AS", "parentLocale": "en" }, { "locale": "en-AU", "parentLocale": "en-GB", "fields": { "year": { "displayName": "Year", "relative": { "0": "This year", "1": "Next year", "-1": "Last year" }, "relativeTime": { "future": { "one": "in {0} year", "other": "in {0} years" }, "past": { "one": "{0} year ago", "other": "{0} years ago" } } }, "month": { "displayName": "Month", "relative": { "0": "This month", "1": "Next month", "-1": "Last month" }, "relativeTime": { "future": { "one": "in {0} month", "other": "in {0} months" }, "past": { "one": "{0} month ago", "other": "{0} months ago" } } }, "day": { "displayName": "Day", "relative": { "0": "today", "1": "tomorrow", "-1": "yesterday" }, "relativeTime": { "future": { "one": "in {0} day", "other": "in {0} days" }, "past": { "one": "{0} day ago", "other": "{0} days ago" } } }, "hour": { "displayName": "Hour", "relativeTime": { "future": { "one": "in {0} hour", "other": "in {0} hours" }, "past": { "one": "{0} hour ago", "other": "{0} hours ago" } } }, "minute": { "displayName": "Minute", "relativeTime": { "future": { "one": "in {0} minute", "other": "in {0} minutes" }, "past": { "one": "{0} minute ago", "other": "{0} minutes ago" } } }, "second": { "displayName": "Second", "relative": { "0": "now" }, "relativeTime": { "future": { "one": "in {0} second", "other": "in {0} seconds" }, "past": { "one": "{0} second ago", "other": "{0} seconds ago" } } } } }, { "locale": "en-BB", "parentLocale": "en-001" }, { "locale": "en-BE", "parentLocale": "en-GB" }, { "locale": "en-BM", "parentLocale": "en-001" }, { "locale": "en-BS", "parentLocale": "en-001" }, { "locale": "en-BW", "parentLocale": "en-001" }, { "locale": "en-BZ", "parentLocale": "en-001" }, { "locale": "en-CA", "parentLocale": "en" }, { "locale": "en-CC", "parentLocale": "en-001" }, { "locale": "en-CK", "parentLocale": "en-001" }, { "locale": "en-CM", "parentLocale": "en-001" }, { "locale": "en-CX", "parentLocale": "en-001" }, { "locale": "en-DG", "parentLocale": "en-GB" }, { "locale": "en-DM", "parentLocale": "en-001" }, { "locale": "en-Dsrt", "pluralRuleFunction": function pluralRuleFunction(n, ord) {
if (ord) return "other";return "other";
}, "fields": { "year": { "displayName": "Year", "relative": { "0": "this year", "1": "next year", "-1": "last year" }, "relativeTime": { "future": { "other": "+{0} y" }, "past": { "other": "-{0} y" } } }, "month": { "displayName": "Month", "relative": { "0": "this month", "1": "next month", "-1": "last month" }, "relativeTime": { "future": { "other": "+{0} m" }, "past": { "other": "-{0} m" } } }, "day": { "displayName": "Day", "relative": { "0": "today", "1": "tomorrow", "-1": "yesterday" }, "relativeTime": { "future": { "other": "+{0} d" }, "past": { "other": "-{0} d" } } }, "hour": { "displayName": "Hour", "relativeTime": { "future": { "other": "+{0} h" }, "past": { "other": "-{0} h" } } }, "minute": { "displayName": "Minute", "relativeTime": { "future": { "other": "+{0} min" }, "past": { "other": "-{0} min" } } }, "second": { "displayName": "Second", "relative": { "0": "now" }, "relativeTime": { "future": { "other": "+{0} s" }, "past": { "other": "-{0} s" } } } } }, { "locale": "en-ER", "parentLocale": "en-001" }, { "locale": "en-FJ", "parentLocale": "en-001" }, { "locale": "en-FK", "parentLocale": "en-GB" }, { "locale": "en-FM", "parentLocale": "en-001" }, { "locale": "en-GD", "parentLocale": "en-001" }, { "locale": "en-GG", "parentLocale": "en-GB" }, { "locale": "en-GH", "parentLocale": "en-001" }, { "locale": "en-GI", "parentLocale": "en-GB" }, { "locale": "en-GM", "parentLocale": "en-001" }, { "locale": "en-GU", "parentLocale": "en" }, { "locale": "en-GY", "parentLocale": "en-001" }, { "locale": "en-HK", "parentLocale": "en-GB" }, { "locale": "en-IE", "parentLocale": "en-GB" }, { "locale": "en-IM", "parentLocale": "en-GB" }, { "locale": "en-IN", "parentLocale": "en-GB" }, { "locale": "en-IO", "parentLocale": "en-GB" }, { "locale": "en-JE", "parentLocale": "en-GB" }, { "locale": "en-JM", "parentLocale": "en-001" }, { "locale": "en-KE", "parentLocale": "en-001" }, { "locale": "en-KI", "parentLocale": "en-001" }, { "locale": "en-KN", "parentLocale": "en-001" }, { "locale": "en-KY", "parentLocale": "en-001" }, { "locale": "en-LC", "parentLocale": "en-001" }, { "locale": "en-LR", "parentLocale": "en-001" }, { "locale": "en-LS", "parentLocale": "en-001" }, { "locale": "en-MG", "parentLocale": "en-001" }, { "locale": "en-MH", "parentLocale": "en" }, { "locale": "en-MO", "parentLocale": "en-GB" }, { "locale": "en-MP", "parentLocale": "en" }, { "locale": "en-MS", "parentLocale": "en-001" }, { "locale": "en-MT", "parentLocale": "en-GB" }, { "locale": "en-MU", "parentLocale": "en-001" }, { "locale": "en-MW", "parentLocale": "en-001" }, { "locale": "en-MY", "parentLocale": "en-001" }, { "locale": "en-NA", "parentLocale": "en-001" }, { "locale": "en-NF", "parentLocale": "en-001" }, { "locale": "en-NG", "parentLocale": "en-001" }, { "locale": "en-NR", "parentLocale": "en-001" }, { "locale": "en-NU", "parentLocale": "en-001" }, { "locale": "en-NZ", "parentLocale": "en-GB" }, { "locale": "en-PG", "parentLocale": "en-001" }, { "locale": "en-PH", "parentLocale": "en-001" }, { "locale": "en-PK", "parentLocale": "en-GB" }, { "locale": "en-PN", "parentLocale": "en-001" }, { "locale": "en-PR", "parentLocale": "en" }, { "locale": "en-PW", "parentLocale": "en-001" }, { "locale": "en-RW", "parentLocale": "en-001" }, { "locale": "en-SB", "parentLocale": "en-001" }, { "locale": "en-SC", "parentLocale": "en-001" }, { "locale": "en-SD", "parentLocale": "en-001" }, { "locale": "en-SG", "parentLocale": "en-GB" }, { "locale": "en-SH", "parentLocale": "en-GB" }, { "locale": "en-SL", "parentLocale": "en-001" }, { "locale": "en-SS", "parentLocale": "en-001" }, { "locale": "en-SX", "parentLocale": "en-001" }, { "locale": "en-SZ", "parentLocale": "en-001" }, { "locale": "en-TC", "parentLocale": "en-001" }, { "locale": "en-TK", "parentLocale": "en-001" }, { "locale": "en-TO", "parentLocale": "en-001" }, { "locale": "en-TT", "parentLocale": "en-001" }, { "locale": "en-TV", "parentLocale": "en-001" }, { "locale": "en-TZ", "parentLocale": "en-001" }, { "locale": "en-UG", "parentLocale": "en-001" }, { "locale": "en-UM", "parentLocale": "en" }, { "locale": "en-US", "parentLocale": "en" }, { "locale": "en-US-POSIX", "parentLocale": "en-US" }, { "locale": "en-VC", "parentLocale": "en-001" }, { "locale": "en-VG", "parentLocale": "en-GB" }, { "locale": "en-VI", "parentLocale": "en" }, { "locale": "en-VU", "parentLocale": "en-001" }, { "locale": "en-WS", "parentLocale": "en-001" }, { "locale": "en-ZA", "parentLocale": "en-001" }, { "locale": "en-ZM", "parentLocale": "en-001" }, { "locale": "en-ZW", "parentLocale": "en-001" }];
module.exports = exports["default"];

Различия файлов скрыты, потому что одна или несколько строк слишком длинны