[call-me] - wip...
This commit is contained in:
+8
-3
@@ -29,7 +29,8 @@ This repository supports multiple languages via JSON translation files in `app/l
|
||||
|
||||
### Translation source of truth
|
||||
|
||||
- Server + client load translations from `app/locales/<locale>.json`.
|
||||
- Translations live in `app/locales/<locale>.json`.
|
||||
- The server does not translate strings; it only serves these JSON files to the browser.
|
||||
- Locales are discovered dynamically by scanning `app/locales/*.json` (no config change required).
|
||||
|
||||
### API
|
||||
@@ -127,13 +128,17 @@ curl http://localhost:8000/translations/es
|
||||
|
||||
1. Create `app/locales/<locale>.json` (example: `app/locales/nl.json`).
|
||||
2. Copy the structure of `app/locales/en.json` and translate only the values.
|
||||
3. Restart the server.
|
||||
3. No restart is required: the client will see the new locale via `GET /locales`.
|
||||
|
||||
Optional:
|
||||
|
||||
- Set `I18N_WATCH=true` to have the server re-scan `app/locales/` and reconfigure i18n automatically when locale files change.
|
||||
- If you want a friendly label (flag + name) in the Settings dropdown, add your locale to the `getLocaleLabel()` mapping in `public/i18n.js`. Otherwise the dropdown shows the raw locale code.
|
||||
|
||||
## Notes
|
||||
|
||||
- This project uses client-side i18n (see `public/i18n.js`). The server remains language-agnostic.
|
||||
- Any server-generated messages (API errors, WebSocket `error` payloads) are currently plain strings and are not translated.
|
||||
|
||||
## Adding / Changing Translation Keys
|
||||
|
||||
- Keep keys consistent across all locale files.
|
||||
|
||||
@@ -13,7 +13,6 @@ const helmet = require('helmet');
|
||||
const path = require('path');
|
||||
const yaml = require('js-yaml');
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const i18n = require('i18n');
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
// Logs
|
||||
@@ -121,50 +120,6 @@ const corsOptions = {
|
||||
// Create Express application
|
||||
const app = express();
|
||||
|
||||
function configureI18n() {
|
||||
const locales = getAvailableLocales();
|
||||
const effectiveLocales = locales.length > 0 ? locales : ['en'];
|
||||
const defaultLocale = effectiveLocales.includes('en') ? 'en' : effectiveLocales[0] || 'en';
|
||||
|
||||
i18n.configure({
|
||||
locales: effectiveLocales,
|
||||
defaultLocale: defaultLocale,
|
||||
directory: LOCALES_DIR,
|
||||
objectNotation: true,
|
||||
updateFiles: false,
|
||||
syncFiles: false,
|
||||
api: {
|
||||
__: 'translate',
|
||||
__n: 'translateN',
|
||||
},
|
||||
});
|
||||
|
||||
return { locales: effectiveLocales, defaultLocale };
|
||||
}
|
||||
|
||||
// Configure i18n (dynamic locales from app/locales/*.json)
|
||||
configureI18n();
|
||||
|
||||
// Optional: watch locales folder and reconfigure automatically (no restart)
|
||||
if (process.env.I18N_WATCH === 'true') {
|
||||
try {
|
||||
let debounceTimer;
|
||||
fs.watch(LOCALES_DIR, { persistent: false }, () => {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(() => {
|
||||
const state = configureI18n();
|
||||
log.info('i18n reconfigured', state);
|
||||
}, 250);
|
||||
});
|
||||
log.info('i18n watch enabled', { directory: LOCALES_DIR });
|
||||
} catch (error) {
|
||||
log.warn('Unable to watch locales directory', {
|
||||
directory: LOCALES_DIR,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Server configurations
|
||||
const port = process.env.PORT || 4000;
|
||||
const host = process.env.HOST || `http://localhost:${port}`;
|
||||
|
||||
Generated
+2
-135
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "call-me",
|
||||
"version": "1.2.92",
|
||||
"version": "1.2.93",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "call-me",
|
||||
"version": "1.2.92",
|
||||
"version": "1.2.93",
|
||||
"license": "AGPLv3",
|
||||
"dependencies": {
|
||||
"@ngrok/ngrok": "1.7.0",
|
||||
@@ -17,7 +17,6 @@
|
||||
"express": "^5.2.1",
|
||||
"helmet": "^8.1.0",
|
||||
"httpolyglot": "0.1.2",
|
||||
"i18n": "^0.15.3",
|
||||
"js-yaml": "4.1.1",
|
||||
"socket.io": "^4.8.3",
|
||||
"swagger-ui-express": "5.0.1"
|
||||
@@ -27,50 +26,6 @@
|
||||
"prettier": "3.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@messageformat/core": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.4.0.tgz",
|
||||
"integrity": "sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@messageformat/date-skeleton": "^1.0.0",
|
||||
"@messageformat/number-skeleton": "^1.0.0",
|
||||
"@messageformat/parser": "^5.1.0",
|
||||
"@messageformat/runtime": "^3.0.1",
|
||||
"make-plural": "^7.0.0",
|
||||
"safe-identifier": "^0.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@messageformat/date-skeleton": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz",
|
||||
"integrity": "sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@messageformat/number-skeleton": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz",
|
||||
"integrity": "sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@messageformat/parser": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.1.tgz",
|
||||
"integrity": "sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"moo": "^0.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@messageformat/runtime": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.2.tgz",
|
||||
"integrity": "sha512-dkIPDCjXcfhSHgNE1/qV6TeczQZR59Yx0xXeafVKgK3QVWoxc38ljwpksUpnzCGvN151KUbCJTDZVmahtf1YZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"make-plural": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngrok/ngrok": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@ngrok/ngrok/-/ngrok-1.7.0.tgz",
|
||||
@@ -902,15 +857,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-printf": {
|
||||
"version": "1.6.10",
|
||||
"resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.10.tgz",
|
||||
"integrity": "sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
@@ -1185,49 +1131,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18n": {
|
||||
"version": "0.15.3",
|
||||
"resolved": "https://registry.npmjs.org/i18n/-/i18n-0.15.3.tgz",
|
||||
"integrity": "sha512-tW/AA5R4lJZLnd60Agcd0PfXB1C2G7UqTrdNewuv/SIYdxcHkCE8w4Zx1SgCjJ+2BLuAAGIG/KXb/xNYF1lO5Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@messageformat/core": "^3.4.0",
|
||||
"debug": "^4.4.3",
|
||||
"fast-printf": "^1.6.10",
|
||||
"make-plural": "^7.4.0",
|
||||
"math-interval-parser": "^2.0.1",
|
||||
"mustache": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/mashpie"
|
||||
}
|
||||
},
|
||||
"node_modules/i18n/node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/i18n/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
|
||||
@@ -1324,21 +1227,6 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/make-plural": {
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.5.0.tgz",
|
||||
"integrity": "sha512-0booA+aVYyVFoR67JBHdfVk0U08HmrBH2FrtmBqBa+NldlqXv/G2Z9VQuQq6Wgp2jDWdybEWGfBkk1cq5264WA==",
|
||||
"license": "Unicode-DFS-2016"
|
||||
},
|
||||
"node_modules/math-interval-parser": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz",
|
||||
"integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -1400,27 +1288,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/moo": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
|
||||
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mustache": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
|
||||
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mustache": "bin/mustache"
|
||||
}
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
@@ -1689,12 +1562,6 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-identifier": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz",
|
||||
"integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
|
||||
+1
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "call-me",
|
||||
"version": "1.2.92",
|
||||
"version": "1.2.93",
|
||||
"description": "Your Go-To for Instant Video Calls",
|
||||
"author": "Miroslav Pejic - miroslav.pejic.85@gmail.com",
|
||||
"license": "AGPLv3",
|
||||
@@ -27,7 +27,6 @@
|
||||
"express": "^5.2.1",
|
||||
"helmet": "^8.1.0",
|
||||
"httpolyglot": "0.1.2",
|
||||
"i18n": "^0.15.3",
|
||||
"js-yaml": "4.1.1",
|
||||
"socket.io": "^4.8.3",
|
||||
"swagger-ui-express": "5.0.1"
|
||||
|
||||
Reference in New Issue
Block a user