diff --git a/I18N_README.md b/I18N_README.md index 7959914..8f7f1de 100644 --- a/I18N_README.md +++ b/I18N_README.md @@ -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/.json`. +- Translations live in `app/locales/.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/.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. diff --git a/app/server.js b/app/server.js index f32e874..cbe504a 100755 --- a/app/server.js +++ b/app/server.js @@ -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}`; diff --git a/package-lock.json b/package-lock.json index a48c589..6b1f257 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 74cca53..8687d93 100755 --- a/package.json +++ b/package.json @@ -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"