diff --git a/index.js b/index.js index e32ed36..2f75ac4 100644 --- a/index.js +++ b/index.js @@ -6,16 +6,16 @@ import { createServer } from 'node:http'; import { Server } from 'socket.io'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { v4 as uuidv4, validate } from 'uuid'; +import { v4 as uuidv4 } from 'uuid'; import session from "express-session"; import { engine } from 'express-handlebars'; import { createClient } from 'redis'; import * as bships from './utils/battleships.js'; import { MailAuth } from './utils/auth.js'; +import { Lang } from './utils/localisation.js'; import { rateLimit } from 'express-rate-limit'; import { RedisStore as LimiterRedisStore } from 'rate-limit-redis'; import SessionRedisStore from 'connect-redis'; -import { I18n } from 'i18n'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -37,11 +37,6 @@ const redis = createClient(); redis.on('error', err => console.log('Redis Client Error', err)); await redis.connect(); -const i18n = new I18n({ - locales: ['en', 'pl'], - directory: path.join(__dirname, 'lang') -}); - const limiter = rateLimit({ windowMs: 40 * 1000, limit: 500, @@ -93,26 +88,47 @@ io.engine.use(sessionMiddleware); app.get('/', async (req, res) => { let login = loginState(req); + const locale = new Lang(req.acceptsLanguages()); + if (login != 2) { res.redirect('/login'); } else if (req.session.nickname == null) { auth.getNickname(req.session.userId).then(nickname => { if (nickname != null) { + req.session.langs = req.acceptsLanguages(); req.session.nickname = nickname; - res.render('index'); + + res.render('index', { + helpers: { + t: (key) => { return locale.t(key) } + } + }); } else { res.redirect('/nickname'); } }); } else { - res.render('index'); + req.session.langs = req.acceptsLanguages(); + + res.render('index', { + helpers: { + t: (key) => { return locale.t(key) } + } + }); } }); app.get('/login', (req, res) => { let login = loginState(req); + + const locale = new Lang(req.acceptsLanguages()); + if (!login) { - res.render('login'); + res.render('login', { + helpers: { + t: (key) => { return locale.t(key) } + } + }); } else if (login == 1) { res.redirect('/auth'); } else { @@ -122,10 +138,17 @@ app.get('/login', (req, res) => { app.get('/auth', (req, res) => { let login = loginState(req); + + const locale = new Lang(req.acceptsLanguages()); + if (!login) { // Niezalogowany res.redirect('/login'); } else if (login == 1) { // W trakcie autoryzacji - res.render('auth'); + res.render('auth', { + helpers: { + t: (key) => { return locale.t(key) } + } + }); } else { // Zalogowany res.redirect('/auth'); } @@ -133,15 +156,23 @@ app.get('/auth', (req, res) => { app.get('/nickname', (req, res) => { let login = loginState(req); + + const locale = new Lang(req.acceptsLanguages()); + if (!login) { // Niezalogowany res.redirect('/login'); } else { - res.render('setup'); + res.render('setup', { + helpers: { + t: (key) => { return locale.t(key) } + } + }); } }); app.post('/api/login', (req, res) => { let login = loginState(req); + if (login == 2) { res.redirect('/'); } else if (login == 0 && req.body.email != null && validateEmail(req.body.email)) { @@ -164,19 +195,25 @@ app.post('/api/login', (req, res) => { res.sendStatus(500); } }).catch((err) => { + const locale = new Lang(req.acceptsLanguages()); + res.render("error", { helpers: { error: "Wystąpił nieznany błąd logowania", - fallback: "/login" + fallback: "/login", + t: (key) => { return locale.t(key) } } }); throw err; }); } else { + const locale = new Lang(req.acceptsLanguages()); + res.render("error", { helpers: { error: "Niepoprawny adres e-mail", - fallback: "/login" + fallback: "/login", + } }); } @@ -192,18 +229,24 @@ app.post('/api/auth', async (req, res) => { req.session.loggedIn = 2; res.redirect('/'); } else { + const locale = new Lang(req.acceptsLanguages()); + res.render("error", { helpers: { error: "Niepoprawny kod logowania", - fallback: "/auth" + fallback: "/auth", + t: (key) => { return locale.t(key) } } }); } } else { + const locale = new Lang(req.acceptsLanguages()); + res.render("error", { helpers: { error: "Niepoprawny kod logowania", - fallback: "/login" + fallback: "/login", + t: (key) => { return locale.t(key) } } }); } @@ -222,6 +265,9 @@ app.post('/api/nickname', (req, res) => { }); app.get('/game', async (req, res) => { + + const locale = new Lang(req.acceptsLanguages()); + const game = await redis.json.get(`game:${req.query.id}`); if (req.session.nickname == null) { res.redirect('/setup'); @@ -229,11 +275,16 @@ app.get('/game', async (req, res) => { res.render("error", { helpers: { error: "Nie znaleziono wskazanej gry", - fallback: "/" + fallback: "/", + t: (key) => { return locale.t(key) } } }); } else { - res.render('board'); + res.render('board', { + helpers: { + t: (key) => { return locale.t(key) } + } + }); } }); @@ -428,9 +479,13 @@ io.on('connection', async (socket) => { let shipAvailable = bships.getShipsAvailable(playerShips)[type] > 0; if (!canPlace) { - socket.emit("toast", "Nie możesz postawić tak statku"); + const locale = new Lang(session.langs); + + socket.emit("toast", locale.t("board.You cannot place a ship like this")); } else if (!shipAvailable) { - socket.emit("toast", "Nie masz już statków tego typu"); + const locale = new Lang(session.langs); + + socket.emit("toast", locale.t("board.You have ran out of ships of that type")); } else { await GInfo.placeShip(socket, { type: type, posX: posX, posY: posY, rot: rot, hits: Array.from(new Array(type+1), () => false) }); socket.emit("placed ship", { type: type, posX: posX, posY: posY, rot: rot }); @@ -491,7 +546,9 @@ io.on('connection', async (socket) => { return; } } else if (hit.status === -1) { - socket.emit("toast", "Już strzeliłeś w to miejsce"); + const locale = new Lang(session.langs); + + socket.emit("toast", locale.t("You have already shot at this field")); return; } diff --git a/lang/en.json b/lang/en.json index b60361c..0e52085 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,13 +1,14 @@ { "errors": { - "Reonnecting...": "Reconnecting...", + "Reconnecting": "Reconnecting...", "Reconnected": "Reconnected", "Reconnection error occured": "Reconnection error occured", - "Reconection failed": "Reconnection failed", + "Reconnection failed": "Reconnection failed", "Disconnected": "Disconnected", "Try to refresh the page if this error reoccurs": "Try to refresh the page if this error reoccurs", "Connection error": "Connection error" }, + "menu": { "navbar": { "Main menu": "Main menu", @@ -17,7 +18,7 @@ "Select game mode": "Select game mode", "PvP": "PvP", "Play against another player": "Play against another player", - "Vs. AI": "Vs. AI", + "Vs AI": "Vs. AI", "Play against the computer": "Play against the computer" }, "PvP": { @@ -38,28 +39,77 @@ "Room code": "Room code", "Join": "Join" }, - "PvP/Prepairing": { + "PvP/Loading": { "PvP / Loading": "PvP / Loading", - "Wait...": "Wait...", + "Wait": "Wait...", "You will be redirected soon": "You will be redirected soon", "Opponent:": "Opponent" }, "Profile": { - "Loading...": "Loading...", - "Player since: ": "Player since: ", + "Loading": "Loading...", + "Player since:": "Player since:", "Victory": "Victory", "Defeat": "Defeat", "Click to view match statistics": "Click to view match statistics", "matches played this month": "matches played this month", "total matches played": "total matches played", "winrate": "winrate", - "No matches played": "No matches played" }, - "General": { "Unknown error occured": "Unknown error occured", "Status:": "Status:" } + }, + + "login": { + "Login": "Login", + "E-mail address": "E-mail address", + "Proceed": "Proceed" + }, + + "auth": { + "Authorisation": "Authorisation", + "Enter the code sent to your e-mail box": "Enter the code sent to your e-mail box", + "Code": "Code", + "Login": "Login" + }, + + "setup": { + "Profile setup": "Profile setup", + "Your nickname will be visible to other players": "Your nickname will be visible to other players", + "Nickname": "Nickname", + "Confirm": "Confirm" + }, + + "error": { + "Error": "Erorr", + "Return": "Return" + }, + + "board": { + "Connecting": "Connecting...", + "Waiting for the server": "Waiting for the server...", + "Back to menu": "Back to menu", + + "Selected ship": "Selected ship", + "Single-masted": "Single-masted", + "Two-masted": "Two-masted", + "Three-masted": "Three-masted", + "Four-masted": "Four-masted", + "Available:": "Available:", + + "Controls": "Controls", + "Change ship": "Change ship", + "Rotate ship": "Rotate ship", + "Change boards": "Change boards", + + "Preparation phase": "Preparation phase", + "Your turn": "Your turn", + "Opponents turn": "Opponent's turn", + + "You cannot place a ship like this": "You cannot place a ship like this", + "You have ran out of ships of that type": "You have ran out of ships of that type", + "You have already shot at this field": "You have already shot at this field" } } \ No newline at end of file diff --git a/lang/pl.json b/lang/pl.json index e69de29..6b72101 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -0,0 +1,117 @@ +{ + "errors": { + "Reconnecting": "Ponowne łączenie...", + "Reconnected": "Połączono ponownie", + "Reconnection error occured": "Wystąpił problem podczas ponownego łączenia", + "Reconnection failed": "Ponowne łączenie nieudane", + "Disconnected": "Rozłączono", + "Try to refresh the page if this error reoccurs": "Spróbuj odświeżyć stronę jeżeli błąd będzie się powtarzał", + "Connection error": "Błąd połączenia" + }, + + "menu": { + "navbar": { + "Main menu": "Menu główne", + "Profile": "Profil" + }, + "index": { + "Select game mode": "Wybierz tryb gry", + "PvP": "PvP", + "Play against another player": "Graj przeciwko innemu graczowi", + "Vs AI": "Vs. AI", + "Play against the computer": "Graj przeciwko komputerowi" + }, + "PvP": { + "PvP": "PvP", + "Create": "Stwórz", + "Create your own room": "Stwórz własny pokój", + "Join": "Dołącz", + "Join a room by a code": "Dołącz do pokoju poprzez kod" + }, + "PvP/Create": { + "PvP / Create": "PvP / Stwórz", + "Room code:": "Kod pokoju:", + "Waiting for an opponent": "Oczekiwanie na przeciwnika", + "Leave the room": "Opuść pokój" + }, + "PvP/Join": { + "PvP / Join": "PvP / Dołącz", + "Room code": "Kod pokoju", + "Join": "Dołącz" + }, + "PvP/Loading": { + "PvP / Loading": "PvP / Wczytywanie", + "Wait": "Czekaj...", + "You will be redirected soon": "Wkrótce zostaniesz przekierowany(-a)", + "Opponent:": "Przeciwnik" + }, + "Profile": { + "Loading": "Wczytywanie...", + "Player since:": "Gracz od:", + "Victory": "Zwycięstwo", + "Defeat": "Porażka", + "Click to view match statistics": "Kliknij by wyświetlić statystyki meczu", + "matches played this month": "meczy zagranych w tym miesiącu", + "total matches played": "meczy zagranych łącznie", + "winrate": "winrate", + + "No matches played": "Nie zagrano meczy" + }, + + "General": { + "Unknown error occured": "Wystąpił nieznany błąd", + "Status:": "Status:" + } + }, + + "login": { + "Login": "Zaloguj się", + "E-mail address": "Adres e-mail", + "Proceed": "Kontynuuj" + }, + + "auth": { + "Authorisation": "Autoryzacja", + "Enter the code sent to your e-mail box": "Podaj kod wysłany do twojej skrzynki e-mail", + "Code": "Kod", + "Login": "Zaloguj się" + }, + + "setup": { + "Profile setup": "Konfiguracja profilu", + "Your nickname will be visible to other players": "Twoja nazwa użytkownika będzie widoczna dla innych graczy", + "Nickname": "Nazwa użytkownika", + "Confirm": "Potwierdź" + }, + + "error": { + "Error": "Błąd", + "Return": "Wróć" + }, + + "board": { + "Connecting": "Łączenie...", + "Waiting for the server": "Oczekiwanie na serwer...", + "Back to menu": "Powrót do menu", + + "Selected ship": "Wybrany statek", + "Single-masted": "Jednomasztowiec", + "Two-masted": "Dwumasztowiec", + "Three-masted": "Trójmasztowiec", + "Four-masted": "Czteromasztowiec", + "Available:": "Dostępne:", + + "Controls": "Sterowanie", + "Change ship": "Zmień statek", + "Rotate ship": "Obróć statek", + "Change boards": "Zmień plansze", + + "Preparation phase": "Faza przygotowań", + "Your turn": "Twoja tura", + "Opponents turn": "Tura przeciwnika", + + "You cannot place a ship like this": "Nie możesz tak postawić statku", + "You have ran out of ships of that type": "Skończyły ci się statki tego typu", + "You have already shot at this field": "Już strzelałeś w to pole" + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b6bcd59..11628d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "express-rate-limit": "^7.2.0", "express-session": "^1.18.0", "geoip-lite": "^1.4.10", - "i18n": "^0.15.1", "mysql": "^2.18.1", "nodemailer": "^6.9.12", "rate-limit-redis": "^4.2.0", @@ -42,45 +41,6 @@ "node": ">=12" } }, - "node_modules/@messageformat/core": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.3.0.tgz", - "integrity": "sha512-YcXd3remTDdeMxAlbvW6oV9d/01/DZ8DHUFwSttO3LMzIZj3iO0NRw+u1xlsNNORFI+u0EQzD52ZX3+Udi0T3g==", - "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.0.1", - "resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz", - "integrity": "sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==" - }, - "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==" - }, - "node_modules/@messageformat/parser": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz", - "integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==", - "dependencies": { - "moo": "^0.5.1" - } - }, - "node_modules/@messageformat/runtime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz", - "integrity": "sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==", - "dependencies": { - "make-plural": "^7.0.0" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -273,11 +233,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==" - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -727,17 +682,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/fast-printf": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", - "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", - "dependencies": { - "boolean": "^3.1.4" - }, - "engines": { - "node": ">=10.0" - } - }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -987,25 +931,6 @@ "node": ">= 0.8" } }, - "node_modules/i18n": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.15.1.tgz", - "integrity": "sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA==", - "dependencies": { - "@messageformat/core": "^3.0.0", - "debug": "^4.3.3", - "fast-printf": "^1.6.9", - "make-plural": "^7.0.0", - "math-interval-parser": "^2.0.1", - "mustache": "^4.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/mashpie" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1113,19 +1038,6 @@ "node": "14 || >=16.14" } }, - "node_modules/make-plural": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.3.0.tgz", - "integrity": "sha512-/K3BC0KIsO+WK2i94LkMPv3wslMrazrQhfi5We9fMbLlLjzoOSJWr7TAdupLlDWaJcWxwoNosBkhFDejiu5VDw==" - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1207,24 +1119,11 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/moo": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", - "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" - }, "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==" }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "bin": { - "mustache": "bin/mustache" - } - }, "node_modules/mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -1530,11 +1429,6 @@ } ] }, - "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==" - }, "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 387fbf5..8d48045 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "express-rate-limit": "^7.2.0", "express-session": "^1.18.0", "geoip-lite": "^1.4.10", - "i18n": "^0.15.1", "mysql": "^2.18.1", "nodemailer": "^6.9.12", "rate-limit-redis": "^4.2.0", diff --git a/public/assets/css/main.css b/public/assets/css/main.css index 3a16b84..5e7b385 100644 --- a/public/assets/css/main.css +++ b/public/assets/css/main.css @@ -63,6 +63,7 @@ nav span:hover { background-color: black; border: solid 1px white; border-radius: 15px; + padding: 0.5rem 1rem; user-select: none; cursor: pointer; transition: all 0.3s; @@ -87,6 +88,7 @@ nav span:hover { background-color: black; border: solid 1px white; border-radius: 15px; + padding: 0.5rem 1rem; user-select: none; cursor: pointer; transition: all 0.3s; diff --git a/public/assets/js/main.js b/public/assets/js/main.js index 2a26e53..64939af 100644 --- a/public/assets/js/main.js +++ b/public/assets/js/main.js @@ -156,16 +156,16 @@ function switchShips() { setTimeout(() => { switch (selectedShip) { case 0: - $("#selectedShip").html("Jednomasztowiec"); + $("#selectedShip").html(window.locale["Single-masted"]); break; case 1: - $("#selectedShip").html("Dwumasztowiec"); + $("#selectedShip").html(window.locale["Two-masted"]); break; case 2: - $("#selectedShip").html("Trójmasztowiec"); + $("#selectedShip").html(window.locale["Three-masted"]); break; case 3: - $("#selectedShip").html("Czteromasztowiec"); + $("#selectedShip").html(window.locale["Four-masted"]); break; } diff --git a/public/assets/js/socket-err-handler.js b/public/assets/js/socket-err-handler.js index d8a5d29..0948a2a 100644 --- a/public/assets/js/socket-err-handler.js +++ b/public/assets/js/socket-err-handler.js @@ -1,7 +1,7 @@ // Handling connection errors socket.on("reconnecting", (number) => { Toastify({ - text: `Ponowne łączenie... ${number}`, + text: `${window.locale["Reconnecting"]} ${number}`, duration: 5000, newWindow: true, gravity: "bottom", @@ -13,7 +13,7 @@ socket.on("reconnecting", (number) => { socket.on("reconnect", () => { Toastify({ - text: "Połączono ponownie", + text: window.locale["Reconnected"], duration: 5000, newWindow: true, gravity: "bottom", @@ -25,7 +25,7 @@ socket.on("reconnect", () => { socket.on("reconnect_error", () => { Toastify({ - text: "Wystąpił problem w trakcie ponownego łączenia", + text: window.locale["Reconnection error occured"], duration: 5000, newWindow: true, gravity: "bottom", @@ -37,7 +37,7 @@ socket.on("reconnect_error", () => { socket.on("reconnect_failed", () => { Toastify({ - text: "Nie udało się połączyć ponownie", + text: window.locale["Reconnection failed"], duration: 5000, newWindow: true, gravity: "bottom", @@ -49,7 +49,7 @@ socket.on("reconnect_failed", () => { socket.on("disconnect", () => { Toastify({ - text: "Rozłączono z serwerem\nSpróbuj odświeżyć stronę jeżeli błąd będzie się powtarzał", + text: `${window.locale["Disconnected"]}\n${window.locale["Try to refresh the page if this error reoccurs"]}`, duration: 5000, newWindow: true, gravity: "bottom", @@ -61,7 +61,7 @@ socket.on("disconnect", () => { socket.on("error", () => { Toastify({ - text: "Błąd połączenia", + text: window.locale["Connection error"], duration: 5000, newWindow: true, gravity: "bottom", diff --git a/public/assets/js/socket-game.js b/public/assets/js/socket-game.js index 4711ff0..0c9f5cb 100644 --- a/public/assets/js/socket-game.js +++ b/public/assets/js/socket-game.js @@ -144,14 +144,12 @@ socket.on("ship sunk", (victimIdx, ship) => { let l = !ship.type ? ship.type + 1 : ship.type + 2; if (victimIdx === playerIdx) { for (let i = 0; i < l; i++) { - console.log("ourship"); setTimeout(() => { bsc.setField(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken"); }, i * 150); } } else { for (let i = 0; i < l; i++) { - console.log("theirship"); setTimeout(() => { bsc.setFieldEnemy(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken"); }, i * 150); @@ -174,8 +172,8 @@ var updateTimer = setInterval(() => { $("#timer").removeClass("active"); } - const minutes = Math.floor(time / 60).toLocaleString('pl-PL', { minimumIntegerDigits: 2, useGrouping: false }); - const seconds = (time - minutes * 60).toLocaleString('pl-PL', { minimumIntegerDigits: 2, useGrouping: false }); + const minutes = Math.floor(time / 60).toLocaleString(undefined, { minimumIntegerDigits: 2, useGrouping: false }); + const seconds = (time - minutes * 60).toLocaleString(undefined, { minimumIntegerDigits: 2, useGrouping: false }); $("#timer").html(`${minutes}:${seconds}`); } @@ -200,7 +198,7 @@ socket.on("game finished", (winnerIdx, oppName) => { }); socket.on('connect', () => { - $(".cover .title").html("Oczekiwanie na serwer..."); + $(".cover .title").html(window.locale["Waiting for the server"]); }); socket.on("players ready", () => { @@ -213,12 +211,12 @@ socket.on("player idx", (idx) => { socket.on('turn update', (turnData) => { if (turnData.phase === "preparation") { - $("#whosTurn").html("Faza przygotowań"); + $("#whosTurn").html(window.locale["Preparation phase"]); $(".boardSwitch").css("opacity", 0.3); } else { postPrep = true; myTurn = turnData.turn === playerIdx; - turnData.turn === playerIdx ? $("#whosTurn").html("Twoja tura") : $("#whosTurn").html("Tura przeciwnika"); + turnData.turn === playerIdx ? $("#whosTurn").html(window.locale["Your turn"]) : $("#whosTurn").html(window.locale["Opponents turn"]); $(".boardSwitch").css("opacity", 1); } @@ -229,9 +227,4 @@ socket.on('turn update', (turnData) => { socket.on('player left', () => { window.location.replace("/"); -}); - -// Profile stats: SELECT ROUND((AVG(statistics.won)) * 100) AS winrate, COUNT(statistics.match_id) AS alltime_matches, COUNT(CASE WHEN (YEAR(matches.date) = YEAR(NOW()) AND MONTH(matches.date) = MONTH(NOW())) THEN matches.match_id END) AS monthly_matches FROM accounts NATURAL JOIN statistics NATURAL JOIN matches WHERE accounts.nickname = "MaciejkaG"; -// Match history: SELECT statistics.match_id, accounts.nickname AS opponent, matches.match_type, statistics.won, matches.duration, matches.date FROM statistics JOIN matches ON matches.match_id = statistics.match_id JOIN accounts ON accounts.user_id = (CASE WHEN matches.host_id != statistics.user_id THEN matches.host_id ELSE matches.guest_id END) WHERE statistics.user_id = "c231c4f7-e179-11ee-920b-fa163e32c013"; -// Profile: SELECT nickname, account_creation FROM accounts WHERE user_id = "c231c4f7-e179-11ee-920b-fa163e32c013"; -// Badges: SELECT badge, achievement_date FROM badges WHERE user_id = "c231c4f7-e179-11ee-920b-fa163e32c013"; \ No newline at end of file +}); \ No newline at end of file diff --git a/public/assets/js/socket.js b/public/assets/js/socket.js index b7b13dc..bddf35e 100644 --- a/public/assets/js/socket.js +++ b/public/assets/js/socket.js @@ -26,7 +26,7 @@ var nickname; socket.emit("my profile", (profile) => { // General profile data let options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }; - $("#playerSince").html(new Date(profile.profile.account_creation).toLocaleDateString("pl-PL", options)); + $("#playerSince").html(new Date(profile.profile.account_creation).toLocaleDateString(undefined, options)); $("#nickname").html(profile.profile.nickname); // Profile stats @@ -45,16 +45,16 @@ socket.emit("my profile", (profile) => { let date = new Date(match.date).toLocaleDateString(undefined, options); - const seconds = (match.duration - minutes * 60).toLocaleString(undefined, { minimumIntegerDigits: 2, useGrouping: false }); const minutes = Math.floor(match.duration / 60).toLocaleString(undefined, { minimumIntegerDigits: 2, useGrouping: false }); + const seconds = (match.duration - minutes * 60).toLocaleString(undefined, { minimumIntegerDigits: 2, useGrouping: false }); const duration = `${minutes}:${seconds}`; - matchHistoryDOM += `

${match.won === 1 ? "Zwycięstwo" : "Porażka"}

vs. ${match.match_type === "pvp" ? match.opponent : "AI"}

Kliknij by wyświetlić statystyki

${date}
${duration}
`; + matchHistoryDOM += `

${match.won === 1 ? window.locale["Victory"] : window.locale["Defeat"]}

vs. ${match.match_type === "pvp" ? match.opponent : "AI"}

${window.locale["Click to view match statistics"]}

${date}
${duration}
`; } if (matchHistoryDOM === "") { - matchHistoryDOM = `

${locale["No matches played"]}

`; + matchHistoryDOM = `

${window.locale["No matches played"]}

`; } $(".matchList").html(matchHistoryDOM); @@ -84,7 +84,7 @@ $("#createGameButton").on("click", function () { break; default: - alert(`${locale["Unknown error occured"]}\n${locale["Status:"]} ${response.status}`); + alert(`${window.locale["Unknown error occured"]}\n${window.locale["Status:"]} ${response.status}`); lockUI(false); break; } @@ -122,7 +122,7 @@ form.addEventListener('submit', (e) => { // break; default: - alert(`${locale["Unknown error occured"]}\n${locale["Status:"]} ${response.status}`); + alert(`${window.locale["Unknown error occured"]}\n${window.locale["Status:"]} ${response.status}`); lockUI(false); switchView("mainMenuView"); break; diff --git a/utils/localisation.js b/utils/localisation.js new file mode 100644 index 0000000..bb866ae --- /dev/null +++ b/utils/localisation.js @@ -0,0 +1,41 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export class Lang { + constructor(langs) { + const languagesPath = path.join(__dirname, '../lang'); + for (let i = 0; i < langs.length; i++) { + const lang = langs[i]; + + if (fs.readdirSync(languagesPath).includes(`${lang}.json`)) { + try { + this.allText = JSON.parse(fs.readFileSync(path.join(languagesPath, `${lang}.json`), 'utf8')); + return; + } catch (e) { + console.log(e); + } + } + } + } + + t(key) { + if (this.allText == null) { + throw new Error(`Language class has been improperly configured. (Unknown localisation module error)`); + } else { + let keySplit = key.split("."); + + try { + return keySplit.reduce((x, y) => x[y], this.allText); + } catch (e) { + if (e instanceof TypeError) { + return "LocKeyErr" + } + } + } + } +} + diff --git a/views/auth.handlebars b/views/auth.handlebars index e3c6db3..4cdb3cc 100644 --- a/views/auth.handlebars +++ b/views/auth.handlebars @@ -10,13 +10,13 @@

Statki

-

Autoryzacja

+

{{ t 'auth.Authorisation' }}

-

Podaj kod wysłany do Twojej skrzynki e-mail

+

{{ t 'auth.Enter the code sent to your e-mail box' }}

- - + +
diff --git a/views/board.handlebars b/views/board.handlebars index 19f2b60..1e46171 100644 --- a/views/board.handlebars +++ b/views/board.handlebars @@ -1,9 +1,9 @@
-

Łączenie...

+

{{ t 'board.Connecting' }}

- +
@@ -13,20 +13,20 @@
-

Wybrany statek

-

Jednomasztowiec

-

Dostępne: 1

+

{{ t 'board.Selected ship' }}

+

{{ t 'board.Single-masted' }}

+

{{ t 'board.Available:' }} -

-

Sterowanie

-

S Zmiana statku

-

R Obrót statku

-

B Zamiana planszy

+

{{ t 'board.Controls' }}

+

S {{ t 'board.Change ship' }}

+

R {{ t 'board.Rotate ship' }}

+

B {{ t 'board.Change boards' }}

- - + +

@@ -50,6 +50,29 @@
+ + diff --git a/views/error.handlebars b/views/error.handlebars index 390de1d..3ddb808 100644 --- a/views/error.handlebars +++ b/views/error.handlebars @@ -28,11 +28,11 @@

Statki

-

Błąd

+

{{ t 'error.Error' }}

{{ error }}

- +
diff --git a/views/index.handlebars b/views/index.handlebars index f838eaa..5db898b 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -1,111 +1,114 @@ - + {{!--

Statki

--}} -