@@ -2,7 +2,15 @@ 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 { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
delete: { id: 'status.delete', defaultMessage: 'Delete' }, | |||
mention: { id: 'status.mention', defaultMessage: 'Mention' }, | |||
reply: { id: 'status.reply', defaultMessage: 'Reply' }, | |||
reblog: { id: 'status.reblog', defaultMessage: 'Reblog' }, | |||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' } | |||
}); | |||
const StatusActionBar = React.createClass({ | |||
propTypes: { | |||
@@ -41,16 +49,16 @@ const StatusActionBar = React.createClass({ | |||
let menu = []; | |||
if (status.getIn(['account', 'id']) === me) { | |||
menu.push({ text: intl.formatMessage({ id: 'status.delete', defaultMessage: 'Delete' }), action: this.handleDeleteClick }); | |||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); | |||
} else { | |||
menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick }); | |||
menu.push({ text: intl.formatMessage(messages.mention), action: this.handleMentionClick }); | |||
} | |||
return ( | |||
<div style={{ marginTop: '10px', overflow: 'hidden' }}> | |||
<div style={{ float: 'left', marginRight: '18px'}}><IconButton title={intl.formatMessage({ id: 'status.reply', defaultMessage: 'Reply' })} icon='reply' onClick={this.handleReplyClick} /></div> | |||
<div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('reblogged')} title={intl.formatMessage({ id: 'status.reblog', defaultMessage: 'Reblog' })} icon='retweet' onClick={this.handleReblogClick} /></div> | |||
<div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('favourited')} title={intl.formatMessage({ id: 'status.favourite', defaultMessage: 'Favourite' })} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> | |||
<div style={{ float: 'left', marginRight: '18px'}}><IconButton title={intl.formatMessage(messages.reply)} icon='reply' onClick={this.handleReplyClick} /></div> | |||
<div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('reblogged')} title={intl.formatMessage(messages.reblog)} icon='retweet' onClick={this.handleReblogClick} /></div> | |||
<div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> | |||
<div style={{ width: '18px', height: '18px', float: 'left' }}> | |||
<DropdownMenu items={menu} icon='ellipsis-h' size={18} /> | |||
@@ -1,7 +1,11 @@ | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import IconButton from './icon_button'; | |||
import { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
toggle_sound: { id: 'video_player.toggle_sound', defaultMessage: 'Toggle sound' } | |||
}); | |||
const videoStyle = { | |||
position: 'relative', | |||
@@ -64,7 +68,7 @@ const VideoPlayer = React.createClass({ | |||
return ( | |||
<div style={{ cursor: 'default', marginTop: '8px', overflow: 'hidden', width: `${width}px`, height: `${height}px`, boxSizing: 'border-box', background: '#000', position: 'relative' }}> | |||
<div style={muteStyle}><IconButton title={intl.formatMessage({ id: 'video_player.toggle_sound', defaultMessage: 'Toggle sound' })} icon={this.state.muted ? 'volume-up' : 'volume-off'} onClick={this.handleClick} /></div> | |||
<div style={muteStyle}><IconButton title={intl.formatMessage(messages.toggle_sound)} icon={this.state.muted ? 'volume-up' : 'volume-off'} onClick={this.handleClick} /></div> | |||
<video src={media.get('url')} autoPlay='true' loop={true} muted={this.state.muted} style={videoStyle} onClick={this.handleVideoClick} /> | |||
</div> | |||
); | |||
@@ -34,6 +34,8 @@ import Favourites from '../features/favourites'; | |||
import HashtagTimeline from '../features/hashtag_timeline'; | |||
import { IntlProvider, addLocaleData } from 'react-intl'; | |||
import en from 'react-intl/locale-data/en'; | |||
import de from 'react-intl/locale-data/de'; | |||
import getMessagesForLocale from '../locales'; | |||
const store = configureStore(); | |||
@@ -41,7 +43,7 @@ const browserHistory = useRouterHistory(createBrowserHistory)({ | |||
basename: '/web' | |||
}); | |||
addLocaleData([...en]); | |||
addLocaleData([...en, ...de]); | |||
const Mastodon = React.createClass({ | |||
@@ -89,7 +91,7 @@ const Mastodon = React.createClass({ | |||
const { locale } = this.props; | |||
return ( | |||
<IntlProvider locale={locale}> | |||
<IntlProvider locale={locale} messages={getMessagesForLocale(locale)}> | |||
<Provider store={store}> | |||
<Router history={browserHistory} render={applyRouterMiddleware(useScroll())}> | |||
<Route path='/' component={UI}> | |||
@@ -2,7 +2,17 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import DropdownMenu from '../../../components/dropdown_menu'; | |||
import { Link } from 'react-router'; | |||
import { injectIntl, FormattedMessage, FormattedNumber } from 'react-intl'; | |||
import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl'; | |||
const messages = defineMessages({ | |||
mention: { id: 'account.mention', defaultMessage: 'Mention' }, | |||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, | |||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock' }, | |||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, | |||
block: { id: 'account.block', defaultMessage: 'Block' }, | |||
follow: { id: 'account.follow', defaultMessage: 'Follow' }, | |||
block: { id: 'account.block', defaultMessage: 'Block' } | |||
}); | |||
const outerStyle = { | |||
borderTop: '1px solid #363c4b', | |||
@@ -41,18 +51,18 @@ const ActionBar = React.createClass({ | |||
let menu = []; | |||
menu.push({ text: intl.formatMessage({ id: 'account.mention', defaultMessage: 'Mention' }), action: this.props.onMention }); | |||
menu.push({ text: intl.formatMessage(messages.mention), action: this.props.onMention }); | |||
if (account.get('id') === me) { | |||
menu.push({ text: intl.formatMessage({ id: 'account.edit_profile', defaultMessage: 'Edit profile' }), href: '/settings/profile' }); | |||
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' }); | |||
} else if (account.getIn(['relationship', 'blocking'])) { | |||
menu.push({ text: intl.formatMessage({ id: 'account.unblock', defaultMessage: 'Unblock' }), action: this.props.onBlock }); | |||
menu.push({ text: intl.formatMessage(messages.unblock), action: this.props.onBlock }); | |||
} else if (account.getIn(['relationship', 'following'])) { | |||
menu.push({ text: intl.formatMessage({ id: 'account.unfollow', defaultMessage: 'Unfollow' }), action: this.props.onFollow }); | |||
menu.push({ text: intl.formatMessage({ id: 'account.block', defaultMessage: 'Block' }), action: this.props.onBlock }); | |||
menu.push({ text: intl.formatMessage(messages.unfollow), action: this.props.onFollow }); | |||
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock }); | |||
} else { | |||
menu.push({ text: intl.formatMessage({ id: 'account.follow', defaultMessage: 'Follow' }), action: this.props.onFollow }); | |||
menu.push({ text: intl.formatMessage({ id: 'account.block', defaultMessage: 'Block' }), action: this.props.onBlock }); | |||
menu.push({ text: intl.formatMessage(messages.follow), action: this.props.onFollow }); | |||
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock }); | |||
} | |||
return ( | |||
@@ -2,6 +2,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import emojify from '../../../emoji'; | |||
import escapeTextContentForBrowser from 'react/lib/escapeTextContentForBrowser'; | |||
import { FormattedMessage } from 'react-intl'; | |||
const Header = React.createClass({ | |||
@@ -23,7 +24,7 @@ const Header = React.createClass({ | |||
} | |||
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) { | |||
info = <span style={{ position: 'absolute', top: '10px', right: '10px', opacity: '0.7', display: 'inline-block', verticalAlign: 'top', background: 'rgba(0, 0, 0, 0.4)', color: '#fff', textTransform: 'uppercase', fontSize: '11px', fontWeight: '500', padding: '4px', borderRadius: '4px' }}>Follows you</span> | |||
info = <span style={{ position: 'absolute', top: '10px', right: '10px', opacity: '0.7', display: 'inline-block', verticalAlign: 'top', background: 'rgba(0, 0, 0, 0.4)', color: '#fff', textTransform: 'uppercase', fontSize: '11px', fontWeight: '500', padding: '4px', borderRadius: '4px' }}><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span> | |||
} | |||
const content = { __html: emojify(account.get('note')) }; | |||
@@ -8,7 +8,12 @@ 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'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' }, | |||
publish: { id: 'compose_form.publish', defaultMessage: 'Publish' } | |||
}); | |||
const getTokenForSuggestions = (str, caretPosition) => { | |||
let word; | |||
@@ -53,7 +58,7 @@ const textareaStyle = { | |||
}; | |||
const renderInputComponent = inputProps => ( | |||
<textarea {...inputProps} placeholder='What is on your mind?' className='compose-form__textarea' style={textareaStyle} /> | |||
<textarea {...inputProps} className='compose-form__textarea' style={textareaStyle} /> | |||
); | |||
const ComposeForm = React.createClass({ | |||
@@ -144,7 +149,7 @@ const ComposeForm = React.createClass({ | |||
} | |||
const inputProps = { | |||
placeholder: intl.formatMessage({ id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' }), | |||
placeholder: intl.formatMessage(messages.placeholder), | |||
value: this.props.text, | |||
onKeyUp: this.handleKeyUp, | |||
onChange: this.handleChange, | |||
@@ -169,7 +174,7 @@ const ComposeForm = React.createClass({ | |||
/> | |||
<div style={{ marginTop: '10px', overflow: 'hidden' }}> | |||
<div style={{ float: 'right' }}><Button text={intl.formatMessage({ id: 'compose_form.publish', defaultMessage: 'Publish' })} onClick={this.handleSubmit} disabled={disabled} /></div> | |||
<div style={{ float: 'right' }}><Button text={intl.formatMessage(messages.publish)} onClick={this.handleSubmit} disabled={disabled} /></div> | |||
<div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={this.props.text} /></div> | |||
<UploadButtonContainer style={{ paddingTop: '4px' }} /> | |||
</div> | |||
@@ -4,7 +4,11 @@ import Avatar from '../../../components/avatar'; | |||
import IconButton from '../../../components/icon_button'; | |||
import DisplayName from '../../../components/display_name'; | |||
import emojify from '../../../emoji'; | |||
import { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' } | |||
}); | |||
const ReplyIndicator = React.createClass({ | |||
@@ -37,7 +41,7 @@ const ReplyIndicator = React.createClass({ | |||
return ( | |||
<div style={{ background: '#9baec8', padding: '10px' }}> | |||
<div style={{ overflow: 'hidden', marginBottom: '5px' }}> | |||
<div style={{ float: 'right', lineHeight: '24px' }}><IconButton title={intl.formatMessage({ id: 'reply_indicator.cancel', defaultMessage: 'Cancel' })} icon='times' onClick={this.handleClick} /></div> | |||
<div style={{ float: 'right', lineHeight: '24px' }}><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div> | |||
<a href={this.props.status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name' style={{ display: 'block', maxWidth: '100%', paddingRight: '25px', color: '#282c37', textDecoration: 'none', overflow: 'hidden', lineHeight: '24px' }}> | |||
<div style={{ float: 'left', marginRight: '5px' }}><Avatar size={24} src={this.props.status.getIn(['account', 'avatar'])} /></div> | |||
@@ -3,7 +3,11 @@ 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'; | |||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | |||
const messages = defineMessages({ | |||
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' } | |||
}); | |||
const getSuggestionValue = suggestion => suggestion.value; | |||
@@ -16,7 +20,7 @@ const renderSuggestion = suggestion => { | |||
}; | |||
const renderSectionTitle = section => ( | |||
<strong>{section.title}</strong> | |||
<strong><FormattedMessage id={`search.${section.title}`} defaultMessage={section.title} /></strong> | |||
); | |||
const getSectionSuggestions = section => section.items; | |||
@@ -95,7 +99,7 @@ const Search = React.createClass({ | |||
render () { | |||
const inputProps = { | |||
placeholder: this.props.intl.formatMessage({ id: 'search.placeholder', defaultMessage: 'Search' }), | |||
placeholder: this.props.intl.formatMessage(messages.placeholder), | |||
value: this.props.value, | |||
onChange: this.onChange, | |||
style: inputStyle | |||
@@ -1,6 +1,10 @@ | |||
import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import IconButton from '../../../components/icon_button'; | |||
import { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
upload: { id: 'upload_button.label', defaultMessage: 'Add media' } | |||
}); | |||
const UploadButton = React.createClass({ | |||
@@ -31,7 +35,7 @@ const UploadButton = React.createClass({ | |||
return ( | |||
<div style={this.props.style}> | |||
<IconButton icon='photo' title={intl.formatMessage({ id: 'upload_button.label', defaultMessage: 'Add media' })} disabled={this.props.disabled} onClick={this.handleClick} size={24} /> | |||
<IconButton icon='photo' title={intl.formatMessage(messages.upload)} disabled={this.props.disabled} onClick={this.handleClick} size={24} /> | |||
<input ref={this.setRef} type='file' multiple={false} onChange={this.handleChange} disabled={this.props.disabled} style={{ display: 'none' }} /> | |||
</div> | |||
); | |||
@@ -1,7 +1,11 @@ | |||
import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||
import IconButton from '../../../components/icon_button'; | |||
import { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
undo: { id: 'upload_form.undo', defaultMessage: 'Undo' } | |||
}); | |||
const UploadForm = React.createClass({ | |||
@@ -19,7 +23,7 @@ const UploadForm = React.createClass({ | |||
const uploads = this.props.media.map(attachment => ( | |||
<div key={attachment.get('id')} style={{ borderRadius: '4px', marginBottom: '10px' }} className='transparent-background'> | |||
<div style={{ width: '100%', height: '100px', borderRadius: '4px', background: `url(${attachment.get('preview_url')}) no-repeat center`, backgroundSize: 'cover' }}> | |||
<IconButton icon='times' title={intl.formatMessage({ id: 'upload_form.undo', defaultMessage: 'Undo' })} size={36} onClick={this.props.onRemoveFile.bind(this, attachment.get('id'))} /> | |||
<IconButton icon='times' title={intl.formatMessage(messages.undo)} size={36} onClick={this.props.onRemoveFile.bind(this, attachment.get('id'))} /> | |||
</div> | |||
</div> | |||
)); | |||
@@ -4,7 +4,11 @@ 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'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
follow: { id: 'account.follow', defaultMessage: 'Follow' } | |||
}); | |||
const outerStyle = { | |||
padding: '10px', | |||
@@ -69,7 +73,7 @@ const Account = React.createClass({ | |||
buttons = ( | |||
<div style={buttonsStyle}> | |||
<IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage({ id: 'account.follow', defaultMessage: 'Follow' })} onClick={this.handleFollow} active={following} /> | |||
<IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(messages.follow)} onClick={this.handleFollow} active={following} /> | |||
</div> | |||
); | |||
} | |||
@@ -3,7 +3,11 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import StatusListContainer from '../ui/containers/status_list_container'; | |||
import Column from '../ui/components/column'; | |||
import { refreshTimeline } from '../../actions/timelines'; | |||
import { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
title: { id: 'column.home', defaultMessage: 'Home' } | |||
}); | |||
const HomeTimeline = React.createClass({ | |||
@@ -21,7 +25,7 @@ const HomeTimeline = React.createClass({ | |||
const { intl } = this.props; | |||
return ( | |||
<Column icon='home' heading={intl.formatMessage({ id: 'column.home', defaultMessage: 'Home' })}> | |||
<Column icon='home' heading={intl.formatMessage(messages.title)}> | |||
<StatusListContainer {...this.props} type='home' /> | |||
</Column> | |||
); | |||
@@ -3,7 +3,11 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; | |||
import StatusListContainer from '../ui/containers/status_list_container'; | |||
import Column from '../ui/components/column'; | |||
import { refreshTimeline } from '../../actions/timelines'; | |||
import { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
title: { id: 'column.mentions', defaultMessage: 'Mentions' } | |||
}); | |||
const MentionsTimeline = React.createClass({ | |||
@@ -21,7 +25,7 @@ const MentionsTimeline = React.createClass({ | |||
const { intl } = this.props; | |||
return ( | |||
<Column icon='at' heading={intl.formatMessage({ id: 'column.mentions', defaultMessage: 'Mentions' })}> | |||
<Column icon='at' heading={intl.formatMessage(messages.title)}> | |||
<StatusListContainer {...this.props} type='mentions' /> | |||
</Column> | |||
); | |||
@@ -7,7 +7,11 @@ import { | |||
updateTimeline, | |||
deleteFromTimelines | |||
} from '../../actions/timelines'; | |||
import { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
title: { id: 'column.public', defaultMessage: 'Public' } | |||
}); | |||
const PublicTimeline = React.createClass({ | |||
@@ -48,7 +52,7 @@ const PublicTimeline = React.createClass({ | |||
const { intl } = this.props; | |||
return ( | |||
<Column icon='globe' heading={intl.formatMessage({ id: 'column.public', defaultMessage: 'Public' })}> | |||
<Column icon='globe' heading={intl.formatMessage(messages.title)}> | |||
<StatusListContainer type='public' /> | |||
</Column> | |||
); | |||
@@ -2,7 +2,15 @@ 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 { injectIntl } from 'react-intl'; | |||
import { defineMessages, injectIntl } from 'react-intl'; | |||
const messages = defineMessages({ | |||
delete: { id: 'status.delete', defaultMessage: 'Delete' }, | |||
mention: { id: 'status.mention', defaultMessage: 'Mention' }, | |||
reply: { id: 'status.reply', defaultMessage: 'Reply' }, | |||
reblog: { id: 'status.reblog', defaultMessage: 'Reblog' }, | |||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' } | |||
}); | |||
const ActionBar = React.createClass({ | |||
@@ -44,16 +52,16 @@ const ActionBar = React.createClass({ | |||
let menu = []; | |||
if (me === status.getIn(['account', 'id'])) { | |||
menu.push({ text: intl.formatMessage({ id: 'status.delete', defaultMessage: 'Delete' }), action: this.handleDeleteClick }); | |||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); | |||
} else { | |||
menu.push({ text: intl.formatMessage({ id: 'status.mention', defaultMessage: 'Mention' }), action: this.handleMentionClick }); | |||
menu.push({ text: intl.formatMessage(messages.mention), action: this.handleMentionClick }); | |||
} | |||
return ( | |||
<div style={{ background: '#2f3441', display: 'flex', flexDirection: 'row', borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', padding: '10px 0' }}> | |||
<div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton title={intl.formatMessage({ id: 'status.reply', defaultMessage: 'Reply' })} icon='reply' onClick={this.handleReplyClick} /></div> | |||
<div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('reblogged')} title={intl.formatMessage({ id: 'status.reblog', defaultMessage: 'Reblog' })} icon='retweet' onClick={this.handleReblogClick} /></div> | |||
<div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('favourited')} title={intl.formatMessage({ id: 'status.favourite', defaultMessage: 'Favourite' })} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> | |||
<div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton title={intl.formatMessage(messages.reply)} icon='reply' onClick={this.handleReplyClick} /></div> | |||
<div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('reblogged')} title={intl.formatMessage(messages.reblog)} icon='retweet' onClick={this.handleReblogClick} /></div> | |||
<div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} activeStyle={{ color: '#ca8f04' }} /></div> | |||
<div style={{ flex: '1 1 auto', textAlign: 'center' }}><DropdownMenu size={18} icon='ellipsis-h' items={menu} /></div> | |||
</div> | |||
); | |||
@@ -0,0 +1,48 @@ | |||
const en = { | |||
"column_back_button.label": "Zurück", | |||
"lightbox.close": "Schließen", | |||
"loading_indicator.label": "Lade...", | |||
"status.mention": "Erwähnen", | |||
"status.delete": "Löschen", | |||
"status.reply": "Antworten", | |||
"status.reblog": "Teilen", | |||
"status.favourite": "Favorisieren", | |||
"status.reblogged_by": "{name} teilte", | |||
"video_player.toggle_sound": "Ton umschalten", | |||
"account.mention": "Erwähnen", | |||
"account.edit_profile": "Profil bearbeiten", | |||
"account.unblock": "Entblocken", | |||
"account.unfollow": "Entfolgen", | |||
"account.block": "Blocken", | |||
"account.follow": "Folgen", | |||
"account.posts": "Beiträge", | |||
"account.follows": "Folgt", | |||
"account.followers": "Folger", | |||
"account.follows_you": "Folgt dir", | |||
"getting_started.heading": "Erste Schritte", | |||
"getting_started.about_addressing": "Du kannst Leuten folgen, falls du ihren Nutzernamen und ihre Domain kennst, in dem du eine e-mail-artige Addresse in das Suchfeld oben an der Seite eingibst.", | |||
"getting_started.about_shortcuts": "Falls der Zielnutzer an derselben Domain ist wie du, funktioniert der Nutzername auch alleine. Das gilt auch für Erwähnungen in Beiträgen.", | |||
"getting_started.about_developer": "Der Entwickler des Projekts kann unter Gargron@mastodon.social gefunden werden", | |||
"column.home": "Home", | |||
"column.mentions": "Erwähnungen", | |||
"column.public": "Gesamtes Bekanntes Netz", | |||
"tabs_bar.compose": "Schreiben", | |||
"tabs_bar.home": "Home", | |||
"tabs_bar.mentions": "Erwähnungen", | |||
"tabs_bar.public": "Gesamtes Netz", | |||
"compose_form.placeholder": "Worüber möchstest du schreiben?", | |||
"compose_form.publish": "Veröffentlichen", | |||
"navigation_bar.settings": "Einstellungen", | |||
"navigation_bar.public_timeline": "Öffentlich", | |||
"navigation_bar.logout": "Abmelden", | |||
"reply_indicator.cancel": "Abbrechen", | |||
"search.placeholder": "Suche", | |||
"search.account": "Konto", | |||
"search.hashtag": "Hashtag", | |||
"suggestions_box.who_to_follow": "Wem folgen", | |||
"suggestions_box.refresh": "Aktualisieren", | |||
"upload_button.label": "Media-Datei anfügen", | |||
"upload_form.undo": "Entfernen" | |||
}; | |||
export default en; |
@@ -0,0 +1,49 @@ | |||
const en = { | |||
"column_back_button.label": "Back", | |||
"lightbox.close": "Close", | |||
"loading_indicator.label": "Loading...", | |||
"status.mention": "Mention", | |||
"status.delete": "Delete", | |||
"status.reply": "Reply", | |||
"status.reblog": "Reblog", | |||
"status.favourite": "Favourite", | |||
"status.reblogged_by": "{name} reblogged", | |||
"video_player.toggle_sound": "Toggle sound", | |||
"account.mention": "Mention", | |||
"account.edit_profile": "Edit profile", | |||
"account.unblock": "Unblock", | |||
"account.unfollow": "Unfollow", | |||
"account.block": "Block", | |||
"account.follow": "Follow", | |||
"account.block": "Block", | |||
"account.posts": "Posts", | |||
"account.follows": "Follows", | |||
"account.followers": "Followers", | |||
"account.follows_you": "Follows you", | |||
"getting_started.heading": "Getting started", | |||
"getting_started.about_addressing": "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 at the top of the sidebar.", | |||
"getting_started.about_shortcuts": "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.", | |||
"getting_started.about_developer": "The developer of this project can be followed as Gargron@mastodon.social", | |||
"column.home": "Home", | |||
"column.mentions": "Mentions", | |||
"column.public": "Public", | |||
"tabs_bar.compose": "Compose", | |||
"tabs_bar.home": "Home", | |||
"tabs_bar.mentions": "Mentions", | |||
"tabs_bar.public": "Public", | |||
"compose_form.placeholder": "What is on your mind?", | |||
"compose_form.publish": "Publish", | |||
"navigation_bar.settings": "Settings", | |||
"navigation_bar.public_timeline": "Public timeline", | |||
"navigation_bar.logout": "Logout", | |||
"reply_indicator.cancel": "Cancel", | |||
"search.placeholder": "Search", | |||
"search.account": "Account", | |||
"search.hashtag": "Hashtag", | |||
"suggestions_box.who_to_follow": "Who to follow", | |||
"suggestions_box.refresh": "Refresh", | |||
"upload_button.label": "Add media", | |||
"upload_form.undo": "Undo" | |||
}; | |||
export default en; |
@@ -0,0 +1,11 @@ | |||
import en from './en'; | |||
import de from './de'; | |||
const locales = { | |||
en, | |||
de | |||
}; | |||
export default function getMessagesForLocale (locale) { | |||
return locales[locale]; | |||
}; |
@@ -14,7 +14,7 @@ const initialState = Immutable.Map({ | |||
const normalizeSuggestions = (state, value, accounts) => { | |||
let newSuggestions = [ | |||
{ | |||
title: 'Account', | |||
title: 'account', | |||
items: accounts.map(item => ({ | |||
type: 'account', | |||
id: item.id, | |||
@@ -25,7 +25,7 @@ const normalizeSuggestions = (state, value, accounts) => { | |||
if (value.indexOf('@') === -1) { | |||
newSuggestions.push({ | |||
title: 'Hashtag', | |||
title: 'hashtag', | |||
items: [ | |||
{ | |||
type: 'hashtag', | |||
@@ -20,7 +20,7 @@ module Mastodon | |||
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. | |||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] | |||
config.i18n.available_locales = [:en] | |||
config.i18n.available_locales = [:en, :de] | |||
config.i18n.default_locale = :en | |||
# config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb') | |||
@@ -0,0 +1,59 @@ | |||
--- | |||
de: | |||
about: | |||
about_instance: "<em>%{instance}</em> ist eine Instanz von Mastodon." | |||
about_mastodon: Mastodon ist ein <em>freier, quelloffener</em> soziales Netzwerkserver. Eine <em>dezentralisierte</em> Alternative zu kommerziellen Plattformen, verhindert es die Risiken, die entstehen, wenn eine einzelne Firma deine Kommunikation monopolisiert. Jeder kann Mastodon verwenden und ganz einfach am <em>sozialen Netzwerk</em> teilnehmen. | |||
get_started: Erste Schritte | |||
source_code: Quellcode | |||
terms: AGB | |||
accounts: | |||
follow: Folgen | |||
followers: Folger | |||
following: Folgt | |||
nothing_here: Hier gibt es nichts! | |||
people_followed_by: Nutzer, denen %{name} folgt | |||
people_who_follow: Nutzer, die %{name} folgen | |||
posts: Beiträge | |||
unfollow: Entfolgen | |||
application_mailer: | |||
signature: Mastodon-Benachrichtigungen von %{instance} | |||
auth: | |||
change_password: Passwort ändern | |||
didnt_get_confirmation: Keine Bestätigung bekommen? | |||
forgot_password: Passwort vergessen? | |||
login: Anmelden | |||
register: Registrieren | |||
resend_confirmation: Bestätigung nochmal versenden | |||
reset_password: Passwort zurücksetzen | |||
set_new_password: Neues Passwort setzen | |||
generic: | |||
changes_saved_msg: Änderungen gespeichert! | |||
powered_by: angetrieben von %{link} | |||
save_changes: Änderungen speichern | |||
validation_errors: | |||
one: Etwas ist noch nicht ganz richtig! Bitte korrigiere den Fehler | |||
other: Etwas ist noch nicht ganz richtig! Bitte korrigiere %{count} Fehler | |||
notification_mailer: | |||
favourite: | |||
body: "Dein Beitrag wurde von %{name} favorisiert:" | |||
subject: "%{name} hat deinen Beitrag favorisiert" | |||
follow: | |||
body: "%{name} folgt dir jetzt!" | |||
subject: "%{name} folgt dir nun" | |||
mention: | |||
body: "%{name} hat dich erwähnt:" | |||
subject: "%{name} hat dich erwähnt" | |||
reblog: | |||
body: "Dein Beitrag wurde von %{name} geteilt:" | |||
subject: "%{name} teilte deinen Beitrag" | |||
pagination: | |||
next: Vorwärts | |||
prev: Zurück | |||
settings: | |||
edit_profile: Profil bearbeiten | |||
preferences: Einstellungen | |||
stream_entries: | |||
favourited: favorisierte einen Beitrag von | |||
is_now_following: folgt nun | |||
will_paginate: | |||
page_gap: "…" |
@@ -0,0 +1,61 @@ | |||
--- | |||
de: | |||
devise: | |||
confirmations: | |||
confirmed: "Vielen Dank für Deine Registrierung. Bitte melde dich jetzt an." | |||
send_instructions: "Du erhältst in wenigen Minuten eine E-Mail, mit der Du Deine Registrierung bestätigen kannst." | |||
send_paranoid_instructions: "Falls Deine E-Mail-Adresse in unserer Datenbank existiert erhältst Du in wenigen Minuten eine E-Mail mit der Du Deine Registrierung bestätigen kannst." | |||
failure: | |||
already_authenticated: "Du bist bereits angemeldet." | |||
inactive: "Dein Account ist nicht aktiv." | |||
invalid: "Ungültige Anmeldedaten." | |||
last_attempt: "Du hast noch einen Versuch bevor dein Account gesperrt wird" | |||
locked: "Dein Account ist gesperrt." | |||
not_found_in_database: "E-Mail-Adresse oder Passwort ungültig." | |||
timeout: "Deine Sitzung ist abgelaufen, bitte melde Dich erneut an." | |||
unauthenticated: "Du musst Dich anmelden oder registrieren, bevor Du fortfahren kannst." | |||
unconfirmed: "Du musst Deinen Account bestätigen, bevor Du fortfahren kannst." | |||
mailer: | |||
confirmation_instructions: | |||
subject: "Mastodon: Anleitung zur Bestätigung Deines Accounts" | |||
password_change: | |||
subject: 'Mastodon: Passwort wurde geändert' | |||
reset_password_instructions: | |||
subject: "Mastodon: Anleitung um Dein Passwort zurückzusetzen" | |||
unlock_instructions: | |||
subject: "Mastodon: Anleitung um Deinen Account freizuschalten" | |||
omniauth_callbacks: | |||
failure: "Du konntest nicht Deinem %{kind}-Account angemeldet werden, weil '%{reason}'." | |||
success: "Du hast Dich erfolgreich mit Deinem %{kind}-Account angemeldet." | |||
passwords: | |||
no_token: "Du kannst diese Seite nur von dem Link aus einer E-Mail zum Passwort-Zurücksetzen aufrufen. Wenn du einen solchen Link aufgerufen hast stelle bitte sicher, dass du die vollständige Adresse aufrufst." | |||
send_instructions: "Du erhältst in wenigen Minuten eine E-Mail mit der Anleitung, wie Du Dein Passwort zurücksetzen kannst." | |||
send_paranoid_instructions: "Falls Deine E-Mail-Adresse in unserer Datenbank existiert erhältst Du in wenigen Minuten eine E-Mail mit der Anleitung, wie Du Dein Passwort zurücksetzen können." | |||
updated: "Dein Passwort wurde geändert. Du bist jetzt angemeldet." | |||
updated_not_active: "Dein Passwort wurde geändert." | |||
registrations: | |||
destroyed: "Dein Account wurde gelöscht." | |||
signed_up: "Du hast dich erfolgreich registriert." | |||
signed_up_but_inactive: "Du hast dich erfolgreich registriert. Wir konnten Dich noch nicht anmelden, da Dein Account inaktiv ist." | |||
signed_up_but_locked: "Du hast dich erfolgreich registriert. Wir konnten Dich noch nicht anmelden, da Dein Account gesperrt ist." | |||
signed_up_but_unconfirmed: "Du hast Dich erfolgreich registriert. Wir konnten Dich noch nicht anmelden, da Dein Account noch nicht bestätigt ist. Du erhältst in Kürze eine E-Mail mit der Anleitung, wie Du Deinen Account freischalten kannst." | |||
update_needs_confirmation: "Deine Daten wurden aktualisiert, aber Du musst Deine neue E-Mail-Adresse bestätigen. Du erhälst in wenigen Minuten eine E-Mail, mit der Du die Änderung Deiner E-Mail-Adresse abschließen kannst." | |||
updated: "Deine Daten wurden aktualisiert." | |||
sessions: | |||
already_signed_out: "Erfolgreich abgemeldet." | |||
signed_in: "Erfolgreich angemeldet." | |||
signed_out: "Erfolgreich abgemeldet." | |||
unlocks: | |||
send_instructions: "Du erhältst in wenigen Minuten eine E-Mail mit der Anleitung, wie Du Deinen Account entsperren können." | |||
send_paranoid_instructions: "Falls Deine E-Mail-Adresse in unserer Datenbank existiert erhältst Du in wenigen Minuten eine E-Mail mit der Anleitung, wie Du Deinen Account entsperren kannst." | |||
unlocked: "Dein Account wurde entsperrt. Du bist jetzt angemeldet." | |||
errors: | |||
messages: | |||
already_confirmed: "wurde bereits bestätigt" | |||
confirmation_period_expired: "muss innerhalb %{period} bestätigt werden, bitte fordere einen neuen Link an" | |||
expired: "ist abgelaufen, bitte neu anfordern" | |||
not_found: "nicht gefunden" | |||
not_locked: "ist nicht gesperrt" | |||
not_saved: | |||
one: "Konnte %{resource} nicht speichern: ein Fehler." | |||
other: "Konnte %{resource} nicht speichern: %{count} Fehler." |
@@ -0,0 +1,112 @@ | |||
--- | |||
de: | |||
activerecord: | |||
attributes: | |||
doorkeeper/application: | |||
name: Name | |||
redirect_uri: Redirect URI | |||
errors: | |||
models: | |||
doorkeeper/application: | |||
attributes: | |||
redirect_uri: | |||
fragment_present: darf kein Fragment enthalten. | |||
invalid_uri: muss ein valider URI (Identifier) sein. | |||
relative_uri: muss ein absoluter URI (Identifier) sein. | |||
secured_uri: muss ein HTTPS/SSL URI (Identifier) sein. | |||
doorkeeper: | |||
applications: | |||
buttons: | |||
authorize: Autorisieren | |||
cancel: Abbrechen | |||
destroy: Löschen | |||
edit: Bearbeiten | |||
submit: Übertragen | |||
confirmations: | |||
destroy: Bist du sicher? | |||
edit: | |||
title: Applikation bearbeiten | |||
form: | |||
error: Whoops! Bitte überprüfe das Formular auf Fehler! | |||
help: | |||
native_redirect_uri: "%{native_redirect_uri} für lokale Tests benutzen" | |||
redirect_uri: Bitte benutze eine Zeile pro URI | |||
scopes: Bitte die "Scopes" mit Leerzeichen trennen. Bitte frei lassen für die Verwendung der Default-Werte. | |||
index: | |||
callback_url: Callback URL | |||
name: Name | |||
new: Neue Applikation | |||
title: Deine Applikationen | |||
new: | |||
title: Neue Applikation | |||
show: | |||
actions: Aktionen | |||
application_id: Applikations-ID | |||
callback_urls: Callback URLs | |||
scopes: Scopes | |||
secret: Secret | |||
title: 'Applikation: %{name}' | |||
authorizations: | |||
buttons: | |||
authorize: Autorisieren | |||
deny: Verweigern | |||
error: | |||
title: Ein Fehler ist aufgetreten | |||
new: | |||
able_to: 'Diese Anwendung wird folgende Rechte haben:' | |||
prompt: Soll %{client_name} für die Benutzung dieses Accounts autorisiert werden? | |||
title: Autorisierung erforderlich | |||
show: | |||
title: Autorisierungscode | |||
authorized_applications: | |||
buttons: | |||
revoke: Ungültig machen | |||
confirmations: | |||
revoke: Bist du sicher? | |||
index: | |||
application: Applikation | |||
created_at: erstellt am | |||
date_format: "%Y-%m-%d %H:%M:%S" | |||
title: Deine autorisierten Applikationen | |||
errors: | |||
messages: | |||
access_denied: Der Resource Owner oder der Autorisierungs-Server hat die Anfrage verweigert. | |||
credential_flow_not_configured: 'Die Prozedur "Resource Owner Password Credentials" ist fehlgeschlagen: Doorkeeper.configure.resource_owner_from_credentials ist nicht konfiguriert.' | |||
invalid_client: 'Client-Autorisierung MKIM ist fehlgeschlagen: Unbekannter Client, keine Autorisierung mitgeliefert oder Autorisierungsmethode nicht unterstützt.' | |||
invalid_grant: Die bereitgestellte Autorisierung ist inkorrekt, abgelaufen, widerrufen, ist mit einem anderen Client verknüpft oder der Redirection URI stimmt nicht mit der Autorisierungs-Anfrage überein. | |||
invalid_redirect_uri: Der Redirect-URI in der Anfrage ist ungültig. | |||
invalid_request: Die Anfrage enthält einen nicht-unterstützten Parameter, ein Parameter fehlt oder sie ist anderweitig fehlerhaft. | |||
invalid_resource_owner: Die angegebenen Zugangsdaten für den "Resource Owner" sind inkorrekt oder dieser Benutzer existiert nicht. | |||
invalid_scope: Der angeforderte Scope ist inkorrekt, unbekannt oder fehlerhaft. | |||
invalid_token: | |||
expired: Der Access Token ist abgelaufen | |||
revoked: Der Access Token wurde annuliert | |||
unknown: Der Access Token ist nicht gültig/korrekt | |||
resource_owner_authenticator_not_configured: 'Die Prozedur "Resource Owner find" ist fehlgeschlagen: Doorkeeper.configure.resource_owner_authenticator ist nicht konfiguriert.' | |||
server_error: Der Autorisierungs-Server hat ein unerwartetes Problem festgestellt und konnte die Anfrage nicht beenden. | |||
temporarily_unavailable: Der Autorisierungs-Server ist derzeit auf Grund von temporärer Überlastung oder Wartungsarbeiten am Server nicht in der Lage, die Anfrage zu bearbeiten . | |||
unauthorized_client: Der Client ist nicht autorisiert, diese Anfrage mit dieser Methode auszuführen. | |||
unsupported_grant_type: Der Autorisierungs-Typ wird nicht vom Autorisierungs-Server unterstützt. | |||
unsupported_response_type: Der Autorisierungs-Server unterstützt diesen Antwort-Typ nicht. | |||
flash: | |||
applications: | |||
create: | |||
notice: Applikation erstellt. | |||
destroy: | |||
notice: Applikation gelöscht. | |||
update: | |||
notice: Applikation geupdated. | |||
authorized_applications: | |||
destroy: | |||
notice: Applikation widerrufen. | |||
layouts: | |||
admin: | |||
nav: | |||
applications: Applikationen | |||
oauth2_provider: OAuth2 Provider | |||
application: | |||
title: OAuth Autorisierung nötig | |||
scopes: | |||
follow: Nutzer folgen, blocken, entblocken und entfolgen | |||
read: deine Daten lesen | |||
write: Beiträge von deinem Konto aus veröffentlichen |
@@ -55,3 +55,5 @@ en: | |||
stream_entries: | |||
favourited: favourited a post by | |||
is_now_following: is now following | |||
will_paginate: | |||
page_gap: "…" |
@@ -0,0 +1,27 @@ | |||
--- | |||
de: | |||
simple_form: | |||
labels: | |||
defaults: | |||
avatar: Avatar | |||
confirm_new_password: Neues Passwort bestätigen | |||
confirm_password: Passwort bestätigen | |||
current_password: Derzeitiges Passwort | |||
display_name: Anzeigename | |||
email: E-mail-Addresse | |||
header: Kopfbild | |||
locale: Sprache | |||
new_password: Neues Passwort | |||
note: Über mich | |||
password: Passwort | |||
username: Nutzername | |||
notification_emails: | |||
favourite: E-mail senden, wenn jemand meinen Beitrag favorisiert | |||
follow: E-mail senden, wenn mir jemand folgt | |||
mention: E-mail senden, wenn mich jemand erwähnt | |||
reblog: E-mail senden, wenn jemand meinen Beitrag teilt | |||
'no': 'Nein' | |||
required: | |||
mark: "*" | |||
text: Pflichtfeld | |||
'yes': 'Ja' |
@@ -1,8 +1,6 @@ | |||
--- | |||
en: | |||
simple_form: | |||
error_notification: | |||
default_message: 'Please review the problems below:' | |||
labels: | |||
defaults: | |||
avatar: Avatar | |||