diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx
index 8883d08068..051b898bd5 100644
--- a/app/assets/javascripts/components/components/status_action_bar.jsx
+++ b/app/assets/javascripts/components/components/status_action_bar.jsx
@@ -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 (
-
-
-
+
+
+
diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/assets/javascripts/components/components/video_player.jsx
index 2c236b996b..9b9b0a2e4f 100644
--- a/app/assets/javascripts/components/components/video_player.jsx
+++ b/app/assets/javascripts/components/components/video_player.jsx
@@ -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 (
);
diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx
index a12b197468..e61107cd1a 100644
--- a/app/assets/javascripts/components/containers/mastodon.jsx
+++ b/app/assets/javascripts/components/containers/mastodon.jsx
@@ -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 (
-
+
diff --git a/app/assets/javascripts/components/features/account/components/action_bar.jsx b/app/assets/javascripts/components/features/account/components/action_bar.jsx
index f8d0514397..cd01de2e20 100644
--- a/app/assets/javascripts/components/features/account/components/action_bar.jsx
+++ b/app/assets/javascripts/components/features/account/components/action_bar.jsx
@@ -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 (
diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/assets/javascripts/components/features/account/components/header.jsx
index 7a086c99a3..b3e9e2a9f7 100644
--- a/app/assets/javascripts/components/features/account/components/header.jsx
+++ b/app/assets/javascripts/components/features/account/components/header.jsx
@@ -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 = Follows you
+ info =
}
const content = { __html: emojify(account.get('note')) };
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 5aa041f09a..32bdeaeca4 100644
--- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx
+++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx
@@ -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 => (
-
+
);
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({
/>
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 4b34f09bff..e913ddfa91 100644
--- a/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx
+++ b/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx
@@ -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 (
-
+
diff --git a/app/assets/javascripts/components/features/compose/components/search.jsx b/app/assets/javascripts/components/features/compose/components/search.jsx
index 65df336cca..b4e618820e 100644
--- a/app/assets/javascripts/components/features/compose/components/search.jsx
+++ b/app/assets/javascripts/components/features/compose/components/search.jsx
@@ -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 => (
- {section.title}
+
);
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
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 cc251835f6..5250ff7482 100644
--- a/app/assets/javascripts/components/features/compose/components/upload_button.jsx
+++ b/app/assets/javascripts/components/features/compose/components/upload_button.jsx
@@ -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 (
-
+
);
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 72c2b95359..ac548033cb 100644
--- a/app/assets/javascripts/components/features/compose/components/upload_form.jsx
+++ b/app/assets/javascripts/components/features/compose/components/upload_form.jsx
@@ -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 => (
));
diff --git a/app/assets/javascripts/components/features/followers/components/account.jsx b/app/assets/javascripts/components/features/followers/components/account.jsx
index 123a40cab4..4a1fca6da8 100644
--- a/app/assets/javascripts/components/features/followers/components/account.jsx
+++ b/app/assets/javascripts/components/features/followers/components/account.jsx
@@ -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 = (
-
+
);
}
diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/assets/javascripts/components/features/home_timeline/index.jsx
index 117a4a72df..e4f4fa7c71 100644
--- a/app/assets/javascripts/components/features/home_timeline/index.jsx
+++ b/app/assets/javascripts/components/features/home_timeline/index.jsx
@@ -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 (
-
+
);
diff --git a/app/assets/javascripts/components/features/mentions_timeline/index.jsx b/app/assets/javascripts/components/features/mentions_timeline/index.jsx
index 9f1caa2350..8583f59a67 100644
--- a/app/assets/javascripts/components/features/mentions_timeline/index.jsx
+++ b/app/assets/javascripts/components/features/mentions_timeline/index.jsx
@@ -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 (
-
+
);
diff --git a/app/assets/javascripts/components/features/public_timeline/index.jsx b/app/assets/javascripts/components/features/public_timeline/index.jsx
index 445a4fc633..c3da09a09b 100644
--- a/app/assets/javascripts/components/features/public_timeline/index.jsx
+++ b/app/assets/javascripts/components/features/public_timeline/index.jsx
@@ -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 (
-
+
);
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 d855176f22..1f46b956e8 100644
--- a/app/assets/javascripts/components/features/status/components/action_bar.jsx
+++ b/app/assets/javascripts/components/features/status/components/action_bar.jsx
@@ -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 (
);
diff --git a/app/assets/javascripts/components/locales/de.jsx b/app/assets/javascripts/components/locales/de.jsx
new file mode 100644
index 0000000000..e6f4a24917
--- /dev/null
+++ b/app/assets/javascripts/components/locales/de.jsx
@@ -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;
diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx
new file mode 100644
index 0000000000..a28c84b031
--- /dev/null
+++ b/app/assets/javascripts/components/locales/en.jsx
@@ -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;
diff --git a/app/assets/javascripts/components/locales/index.jsx b/app/assets/javascripts/components/locales/index.jsx
new file mode 100644
index 0000000000..212cbcee5a
--- /dev/null
+++ b/app/assets/javascripts/components/locales/index.jsx
@@ -0,0 +1,11 @@
+import en from './en';
+import de from './de';
+
+const locales = {
+ en,
+ de
+};
+
+export default function getMessagesForLocale (locale) {
+ return locales[locale];
+};
diff --git a/app/assets/javascripts/components/reducers/search.jsx b/app/assets/javascripts/components/reducers/search.jsx
index f3ee17f60a..9c20418639 100644
--- a/app/assets/javascripts/components/reducers/search.jsx
+++ b/app/assets/javascripts/components/reducers/search.jsx
@@ -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',
diff --git a/config/application.rb b/config/application.rb
index e992c2481b..114de57fb5 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -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')
diff --git a/config/locales/de.yml b/config/locales/de.yml
new file mode 100644
index 0000000000..648be5db20
--- /dev/null
+++ b/config/locales/de.yml
@@ -0,0 +1,59 @@
+---
+de:
+ about:
+ about_instance: "%{instance} ist eine Instanz von Mastodon."
+ about_mastodon: Mastodon ist ein freier, quelloffener soziales Netzwerkserver. Eine dezentralisierte 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 sozialen Netzwerk 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: "…"
diff --git a/config/locales/devise.de.yml b/config/locales/devise.de.yml
new file mode 100644
index 0000000000..181502f9c4
--- /dev/null
+++ b/config/locales/devise.de.yml
@@ -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."
diff --git a/config/locales/doorkeeper.de.yml b/config/locales/doorkeeper.de.yml
new file mode 100644
index 0000000000..0c606f6a28
--- /dev/null
+++ b/config/locales/doorkeeper.de.yml
@@ -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
diff --git a/config/locales/en.yml b/config/locales/en.yml
index ab16ed0821..426f3928a1 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -55,3 +55,5 @@ en:
stream_entries:
favourited: favourited a post by
is_now_following: is now following
+ will_paginate:
+ page_gap: "…"
diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml
new file mode 100644
index 0000000000..6e6758df28
--- /dev/null
+++ b/config/locales/simple_form.de.yml
@@ -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'
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index bd22a1f3d3..b8a69a0751 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -1,8 +1,6 @@
---
en:
simple_form:
- error_notification:
- default_message: 'Please review the problems below:'
labels:
defaults:
avatar: Avatar