Skip to content

Commit

Permalink
Merge pull request #22 from keremcubuk/react-intl
Browse files Browse the repository at this point in the history
React intl
  • Loading branch information
keremcubuk authored May 3, 2020
2 parents 4f3c5b5 + fed4d9c commit c6a12ea
Show file tree
Hide file tree
Showing 55 changed files with 2,387 additions and 146 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ While creating this project, I always need to a react-native standart for our te
<dd>It's natural to want to add pages (e.g. `/about`) to your application, and routing makes this possible.</dd>

<dt>Industry-standard i18n internationalization support</dt>
<dd>Scalable apps need to support multiple languages, easily add and support multiple languages with `react-native-localize`.</dd>
<dd>Scalable apps need to support multiple languages, easily add and support multiple languages with `react-intl`.</dd>

<dt>Static code analysis</dt>
<dd>Focus on writing new features without worrying about formatting or code quality. With the right editor setup, your code will automatically be formatted and linted as you work.</dd>
Expand Down
6 changes: 5 additions & 1 deletion app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import { Provider } from 'react-redux';

// Import root app
import App from './containers/App';
import LanguageProvider from 'containers/LanguageProvider';

import configureStore from './configureStore';
import { translationMessages } from './i18n';

// Create redux store with history
const initialState = {};
Expand All @@ -21,7 +23,9 @@ const store = configureStore(initialState);
function AppRoot() {
return (
<Provider store={store}>
<App />
<LanguageProvider messages={translationMessages}>
<App />
</LanguageProvider>
</Provider>
);
}
Expand Down
17 changes: 0 additions & 17 deletions app/containers/App/tests/__snapshots__/index.test.js.snap

This file was deleted.

12 changes: 3 additions & 9 deletions app/containers/App/tests/selectors.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import { makeSelectLocation } from 'containers/App/selectors';
// import { makeSelectApp } from 'containers/App/selectors';

describe('makeSelectLocation', () => {
describe('makeSelectApp', () => {
it('should select the location', () => {
const router = {
location: { pathname: '/foo' },
};
const mockedState = {
router,
};
expect(makeSelectLocation()(mockedState)).toEqual(router.location);
expect(true).toEqual(true);
});
});
2 changes: 1 addition & 1 deletion app/containers/HelpScreen/tests/index.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render } from 'react-testing-library';
import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';

import HelpScreen from '../index';
Expand Down
55 changes: 55 additions & 0 deletions app/containers/LanguageProvider/LocaleToggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
*
* LanguageToggle
*
*/

import React from 'react';
import { View, Picker } from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';

import { appLocales } from 'app/i18n';
import { changeLocale } from 'containers/LanguageProvider/actions';
import { makeSelectLocale } from 'containers/LanguageProvider/selectors';
import messages from './messages';

export function LocaleToggle(props) {
return (
<View>
<Picker
selectedValue={props.locale}
style={{ height: 50, width: 50 }}
onValueChange={itemValue => props.onLocaleToggle(itemValue)}>
{appLocales.map(locale => (
<Picker.Item key={locale} label={messages[locale].defaultMessage} value={locale} />
))}
</Picker>
</View>
);
}

LocaleToggle.propTypes = {
onLocaleToggle: PropTypes.func,
locale: PropTypes.string,
};

const mapStateToProps = createSelector(
makeSelectLocale(),
locale => ({
locale,
}),
);

export function mapDispatchToProps(dispatch) {
return {
onLocaleToggle: locale => dispatch(changeLocale(locale)),
dispatch,
};
}

export default connect(
mapStateToProps,
mapDispatchToProps,
)(LocaleToggle);
14 changes: 14 additions & 0 deletions app/containers/LanguageProvider/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
*
* LanguageProvider actions
*
*/

import { CHANGE_LOCALE } from './constants';

export function changeLocale(languageLocale) {
return {
type: CHANGE_LOCALE,
locale: languageLocale,
};
}
7 changes: 7 additions & 0 deletions app/containers/LanguageProvider/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
*
* LanguageProvider constants
*
*/

export const CHANGE_LOCALE = 'app/LanguageToggle/CHANGE_LOCALE';
44 changes: 44 additions & 0 deletions app/containers/LanguageProvider/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
*
* LanguageProvider
*
* this component connects the redux state language locale to the
* IntlProvider component and i18n messages (loaded from `app/translations`)
*/

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { IntlProvider } from 'react-intl';

import { makeSelectLocale } from './selectors';

global.Intl = require('intl');

export function LanguageProvider(props) {
return (
<IntlProvider
locale={props.locale}
key={props.locale}
messages={props.messages[props.locale]}
textComponent={React.Fragment}>
{React.Children.only(props.children)}
</IntlProvider>
);
}

LanguageProvider.propTypes = {
locale: PropTypes.string,
messages: PropTypes.object,
children: PropTypes.element.isRequired,
};

const mapStateToProps = createSelector(
makeSelectLocale(),
locale => ({
locale,
}),
);

export default connect(mapStateToProps)(LanguageProvider);
19 changes: 19 additions & 0 deletions app/containers/LanguageProvider/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* LocaleToggle Messages
*
* This contains all the text for the LanguageToggle component.
*/
import { defineMessages } from 'react-intl';

export const scope = 'app.containers.LocaleToggle';

export default defineMessages({
en: {
id: `${scope}.en`,
defaultMessage: 'en',
},
fr: {
id: `${scope}.fr`,
defaultMessage: 'fr',
},
});
26 changes: 26 additions & 0 deletions app/containers/LanguageProvider/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
*
* LanguageProvider reducer
*
*/

import produce from 'immer';

import { CHANGE_LOCALE } from './constants';
import { DEFAULT_LOCALE } from '../../i18n';

export const initialState = {
locale: DEFAULT_LOCALE,
};

/* eslint-disable default-case, no-param-reassign */
const languageProviderReducer = (state = initialState, action) =>
produce(state, draft => {
switch (action.type) {
case CHANGE_LOCALE:
draft.locale = action.locale;
break;
}
});

export default languageProviderReducer;
20 changes: 20 additions & 0 deletions app/containers/LanguageProvider/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createSelector } from 'reselect';
import { initialState } from './reducer';

