Add language routing
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import LangLink from '../../../../utils/Language-Link.jsx';
|
||||
import { connect } from 'react-redux';
|
||||
import FA from 'react-fontawesome';
|
||||
import PureComponent from 'react-pure-render/component';
|
||||
@ -54,7 +54,7 @@ export class Block extends PureComponent {
|
||||
className={ challengeClassName }
|
||||
key={ title }
|
||||
>
|
||||
<Link to={ `/challenges/${blockName}/${dashedName}` }>
|
||||
<LangLink to={ `/challenges/${blockName}/${dashedName}` }>
|
||||
<span
|
||||
onClick={ () => updateCurrentChallenge(challenge) }
|
||||
>
|
||||
@ -66,7 +66,7 @@ export class Block extends PureComponent {
|
||||
''
|
||||
}
|
||||
</span>
|
||||
</Link>
|
||||
</LangLink>
|
||||
</p>
|
||||
);
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ export default function fetchChallengesSaga(action$, getState, { services }) {
|
||||
))
|
||||
.flatMap(({ type, payload: { dashedName, block } = {} }) => {
|
||||
const state = getState();
|
||||
const lang = state.app.languageTag;
|
||||
if (type === replaceChallenge) {
|
||||
const { challenge: newChallenge } = challengeSelector({
|
||||
...state,
|
||||
@ -38,8 +39,10 @@ export default function fetchChallengesSaga(action$, getState, { services }) {
|
||||
return Observable.just(null);
|
||||
}
|
||||
const options = { service: 'map' };
|
||||
options.params = { lang };
|
||||
if (type === fetchChallenge) {
|
||||
options.params = { dashedName, block };
|
||||
options.params.dashedName = dashedName;
|
||||
options.params.block = block;
|
||||
}
|
||||
return services.readService$(options)
|
||||
.flatMap(({ entities, result, redirect } = {}) => {
|
||||
|
@ -2,7 +2,7 @@ import { modernChallenges, map, challenges } from './challenges';
|
||||
import NotFound from '../components/NotFound/index.jsx';
|
||||
|
||||
export default {
|
||||
path: '/',
|
||||
path: '/:lang',
|
||||
childRoutes: [
|
||||
challenges,
|
||||
modernChallenges,
|
||||
|
42
common/app/utils/Language-Link.jsx
Normal file
42
common/app/utils/Language-Link.jsx
Normal file
@ -0,0 +1,42 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router';
|
||||
import supportedLanguages from '../../utils/supported-languages';
|
||||
|
||||
const toLowerCase = String.prototype.toLowerCase;
|
||||
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}`;
|
||||
}
|
||||
|
||||
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);
|
@ -195,6 +195,11 @@
|
||||
"theme": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"languageTag": {
|
||||
"type": "string",
|
||||
"description": "A IETF language tag",
|
||||
"default": "en"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
@ -256,6 +261,13 @@
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "updateTheme"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "updateLanguage"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
|
4
common/utils/supported-languages.js
Normal file
4
common/utils/supported-languages.js
Normal file
@ -0,0 +1,4 @@
|
||||
export default {
|
||||
en: 'English',
|
||||
es: 'Spanish'
|
||||
};
|
@ -83,5 +83,5 @@ export default function about(app) {
|
||||
}
|
||||
|
||||
router.get('/about', showAbout);
|
||||
app.use(router);
|
||||
app.use('/:lang', router);
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ function findNonprofit(name) {
|
||||
|
||||
export default function commit(app) {
|
||||
const router = app.loopback.Router();
|
||||
const api = app.loopback.Router();
|
||||
const { Pledge } = app.models;
|
||||
|
||||
router.get(
|
||||
@ -68,19 +69,20 @@ export default function commit(app) {
|
||||
renderDirectory
|
||||
);
|
||||
|
||||
router.post(
|
||||
api.post(
|
||||
'/commit/stop-commitment',
|
||||
sendNonUserToCommit,
|
||||
stopCommit
|
||||
);
|
||||
|
||||
router.post(
|
||||
api.post(
|
||||
'/commit/complete-goal',
|
||||
sendNonUserToCommit,
|
||||
completeCommitment
|
||||
);
|
||||
|
||||
app.use(router);
|
||||
app.use(api);
|
||||
app.use(':/lang', router);
|
||||
|
||||
function commitToNonprofit(req, res, next) {
|
||||
const { user } = req;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { defaultProfileImage } from '../../common/utils/constantStrings.json';
|
||||
import supportedLanguages from '../../common/utils/supported-languages';
|
||||
|
||||
const message =
|
||||
'Learn to Code and Help Nonprofits';
|
||||
@ -6,24 +7,37 @@ const message =
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
router.get('/', addDefaultImage, index);
|
||||
|
||||
app.use(
|
||||
'/:lang',
|
||||
(req, res, next) => {
|
||||
// add url language to request for all routers
|
||||
req._urlLang = req.params.lang;
|
||||
next();
|
||||
},
|
||||
router
|
||||
);
|
||||
app.use(router);
|
||||
|
||||
function addDefaultImage(req, res, next) {
|
||||
if (!req.user || req.user.picture) {
|
||||
return next();
|
||||
}
|
||||
req.user.picture = defaultProfileImage;
|
||||
return req.user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
return next();
|
||||
});
|
||||
return req.user.update$({ picture: defaultProfileImage })
|
||||
.subscribe(
|
||||
() => next(),
|
||||
next
|
||||
);
|
||||
}
|
||||
|
||||
function index(req, res, next) {
|
||||
if (!supportedLanguages[req._urlLang]) {
|
||||
return next();
|
||||
}
|
||||
|
||||
function index(req, res) {
|
||||
if (req.user) {
|
||||
return res.redirect('/challenges/current-challenge');
|
||||
}
|
||||
|
||||
return res.render('home', { title: message });
|
||||
}
|
||||
};
|
||||
|
@ -6,24 +6,32 @@ import secrets from '../../config/secrets';
|
||||
module.exports = function(app) {
|
||||
const router = app.loopback.Router();
|
||||
const User = app.models.User;
|
||||
router.get('/api/github', githubCalls);
|
||||
router.get('/chat', chat);
|
||||
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
|
||||
router.get('/twitch', twitch);
|
||||
router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
|
||||
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
|
||||
const noLangRouter = app.loopback.Router();
|
||||
noLangRouter.get('/api/github', githubCalls);
|
||||
noLangRouter.get('/chat', chat);
|
||||
noLangRouter.get('/twitch', twitch);
|
||||
noLangRouter.get('/unsubscribe/:email', unsubscribeMonthly);
|
||||
noLangRouter.get(
|
||||
'/unsubscribe-notifications/:email',
|
||||
unsubscribeNotifications
|
||||
);
|
||||
noLangRouter.get('/unsubscribe-quincy/:email', unsubscribeQuincy);
|
||||
noLangRouter.get('/submit-cat-photo', submitCatPhoto);
|
||||
noLangRouter.get(
|
||||
'/the-fastest-web-page-on-the-internet',
|
||||
theFastestWebPageOnTheInternet
|
||||
);
|
||||
noLangRouter.get('/shop/cancel-stickers', cancelStickers);
|
||||
noLangRouter.get('/shop/confirm-stickers', confirmStickers);
|
||||
|
||||
router.get('/unsubscribed', unsubscribed);
|
||||
router.get('/nonprofits', nonprofits);
|
||||
router.get('/nonprofits-form', nonprofitsForm);
|
||||
router.get('/unsubscribe/:email', unsubscribeMonthly);
|
||||
router.get('/unsubscribe-notifications/:email', unsubscribeNotifications);
|
||||
router.get('/unsubscribe-quincy/:email', unsubscribeQuincy);
|
||||
router.get('/unsubscribed', unsubscribed);
|
||||
router.get('/get-started', getStarted);
|
||||
router.get('/submit-cat-photo', submitCatPhoto);
|
||||
router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
|
||||
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
|
||||
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
|
||||
router.get('/stories', showTestimonials);
|
||||
router.get('/shop', showShop);
|
||||
router.get('/shop/cancel-stickers', cancelStickers);
|
||||
router.get('/shop/confirm-stickers', confirmStickers);
|
||||
router.get('/all-stories', showAllTestimonials);
|
||||
router.get('/terms', terms);
|
||||
router.get('/privacy', privacy);
|
||||
@ -34,12 +42,9 @@ module.exports = function(app) {
|
||||
);
|
||||
router.get('/code-of-conduct', codeOfConduct);
|
||||
router.get('/academic-honesty', academicHonesty);
|
||||
router.get(
|
||||
'/the-fastest-web-page-on-the-internet',
|
||||
theFastestWebPageOnTheInternet
|
||||
);
|
||||
|
||||
app.use(router);
|
||||
app.use(noLangRouter);
|
||||
app.use('/:lang', router);
|
||||
|
||||
function chat(req, res) {
|
||||
res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp');
|
||||
@ -242,12 +247,6 @@ module.exports = function(app) {
|
||||
});
|
||||
}
|
||||
|
||||
function getStarted(req, res) {
|
||||
res.render('resources/get-started', {
|
||||
title: 'How to get started with Free Code Camp'
|
||||
});
|
||||
}
|
||||
|
||||
function githubCalls(req, res, next) {
|
||||
var githubHeaders = {
|
||||
headers: {
|
||||
|
14
server/boot/a-react.js → server/boot/react.js
vendored
14
server/boot/a-react.js → server/boot/react.js
vendored
@ -12,8 +12,6 @@ const log = debug('fcc:react-server');
|
||||
// add routes here as they slowly get reactified
|
||||
// remove their individual controllers
|
||||
const routes = [
|
||||
'/videos',
|
||||
'/videos/*',
|
||||
'/challenges',
|
||||
'/challenges/*',
|
||||
'/map'
|
||||
@ -24,6 +22,12 @@ const devRoutes = [];
|
||||
export default function reactSubRouter(app) {
|
||||
var router = app.loopback.Router();
|
||||
|
||||
router.get('/videos', (req, res) => res.redirect('/map'));
|
||||
router.get(
|
||||
'/videos/:dashedName',
|
||||
(req, res) => res.redirect(`/challenges/${req.params.dashedName}`)
|
||||
);
|
||||
|
||||
// These routes are in production
|
||||
routes.forEach((route) => {
|
||||
router.get(route, serveReactApp);
|
||||
@ -35,13 +39,15 @@ export default function reactSubRouter(app) {
|
||||
});
|
||||
}
|
||||
|
||||
app.use(router);
|
||||
app.use('/:lang', router);
|
||||
|
||||
function serveReactApp(req, res, next) {
|
||||
const { lang } = req;
|
||||
const serviceOptions = { req };
|
||||
createApp({
|
||||
serviceOptions,
|
||||
location: req.path
|
||||
location: req.originalUrl,
|
||||
initialState: { app: { languageTag: lang } }
|
||||
})
|
||||
// if react-router does not find a route send down the chain
|
||||
.filter(({ redirect, props }) => {
|
@ -3,6 +3,7 @@ import moment from 'moment-timezone';
|
||||
import { Observable } from 'rx';
|
||||
import debugFactory from 'debug';
|
||||
|
||||
import supportedLanguages from '../../common/utils/supported-languages';
|
||||
import {
|
||||
frontEndChallengeId,
|
||||
dataVisChallengeId,
|
||||
@ -123,8 +124,9 @@ function buildDisplayChallenges(challengeMap = {}, timezone) {
|
||||
}
|
||||
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
var User = app.models.User;
|
||||
const router = app.loopback.Router();
|
||||
const api = app.loopback.Router();
|
||||
const User = app.models.User;
|
||||
function findUserByUsername$(username, fields) {
|
||||
return observeQuery(
|
||||
User,
|
||||
@ -145,39 +147,39 @@ module.exports = function(app) {
|
||||
router.get('/signin', getSignin);
|
||||
router.get('/signout', signout);
|
||||
router.get('/forgot', getForgot);
|
||||
router.post('/forgot', postForgot);
|
||||
api.post('/forgot', postForgot);
|
||||
router.get('/reset-password', getReset);
|
||||
router.post('/reset-password', postReset);
|
||||
api.post('/reset-password', postReset);
|
||||
router.get('/email-signup', getEmailSignup);
|
||||
router.get('/email-signin', getEmailSignin);
|
||||
router.get('/deprecated-signin', getDepSignin);
|
||||
router.get('/update-email', getUpdateEmail);
|
||||
router.get(
|
||||
api.get(
|
||||
'/toggle-lockdown-mode',
|
||||
sendNonUserToMap,
|
||||
toggleLockdownMode
|
||||
);
|
||||
router.get(
|
||||
api.get(
|
||||
'/toggle-announcement-email-mode',
|
||||
sendNonUserToMap,
|
||||
toggleReceivesAnnouncementEmails
|
||||
);
|
||||
router.get(
|
||||
api.get(
|
||||
'/toggle-notification-email-mode',
|
||||
sendNonUserToMap,
|
||||
toggleReceivesNotificationEmails
|
||||
);
|
||||
router.get(
|
||||
api.get(
|
||||
'/toggle-quincy-email-mode',
|
||||
sendNonUserToMap,
|
||||
toggleReceivesQuincyEmails
|
||||
);
|
||||
router.post(
|
||||
api.post(
|
||||
'/account/delete',
|
||||
ifNoUser401,
|
||||
postDeleteAccount
|
||||
);
|
||||
router.get(
|
||||
api.get(
|
||||
'/account',
|
||||
sendNonUserToMap,
|
||||
getAccount
|
||||
@ -188,32 +190,31 @@ module.exports = function(app) {
|
||||
flashIfNotVerified,
|
||||
getSettings
|
||||
);
|
||||
// router.get('/vote1', vote1);
|
||||
// router.get('/vote2', vote2);
|
||||
|
||||
// Ensure these are the last routes!
|
||||
router.get(
|
||||
api.get(
|
||||
'/:username/front-end-certification',
|
||||
showCert.bind(null, certTypes.frontEnd)
|
||||
);
|
||||
|
||||
router.get(
|
||||
api.get(
|
||||
'/:username/data-visualization-certification',
|
||||
showCert.bind(null, certTypes.dataVis)
|
||||
);
|
||||
|
||||
router.get(
|
||||
api.get(
|
||||
'/:username/back-end-certification',
|
||||
showCert.bind(null, certTypes.backEnd)
|
||||
);
|
||||
|
||||
router.get(
|
||||
api.get(
|
||||
'/:username/full-stack-certification',
|
||||
(req, res) => res.redirect(req.url.replace('full-stack', 'back-end'))
|
||||
);
|
||||
|
||||
router.get('/:username', returnUser);
|
||||
|
||||
app.use('/:lang', router);
|
||||
app.use(router);
|
||||
|
||||
function getSignin(req, res) {
|
||||
@ -280,7 +281,7 @@ module.exports = function(app) {
|
||||
|
||||
function returnUser(req, res, next) {
|
||||
const username = req.params.username.toLowerCase();
|
||||
const { user, path } = req;
|
||||
const { user } = req;
|
||||
|
||||
// timezone of signed-in account
|
||||
// to show all date related components
|
||||
@ -298,10 +299,7 @@ module.exports = function(app) {
|
||||
return User.findOne$(query)
|
||||
.filter(userPortfolio => {
|
||||
if (!userPortfolio) {
|
||||
req.flash('errors', {
|
||||
msg: `We couldn't find a page for ${ path }`
|
||||
});
|
||||
res.redirect('/');
|
||||
next();
|
||||
}
|
||||
return !!userPortfolio;
|
||||
})
|
||||
@ -354,7 +352,8 @@ module.exports = function(app) {
|
||||
calender,
|
||||
github: userPortfolio.githubURL,
|
||||
moment,
|
||||
encodeFcc
|
||||
encodeFcc,
|
||||
supportedLanguages
|
||||
}));
|
||||
})
|
||||
.doOnNext(data => {
|
||||
|
27
server/boot/z-lang-redirect.js
Normal file
27
server/boot/z-lang-redirect.js
Normal file
@ -0,0 +1,27 @@
|
||||
import supportedLanguages from '../../common/utils/supported-languages';
|
||||
import passThroughs from '../utils/lang-passthrough-urls';
|
||||
// import debug from 'debug';
|
||||
|
||||
// const log = debug('fcc:controller:lang-redirect');
|
||||
const toLowerCase = String.prototype.toLowerCase;
|
||||
|
||||
export default function redirectLang(app) {
|
||||
app.all('*', function(req, res, next) {
|
||||
const { url, path } = req;
|
||||
const langCode = toLowerCase.call(url.split('/')[1]);
|
||||
|
||||
if (passThroughs[langCode]) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (supportedLanguages[langCode]) {
|
||||
req.flash('errors', {
|
||||
msg: `404: We couldn't find path ${ path }`
|
||||
});
|
||||
return res.redirect('/map');
|
||||
}
|
||||
|
||||
// language aware redirect
|
||||
return res.redirect(url);
|
||||
});
|
||||
}
|
@ -50,6 +50,7 @@
|
||||
"./middlewares/global-locals": {},
|
||||
"./middlewares/revision-helpers": {},
|
||||
"./middlewares/migrate-completed-challenges": {},
|
||||
"./middlewares/add-lang": {},
|
||||
"./middlewares/flash-cheaters": {}
|
||||
},
|
||||
"files": {},
|
||||
|
66
server/middlewares/add-lang.js
Normal file
66
server/middlewares/add-lang.js
Normal file
@ -0,0 +1,66 @@
|
||||
import supportedLanguages 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
|
||||
function langRedirect(...args) {
|
||||
const url = args.length === 2 ? args[1] : args[0];
|
||||
const { lang } = this.req;
|
||||
const maybeLang = toLowerCase.call(url.split('/')[1]);
|
||||
|
||||
if (
|
||||
passthroughs[maybeLang] ||
|
||||
supportedLanguages[maybeLang]
|
||||
) {
|
||||
return this._oldRedirect(...arguments);
|
||||
}
|
||||
|
||||
// if language present add to url
|
||||
if (lang) {
|
||||
return this._oldRedirect(`/${lang}${url}`);
|
||||
}
|
||||
|
||||
// default to english
|
||||
return this._oldRedirect(`/en${url}`);
|
||||
}
|
||||
|
||||
// prefer url lang over user lang
|
||||
// if url lang is not supported move to user lang
|
||||
// if user lang is not supported default to english
|
||||
export default function addLang() {
|
||||
return function(req, res, next) {
|
||||
const { url, user = {} } = req;
|
||||
const maybeLang = url.split('/')[1];
|
||||
const restUrl = url.split('/').slice(2).join('/');
|
||||
const userLang = user.languageTag;
|
||||
let finalLang;
|
||||
if (supportedLanguages[maybeLang]) {
|
||||
finalLang = maybeLang;
|
||||
} else if (supportedLanguages[userLang]) {
|
||||
finalLang = userLang;
|
||||
} else {
|
||||
finalLang = 'en';
|
||||
}
|
||||
// found url lang tag that is not yet supported
|
||||
// redirect to fix url with supported lang tag
|
||||
if (langTagRegex.test(maybeLang) && !supportedLanguages[maybeLang]) {
|
||||
log(`unsupported lang tag ${maybeLang}`);
|
||||
return res.redirect(`/${finalLang}/${restUrl}`);
|
||||
}
|
||||
res.locals.supportedLanguages = supportedLanguages;
|
||||
|
||||
if (supportedLanguages[finalLang]) {
|
||||
req.lang = finalLang;
|
||||
res.locals.lang = finalLang;
|
||||
}
|
||||
|
||||
res._oldRedirect = res.redirect;
|
||||
res.redirect = langRedirect;
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import { Observable } from 'rx';
|
||||
import { Schema, valuesOf, arrayOf, normalize } from 'normalizr';
|
||||
import { nameify, dasherize, unDasherize } from '../utils';
|
||||
import debug from 'debug';
|
||||
import { nameify, dasherize, unDasherize } from '../utils';
|
||||
import supportedLanguages from '../../common/utils/supported-languages';
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production';
|
||||
const isBeta = !!process.env.BETA;
|
||||
const challengesRegex = /^(bonfire|waypoint|zipline|basejump|checkpoint)/i;
|
||||
const log = debug('fcc:challenges');
|
||||
const log = debug('fcc:services:challenges');
|
||||
const challenge = new Schema('challenge', { idAttribute: 'dashedName' });
|
||||
const block = new Schema('block', { idAttribute: 'dashedName' });
|
||||
const superBlock = new Schema('superBlock', { idAttribute: 'dashedName' });
|
||||
@ -99,6 +100,34 @@ function cachedMap(Block) {
|
||||
.shareReplay();
|
||||
}
|
||||
|
||||
function mapChallengeToLang({ translations = {}, ...challenge }, lang) {
|
||||
if (!supportedLanguages[lang]) {
|
||||
lang = 'en';
|
||||
}
|
||||
if (lang !== 'en') {
|
||||
challenge.title =
|
||||
translations[lang] && translations[lang].title ||
|
||||
challenge.title;
|
||||
|
||||
challenge.description =
|
||||
translations[lang] && translations[lang].description ||
|
||||
challenge.description;
|
||||
}
|
||||
return challenge;
|
||||
}
|
||||
|
||||
function getMapForLang(lang) {
|
||||
return ({ entities: { challenge: challengeMap, ...entities }, result }) => {
|
||||
entities.challenge = Object.keys(challengeMap)
|
||||
.reduce((translatedChallengeMap, key) => {
|
||||
translatedChallengeMap[key] =
|
||||
mapChallengeToLang(challengeMap[key], lang);
|
||||
return translatedChallengeMap;
|
||||
}, {});
|
||||
return { result, entities };
|
||||
};
|
||||
}
|
||||
|
||||
function shouldNotFilterComingSoon({ isComingSoon, isBeta: challengeIsBeta }) {
|
||||
return isDev ||
|
||||
!isComingSoon ||
|
||||
@ -123,7 +152,8 @@ function getFirstChallenge(challengeMap$) {
|
||||
function getChallengeAndBlock(
|
||||
challengeDashedName,
|
||||
blockDashedName,
|
||||
challengeMap$
|
||||
challengeMap$,
|
||||
lang
|
||||
) {
|
||||
return challengeMap$
|
||||
.flatMap(({ entities }) => {
|
||||
@ -134,7 +164,11 @@ function getChallengeAndBlock(
|
||||
!challenge ||
|
||||
!shouldNotFilterComingSoon(challenge)
|
||||
) {
|
||||
return getChallengeByDashedName(challengeDashedName, challengeMap$);
|
||||
return getChallengeByDashedName(
|
||||
challengeDashedName,
|
||||
challengeMap$,
|
||||
lang
|
||||
);
|
||||
}
|
||||
return Observable.just({
|
||||
redirect: block.dashedName !== blockDashedName ?
|
||||
@ -142,7 +176,7 @@ function getChallengeAndBlock(
|
||||
false,
|
||||
entities: {
|
||||
challenge: {
|
||||
[challenge.dashedName]: challenge
|
||||
[challenge.dashedName]: mapChallengeToLang(challenge, lang)
|
||||
}
|
||||
},
|
||||
result: {
|
||||
@ -153,7 +187,7 @@ function getChallengeAndBlock(
|
||||
});
|
||||
}
|
||||
|
||||
function getChallengeByDashedName(dashedName, challengeMap$) {
|
||||
function getChallengeByDashedName(dashedName, challengeMap$, lang) {
|
||||
const challengeName = unDasherize(dashedName)
|
||||
.replace(challengesRegex, '');
|
||||
const testChallengeName = new RegExp(challengeName, 'i');
|
||||
@ -179,7 +213,11 @@ function getChallengeByDashedName(dashedName, challengeMap$) {
|
||||
.map(challenge => ({
|
||||
redirect:
|
||||
`/challenges/${challenge.block}/${challenge.dashedName}`,
|
||||
entities: { challenge: { [challenge.dashedName]: challenge } },
|
||||
entities: {
|
||||
challenge: {
|
||||
[challenge.dashedName]: mapChallengeToLang(challenge, lang)
|
||||
}
|
||||
},
|
||||
result: {
|
||||
challenge: challenge.dashedName,
|
||||
block: challenge.block
|
||||
@ -192,16 +230,19 @@ export default function mapService(app) {
|
||||
const challengeMap$ = cachedMap(Block);
|
||||
return {
|
||||
name: 'map',
|
||||
read: (req, resource, { block, dashedName } = {}, config, cb) => {
|
||||
read: (req, resource, { lang, block, dashedName } = {}, config, cb) => {
|
||||
log(`${lang} language requested`);
|
||||
if (block && dashedName) {
|
||||
return getChallengeAndBlock(dashedName, block, challengeMap$)
|
||||
return getChallengeAndBlock(dashedName, block, challengeMap$, lang)
|
||||
.subscribe(challenge => cb(null, challenge), cb);
|
||||
}
|
||||
if (dashedName) {
|
||||
return getChallengeByDashedName(dashedName, challengeMap$)
|
||||
return getChallengeByDashedName(dashedName, challengeMap$, lang)
|
||||
.subscribe(challenge => cb(null, challenge), cb);
|
||||
}
|
||||
return challengeMap$.subscribe(map => cb(null, map), cb);
|
||||
return challengeMap$
|
||||
.map(getMapForLang(lang))
|
||||
.subscribe(map => cb(null, map), cb);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
4
server/utils/lang-passthrough-urls.js
Normal file
4
server/utils/lang-passthrough-urls.js
Normal file
@ -0,0 +1,4 @@
|
||||
export default [
|
||||
'auth',
|
||||
'services'
|
||||
].reduce((throughs, route) => (throughs[route] = true, throughs), {});
|
@ -103,7 +103,13 @@ block content
|
||||
a.btn.btn-lg.btn-block.btn-primary.btn-link-social(href='/update-email')
|
||||
i.fa.fa-envelope
|
||||
| Update my Email
|
||||
|
||||
if supportedLanguages
|
||||
.col-xs-12
|
||||
select#lang-select.form-control.btn.btn-lg.btn-block.btn-primary.btn-link-social(name='langTag')
|
||||
option(disabled selected=languageTag ? false : true) Prefered Language
|
||||
for languageDisplay, lang in supportedLanguages
|
||||
option(value=lang selected=lang === languageTag ? 'selected' : false)= languageDisplay
|
||||
option(disabled) More to come...
|
||||
.spacer
|
||||
h2.text-center Danger Zone
|
||||
.row
|
||||
|
Reference in New Issue
Block a user