feat(server): add i18n for translation of api
Implementation of i18n system to API translation. This change use i18next lib, simple and extensible internationalization-framework, with support to plugins. - out of router: use passing i18next on index.js for any class. Look tv-guide-service.js - on a route: use req.t(). look api.js
This commit is contained in:
parent
1002b0dc76
commit
5913b7b3e9
28
index.js
28
index.js
@ -5,6 +5,9 @@ const path = require('path')
|
||||
const express = require('express')
|
||||
const bodyParser = require('body-parser')
|
||||
const fileUpload = require('express-fileupload');
|
||||
const i18next = require('i18next');
|
||||
const i18nextMiddleware = require('i18next-http-middleware');
|
||||
const i18nextBackend = require('i18next-fs-backend');
|
||||
|
||||
const api = require('./src/api')
|
||||
const dbMigration = require('./src/database-migration');
|
||||
@ -88,9 +91,24 @@ eventService = new EventService();
|
||||
initDB(db, channelDB)
|
||||
|
||||
|
||||
const guideService = new TVGuideService(xmltv, db, cacheImageService);
|
||||
i18next
|
||||
.use(i18nextBackend)
|
||||
.use(i18nextMiddleware.LanguageDetector)
|
||||
.init({
|
||||
// debug: true,
|
||||
initImmediate: false,
|
||||
backend: {
|
||||
loadPath: path.join(__dirname, '/locales/server/{{lng}}.json'),
|
||||
addPath: path.join(__dirname, '/locales/server/{{lng}}.json')
|
||||
},
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
preload: ['en'],
|
||||
});
|
||||
|
||||
|
||||
const guideService = new TVGuideService(xmltv, db, cacheImageService, null, i18next);
|
||||
|
||||
|
||||
let xmltvInterval = {
|
||||
interval: null,
|
||||
@ -175,6 +193,10 @@ let hdhr = HDHR(db, channelDB)
|
||||
let app = express()
|
||||
eventService.setup(app);
|
||||
|
||||
app.use(
|
||||
i18nextMiddleware.handle(i18next, {})
|
||||
);
|
||||
|
||||
app.use(fileUpload({
|
||||
createParentPath: true
|
||||
}));
|
||||
@ -271,7 +293,7 @@ async function sendEventAfterTime() {
|
||||
eventService.push(
|
||||
"lifecycle",
|
||||
{
|
||||
"message": `Server Started`,
|
||||
"message": i18next.t("event.server_started"),
|
||||
"detail" : {
|
||||
"time": t,
|
||||
},
|
||||
@ -290,7 +312,7 @@ onShutdown("log" , [], async() => {
|
||||
eventService.push(
|
||||
"lifecycle",
|
||||
{
|
||||
"message": `Initiated Server Shutdown`,
|
||||
"message": i18next.t("event.server_shutdown"),
|
||||
"detail" : {
|
||||
"time": t,
|
||||
},
|
||||
|
||||
15
locales/server/en.json
Normal file
15
locales/server/en.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"event":{
|
||||
"server_started": "Server Started",
|
||||
"server_shutdown": "Initiated Server Shutdown"
|
||||
},
|
||||
"api": {
|
||||
"plex_server_not_found": "Plex server not found.",
|
||||
"missing_name": "Missing name"
|
||||
},
|
||||
"tvGuide": {
|
||||
"no_channels": "No channels configured",
|
||||
"no_channels_summary": "Use the dizqueTV web UI to configure channels.",
|
||||
"xmltv_updated": "XMLTV updated at server time {{t}}"
|
||||
}
|
||||
}
|
||||
135
package-lock.json
generated
135
package-lock.json
generated
@ -10,14 +10,20 @@
|
||||
"dependencies": {
|
||||
"angular": "^1.8.0",
|
||||
"angular-router-browserify": "0.0.2",
|
||||
"angular-sanitize": "^1.8.2",
|
||||
"angular-vs-repeat": "2.0.13",
|
||||
"axios": "^0.21.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"diskdb": "0.1.17",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.2.1",
|
||||
"i18next": "^20.3.2",
|
||||
"i18next-fs-backend": "^1.1.1",
|
||||
"i18next-http-backend": "^1.2.6",
|
||||
"i18next-http-middleware": "^3.1.4",
|
||||
"JSONStream": "1.0.5",
|
||||
"merge": "2.1.1",
|
||||
"ng-i18next": "^1.0.7",
|
||||
"node-graceful-shutdown": "1.1.0",
|
||||
"node-ssdp": "^4.0.0",
|
||||
"random-js": "2.1.0",
|
||||
@ -1175,12 +1181,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||
"dev": true,
|
||||
"version": "7.14.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz",
|
||||
"integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
@ -1898,6 +1906,11 @@
|
||||
"resolved": "https://registry.npmjs.org/angular-router-browserify/-/angular-router-browserify-0.0.2.tgz",
|
||||
"integrity": "sha1-euL98uLowGxYz8aXrz56XohkJBg="
|
||||
},
|
||||
"node_modules/angular-sanitize": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.8.2.tgz",
|
||||
"integrity": "sha512-OB6Goa+QN3byf5asQ7XRl7DKZejm/F/ZOqa9z1skqYVOWA2hoBxoCmt9E7+i7T/TbxZP5zYzKxNZVVJNu860Hg=="
|
||||
},
|
||||
"node_modules/angular-vs-repeat": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/angular-vs-repeat/-/angular-vs-repeat-2.0.13.tgz",
|
||||
@ -4023,6 +4036,14 @@
|
||||
"sha.js": "^2.4.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
|
||||
"integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==",
|
||||
"dependencies": {
|
||||
"node-fetch": "2.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-browserify": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
|
||||
@ -7157,6 +7178,32 @@
|
||||
"url": "https://github.com/sponsors/typicode"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.2.tgz",
|
||||
"integrity": "sha512-e8CML2R9Ng2sSQOM80wb/PrM2j8mDm84o/T4Amzn9ArVyNX5/ENWxxAXkRpZdTQNDaxKImF93Wep4mAoozFrKw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-fs-backend": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-1.1.1.tgz",
|
||||
"integrity": "sha512-RFkfy10hNxJqc7MVAp5iAZq0Tum6msBCNebEe3OelOBvrROvzHUPaR8Qe10RQrOGokTm0W4vJGEJzruFkEt+hQ=="
|
||||
},
|
||||
"node_modules/i18next-http-backend": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.2.6.tgz",
|
||||
"integrity": "sha512-NeNNRofj+rR6Cw+/Elf8bCVaCiqWg2Y6F+CrmDvHiPzAW2Dtxxlk8O0na2et/rr1n3ST6rJr4nMXH/QOFuhaeA==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "3.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-http-middleware": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.4.tgz",
|
||||
"integrity": "sha512-OVjxnw1w9RqhrOBSsXBXbk/7kzpLu+YvaL0LZnLHTCzjs8dMG8/BuoaDxi1l30T1lUfmweb+er/xwRHV3lp0RQ=="
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
@ -8771,6 +8818,23 @@
|
||||
"rimraf": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ng-i18next": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ng-i18next/-/ng-i18next-1.0.7.tgz",
|
||||
"integrity": "sha512-eLEAvxi50w05wJdVxlkEF61mIQmhBSn8xhE+9a2y4nkpyJAUf7TTpA9SrID21UYdZQIIyWYoMie+QemRhRnGtw==",
|
||||
"peerDependencies": {
|
||||
"angular": ">=1.6.0",
|
||||
"i18next": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-graceful-shutdown": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-graceful-shutdown/-/node-graceful-shutdown-1.1.0.tgz",
|
||||
@ -10082,8 +10146,7 @@
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
"version": "0.14.4",
|
||||
@ -13087,10 +13150,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||
"dev": true,
|
||||
"version": "7.14.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz",
|
||||
"integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
@ -13666,6 +13728,11 @@
|
||||
"resolved": "https://registry.npmjs.org/angular-router-browserify/-/angular-router-browserify-0.0.2.tgz",
|
||||
"integrity": "sha1-euL98uLowGxYz8aXrz56XohkJBg="
|
||||
},
|
||||
"angular-sanitize": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.8.2.tgz",
|
||||
"integrity": "sha512-OB6Goa+QN3byf5asQ7XRl7DKZejm/F/ZOqa9z1skqYVOWA2hoBxoCmt9E7+i7T/TbxZP5zYzKxNZVVJNu860Hg=="
|
||||
},
|
||||
"angular-vs-repeat": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/angular-vs-repeat/-/angular-vs-repeat-2.0.13.tgz",
|
||||
@ -15435,6 +15502,14 @@
|
||||
"sha.js": "^2.4.8"
|
||||
}
|
||||
},
|
||||
"cross-fetch": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
|
||||
"integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==",
|
||||
"requires": {
|
||||
"node-fetch": "2.6.1"
|
||||
}
|
||||
},
|
||||
"crypto-browserify": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
|
||||
@ -17974,6 +18049,32 @@
|
||||
"integrity": "sha512-xK7lO0EtSzfFPiw+oQncQVy/XqV7UVVjxBByc+Iv5iK3yhW9boDoWgvZy3OGo48QKg/hUtZkzz0hi2HXa0kn7w==",
|
||||
"dev": true
|
||||
},
|
||||
"i18next": {
|
||||
"version": "20.3.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.2.tgz",
|
||||
"integrity": "sha512-e8CML2R9Ng2sSQOM80wb/PrM2j8mDm84o/T4Amzn9ArVyNX5/ENWxxAXkRpZdTQNDaxKImF93Wep4mAoozFrKw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.0"
|
||||
}
|
||||
},
|
||||
"i18next-fs-backend": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-1.1.1.tgz",
|
||||
"integrity": "sha512-RFkfy10hNxJqc7MVAp5iAZq0Tum6msBCNebEe3OelOBvrROvzHUPaR8Qe10RQrOGokTm0W4vJGEJzruFkEt+hQ=="
|
||||
},
|
||||
"i18next-http-backend": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-1.2.6.tgz",
|
||||
"integrity": "sha512-NeNNRofj+rR6Cw+/Elf8bCVaCiqWg2Y6F+CrmDvHiPzAW2Dtxxlk8O0na2et/rr1n3ST6rJr4nMXH/QOFuhaeA==",
|
||||
"requires": {
|
||||
"cross-fetch": "3.1.4"
|
||||
}
|
||||
},
|
||||
"i18next-http-middleware": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-middleware/-/i18next-http-middleware-3.1.4.tgz",
|
||||
"integrity": "sha512-OVjxnw1w9RqhrOBSsXBXbk/7kzpLu+YvaL0LZnLHTCzjs8dMG8/BuoaDxi1l30T1lUfmweb+er/xwRHV3lp0RQ=="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
@ -19256,6 +19357,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ng-i18next": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ng-i18next/-/ng-i18next-1.0.7.tgz",
|
||||
"integrity": "sha512-eLEAvxi50w05wJdVxlkEF61mIQmhBSn8xhE+9a2y4nkpyJAUf7TTpA9SrID21UYdZQIIyWYoMie+QemRhRnGtw==",
|
||||
"requires": {}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"node-graceful-shutdown": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-graceful-shutdown/-/node-graceful-shutdown-1.1.0.tgz",
|
||||
@ -20288,8 +20400,7 @@
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.14.4",
|
||||
|
||||
@ -19,14 +19,20 @@
|
||||
"dependencies": {
|
||||
"angular": "^1.8.0",
|
||||
"angular-router-browserify": "0.0.2",
|
||||
"angular-sanitize": "^1.8.2",
|
||||
"angular-vs-repeat": "2.0.13",
|
||||
"axios": "^0.21.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"diskdb": "0.1.17",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.2.1",
|
||||
"i18next": "^20.3.2",
|
||||
"i18next-fs-backend": "^1.1.1",
|
||||
"i18next-http-backend": "^1.2.6",
|
||||
"i18next-http-middleware": "^3.1.4",
|
||||
"JSONStream": "1.0.5",
|
||||
"merge": "2.1.1",
|
||||
"ng-i18next": "^1.0.7",
|
||||
"node-graceful-shutdown": "1.1.0",
|
||||
"node-ssdp": "^4.0.0",
|
||||
"random-js": "2.1.0",
|
||||
|
||||
@ -62,7 +62,7 @@ function api(db, channelDB, fillerDB, customShowDB, xmltvInterval, guideService
|
||||
name: req.body.name,
|
||||
});
|
||||
if (servers.length != 1) {
|
||||
return res.status(404).send("Plex server not found.");
|
||||
return res.status(404).send(req.t("api.plex_server_not_found"));
|
||||
}
|
||||
let plex = new Plex(servers[0]);
|
||||
let s = await Promise.race( [
|
||||
|
||||
@ -8,7 +8,7 @@ class TVGuideService
|
||||
/****
|
||||
*
|
||||
**/
|
||||
constructor(xmltv, db, cacheImageService, eventService) {
|
||||
constructor(xmltv, db, cacheImageService, eventService, i18next) {
|
||||
this.cached = null;
|
||||
this.lastUpdate = 0;
|
||||
this.updateTime = 0;
|
||||
@ -20,6 +20,7 @@ class TVGuideService
|
||||
this.cacheImageService = cacheImageService;
|
||||
this.eventService = eventService;
|
||||
this._throttle = throttle;
|
||||
this.i18next = i18next;
|
||||
}
|
||||
|
||||
async get() {
|
||||
@ -322,9 +323,9 @@ class TVGuideService
|
||||
program: {
|
||||
duration: 24*60*60*1000,
|
||||
icon: FALLBACK_ICON,
|
||||
showTitle: "No channels configured",
|
||||
showTitle: this.i18next.t("tvGuide.no_channels"),
|
||||
date: formatDateYYYYMMDD(new Date()),
|
||||
summary : "Use the dizqueTV web UI to configure channels."
|
||||
summary : this.i18next.t("tvGuide.no_channels_summary")
|
||||
}
|
||||
} )
|
||||
]
|
||||
@ -365,7 +366,7 @@ class TVGuideService
|
||||
eventService.push(
|
||||
"xmltv",
|
||||
{
|
||||
"message": `XMLTV updated at server time = ${t}`,
|
||||
"message": this.i18next.t("tvGuide.xmltv_updated", {t}),
|
||||
"module" : "xmltv",
|
||||
"detail" : {
|
||||
"time": new Date(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user