├── .gitignore
├── .jshintrc
├── .travis.yml
├── README.md
├── index.js
├── package.json
└── test
├── .jshintrc
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "curly": true,
6 | "eqeqeq": true,
7 | "immed": true,
8 | "indent": 2,
9 | "latedef": false,
10 | "newcap": true,
11 | "noarg": true,
12 | "quotmark": "single",
13 | "regexp": true,
14 | "undef": true,
15 | "unused": true,
16 | "trailing": true,
17 | "smarttabs": true,
18 | "validthis": true
19 | }
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4"
4 | - "0.12"
5 |
6 | install:
7 | - npm i -g jshint
8 | - npm i -g mocha
9 | - npm install
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # geolang-express
2 | [](https://travis-ci.org/koalazak/geolang-express)
3 | [](http://badge.fury.io/js/geolang-express)
4 | [](https://david-dm.org/koalazak/geolang-express)
5 |
6 | A simple geolocation middleware for Express.js. This module expose `countryLang` and object with more data `countryData` on app.locals making data visibles in views too.
7 | Also, set the session `cookieLangName` with the language of visitors country, useful for i18n modules.
8 |
9 | NOTE: When using this module, we recommend also using the [i18n-express](https://github.com/koalazak/i18n-express) module, which use the `cookieLangName` session to set i18n translations according that value.
10 |
11 |
12 | ## Requirements
13 |
14 | - Node >= 0.12
15 | - Express.js session enabled
16 |
17 | ## Instalation
18 |
19 | ```bash
20 | $ npm install geolang-express
21 | ```
22 |
23 | ## Usage
24 |
25 | ```js
26 | var geolang=require("geolang-express");
27 |
28 | app.use( geolang(options) );
29 | ```
30 |
31 | ## Options
32 |
33 | - `cookieLangName` : *(default: `ulang`)* If you provide a cookie name, try to get user lang from this session/cookie.
34 | - `defaultCountry` : *(default: `US`)* Default country, for example if the IP is 127.0.0.1
35 | - `siteLangs` : *(default: `['en']`)* Array of supported langs. (posbile values for clang and json files. see [i18n-express](https://github.com/koalazak/i18n-express))
36 |
37 |
38 | ## Example
39 |
40 | In your Express app.js:
41 |
42 | ```javascript
43 | var express = require('express');
44 | var path = require('path');
45 | var cookieParser = require('cookie-parser');
46 | var bodyParser = require('body-parser');
47 | var i18n=require("i18n-express");
48 | var geolang=require("geolang-express");
49 |
50 | var indexRoutes = require('./routes/index');
51 |
52 | var app = express();
53 |
54 | // view engine setup
55 | app.set('views', path.join(__dirname, 'views'));
56 | app.set('view engine', 'ejs');
57 | app.use(cookieParser());
58 | app.use(express.static(path.join(__dirname, 'public')));
59 | app.use(session({
60 | secret: 'secret',
61 | saveUninitialized: true,
62 | resave: true
63 | }));
64 |
65 |
66 | app.use(geolang({
67 | siteLangs: ["en","es"],
68 | cookieLangName: 'ulang'
69 | }));
70 |
71 |
72 | /*
73 | //Read documentation if you need to use i18n-express package
74 | app.use(i18n({
75 | translationsPath: path.join(__dirname, 'i18n'),
76 | siteLangs: ["en","es"],
77 | cookieLangName: 'ulang'
78 | }));
79 | */
80 |
81 |
82 | app.use('/', indexRoutes);
83 |
84 | module.exports = app;
85 |
86 |
87 | ```
88 |
89 | Now in your ejs view you have `countryLang` and object with more data `countryData`:
90 |
91 | ```html
92 |
93 |
94 |
Language by Country IP set to: <%=countryLang%>
95 |
96 |
97 | ```
98 |
99 | Or in your handlebars view:
100 |
101 | ```html
102 |
103 |
104 |
Language by Country IP set to: <%=countryLang%>
105 |
106 |
107 | ```
108 |
109 | ## License
110 |
111 | MIT
112 |
113 | ## Author
114 |
115 | - [Facu ZAK](https://github.com/koalazak)
116 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var geoip = require('geoip-ultralight');
2 | var countries = require('country-data').countries;
3 | var languages = require('country-data').languages;
4 |
5 | function getCountryCode(ip, defaultCountry){
6 |
7 | if (ip.indexOf('::ffff:') > -1) {
8 | ip = ip.split(':').reverse()[0];
9 | }
10 | var countryCode;
11 | var lookedUpCountry = geoip.lookupCountry(ip);
12 | if (lookedUpCountry) {
13 | countryCode = lookedUpCountry;
14 | }
15 | if ((ip === '127.0.0.1') || (!lookedUpCountry)) {
16 | countryCode = defaultCountry;
17 | }
18 | return countryCode;
19 | }
20 |
21 | exports = module.exports = function (opts) {
22 |
23 | var cookieLangName = opts.cookieLangName || "ulang";
24 | var defaultCountry = opts.defaultCountry || "US";
25 | var siteLangs = opts.siteLangs || ['en'];
26 |
27 | if (siteLangs.constructor !== Array) throw new Error('siteLangs must be an Array with supported langs.');
28 |
29 | return function geolang(req, res, next) {
30 |
31 | // Geo data isn't in locals
32 | if(!('countryLangData' in req.app.locals)) {
33 |
34 | var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
35 | var countryCode=getCountryCode(ip, defaultCountry);
36 | var countryData=countries[countryCode];
37 | var countryLang = languages[countryData.languages[0]];
38 |
39 | req.app.locals.countryData=countryData;
40 | req.app.locals.countryLangData=countryLang;
41 | req.app.locals.countryLang=countryLang.alpha2;
42 | }
43 |
44 | // Lang isn't already set so skip
45 | if (!(cookieLangName && req.session && req.session[cookieLangName])) {
46 | if (siteLangs.indexOf(req.app.locals.countryLang) > -1) {
47 | req.session[cookieLangName]=req.app.locals.countryLang;
48 | }
49 | }
50 |
51 | next();
52 | };
53 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "geolang-express",
3 | "version": "1.0.0",
4 | "description": "A simple geolocation middleware for Express.js to set site lang by client IP.",
5 | "main": "index.js",
6 | "scripts": {
7 | "jshint": "jshint .",
8 | "test": "mocha"
9 | },
10 | "dependencies": {
11 | "geoip-ultralight": "*",
12 | "country-data": "*"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/koalazak/geolang-express.git"
17 | },
18 | "keywords": [
19 | "geoip",
20 | "express",
21 | "i18n",
22 | "language",
23 | "geo",
24 | "middleware",
25 | "express.js",
26 | "geolocation",
27 | "web",
28 | "site"
29 | ],
30 | "author": "koalazak ",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/koalazak/geolang-express/issues"
34 | },
35 | "homepage": "https://github.com/koalazak/geolang-express"
36 | }
37 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.jshintrc",
3 | "expr": true,
4 | "jasmine": true,
5 | "globals": {
6 | "before": false,
7 | "after": false,
8 | "expect": true
9 | }
10 | }
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var path = require('path');
3 |
4 | var geolangMod = require('..');
5 | var res={};
6 |
7 | function getExpressReq(headers, session, query){
8 |
9 | var headers = headers || {};
10 | var session = session || {};
11 | var query = query || {};
12 |
13 | return {
14 | session: session,
15 | cookies: {},
16 | query: query,
17 | headers: headers,
18 | app: {locals:{}},
19 | connection: {}
20 | }
21 | }
22 |
23 | describe('geolang-express', function() {
24 | describe('middleware', function () {
25 | it('should set default Country and Language', function (done) {
26 |
27 | var req=getExpressReq({'x-forwarded-for':'127.0.0.1'});
28 |
29 | var mws=geolangMod({
30 | siteLangs: ['en','es']
31 | });
32 |
33 | mws(req, res, function(){
34 | assert.equal('US', req.app.locals.countryData.alpha2);
35 | assert.equal('en', req.app.locals.countryLang);
36 |
37 | done();
38 | });
39 |
40 | });
41 |
42 | it('should set user defined default Country and Language', function (done) {
43 |
44 | var req=getExpressReq({'x-forwarded-for':'127.0.0.1'});
45 |
46 | var mws=geolangMod({
47 | siteLangs: ['en','es'],
48 | defaultCountry: "AR"
49 | });
50 |
51 | mws(req, res, function(){
52 | assert.equal('AR', req.app.locals.countryData.alpha2);
53 | assert.equal('es', req.app.locals.countryLang);
54 |
55 | done();
56 | });
57 |
58 | });
59 |
60 | it('should set Jamaica Country and Language', function (done) {
61 |
62 | var req=getExpressReq({'x-forwarded-for':'96.43.160.3'});
63 |
64 | var mws=geolangMod({
65 | siteLangs: ['en','es'],
66 | defaultCountry: "AR"
67 | });
68 |
69 | mws(req, res, function(){
70 | assert.equal('JM', req.app.locals.countryData.alpha2);
71 | assert.equal('en', req.app.locals.countryLang);
72 |
73 | done();
74 | });
75 |
76 | });
77 |
78 | it('should set Italy Country and Language. And store in session', function (done) {
79 |
80 | var req=getExpressReq({'x-forwarded-for':'62.77.32.3'});
81 |
82 | var mws=geolangMod({
83 | siteLangs: ['en','es','it'],
84 | defaultCountry: "AR"
85 | });
86 |
87 | mws(req, res, function(){
88 | assert.equal('IT', req.app.locals.countryData.alpha2);
89 | assert.equal('it', req.app.locals.countryLang);
90 | assert.equal('it', req.session.ulang);
91 |
92 | done();
93 | });
94 |
95 | });
96 |
97 | });
98 | });
99 |
--------------------------------------------------------------------------------