Skip to content

Commit

Permalink
Merge pull request #23 from funktionswerk/issue-22-support-accept-lan…
Browse files Browse the repository at this point in the history
…guage-header

Issue 22 support accept language header
  • Loading branch information
kaywolter committed May 17, 2019
2 parents b5cf381 + ab1e87f commit f6d5b4c
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 15 deletions.
24 changes: 21 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var I18n = require('i18n');
var Boom = require('boom');
var Hoek = require('hoek');
var acceptLanguageParser = require('accept-language-parser');
var _ = require('lodash');
var pkg = require('./package.json');

Expand All @@ -14,6 +15,20 @@ exports.extractDefaultLocale = function(allLocales){
return allLocales[0];
};

function detectLocaleFromAcceptedLanguages(acceptedLanguages, localesSupported) {
var acceptedLanguageCodes = acceptLanguageParser.parse(acceptedLanguages);
var matchedLanguageFound = acceptedLanguageCodes.find(function (languageCode) {
return localesSupported.includes(languageCode.code);
});
if (matchedLanguageFound) {
var matchedLanguageCode = matchedLanguageFound.code;
if (matchedLanguageFound.region && localesSupported.includes(matchedLanguageFound.code + '-' + matchedLanguageFound.region)) {
return matchedLanguageFound.code + '-' + matchedLanguageFound.region;
}
return matchedLanguageFound.code;
}
}

exports.plugin = {
name: pkg.name,
version: pkg.version,
Expand Down Expand Up @@ -46,9 +61,12 @@ exports.plugin = {
}
request.i18n.setLocale(request.query[pluginOptions.queryParameter]);
} else if (pluginOptions.languageHeaderField && request.headers[pluginOptions.languageHeaderField]) {
var languageCode = request.headers[pluginOptions.languageHeaderField];
if (languageCode) {
request.i18n.setLocale(languageCode);
var matchedLanguageCode = detectLocaleFromAcceptedLanguages(
request.headers[pluginOptions.languageHeaderField],
pluginOptions.locales
);
if (matchedLanguageCode) {
request.i18n.setLocale(matchedLanguageCode);
}
}
return h.continue;
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hapi-i18n",
"version": "2.0.0",
"version": "2.1.0",
"description": "Translation module for hapi based on mashpie's i18n module",
"main": "index.js",
"directories": {
Expand Down Expand Up @@ -36,6 +36,7 @@
"vision": "^5.3.0"
},
"dependencies": {
"accept-language-parser": "^1.5.0",
"boom": "^7.1.1",
"hoek": "^5.0.2",
"i18n": "^0.8.3",
Expand Down
6 changes: 5 additions & 1 deletion test/locales/de.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"All's well that ends well.": "Ende gut, alles gut.",
"Validation failed": "Prüfung fehlgeschlagen"
"Validation failed": "Prüfung fehlgeschlagen",
"%s bottles of beer on the wall.": {
"one": "%s Bierflasche an der Wand.",
"other": "%s Bierflaschen an der Wand."
}
}
3 changes: 3 additions & 0 deletions test/locales/en-GB.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"All's well that ends well.": "All's well that ends well (british version)."
}
2 changes: 1 addition & 1 deletion test/locales/en.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"All's well that ends well.": "All's well that ends well."
}
}
4 changes: 4 additions & 0 deletions test/locales/fr.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"All's well that ends well.": "Tout est bien qui finit bien.",
"%s bottles of beer on the wall.": {
"one": "%s bouteille de bière sur le mur.",
"other": "%s bouteilles de bière sur le mur."
},
"": ""
}
44 changes: 36 additions & 8 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const Handlebars = require('handlebars');


const translateString_en = 'All\'s well that ends well.';
const translateString_en_GB = 'All\'s well that ends well (british version).';
const translateString_de = 'Ende gut, alles gut.';
const translateString_fr = 'Tout est bien qui finit bien.';

