mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Initial support for translations
This commit is contained in:
23
src/js/languages.js
Normal file
23
src/js/languages.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @type {Object<string, {name: string, data: any, code: string, region: string}>}
|
||||
*/
|
||||
export const LANGUAGES = {
|
||||
en: {
|
||||
name: "English",
|
||||
data: null,
|
||||
code: "en",
|
||||
region: "",
|
||||
},
|
||||
de: {
|
||||
name: "Deutsch",
|
||||
data: require("./built-temp/base-de.json"),
|
||||
code: "de",
|
||||
region: "",
|
||||
},
|
||||
fr: {
|
||||
name: "Français",
|
||||
data: require("./built-temp/base-fr.json"),
|
||||
code: "fr",
|
||||
region: "",
|
||||
},
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { ExplainedResult } from "../core/explained_result";
|
||||
import { THEMES, THEME, applyGameTheme } from "../game/theme";
|
||||
import { IS_DEMO } from "../core/config";
|
||||
import { T } from "../translations";
|
||||
import { LANGUAGES } from "../languages";
|
||||
|
||||
const logger = createLogger("application_settings");
|
||||
|
||||
@@ -63,6 +64,16 @@ export const scrollWheelSensitivities = [
|
||||
|
||||
/** @type {Array<BaseSetting>} */
|
||||
export const allApplicationSettings = [
|
||||
new EnumSetting("language", {
|
||||
options: Object.keys(LANGUAGES),
|
||||
valueGetter: key => key,
|
||||
textGetter: key => LANGUAGES[key].name,
|
||||
category: categoryApp,
|
||||
restartRequired: true,
|
||||
changeCb: (app, id) => null,
|
||||
magicValue: "auto-detect",
|
||||
}),
|
||||
|
||||
new EnumSetting("uiScale", {
|
||||
options: uiScales.sort((a, b) => a.size - b.size),
|
||||
valueGetter: scale => scale.id,
|
||||
@@ -165,6 +176,7 @@ class SettingsStorage {
|
||||
this.theme = "light";
|
||||
this.refreshRate = "60";
|
||||
this.scrollWheelSensitivity = "regular";
|
||||
this.language = "auto-detect";
|
||||
|
||||
this.alwaysMultiplace = false;
|
||||
this.offerHints = true;
|
||||
@@ -259,8 +271,17 @@ export class ApplicationSettings extends ReadWriteProxy {
|
||||
return this.getAllSettings().keybindingOverrides;
|
||||
}
|
||||
|
||||
getLanguage() {
|
||||
return this.getAllSettings().language;
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
updateLanguage(id) {
|
||||
assert(LANGUAGES[id], "Language not known: " + id);
|
||||
return this.updateSetting("language", id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string|boolean} value
|
||||
@@ -337,7 +358,7 @@ export class ApplicationSettings extends ReadWriteProxy {
|
||||
}
|
||||
|
||||
getCurrentVersion() {
|
||||
return 8;
|
||||
return 9;
|
||||
}
|
||||
|
||||
/** @param {{settings: SettingsStorage, version: number}} data */
|
||||
@@ -364,6 +385,11 @@ export class ApplicationSettings extends ReadWriteProxy {
|
||||
data.version = 8;
|
||||
}
|
||||
|
||||
if (data.version < 9) {
|
||||
data.settings.language = "auto-detect";
|
||||
data.version = 9;
|
||||
}
|
||||
|
||||
return ExplainedResult.good();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createLogger } from "../core/logging";
|
||||
import { findNiceValue, waitNextFrame } from "../core/utils";
|
||||
import { cachebust } from "../core/cachebust";
|
||||
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
||||
import { T } from "../translations";
|
||||
import { T, autoDetectLanguageId, updateApplicationLanguage } from "../translations";
|
||||
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||
import { CHANGELOG } from "../changelog";
|
||||
import { globalConfig } from "../core/config";
|
||||
@@ -143,6 +143,19 @@ export class PreloadState extends GameState {
|
||||
}
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Initializing language"))
|
||||
.then(() => {
|
||||
if (this.app.settings.getLanguage() === "auto-detect") {
|
||||
const language = autoDetectLanguageId();
|
||||
logger.log("Setting language to", language);
|
||||
return this.app.settings.updateLanguage(language);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
const language = this.app.settings.getLanguage();
|
||||
updateApplicationLanguage(language);
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Initializing sounds"))
|
||||
.then(() => {
|
||||
// Notice: We don't await the sounds loading itself
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { globalConfig } from "./core/config";
|
||||
import { createLogger } from "./core/logging";
|
||||
import { LANGUAGES } from "./languages";
|
||||
|
||||
const logger = createLogger("translations");
|
||||
|
||||
// @ts-ignore
|
||||
const baseTranslations = require("./built-temp/base-en.json");
|
||||
|
||||
export const T = baseTranslations;
|
||||
export let T = baseTranslations;
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.testTranslations) {
|
||||
// Replaces all translations by fake translations to see whats translated and what not
|
||||
@@ -19,3 +23,112 @@ if (G_IS_DEV && globalConfig.debug.testTranslations) {
|
||||
};
|
||||
mapTranslations(T);
|
||||
}
|
||||
|
||||
export function applyLanguage(languageCode) {
|
||||
logger.log("Applying language:", languageCode);
|
||||
const data = LANGUAGES[languageCode];
|
||||
if (!data) {
|
||||
logger.error("Language not found:", languageCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Language key is something like de-DE or en or en-US
|
||||
function mapLanguageCodeToId(languageKey) {
|
||||
const key = languageKey.toLowerCase();
|
||||
const shortKey = key.split("-")[0];
|
||||
|
||||
// Try to match by key or short key
|
||||
for (const id in LANGUAGES) {
|
||||
const data = LANGUAGES[id];
|
||||
const code = data.code.toLowerCase();
|
||||
if (code === key) {
|
||||
console.log("-> Match", languageKey, "->", id);
|
||||
return id;
|
||||
}
|
||||
if (code === shortKey) {
|
||||
console.log("-> Match by short key", languageKey, "->", id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
// If none found, try to find a better alternative by using the base language at least
|
||||
for (const id in LANGUAGES) {
|
||||
const data = LANGUAGES[id];
|
||||
const code = data.code.toLowerCase();
|
||||
const shortCode = code.split("-")[0];
|
||||
|
||||
if (shortCode === key) {
|
||||
console.log("-> Desperate Match", languageKey, "->", id);
|
||||
return id;
|
||||
}
|
||||
if (shortCode === shortKey) {
|
||||
console.log("-> Desperate Match by short key", languageKey, "->", id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to auto-detect a language
|
||||
* @returns {string}
|
||||
*/
|
||||
export function autoDetectLanguageId() {
|
||||
let languages = [];
|
||||
if (navigator.languages) {
|
||||
languages = navigator.languages.slice();
|
||||
} else if (navigator.language) {
|
||||
languages = [navigator.language];
|
||||
} else {
|
||||
logger.warn("Navigator has no languages prop");
|
||||
}
|
||||
languages = ["de-De"];
|
||||
|
||||
for (let i = 0; i < languages.length; ++i) {
|
||||
logger.log("Trying to find language target for", languages[i]);
|
||||
const trans = mapLanguageCodeToId(languages[i]);
|
||||
if (trans) {
|
||||
return trans;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function matchDataRecursive(dest, src) {
|
||||
if (typeof dest !== "object" || typeof src !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key in dest) {
|
||||
if (src[key]) {
|
||||
// console.log("copy", key);
|
||||
const data = dest[key];
|
||||
if (typeof data === "object") {
|
||||
matchDataRecursive(dest[key], src[key]);
|
||||
} else if (typeof data === "string" || typeof data === "number") {
|
||||
// console.log("match string", key);
|
||||
dest[key] = src[key];
|
||||
} else {
|
||||
logger.log("Unknown type:", typeof data, "in key", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function updateApplicationLanguage(id) {
|
||||
logger.log("Setting application language:", id);
|
||||
|
||||
const data = LANGUAGES[id];
|
||||
|
||||
if (!data) {
|
||||
logger.error("Unknown language:", id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.data) {
|
||||
logger.log("Applying translations ...");
|
||||
matchDataRecursive(T, data.data);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user