i18next-http-middleware 3.1.6 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -16
- package/cjs/httpFunctions.js +20 -4
- package/cjs/index.js +35 -3
- package/esm/httpFunctions.js +20 -4
- package/esm/index.js +33 -3
- package/lib/httpFunctions.js +26 -9
- package/lib/index.js +77 -16
- package/package.json +2 -1
- package/CHANGELOG.md +0 -55
package/README.md
CHANGED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
[](https://travis-ci.org/i18next/i18next-http-middleware)
|
|
6
6
|
[](https://www.npmjs.com/package/i18next-http-middleware)
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
This is a middleware to be used with Node.js web frameworks like express or Fastify and also for Deno.
|
|
10
9
|
|
|
11
10
|
It's based on the deprecated [i18next-express-middleware](https://github.com/i18next/i18next-express-middleware) and can be used as a drop-in replacement.
|
|
12
|
-
|
|
11
|
+
_It's not bound to a specific http framework anymore._
|
|
12
|
+
|
|
13
|
+
## Advice:
|
|
14
|
+
|
|
15
|
+
To get started with server side internationalization, you may also have a look at [this blog post](https://dev.to/adrai/how-does-server-side-internationalization-i18n-look-like-5f4c) also using [using i18next-http-middleware](https://dev.to/adrai/how-does-server-side-internationalization-i18n-look-like-5f4c#ssr).
|
|
13
16
|
|
|
14
17
|
# Getting started
|
|
15
18
|
|
|
@@ -52,6 +55,7 @@ div = t('myKey')
|
|
|
52
55
|
```
|
|
53
56
|
|
|
54
57
|
### Fastify usage
|
|
58
|
+
|
|
55
59
|
```js
|
|
56
60
|
var i18next = require('i18next')
|
|
57
61
|
var middleware = require('i18next-http-middleware')
|
|
@@ -83,6 +87,46 @@ app.get('myRoute', (request, reply) => {
|
|
|
83
87
|
})
|
|
84
88
|
```
|
|
85
89
|
|
|
90
|
+
### Hapi usage
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
const i18next = require('i18next')
|
|
94
|
+
const middleware = require('i18next-http-middleware')
|
|
95
|
+
const Hapi = require('@hapi/hapi')
|
|
96
|
+
|
|
97
|
+
i18next.use(middleware.LanguageDetector).init({
|
|
98
|
+
preload: ['en', 'de', 'it'],
|
|
99
|
+
...otherOptions
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const server = Hapi.server({
|
|
103
|
+
port: port,
|
|
104
|
+
host: '0.0.0.0',
|
|
105
|
+
|
|
106
|
+
await server.register({
|
|
107
|
+
plugin: i18nextMiddleware.hapiPlugin,
|
|
108
|
+
options: {
|
|
109
|
+
i18next,
|
|
110
|
+
ignoreRoutes: ['/foo'] // or function(req, res, options, i18next) { /* return true to ignore
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// in your request handler
|
|
115
|
+
server.route({
|
|
116
|
+
method: 'GET',
|
|
117
|
+
path: '/myRoute',
|
|
118
|
+
handler: (request, h) => {
|
|
119
|
+
var lng = request.language // 'de-CH'
|
|
120
|
+
var lngs = v.languages // ['de-CH', 'de', 'en']
|
|
121
|
+
request.i18n.changeLanguage('en') // will not load that!!! assert it was preloaded
|
|
122
|
+
|
|
123
|
+
var exists = request.i18n.exists('myKey')
|
|
124
|
+
var translation = request.t('myKey')
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
|
|
86
130
|
### Deno usage
|
|
87
131
|
|
|
88
132
|
#### abc
|
|
@@ -92,7 +136,7 @@ import i18next from 'https://deno.land/x/i18next/index.js'
|
|
|
92
136
|
import Backend from 'https://cdn.jsdelivr.net/gh/i18next/i18next-fs-backend/index.js'
|
|
93
137
|
import i18nextMiddleware from 'https://deno.land/x/i18next_http_middleware/index.js'
|
|
94
138
|
import { Application } from 'https://deno.land/x/abc/mod.ts'
|
|
95
|
-
import { config } from
|
|
139
|
+
import { config } from 'https://deno.land/x/dotenv/dotenv.ts'
|
|
96
140
|
|
|
97
141
|
i18next
|
|
98
142
|
.use(Backend)
|
|
@@ -112,12 +156,10 @@ i18next
|
|
|
112
156
|
const port = config.PORT || 8080
|
|
113
157
|
const app = new Application()
|
|
114
158
|
const handle = i18nextMiddleware.handle(i18next)
|
|
115
|
-
app.use((next) =>
|
|
116
|
-
(c) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
)
|
|
159
|
+
app.use((next) => (c) => {
|
|
160
|
+
handle(c.request, c.response, () => {})
|
|
161
|
+
return next(c)
|
|
162
|
+
})
|
|
121
163
|
app.get('/', (c) => c.request.t('home.title'))
|
|
122
164
|
await app.start({ port })
|
|
123
165
|
```
|
|
@@ -129,7 +171,7 @@ import i18next from 'https://deno.land/x/i18next/index.js'
|
|
|
129
171
|
import Backend from 'https://cdn.jsdelivr.net/gh/i18next/i18next-fs-backend/index.js'
|
|
130
172
|
import i18nextMiddleware from 'https://deno.land/x/i18next_http_middleware/index.js'
|
|
131
173
|
import { createApp } from 'https://servestjs.org/@v1.0.0-rc2/mod.ts'
|
|
132
|
-
import { config } from
|
|
174
|
+
import { config } from 'https://deno.land/x/dotenv/dotenv.ts'
|
|
133
175
|
|
|
134
176
|
i18next
|
|
135
177
|
.use(Backend)
|
|
@@ -153,7 +195,7 @@ app.get('/', async (req) => {
|
|
|
153
195
|
await req.respond({
|
|
154
196
|
status: 200,
|
|
155
197
|
headers: new Headers({
|
|
156
|
-
'content-type': 'text/plain'
|
|
198
|
+
'content-type': 'text/plain'
|
|
157
199
|
}),
|
|
158
200
|
body: req.t('home.title')
|
|
159
201
|
})
|
|
@@ -249,7 +291,7 @@ Define your own functions to handle your custom request or response
|
|
|
249
291
|
middleware.handle(i18next, {
|
|
250
292
|
getPath: (req) => req.path,
|
|
251
293
|
getUrl: (req) => req.url,
|
|
252
|
-
setUrl: (req, url) => req.url = url,
|
|
294
|
+
setUrl: (req, url) => (req.url = url),
|
|
253
295
|
getQuery: (req) => req.query,
|
|
254
296
|
getParams: (req) => req.params,
|
|
255
297
|
getBody: (req) => req.body,
|
|
@@ -270,6 +312,8 @@ Detects user language from current request. Comes with support for:
|
|
|
270
312
|
- querystring
|
|
271
313
|
- session
|
|
272
314
|
|
|
315
|
+
Based on the i18next language detection handling: https://www.i18next.com/misc/creating-own-plugins#languagedetector
|
|
316
|
+
|
|
273
317
|
Wiring up:
|
|
274
318
|
|
|
275
319
|
```js
|
|
@@ -297,7 +341,7 @@ As with all modules you can either pass the constructor function (class) to the
|
|
|
297
341
|
lookupPath: 'lng',
|
|
298
342
|
lookupFromPathIndex: 0,
|
|
299
343
|
|
|
300
|
-
// cache user language
|
|
344
|
+
// cache user language, you can define if an how the detected language should be "saved" => 'cookie' and/or 'session'
|
|
301
345
|
caches: false, // ['cookie']
|
|
302
346
|
|
|
303
347
|
ignoreCase: true, // ignore case of detected language
|
|
@@ -348,15 +392,14 @@ lngDetector.init(options)
|
|
|
348
392
|
module.exports = {
|
|
349
393
|
name: 'myDetectorsName',
|
|
350
394
|
|
|
351
|
-
lookup: function(req, res, options) {
|
|
395
|
+
lookup: function (req, res, options) {
|
|
352
396
|
// options -> are passed in options
|
|
353
397
|
return 'en'
|
|
354
398
|
},
|
|
355
399
|
|
|
356
|
-
cacheUserLanguage: function(req, res, lng, options) {
|
|
400
|
+
cacheUserLanguage: function (req, res, lng, options) {
|
|
357
401
|
// options -> are passed in options
|
|
358
402
|
// lng -> current language, will be called after init and on changeLanguage
|
|
359
|
-
|
|
360
403
|
// store it
|
|
361
404
|
}
|
|
362
405
|
}
|
package/cjs/httpFunctions.js
CHANGED
|
@@ -15,6 +15,7 @@ var getPath = function getPath(req) {
|
|
|
15
15
|
exports.getPath = getPath;
|
|
16
16
|
|
|
17
17
|
var getUrl = function getUrl(req) {
|
|
18
|
+
if (req.url && req.url.href) return req.url.href;
|
|
18
19
|
if (req.url) return req.url;
|
|
19
20
|
if (req.raw && req.raw.url) return req.raw.url;
|
|
20
21
|
console.log('no possibility found to get url');
|
|
@@ -42,8 +43,12 @@ var getOriginalUrl = function getOriginalUrl(req) {
|
|
|
42
43
|
exports.getOriginalUrl = getOriginalUrl;
|
|
43
44
|
|
|
44
45
|
var getQuery = function getQuery(req) {
|
|
45
|
-
if (req.query && typeof req.query.entries === 'function' && typeof Object.fromEntries === 'function' && typeof req.query[Symbol.iterator] === 'function')
|
|
46
|
+
if (req.query && typeof req.query.entries === 'function' && typeof Object.fromEntries === 'function' && typeof req.query[Symbol.iterator] === 'function') {
|
|
47
|
+
return Object.fromEntries(req.query);
|
|
48
|
+
}
|
|
49
|
+
|
|
46
50
|
if (req.query) return req.query;
|
|
51
|
+
if (req.searchParams) return req.searchParams;
|
|
47
52
|
if (req.raw && req.raw.query) return req.raw.query;
|
|
48
53
|
if (req.ctx && req.ctx.queryParams) return req.ctx.queryParams;
|
|
49
54
|
var url = req.url || req.raw && req.raw.url;
|
|
@@ -90,10 +95,14 @@ var getCookies = function getCookies(req) {
|
|
|
90
95
|
exports.getCookies = getCookies;
|
|
91
96
|
|
|
92
97
|
var getBody = function getBody(req) {
|
|
93
|
-
if (req.ctx && typeof req.ctx.body === 'function')
|
|
98
|
+
if (req.ctx && typeof req.ctx.body === 'function') {
|
|
99
|
+
return req.ctx.body.bind(req.ctx);
|
|
100
|
+
}
|
|
101
|
+
|
|
94
102
|
if (req.ctx && req.ctx.body) return req.ctx.body;
|
|
95
103
|
if (req.json) return req.json;
|
|
96
104
|
if (req.body) return req.body;
|
|
105
|
+
if (req.payload) return req.payload;
|
|
97
106
|
console.log('no possibility found to get body');
|
|
98
107
|
return {};
|
|
99
108
|
};
|
|
@@ -114,8 +123,15 @@ var setHeader = function setHeader(res, name, value) {
|
|
|
114
123
|
if (res._headerSent || res.headersSent) return;
|
|
115
124
|
if (typeof res.setHeader === 'function') return res.setHeader(name, value);
|
|
116
125
|
if (typeof res.header === 'function') return res.header(name, value);
|
|
117
|
-
|
|
118
|
-
if (res.
|
|
126
|
+
|
|
127
|
+
if (res.responseHeaders && typeof res.responseHeaders.set === 'function') {
|
|
128
|
+
return res.responseHeaders.set(name, value);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (res.headers && typeof res.headers.set === 'function') {
|
|
132
|
+
return res.headers.set(name, value);
|
|
133
|
+
}
|
|
134
|
+
|
|
119
135
|
console.log('no possibility found to set header');
|
|
120
136
|
};
|
|
121
137
|
|
package/cjs/index.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.addRoute = addRoute;
|
|
|
10
10
|
exports.default = void 0;
|
|
11
11
|
exports.getResourcesHandler = getResourcesHandler;
|
|
12
12
|
exports.handle = handle;
|
|
13
|
+
exports.hapiPlugin = void 0;
|
|
13
14
|
exports.missingKeyHandler = missingKeyHandler;
|
|
14
15
|
exports.plugin = plugin;
|
|
15
16
|
|
|
@@ -25,6 +26,12 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio
|
|
|
25
26
|
|
|
26
27
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
27
28
|
|
|
29
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
30
|
+
|
|
31
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
32
|
+
|
|
33
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
34
|
+
|
|
28
35
|
var LanguageDetector = _LanguageDetector.default;
|
|
29
36
|
exports.LanguageDetector = LanguageDetector;
|
|
30
37
|
|
|
@@ -92,7 +99,11 @@ function handle(i18next) {
|
|
|
92
99
|
}
|
|
93
100
|
});
|
|
94
101
|
var lng = req.lng;
|
|
95
|
-
|
|
102
|
+
|
|
103
|
+
if (!lng && i18next.services.languageDetector) {
|
|
104
|
+
lng = i18next.services.languageDetector.detect(req, res);
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
req.language = req.locale = req.lng = lng;
|
|
97
108
|
|
|
98
109
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
@@ -120,7 +131,10 @@ function handle(i18next) {
|
|
|
120
131
|
res.locals.languageDir = i18next.dir(lng);
|
|
121
132
|
}
|
|
122
133
|
|
|
123
|
-
if (i18next.services.languageDetector)
|
|
134
|
+
if (i18next.services.languageDetector) {
|
|
135
|
+
i18next.services.languageDetector.cacheUserLanguage(req, res, lng);
|
|
136
|
+
}
|
|
137
|
+
|
|
124
138
|
if (!req.lng) return next();
|
|
125
139
|
i18next.loadLanguages(req.lng, function () {
|
|
126
140
|
return next();
|
|
@@ -137,6 +151,21 @@ function plugin(instance, options, next) {
|
|
|
137
151
|
return next();
|
|
138
152
|
}
|
|
139
153
|
|
|
154
|
+
var hapiPlugin = {
|
|
155
|
+
name: 'i18next-http-middleware',
|
|
156
|
+
version: '1',
|
|
157
|
+
register: function register(server, options) {
|
|
158
|
+
options.attachLocals = true;
|
|
159
|
+
var middleware = handle(options.i18next, _objectSpread({}, options));
|
|
160
|
+
server.ext('onPreHandler', function (request, h) {
|
|
161
|
+
middleware(request, request.raw.res || h.response() || request.Response, function () {
|
|
162
|
+
return h.continue;
|
|
163
|
+
});
|
|
164
|
+
return h.continue;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
exports.hapiPlugin = hapiPlugin;
|
|
140
169
|
plugin[Symbol.for('skip-override')] = true;
|
|
141
170
|
|
|
142
171
|
function getResourcesHandler(i18next) {
|
|
@@ -167,7 +196,9 @@ function getResourcesHandler(i18next) {
|
|
|
167
196
|
var languages = options.getQuery(req)[options.lngParam || 'lng'] ? options.getQuery(req)[options.lngParam || 'lng'].split(' ') : [];
|
|
168
197
|
var namespaces = options.getQuery(req)[options.nsParam || 'ns'] ? options.getQuery(req)[options.nsParam || 'ns'].split(' ') : [];
|
|
169
198
|
namespaces.forEach(function (ns) {
|
|
170
|
-
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0)
|
|
199
|
+
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
200
|
+
i18next.options.ns.push(ns);
|
|
201
|
+
}
|
|
171
202
|
});
|
|
172
203
|
i18next.services.backendConnector.load(languages, namespaces, function () {
|
|
173
204
|
languages.forEach(function (lng) {
|
|
@@ -255,6 +286,7 @@ function addRoute(i18next, route, lngs, app, verb, fc) {
|
|
|
255
286
|
|
|
256
287
|
var _default = {
|
|
257
288
|
plugin: plugin,
|
|
289
|
+
hapiPlugin: hapiPlugin,
|
|
258
290
|
handle: handle,
|
|
259
291
|
getResourcesHandler: getResourcesHandler,
|
|
260
292
|
missingKeyHandler: missingKeyHandler,
|
package/esm/httpFunctions.js
CHANGED
|
@@ -5,6 +5,7 @@ export var getPath = function getPath(req) {
|
|
|
5
5
|
console.log('no possibility found to get path');
|
|
6
6
|
};
|
|
7
7
|
export var getUrl = function getUrl(req) {
|
|
8
|
+
if (req.url && req.url.href) return req.url.href;
|
|
8
9
|
if (req.url) return req.url;
|
|
9
10
|
if (req.raw && req.raw.url) return req.raw.url;
|
|
10
11
|
console.log('no possibility found to get url');
|
|
@@ -23,8 +24,12 @@ export var getOriginalUrl = function getOriginalUrl(req) {
|
|
|
23
24
|
return getUrl(req);
|
|
24
25
|
};
|
|
25
26
|
export var getQuery = function getQuery(req) {
|
|
26
|
-
if (req.query && typeof req.query.entries === 'function' && typeof Object.fromEntries === 'function' && typeof req.query[Symbol.iterator] === 'function')
|
|
27
|
+
if (req.query && typeof req.query.entries === 'function' && typeof Object.fromEntries === 'function' && typeof req.query[Symbol.iterator] === 'function') {
|
|
28
|
+
return Object.fromEntries(req.query);
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
if (req.query) return req.query;
|
|
32
|
+
if (req.searchParams) return req.searchParams;
|
|
28
33
|
if (req.raw && req.raw.query) return req.raw.query;
|
|
29
34
|
if (req.ctx && req.ctx.queryParams) return req.ctx.queryParams;
|
|
30
35
|
var url = req.url || req.raw && req.raw.url;
|
|
@@ -59,10 +64,14 @@ export var getCookies = function getCookies(req) {
|
|
|
59
64
|
console.log('no possibility found to get cookies');
|
|
60
65
|
};
|
|
61
66
|
export var getBody = function getBody(req) {
|
|
62
|
-
if (req.ctx && typeof req.ctx.body === 'function')
|
|
67
|
+
if (req.ctx && typeof req.ctx.body === 'function') {
|
|
68
|
+
return req.ctx.body.bind(req.ctx);
|
|
69
|
+
}
|
|
70
|
+
|
|
63
71
|
if (req.ctx && req.ctx.body) return req.ctx.body;
|
|
64
72
|
if (req.json) return req.json;
|
|
65
73
|
if (req.body) return req.body;
|
|
74
|
+
if (req.payload) return req.payload;
|
|
66
75
|
console.log('no possibility found to get body');
|
|
67
76
|
return {};
|
|
68
77
|
};
|
|
@@ -77,8 +86,15 @@ export var setHeader = function setHeader(res, name, value) {
|
|
|
77
86
|
if (res._headerSent || res.headersSent) return;
|
|
78
87
|
if (typeof res.setHeader === 'function') return res.setHeader(name, value);
|
|
79
88
|
if (typeof res.header === 'function') return res.header(name, value);
|
|
80
|
-
|
|
81
|
-
if (res.
|
|
89
|
+
|
|
90
|
+
if (res.responseHeaders && typeof res.responseHeaders.set === 'function') {
|
|
91
|
+
return res.responseHeaders.set(name, value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (res.headers && typeof res.headers.set === 'function') {
|
|
95
|
+
return res.headers.set(name, value);
|
|
96
|
+
}
|
|
97
|
+
|
|
82
98
|
console.log('no possibility found to set header');
|
|
83
99
|
};
|
|
84
100
|
export var setContentType = function setContentType(res, type) {
|
package/esm/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
+
|
|
3
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
4
|
+
|
|
5
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
+
|
|
1
7
|
import * as utils from './utils.js';
|
|
2
8
|
import LD from './LanguageDetector.js';
|
|
3
9
|
import { extendOptionsWithDefaults } from './httpFunctions.js';
|
|
@@ -67,7 +73,11 @@ export function handle(i18next) {
|
|
|
67
73
|
}
|
|
68
74
|
});
|
|
69
75
|
var lng = req.lng;
|
|
70
|
-
|
|
76
|
+
|
|
77
|
+
if (!lng && i18next.services.languageDetector) {
|
|
78
|
+
lng = i18next.services.languageDetector.detect(req, res);
|
|
79
|
+
}
|
|
80
|
+
|
|
71
81
|
req.language = req.locale = req.lng = lng;
|
|
72
82
|
|
|
73
83
|
if (lng && options.getHeader(res, 'Content-Language') !== lng) {
|
|
@@ -95,7 +105,10 @@ export function handle(i18next) {
|
|
|
95
105
|
res.locals.languageDir = i18next.dir(lng);
|
|
96
106
|
}
|
|
97
107
|
|
|
98
|
-
if (i18next.services.languageDetector)
|
|
108
|
+
if (i18next.services.languageDetector) {
|
|
109
|
+
i18next.services.languageDetector.cacheUserLanguage(req, res, lng);
|
|
110
|
+
}
|
|
111
|
+
|
|
99
112
|
if (!req.lng) return next();
|
|
100
113
|
i18next.loadLanguages(req.lng, function () {
|
|
101
114
|
return next();
|
|
@@ -110,6 +123,20 @@ export function plugin(instance, options, next) {
|
|
|
110
123
|
});
|
|
111
124
|
return next();
|
|
112
125
|
}
|
|
126
|
+
export var hapiPlugin = {
|
|
127
|
+
name: 'i18next-http-middleware',
|
|
128
|
+
version: '1',
|
|
129
|
+
register: function register(server, options) {
|
|
130
|
+
options.attachLocals = true;
|
|
131
|
+
var middleware = handle(options.i18next, _objectSpread({}, options));
|
|
132
|
+
server.ext('onPreHandler', function (request, h) {
|
|
133
|
+
middleware(request, request.raw.res || h.response() || request.Response, function () {
|
|
134
|
+
return h.continue;
|
|
135
|
+
});
|
|
136
|
+
return h.continue;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
};
|
|
113
140
|
plugin[Symbol.for('skip-override')] = true;
|
|
114
141
|
export function getResourcesHandler(i18next) {
|
|
115
142
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
@@ -139,7 +166,9 @@ export function getResourcesHandler(i18next) {
|
|
|
139
166
|
var languages = options.getQuery(req)[options.lngParam || 'lng'] ? options.getQuery(req)[options.lngParam || 'lng'].split(' ') : [];
|
|
140
167
|
var namespaces = options.getQuery(req)[options.nsParam || 'ns'] ? options.getQuery(req)[options.nsParam || 'ns'].split(' ') : [];
|
|
141
168
|
namespaces.forEach(function (ns) {
|
|
142
|
-
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0)
|
|
169
|
+
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
170
|
+
i18next.options.ns.push(ns);
|
|
171
|
+
}
|
|
143
172
|
});
|
|
144
173
|
i18next.services.backendConnector.load(languages, namespaces, function () {
|
|
145
174
|
languages.forEach(function (lng) {
|
|
@@ -224,6 +253,7 @@ export function addRoute(i18next, route, lngs, app, verb, fc) {
|
|
|
224
253
|
}
|
|
225
254
|
export default {
|
|
226
255
|
plugin: plugin,
|
|
256
|
+
hapiPlugin: hapiPlugin,
|
|
227
257
|
handle: handle,
|
|
228
258
|
getResourcesHandler: getResourcesHandler,
|
|
229
259
|
missingKeyHandler: missingKeyHandler,
|
package/lib/httpFunctions.js
CHANGED
|
@@ -5,6 +5,7 @@ export const getPath = (req) => {
|
|
|
5
5
|
console.log('no possibility found to get path')
|
|
6
6
|
}
|
|
7
7
|
export const getUrl = (req) => {
|
|
8
|
+
if (req.url && req.url.href) return req.url.href
|
|
8
9
|
if (req.url) return req.url
|
|
9
10
|
if (req.raw && req.raw.url) return req.raw.url
|
|
10
11
|
console.log('no possibility found to get url')
|
|
@@ -22,8 +23,16 @@ export const getOriginalUrl = (req) => {
|
|
|
22
23
|
return getUrl(req)
|
|
23
24
|
}
|
|
24
25
|
export const getQuery = (req) => {
|
|
25
|
-
if (
|
|
26
|
+
if (
|
|
27
|
+
req.query &&
|
|
28
|
+
typeof req.query.entries === 'function' &&
|
|
29
|
+
typeof Object.fromEntries === 'function' &&
|
|
30
|
+
typeof req.query[Symbol.iterator] === 'function'
|
|
31
|
+
) {
|
|
32
|
+
return Object.fromEntries(req.query)
|
|
33
|
+
}
|
|
26
34
|
if (req.query) return req.query
|
|
35
|
+
if (req.searchParams) return req.searchParams
|
|
27
36
|
if (req.raw && req.raw.query) return req.raw.query
|
|
28
37
|
if (req.ctx && req.ctx.queryParams) return req.ctx.queryParams
|
|
29
38
|
const url = req.url || (req.raw && req.raw.url)
|
|
@@ -47,19 +56,23 @@ export const getCookies = (req) => {
|
|
|
47
56
|
if (getHeaders(req)) {
|
|
48
57
|
const list = {}
|
|
49
58
|
const rc = getHeaders(req).cookie
|
|
50
|
-
rc &&
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
rc &&
|
|
60
|
+
rc.split(';').forEach((cookie) => {
|
|
61
|
+
const parts = cookie.split('=')
|
|
62
|
+
list[parts.shift().trim()] = decodeURI(encodeURI(parts.join('=')))
|
|
63
|
+
})
|
|
54
64
|
return list
|
|
55
65
|
}
|
|
56
66
|
console.log('no possibility found to get cookies')
|
|
57
67
|
}
|
|
58
68
|
export const getBody = (req) => {
|
|
59
|
-
if (req.ctx && typeof req.ctx.body === 'function')
|
|
69
|
+
if (req.ctx && typeof req.ctx.body === 'function') {
|
|
70
|
+
return req.ctx.body.bind(req.ctx)
|
|
71
|
+
}
|
|
60
72
|
if (req.ctx && req.ctx.body) return req.ctx.body
|
|
61
73
|
if (req.json) return req.json
|
|
62
74
|
if (req.body) return req.body
|
|
75
|
+
if (req.payload) return req.payload
|
|
63
76
|
console.log('no possibility found to get body')
|
|
64
77
|
return {}
|
|
65
78
|
}
|
|
@@ -74,8 +87,12 @@ export const setHeader = (res, name, value) => {
|
|
|
74
87
|
if (res._headerSent || res.headersSent) return
|
|
75
88
|
if (typeof res.setHeader === 'function') return res.setHeader(name, value)
|
|
76
89
|
if (typeof res.header === 'function') return res.header(name, value)
|
|
77
|
-
if (res.responseHeaders && typeof res.responseHeaders.set === 'function')
|
|
78
|
-
|
|
90
|
+
if (res.responseHeaders && typeof res.responseHeaders.set === 'function') {
|
|
91
|
+
return res.responseHeaders.set(name, value)
|
|
92
|
+
}
|
|
93
|
+
if (res.headers && typeof res.headers.set === 'function') {
|
|
94
|
+
return res.headers.set(name, value)
|
|
95
|
+
}
|
|
79
96
|
console.log('no possibility found to set header')
|
|
80
97
|
}
|
|
81
98
|
export const setContentType = (res, type) => {
|
|
@@ -86,7 +103,7 @@ export const setContentType = (res, type) => {
|
|
|
86
103
|
export const setStatus = (res, code) => {
|
|
87
104
|
if (typeof res.status === 'function') return res.status(code)
|
|
88
105
|
// eslint-disable-next-line no-return-assign
|
|
89
|
-
if (res.status) return res.status = code
|
|
106
|
+
if (res.status) return (res.status = code)
|
|
90
107
|
console.log('no possibility found to set status')
|
|
91
108
|
}
|
|
92
109
|
export const send = (res, body) => {
|
package/lib/index.js
CHANGED
|
@@ -30,14 +30,16 @@ export function handle (i18next, options = {}) {
|
|
|
30
30
|
return next()
|
|
31
31
|
}
|
|
32
32
|
} else {
|
|
33
|
-
const ignores =
|
|
33
|
+
const ignores =
|
|
34
|
+
(options.ignoreRoutes instanceof Array && options.ignoreRoutes) || []
|
|
34
35
|
for (let i = 0; i < ignores.length; i++) {
|
|
35
36
|
if (options.getPath(req).indexOf(ignores[i]) > -1) return next()
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
const i18n = i18next.cloneInstance({ initImmediate: false })
|
|
40
|
-
i18n.on('languageChanged',
|
|
41
|
+
i18n.on('languageChanged', lng => {
|
|
42
|
+
// Keep language in sync
|
|
41
43
|
req.language = req.locale = req.lng = lng
|
|
42
44
|
|
|
43
45
|
if (options.attachLocals) res.locals = res.locals || {}
|
|
@@ -58,7 +60,9 @@ export function handle (i18next, options = {}) {
|
|
|
58
60
|
})
|
|
59
61
|
|
|
60
62
|
let lng = req.lng
|
|
61
|
-
if (!lng && i18next.services.languageDetector)
|
|
63
|
+
if (!lng && i18next.services.languageDetector) {
|
|
64
|
+
lng = i18next.services.languageDetector.detect(req, res)
|
|
65
|
+
}
|
|
62
66
|
|
|
63
67
|
// set locale
|
|
64
68
|
req.language = req.locale = req.lng = lng
|
|
@@ -71,7 +75,13 @@ export function handle (i18next, options = {}) {
|
|
|
71
75
|
i18n.changeLanguage(lng || i18next.options.fallbackLng[0])
|
|
72
76
|
|
|
73
77
|
if (req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
|
|
74
|
-
options.setUrl(
|
|
78
|
+
options.setUrl(
|
|
79
|
+
req,
|
|
80
|
+
utils.removeLngFromUrl(
|
|
81
|
+
options.getUrl(req),
|
|
82
|
+
i18next.services.languageDetector.options.lookupFromPathIndex
|
|
83
|
+
)
|
|
84
|
+
)
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
const t = i18n.t.bind(i18n)
|
|
@@ -91,7 +101,9 @@ export function handle (i18next, options = {}) {
|
|
|
91
101
|
res.locals.languageDir = i18next.dir(lng)
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
if (i18next.services.languageDetector)
|
|
104
|
+
if (i18next.services.languageDetector) {
|
|
105
|
+
i18next.services.languageDetector.cacheUserLanguage(req, res, lng)
|
|
106
|
+
}
|
|
95
107
|
// load resources
|
|
96
108
|
if (!req.lng) return next()
|
|
97
109
|
i18next.loadLanguages(req.lng, () => next())
|
|
@@ -101,9 +113,31 @@ export function handle (i18next, options = {}) {
|
|
|
101
113
|
export function plugin (instance, options, next) {
|
|
102
114
|
options.attachLocals = true
|
|
103
115
|
const middleware = handle(options.i18next, options)
|
|
104
|
-
instance.addHook('preHandler', (request, reply, next) =>
|
|
116
|
+
instance.addHook('preHandler', (request, reply, next) =>
|
|
117
|
+
middleware(request, reply, next)
|
|
118
|
+
)
|
|
105
119
|
return next()
|
|
106
120
|
}
|
|
121
|
+
|
|
122
|
+
export const hapiPlugin = {
|
|
123
|
+
name: 'i18next-http-middleware',
|
|
124
|
+
version: '1',
|
|
125
|
+
register: (server, options) => {
|
|
126
|
+
options.attachLocals = true
|
|
127
|
+
const middleware = handle(options.i18next, {
|
|
128
|
+
...options
|
|
129
|
+
})
|
|
130
|
+
server.ext('onPreHandler', (request, h) => {
|
|
131
|
+
middleware(
|
|
132
|
+
request,
|
|
133
|
+
request.raw.res || h.response() || request.Response,
|
|
134
|
+
() => h.continue
|
|
135
|
+
)
|
|
136
|
+
return h.continue
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
107
141
|
plugin[Symbol.for('skip-override')] = true
|
|
108
142
|
|
|
109
143
|
export function getResourcesHandler (i18next, options = {}) {
|
|
@@ -115,32 +149,55 @@ export function getResourcesHandler (i18next, options = {}) {
|
|
|
115
149
|
|
|
116
150
|
if (!i18next.services.backendConnector) {
|
|
117
151
|
options.setStatus(res, 404)
|
|
118
|
-
return options.send(
|
|
152
|
+
return options.send(
|
|
153
|
+
res,
|
|
154
|
+
'i18next-express-middleware:: no backend configured'
|
|
155
|
+
)
|
|
119
156
|
}
|
|
120
157
|
|
|
121
158
|
const resources = {}
|
|
122
159
|
|
|
123
160
|
options.setContentType(res, 'application/json')
|
|
124
|
-
if (
|
|
161
|
+
if (
|
|
162
|
+
options.cache !== undefined
|
|
163
|
+
? options.cache
|
|
164
|
+
: typeof process !== 'undefined' &&
|
|
165
|
+
process.env &&
|
|
166
|
+
process.env.NODE_ENV === 'production'
|
|
167
|
+
) {
|
|
125
168
|
options.setHeader(res, 'Cache-Control', 'public, max-age=' + maxAge)
|
|
126
|
-
options.setHeader(
|
|
169
|
+
options.setHeader(
|
|
170
|
+
res,
|
|
171
|
+
'Expires',
|
|
172
|
+
new Date(new Date().getTime() + maxAge * 1000).toUTCString()
|
|
173
|
+
)
|
|
127
174
|
} else {
|
|
128
175
|
options.setHeader(res, 'Pragma', 'no-cache')
|
|
129
176
|
options.setHeader(res, 'Cache-Control', 'no-cache')
|
|
130
177
|
}
|
|
131
178
|
|
|
132
|
-
const languages = options.getQuery(req)[options.lngParam || 'lng']
|
|
133
|
-
|
|
179
|
+
const languages = options.getQuery(req)[options.lngParam || 'lng']
|
|
180
|
+
? options.getQuery(req)[options.lngParam || 'lng'].split(' ')
|
|
181
|
+
: []
|
|
182
|
+
const namespaces = options.getQuery(req)[options.nsParam || 'ns']
|
|
183
|
+
? options.getQuery(req)[options.nsParam || 'ns'].split(' ')
|
|
184
|
+
: []
|
|
134
185
|
|
|
135
186
|
// extend ns
|
|
136
187
|
namespaces.forEach(ns => {
|
|
137
|
-
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0)
|
|
188
|
+
if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) {
|
|
189
|
+
i18next.options.ns.push(ns)
|
|
190
|
+
}
|
|
138
191
|
})
|
|
139
192
|
|
|
140
193
|
i18next.services.backendConnector.load(languages, namespaces, function () {
|
|
141
194
|
languages.forEach(lng => {
|
|
142
195
|
namespaces.forEach(ns => {
|
|
143
|
-
utils.setPath(
|
|
196
|
+
utils.setPath(
|
|
197
|
+
resources,
|
|
198
|
+
[lng, ns],
|
|
199
|
+
i18next.getResourceBundle(lng, ns)
|
|
200
|
+
)
|
|
144
201
|
})
|
|
145
202
|
})
|
|
146
203
|
})
|
|
@@ -160,7 +217,10 @@ export function missingKeyHandler (i18next, options = {}) {
|
|
|
160
217
|
|
|
161
218
|
if (!i18next.services.backendConnector) {
|
|
162
219
|
options.setStatus(res, 404)
|
|
163
|
-
return options.send(
|
|
220
|
+
return options.send(
|
|
221
|
+
res,
|
|
222
|
+
'i18next-express-middleware:: no backend configured'
|
|
223
|
+
)
|
|
164
224
|
}
|
|
165
225
|
|
|
166
226
|
const body = options.getBody(req)
|
|
@@ -168,8 +228,8 @@ export function missingKeyHandler (i18next, options = {}) {
|
|
|
168
228
|
if (typeof body === 'function') {
|
|
169
229
|
const promise = body()
|
|
170
230
|
if (promise && typeof promise.then === 'function') {
|
|
171
|
-
return new Promise(
|
|
172
|
-
promise.then(
|
|
231
|
+
return new Promise(resolve => {
|
|
232
|
+
promise.then(b => {
|
|
173
233
|
for (const m in b) {
|
|
174
234
|
i18next.services.backendConnector.saveMissing([lng], ns, m, b[m])
|
|
175
235
|
}
|
|
@@ -219,6 +279,7 @@ export function addRoute (i18next, route, lngs, app, verb, fc) {
|
|
|
219
279
|
|
|
220
280
|
export default {
|
|
221
281
|
plugin,
|
|
282
|
+
hapiPlugin,
|
|
222
283
|
handle,
|
|
223
284
|
getResourcesHandler,
|
|
224
285
|
missingKeyHandler,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18next-http-middleware",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./cjs/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@babel/cli": "7.16.0",
|
|
25
25
|
"@babel/core": "7.16.5",
|
|
26
26
|
"@babel/preset-env": "7.16.5",
|
|
27
|
+
"@hapi/hapi": "^20.2.1",
|
|
27
28
|
"@types/express-serve-static-core": "^4.17.27",
|
|
28
29
|
"babel-plugin-add-module-exports": "1.0.4",
|
|
29
30
|
"eslint": "7.29.0",
|
package/CHANGELOG.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
## [v3.1.6](https://github.com/i18next/i18next-http-middleware/compare/v3.1.5...v3.1.6)
|
|
2
|
-
- add Fastify type support [#47](https://github.com/i18next/i18next-http-middleware/pull/47)
|
|
3
|
-
|
|
4
|
-
## [v3.1.5](https://github.com/i18next/i18next-http-middleware/compare/v3.1.4...v3.1.5)
|
|
5
|
-
- fallbackLng option can also be a function
|
|
6
|
-
|
|
7
|
-
## [v3.1.4](https://github.com/i18next/i18next-http-middleware/compare/v3.1.3...v3.1.4)
|
|
8
|
-
- fix for [point-of-view](https://github.com/fastify/point-of-view)
|
|
9
|
-
- update all dependencies
|
|
10
|
-
|
|
11
|
-
## [v3.1.3](https://github.com/i18next/i18next-http-middleware/compare/v3.1.2...v3.1.3)
|
|
12
|
-
- optimize getQuery() function to check if req.query is iterable
|
|
13
|
-
|
|
14
|
-
## [v3.1.2](https://github.com/i18next/i18next-http-middleware/compare/v3.1.1...v3.1.2)
|
|
15
|
-
- fix the type of the lookup method [#37](https://github.com/i18next/i18next-http-middleware/pull/37)
|
|
16
|
-
|
|
17
|
-
## [v3.1.1](https://github.com/i18next/i18next-http-middleware/compare/v3.1.0...v3.1.1)
|
|
18
|
-
- make sure no undefined language is detected
|
|
19
|
-
|
|
20
|
-
## [v3.1.0](https://github.com/i18next/i18next-http-middleware/compare/v3.0.6...v3.1.0)
|
|
21
|
-
- added types
|
|
22
|
-
|
|
23
|
-
## [v3.0.6](https://github.com/i18next/i18next-http-middleware/compare/v3.0.5...v3.0.6)
|
|
24
|
-
- ignoreCase option true by default
|
|
25
|
-
|
|
26
|
-
## [v3.0.5](https://github.com/i18next/i18next-http-middleware/compare/v3.0.4...v3.0.5)
|
|
27
|
-
- introduce ignoreCase option
|
|
28
|
-
|
|
29
|
-
## [v3.0.4](https://github.com/i18next/i18next-http-middleware/compare/v3.0.3...v3.0.4)
|
|
30
|
-
- fix language detection algorithm to handle fallbackLng correctly
|
|
31
|
-
|
|
32
|
-
## [v3.0.3](https://github.com/i18next/i18next-http-middleware/compare/v3.0.2...v3.0.3)
|
|
33
|
-
- prevent URIError with cookie language detector
|
|
34
|
-
|
|
35
|
-
## [v3.0.2](https://github.com/i18next/i18next-http-middleware/compare/v3.0.1...v3.0.2)
|
|
36
|
-
- transpile also esm
|
|
37
|
-
|
|
38
|
-
## [v3.0.1](https://github.com/i18next/i18next-http-middleware/compare/v3.0.0...v3.0.1)
|
|
39
|
-
- introduce lookupHeaderRegex option
|
|
40
|
-
|
|
41
|
-
## [v3.0.0](https://github.com/i18next/i18next-http-middleware/compare/v2.1.2...v3.0.0)
|
|
42
|
-
- **BREAKING** needs i18next >= 19.5.0
|
|
43
|
-
- let i18next figure out which detected lng is best match
|
|
44
|
-
|
|
45
|
-
## [v2.1.2](https://github.com/i18next/i18next-http-middleware/compare/v2.1.1...v2.1.2)
|
|
46
|
-
- fix: get whitelist from correct property
|
|
47
|
-
|
|
48
|
-
## [v2.1.1](https://github.com/i18next/i18next-http-middleware/compare/v2.1.0...v2.1.1)
|
|
49
|
-
- extend httpFunctions getQuery to handle some edge cases
|
|
50
|
-
|
|
51
|
-
## [v2.1.0](https://github.com/i18next/i18next-http-middleware/compare/v2.0.0...v2.1.0)
|
|
52
|
-
- LanguageDetector: cookie, new option: cookiePath
|
|
53
|
-
|
|
54
|
-
## [v2.0.0](https://github.com/i18next/i18next-http-middleware/compare/v1.3.1...v2.0.0)
|
|
55
|
-
- potentially BREAKING: change cookie defaults: cookieSameSite: 'strict'
|