-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
266 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
specifications | ||
binaries | ||
bin | ||
test | ||
.travis.yml | ||
Gruntfile.js | ||
.gitignore | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
}); | ||
}); |