@@ -38,6 +38,7 @@ export default {
},
data() {
return {
+ appState: this.$bluemap.appState,
controls: this.$bluemap.mapViewer.controlsManager.data,
}
},
diff --git a/src/components/Menu/MarkerSet.vue b/src/components/Menu/MarkerSet.vue
index 87f35e1..1439012 100644
--- a/src/components/Menu/MarkerSet.vue
+++ b/src/components/Menu/MarkerSet.vue
@@ -2,17 +2,17 @@
-
{{ markerSet.label }}
+
{{ label }}
{{ markerSet.markers.length }}
- {{ markerSet.markers.length !== 1 ? "markers" : "marker" }}
+ {{ $tc('markers.marker', markerSet.markers.length) }}
{{ filteredMarkerSets.length }}
- {{ filteredMarkerSets.length !== 1 ? "marker-sets" : "marker-set" }}
+ {{ $tc('markers.markerSet', filteredMarkerSets.length) }}
@@ -41,6 +41,10 @@ export default {
return this.markerSet.markerSets.filter(markerSet => {
return (markerSet.id !== "bm-popup-set");
});
+ },
+ label() {
+ if (this.markerSet.id === "bm-players") return this.$t("players.title");
+ return this.markerSet.label;
}
},
methods: {
diff --git a/src/components/Menu/SettingsMenu.vue b/src/components/Menu/SettingsMenu.vue
index ac2f27d..46cfc89 100644
--- a/src/components/Menu/SettingsMenu.vue
+++ b/src/components/Menu/SettingsMenu.vue
@@ -1,16 +1,16 @@
- {{$t('controls.perspective.title')}}
- {{$t('controls.flatView.title')}}
- {{$t('controls.freeFlight.title')}}
+ {{$t('controls.perspective.button')}}
+ {{$t('controls.flatView.button')}}
+ {{$t('controls.freeFlight.button')}}
Sunlight
+ @update="mapViewer.uniforms.sunlightStrength.value = $event">{{$t('lighting.sunlight')}}
Ambient-Light
+ @update="mapViewer.uniforms.ambientLight.value = $event">{{$t('lighting.ambientLight')}}
@@ -40,9 +40,16 @@
>{{theme.name}}
- {{ $t("debug.title") }}
+
+ {{lang.name}}
+
- {{ $t("resetAllSettings.title") }}
+ {{ $t("debug.button") }}
+
+ {{ $t("resetAllSettings.button") }}
@@ -54,15 +61,15 @@ import SwitchButton from "@/components/Menu/SwitchButton";
import i18n from "../../i18n";
const themes = [
- {name: i18n.t("theme.default"), value: null},
- {name: i18n.t("theme.dark"), value: 'dark'},
- {name: i18n.t("theme.light"), value: 'light'},
+ {get name(){ return i18n.t("theme.default")}, value: null},
+ {get name(){ return i18n.t("theme.dark")}, value: 'dark'},
+ {get name(){ return i18n.t("theme.light")}, value: 'light'},
];
const qualityStages = [
- {name: i18n.t("resolution.high"), value: 2},
- {name: i18n.t("resolution.normal"), value: 1},
- {name: i18n.t("resolution.low"), value: 0.5},
+ {get name(){ return i18n.t("resolution.high")}, value: 2},
+ {get name(){ return i18n.t("resolution.normal")}, value: 1},
+ {get name(){ return i18n.t("resolution.low")}, value: 0.5},
];
export default {
diff --git a/src/i18n/en.js b/src/i18n/en.js
deleted file mode 100644
index b28385e..0000000
--- a/src/i18n/en.js
+++ /dev/null
@@ -1,85 +0,0 @@
-export default {
- menu: {
- title: "Menu",
- ctrlBar: "Menu"
- },
- maps: {
- title: "Maps",
- ctrlBar: "Map-List"
- },
- markers: {
- title: "Markers",
- ctrlBar: "Marker-List"
- },
- settings: {
- title: "settings"
- },
- goFullscreen: {
- title: "Go Fullscreen"
- },
- resetCamera: {
- title: "Reset Camera",
- ctrlBar: "Reset Camera & Position"
- },
- updateMap: {
- title: "Update Map",
- description: "Clear Tile Cache"
- },
- lighting: {
- title: "Lighting",
- dayNightSwitch: {
- ctrlBar: "Day/Night"
- },
- sunlight: "Sunlight",
- ambientLight: "Ambient-Light"
- },
- resolution: {
- title: "Resolution",
- high: "High (SSAA, x2)",
- normal: "Normal (Native, x1)",
- low: "Low (Upscaling, x0.5)"
- },
- freeFlightControls: {
- title: "Free-Flight Controls",
- mouseSensitivity: "Mouse-Sensitivity",
- invertMouseY: "Invert Mouse Y"
- },
- renderDistance: {
- title: "Render Distance",
- hiresLayer: "Hires layer",
- lowersLayer: "Lowres layer"
- },
- theme: {
- title: "Theme",
- default: "Default (System/Browser)",
- dark: "Dark",
- light: "Light"
- },
- debug: {
- title: "Debug"
- },
- resetAllSettings: {
- title: "Reset All Settings"
- },
- players: {
- title: "Players",
- ctrlBar: "Player-List"
- },
- compass: {
- ctrlBar: "Compass / Face North"
- },
- controls: {
- perspective: {
- title: "Perspective",
- ctrlBar: "Perspective-View"
- },
- flatView: {
- title: "Flat",
- ctrlBar: "Orthographic/Flat-View",
- },
- freeFlight: {
- title: "Free-Flight",
- ctrlBar: "Free-Flight/Spectator Mode"
- }
- }
-};
diff --git a/src/i18n/fallback.js b/src/i18n/fallback.js
new file mode 100644
index 0000000..35f24b4
--- /dev/null
+++ b/src/i18n/fallback.js
@@ -0,0 +1,143 @@
+export default {
+ pageTitle: "BlueMap - {map}",
+ menu: {
+ title: "Menu",
+ tooltip: "Menu"
+ },
+ maps: {
+ title: "Maps",
+ button: "Maps",
+ tooltip: "Map-List"
+ },
+ markers: {
+ title: "Markers",
+ button: "Markers",
+ tooltip: "Marker-List",
+ marker: "marker | markers",
+ markerSet: "marker-set | marker-sets"
+ },
+ settings: {
+ title: "Settings",
+ button: "Settings"
+ },
+ goFullscreen: {
+ button: "Go Fullscreen"
+ },
+ resetCamera: {
+ button: "Reset Camera",
+ tooltip: "Reset Camera & Position"
+ },
+ updateMap: {
+ button: "Update Map",
+ tooltip: "Clear Tile Cache"
+ },
+ lighting: {
+ title: "Lighting",
+ dayNightSwitch: {
+ tooltip: "Day/Night"
+ },
+ sunlight: "Sunlight",
+ ambientLight: "Ambient-Light"
+ },
+ resolution: {
+ title: "Resolution",
+ high: "High (SSAA, x2)",
+ normal: "Normal (Native, x1)",
+ low: "Low (Upscaling, x0.5)"
+ },
+ freeFlightControls: {
+ title: "Free-Flight Controls",
+ mouseSensitivity: "Mouse-Sensitivity",
+ invertMouseY: "Invert Mouse Y"
+ },
+ renderDistance: {
+ title: "Render Distance",
+ hiresLayer: "Hires layer",
+ lowersLayer: "Lowres layer"
+ },
+ theme: {
+ title: "Theme",
+ default: "Default (System/Browser)",
+ dark: "Dark",
+ light: "Light"
+ },
+ debug: {
+ button: "Debug"
+ },
+ resetAllSettings: {
+ button: "Reset All Settings"
+ },
+ players: {
+ title: "Players",
+ tooltip: "Player-List"
+ },
+ compass: {
+ tooltip: "Compass / Face North"
+ },
+ controls: {
+ perspective: {
+ button: "Perspective",
+ tooltip: "Perspective-View"
+ },
+ flatView: {
+ button: "Flat",
+ tooltip: "Orthographic / Flat-View",
+ },
+ freeFlight: {
+ button: "Free-Flight",
+ tooltip: "Free-Flight / Spectator Mode"
+ }
+ },
+ language: {
+ title: "Language",
+ },
+ blockTooltip: {
+ block: "Block",
+ position: "Position",
+ chunk: "Chunk",
+ region: {
+ region: "Region",
+ file: "File"
+ },
+ light: {
+ light: "Light",
+ sun: "Sun",
+ block: "Block",
+ }
+ },
+ info: {
+ title: "Info",
+ button: "Info",
+ content: `
+
+
+
Mouse-Controls:
+
+ move | left-click + drag |
+ zoom | mousewheel (scroll) |
+ rotate / tilt | right-click + drag |
+
+
+
+
Keyboard-Controls:
+
+ move | wasd / arrow-keys |
+ zoom | Numpad: +/- or Ins/Home |
+ rotate / tilt | Left-Alt + wasd / arrow-keys or Delete/End/Page Up/Page Down |
+
+
+
+
Touch-Controls:
+
+ move | touch + drag |
+ zoom | touch with two fingers + pinch |
+ rotate / tilt | touch with two fingers + rotate / move up/down |
+
+
+
+
+ This map has been generated with ♥ using BlueMap.
+
+ `
+ }
+};
diff --git a/src/i18n/index.js b/src/i18n/index.js
index f855c2d..13b640d 100644
--- a/src/i18n/index.js
+++ b/src/i18n/index.js
@@ -1,12 +1,36 @@
import VueI18n from 'vue-i18n';
-import en from './en';
import Vue from 'vue';
+import fallback from './fallback';
+
+VueI18n.prototype.setLanguage = async function(lang) {
+ if (!this.messages[lang]){
+ try {
+ let messages = {};
+ messages = (await import(/* webpackIgnore: true */ `../lang/${lang}.js`)).default;
+
+ this.setLocaleMessage(lang, messages);
+
+ this.locale = lang;
+ document.querySelector('html').setAttribute('lang', lang);
+ } catch (e) {
+ console.error(`Failed to load language '${lang}'!`, e);
+ }
+ }
+}
+
+VueI18n.prototype.loadLanguageSettings = async function() {
+ let settings = (await import(/* webpackIgnore: true */ "../lang/settings.js")).default;
+ this.languages = settings.languages;
+ this.setLanguage(settings.default);
+};
Vue.use(VueI18n);
const i18n = new VueI18n({
- locale: 'en',
- messages: { en }
+ locale: 'fallback',
+ fallbackLocale: 'fallback',
+ silentFallbackWarn: true,
+ messages: { fallback }
});
export default i18n;
diff --git a/src/js/BlueMapApp.js b/src/js/BlueMapApp.js
index 4d9b157..8d67822 100644
--- a/src/js/BlueMapApp.js
+++ b/src/js/BlueMapApp.js
@@ -35,6 +35,7 @@ import {MainMenu} from "@/js/MainMenu";
import {PopupMarker} from "@/js/PopupMarker";
import {MarkerSet} from "bluemap/src/markers/MarkerSet";
import {getCookie, round, setCookie} from "@/js/Utils";
+import i18n from "../i18n";
export class BlueMapApp {
@@ -129,15 +130,15 @@ export class BlueMapApp {
this.events.addEventListener("bluemapCameraMoved", this.cameraMoved);
this.events.addEventListener("bluemapMapInteraction", this.mapInteraction);
- // load user settings
- this.loadUserSettings();
-
- // save user settings
- this.saveUserSettings();
-
// start app update loop
if(this.updateLoop) clearTimeout(this.updateLoop);
this.updateLoop = setTimeout(this.update, 1000);
+
+ // load user settings
+ await this.loadUserSettings();
+
+ // save user settings
+ this.saveUserSettings();
}
update = async () => {
@@ -192,6 +193,8 @@ export class BlueMapApp {
this.resetCamera();
}
}
+
+ this.updatePageAddress();
});
}
@@ -210,6 +213,7 @@ export class BlueMapApp {
controls.controls = this.mapControls;
this.appState.controls.state = "perspective";
+ this.updatePageAddress();
}
/**
@@ -348,7 +352,10 @@ export class BlueMapApp {
cm.tilt = MathUtils.lerp(startTilt, 0, ep);
}, transition, finished => {
this.mapControls.reset();
- if (finished) cm.controls = this.mapControls;
+ if (finished){
+ cm.controls = this.mapControls;
+ this.updatePageAddress();
+ }
});
this.appState.controls.state = "perspective";
@@ -378,7 +385,10 @@ export class BlueMapApp {
cm.tilt = MathUtils.lerp(startTilt, 0, ep);
}, transition, finished => {
this.mapControls.reset();
- if (finished) cm.controls = this.mapControls;
+ if (finished){
+ cm.controls = this.mapControls;
+ this.updatePageAddress();
+ }
});
this.appState.controls.state = "flat";
@@ -410,7 +420,10 @@ export class BlueMapApp {
cm.ortho = MathUtils.lerp(startOrtho, 0, Math.min(p * 2, 1));
cm.tilt = MathUtils.lerp(startTilt, 0, ep);
}, transition, finished => {
- if (finished) cm.controls = this.freeFlightControls;
+ if (finished){
+ cm.controls = this.freeFlightControls;
+ this.updatePageAddress();
+ }
});
this.appState.controls.state = "free";
@@ -460,7 +473,7 @@ export class BlueMapApp {
location.reload();
}
- loadUserSettings(){
+ async loadUserSettings(){
if (!this.settings.useCookies) return;
if (this.loadUserSetting("resetSettings", false)) {
@@ -479,6 +492,7 @@ export class BlueMapApp {
this.appState.controls.invertMouse = this.loadUserSetting("invertMouse", this.appState.controls.invertMouse);
this.updateControlsSettings();
this.setTheme(this.loadUserSetting("theme", this.appState.theme));
+ await i18n.setLanguage(this.loadUserSetting("lang", i18n.locale));
this.setDebug(this.loadUserSetting("debug", this.appState.debug));
alert(this.events, "Settings loaded!", "info");
@@ -496,6 +510,7 @@ export class BlueMapApp {
this.saveUserSetting("mouseSensitivity", this.appState.controls.mouseSensitivity);
this.saveUserSetting("invertMouse", this.appState.controls.invertMouse);
this.saveUserSetting("theme", this.appState.theme);
+ this.saveUserSetting("lang", i18n.locale);
this.saveUserSetting("debug", this.appState.debug);
alert(this.events, "Settings saved!", "info");
@@ -539,6 +554,10 @@ export class BlueMapApp {
}
history.replaceState(undefined, undefined, hash);
+
+ document.title = i18n.t("pageTitle", {
+ map: this.mapViewer.map ? this.mapViewer.map.data.name : "?"
+ });
}
loadPageAddress = async () => {
diff --git a/src/js/MainMenu.js b/src/js/MainMenu.js
index 60f116c..1f6e871 100644
--- a/src/js/MainMenu.js
+++ b/src/js/MainMenu.js
@@ -41,17 +41,26 @@ export class MainMenu {
return this.pageStack[this.pageStack.length - 1];
}
- openPage(id = "root", title = i18n.t("menu.title"), data = {}) {
+ openPage(id = "root", title = () => i18n.t("menu.title"), data = {}) {
if (!this.isOpen){
this.pageStack.splice(0, this.pageStack.length);
this.isOpen = true;
}
- this.pageStack.push({
- id: id,
- title: title,
- ...data
- });
+ if (typeof title === "function"){
+ this.pageStack.push({
+ id: id,
+ get title(){ return title() },
+ ...data
+ });
+ } else {
+ this.pageStack.push({
+ id: id,
+ title: title,
+ ...data
+ });
+ }
+
}
closePage() {
diff --git a/src/js/PopupMarker.js b/src/js/PopupMarker.js
index 9351908..051056c 100644
--- a/src/js/PopupMarker.js
+++ b/src/js/PopupMarker.js
@@ -26,6 +26,7 @@ import {Marker} from "bluemap/src/markers/Marker";
import {CSS2DObject} from "bluemap/src/util/CSS2DRenderer";
import {animate, htmlToElement} from "bluemap/src/util/Utils";
import {BoxGeometry, MeshBasicMaterial, Mesh, Vector2} from "three";
+import i18n from "../i18n";
export class PopupMarker extends Marker {
@@ -90,7 +91,7 @@ export class PopupMarker extends Marker {
if (isHires) {
this.element.innerHTML = `
-
Block:
+
${i18n.t("blockTooltip.block")}:
x: ${this.position.x}
y: ${this.position.y}
@@ -101,7 +102,7 @@ export class PopupMarker extends Marker {
} else {
this.element.innerHTML = `
-
Position:
+
${i18n.t("blockTooltip.position")}:
x: ${this.position.x}
z: ${this.position.z}
@@ -118,7 +119,7 @@ export class PopupMarker extends Marker {
this.element.innerHTML += `
-
Chunk:
+
${i18n.t("blockTooltip.chunk")}:
x: ${chunkCoords.x}
y: ${chunkCoords.y}
@@ -127,13 +128,13 @@ export class PopupMarker extends Marker {
-
Region:
+
${i18n.t("blockTooltip.region.region")}:
x: ${regionCoords.x}
z: ${regionCoords.y}
-
File: ${regionFile}
+
${i18n.t("blockTooltip.region.file")}: ${regionFile}
`;
@@ -149,10 +150,10 @@ export class PopupMarker extends Marker {
this.element.innerHTML += `
-
Light:
+
${i18n.t("blockTooltip.light.light")}:
-
Sun: ${sunlight}
-
Block: ${blocklight}
+
${i18n.t("blockTooltip.light.sun")}: ${sunlight}
+
${i18n.t("blockTooltip.light.block")}: ${blocklight}
`;
diff --git a/src/main.js b/src/main.js
index 487c84a..b4bcdd7 100644
--- a/src/main.js
+++ b/src/main.js
@@ -35,22 +35,23 @@ String.prototype.includesCI = function (val) {
// bluemap app
const bluemap = new BlueMapApp(document.getElementById("map-container"));
+window.bluemap = bluemap;
// init vue
Vue.config.productionTip = false;
Object.defineProperty(Vue.prototype, '$bluemap', {
- get () { return bluemap }
+ get() { return bluemap; }
});
-let vue = new Vue({
+const vue = new Vue({
i18n,
render: h => h(App)
}).$mount('#app');
-// make bluemap accessible in console
-window.bluemap = bluemap;
+// load languages
+i18n.loadLanguageSettings();
// load bluemap next tick (to let the assets load first)
vue.$nextTick(() => {
bluemap.load().catch(error => console.error(error));
-});
\ No newline at end of file
+});
diff --git a/src/scss/global.scss b/src/scss/global.scss
index cf341dd..73495c6 100644
--- a/src/scss/global.scss
+++ b/src/scss/global.scss
@@ -51,6 +51,62 @@ body {
padding: 0;
}
+h1, h2, h3, h4, h5, h6 {
+ font-weight: inherit;
+ font-size: inherit;
+ text-align: left;
+ margin: 1em 0 0.5em 0;
+ padding: 0;
+}
+
+h1, h2 {
+ position: relative;
+
+ font-size: 1.2em;
+
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 0.5em;
+ padding-bottom: 0.5em;
+
+ width: calc(100% - 0.5em);
+ overflow: hidden;
+
+ &:after {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ content: '';
+
+ width: 100%;
+ height: 1px;
+ background-color: var(--theme-bg-light);
+ }
+}
+
+h1 {
+ width: 100%;
+ text-align: center;
+ padding-left: 0;
+}
+
+p {
+ margin: 0.5em;
+ padding: 0;
+}
+
+a {
+ color: inherit;
+ text-decoration: underline;
+}
+
+kbd {
+ background-color: var(--theme-bg-light);
+ border-radius: 0.2em;
+ margin: 0;
+ padding: 0 0.2em;
+}
+
// normalize input fields
input {
display: inline-block;