Expand Down Expand Up @@ -96,7 +97,7 @@ async function setupServer() {
await doSomething();
return ({
locale: request.i18n.getLocale(),
requestedLocale: request.headers['language'],
requestedLocale: request.headers['accept-language'],
message: request.i18n.__(translateString_en)
});
}
Expand Down Expand Up @@ -142,6 +143,7 @@ async function setupServer() {
return h.view('test',{
title: 'Hapi i18n handlebars test',
message: 'All\'s well that ends well.',
song: request.i18n.__n('%s bottles of beer on the wall.', 99),
languageCode: request.params.languageCode
});
}
Expand Down Expand Up @@ -186,9 +188,9 @@ describe('Localization', function () {

it('can be added as plugin', async () => {
const i18n_options = {
locales: ['de', 'en', 'fr'],
locales: ['de', 'en-GB', 'en', 'fr'],
directory: __dirname + '/locales',
languageHeaderField: 'language',
languageHeaderField: 'accept-language',
queryParameter: 'lang'
};
await server.register({plugin: Locale, options:i18n_options});
Expand Down Expand Up @@ -231,13 +233,24 @@ describe('Localization', function () {
method: 'GET',
url: '/localized/with/headers',
headers: {
'language': 'fr'
'accept-language': 'fr-CA,en-GB,en-US;q=0.9;q=0.7,en;q=0.8'
}
}
);
response.result.locale.should.equal('fr');
response.result.requestedLocale.should.equal('fr');
response.result.requestedLocale.should.equal('fr-CA,en-GB,en-US;q=0.9;q=0.7,en;q=0.8');
response.result.message.should.equal(translateString_fr);
response = await server.inject(
{
method: 'GET',
url: '/localized/with/headers',
headers: {
'accept-language': 'es,en-GB,en-US;q=0.9;q=0.7,en;q=0.8'
}
}
);
response.result.locale.should.equal('en-GB');
response.result.message.should.equal(translateString_en_GB);
response = await server.inject(
{
method: 'GET',
Expand All @@ -249,6 +262,21 @@ describe('Localization', function () {
response.result.message.should.equal(translateString_de);
});

it('uses the default locale if language codes in header don\'t match', async () => {
let response = await server.inject(
{
method: 'GET',
url: '/localized/with/headers',
headers: {
'accept-language': 'es,it'
}
}
);
response.result.locale.should.equal('de');
response.result.message.should.equal(translateString_de);
});


it('uses the language query parameter over the header parameter because this is more explicit', async () => {
const response = await server.inject(
{
Expand All @@ -267,7 +295,7 @@ describe('Localization', function () {
method: 'GET',
url: '/localized/with/empty',
headers: {
'language': 'fr'
'accept-language': 'fr'
}
})
.then ( (response) => {
Expand All @@ -282,7 +310,7 @@ describe('Localization', function () {
method: 'GET',
url: '/fr/localized/resource',
headers: {
'language': 'en'
'accept-language': 'en'
}
}
);
Expand All @@ -299,7 +327,7 @@ describe('Localization', function () {
}
);
response.statusCode.should.equal(200);
response.result.should.equal('<!DOCTYPE html><html lang=fr><body><p>Tout est bien qui finit bien.</p></body></html>\n');
response.result.should.equal('<!DOCTYPE html><html lang=fr><body><p>Tout est bien qui finit bien.</p><p>99 bouteilles de bière sur le mur.</p></body></html>\n');
});

it('returns status code NOT-FOUND if the requested locale is not available', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/views/test.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!DOCTYPE html><html lang={{languageCode}}><body><p>{{#i18n message}}{{/i18n}}</p></body></html>
<!DOCTYPE html><html lang={{languageCode}}><body><p>{{#i18n message}}{{/i18n}}</p><p>{{song}}</p></body></html>

0 comments on commit f6d5b4c

Please sign in to comment.