├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── examples
├── basic-handlebars
│ ├── README.md
│ ├── index.js
│ ├── locales
│ │ ├── de
│ │ │ └── translation.json
│ │ └── en
│ │ │ └── translation.json
│ ├── package.json
│ └── views
│ │ ├── index.hbs
│ │ └── layouts
│ │ └── main.hbs
├── basic-locize
│ ├── README.md
│ ├── index.js
│ ├── package-lock.json
│ └── package.json
├── basic-pug
│ ├── README.md
│ ├── index.js
│ ├── locales
│ │ ├── de
│ │ │ └── translation.json
│ │ └── en
│ │ │ └── translation.json
│ ├── package.json
│ └── views
│ │ └── index.pug
└── basic
│ ├── README.md
│ ├── index.js
│ ├── locales
│ ├── de
│ │ └── translation.json
│ └── en
│ │ └── translation.json
│ └── package.json
├── index.d.ts
├── package.json
└── src
├── LanguageDetector.js
├── index.js
├── languageLookups
├── cookie.js
├── header.js
├── path.js
├── querystring.js
└── session.js
└── utils.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0"]
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 | root = true
3 |
4 | [*.{js,jsx,json}]
5 | end_of_line = lf
6 | insert_final_newline = true
7 | charset = utf-8
8 | indent_style = space
9 | indent_size = 2
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/dist/*
2 | **/node_modules/*
3 | **/*.min.*
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | ---
2 | parser: babel-eslint
3 |
4 | ecmaFeatures:
5 | arrowFunctions: true
6 | blockBindings: true
7 | classes: true
8 | defaultParams: true
9 | destructuring: true
10 | forOf: true
11 | generators: false
12 | modules: true
13 | objectLiteralComputedProperties: true
14 | objectLiteralDuplicateProperties: false
15 | objectLiteralShorthandMethods: true
16 | objectLiteralShorthandProperties: true
17 | spread: true
18 | superInFunctions: true
19 | templateStrings: true
20 |
21 | env:
22 | browser: true
23 | node: true
24 | es6: true
25 |
26 | globals:
27 | __resourceQuery: false
28 | bootstrap: false
29 | describe: false
30 | describeSaga: false
31 | describeEvent: false
32 | describeCommand: false
33 | describeScene: false
34 | before: false
35 | it: false
36 | xit: false
37 | window : false
38 | beforeEach : false
39 | afterEach : false
40 | after : false
41 | before : false
42 | beforeEachChapter: false
43 | describeScenario: false
44 | describeChapter: false
45 | describeStep: false
46 | document : false
47 | window: false
48 | File : false
49 | FormData: false
50 | QCodeDecoder: false
51 | $: false
52 | L: false
53 | btoa: false
54 | escape: false
55 | angular: false
56 | jQuery: false
57 | ga: false
58 |
59 | settings:
60 | jsx: true
61 |
62 | ecmaFeatures:
63 | jsx: true
64 |
65 | rules:
66 |
67 | # ERRORS
68 | curly: [2, "multi-line"]
69 |
70 | # WARNINGS
71 | no-unused-vars: [1, {vars: all, args: none}]
72 | semi-spacing: 1
73 | no-empty: 1
74 | handle-callback-err: 1
75 | eqeqeq: 1
76 | quotes: [1, 'single']
77 | no-unused-expressions: 1
78 | no-throw-literal: 1
79 | semi: 1
80 | block-scoped-var: 1
81 | no-alert: 1
82 | no-console: 1
83 | new-cap: 1
84 | space-unary-ops: 1
85 |
86 | # DISABLED
87 | space-after-keywords: 0
88 | dot-notation: 0
89 | consistent-return: 0
90 | brace-style: 0
91 | no-multi-spaces: 0
92 | no-underscore-dangle: 0
93 | key-spacing: 0
94 | comma-spacing: 0
95 | no-shadow: 0
96 | no-mixed-requires: 0
97 | space-infix-ops: 0
98 | strict: 0
99 | camelcase: 0
100 | no-wrap-func: 0
101 | comma-dangle: 0
102 | no-extra-semi: 0
103 | no-use-before-define: [0, "nofunc"]
104 |
105 | # AUTOMATED BY EDITORCONFIG
106 | eol-last: 0
107 | no-trailing-spaces: 0
108 | indent: 0
109 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore specific files
2 | .settings.xml
3 | .monitor
4 | .DS_Store
5 | *.orig
6 | npm-debug.log
7 | npm-debug.log.*
8 | *.dat
9 |
10 | # Ignore various temporary files
11 | *~
12 | *.swp
13 |
14 |
15 | # Ignore various Node.js related directories and files
16 | node_modules
17 | node_modules/**/*
18 | coverage/**/*
19 | lib/**/*
20 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test/
2 | src/
3 | coverage/
4 | examples/
5 | .babelrc
6 | .editorconfig
7 | .eslintignore
8 | .eslintrc
9 | .gitignore
10 | karma.conf.js
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 2.0.0
2 |
3 | - typescript: Update typings to export I18NextRequest instead of extending global express Request [211](https://github.com/i18next/i18next-express-middleware/pull/211)
4 |
5 | ### 1.9.1
6 |
7 | - only setHeader if lng has value
8 |
9 | ### 1.9.0
10 |
11 | - Remove Express alias of res.setHeader to support non-Express use with connect [199](https://github.com/i18next/i18next-express-middleware/pull/199)
12 |
13 | ### 1.8.2
14 |
15 | - typescript: fix: TypeScript i18next import [192](https://github.com/i18next/i18next-express-middleware/pull/192)
16 |
17 | ### 1.8.1
18 |
19 | - typescript: update LanguageDetector (fix type error since i18next v17.0.8) [190](https://github.com/i18next/i18next-express-middleware/pull/190)
20 |
21 | ### 1.8.0
22 |
23 | - Add optional "lookupHeader" to replace default header [180](https://github.com/i18next/i18next-express-middleware/pull/180)
24 |
25 | ### 1.7.3
26 |
27 | - typescript: Update typings to allow req.i18n and req.t without Typescript error. [177](https://github.com/i18next/i18next-express-middleware/pull/177)
28 |
29 | ### 1.7.2
30 |
31 | - typescript: Update typings to allow req.language and req.languages [176](https://github.com/i18next/i18next-express-middleware/pull/176)
32 |
33 | ### 1.7.1
34 |
35 | - Insensitive checking language-locale [172](https://github.com/i18next/i18next-express-middleware/pull/172)
36 |
37 | ### 1.7.0
38 |
39 | - add typings [170](https://github.com/i18next/i18next-express-middleware/pull/170)
40 |
41 | ### 1.6.0
42 |
43 | - Handle spaces in Accept-Language header [169](https://github.com/i18next/i18next-express-middleware/pull/169)
44 |
45 | ### 1.5.0
46 |
47 | - ignoreRoute can now be a function [#57](https://github.com/i18next/i18next-express-middleware/issues/57)
48 |
49 | ### 1.4.1
50 |
51 | - Update locals on languageChanged [PR163](https://github.com/i18next/i18next-express-middleware/pull/163)
52 |
53 | ### 1.4.0
54 |
55 | - Set Content-Language response header [PR159](https://github.com/i18next/i18next-express-middleware/pull/159)
56 |
57 | ### 1.3.2
58 |
59 | - npm ignore example folder
60 |
61 | ### 1.3.1
62 |
63 | - check if cookie secure already set [PR155](https://github.com/i18next/i18next-express-middleware/pull/155)
64 |
65 | ### 1.3.0
66 |
67 | - allow setting of cookie secure [PR154](https://github.com/i18next/i18next-express-middleware/pull/154)
68 |
69 | ### 1.2.1
70 |
71 | - add language detector to default export [PR153](https://github.com/i18next/i18next-express-middleware/pull/153)
72 |
73 | ### 1.2.0
74 |
75 | - fixes cookie set crash on newer versions of express (v4) [PR151](https://github.com/i18next/i18next-express-middleware/pull/151)
76 |
77 | ### 1.1.1
78 |
79 | - fixes 1.1.0 detection should return a string not an array
80 |
81 | ### 1.1.0
82 |
83 | - support returning fallback language when is set as Object [PR146](https://github.com/i18next/i18next-express-middleware/pull/146)
84 |
85 | ### 1.0.11
86 |
87 | - check if the headers are sent before cookies set [PR145](https://github.com/i18next/i18next-express-middleware/pull/145)
88 |
89 | ### 1.0.10
90 |
91 | - Use a named function for the middleware over an anonymous [PR144](https://github.com/i18next/i18next-express-middleware/pull/144)
92 |
93 | ### 1.0.9
94 |
95 | - Guard against non-string detections [PR141](https://github.com/i18next/i18next-express-middleware/pull/141)
96 |
97 | ### 1.0.7
98 |
99 | - Fix path detection can crash app (if req.originalUrl is not set) [#137](https://github.com/i18next/i18next-express-middleware/pull/137)
100 |
101 | ### 1.0.6
102 |
103 | - update cookie dependency adding overwrite flag [#127](https://github.com/i18next/i18next-express-middleware/issues/127)
104 |
105 | ### 1.0.5
106 |
107 | - fixes persisting lng on calling changeLanguage [#918](https://github.com/i18next/i18next/issues/918)
108 |
109 | ### 1.0.4
110 |
111 | - fixes rare issue in race condition of init call in cloned instance
112 |
113 | ### 1.0.3
114 |
115 | - Fix path lookup logic where query was being included in path [PR125](https://github.com/i18next/i18next-express-middleware/pull/125)
116 |
117 | ### 1.0.2
118 |
119 | - call next without loading languages if there is no lng
120 |
121 | ### 1.0.1
122 |
123 | - fixes call to loadLanguages
124 |
125 | ### 1.0.0
126 |
127 | - use an i18next instance clone instead of a pseudo/mock object, keep clone lng and req.lng in sync
128 |
129 | ### 0.4.0
130 |
131 | - adds localized routes
132 | - adds default export for es6 users not want to use named exports.default
133 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 i18next
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DEPRECATED
2 |
3 | Can be replaced with: [i18next-http-middleware](https://github.com/i18next/i18next-http-middleware)
4 |
5 | ---
6 |
7 | # Introduction
8 |
9 | This is a middleware to use i18next in express.js.
10 |
11 | # Getting started
12 |
13 | Source can be loaded via [npm](https://www.npmjs.com/package/i18next-express-middleware).
14 |
15 | ```
16 | # npm package
17 | $ npm install i18next-express-middleware
18 | ```
19 |
20 | ## wire up i18next to request object
21 |
22 | ```js
23 | var i18next = require("i18next");
24 | var middleware = require("i18next-express-middleware");
25 | var express = require("express");
26 |
27 | i18next.use(middleware.LanguageDetector).init({
28 | preload: ["en", "de", "it"],
29 | ...otherOptions
30 | });
31 |
32 | var app = express();
33 | app.use(
34 | middleware.handle(i18next, {
35 | ignoreRoutes: ["/foo"], // or function(req, res, options, i18next) { /* return true to ignore */ }
36 | removeLngFromUrl: false
37 | })
38 | );
39 |
40 | // in your request handler
41 | app.get("myRoute", function(req, res) {
42 | var lng = req.language; // 'de-CH'
43 | var lngs = req.languages; // ['de-CH', 'de', 'en']
44 | req.i18n.changeLanguage("en"); // will not load that!!! assert it was preloaded
45 |
46 | var exists = req.i18n.exists("myKey");
47 | var translation = req.t("myKey");
48 | });
49 |
50 | // in your views, eg. in pug (ex. jade)
51 | div = t("myKey");
52 | ```
53 |
54 | ## add routes
55 |
56 | ```js
57 | // missing keys; make sure the body is parsed (i.e. with [body-parser](https://github.com/expressjs/body-parser#bodyparserjsonoptions))
58 | app.post("/locales/add/:lng/:ns", middleware.missingKeyHandler(i18next));
59 |
60 | // multiload backend route
61 | app.get("/locales/resources.json", middleware.getResourcesHandler(i18next));
62 | ```
63 |
64 | ## add localized routes
65 |
66 | You can add your routes directly to the express app
67 |
68 | ```js
69 | var express = require("express"),
70 | app = express(),
71 | i18next = require("i18next"),
72 | FilesystemBackend = require("i18next-node-fs-backend"),
73 | i18nextMiddleware = require("i18next-express-middleware"),
74 | port = 3000;
75 |
76 | i18next
77 | .use(i18nextMiddleware.LanguageDetector)
78 | .use(FilesystemBackend)
79 | .init({ preload: ["en", "de", "it"], ...otherOptions }, function() {
80 | i18nextMiddleware.addRoute(
81 | i18next,
82 | "/:lng/key-to-translate",
83 | ["en", "de", "it"],
84 | app,
85 | "get",
86 | function(req, res) {
87 | //endpoint function
88 | }
89 | );
90 | });
91 | app.use(i18nextMiddleware.handle(i18next));
92 | app.listen(port, function() {
93 | console.log("Server listening on port", port);
94 | });
95 | ```
96 |
97 | or to an express router
98 |
99 | ```js
100 | var express = require("express"),
101 | app = express(),
102 | i18next = require("i18next"),
103 | FilesystemBackend = require("i18next-node-fs-backend"),
104 | i18nextMiddleware = require("i18next-express-middleware"),
105 | router = require("express").Router(),
106 | port = 3000;
107 |
108 | i18next
109 | .use(i18nextMiddleware.LanguageDetector)
110 | .use(FilesystemBackend)
111 | .init({ preload: ["en", "de", "it"], ...otherOptions }, function() {
112 | i18nextMiddleware.addRoute(
113 | i18next,
114 | "/:lng/key-to-translate",
115 | ["en", "de", "it"],
116 | router,
117 | "get",
118 | function(req, res) {
119 | //endpoint function
120 | }
121 | );
122 | app.use("/", router);
123 | });
124 | app.use(i18nextMiddleware.handle(i18next));
125 | app.listen(port, function() {
126 | console.log("Server listening on port", port);
127 | });
128 | ```
129 |
130 | ## language detection
131 |
132 | Detects user language from current request. Comes with support for:
133 |
134 | - path
135 | - cookie
136 | - header
137 | - querystring
138 | - session
139 |
140 | Wiring up:
141 |
142 | ```js
143 | var i18next = require("i18next");
144 | var middleware = require("i18next-express-middleware");
145 |
146 | i18next.use(middleware.LanguageDetector).init(i18nextOptions);
147 | ```
148 |
149 | As with all modules you can either pass the constructor function (class) to the i18next.use or a concrete instance.
150 |
151 | ## Detector Options
152 |
153 | ```js
154 | {
155 | // order and from where user language should be detected
156 | order: [/*'path', 'session', */ 'querystring', 'cookie', 'header'],
157 |
158 | // keys or params to lookup language from
159 | lookupQuerystring: 'lng',
160 | lookupCookie: 'i18next',
161 | lookupHeader: 'accept-language',
162 | lookupSession: 'lng',
163 | lookupPath: 'lng',
164 | lookupFromPathIndex: 0,
165 |
166 | // cache user language
167 | caches: false, // ['cookie']
168 |
169 | // optional expire and domain for set cookie
170 | cookieExpirationDate: new Date(),
171 | cookieDomain: 'myDomain',
172 | cookieSecure: true // if need secure cookie
173 | }
174 | ```
175 |
176 | Options can be passed in:
177 |
178 | **preferred** - by setting options.detection in i18next.init:
179 |
180 | ```js
181 | var i18next = require("i18next");
182 | var middleware = require("i18next-express-middleware");
183 |
184 | i18next.use(middleware.LanguageDetector).init({
185 | detection: options
186 | });
187 | ```
188 |
189 | on construction:
190 |
191 | ```js
192 | var middleware = require("i18next-express-middleware");
193 | var lngDetector = new middleware.LanguageDetector(null, options);
194 | ```
195 |
196 | via calling init:
197 |
198 | ```js
199 | var middleware = require("i18next-express-middleware");
200 |
201 | var lngDetector = new middleware.LanguageDetector();
202 | lngDetector.init(options);
203 | ```
204 |
205 | ## Adding own detection functionality
206 |
207 | ### interface
208 |
209 | ```js
210 | module.exports = {
211 | name: 'myDetectorsName',
212 |
213 | lookup: function(req, res, options) {
214 | // options -> are passed in options
215 | return 'en';
216 | },
217 |
218 | cacheUserLanguage: function(req, res, lng, options) {
219 | // options -> are passed in options
220 | // lng -> current language, will be called after init and on changeLanguage
221 |
222 | // store it
223 | }
224 | };
225 | ```
226 |
227 | ### adding it
228 |
229 | ```js
230 | var i18next = require("i18next");
231 | var middleware = require("i18next-express-middleware");
232 |
233 | var lngDetector = new middleware.LanguageDetector();
234 | lngDetector.addDetector(myDetector);
235 |
236 | i18next.use(lngDetector).init({
237 | detection: options
238 | });
239 | ```
240 |
241 | ---
242 |
243 |
Gold Sponsors
244 |
245 |
246 |
247 |
248 |
249 |
250 |
--------------------------------------------------------------------------------
/examples/basic-handlebars/README.md:
--------------------------------------------------------------------------------
1 | # run the sample
2 |
3 | ```
4 | $ npm i
5 | $ npm start
6 | ```
7 |
8 | open: http://localhost:8080
9 |
--------------------------------------------------------------------------------
/examples/basic-handlebars/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const i18next = require('i18next');
3 | const i18nextMiddleware = require('i18next-express-middleware');
4 | const Backend = require('i18next-node-fs-backend');
5 | const expressHbs = require('express-handlebars');
6 |
7 | const app = express();
8 | const port = process.env.PORT || 8080;
9 |
10 | i18next
11 | .use(Backend)
12 | .use(i18nextMiddleware.LanguageDetector)
13 | .init({
14 | backend: {
15 | loadPath: __dirname + '/locales/{{lng}}/{{ns}}.json',
16 | addPath: __dirname + '/locales/{{lng}}/{{ns}}.missing.json'
17 | },
18 | fallbackLng: 'en',
19 | preload: ['en', 'de'],
20 | saveMissing: true
21 | });
22 |
23 | app.engine('hbs', expressHbs({extname:'hbs', defaultLayout:'main.hbs'}));
24 | app.set('view engine', 'hbs')
25 | app.use(i18nextMiddleware.handle(i18next));
26 |
27 | app.get('/', (req, res) => {
28 | res.render('index')
29 | });
30 |
31 | app.listen(port, (err) => {
32 | console.log(`Server is listening on port ${port}`);
33 | });
34 |
--------------------------------------------------------------------------------
/examples/basic-handlebars/locales/de/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "home": {
3 | "title": "Hallo Welt!"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic-handlebars/locales/en/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "home": {
3 | "title": "Hello World!"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic-handlebars/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "i18next-express-basic",
3 | "version": "1.0.0",
4 | "description": "Node Express server with i18next.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "express": "4.15.3",
11 | "express-handlebars": "3.0.0",
12 | "i18next": "8.3.0",
13 | "i18next-express-middleware": "1.0.5",
14 | "i18next-node-fs-backend": "1.0.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/basic-handlebars/views/index.hbs:
--------------------------------------------------------------------------------
1 | {{t "home.title" }}
2 |
7 |
--------------------------------------------------------------------------------
/examples/basic-handlebars/views/layouts/main.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | i18next - express with hbs
4 |
5 |
6 |
7 | {{{body}}}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/basic-locize/README.md:
--------------------------------------------------------------------------------
1 | # run the sample
2 |
3 | ```
4 | $ npm i
5 | $ npm start
6 | ```
7 |
8 | open: http://localhost:8080
9 |
--------------------------------------------------------------------------------
/examples/basic-locize/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const i18next = require('i18next');
3 | const i18nextMiddleware = require('i18next-express-middleware');
4 | const Backend = require('i18next-node-locize-backend');
5 |
6 | const app = express();
7 | const port = process.env.PORT || 8080;
8 |
9 | i18next
10 | .use(Backend)
11 | .use(i18nextMiddleware.LanguageDetector)
12 | .init({
13 | backend: {
14 | referenceLng: 'en',
15 | projectId: '79a28dc3-b858-44a4-9603-93455e9e8c65'
16 | // apiKey: 'do not show in production',
17 | },
18 | fallbackLng: 'en',
19 | preload: ['en', 'de'],
20 | debug: true,
21 | saveMissing: true
22 | });
23 |
24 | app.use(i18nextMiddleware.handle(i18next));
25 |
26 | app.get('/', (req, res) => {
27 | res.send(req.t('home.title'));
28 | });
29 |
30 | app.listen(port, err => {
31 | console.log(`Server is listening on port ${port}`);
32 | });
33 |
--------------------------------------------------------------------------------
/examples/basic-locize/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "i18next-express-basic",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/runtime": {
8 | "version": "7.5.4",
9 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.4.tgz",
10 | "integrity": "sha512-Na84uwyImZZc3FKf4aUF1tysApzwf3p2yuFBIyBfbzT5glzKTdvYI4KVW4kcgjrzoGUjC7w3YyCHcJKaRxsr2Q==",
11 | "requires": {
12 | "regenerator-runtime": "^0.13.2"
13 | }
14 | },
15 | "accepts": {
16 | "version": "1.3.7",
17 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
18 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
19 | "requires": {
20 | "mime-types": "~2.1.24",
21 | "negotiator": "0.6.2"
22 | }
23 | },
24 | "ajv": {
25 | "version": "6.10.1",
26 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.1.tgz",
27 | "integrity": "sha512-w1YQaVGNC6t2UCPjEawK/vo/dG8OOrVtUmhBT1uJJYxbl5kU2Tj3v6LGqBcsysN1yhuCStJCCA3GqdvKY8sqXQ==",
28 | "requires": {
29 | "fast-deep-equal": "^2.0.1",
30 | "fast-json-stable-stringify": "^2.0.0",
31 | "json-schema-traverse": "^0.4.1",
32 | "uri-js": "^4.2.2"
33 | }
34 | },
35 | "array-flatten": {
36 | "version": "1.1.1",
37 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
38 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
39 | },
40 | "asn1": {
41 | "version": "0.2.4",
42 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
43 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
44 | "requires": {
45 | "safer-buffer": "~2.1.0"
46 | }
47 | },
48 | "assert-plus": {
49 | "version": "1.0.0",
50 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
51 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
52 | },
53 | "asynckit": {
54 | "version": "0.4.0",
55 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
56 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
57 | },
58 | "aws-sign2": {
59 | "version": "0.7.0",
60 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
61 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
62 | },
63 | "aws4": {
64 | "version": "1.8.0",
65 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
66 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
67 | },
68 | "bcrypt-pbkdf": {
69 | "version": "1.0.2",
70 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
71 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
72 | "requires": {
73 | "tweetnacl": "^0.14.3"
74 | }
75 | },
76 | "body-parser": {
77 | "version": "1.19.0",
78 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
79 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
80 | "requires": {
81 | "bytes": "3.1.0",
82 | "content-type": "~1.0.4",
83 | "debug": "2.6.9",
84 | "depd": "~1.1.2",
85 | "http-errors": "1.7.2",
86 | "iconv-lite": "0.4.24",
87 | "on-finished": "~2.3.0",
88 | "qs": "6.7.0",
89 | "raw-body": "2.4.0",
90 | "type-is": "~1.6.17"
91 | },
92 | "dependencies": {
93 | "qs": {
94 | "version": "6.7.0",
95 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
96 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
97 | }
98 | }
99 | },
100 | "bytes": {
101 | "version": "3.1.0",
102 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
103 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
104 | },
105 | "caseless": {
106 | "version": "0.12.0",
107 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
108 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
109 | },
110 | "combined-stream": {
111 | "version": "1.0.8",
112 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
113 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
114 | "requires": {
115 | "delayed-stream": "~1.0.0"
116 | }
117 | },
118 | "content-disposition": {
119 | "version": "0.5.3",
120 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
121 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
122 | "requires": {
123 | "safe-buffer": "5.1.2"
124 | },
125 | "dependencies": {
126 | "safe-buffer": {
127 | "version": "5.1.2",
128 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
129 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
130 | }
131 | }
132 | },
133 | "content-type": {
134 | "version": "1.0.4",
135 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
136 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
137 | },
138 | "cookie": {
139 | "version": "0.4.0",
140 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
141 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
142 | },
143 | "cookie-signature": {
144 | "version": "1.0.6",
145 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
146 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
147 | },
148 | "cookies": {
149 | "version": "0.7.1",
150 | "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz",
151 | "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=",
152 | "requires": {
153 | "depd": "~1.1.1",
154 | "keygrip": "~1.0.2"
155 | }
156 | },
157 | "core-util-is": {
158 | "version": "1.0.2",
159 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
160 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
161 | },
162 | "dashdash": {
163 | "version": "1.14.1",
164 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
165 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
166 | "requires": {
167 | "assert-plus": "^1.0.0"
168 | }
169 | },
170 | "debug": {
171 | "version": "2.6.9",
172 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
173 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
174 | "requires": {
175 | "ms": "2.0.0"
176 | }
177 | },
178 | "delayed-stream": {
179 | "version": "1.0.0",
180 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
181 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
182 | },
183 | "depd": {
184 | "version": "1.1.2",
185 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
186 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
187 | },
188 | "destroy": {
189 | "version": "1.0.4",
190 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
191 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
192 | },
193 | "ecc-jsbn": {
194 | "version": "0.1.2",
195 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
196 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
197 | "requires": {
198 | "jsbn": "~0.1.0",
199 | "safer-buffer": "^2.1.0"
200 | }
201 | },
202 | "ee-first": {
203 | "version": "1.1.1",
204 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
205 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
206 | },
207 | "encodeurl": {
208 | "version": "1.0.2",
209 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
210 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
211 | },
212 | "escape-html": {
213 | "version": "1.0.3",
214 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
215 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
216 | },
217 | "etag": {
218 | "version": "1.8.1",
219 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
220 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
221 | },
222 | "express": {
223 | "version": "4.17.1",
224 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
225 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
226 | "requires": {
227 | "accepts": "~1.3.7",
228 | "array-flatten": "1.1.1",
229 | "body-parser": "1.19.0",
230 | "content-disposition": "0.5.3",
231 | "content-type": "~1.0.4",
232 | "cookie": "0.4.0",
233 | "cookie-signature": "1.0.6",
234 | "debug": "2.6.9",
235 | "depd": "~1.1.2",
236 | "encodeurl": "~1.0.2",
237 | "escape-html": "~1.0.3",
238 | "etag": "~1.8.1",
239 | "finalhandler": "~1.1.2",
240 | "fresh": "0.5.2",
241 | "merge-descriptors": "1.0.1",
242 | "methods": "~1.1.2",
243 | "on-finished": "~2.3.0",
244 | "parseurl": "~1.3.3",
245 | "path-to-regexp": "0.1.7",
246 | "proxy-addr": "~2.0.5",
247 | "qs": "6.7.0",
248 | "range-parser": "~1.2.1",
249 | "safe-buffer": "5.1.2",
250 | "send": "0.17.1",
251 | "serve-static": "1.14.1",
252 | "setprototypeof": "1.1.1",
253 | "statuses": "~1.5.0",
254 | "type-is": "~1.6.18",
255 | "utils-merge": "1.0.1",
256 | "vary": "~1.1.2"
257 | },
258 | "dependencies": {
259 | "qs": {
260 | "version": "6.7.0",
261 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
262 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
263 | },
264 | "safe-buffer": {
265 | "version": "5.1.2",
266 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
267 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
268 | }
269 | }
270 | },
271 | "extend": {
272 | "version": "3.0.2",
273 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
274 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
275 | },
276 | "extsprintf": {
277 | "version": "1.3.0",
278 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
279 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
280 | },
281 | "fast-deep-equal": {
282 | "version": "2.0.1",
283 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
284 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
285 | },
286 | "fast-json-stable-stringify": {
287 | "version": "2.0.0",
288 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
289 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
290 | },
291 | "finalhandler": {
292 | "version": "1.1.2",
293 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
294 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
295 | "requires": {
296 | "debug": "2.6.9",
297 | "encodeurl": "~1.0.2",
298 | "escape-html": "~1.0.3",
299 | "on-finished": "~2.3.0",
300 | "parseurl": "~1.3.3",
301 | "statuses": "~1.5.0",
302 | "unpipe": "~1.0.0"
303 | }
304 | },
305 | "forever-agent": {
306 | "version": "0.6.1",
307 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
308 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
309 | },
310 | "form-data": {
311 | "version": "2.3.3",
312 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
313 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
314 | "requires": {
315 | "asynckit": "^0.4.0",
316 | "combined-stream": "^1.0.6",
317 | "mime-types": "^2.1.12"
318 | }
319 | },
320 | "forwarded": {
321 | "version": "0.1.2",
322 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
323 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
324 | },
325 | "fresh": {
326 | "version": "0.5.2",
327 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
328 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
329 | },
330 | "getpass": {
331 | "version": "0.1.7",
332 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
333 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
334 | "requires": {
335 | "assert-plus": "^1.0.0"
336 | }
337 | },
338 | "har-schema": {
339 | "version": "2.0.0",
340 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
341 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
342 | },
343 | "har-validator": {
344 | "version": "5.1.3",
345 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
346 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
347 | "requires": {
348 | "ajv": "^6.5.5",
349 | "har-schema": "^2.0.0"
350 | }
351 | },
352 | "http-errors": {
353 | "version": "1.7.2",
354 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
355 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
356 | "requires": {
357 | "depd": "~1.1.2",
358 | "inherits": "2.0.3",
359 | "setprototypeof": "1.1.1",
360 | "statuses": ">= 1.5.0 < 2",
361 | "toidentifier": "1.0.0"
362 | }
363 | },
364 | "http-signature": {
365 | "version": "1.2.0",
366 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
367 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
368 | "requires": {
369 | "assert-plus": "^1.0.0",
370 | "jsprim": "^1.2.2",
371 | "sshpk": "^1.7.0"
372 | }
373 | },
374 | "i18next": {
375 | "version": "17.0.6",
376 | "resolved": "https://registry.npmjs.org/i18next/-/i18next-17.0.6.tgz",
377 | "integrity": "sha512-bdNhzhcM6RG5m82RypVguCrAQNie/ycxW0Q5C6K9UDWD5hqApZfdJFbj4Ikz9jxIR+Ja1eg0yCQLhlCT+opwIg==",
378 | "requires": {
379 | "@babel/runtime": "^7.3.1"
380 | }
381 | },
382 | "i18next-express-middleware": {
383 | "version": "1.8.0",
384 | "resolved": "https://registry.npmjs.org/i18next-express-middleware/-/i18next-express-middleware-1.8.0.tgz",
385 | "integrity": "sha512-vbEV+YbGoXNpv59WM7MHvdbBpPUS6itMIgpX21mjmu8/naS/NlmZe6vMNFGUIWs5PbXGnKWm7ucKxYmc2RV8vA==",
386 | "requires": {
387 | "cookies": "0.7.1"
388 | }
389 | },
390 | "i18next-node-locize-backend": {
391 | "version": "2.4.3",
392 | "resolved": "https://registry.npmjs.org/i18next-node-locize-backend/-/i18next-node-locize-backend-2.4.3.tgz",
393 | "integrity": "sha512-nQvGD2H/Achvb9lK/fuP7fP8TOhmfEKLv/dZS5wLjUOpg9gQP1zmr8UEx7nd9EvKtz+LzVFUg0R209wNjAsKiA==",
394 | "requires": {
395 | "request": "^2.88.0"
396 | }
397 | },
398 | "iconv-lite": {
399 | "version": "0.4.24",
400 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
401 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
402 | "requires": {
403 | "safer-buffer": ">= 2.1.2 < 3"
404 | }
405 | },
406 | "inherits": {
407 | "version": "2.0.3",
408 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
409 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
410 | },
411 | "ipaddr.js": {
412 | "version": "1.9.0",
413 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
414 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
415 | },
416 | "is-typedarray": {
417 | "version": "1.0.0",
418 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
419 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
420 | },
421 | "isstream": {
422 | "version": "0.1.2",
423 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
424 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
425 | },
426 | "jsbn": {
427 | "version": "0.1.1",
428 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
429 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
430 | },
431 | "json-schema": {
432 | "version": "0.2.3",
433 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
434 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
435 | },
436 | "json-schema-traverse": {
437 | "version": "0.4.1",
438 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
439 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
440 | },
441 | "json-stringify-safe": {
442 | "version": "5.0.1",
443 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
444 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
445 | },
446 | "jsprim": {
447 | "version": "1.4.1",
448 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
449 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
450 | "requires": {
451 | "assert-plus": "1.0.0",
452 | "extsprintf": "1.3.0",
453 | "json-schema": "0.2.3",
454 | "verror": "1.10.0"
455 | }
456 | },
457 | "keygrip": {
458 | "version": "1.0.3",
459 | "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz",
460 | "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g=="
461 | },
462 | "media-typer": {
463 | "version": "0.3.0",
464 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
465 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
466 | },
467 | "merge-descriptors": {
468 | "version": "1.0.1",
469 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
470 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
471 | },
472 | "methods": {
473 | "version": "1.1.2",
474 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
475 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
476 | },
477 | "mime": {
478 | "version": "1.6.0",
479 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
480 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
481 | },
482 | "mime-db": {
483 | "version": "1.40.0",
484 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
485 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
486 | },
487 | "mime-types": {
488 | "version": "2.1.24",
489 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
490 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
491 | "requires": {
492 | "mime-db": "1.40.0"
493 | }
494 | },
495 | "ms": {
496 | "version": "2.0.0",
497 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
498 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
499 | },
500 | "negotiator": {
501 | "version": "0.6.2",
502 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
503 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
504 | },
505 | "oauth-sign": {
506 | "version": "0.9.0",
507 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
508 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
509 | },
510 | "on-finished": {
511 | "version": "2.3.0",
512 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
513 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
514 | "requires": {
515 | "ee-first": "1.1.1"
516 | }
517 | },
518 | "parseurl": {
519 | "version": "1.3.3",
520 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
521 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
522 | },
523 | "path-to-regexp": {
524 | "version": "0.1.7",
525 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
526 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
527 | },
528 | "performance-now": {
529 | "version": "2.1.0",
530 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
531 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
532 | },
533 | "proxy-addr": {
534 | "version": "2.0.5",
535 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
536 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
537 | "requires": {
538 | "forwarded": "~0.1.2",
539 | "ipaddr.js": "1.9.0"
540 | }
541 | },
542 | "psl": {
543 | "version": "1.2.0",
544 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz",
545 | "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA=="
546 | },
547 | "punycode": {
548 | "version": "2.1.1",
549 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
550 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
551 | },
552 | "qs": {
553 | "version": "6.5.2",
554 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
555 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
556 | },
557 | "range-parser": {
558 | "version": "1.2.1",
559 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
560 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
561 | },
562 | "raw-body": {
563 | "version": "2.4.0",
564 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
565 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
566 | "requires": {
567 | "bytes": "3.1.0",
568 | "http-errors": "1.7.2",
569 | "iconv-lite": "0.4.24",
570 | "unpipe": "1.0.0"
571 | }
572 | },
573 | "regenerator-runtime": {
574 | "version": "0.13.2",
575 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
576 | "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
577 | },
578 | "request": {
579 | "version": "2.88.0",
580 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
581 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
582 | "requires": {
583 | "aws-sign2": "~0.7.0",
584 | "aws4": "^1.8.0",
585 | "caseless": "~0.12.0",
586 | "combined-stream": "~1.0.6",
587 | "extend": "~3.0.2",
588 | "forever-agent": "~0.6.1",
589 | "form-data": "~2.3.2",
590 | "har-validator": "~5.1.0",
591 | "http-signature": "~1.2.0",
592 | "is-typedarray": "~1.0.0",
593 | "isstream": "~0.1.2",
594 | "json-stringify-safe": "~5.0.1",
595 | "mime-types": "~2.1.19",
596 | "oauth-sign": "~0.9.0",
597 | "performance-now": "^2.1.0",
598 | "qs": "~6.5.2",
599 | "safe-buffer": "^5.1.2",
600 | "tough-cookie": "~2.4.3",
601 | "tunnel-agent": "^0.6.0",
602 | "uuid": "^3.3.2"
603 | }
604 | },
605 | "safe-buffer": {
606 | "version": "5.2.0",
607 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
608 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
609 | },
610 | "safer-buffer": {
611 | "version": "2.1.2",
612 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
613 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
614 | },
615 | "send": {
616 | "version": "0.17.1",
617 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
618 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
619 | "requires": {
620 | "debug": "2.6.9",
621 | "depd": "~1.1.2",
622 | "destroy": "~1.0.4",
623 | "encodeurl": "~1.0.2",
624 | "escape-html": "~1.0.3",
625 | "etag": "~1.8.1",
626 | "fresh": "0.5.2",
627 | "http-errors": "~1.7.2",
628 | "mime": "1.6.0",
629 | "ms": "2.1.1",
630 | "on-finished": "~2.3.0",
631 | "range-parser": "~1.2.1",
632 | "statuses": "~1.5.0"
633 | },
634 | "dependencies": {
635 | "ms": {
636 | "version": "2.1.1",
637 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
638 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
639 | }
640 | }
641 | },
642 | "serve-static": {
643 | "version": "1.14.1",
644 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
645 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
646 | "requires": {
647 | "encodeurl": "~1.0.2",
648 | "escape-html": "~1.0.3",
649 | "parseurl": "~1.3.3",
650 | "send": "0.17.1"
651 | }
652 | },
653 | "setprototypeof": {
654 | "version": "1.1.1",
655 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
656 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
657 | },
658 | "sshpk": {
659 | "version": "1.16.1",
660 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
661 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
662 | "requires": {
663 | "asn1": "~0.2.3",
664 | "assert-plus": "^1.0.0",
665 | "bcrypt-pbkdf": "^1.0.0",
666 | "dashdash": "^1.12.0",
667 | "ecc-jsbn": "~0.1.1",
668 | "getpass": "^0.1.1",
669 | "jsbn": "~0.1.0",
670 | "safer-buffer": "^2.0.2",
671 | "tweetnacl": "~0.14.0"
672 | }
673 | },
674 | "statuses": {
675 | "version": "1.5.0",
676 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
677 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
678 | },
679 | "toidentifier": {
680 | "version": "1.0.0",
681 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
682 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
683 | },
684 | "tough-cookie": {
685 | "version": "2.4.3",
686 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
687 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
688 | "requires": {
689 | "psl": "^1.1.24",
690 | "punycode": "^1.4.1"
691 | },
692 | "dependencies": {
693 | "punycode": {
694 | "version": "1.4.1",
695 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
696 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
697 | }
698 | }
699 | },
700 | "tunnel-agent": {
701 | "version": "0.6.0",
702 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
703 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
704 | "requires": {
705 | "safe-buffer": "^5.0.1"
706 | }
707 | },
708 | "tweetnacl": {
709 | "version": "0.14.5",
710 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
711 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
712 | },
713 | "type-is": {
714 | "version": "1.6.18",
715 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
716 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
717 | "requires": {
718 | "media-typer": "0.3.0",
719 | "mime-types": "~2.1.24"
720 | }
721 | },
722 | "unpipe": {
723 | "version": "1.0.0",
724 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
725 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
726 | },
727 | "uri-js": {
728 | "version": "4.2.2",
729 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
730 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
731 | "requires": {
732 | "punycode": "^2.1.0"
733 | }
734 | },
735 | "utils-merge": {
736 | "version": "1.0.1",
737 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
738 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
739 | },
740 | "uuid": {
741 | "version": "3.3.2",
742 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
743 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
744 | },
745 | "vary": {
746 | "version": "1.1.2",
747 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
748 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
749 | },
750 | "verror": {
751 | "version": "1.10.0",
752 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
753 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
754 | "requires": {
755 | "assert-plus": "^1.0.0",
756 | "core-util-is": "1.0.2",
757 | "extsprintf": "^1.2.0"
758 | }
759 | }
760 | }
761 | }
762 |
--------------------------------------------------------------------------------
/examples/basic-locize/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "i18next-express-basic",
3 | "version": "1.0.0",
4 | "description": "Node Express server with i18next.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "express": "4.17.1",
11 | "i18next": "17.0.6",
12 | "i18next-express-middleware": "1.8.0",
13 | "i18next-node-locize-backend": "^2.4.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/basic-pug/README.md:
--------------------------------------------------------------------------------
1 | # run the sample
2 |
3 | ```
4 | $ npm i
5 | $ npm start
6 | ```
7 |
8 | open: http://localhost:8080
9 |
--------------------------------------------------------------------------------
/examples/basic-pug/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const i18next = require('i18next');
3 | const i18nextMiddleware = require('i18next-express-middleware');
4 | const Backend = require('i18next-node-fs-backend');
5 |
6 | const app = express();
7 | const port = process.env.PORT || 8080;
8 |
9 | i18next
10 | .use(Backend)
11 | .use(i18nextMiddleware.LanguageDetector)
12 | .init({
13 | backend: {
14 | loadPath: __dirname + '/locales/{{lng}}/{{ns}}.json',
15 | addPath: __dirname + '/locales/{{lng}}/{{ns}}.missing.json'
16 | },
17 | detection: {
18 | order: ['querystring', 'cookie'],
19 | caches: ['cookie']
20 | },
21 | fallbackLng: 'en',
22 | preload: ['en', 'de'],
23 | saveMissing: true
24 | });
25 |
26 | app.set('view engine', 'pug')
27 | app.use(i18nextMiddleware.handle(i18next));
28 |
29 | app.get('/', (req, res) => {
30 | res.render('index')
31 | });
32 |
33 | app.listen(port, (err) => {
34 | console.log(`Server is listening on port ${port}`);
35 | });
36 |
--------------------------------------------------------------------------------
/examples/basic-pug/locales/de/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "home": {
3 | "title": "Hallo Welt!"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic-pug/locales/en/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "home": {
3 | "title": "Hello World!"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic-pug/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "i18next-express-basic",
3 | "version": "1.0.0",
4 | "description": "Node Express server with i18next.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "express": "4.15.3",
11 | "i18next": "8.3.0",
12 | "i18next-express-middleware": "1.0.5",
13 | "i18next-node-fs-backend": "1.0.0",
14 | "pug": "2.0.0-rc.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/basic-pug/views/index.pug:
--------------------------------------------------------------------------------
1 | html
2 | head
3 | title i18next - express with pug
4 | body
5 | h1= t('home.title')
6 | div
7 | a(href="/?lng=en") english
8 | | |
9 | a(href="/?lng=de") deutsch
10 |
--------------------------------------------------------------------------------
/examples/basic/README.md:
--------------------------------------------------------------------------------
1 | # run the sample
2 |
3 | ```
4 | $ npm i
5 | $ npm start
6 | ```
7 |
8 | open: http://localhost:8080
9 |
--------------------------------------------------------------------------------
/examples/basic/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const i18next = require('i18next');
3 | const i18nextMiddleware = require('i18next-express-middleware');
4 | const Backend = require('i18next-node-fs-backend');
5 |
6 | const app = express();
7 | const port = process.env.PORT || 8080;
8 |
9 | i18next
10 | .use(Backend)
11 | .use(i18nextMiddleware.LanguageDetector)
12 | .init({
13 | // debug: true,
14 | backend: {
15 | loadPath: __dirname + '/locales/{{lng}}/{{ns}}.json',
16 | addPath: __dirname + '/locales/{{lng}}/{{ns}}.missing.json'
17 | },
18 | fallbackLng: 'en',
19 | preload: ['en', 'de'],
20 | saveMissing: true
21 | });
22 |
23 | app.use(i18nextMiddleware.handle(i18next));
24 |
25 | app.get('/', (req, res) => {
26 | res.send(req.t('home.title'));
27 | });
28 |
29 | app.listen(port, (err) => {
30 | console.log(`Server is listening on port ${port}`);
31 | });
32 |
--------------------------------------------------------------------------------
/examples/basic/locales/de/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "home": {
3 | "title": "Hallo Welt!"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic/locales/en/translation.json:
--------------------------------------------------------------------------------
1 | {
2 | "home": {
3 | "title": "Hello World!"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "i18next-express-basic",
3 | "version": "1.0.0",
4 | "description": "Node Express server with i18next.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "express": "4.17.1",
11 | "i18next": "19.3.4",
12 | "i18next-express-middleware": "1.9.1",
13 | "i18next-node-fs-backend": "2.1.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as express from 'express';
2 | import * as i18next from 'i18next';
3 |
4 |
5 | declare module 'i18next-express-middleware' {
6 | type I18next = i18next.i18n;
7 | type IgnoreRoutesFunction = (req: express.Request, res: express.Response, options: HandleOptions, i18next: I18next) => boolean;
8 | type App = express.Application | express.Router;
9 |
10 | type I18NextRequest = express.Request & {
11 | language: string;
12 | languages: string[];
13 | i18n:i18next.i18n;
14 | t:i18next.TFunction;
15 | }
16 |
17 | interface HandleOptions {
18 | ignoreRoutes?: string[] | IgnoreRoutesFunction;
19 | removeLngFromUrl?: boolean;
20 | }
21 | interface GetResourcesHandlerOptions {
22 | maxAge?: number;
23 | cache?: boolean;
24 | lngParam?: string;
25 | nsParam?: string;
26 | }
27 | interface MissingKeyHandlerOptions {
28 | lngParam?: string;
29 | nsParam?: string;
30 | }
31 |
32 | export function handle(i18next: I18next, options?: HandleOptions): express.Handler;
33 | export function getResourcesHandler(i18next: I18next, options?: GetResourcesHandlerOptions): express.Handler;
34 | export function missingKeyHandler(i18next: I18next, options?: MissingKeyHandlerOptions): express.Handler;
35 | export function addRoute(i18next: I18next, route: string, lngs: string[], app: App, verb: string, fc: express.RequestHandler): void;
36 | export function addRoute(i18next: I18next, route: string, lngs: string[], app: App, fc: express.RequestHandler): void;
37 |
38 | // LanguageDetector
39 | type LanguageDetectorServices = any;
40 | type LanguageDetectorOrder = string[];
41 | type LanguageDetectorCaches = boolean | string[];
42 | interface LanguageDetectorOptions {
43 | order?: LanguageDetectorOrder;
44 | lookupQuerystring?: string;
45 | lookupCookie?: string;
46 | lookupSession?: string;
47 | lookupFromPathIndex?: number;
48 | caches?: LanguageDetectorCaches;
49 | cookieExpirationDate?: Date;
50 | cookieDomain?: string;
51 | }
52 | interface LanguageDetectorAllOptions {
53 | fallbackLng: boolean | string | string[];
54 | }
55 | interface LanguageDetectorInterfaceOptions {
56 | [name: string]: any;
57 | }
58 | interface LanguageDetectorInterface {
59 | name: string;
60 | lookup: (req: express.Request, res: express.Response, options?: LanguageDetectorInterfaceOptions) => string | string[];
61 |
62 | cacheUserLanguage?: (req: express.Request, res: express.Response, lng: string, options?: object) => void;
63 | }
64 |
65 | export class LanguageDetector implements i18next.Module {
66 | type: "languageDetector";
67 |
68 | constructor(services: LanguageDetectorServices, options?: LanguageDetectorOptions, allOptions?: LanguageDetectorAllOptions);
69 | constructor(options?: LanguageDetectorOptions, allOptions?: LanguageDetectorAllOptions);
70 |
71 | init(services: LanguageDetectorServices, options?: LanguageDetectorOptions, allOptions?: LanguageDetectorAllOptions): void;
72 | init(options?: LanguageDetectorOptions, allOptions?: LanguageDetectorAllOptions): void;
73 | addDetector(detector: LanguageDetectorInterface): void;
74 | detect(req: express.Request, res: express.Response, detectionOrder: LanguageDetectorOrder): void;
75 | cacheUserLanguage(req: express.Request, res: express.Response, lng: string, caches: LanguageDetectorCaches): void;
76 | }
77 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "i18next-express-middleware",
3 | "version": "2.0.0",
4 | "description": "express middleware for i18next",
5 | "main": "./lib/index.js",
6 | "types": "./index.d.ts",
7 | "keywords": [
8 | "i18next",
9 | "i18next-express",
10 | "express",
11 | "middleware"
12 | ],
13 | "homepage": "https://github.com/i18next/i18next-express-middleware",
14 | "bugs": "https://github.com/i18next/i18next-express-middleware/issues",
15 | "repository": {
16 | "type": "git",
17 | "url": "git://github.com/i18next/i18next-express-middleware.git"
18 | },
19 | "dependencies": {
20 | "cookies": "0.7.1"
21 | },
22 | "devDependencies": {
23 | "@types/express": "4.16.0",
24 | "babel-cli": "6.7.7",
25 | "babel-core": "6.7.7",
26 | "babel-eslint": "5.0.0",
27 | "babel-preset-es2015": "6.3.13",
28 | "babel-preset-stage-0": "6.5.0",
29 | "chai": "2.3.0",
30 | "eslint": "2.7.0",
31 | "i18next": "13.0.1",
32 | "mocha": "2.2.5",
33 | "sinon": "1.17.1"
34 | },
35 | "scripts": {
36 | "transpile": "babel src -d lib",
37 | "build": "npm run transpile",
38 | "version": "npm run build",
39 | "postversion": "git push && git push --tags && rm -rf build/temp"
40 | },
41 | "author": "Jan Mühlemann (https://github.com/jamuhl)",
42 | "license": "MIT"
43 | }
44 |
--------------------------------------------------------------------------------
/src/LanguageDetector.js:
--------------------------------------------------------------------------------
1 | import * as utils from './utils';
2 | import cookieLookup from './languageLookups/cookie';
3 | import querystringLookup from './languageLookups/querystring';
4 | import pathLookup from './languageLookups/path';
5 | import headerLookup from './languageLookups/header';
6 | import sessionLookup from './languageLookups/session';
7 |
8 | function getDefaults() {
9 | return {
10 | order: [/*'path', 'session' */ 'querystring', 'cookie', 'header'],
11 | lookupQuerystring: 'lng',
12 | lookupCookie: 'i18next',
13 | lookupSession: 'lng',
14 | lookupFromPathIndex: 0,
15 |
16 | // cache user language
17 | caches: false // ['cookie']
18 | //cookieExpirationDate: new Date(),
19 | //cookieDomain: 'myDomain'
20 | };
21 | }
22 |
23 | class LanguageDetector {
24 | constructor(services, options = {}, allOptions = {}) {
25 | this.type = 'languageDetector';
26 | this.detectors = {};
27 |
28 | this.init(services, options, allOptions);
29 | }
30 |
31 | init(services, options = {}, allOptions = {}) {
32 | this.services = services;
33 | this.options = utils.defaults(options, this.options || {}, getDefaults());
34 | this.allOptions = allOptions;
35 |
36 | this.addDetector(cookieLookup);
37 | this.addDetector(querystringLookup);
38 | this.addDetector(pathLookup);
39 | this.addDetector(headerLookup);
40 | this.addDetector(sessionLookup);
41 | }
42 |
43 | addDetector(detector) {
44 | this.detectors[detector.name] = detector;
45 | }
46 |
47 | detect(req, res, detectionOrder) {
48 | if (arguments.length < 2) return;
49 | if (!detectionOrder) detectionOrder = this.options.order;
50 |
51 | let found;
52 | detectionOrder.forEach(detectorName => {
53 | if (found || !this.detectors[detectorName]) return;
54 |
55 | let detections = this.detectors[detectorName].lookup(req, res, this.options);
56 | if(!detections) return;
57 | if (!Array.isArray(detections)) detections = [detections];
58 |
59 | detections.forEach(lng => {
60 | if (found || typeof lng !== 'string') return;
61 |
62 | let cleanedLng = this.services.languageUtils.formatLanguageCode(lng);
63 |
64 | if (this.services.languageUtils.isWhitelisted(cleanedLng)) {
65 | found = cleanedLng;
66 | req.i18nextLookupName = detectorName;
67 | };
68 | });
69 | });
70 |
71 | if (!found) {
72 | let fallbacks = this.allOptions.fallbackLng;
73 | if (typeof fallbacks === 'string') fallbacks = [fallbacks];
74 | if (!fallbacks) fallbacks = [];
75 |
76 | if (Object.prototype.toString.apply(fallbacks) === '[object Array]') {
77 | found = fallbacks[0];
78 | } else {
79 | found = fallbacks[0] || fallbacks.default && fallbacks.default[0];
80 | }
81 | };
82 |
83 | return found;
84 | }
85 |
86 | cacheUserLanguage(req, res, lng, caches) {
87 | if (arguments.length < 3) return;
88 | if (!caches) caches = this.options.caches;
89 | if (!caches) return;
90 | caches.forEach(cacheName => {
91 | if (this.detectors[cacheName] && this.detectors[cacheName].cacheUserLanguage) this.detectors[cacheName].cacheUserLanguage(req, res, lng, this.options);
92 | });
93 | }
94 | }
95 |
96 | LanguageDetector.type = 'languageDetector';
97 |
98 | export default LanguageDetector;
99 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as utils from './utils';
2 | import LD from './LanguageDetector';
3 |
4 | export var LanguageDetector = LD;
5 |
6 | export function handle(i18next, options = {}) {
7 | return function i18nextMiddleware(req, res, next) {
8 | if (typeof options.ignoreRoutes === 'function') {
9 | if (options.ignoreRoutes(req, res, options, i18next)) {
10 | return next();
11 | }
12 | } else {
13 | let ignores = options.ignoreRoutes instanceof Array&&options.ignoreRoutes || [];
14 | for (var i=0;i< ignores.length;i++){
15 | if (req.path.indexOf(ignores[i]) > -1) return next();
16 | }
17 | }
18 |
19 | let i18n = i18next.cloneInstance({ initImmediate: false });
20 | i18n.on('languageChanged', (lng) => { // Keep language in sync
21 | req.language = req.locale = req.lng = lng;
22 |
23 | if (res.locals) {
24 | res.locals.language = lng;
25 | res.locals.languageDir = i18next.dir(lng);
26 | }
27 |
28 | if (!res.headersSent && lng) {
29 | res.setHeader('Content-Language', lng);
30 | }
31 |
32 | req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
33 |
34 | if (i18next.services.languageDetector) {
35 | i18next.services.languageDetector.cacheUserLanguage(req, res, lng);
36 | }
37 | });
38 |
39 | let lng = req.lng;
40 | if (!req.lng && i18next.services.languageDetector) lng = i18next.services.languageDetector.detect(req, res);
41 |
42 | // set locale
43 | req.language = req.locale = req.lng = lng;
44 | if (!res.headersSent && lng) {
45 | res.setHeader('Content-Language', lng);
46 | }
47 | req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
48 |
49 | // trigger sync to instance - might trigger async load!
50 | i18n.changeLanguage(lng || i18next.options.fallbackLng[0]);
51 |
52 | if(req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
53 | req.url = utils.removeLngFromUrl(req.url, i18next.services.languageDetector.options.lookupFromPathIndex);
54 | }
55 |
56 | let t = i18n.t.bind(i18n);
57 | let exists = i18n.exists.bind(i18n);
58 |
59 | // assert for req
60 | req.i18n = i18n;
61 | req.t = t;
62 |
63 | // assert for res -> template
64 | if (res.locals) {
65 | res.locals.t = t;
66 | res.locals.exists = exists;
67 | res.locals.i18n = i18n;
68 | res.locals.language = lng;
69 | res.locals.languageDir = i18next.dir(lng);
70 | }
71 |
72 | if (i18next.services.languageDetector) i18next.services.languageDetector.cacheUserLanguage(req, res, lng);
73 |
74 | // load resources
75 | if (!req.lng) return next();
76 | i18next.loadLanguages(req.lng, function() {
77 | next();
78 | });
79 | };
80 | };
81 |
82 | export function getResourcesHandler(i18next, options) {
83 | options = options || {};
84 | let maxAge = options.maxAge || 60 * 60 * 24 * 30;
85 |
86 | return function(req, res) {
87 | if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');
88 |
89 | let resources = {};
90 |
91 | res.contentType('json');
92 | if (options.cache !== undefined ? options.cache : process.env.NODE_ENV === 'production') {
93 | res.header('Cache-Control', 'public, max-age=' + maxAge);
94 | res.header('Expires', (new Date(new Date().getTime() + maxAge * 1000)).toUTCString());
95 | } else {
96 | res.header('Pragma', 'no-cache');
97 | res.header('Cache-Control', 'no-cache');
98 | }
99 |
100 | let languages = req.query[options.lngParam || 'lng'] ? req.query[options.lngParam || 'lng'].split(' ') : [];
101 | let namespaces = req.query[options.nsParam || 'ns'] ? req.query[options.nsParam || 'ns'].split(' ') : [];
102 |
103 | // extend ns
104 | namespaces.forEach(ns => {
105 | if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) i18next.options.ns.push(ns);
106 | });
107 |
108 | i18next.services.backendConnector.load(languages, namespaces, function() {
109 | languages.forEach(lng => {
110 | namespaces.forEach(ns => {
111 | utils.setPath(resources, [lng, ns], i18next.getResourceBundle(lng, ns));
112 | });
113 | });
114 |
115 | res.send(resources);
116 | });
117 | };
118 | };
119 |
120 | export function missingKeyHandler(i18next, options) {
121 | options = options || {};
122 |
123 | return function(req, res) {
124 | let lng = req.params[options.lngParam || 'lng'];
125 | let ns = req.params[options.nsParam || 'ns'];
126 |
127 | if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');
128 |
129 | for (var m in req.body) {
130 | i18next.services.backendConnector.saveMissing([lng], ns, m, req.body[m]);
131 | }
132 | res.send('ok');
133 | };
134 | };
135 |
136 | export function addRoute(i18next, route, lngs, app, verb, fc) {
137 | if (typeof verb === 'function') {
138 | fc = verb;
139 | verb = 'get';
140 | }
141 |
142 | // Combine `fc` and possible more callbacks to one array
143 | var callbacks = [fc].concat(Array.prototype.slice.call(arguments, 6));
144 |
145 | for (var i = 0, li = lngs.length; i < li; i++) {
146 | var parts = String(route).split('/');
147 | var locRoute = [];
148 | for (var y = 0, ly = parts.length; y < ly; y++) {
149 | var part = parts[y];
150 | // if the route includes the parameter :lng
151 | // this is replaced with the value of the language
152 | if (part === ':lng') {
153 | locRoute.push(lngs[i]);
154 | } else if (part.indexOf(':') === 0 || part === '') {
155 | locRoute.push(part);
156 | } else {
157 | locRoute.push(i18next.t(part, { lng: lngs[i] }));
158 | }
159 | }
160 |
161 | var routes = [locRoute.join('/')];
162 | app[verb || 'get'].apply(app, routes.concat(callbacks));
163 | }
164 | };
165 |
166 | export default {
167 | handle,
168 | getResourcesHandler,
169 | missingKeyHandler,
170 | addRoute,
171 | LanguageDetector
172 | }
173 |
--------------------------------------------------------------------------------
/src/languageLookups/cookie.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'cookies';
2 |
3 | export default {
4 | name: 'cookie',
5 |
6 | lookup(req, res, options) {
7 | let found;
8 |
9 | if (options.lookupCookie && typeof req !== 'undefined') {
10 | if (req.cookies) {
11 | found = req.cookies[options.lookupCookie];
12 | } else {
13 | const cookies = new Cookies(req, res);
14 | found = cookies.get(options.lookupCookie);
15 | }
16 | }
17 |
18 | return found;
19 | },
20 |
21 | cacheUserLanguage(req, res, lng, options = {}) {
22 | if (options.lookupCookie && req !== 'undefined' && !(res._headerSent || res.headersSent)) {
23 | const cookies = new Cookies(req, res);
24 |
25 | let expirationDate = options.cookieExpirationDate;
26 | if (!expirationDate) {
27 | expirationDate = new Date();
28 | expirationDate.setFullYear(expirationDate.getFullYear() + 1);
29 | }
30 |
31 | const cookieOptions = {
32 | expires: expirationDate,
33 | domain: options.cookieDomain,
34 | httpOnly: false,
35 | overwrite: true
36 | };
37 |
38 | if (options.hasOwnProperty('cookieSecure')) {
39 | cookieOptions.secure = options.cookieSecure;
40 | }
41 |
42 | cookies.set(options.lookupCookie, lng, cookieOptions);
43 | }
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/src/languageLookups/header.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'header',
3 |
4 | lookup(req, res, options) {
5 | let found;
6 |
7 | if (typeof req !== 'undefined') {
8 | let headers = req.headers;
9 | if (!headers) return found;
10 |
11 | let locales = [];
12 | let acceptLanguage = options.lookupHeader ? headers[options.lookupHeader] : headers['accept-language'];
13 |
14 | if (acceptLanguage) {
15 | let lngs = [], i, m;
16 | const rgx = /(([a-z]{2})-?([A-Z]{2})?)\s*;?\s*(q=([0-9.]+))?/gi;
17 |
18 | do {
19 | m = rgx.exec(acceptLanguage);
20 | if (m) {
21 | const lng = m[1], weight = m[5] || '1', q = Number(weight);
22 | if (lng && !isNaN(q)) {
23 | lngs.push({lng, q});
24 | }
25 | }
26 | } while (m);
27 |
28 | lngs.sort(function(a,b) {
29 | return b.q - a.q;
30 | });
31 |
32 | for (i = 0; i < lngs.length; i++) {
33 | locales.push(lngs[i].lng);
34 | }
35 |
36 | if (locales.length) found = locales;
37 | }
38 | }
39 |
40 | return found;
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/src/languageLookups/path.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'path',
3 |
4 | lookup (req, res, options) {
5 | let found;
6 |
7 | if (req === undefined) {
8 | return found;
9 | }
10 |
11 | if (options.lookupPath !== undefined && req.params) {
12 | found = req.params[options.lookupPath];
13 | }
14 |
15 | if (!found && typeof options.lookupFromPathIndex === 'number' && req.originalUrl) {
16 | let path = req.originalUrl.split('?')[0];
17 | let parts = path.split('/');
18 | if (parts[0] === '') { // Handle paths that start with a slash, i.e., '/foo' -> ['', 'foo']
19 | parts.shift();
20 | }
21 |
22 | if (parts.length > options.lookupFromPathIndex) {
23 | found = parts[options.lookupFromPathIndex];
24 | }
25 | }
26 |
27 | return found;
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/src/languageLookups/querystring.js:
--------------------------------------------------------------------------------
1 | import url from 'url';
2 |
3 | export default {
4 | name: 'querystring',
5 |
6 | lookup(req, res, options) {
7 | let found;
8 |
9 | if (options.lookupQuerystring !== undefined && typeof req !== 'undefined') {
10 | if (req.query) {
11 | found = req.query[options.lookupQuerystring];
12 | } else {
13 | let querystring = url.parse(req.url, true);
14 | found = querystring.query[options.lookupQuerystring];
15 | }
16 | }
17 |
18 | return found;
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/src/languageLookups/session.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'session',
3 |
4 | lookup(req, res, options) {
5 | let found;
6 |
7 | if (options.lookupSession !== undefined && typeof req && req.session) {
8 | found = req.session[options.lookupSession];
9 | }
10 |
11 | return found;
12 | },
13 |
14 | cacheUserLanguage(req, res, lng, options = {}) {
15 | if (options.lookupSession && req && req.session) {
16 | req.session[options.lookupSession] = lng;
17 | }
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export function setPath(object, path, newValue) {
2 | let stack;
3 | if (typeof path !== 'string') stack = [].concat(path);
4 | if (typeof path === 'string') stack = path.split('.');
5 |
6 | while(stack.length > 1) {
7 | let key = stack.shift();
8 | if (key.indexOf('###') > -1) key = key.replace(/###/g, '.');
9 | if (!object[key]) object[key] = {};
10 | object = object[key];
11 | }
12 |
13 | let key = stack.shift();
14 | if (key.indexOf('###') > -1) key = key.replace(/###/g, '.');
15 | object[key] = newValue;
16 | }
17 |
18 | let arr = [];
19 | let each = arr.forEach;
20 | let slice = arr.slice;
21 |
22 | export function defaults(obj) {
23 | each.call(slice.call(arguments, 1), function(source) {
24 | if (source) {
25 | for (var prop in source) {
26 | if (obj[prop] === undefined) obj[prop] = source[prop];
27 | }
28 | }
29 | });
30 | return obj;
31 | }
32 |
33 | export function extend(obj) {
34 | each.call(slice.call(arguments, 1), function(source) {
35 | if (source) {
36 | for (var prop in source) {
37 | obj[prop] = source[prop];
38 | }
39 | }
40 | });
41 | return obj;
42 | }
43 |
44 | export function removeLngFromUrl(url, lookupFromPathIndex) {
45 | let first = '';
46 | let pos = lookupFromPathIndex;
47 |
48 | if (url[0] === '/') {
49 | pos++;
50 | first = '/';
51 | }
52 |
53 | // Build new url
54 | let parts = url.split('/');
55 | parts.splice(pos,1);
56 | url = parts.join('/');
57 | if (url[0] !== '/') url = first + url;
58 |
59 | return url;
60 | }
61 |
--------------------------------------------------------------------------------