Commit 99175292 authored by Eugen Rochko's avatar Eugen Rochko

Add react-intl

parent a235ef58
{
"presets": ["react-app"],
"plugins": [
[
"react-intl", {
"messagesDir": "./build/messages",
"enforceDescriptions": false
}
]
]
}
......@@ -9,17 +9,24 @@
"react": "^15.5.3",
"react-custom-scrollbars": "^4.1.2",
"react-dom": "^15.5.3",
"react-dropdown": "^1.3.0",
"react-intl": "^2.4.0",
"react-motion": "^0.4.7",
"react-redux": "^5.0.5",
"react-router-dom": "^4.0.0",
"react-snapshot": "^1.1.0",
"redux": "^3.7.1",
"redux-thunk": "^2.2.0",
"reselect": "^3.0.1"
"reselect": "^3.0.1",
"twemoji": "^2.5.0"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-plugin-react-intl": "^2.3.1",
"babel-preset-react-app": "^3.0.2",
"node-sass": "^4.5.2",
"npm-run-all": "^4.0.2",
"react-intl-translations-manager": "^5.0.0",
"react-scripts": "0.9.5"
},
"scripts": {
......@@ -31,7 +38,9 @@
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
"deploy": "gh-pages -d build",
"translate": "NODE_ENV=production babel ./src >/dev/null",
"manage:translations": "node ./translationRunner.js"
},
"eslintConfig": {
"extends": "react-app"
......
......@@ -8,15 +8,26 @@ import Home from './Home';
import Sponsorship from './Sponsorship';
import ScrollToTop from './ScrollToTop';
const App = () => (
<Router>
<ScrollToTop>
<div className='app'>
<Route exact path='/' component={Home} />
<Route path='/sponsors' component={Sponsorship} />
</div>
</ScrollToTop>
</Router>
import { addLocaleData, IntlProvider } from 'react-intl';
import en from 'react-intl/locale-data/en';
addLocaleData([...en]);
const messages = require.context('./locales/', false, /\.json$/);
const messagesForLocale = locale => messages[`./${locale}.json`];
const App = ({ usersLocale }) => (
<IntlProvider locale={usersLocale} messages={messagesForLocale(usersLocale)}>
<Router>
<ScrollToTop>
<div className='app'>
<Route exact path='/' component={Home} />
<Route path='/sponsors' component={Sponsorship} />
</div>
</ScrollToTop>
</Router>
</IntlProvider>
);
export default App;
import App from './App';
import { connect } from 'react-redux';
const mapStateToProps = state => ({
usersLocale: state.locale,
});
export default connect(mapStateToProps)(App);
import React from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
const Credits = () => (
<div className='credits' id='credits'>
<div className='container row optional-row'>
<div className='funding'>
<h3>Sponsors</h3>
<p>Mastodon is free, open-source software. There is no advertising, monetizing, or venture capital. Your donations directly support full-time development of the project.</p>
<a href='https://patreon.com/mastodon' className='cta button'>Support on Patreon</a> <Link to='/sponsors' className='cta button alt'>View sponsors</Link>
<h3><FormattedMessage id='credits.sponsors' defaultMessage='Sponsors' /></h3>
<p><FormattedMessage id='credits.support_text' defaultMessage='Mastodon is free, open-source software. There is no advertising, monetizing, or venture capital. Your donations directly support full-time development of the project.' /></p>
<a href='https://patreon.com/mastodon' className='cta button'><FormattedMessage id='credits.support_on_patreon' defaultMessage='Support on Patreon' /></a> <Link to='/sponsors' className='cta button alt'><FormattedMessage id='credits.view_sponsors' defaultMessage='View sponsors' /></Link>
</div>
<div className='branding'>
<h3>Branding</h3>
<p>Download logos, icons, and elephants</p>
<a href='/press-kit.zip' className='cta button alt'>Get press kit</a>
<h3><FormattedMessage id='credits.branding' defaultMessage='Branding' /></h3>
<p><FormattedMessage id='credits.branding_text' defaultMessage='Download logos, icons, and elephants' /></p>
<a href='/press-kit.zip' className='cta button alt'><FormattedMessage id='credits.get_press_kit' defaultMessage='Get press kit' /></a>
</div>
</div>
<div className='container color-scheme'>
<h3>Mastodon color scheme</h3>
<h3><FormattedMessage id='credits.color_scheme' defaultMessage='Mastodon color scheme' /></h3>
<div className='row'>
<div className='color-swatch color-1'>#282c37</div>
<div className='color-swatch color-2'>#9baec8</div>
......
import React from 'react';
import { FormattedHTMLMessage as FormattedMessage } from 'react-intl';
const Features = () => (
<div id='features'>
<div className='feature container'>
<div className='right visual'><i className='ion-android-globe' /></div>
<div className='left text'>
<h2>Find your perfect community</h2>
<p>Mastodon isnt one place and one set of rules: its <strong>thousands of unique, interconnected communities</strong> to choose from, filled with different people, interests, languages, and needs. Don’t like the rules? You’re free to join any community you like, or better yet: you can host your own, on your own terms!</p>
<h2><FormattedMessage id='features.find_your_community' defaultMessage='Find your perfect community' /></h2>
<p><FormattedMessage id='features.find_your_community_text' defaultMessage='Mastodon isn’t one place and one set of rules: it’s <strong>thousands of unique, interconnected communities</strong> to choose from, filled with different people, interests, languages, and needs. Don’t like the rules? You’re free to join any community you like, or better yet: you can host your own, on your own terms!' /></p>
</div>
</div>
<div className='feature container'>
<div className='left visual'><i className='ion-chatboxes' /></div>
<div className='right text'>
<h2>Take control of your content</h2>
<p>With powerful tools to <strong>control who sees your posts</strong> and a <strong>500-character limit</strong>, Mastodon empowers you to share your ideas, unabridged. The best part? <strong>All posts are in chronological order</strong>, not “optimized” to push ads into your timeline. With apps for iOS, Android, and every other platform imaginable, <strong>Mastodon is always at your fingertips</strong>.</p>
<h2><FormattedMessage id='features.take_control' defaultMessage='Take control of your content' /></h2>
<p><FormattedMessage id='features.take_control_text' defaultMessage='With powerful tools to <strong>control who sees your posts</strong> and a <strong>500-character limit</strong>, Mastodon empowers you to share your ideas, unabridged. The best part? <strong>All posts are in chronological order</strong>, not “optimized” to push ads into your timeline. With apps for iOS, Android, and every other platform imaginable, <strong>Mastodon is always at your fingertips</strong>.' /></p>
</div>
</div>
<div className='feature container'>
<div className='right visual'><i className='ion-ios-people' /></div>
<div className='left text'>
<h2>Putting the user first</h2>
<p>Youre a person, not a product. Mastodon is a free, open-source development that has been crowdfunded, not financed. All instances are <strong>independently owned, operated, and moderated</strong>. There is no monopoly by a single commercial company, no ads, and no tracking. <strong>Mastodon works for you</strong>, and not the other way around.</p>
<h2><FormattedMessage id='features.user_first' defaultMessage='Putting the user first' /></h2>
<p><FormattedMessage id='features.user_first_text' defaultMessage='You’re a person, not a product. Mastodon is a free, open-source development that has been crowdfunded, not financed. All instances are <strong>independently owned, operated, and moderated</strong>. There is no monopoly by a single commercial company, no ads, and no tracking. <strong>Mastodon works for you</strong>, and not the other way around.' /></p>
</div>
</div>
<div className='feature container'>
<div className='left visual'><i className='ion-bonfire' /></div>
<div className='right text'>
<h2>Feel safe in your community</h2>
<p>Mastodon comes with <strong>effective anti-abuse tools</strong> to help protect yourself from online abuse. With small, interconnected communities, it means that there are <strong>more moderators</strong> you can approach to help with a situation. This also means you can choose who sees your posts: friends, your community, or the entire fediverse.</p>
<h2><FormattedMessage id='features.safety' defaultMessage='Feel safe in your community' /></h2>
<p><FormattedMessage id='features.safety_text' defaultMessage='Mastodon comes with <strong>effective anti-abuse tools</strong> to help protect yourself from online abuse. With small, interconnected communities, it means that there are <strong>more moderators</strong> you can approach to help with a situation. This also means you can choose who sees your posts: friends, your community, or the entire fediverse.' /></p>
</div>
</div>
<div className='feature container'>
<div className='additional-features'>
<h3>Additional features</h3>
<h3><FormattedMessage id='features.additional' defaultMessage='Additional features' /></h3>
<ul>
<li><i className='ion-android-checkbox' /><div>Robust anti-abuse tools</div></li>
<li><i className='ion-android-checkbox' /><div>Flexible post filtering</div></li>
<li><i className='ion-android-checkbox' /><div>A huge audience</div></li>
<li><i className='ion-android-checkbox' /><div>Easily deploy your own</div></li>
<li><i className='ion-android-checkbox' /><div>They're called toots</div></li>
<li><i className='ion-android-checkbox' /><div>Embed media in your posts</div></li>
<li><i className='ion-android-checkbox' /><div>Built on open web standards</div></li>
<li><i className='ion-android-checkbox' /><div>Spoiler warnings</div></li>
<li><i className='ion-android-checkbox' /><div>You decide what's relevant</div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.anti_abuse' defaultMessage='Robust anti-abuse tools' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.post_filtering' defaultMessage='Flexible post filtering' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.huge_audience' defaultMessage='A huge audience' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.deploy_own' defaultMessage='Easily deploy your own' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.toots' defaultMessage='They’re called toots' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.media_embedding' defaultMessage='Embed media in your posts' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.open_standards' defaultMessage='Built on open web standards' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.spoiler_warnings' defaultMessage='Spoiler warnings' /></div></li>
<li><i className='ion-android-checkbox' /><div><FormattedMessage id='features.relevancy' defaultMessage='You decide what’s relevant' /></div></li>
</ul>
</div>
</div>
......
import React from 'react';
import { FormattedHTMLMessage as FormattedMessage } from 'react-intl';
import Features from './Features';
import Wizard from './WizardContainer';
......@@ -24,11 +25,11 @@ const Home = () => (
<Navigation />
<div className='text'>
<h1>Social networking, <strong>back in your hands</strong></h1>
<p>The worlds largest free, open-source, decentralized microblogging network</p>
<h1><FormattedMessage id='home.headline' defaultMessage='Social networking, <strong>back in your hands</strong>' /></h1>
<p><FormattedMessage id='home.tagline' defaultMessage='The world’s largest free, open-source, decentralized microblogging network' /></p>
<a href='#getting-started' className='cta button'>Get started</a>
<a href='#how-it-works' className='cta button alt'>How it works</a>
<a href='#getting-started' className='cta button'><FormattedMessage id='home.get_started' defaultMessage='Get started' /></a>
<a href='#how-it-works' className='cta button alt'><FormattedMessage id='home.how_it_works' defaultMessage='How it works' /></a>
</div>
<div className='hero'>
......@@ -45,7 +46,7 @@ const Home = () => (
<div className='as-seen-on'>
<div className='container'>
<h2>As seen on</h2>
<h2><FormattedMessage id='home.as_seen_on' defaultMessage='As seen on' /></h2>
<div className='logo-grid'>
<div>
......@@ -66,9 +67,9 @@ const Home = () => (
<div className='bottom-cta'>
<div className='container'>
<h3>Install your own</h3>
<p>If you are interested in running your own instance &mdash; for your friends, family or organization &mdash; you can get started by reading the installation documentation. You only host your own users and the content that they subscribe to.</p>
<a href='https://github.com/tootsuite/documentation#running-mastodon' className='cta button alt'>Read the docs</a>
<h3><FormattedMessage id='home.install_your_own' defaultMessage='Install your own' /></h3>
<p><FormattedMessage id='home.install_your_own_text' defaultMessage='If you are interested in running your own instance &mdash; for your friends, family or organization &mdash; you can get started by reading the installation documentation. You only host your own users and the content that they subscribe to.' /></p>
<a href='https://github.com/tootsuite/documentation#running-mastodon' className='cta button alt'><FormattedMessage id='home.read_the_docs' defaultMessage='Read the docs' /></a>
</div>
</div>
......
import React from 'react';
import { FormattedMessage } from 'react-intl';
import elephantGrid from './assets/instance_badges2.png';
......@@ -7,11 +8,11 @@ const HowItWorks = () => (
<div className='container'>
<div className='row'>
<div className='specs'>
<h2>How it works</h2>
<h2><FormattedMessage id='how_it_works.how_it_works' defaultMessage='How it works' /></h2>
<p>Anyone can run a server of Mastodon. Each server hosts individual user accounts, the content they produce, and the content they subscribe to.</p>
<p>Each user account has a globally unique name (e.g. @user@example.com), consisting of the local username (@user), and the domain name of the server it is on (example.com).</p>
<p>Users can follow each other, regardless of where they're hosted &mdash; when a local user follows a user from a different server, the server subscribes to that user's updates for the first time.</p>
<p><FormattedMessage id='how_it_works.how_it_works_text1' defaultMessage='Anyone can run a server of Mastodon. Each server hosts individual user accounts, the content they produce, and the content they subscribe to.' /></p>
<p><FormattedMessage id='how_it_works.how_it_works_text2' defaultMessage='Each user account has a globally unique name (e.g. @user@example.com), consisting of the local username (@user), and the domain name of the server it is on (example.com).' /></p>
<p><FormattedMessage id='how_it_works.how_it_works_text3' defaultMessage='Users can follow each other, regardless of where they’re hosted &mdash; when a local user follows a user from a different server, the server subscribes to that user’s updates for the first time.' /></p>
</div>
<div className='network-grid'>
......@@ -20,8 +21,8 @@ const HowItWorks = () => (
</div>
<div className='benefits'>
<h3>Why is that cool?</h3>
<p>Servers are run independently by different people and organizations. They can apply wildly different moderation policies, so you can find or make one that fits your taste perfectly. A decentralized network is harder for governments to censor. If one server goes bankrupt or starts acting unethically, the network persists so you never have to worry about migrating your friends and audience to a yet another platform again.</p>
<h3><FormattedMessage id='how_it_works.why_its_cool' defaultMessage='Why is that cool?' /></h3>
<p><FormattedMessage id='how_it_works.why_its_cool_text' defaultMessage='Servers are run independently by different people and organizations. They can apply wildly different moderation policies, so you can find or make one that fits your taste perfectly. A decentralized network is harder for governments to censor. If one server goes bankrupt or starts acting unethically, the network persists so you never have to worry about migrating your friends and audience to a yet another platform again.' /></p>
</div>
</div>
</div>
......
import React from 'react';
import Dropdown from 'react-dropdown';
import flagEn from 'twemoji/2/svg/1f1ec-1f1e7.svg';
import flagJa from 'twemoji/2/svg/1f1ef-1f1f5.svg';
const options = [
{ value: 'en', label: <span><img src={flagEn} className='emoji' alt='' /> English</span> },
{ value: 'ja', label: <span><img src={flagJa} className='emoji' alt='' /> 日本語</span> }
];
const LanguageSelect = ({ value, onChange }) => (
<Dropdown
options={options}
value={{ value, label: '文A' }}
onChange={onChange}
/>
);
export default LanguageSelect;
import LanguageSelect from './LanguageSelect';
import { connect } from 'react-redux';
import { changeLocale } from './actions';
const mapStateToProps = state => ({
value: state.locale
});
const mapDispatchToProps = dispatch => ({
onChange: option => dispatch(changeLocale(option.value))
});
export default connect(mapStateToProps, mapDispatchToProps)(LanguageSelect);
import React from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import githubLogo from './assets/github-logo.svg';
import mastodonLogo from './assets/logo_full.svg';
const Navigation = () => (
......@@ -15,14 +15,10 @@ const Navigation = () => (
</ul>
<ul className='right'>
<li className='optional-link'><a href='https://discourse.joinmastodon.org'>Support</a></li>
<li className='optional-link-2'><a href='https://github.com/tootsuite/documentation'>Documentation</a></li>
<li><Link to='/sponsors'>Sponsors</Link></li>
<li>
<a href='https://github.com/tootsuite/mastodon'>
Source code <img className='link-logo after' src={githubLogo} alt='Github Logo'/>
</a>
</li>
<li className='optional-link'><a href='https://discourse.joinmastodon.org'><FormattedMessage id='nav.support' defaultMessage='Support' /></a></li>
<li className='optional-link-2'><a href='https://github.com/tootsuite/documentation'><FormattedMessage id='nav.docs' defaultMessage='Documentation' /></a></li>
<li><Link to='/sponsors'><FormattedMessage id='nav.sponsors' defaultMessage='Sponsors' /></Link></li>
<li><a href='https://github.com/tootsuite/mastodon'><FormattedMessage id='nav.code' defaultMessage='Source code' /></a></li>
</ul>
</div>
);
......
import React from 'react';
import { Scrollbars } from 'react-custom-scrollbars';
import { FormattedHTMLMessage as FormattedMessage } from 'react-intl';
import Navigation from './Navigation';
......@@ -25,8 +26,8 @@ const Sponsorship = () => (
<Navigation />
<div className='text'>
<h2>Sponsors of Mastodon</h2>
<p className='lead'>Mastodon is <strong>free, open-source software</strong>. There is no advertising, monetizing, or venture capital. <strong>Your donations directly support full-time development of the project</strong>. Thank you to the following people and companies:</p>
<h2><FormattedMessage id='sponsorship.sponsors_of_mastodon' defaultMessage='Sponsors of Mastodon' /></h2>
<p className='lead'><FormattedMessage id='sponsorship.thanks_to' defaultMessage='Mastodon is <strong>free, open-source software</strong>. There is no advertising, monetizing, or venture capital. <strong>Your donations directly support full-time development of the project</strong>. Thank you to the following people and companies:' /></p>
</div>
{/*<div className='container'>
......@@ -40,12 +41,12 @@ const Sponsorship = () => (
<div className='container'>
<div className='text cta-text'>
<a href='https://patreon.com/mastodon' className='cta button'>Become a sponsor</a>
<a href='https://patreon.com/mastodon' className='cta button'><FormattedMessage id='sponsorship.become_a_sponsor' defaultMessage='Become a sponsor' /></a>
</div>
<div className='tier'>
<h3>Silver sponsors</h3>
<p><strong>Silver sponsors</strong> are those that have pledged $40 to $99 to Mastodon.</p>
<h3><FormattedMessage id='sponsorship.silver_sponsors' defaultMessage='Silver sponsors' /></h3>
<p><FormattedMessage id='sponsorship.silver_sponsors_text' defaultMessage='<strong>Silver sponsors</strong> are those that have pledged $40 to $99 to Mastodon.' /></p>
<div className='sponsors-list--badges'>
<ul>
......@@ -68,8 +69,8 @@ const Sponsorship = () => (
<hr />
<div className='tier'>
<h3>Sponsors</h3>
<p><strong>Sponsors</strong> are those that pledged $10 to $39 to Mastodon.</p>
<h3><FormattedMessage id='sponsorship.sponsors' defaultMessage='Sponsors' /></h3>
<p><FormattedMessage id='sponsorship.sponsors_text' defaultMessage='<strong>Sponsors</strong> are those that pledged $10 to $39 to Mastodon.' /></p>
<div className='sponsors-list--names'>
<Scrollbars style={{ height: 500 }} renderThumbVertical={renderThumb}>
......@@ -704,9 +705,9 @@ const Sponsorship = () => (
<div className='final-cta'>
<div className='container'>
<h3>Support the project</h3>
<p>Every little bit helps, and we appreciate all contributions.</p>
<a href='https://patreon.com/mastodon' className='cta button alt'>Become a sponsor</a>
<h3><FormattedMessage id='sponsorship.support_the_project' defaultMessage='Support the project' /></h3>
<p><FormattedMessage id='sponsorship.every_bit_helps' defaultMessage='Every little bit helps, and we appreciate all contributions.' /></p>
<a href='https://patreon.com/mastodon' className='cta button alt'><FormattedMessage id='sponsorship.become_a_sponsor' defaultMessage='Become a sponsor' /></a>
</div>
</div>
</div>
......
import React from 'react';
import WizardRow from './WizardRow';
import { Scrollbars } from 'react-custom-scrollbars';
import { FormattedHTMLMessage as FormattedMessage, injectIntl, defineMessages } from 'react-intl';
export default class Wizard extends React.PureComponent {
const messages = defineMessages({
search: { id: 'wizard.search', defaultMessage: 'Search for an instance' }
});
class Wizard extends React.PureComponent {
componentDidMount () {
this.props.onMount();
......@@ -27,28 +32,29 @@ export default class Wizard extends React.PureComponent {
}
render () {
const { instances, searchValue } = this.props;
const { instances, searchValue, intl } = this.props;
const hasValue = searchValue.length > 0;
return (
<div className='wizard-page' id='getting-started'>
<h2><strong>Get started:</strong> Choose an instance</h2>
<p>Each server is a separate, independently owned gateway into the fediverse. You can talk to your friends regardless of which one you choose, but each will have different moderation policies and interests, so choose the one that feels the most comfortable to you. </p>
<h2><FormattedMessage id='wizard.get_started' defaultMessage='<strong>Get started:</strong> Choose an instance' /></h2>
<p><FormattedMessage id='wizard.text' defaultMessage='Each server is a separate, independently owned gateway into the fediverse. You can talk to your friends regardless of which one you choose, but each will have different moderation policies and interests, so choose the one that feels the most comfortable to you.' /></p>
<div className='wizard-controls'>
<div className='external-wizard'>
<a className='cta button' target='_blank' href="https://instances.social">Help me choose</a>
<a className='cta button' target='_blank' href="https://instances.social"><FormattedMessage id='wizard.help_me_choose' defaultMessage='Help me choose' /></a>
</div>
<div className='spacer'></div>
<div className='search'>
<label for='instance-search' className='accessibly-hidden'>Search for an instance</label>
<label htmlFor='instance-search' className='accessibly-hidden'><FormattedMessage id='wizard.search' defaultMessage='Search for an instance' /></label>
<input
id='instance-search'
className='search__input'
type='text'
placeholder='Search'
placeholder={intl.formatMessage(messages.search)}
value={searchValue}
onChange={this.handleChange}
/>
......@@ -62,21 +68,23 @@ export default class Wizard extends React.PureComponent {
<div className='wizard'>
<div className='wizard-header'>
<div className='wizard-column'>Server</div>
<div className='wizard-column optional-column'>Stability</div>
<div className='wizard-column'>Population</div>
<div className='wizard-column optional-column'>Theme</div>
<div className='wizard-column'><FormattedMessage id='wizard.column.server' defaultMessage='Server' /></div>
<div className='wizard-column optional-column'><FormattedMessage id='wizard.column.stability' defaultMessage='Stability' /></div>
<div className='wizard-column'><FormattedMessage id='wizard.column.population' defaultMessage='Population' /></div>
<div className='wizard-column optional-column'><FormattedMessage id='wizard.column.theme' defaultMessage='Theme' /></div>
</div>
<Scrollbars className='wizard-content' style={{ height: 500 }} renderThumbVertical={this.renderThumb}>
{instances.map(item =>
<WizardRow key={item._id} instance={item} />
<WizardRow key={item.id} instance={item} />
)}
</Scrollbars>
</div>
<p className='hint'><strong>Tip:</strong><span>This isn't just your home, it's also the address that other people can find you by. It'll be <samp>@username@example.com</samp> like choosing an email address.</span></p>
<p className='hint'><FormattedMessage tagName='strong' id='wizard.tip' defaultMessage='Tip:' /><FormattedMessage id='wizard.tip_text' defaultMessage='This isn’t just your home, it’s also the address that other people can find you by. It’ll be <samp>@username@example.com</samp> like choosing an email address.' /></p>
</div>
);
}
}
export default injectIntl(Wizard);
import React from 'react';
import { defineMessages, injectIntl } from 'react-intl';
const WizardRow = ({ instance }) => {
const messages = defineMessages({
stable: { id: 'wizard_row.stability.stable', defaultMessage: 'Stable' },
intermittent: { id: 'wizard_row.stability.intermittent', defaultMessage: 'Intermittent' },
awful: { id: 'wizard_row.stability.awful', defaultMessage: 'Awful' },
full: { id: 'wizard_row.population.full', defaultMessage: 'Full' },
medium: { id: 'wizard_row.population.medium', defaultMessage: 'Medium' },
new: { id: 'wizard_row.population.new', defaultMessage: 'New' }
});
const WizardRow = ({ instance, intl }) => {
const theme = (instance.info && instance.info.topic) || 'General';
let stabilityColor, stabilityLabel,
populationColor, populationLabel;
if (instance.uptime > 0.95) {
stabilityLabel = 'Stable';
stabilityLabel = intl.formatMessage(messages.stable);
stabilityColor = 'green';
} else if (instance.uptime > 0.50) {
stabilityLabel = 'Intermittent'
stabilityLabel = intl.formatMessage(messages.intermittent);
stabilityColor = 'yellow';
} else {
stabilityLabel = 'Awful'
stabilityLabel = intl.formatMessage(messages.awful);
stabilityColor = 'red';
}
if (!instance.open_registrations) {
populationLabel = 'Full';
populationLabel = intl.formatMessage(messages.full);
populationColor = 'red';
} else if (instance.users > 10000) {
populationLabel = 'Medium';
populationLabel = intl.formatMessage(messages.medium);
populationColor = 'yellow';
} else {
populationLabel = 'New';
populationLabel = intl.formatMessage(messages.new);
populationColor = 'green';
}
......@@ -38,4 +48,4 @@ const WizardRow = ({ instance }) => {
);
};
export default WizardRow;
export default injectIntl(WizardRow);
......@@ -4,6 +4,7 @@ const INSTANCES_API_TOKEN = 'JEzPe4Ff5c5WA7k4IP5tx0rJMDzEMFxhmXXZvBG4LFSF0Almf0e
export const INSTANCES_FETCH_SUCCESS = 'INSTANCES_FETCH_SUCCESS';
export const SEARCH_VALUE_CHANGE = 'SEARCH_VALUE_CHANGE';
export const LOCALE_CHANGE = 'LOCALE_CHANGE';
export function fetchInstances() {
return (dispatch, getState) => {
......@@ -30,3 +31,10 @@ export function changeSearchValue(data) {
data,
};
};
export function changeLocale(data) {
return {
type: LOCALE_CHANGE,
data,
};
};
......@@ -4,14 +4,14 @@ import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
import App from './App';
import AppContainer from './AppContainer';
import './index.css';
const store = createStore(reducer, applyMiddleware(thunk));
render(
<Provider store={store}>
<App />
<AppContainer />
</Provider>,
document.getElementById('root')
);
......@@ -9,3 +9,4 @@
@import 'scss/HowItWorks.scss';
@import 'scss/Credits.scss';
@import 'scss/Sponsorship.scss';
@import 'scss/LanguageSelect.scss';
{
"credits.branding": "Branding",
"credits.branding_text": "Download logos, icons, and elephants",
"credits.color_scheme": "Mastodon color scheme",
"credits.get_press_kit": "Get press kit",
"credits.sponsors": "Sponsors",
"credits.support_on_patreon": "Support on Patreon",
"credits.support_text": "Mastodon is free, open-source software. There is no advertising, monetizing, or venture capital. Your donations directly support full-time development of the project.",
"credits.view_sponsors": "View sponsors",
"features.additional": "Additional features",
"features.anti_abuse": "Robust anti-abuse tools",
"features.deploy_own": "Easily deploy your own",
"features.find_your_community": "Find your perfect community",
"features.find_your_community_text": "Mastodon isn’t one place and one set of rules: it’s <strong>thousands of unique, interconnected communities</strong> to choose from, filled with different people, interests, languages, and needs. Don’t like the rules? You’re free to join any community you like, or better yet: you can host your own, on your own terms!",
"features.huge_audience": "A huge audience",
"features.media_embedding": "Embed media in your posts",
"features.open_standards": "Built on open web standards",
"features.post_filtering": "Flexible post filtering",
"features.relevancy": "You decide what’s relevant",
"features.safety": "Feel safe in your community",
"features.safety_text": "Mastodon comes with <strong>effective anti-abuse tools</strong> to help protect yourself from online abuse. With small, interconnected communities, it means that there are <strong>more moderators</strong> you can approach to help with a situation. This also means you can choose who sees your posts: friends, your community, or the entire fediverse.",
"features.spoiler_warnings": "Spoiler warnings",
"features.take_control": "Take control of your content",
"features.take_control_text": "With powerful tools to <strong>control who sees your posts</strong> and a <strong>500-character limit</strong>, Mastodon empowers you to share your ideas, unabridged. The best part? <strong>All posts are in chronological order</strong>, not “optimized” to push ads into your timeline. With apps for iOS, Android, and every other platform imaginable, <strong>Mastodon is always at your fingertips</strong>.",
"features.toots": "They’re called toots",
"features.user_first": "Putting the user first",
"features.user_first_text": "You’re a person, not a product. Mastodon is a free, open-source development that has been crowdfunded, not financed. All instances are <strong>independently owned, operated, and moderated</strong>. There is no monopoly by a single commercial company, no ads, and no tracking. <strong>Mastodon works for you</strong>, and not the other way around.",
"home.as_seen_on": "As seen on",
"home.get_started": "Get started",
"home.headline": "Social networking, <strong>back in your hands</strong>",
"home.how_it_works": "How it works",
"home.install_your_own": "Install your own",
"home.install_your_own_text": "If you are interested in running your own instance — for your friends, family or organization — you can get started by reading the installation documentation. You only host your own users and the content that they subscribe to.",
"home.read_the_docs": "Read the docs",
"home.tagline": "The world’s largest free, open-source, decentralized microblogging network",
"how_it_works.how_it_works": "How it works",
"how_it_works.how_it_works_text1": "Anyone can run a server of Mastodon. Each server hosts individual user accounts, the content they produce, and the content they subscribe to.",
"how_it_works.how_it_works_text2": "Each user account has a globally unique name (e.g. @user@example.com), consisting of the local username (@user), and the domain name of the server it is on (example.com).",
"how_it_works.how_it_works_text3": "Users can follow each other, regardless of where they’re hosted — when a local user follows a user from a different server, the server subscribes to that user’s updates for the first time.",
"how_it_works.why_its_cool": "Why is that cool?",
"how_it_works.why_its_cool_text": "Servers are run independently by different people and organizations. They can apply wildly different moderation policies, so you can find or make one that fits your taste perfectly. A decentralized network is harder for governments to censor. If one server goes bankrupt or starts acting unethically, the network persists so you never have to worry about migrating your friends and audience to a yet another platform again.",
"nav.code": "Source code",
"nav.docs": "Documentation",
"nav.sponsors": "Sponsors",
"nav.support": "Support",
"sponsorship.become_a_sponsor": "Become a sponsor",
"sponsorship.every_bit_helps": "Every little bit helps, and we appreciate all contributions.",
"sponsorship.silver_sponsors": "Silver sponsors",
"sponsorship.silver_sponsors_text": "<strong>Silver sponsors</strong> are those that have pledged $40 to $99 to Mastodon.",
"sponsorship.sponsors": "Sponsors",
"sponsorship.sponsors_of_mastodon": "Sponsors of Mastodon",
"sponsorship.sponsors_text": "<strong>Sponsors</strong> are those that pledged $10 to $39 to Mastodon.",
"sponsorship.support_the_project": "Support the project",
"sponsorship.thanks_to": "Mastodon is <strong>free, open-source software</strong>. There is no advertising, monetizing, or venture capital. <strong>Your donations directly support full-time development of the project</strong>. Thank you to the following people and companies:",
"wizard.column.population": "Population",
"wizard.column.server": "Server",
"wizard.column.stability": "Stability",
"wizard.column.theme": "Theme",
"wizard.get_started": "<strong>Get started:</strong> Choose an instance",
"wizard.help_me_choose": "Help me choose",
"wizard.search": "Search for an instance",
"wizard.text": "Each server is a separate, independently owned gateway into the fediverse. You can talk to your friends regardless of which one you choose, but each will have different moderation policies and interests, so choose the one that feels the most comfortable to you.",
"wizard.tip": "Tip:",
"wizard.tip_text": "This isn’t just your home, it’s also the address that other people can find you by. It’ll be <samp>@username@example.com</samp> like choosing an email address.",
"wizard_row.population.full": "Full",
"wizard_row.population.medium": "Medium",
"wizard_row.population.new": "New",
"wizard_row.stability.awful": "Awful",
"wizard_row.stability.intermittent": "Intermittent",
"wizard_row.stability.stable": "Stable"
}
\ No newline at end of file
[
]
\ No newline at end of file