Feature(langauge): Make client history language aware

Remove need for language aware links
This commit is contained in:
Berkeley Martinez
2016-06-20 21:01:14 -07:00
parent 1da593df26
commit 95aab958aa
11 changed files with 98 additions and 53 deletions

View File

@ -10,6 +10,7 @@ import {
} from 'react-router-redux';
import { render } from 'redux-epic';
import { createHistory } from 'history';
import useLangRoutes from './use-lang-routes.js';
import createApp from '../common/app';
import provideStore from '../common/app/provide-store';
@ -36,7 +37,7 @@ initialState.app.csrfToken = csrfToken;
const serviceOptions = { xhrPath: '/services', context: { _csrf: csrfToken } };
const history = createHistory();
const history = useLangRoutes(createHistory)();
const devTools = window.devToolsExtension ? window.devToolsExtension() : f => f;
const adjustUrlOnReplay = !!window.devToolsExtension;

44
client/use-lang-routes.js Normal file
View File

@ -0,0 +1,44 @@
import { addLang, getLangFromPath } from '../common/app/utils/lang.js';
function addLangToLocation(location, lang) {
if (!location) {
return location;
}
if (typeof location === 'string') {
return addLang(location, lang);
}
return {
...location,
pathname: addLang(location.pathname, lang)
};
}
function getLangFromLocation(location) {
if (!location) {
return location;
}
if (typeof location === 'string') {
return getLangFromPath(location);
}
return getLangFromPath(location.pathname);
}
export default function useLangRoutes(createHistory) {
return (options = {}) => {
let lang = 'en';
const history = createHistory(options);
const unsubscribeFromHistory = history.listen(nextLocation => {
lang = getLangFromLocation(nextLocation);
});
const push = location => history.push(addLangToLocation(location, lang));
const replace = location => history.replace(
addLangToLocation(location, lang)
);
return {
...history,
push,
replace,
unsubscribe() { unsubscribeFromHistory(); }
};
};
}

View File

@ -36,15 +36,15 @@ const mapStateToProps = createSelector(
isMapAlreadyLoaded,
showChallengeComplete
) => ({
shouldShowSignIn,
isSignedIn: !!username,
username,
points,
picture,
toast,
shouldShowSignIn,
isMapDrawerOpen,
isMapAlreadyLoaded,
showChallengeComplete
showChallengeComplete,
isSignedIn: !!username
})
);

View File

@ -5,6 +5,7 @@ const initialState = {
title: 'Learn To Code | Free Code Camp',
shouldShowSignIn: false,
user: '',
lang: '',
csrfToken: '',
windowHeight: 0,
navHeight: 0,

View File

@ -1,5 +1,5 @@
import React, { PropTypes } from 'react';
import LangLink from '../../../../utils/Language-Link.jsx';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import FA from 'react-fontawesome';
import PureComponent from 'react-pure-render/component';
@ -61,7 +61,7 @@ export class Block extends PureComponent {
className={ challengeClassName }
key={ title }
>
<LangLink to={ `/challenges/${blockName}/${dashedName}` }>
<Link to={ `/challenges/${blockName}/${dashedName}` }>
<span
onClick={ () => updateCurrentChallenge(challenge) }
>
@ -73,7 +73,7 @@ export class Block extends PureComponent {
''
}
</span>
</LangLink>
</Link>
</p>
);
});

View File

@ -1,6 +1,6 @@
import { modernChallenges, map, challenges } from './challenges';
import NotFound from '../components/NotFound/index.jsx';
import { addLang } from '../utils/add-lang';
import { addLang } from '../utils/lang';
export default {
path: '/:lang',

View File

@ -1,30 +0,0 @@
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { addLang } from './add-lang';
const mapStateToProps = state => ({ lang: state.app.lang });
export class LangLink extends React.Component {
static displayName = 'LangLink';
static propTypes = {
to: PropTypes.string,
lang: PropTypes.string
};
render() {
const {
to,
lang,
...props
} = this.props;
return (
<Link
to={ addLang(to, lang) }
{ ...props }
/>
);
}
}
export default connect(mapStateToProps)(LangLink);

View File

@ -1,13 +0,0 @@
import supportedLanguages from '../../utils/supported-languages';
const toLowerCase = String.prototype.toLowerCase;
export function addLang(url, lang) {
const maybeLang = toLowerCase.call(url.split('/')[1]);
if (supportedLanguages[maybeLang]) {
return url;
}
if (supportedLanguages[lang]) {
return `/${lang}${url}`;
}
return `/en${url}`;
}

39
common/app/utils/lang.js Normal file
View File

@ -0,0 +1,39 @@
import
supportedLanguages,
{ langTagRegex }
from '../../utils/supported-languages';
const toLowerCase = String.prototype.toLowerCase;
export function getLangFromPath(path) {
const maybeLang = toLowerCase.call(path.split('/')[1]);
if (supportedLanguages[maybeLang]) {
return maybeLang;
}
return 'en';
}
export function addLang(path, lang, primaryLang) {
// if maybeLang is supported continue
// if maybeLang is unsupported lang, remove and use lang
// if maybeLang is not lang tag, add lang tag.
// if both primary and lang are not lang tags default en
const maybeLang = toLowerCase.call(path.split('/')[1]);
const restUrl = path.split('/').slice(2).join('/');
if (supportedLanguages[maybeLang]) {
return path;
}
if (
langTagRegex.test(maybeLang) &&
!supportedLanguages[maybeLang]
) {
return `/${primaryLang || lang }/${restUrl}`;
}
if (supportedLanguages[primaryLang || lang]) {
return `/${primaryLang || lang}${path}`;
}
return `/en${path}`;
}

View File

@ -1,3 +1,4 @@
export const langTagRegex = /^[a-z]{2}(?:-[a-zA-Z]{2,3})?$/;
export default {
en: 'English',
es: 'Spanish'

View File

@ -1,9 +1,11 @@
import supportedLanguages from '../../common/utils/supported-languages';
import
supportedLanguages,
{ langTagRegex }
from '../../common/utils/supported-languages';
import passthroughs from '../utils/lang-passthrough-urls';
import debug from 'debug';
const log = debug('fcc:middlewares:lang');
const langTagRegex = /^[a-z]{2}(?:-[a-zA-Z]{2,3})?$/;
const toLowerCase = String.prototype.toLowerCase;
// redirect(statusOrUrl: String|Number, url?: String) => Void