Skip to content

Commit

Permalink
Fixes request-language
Browse files Browse the repository at this point in the history
  • Loading branch information
tinganho committed Feb 15, 2015
1 parent 93c2c8a commit 7d6e29b
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 9 deletions.
3 changes: 1 addition & 2 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
specifications
binaries
bin
test
.travis.yml
Gruntfile.js
.gitignore
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
request-language [![Build Status](https://travis-ci.org/tinganho/express-request-language.png)](https://travis-ci.org/tinganho/express-request-language)
========================
A middleware to figure out your request's language either by parsing `Accept-Language` header or by looking at a language cookie's value. `request-language` plays nicely with [L10ns][] by abstracting all our language setting logic for you.
A middleware to figure out your request's language either by parsing `Accept-Language` header or by looking at a language cookie's value. `request-language` plays nicely with [L10ns][] by abstracting all your language setting logic for you.

### Installation:

Expand All @@ -20,7 +20,7 @@ app.use(requestLanguage({
cookie: {
name: 'language',
options: { maxAge: 24*3600*1000 },
url: '/languages/{language}',
url: '/languages/{language}'
}
}));
...
Expand All @@ -39,7 +39,7 @@ app.use(requestLanguage({
cookie: {
name: 'language',
options: { maxAge: 24*3600*1000 },
url: '/languages/{language}',
url: '/languages/{language}'
},
localizations: localizations
}));
Expand Down
3 changes: 3 additions & 0 deletions bin/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env sh

./node_modules/mocha/bin/mocha test/test.js
70 changes: 69 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,79 @@

'use strict';

var acceptLanguage = require('accept-language');
var changeLanguageURL;


/**
* Set locals and call localizations
*
* @param {Object} props requestLanguage.props
* @param {Object} req Express request
* @param {String} language
* @return void
* @private
*/
function set(props, req, language) {
if(language) {
req.locals.language = language;
}
else {
language = req.locals.language = acceptLanguage.get(req.headers['accept-language']);
}

if(typeof props.localizations === 'function') {
props.localizations(language);
}
}

module.exports = function(props) {
'use strict';

if(props.cookie) {
if(props.cookie.url) {
if(!/\{language\}/.test(props.cookie.url)) {
throw new TypeError('You haven\'t defined the markup `{language}` in your cookie.url settings.');
}

if(props.cookie.url.charAt(0) !== '/') {
props.cookie.url = '/' + props.cookie.url;
}

props.cookie.url = '^' + props.cookie.url;

changeLanguageURL = new RegExp(props.cookie.url
.replace('/', '\\/')
.replace('{language}', '(.*)'));
}
}

acceptLanguage.languages(props.languages);

return function(req, res, next) {
acceptLanguage.languages(props.languages);
var language;
if(typeof props.cookie !== 'undefined') {
if(typeof props.cookie.url === 'string') {
changeLanguageURL.index = 0;
var match = changeLanguageURL.exec(req.url);
if(match !== null) {
res.cookie(props.cookie.name, match[1], props.cookie.options);
return res.redirect('back');
}
}

language = req.cookies[props.cookie.name];
if(typeof language === 'string') {
if(props.languages.indexOf(language) !== -1) {
set(props, req, language);
return next();
}
}
language = req.cookies[props.cookie.name] = acceptLanguage.get(req.headers['accept-language']);
res.cookie(props.cookie.name, language, props.cookie.options);
}

set(props, req, language);
next();
};
};
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "express-request-language",
"name": "connect-language",
"version": "1.0.0",
"description": "Figure out a request's language tag by parsing Accept-Language header and stored cookies.",
"main": "index.js",
"scripts": {
"test": "test"
"test": "bin/test"
},
"repository": {
"type": "git",
Expand All @@ -22,6 +22,12 @@
},
"homepage": "https://github.com/tinganho/express-request-language",
"dependencies": {
"accept-language": "^2.0.6"
"accept-language": "^2.0.7"
},
"devDependencies": {
"chai": "^2.0.0",
"mocha": "^2.1.0",
"sinon": "^1.12.2",
"sinon-chai": "^2.7.0"
}
}
181 changes: 181 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@

/**
* Module dependencies.
*/
var requestLangauge = require('../');
var sinon = require('sinon');
var chai = require('chai');
var expect = require('chai').expect;
var sinonChai = require('sinon-chai');
var spy = sinon.spy;
var noop = function() {};


/**
* Chai plugins.
*/
chai.should();
chai.use(sinonChai);

/**
* Get moched request with Accept-Language header
*
* @param {String} acceptLanguage
* @param {String} storedLanguage
* @return {String}
* @private
*/
function getRequest(acceptLanguage, storedLanguage) {
return {
locals: {},
headers: {
'accept-langauge': acceptLanguage
},
cookies: {
language: storedLanguage
},
cookie: noop
};
};

var next = function() {};
var res = {
writeHead: function() {},
end: function() {},
cookie: function() {}
};

/**
* Specs.
*/
describe('request-language', function() {
it('should be able to return the default language with no cookie and localization set', function() {
var middleware = requestLangauge({
languages: ['en-US', 'zh-CN']
});
var req = getRequest('');
middleware(req, res, next);
expect(req.locals.language).to.equal('en-US');
});

it('should be able to set a named cookie', function() {
var middleware = requestLangauge({
languages: ['en-US', 'zh-CN'],
cookie: {
name: 'language'
}
});
var req = getRequest('zh-CN;q=0.8,en-US;q=1');
middleware(req, res, next);
expect(req.cookies.language).to.equal('en-US');
});

it('should be able to set a new cookie', function() {
var middleware = requestLangauge({
languages: ['en-US', 'zh-CN'],
cookie: {
name: 'language',
options: {}
}
});
var req = getRequest('zh-CN;q=0.8,en-US;q=1');
var res = { cookie: spy() };
middleware(req, res, next);
res.cookie.should.have.been.calledOnce;
res.cookie.should.have.been.calledWith('language', 'en-US', {});
});

it('should be able to set a new cookie with options', function() {
var middleware = requestLangauge({
languages: ['en-US', 'zh-CN'],
cookie: {
name: 'language',
options: { maxAge: 1000 }
}
});
var req = getRequest('zh-CN;q=0.8,en-US;q=1');
var res = { cookie: spy() };
middleware(req, res, next);
res.cookie.should.have.been.calledOnce;
res.cookie.should.have.been.calledWith('language', 'en-US', { maxAge: 1000 });
});

it('should not set a new cookie if it is already set', function() {
var middleware = requestLangauge({
languages: ['en-US', 'zh-CN'],
cookie: {
name: 'language',
options: {}
}
});
var req = getRequest('zh-CN;q=0.8,en-US;q=1', 'zh-CN');
var res = { cookie: spy() };
middleware(req, res, next);
res.cookie.should.have.not.been.called;
expect(req.locals.language).to.equal('zh-CN');
expect(req.cookies.language).to.equal('zh-CN');
});

it('should be able to set a cookie url', function() {
var middleware = requestLangauge({
languages: ['en-US', 'zh-CN'],
cookie: {
name: 'language',
options: {},
url: 'languages/{language}'
}
});
var req = getRequest('zh-CN;q=0.8,en-US;q=1', 'zh-CN');
req.url = '/languages/en-US';
var res = { redirect: spy(), cookie: spy() };
middleware(req, res, next);
res.redirect.should.have.been.calledOnce;
res.redirect.should.have.been.calledWith('back');
res.cookie.should.have.been.calledOnce;
res.cookie.should.have.been.calledWith('language', 'en-US', {});
});

it('should be able to set a localization(language) function with no stored cookie and no cookie setting', function() {
var props = {
languages: ['en-US', 'zh-CN'],
localizations: spy()
};
var middleware = requestLangauge(props);
var req = getRequest('zh-CN;q=0.8,en-US;q=1');
middleware(req, res, next);
props.localizations.should.have.been.calledOnce;
props.localizations.should.have.been.calledWith('en-US');
});

it('should be able to set a localization(language) function with no stored cookie and with cookie setting', function() {
var props = {
languages: ['en-US', 'zh-CN'],
cookie: {
name: 'language',
options: {}
},
localizations: spy()
};
var middleware = requestLangauge(props);
var req = getRequest('zh-CN;q=0.8,en-US;q=1');
middleware(req, res, next);
props.localizations.should.have.been.calledOnce;
props.localizations.should.have.been.calledWith('en-US');
});

it('should be able to set a localization(language) function with a stored cookie', function() {
var props = {
languages: ['en-US', 'zh-CN'],
cookie: {
name: 'language',
options: {}
},
localizations: spy()
};
var middleware = requestLangauge(props);
var req = getRequest('zh-CN;q=0.8,en-US;q=1', 'zh-CN');
middleware(req, res, next);
props.localizations.should.have.been.calledOnce;
props.localizations.should.have.been.calledWith('zh-CN');
});
});

0 comments on commit 7d6e29b

Please sign in to comment.