/**
* Direct selector to the language domain
*/

const selectLanguage = state => state.language || initialState;

/**
* Select the language locale
*/

const makeSelectLocale = () =>
createSelector(
selectLanguage,
languageState => languageState.locale,
);

export { selectLanguage, makeSelectLocale };
15 changes: 15 additions & 0 deletions app/containers/LanguageProvider/tests/actions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { changeLocale } from '../actions';

import { CHANGE_LOCALE } from '../constants';

describe('LanguageProvider actions', () => {
describe('Change Local Action', () => {
it('has a type of CHANGE_LOCALE', () => {
const expected = {
type: CHANGE_LOCALE,
locale: 'de',
};
expect(changeLocale('de')).toEqual(expected);
});
});
});
48 changes: 48 additions & 0 deletions app/containers/LanguageProvider/tests/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import { render } from '@testing-library/react';
import { FormattedMessage, defineMessages } from 'react-intl';
import { Provider } from 'react-redux';

import ConnectedLanguageProvider, { LanguageProvider } from '../index';
import configureStore from '../../../configureStore';

import { translationMessages } from '../../../i18n';

const messages = defineMessages({
someMessage: {
id: 'some.id',
defaultMessage: 'This is some default message',
en: 'This is some en message',
},
});

describe('<LanguageProvider />', () => {
it('should render its children', () => {
const children = <h1>Test</h1>;
const { container } = render(
<LanguageProvider messages={messages} locale="en">
{children}
</LanguageProvider>,
);
expect(container.firstChild).not.toBeNull();
});
});

describe('<ConnectedLanguageProvider />', () => {
let store;

beforeAll(() => {
store = configureStore({});
});

it('should render the default language messages', () => {
const { queryByText } = render(
<Provider store={store}>
<ConnectedLanguageProvider messages={translationMessages}>
<FormattedMessage {...messages.someMessage} />
</ConnectedLanguageProvider>
</Provider>,
);
expect(queryByText(messages.someMessage.defaultMessage)).not.toBeNull();
});
});
22 changes: 22 additions & 0 deletions app/containers/LanguageProvider/tests/reducer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import languageProviderReducer from '../reducer';
import { CHANGE_LOCALE } from '../constants';

/* eslint-disable default-case, no-param-reassign */
describe('languageProviderReducer', () => {
it('returns the initial state', () => {
expect(languageProviderReducer(undefined, {})).toEqual({
locale: 'en',
});
});

it('changes the locale', () => {
expect(
languageProviderReducer(undefined, {
type: CHANGE_LOCALE,
locale: 'de',
}),
).toEqual({
locale: 'de',
});
});
});
11 changes: 11 additions & 0 deletions app/containers/LanguageProvider/tests/selectors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { selectLanguage } from '../selectors';

describe('selectLanguage', () => {
it('should select the global state', () => {
const globalState = {};
const mockedState = {
language: globalState,
};
expect(selectLanguage(mockedState)).toEqual(globalState);
});
});
Loading

0 comments on commit c6a12ea

Please sign in to comment.