add locale support

This commit is contained in:
2025-05-12 23:10:33 +02:00
parent c4d2349b6a
commit 583b01d92e
35 changed files with 177 additions and 45 deletions
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 320 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 258 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 380 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 569 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 198 B

After

Width:  |  Height:  |  Size: 198 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 381 B

+19
View File
@@ -0,0 +1,19 @@
{
"title": "Rollenspiel Würfel",
"reset-dices": "Würfel zurücksetzen",
"clear-history": "Verlauf löschen",
"export-data": "Daten exportieren",
"import-data": "Daten importieren",
"dark-mode": "Dunkler Modus",
"light-mode": "Heller Modus",
"update": "Update versuchen",
"add-selected": "Ausgewählte addieren",
"roll-selected": "Ausgewählte würfeln",
"new-dices": "Neue Würfel",
"roll": "Würfeln",
"custom": "Benutzerdefiniert",
"new-dice": "Neuer Würfel",
"regex": "(\\d+)?[w|W](\\d+)([\\+|\\-]\\d+)?(\\[(.+)\\])?",
"regex-placeholder": "1W6+1 + 2W4",
"D": "W"
}
+19
View File
@@ -0,0 +1,19 @@
{
"title": "Role Playing Game Dices",
"reset-dices": "Reset Dices",
"clear-history": "Clear History",
"export-data": "Export Data",
"import-data": "Import Data",
"dark-mode": "Dark Mode",
"light-mode": "Light Mode",
"update": "Try to Update",
"add-selected": "Add Selected",
"roll-selected": "Roll Selected",
"new-dices": "New Dices",
"roll": "Roll",
"custom": "Custom",
"new-dice": "New Dice",
"regex": "(\\d+)?[d|D](\\d+)([\\+|\\-]\\d+)?(\\[(.+)\\])?",
"regex-placeholder": "1D6+1 + 2D4",
"D": "D"
}
Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 194 B

After

Width:  |  Height:  |  Size: 194 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 202 B

After

Width:  |  Height:  |  Size: 202 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 192 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 686 B

After

Width:  |  Height:  |  Size: 686 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 286 B

After

Width:  |  Height:  |  Size: 286 B

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 405 B

