From 01e43c3e5799b575a70798056945365ddf51f3ad Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 16 Nov 2016 17:20:52 +0100 Subject: [PATCH] Adding react-intl i18n to the frontend. No translations yet --- .../components/column_back_button.jsx | 3 +- .../components/components/lightbox.jsx | 7 +- .../components/loading_indicator.jsx | 4 +- .../components/relative_timestamp.jsx | 66 ++++++------------- .../components/components/status.jsx | 19 +++--- .../components/status_action_bar.jsx | 21 +++--- .../components/components/status_list.jsx | 5 +- .../components/components/video_player.jsx | 15 +++-- .../components/containers/mastodon.jsx | 46 +++++++------ .../account/components/action_bar.jsx | 39 +++++------ .../features/account_timeline/index.jsx | 12 ++-- .../compose/components/compose_form.jsx | 8 ++- .../compose/components/navigation_bar.jsx | 9 +-- .../compose/components/reply_indicator.jsx | 14 ++-- .../features/compose/components/search.jsx | 5 +- .../compose/components/suggestions_box.jsx | 9 +-- .../compose/components/upload_button.jsx | 7 +- .../compose/components/upload_form.jsx | 11 ++-- .../components/features/favourites/index.jsx | 18 ++--- .../features/followers/components/account.jsx | 17 ++--- .../features/getting_started/index.jsx | 12 ++-- .../features/home_timeline/index.jsx | 13 ++-- .../features/mentions_timeline/index.jsx | 11 ++-- .../features/public_timeline/index.jsx | 7 +- .../components/features/reblogs/index.jsx | 18 ++--- .../features/status/components/action_bar.jsx | 21 +++--- .../status/components/detailed_status.jsx | 18 ++--- .../features/ui/components/tabs_bar.jsx | 9 +-- app/helpers/home_helper.rb | 1 + package.json | 2 +- yarn.lock | 39 +++++++++-- 31 files changed, 263 insertions(+), 223 deletions(-) diff --git a/app/assets/javascripts/components/components/column_back_button.jsx b/app/assets/javascripts/components/components/column_back_button.jsx index c3ed8f8d09..2f499c7491 100644 --- a/app/assets/javascripts/components/components/column_back_button.jsx +++ b/app/assets/javascripts/components/components/column_back_button.jsx @@ -1,4 +1,5 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { FormattedMessage } from 'react-intl'; const outerStyle = { padding: '15px', @@ -30,7 +31,7 @@ const ColumnBackButton = React.createClass({ return (
- Back +
); } diff --git a/app/assets/javascripts/components/components/lightbox.jsx b/app/assets/javascripts/components/components/lightbox.jsx index aef0bac14a..537bab9541 100644 --- a/app/assets/javascripts/components/components/lightbox.jsx +++ b/app/assets/javascripts/components/components/lightbox.jsx @@ -1,6 +1,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import IconButton from './icon_button'; import { Motion, spring } from 'react-motion'; +import { injectIntl } from 'react-intl'; const overlayStyle = { position: 'fixed', @@ -40,14 +41,14 @@ const Lightbox = React.createClass({ mixins: [PureRenderMixin], render () { - const { isVisible, onOverlayClicked, onCloseClicked, children } = this.props; + const { intl, isVisible, onOverlayClicked, onCloseClicked, children } = this.props; return (
{({ y }) =>
- + {children}
} @@ -58,4 +59,4 @@ const Lightbox = React.createClass({ }); -export default Lightbox; +export default injectIntl(Lightbox); diff --git a/app/assets/javascripts/components/components/loading_indicator.jsx b/app/assets/javascripts/components/components/loading_indicator.jsx index 7b738ac32c..fd5acae84e 100644 --- a/app/assets/javascripts/components/components/loading_indicator.jsx +++ b/app/assets/javascripts/components/components/loading_indicator.jsx @@ -1,3 +1,5 @@ +import { FormattedMessage } from 'react-intl'; + const LoadingIndicator = () => { const style = { textAlign: 'center', @@ -7,7 +9,7 @@ const LoadingIndicator = () => { paddingTop: '120px' }; - return
Loading...
; + return
; }; export default LoadingIndicator; diff --git a/app/assets/javascripts/components/components/relative_timestamp.jsx b/app/assets/javascripts/components/components/relative_timestamp.jsx index 1de44bb950..96f99cca86 100644 --- a/app/assets/javascripts/components/components/relative_timestamp.jsx +++ b/app/assets/javascripts/components/components/relative_timestamp.jsx @@ -1,52 +1,24 @@ -import moment from 'moment'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { + FormattedMessage, + FormattedDate, + FormattedRelative +} from 'react-intl'; -moment.updateLocale('en', { - relativeTime : { - future: "in %s", - past: "%s", - s: "%ds", - m: "1m", - mm: "%dm", - h: "1h", - hh: "%dh", - d: "1d", - dd: "%dd", - M: "1mo", - MM: "%dmo", - y: "1y", - yy: "%dy" +const RelativeTimestamp = ({ timestamp, now }) => { + const diff = (new Date(now)) - (new Date(timestamp)); + + if (diff < 0) { + return + } else if (diff > (3600 * 24 * 7 * 1000)) { + return + } else { + return } -}); +}; -const RelativeTimestamp = React.createClass({ - - propTypes: { - timestamp: React.PropTypes.string.isRequired, - now: React.PropTypes.any - }, - - mixins: [PureRenderMixin], - - render () { - const timestamp = moment(this.props.timestamp); - const now = this.props.now; - - let string = ''; - - if (timestamp.isAfter(now)) { - string = 'Just now'; - } else { - string = timestamp.from(now); - } - - return ( - - {string} - - ); - } - -}); +RelativeTimestamp.propTypes = { + timestamp: React.PropTypes.string.isRequired, + now: React.PropTypes.any +}; export default RelativeTimestamp; diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx index ff14ba4f1d..8077308c44 100644 --- a/app/assets/javascripts/components/components/status.jsx +++ b/app/assets/javascripts/components/components/status.jsx @@ -1,12 +1,13 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import Avatar from './avatar'; -import RelativeTimestamp from './relative_timestamp'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import DisplayName from './display_name'; -import MediaGallery from './media_gallery'; -import VideoPlayer from './video_player'; -import StatusContent from './status_content'; -import StatusActionBar from './status_action_bar'; +import Avatar from './avatar'; +import RelativeTimestamp from './relative_timestamp'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import DisplayName from './display_name'; +import MediaGallery from './media_gallery'; +import VideoPlayer from './video_player'; +import StatusContent from './status_content'; +import StatusActionBar from './status_action_bar'; +import { FormattedMessage } from 'react-intl'; const Status = React.createClass({ @@ -59,7 +60,7 @@ const Status = React.createClass({
- {displayName} reblogged + {displayName} }} />
diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx index e80ce824f0..8883d08068 100644 --- a/app/assets/javascripts/components/components/status_action_bar.jsx +++ b/app/assets/javascripts/components/components/status_action_bar.jsx @@ -1,7 +1,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import IconButton from './icon_button'; -import DropdownMenu from './dropdown_menu'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import IconButton from './icon_button'; +import DropdownMenu from './dropdown_menu'; +import { injectIntl } from 'react-intl'; const StatusActionBar = React.createClass({ propTypes: { @@ -36,20 +37,20 @@ const StatusActionBar = React.createClass({ }, render () { - const { status, me } = this.props; + const { status, me, intl } = this.props; let menu = []; if (status.getIn(['account', 'id']) === me) { - menu.push({ text: 'Delete', action: this.handleDeleteClick }); + menu.push({ text: intl.formatMessage({ id: 'status.delete', defaultMessage: 'Delete' }), action: this.handleDeleteClick }); } else { - menu.push({ text: 'Mention', action: this.handleMentionClick }); + menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick }); } return (
-
-
-
+
+
+
@@ -60,4 +61,4 @@ const StatusActionBar = React.createClass({ }); -export default StatusActionBar; +export default injectIntl(StatusActionBar); diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/assets/javascripts/components/components/status_list.jsx index 58aa94cc01..d5d69c9899 100644 --- a/app/assets/javascripts/components/components/status_list.jsx +++ b/app/assets/javascripts/components/components/status_list.jsx @@ -3,7 +3,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PureRenderMixin from 'react-addons-pure-render-mixin'; import { ScrollContainer } from 'react-router-scroll'; import StatusContainer from '../containers/status_container'; -import moment from 'moment'; const StatusList = React.createClass({ @@ -21,14 +20,14 @@ const StatusList = React.createClass({ getInitialState () { return { - now: moment() + now: new Date() }; }, mixins: [PureRenderMixin], componentDidMount () { - this._interval = setInterval(() => this.setState({ now: moment() }), 60000); + this._interval = setInterval(() => this.setState({ now: new Date() }), 60000); }, componentWillUnmount () { diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/assets/javascripts/components/components/video_player.jsx index 209db33a7f..2c236b996b 100644 --- a/app/assets/javascripts/components/components/video_player.jsx +++ b/app/assets/javascripts/components/components/video_player.jsx @@ -1,6 +1,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import IconButton from './icon_button'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import IconButton from './icon_button'; +import { injectIntl } from 'react-intl'; const videoStyle = { position: 'relative', @@ -59,14 +60,16 @@ const VideoPlayer = React.createClass({ }, render () { + const { media, intl, width, height } = this.props; + return ( -
-
-
); - }, + } }); -export default ActionBar; +export default injectIntl(ActionBar); diff --git a/app/assets/javascripts/components/features/account_timeline/index.jsx b/app/assets/javascripts/components/features/account_timeline/index.jsx index cae88efdb3..7a3dbe160d 100644 --- a/app/assets/javascripts/components/features/account_timeline/index.jsx +++ b/app/assets/javascripts/components/features/account_timeline/index.jsx @@ -1,12 +1,12 @@ -import { connect } from 'react-redux'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import ImmutablePropTypes from 'react-immutable-proptypes'; +import { connect } from 'react-redux'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import { fetchAccountTimeline, expandAccountTimeline -} from '../../actions/accounts'; -import StatusList from '../../components/status_list'; -import LoadingIndicator from '../../components/loading_indicator'; +} from '../../actions/accounts'; +import StatusList from '../../components/status_list'; +import LoadingIndicator from '../../components/loading_indicator'; const mapStateToProps = (state, props) => ({ statusIds: state.getIn(['timelines', 'accounts_timelines', Number(props.params.accountId)]), diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index ead8e00088..5aa041f09a 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -8,6 +8,7 @@ import Autosuggest from 'react-autosuggest'; import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container'; import { debounce } from 'react-decoration'; import UploadButtonContainer from '../containers/upload_button_container'; +import { injectIntl } from 'react-intl'; const getTokenForSuggestions = (str, caretPosition) => { let word; @@ -134,6 +135,7 @@ const ComposeForm = React.createClass({ }, render () { + const { intl } = this.props; let replyArea = ''; const disabled = this.props.is_submitting || this.props.is_uploading; @@ -142,7 +144,7 @@ const ComposeForm = React.createClass({ } const inputProps = { - placeholder: 'What is on your mind?', + placeholder: intl.formatMessage({ id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' }), value: this.props.text, onKeyUp: this.handleKeyUp, onChange: this.handleChange, @@ -167,7 +169,7 @@ const ComposeForm = React.createClass({ />
-
+
@@ -177,4 +179,4 @@ const ComposeForm = React.createClass({ }); -export default ComposeForm; +export default injectIntl(ComposeForm); diff --git a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx index d2dae141fa..4ba6d22901 100644 --- a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx +++ b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx @@ -1,9 +1,10 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Avatar from '../../../components/avatar'; -import IconButton from '../../../components/icon_button'; -import DisplayName from '../../../components/display_name'; -import { Link } from 'react-router'; +import IconButton from '../../../components/icon_button'; +import DisplayName from '../../../components/display_name'; +import { Link } from 'react-router'; +import { FormattedMessage } from 'react-intl'; const NavigationBar = React.createClass({ propTypes: { @@ -19,7 +20,7 @@ const NavigationBar = React.createClass({
{this.props.account.get('acct')} - Settings · Public timeline · Logout + · ·
); diff --git a/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx b/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx index 6298d3de90..4b34f09bff 100644 --- a/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx +++ b/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx @@ -3,11 +3,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import Avatar from '../../../components/avatar'; import IconButton from '../../../components/icon_button'; import DisplayName from '../../../components/display_name'; -import emojione from 'emojione'; - -emojione.imageType = 'png'; -emojione.sprites = false; -emojione.imagePathPNG = '/emoji/'; +import emojify from '../../../emoji'; +import { injectIntl } from 'react-intl'; const ReplyIndicator = React.createClass({ @@ -34,12 +31,13 @@ const ReplyIndicator = React.createClass({ }, render () { - let content = { __html: emojione.unicodeToImage(this.props.status.get('content')) }; + const { intl } = this.props; + const content = { __html: emojify(this.props.status.get('content')) }; return (
-
+
@@ -54,4 +52,4 @@ const ReplyIndicator = React.createClass({ }); -export default ReplyIndicator; +export default injectIntl(ReplyIndicator); diff --git a/app/assets/javascripts/components/features/compose/components/search.jsx b/app/assets/javascripts/components/features/compose/components/search.jsx index 37efbe5df2..65df336cca 100644 --- a/app/assets/javascripts/components/features/compose/components/search.jsx +++ b/app/assets/javascripts/components/features/compose/components/search.jsx @@ -3,6 +3,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import Autosuggest from 'react-autosuggest'; import AutosuggestAccountContainer from '../containers/autosuggest_account_container'; import { debounce } from 'react-decoration'; +import { injectIntl } from 'react-intl'; const getSuggestionValue = suggestion => suggestion.value; @@ -94,7 +95,7 @@ const Search = React.createClass({ render () { const inputProps = { - placeholder: 'Search', + placeholder: this.props.intl.formatMessage({ id: 'search.placeholder', defaultMessage: 'Search' }), value: this.props.value, onChange: this.onChange, style: inputStyle @@ -125,4 +126,4 @@ const Search = React.createClass({ }); -export default Search; +export default injectIntl(Search); diff --git a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx b/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx index 70c2fca6e0..697902275b 100644 --- a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx +++ b/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx @@ -1,6 +1,7 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import AccountContainer from '../../followers/containers/account_container'; +import AccountContainer from '../../followers/containers/account_container'; +import { FormattedMessage } from 'react-intl'; const outerStyle = { position: 'relative' @@ -66,13 +67,13 @@ const SuggestionsBox = React.createClass({ let nextLink = ''; if (accountIds.size > perWindow) { - nextLink =
Refresh; + nextLink = ; } return (
- Who to follow {nextLink} + {nextLink} {accountIds.skip(perWindow * this.state.index).take(perWindow).map(accountId => )} diff --git a/app/assets/javascripts/components/features/compose/components/upload_button.jsx b/app/assets/javascripts/components/features/compose/components/upload_button.jsx index 8289e0a093..cc251835f6 100644 --- a/app/assets/javascripts/components/features/compose/components/upload_button.jsx +++ b/app/assets/javascripts/components/features/compose/components/upload_button.jsx @@ -1,5 +1,6 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import IconButton from '../../../components/icon_button'; +import { injectIntl } from 'react-intl'; const UploadButton = React.createClass({ @@ -26,9 +27,11 @@ const UploadButton = React.createClass({ }, render () { + const { intl } = this.props; + return (
- +
); @@ -36,4 +39,4 @@ const UploadButton = React.createClass({ }); -export default UploadButton; +export default injectIntl(UploadButton); diff --git a/app/assets/javascripts/components/features/compose/components/upload_form.jsx b/app/assets/javascripts/components/features/compose/components/upload_form.jsx index eab504b484..72c2b95359 100644 --- a/app/assets/javascripts/components/features/compose/components/upload_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/upload_form.jsx @@ -1,6 +1,7 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import IconButton from '../../../components/icon_button'; +import IconButton from '../../../components/icon_button'; +import { injectIntl } from 'react-intl'; const UploadForm = React.createClass({ @@ -13,10 +14,12 @@ const UploadForm = React.createClass({ mixins: [PureRenderMixin], render () { + const { intl } = this.props; + const uploads = this.props.media.map(attachment => (
- +
)); @@ -30,4 +33,4 @@ const UploadForm = React.createClass({ }); -export default UploadForm; +export default injectIntl(UploadForm); diff --git a/app/assets/javascripts/components/features/favourites/index.jsx b/app/assets/javascripts/components/features/favourites/index.jsx index 8f15bcad9c..5c9ea498b8 100644 --- a/app/assets/javascripts/components/features/favourites/index.jsx +++ b/app/assets/javascripts/components/features/favourites/index.jsx @@ -1,12 +1,12 @@ -import { connect } from 'react-redux'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import LoadingIndicator from '../../components/loading_indicator'; -import { fetchFavourites } from '../../actions/interactions'; -import { ScrollContainer } from 'react-router-scroll'; -import AccountContainer from '../followers/containers/account_container'; -import Column from '../ui/components/column'; -import ColumnBackButton from '../../components/column_back_button'; +import { connect } from 'react-redux'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import LoadingIndicator from '../../components/loading_indicator'; +import { fetchFavourites } from '../../actions/interactions'; +import { ScrollContainer } from 'react-router-scroll'; +import AccountContainer from '../followers/containers/account_container'; +import Column from '../ui/components/column'; +import ColumnBackButton from '../../components/column_back_button'; const mapStateToProps = (state, props) => ({ accountIds: state.getIn(['user_lists', 'favourited_by', Number(props.params.statusId)]) diff --git a/app/assets/javascripts/components/features/followers/components/account.jsx b/app/assets/javascripts/components/features/followers/components/account.jsx index adcd903600..123a40cab4 100644 --- a/app/assets/javascripts/components/features/followers/components/account.jsx +++ b/app/assets/javascripts/components/features/followers/components/account.jsx @@ -1,9 +1,10 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Avatar from '../../../components/avatar'; -import DisplayName from '../../../components/display_name'; -import { Link } from 'react-router'; -import IconButton from '../../../components/icon_button'; +import Avatar from '../../../components/avatar'; +import DisplayName from '../../../components/display_name'; +import { Link } from 'react-router'; +import IconButton from '../../../components/icon_button'; +import { injectIntl } from 'react-intl'; const outerStyle = { padding: '10px', @@ -51,7 +52,7 @@ const Account = React.createClass({ }, render () { - const { account, me, withNote } = this.props; + const { account, me, withNote, intl } = this.props; if (!account) { return
; @@ -68,7 +69,7 @@ const Account = React.createClass({ buttons = (
- +
); } @@ -91,4 +92,4 @@ const Account = React.createClass({ }); -export default Account; +export default injectIntl(Account); diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index d5792dd9e6..5a9c4db89e 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -1,15 +1,15 @@ -import Column from '../ui/components/column'; +import Column from '../ui/components/column'; import { Link } from 'react-router'; +import { FormattedMessage } from 'react-intl'; const GettingStarted = () => { return (
-

Getting started

-

You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form in the bottom of the sidebar.

-

If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.

-

The developer of this project can be followed as Gargron@mastodon.social

-

Also check out the public timeline for a start!

+

+

+

+

); diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/assets/javascripts/components/features/home_timeline/index.jsx index e4cd8bdca9..117a4a72df 100644 --- a/app/assets/javascripts/components/features/home_timeline/index.jsx +++ b/app/assets/javascripts/components/features/home_timeline/index.jsx @@ -1,8 +1,9 @@ -import { connect } from 'react-redux'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { connect } from 'react-redux'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; import StatusListContainer from '../ui/containers/status_list_container'; -import Column from '../ui/components/column'; +import Column from '../ui/components/column'; import { refreshTimeline } from '../../actions/timelines'; +import { injectIntl } from 'react-intl'; const HomeTimeline = React.createClass({ @@ -17,8 +18,10 @@ const HomeTimeline = React.createClass({ }, render () { + const { intl } = this.props; + return ( - + ); @@ -26,4 +29,4 @@ const HomeTimeline = React.createClass({ }); -export default connect()(HomeTimeline); +export default connect()(injectIntl(HomeTimeline)); diff --git a/app/assets/javascripts/components/features/mentions_timeline/index.jsx b/app/assets/javascripts/components/features/mentions_timeline/index.jsx index 919a75d189..9f1caa2350 100644 --- a/app/assets/javascripts/components/features/mentions_timeline/index.jsx +++ b/app/assets/javascripts/components/features/mentions_timeline/index.jsx @@ -1,8 +1,9 @@ import { connect } from 'react-redux'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; import StatusListContainer from '../ui/containers/status_list_container'; -import Column from '../ui/components/column'; +import Column from '../ui/components/column'; import { refreshTimeline } from '../../actions/timelines'; +import { injectIntl } from 'react-intl'; const MentionsTimeline = React.createClass({ @@ -17,8 +18,10 @@ const MentionsTimeline = React.createClass({ }, render () { + const { intl } = this.props; + return ( - + ); @@ -26,4 +29,4 @@ const MentionsTimeline = React.createClass({ }); -export default connect()(MentionsTimeline); +export default connect()(injectIntl(MentionsTimeline)); diff --git a/app/assets/javascripts/components/features/public_timeline/index.jsx b/app/assets/javascripts/components/features/public_timeline/index.jsx index 12f73ba9bf..445a4fc633 100644 --- a/app/assets/javascripts/components/features/public_timeline/index.jsx +++ b/app/assets/javascripts/components/features/public_timeline/index.jsx @@ -7,6 +7,7 @@ import { updateTimeline, deleteFromTimelines } from '../../actions/timelines'; +import { injectIntl } from 'react-intl'; const PublicTimeline = React.createClass({ @@ -44,8 +45,10 @@ const PublicTimeline = React.createClass({ }, render () { + const { intl } = this.props; + return ( - + ); @@ -53,4 +56,4 @@ const PublicTimeline = React.createClass({ }); -export default connect()(PublicTimeline); +export default connect()(injectIntl(PublicTimeline)); diff --git a/app/assets/javascripts/components/features/reblogs/index.jsx b/app/assets/javascripts/components/features/reblogs/index.jsx index 2b62d3a270..5f22065f62 100644 --- a/app/assets/javascripts/components/features/reblogs/index.jsx +++ b/app/assets/javascripts/components/features/reblogs/index.jsx @@ -1,12 +1,12 @@ -import { connect } from 'react-redux'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import LoadingIndicator from '../../components/loading_indicator'; -import { fetchReblogs } from '../../actions/interactions'; -import { ScrollContainer } from 'react-router-scroll'; -import AccountContainer from '../followers/containers/account_container'; -import Column from '../ui/components/column'; -import ColumnBackButton from '../../components/column_back_button'; +import { connect } from 'react-redux'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import LoadingIndicator from '../../components/loading_indicator'; +import { fetchReblogs } from '../../actions/interactions'; +import { ScrollContainer } from 'react-router-scroll'; +import AccountContainer from '../followers/containers/account_container'; +import Column from '../ui/components/column'; +import ColumnBackButton from '../../components/column_back_button'; const mapStateToProps = (state, props) => ({ accountIds: state.getIn(['user_lists', 'reblogged_by', Number(props.params.statusId)]) diff --git a/app/assets/javascripts/components/features/status/components/action_bar.jsx b/app/assets/javascripts/components/features/status/components/action_bar.jsx index b1202ad8e1..d855176f22 100644 --- a/app/assets/javascripts/components/features/status/components/action_bar.jsx +++ b/app/assets/javascripts/components/features/status/components/action_bar.jsx @@ -1,7 +1,8 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import IconButton from '../../../components/icon_button'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import IconButton from '../../../components/icon_button'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import DropdownMenu from '../../../components/dropdown_menu'; +import DropdownMenu from '../../../components/dropdown_menu'; +import { injectIntl } from 'react-intl'; const ActionBar = React.createClass({ @@ -38,21 +39,21 @@ const ActionBar = React.createClass({ }, render () { - const { status, me } = this.props; + const { status, me, intl } = this.props; let menu = []; if (me === status.getIn(['account', 'id'])) { - menu.push({ text: 'Delete', action: this.handleDeleteClick }); + menu.push({ text: intl.formatMessage({ id: 'status.delete', defaultMessage: 'Delete' }), action: this.handleDeleteClick }); } else { - menu.push({ text: 'Mention', action: this.handleMentionClick }); + menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick }); } return (
-
-
-
+
+
+
); @@ -60,4 +61,4 @@ const ActionBar = React.createClass({ }); -export default ActionBar; +export default injectIntl(ActionBar); diff --git a/app/assets/javascripts/components/features/status/components/detailed_status.jsx b/app/assets/javascripts/components/features/status/components/detailed_status.jsx index 71335970f1..8efdf195f8 100644 --- a/app/assets/javascripts/components/features/status/components/detailed_status.jsx +++ b/app/assets/javascripts/components/features/status/components/detailed_status.jsx @@ -1,12 +1,12 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Avatar from '../../../components/avatar'; -import DisplayName from '../../../components/display_name'; -import StatusContent from '../../../components/status_content'; -import MediaGallery from '../../../components/media_gallery'; -import VideoPlayer from '../../../components/video_player'; -import moment from 'moment'; -import { Link } from 'react-router'; +import Avatar from '../../../components/avatar'; +import DisplayName from '../../../components/display_name'; +import StatusContent from '../../../components/status_content'; +import MediaGallery from '../../../components/media_gallery'; +import VideoPlayer from '../../../components/video_player'; +import { Link } from 'react-router'; +import { FormattedDate, FormattedNumber } from 'react-intl'; const DetailedStatus = React.createClass({ @@ -54,7 +54,7 @@ const DetailedStatus = React.createClass({ {media}
- {moment(status.get('created_at')).format('HH:mm, DD MMM Y')} · {status.get('reblogs_count')} · {status.get('favourites_count')} + · ·
); diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx index 723f8c9ace..8313d88265 100644 --- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx +++ b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx @@ -1,4 +1,5 @@ import { Link } from 'react-router'; +import { FormattedMessage } from 'react-intl'; const outerStyle = { background: '#373b4a', @@ -28,10 +29,10 @@ const tabActiveStyle = { const TabsBar = () => { return (
- Compose - Home - Mentions - Public + + + +
); }; diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb index 66d7ea9aff..6f87c7b72d 100644 --- a/app/helpers/home_helper.rb +++ b/app/helpers/home_helper.rb @@ -5,6 +5,7 @@ module HomeHelper { token: @token, account: render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json), + locale: I18n.locale, } end end diff --git a/package.json b/package.json index e514e03b9d..6264b62f58 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "immutable": "^3.8.1", "jsdom": "^9.6.0", "mocha": "^3.1.1", - "moment": "^2.14.1", "react": "^15.3.2", "react-addons-perf": "^15.3.2", "react-addons-pure-render-mixin": "^15.3.1", @@ -46,6 +45,7 @@ "http-link-header": "^0.5.0", "react-autosuggest": "^7.0.1", "react-decoration": "^1.4.0", + "react-intl": "^2.1.5", "react-motion": "^0.4.5", "react-responsive": "^1.1.5", "react-router-scroll": "^0.3.2" diff --git a/yarn.lock b/yarn.lock index 55f151753d..fac059911c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2497,12 +2497,38 @@ interpret@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" +intl-format-cache@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.0.5.tgz#b484cefcb9353f374f25de389a3ceea1af18d7c9" + +intl-messageformat-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.2.0.tgz#5906b7f953ab7470e0dc8549097b648b991892ff" + +intl-messageformat@^1.3.0, intl-messageformat@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-1.3.0.tgz#f7d926aded7a3ab19b2dc601efd54e99a4bd4eae" + dependencies: + intl-messageformat-parser "1.2.0" + +intl-relativeformat@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-1.3.0.tgz#893dc7076fccd380cf091a2300c380fa57ace45b" + dependencies: + intl-messageformat "1.3.0" + invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1, invariant@2.x.x: version "2.2.1" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54" dependencies: loose-envify "^1.0.0" +invariant@^2.1.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + ipaddr.js@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" @@ -3151,10 +3177,6 @@ module-deps@^4.0.2: through2 "^2.0.0" xtend "^4.0.0" -moment@^2.14.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.15.1.tgz#e979c2a29e22888e60f396f2220a6118f85cd94c" - ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -3912,6 +3934,15 @@ react-inspector@^1.1.0: dependencies: is-dom "^1.0.5" +react-intl: + version "2.1.5" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.1.5.tgz#f9795ea34b790dcb5d0d8ef7060dddbe85bf8763" + dependencies: + intl-format-cache "^2.0.5" + intl-messageformat "^1.3.0" + intl-relativeformat "^1.3.0" + invariant "^2.1.1" + react-komposer@^1.9.0: version "1.13.1" resolved "https://registry.yarnpkg.com/react-komposer/-/react-komposer-1.13.1.tgz#4b8ac4bcc71323bd7413dcab95c831197f50eed0"