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"}${date}${duration} `;
+ matchHistoryDOM += `${match.won === 1 ? window.locale["Victory"] : window.locale["Defeat"]}
vs. ${match.match_type === "pvp" ? match.opponent : "AI"}${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 @@
-
+
{{!-- --}}
-
+
-
Wybierz tryb gry
+
{{ t 'menu.index.Select game mode' }}
-
Vs. AI
-
Graj przeciwko komputerowi
+
{{ t 'menu.index.Vs AI' }}
+
{{ t 'menu.index.Play against the computer' }}
-