+76 -29
View File
@@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Role Playing Game Dices</title> <title data-i18n="title">Role Playing Game Dices</title>
<meta name="application-name" content="Role Playing Game Dices"> <meta name="application-name" content="Role Playing Game Dices">
<meta name="application-version" content="v0.2.2"> <meta name="application-version" content="v0.2.2">
@@ -37,20 +37,45 @@
<label id="sidebar-toggle-close" class="sidebar-toggle text-fresh float-right" for="sidebar-toggle-input"><img <label id="sidebar-toggle-close" class="sidebar-toggle text-fresh float-right" for="sidebar-toggle-input"><img
src="./assets/close.svg"></label> src="./assets/close.svg"></label>
<ul class="menu"> <ul class="menu">
<li onclick="resetDices()"><img src="./assets/dices.svg"> Reset Dices</li> <li onclick="resetDices()">
<li onclick="clearHistory()"><img src="./assets/history.svg"> Clear History</li> <img src="./assets/dices.svg">
<li onclick="exportData()"><img src="./assets/export.svg"> Export Data</li> <span data-i18n="reset-dices">Reset Dices</span>
<li><label><img src="./assets/import.svg"> Import Data </li>
<li onclick="clearHistory()">
<img src="./assets/history.svg">
<span data-i18n="clear-history">Clear History</span>
</li>
<li onclick="exportData()">
<img src="./assets/export.svg">
<span data-i18n="export-data">Export Data</span>
</li>
<li>
<label>
<img src="./assets/import.svg">
<span data-i18n="import-data">Import Data</span>
<input id="importFile" type="file" accept="application/json" /> <input id="importFile" type="file" accept="application/json" />
</label> </label>
</li> </li>
</ul> </ul>
<ul class="menu"> <ul class="menu">
<li onclick="toggleDarkMode()"><img id="dark-mode-icon" src="./assets/dark.svg"><span <li onclick="toggleDarkMode()">
id="dark-mode-text">Dark Mode</span></li> <img id="dark-mode-icon" src="./assets/dark.svg">
<span id="dark-mode-text">Dark Mode</span>
</li>
</ul> </ul>
<ul class="menu"> <ul class="menu">
<li onclick="clearAndRefresh()"><img id="dark-mode-icon" src="./assets/update.svg"><span>Try to Update</span></li> <li onclick="setLocale('en')">
<span>English</span>
</li>
<li onclick="setLocale('de')">
<span>Deutsch</span>
</li>
</ul>
<ul class="menu">
<li onclick="clearAndRefresh()">
<img id="dark-mode-icon" src="./assets/update.svg">
<span data-i18n="update">Try to Update</span>
</li>
</ul> </ul>
<script> <script>
Sidebar(); Sidebar();
@@ -62,10 +87,14 @@
<div class="form-container"> <div class="form-container">
<div class="actions"> <div class="actions">
<button id="roll-add-button" onclick="addSelected()"><img src="./assets/add.svg"> Add <button id="roll-add-button" onclick="addSelected()">
Selected</button> <img src="./assets/add.svg">
<button id="roll-button" onclick="rollSelected()"><img src="./assets/roll.svg"> Roll <span data-i18n="add-selected">Add Selected</span>
Selected</button> </button>
<button id="roll-button" onclick="rollSelected()">
<img src="./assets/roll.svg">
<span data-i18n="roll-selected">Roll Selected</span>
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -73,35 +102,47 @@
<div class="history-container"> <div class="history-container">
<div class="actions"> <div class="actions">
<button id="history-undo-button" onclick="undo()"><img src="./assets/undo.svg"></button> <button id="history-undo-button" onclick="undo()">
<button id="history-button" onclick="clearHistory()"><img src="./assets/history.svg"> Clear <img src="./assets/undo.svg">
History</button> </button>
<button id="history-redo-button" onclick="redo()"><img src="./assets/redo.svg"></button> <button id="history-button" onclick="clearHistory()">
<img src="./assets/history.svg">
<span data-i18n="clear-history">Clear History</span>
</button>
<button id="history-redo-button" onclick="redo()">
<img src="./assets/redo.svg">
</button>
</div> </div>
<div class="history" id="history"></div> <div class="history" id="history"></div>
<div class="form-container"> <div class="form-container">
<div class="form"> <div class="form">
<input type="text" id="inputText" placeholder="1D6+1 + 2W4"> <input type="text" id="inputText" placeholder="1D6+1 + 2D4" data-i18n-placeholder="regex-placeholder">
</div> </div>
<div class="actions"> <div class="actions">
<button onclick="addDicesText()"><img src="./assets/plus.svg"> New Dices</button> <button onclick="addDicesText()">
<button onclick="rollText()"><img src="./assets/roll.svg"> Roll</button> <img src="./assets/plus.svg">
<span data-i18n="new-dices">New Dices</span>
</button>
<button onclick="rollText()">
<img src="./assets/roll.svg">
<span data-i18n="roll">Roll</span>
</button>
</div> </div>
<div class="form"> <div class="form">
<input min="1" value="1" type="number" id="inputCount"> <input min="1" value="1" type="number" id="inputCount">
D D
<select value="6" id="inputSides" onchange="updateCustom()"> <select value="6" id="inputSides" onchange="updateCustom()">
<option>4</option> <option value="4">4</option>
<option>6</option> <option value="6">6</option>
<option>8</option> <option value="8">8</option>
<option>10</option> <option value="10">10</option>
<option>12</option> <option value="12">12</option>
<option>20</option> <option value="20">20</option>
<option>100</option> <option value="100">100</option>
<option value="">custom</option> <option value="" data-i18n="custom">custom</option>
</select> </select>
<input class="hidden" min="2" value="2" type="number" id="inputCustom"> <input class="hidden" min="2" value="2" type="number" id="inputCustom">
+ +
@@ -110,8 +151,14 @@
<input type="color" id="inputColor"> <input type="color" id="inputColor">
</div> </div>
<div class="actions"> <div class="actions">
<button onclick="addDiceForm()"><img src="./assets/plus.svg"> New Dice</button> <button onclick="addDiceForm()">
<button onclick="rollForm()"><img src="./assets/roll.svg"> Roll</button> <img src="./assets/plus.svg">
<span data-i18n="new-dice">New Dice</span>
</button>
<button onclick="rollForm()">
<img src="./assets/roll.svg">
<span data-i18n="roll">Roll</span>
</button>
</div> </div>
</div> </div>
</div> </div>
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
+63 -16
View File
@@ -12,7 +12,7 @@ class Dice {
if (this.count > 1) { if (this.count > 1) {
result += this.count; result += this.count;
} }
result += "D" + this.sides; result += (localeData && localeData['D'] || "D") + this.sides;
if (this.addition) { if (this.addition) {
result += (this.addition < 0 ? "-" : "+") + Math.abs(this.addition); result += (this.addition < 0 ? "-" : "+") + Math.abs(this.addition);
} }
@@ -23,7 +23,7 @@ class Dice {
} }
fromText(value) { fromText(value) {
const result = value.match(dice_regex); const result = value.match(localeData && localeData['regex'] || dice_regex);
if (result) { if (result) {
if (result[1]) { if (result[1]) {
this.count = +result[1]; this.count = +result[1];
@@ -35,6 +35,8 @@ class Dice {
if (result[5]) { if (result[5]) {
this.color = result[5]; this.color = result[5];
} }
} else {
throw Error("Invalid Text: " + value);
} }
} }
} }
@@ -51,14 +53,16 @@ class DiceHistoryEntry {
const default_sides = [4, 6, 8, 10, 12, 20, 100]; const default_sides = [4, 6, 8, 10, 12, 20, 100];
const default_colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2", "#6c584c"]; const default_colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2", "#6c584c"];
const dice_regex = /(\d+)?[D|d](\d+)([\+|\-]\d+)?(\[(.+)\])?/; const dice_regex = new RegExp("(\\d+)?[D|d](\\d+)([\\+|\\-]\\d+)?(\\[(.+)\\])?");
let darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; let darkMode = localStorage.getItem('dark-mode') === 'false' ? false : localStorage.getItem('dark-mode') === 'true' || window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
let locale = localStorage.getItem('locale') || 'en';
let localeData = {};
let dices = []; let dices = [];
let history = []; let history = [];
let redoHistory = []; let redoHistory = [];
let dicesContainer = document.getElementById("dices"); let dicesContainer = document.getElementById("dices");
let historyContainer = document.getElementById("history"); let historyContainer = document.getElementById("history");
@@ -264,6 +268,7 @@ function renderHistory() {
historyContainer.appendChild(diceTime); historyContainer.appendChild(diceTime);
if (entry.time) { if (entry.time) {
entry.time.locale(locale);
diceTime.innerText = entry.time.fromNow(); diceTime.innerText = entry.time.fromNow();
diceTime.title = entry.time.format("LLLL"); diceTime.title = entry.time.format("LLLL");
} }
@@ -468,13 +473,14 @@ function toggleDarkMode() {
darkMode = false; darkMode = false;
document.body.classList.remove("dark"); document.body.classList.remove("dark");
document.getElementById("dark-mode-icon").src = "./assets/dark.svg"; document.getElementById("dark-mode-icon").src = "./assets/dark.svg";
document.getElementById("dark-mode-text").innerText = "Dark Mode"; document.getElementById("dark-mode-text").innerText = localeData && localeData['dark-mode'] || "Dark Mode";
} else { } else {
darkMode = true; darkMode = true;
document.body.classList.add("dark"); document.body.classList.add("dark");
document.getElementById("dark-mode-icon").src = "./assets/light.svg"; document.getElementById("dark-mode-icon").src = "./assets/light.svg";
document.getElementById("dark-mode-text").innerText = "Light Mode"; document.getElementById("dark-mode-text").innerText = localeData && localeData['light-mode'] || "Light Mode";
} }
localStorage.setItem('dark-mode', darkMode);
renderDices(); renderDices();
renderHistory(); renderHistory();
} }
@@ -508,6 +514,17 @@ function redo() {
renderHistory(); renderHistory();
} }
function renderDarkMode() {
if (darkMode) {
document.body.classList.add("dark");
document.getElementById("dark-mode-icon").src = "./assets/light.svg";
document.getElementById("dark-mode-text").innerText = localeData && localeData['light-mode'] || "Light Mode";
} else {
document.getElementById("dark-mode-icon").src = "./assets/dark.svg";
document.getElementById("dark-mode-text").innerText = localeData && localeData['dark-mode'] || "Dark Mode";
}
}
async function clearAndRefresh() { async function clearAndRefresh() {
if ('caches' in window) { if ('caches' in window) {
const keyList = await caches.keys(); const keyList = await caches.keys();
@@ -516,6 +533,33 @@ async function clearAndRefresh() {
window.location.reload() window.location.reload()
} }
async function setLocale(newLocale) {
locale = newLocale;
localStorage.setItem('locale', locale);
const response = await fetch('assets/i18n/' + locale + '.json');
localeData = await response.json();
if (localeData) {
document.querySelectorAll('[data-i18n]').forEach((el) => {
const key = el.getAttribute('data-i18n');
if (localeData[key]) {
el.innerHTML = localeData[key];
}
})
document.querySelectorAll('[data-i18n-placeholder]').forEach((el) => {
const key = el.getAttribute('data-i18n-placeholder');
if (localeData[key]) {
el.placeholder = localeData[key];
}
})
}
moment.locale(locale);
renderDices();
renderHistory();
renderDarkMode();
updateCustom();
}
if (localStorage.getItem('dices')) { if (localStorage.getItem('dices')) {
dices = JSON.parse(localStorage.getItem('dices')).map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color)); dices = JSON.parse(localStorage.getItem('dices')).map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color));
} else { } else {
@@ -542,6 +586,17 @@ document.getElementById("inputText").addEventListener("keyup", (event) => {
document.addEventListener("keyup", (event) => { document.addEventListener("keyup", (event) => {
let prevent = false;
document.querySelectorAll('input').forEach((input) => {
if (input === document.activeElement) {
prevent = true;
}
})
if (prevent) {
return;
}
if (!isNaN(+event.key)) { if (!isNaN(+event.key)) {
let number = +event.key; let number = +event.key;
if (number == 0) { if (number == 0) {
@@ -561,15 +616,7 @@ document.addEventListener("keyup", (event) => {
} }
}); });
renderDices(); setLocale(locale);
renderHistory();
updateCustom();
if (darkMode) {
document.body.classList.add("dark");
document.getElementById("dark-mode-icon").src = "./assets/light.svg";
document.getElementById("dark-mode-text").innerText = "Light Mode";
}
if ("serviceWorker" in navigator) { if ("serviceWorker" in navigator) {
window.addEventListener("load", function () { window.addEventListener("load", function () {
Regular → Executable
View File