Make LiveAtlas messages translatable

This commit is contained in:
James Lyne 2021-05-20 16:39:41 +01:00
parent 131e1dd685
commit fcf124c9ef
16 changed files with 177 additions and 95 deletions

View File

@ -28,66 +28,84 @@
window.liveAtlasConfig = {
// Server URLS can be defined here instead of using the standalone/config.js file.
// Multiple servers are supported, see https://github.com/JLyne/LiveAtlas/wiki/Configuring-Multiple-Servers.
servers: {
creative: {
label: 'Creative',
dynmap: {
configuration: 'http://dynmap.local/creative/MySQL_configuration.php',
update: 'http://dynmap.local/creative/MySQL_update.php?world={world}&ts={timestamp}',
sendmessage: 'http://dynmap.local/creative/MySQL_sendmessage.php',
login: 'http://dynmap.local/creative/MySQL_login.php',
register: 'http://dynmap.local/creative/MySQL_register.php',
tiles: 'http://dynmap.local/creative/MySQL_tiles.php?tile=',
markers: 'http://dynmap.local/creative/MySQL_markers.php?marker='
}
},
survival: {
label: 'Survival',
dynmap: {
configuration: 'http://dynmap.local/survival/MySQL_configuration.php',
update: 'http://dynmap.local/survival/MySQL_update.php?world={world}&ts={timestamp}',
sendmessage: 'http://dynmap.local/survival/MySQL_sendmessage.php',
login: 'http://dynmap.local/survival/MySQL_login.php',
register: 'http://dynmap.local/survival/MySQL_register.php',
tiles: 'http://dynmap.local/survival/MySQL_tiles.php?tile=',
markers: 'http://dynmap.local/survival/MySQL_markers.php?marker='
}
},
build: {
label: 'Build',
dynmap: {
configuration: 'http://dynmap.local/build/MySQL_configuration.php',
update: 'http://dynmap.local/build/MySQL_update.php?world={world}&ts={timestamp}',
sendmessage: 'http://dynmap.local/build/MySQL_sendmessage.php',
login: 'http://dynmap.local/build/MySQL_login.php',
register: 'http://dynmap.local/build/MySQL_register.php',
tiles: 'http://dynmap.local/build/MySQL_tiles.php?tile=',
markers: 'http://dynmap.local/build/MySQL_markers.php?marker='
}
},
test: {
label: 'Local Test',
url: '/map/test',
dynmap: {
configuration: 'http://dynmap.local:8123/up/configuration',
update: 'http://dynmap.local:8123/up/world/{world}/{timestamp}',
sendmessage: 'http://dynmap.local:8123/up/sendmessage',
login: 'http://dynmap.local:8123/up/login',
register: 'http://dynmap.local:8123/up/register',
tiles: 'http://dynmap.local:8123/tiles/',
markers: 'http://dynmap.local:8123/tiles/'
}
},
},
// servers: {
// creative: {
// label: 'Creative',
// dynmap: {
// configuration: 'http://dynmap.local/creative/MySQL_configuration.php',
// update: 'http://dynmap.local/creative/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/creative/MySQL_sendmessage.php',
// login: 'http://dynmap.local/creative/MySQL_login.php',
// register: 'http://dynmap.local/creative/MySQL_register.php',
// tiles: 'http://dynmap.local/creative/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/creative/MySQL_markers.php?marker='
// }
// },
// survival: {
// label: 'Survival',
// dynmap: {
// configuration: 'http://dynmap.local/survival/MySQL_configuration.php',
// update: 'http://dynmap.local/survival/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/survival/MySQL_sendmessage.php',
// login: 'http://dynmap.local/survival/MySQL_login.php',
// register: 'http://dynmap.local/survival/MySQL_register.php',
// tiles: 'http://dynmap.local/survival/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/survival/MySQL_markers.php?marker='
// }
// },
// build: {
// label: 'Build',
// dynmap: {
// configuration: 'http://dynmap.local/build/MySQL_configuration.php',
// update: 'http://dynmap.local/build/MySQL_update.php?world={world}&ts={timestamp}',
// sendmessage: 'http://dynmap.local/build/MySQL_sendmessage.php',
// login: 'http://dynmap.local/build/MySQL_login.php',
// register: 'http://dynmap.local/build/MySQL_register.php',
// tiles: 'http://dynmap.local/build/MySQL_tiles.php?tile=',
// markers: 'http://dynmap.local/build/MySQL_markers.php?marker='
// }
// },
// test: {
// label: 'Local Test',
// url: '/map/test',
// dynmap: {
// configuration: 'http://dynmap.local:8123/up/configuration',
// update: 'http://dynmap.local:8123/up/world/{world}/{timestamp}',
// sendmessage: 'http://dynmap.local:8123/up/sendmessage',
// login: 'http://dynmap.local:8123/up/login',
// register: 'http://dynmap.local:8123/up/register',
// tiles: 'http://dynmap.local:8123/tiles/',
// markers: 'http://dynmap.local:8123/tiles/'
// }
// },
// },
// These messages are used throughout LiveAtlas and can be translated here
// If a message you want to translate isn't here, it is likely controlled by dynmap itself
// see https://github.com/webbukkit/dynmap/wiki/Configuration.txt
messages: {
headingServers: 'Servers',
chatNoMessages: 'No chat messages yet...',
chatTitle: 'Chat',
chatLogin: 'Please {{link}} to send chat messages',
chatLoginLink: 'login',
chatSend: 'Send',
chatPlaceholder: 'Type your chat message here...',
chatErrorUnknown: 'Unexpected error while sending chat message',
chatErrorDisabled: 'Chat is not enabled',
serversHeading: 'Servers',
worldsSkeleton: 'No maps have been configured',
playersSkeleton: 'No players are currently online',
playersTitle: 'Click to center on player\nDouble-click to follow player',
playersTitleHidden: 'This player is currently hidden from the map\nDouble-click to follow player when they become visible',
playersTitleOtherWorld: 'This player is in another world.\nClick to center on player\nDouble-click to follow player',
followingHeading: 'Following',
followingUnfollow: 'Unfollow',
followingTitleUnfollow: 'Stop following this player',
followingHidden: 'Currently hidden',
linkTitle: 'Copy link to current location',
loadingTitle: 'Loading...',
locationRegion: 'Region',
locationChunk: 'Chunk',
}
};
</script>

View File

@ -66,18 +66,33 @@ function buildMessagesConfig(response: any): LiveAtlasMessageConfig {
chatPlayerQuit: response.quitmessage || '',
chatAnonymousJoin: response['msg-hiddennamejoin'] || '',
chatAnonymousQuit: response['msg-hiddennamequit'] || '',
chatTitle: liveAtlasMessages.chatTitle || '',
chatLogin: liveAtlasMessages.chatLogin || '',
chatLoginLink: liveAtlasMessages.chatLoginLink || '',
chatNoMessages: liveAtlasMessages.chatNoMessages || '',
chatSend: liveAtlasMessages.chatSend || '',
chatPlaceholder: liveAtlasMessages.chatPlaceholder || '',
chatErrorNotAllowed: response['msg-chatnotallowed'] || '',
chatErrorRequiresLogin: response['msg-chatrequireslogin'] || '',
chatErrorCooldown: response.spammessage || '',
chatErrorDisabled: liveAtlasMessages.chatErrorDisabled || '',
chatErrorUnknown: liveAtlasMessages.chatErrorUnknown || '',
headingWorlds: response['msg-maptypes'] || '',
headingPlayers: response['msg-players'] || '',
headingServers: liveAtlasMessages.headingServers || '',
serversHeading: liveAtlasMessages.serversHeading || '',
worldsHeading: response['msg-maptypes'] || '',
worldsSkeleton: liveAtlasMessages.worldsSkeleton || '',
playersHeading: response['msg-players'] || '',
playersSkeleton: liveAtlasMessages.playersSkeleton || '',
playersTitle: liveAtlasMessages.playersTitle || '',
playersTitleHidden: liveAtlasMessages.playersTitleHidden || '',
playersTitleOtherWorld: liveAtlasMessages.playersTitleOtherWorld || '',
followingHeading: liveAtlasMessages.followingHeading || '',
followingHidden: liveAtlasMessages.followingHidden || '',
followingUnfollow: liveAtlasMessages.followingUnfollow || '',
followingTitleUnfollow: liveAtlasMessages.followingTitleUnfollow || '',
linkTitle: liveAtlasMessages.linkTitle || '',
loadingTitle: liveAtlasMessages.loadingTitle || '',
locationRegion: liveAtlasMessages.locationRegion || '',
locationChunk: liveAtlasMessages.locationChunk || '',
}
}

View File

@ -23,7 +23,7 @@
<form v-if="sendingEnabled" class="chat__form" @submit.prevent="sendMessage">
<div v-if="sendingError" class="chat__error">{{ sendingError }}</div>
<input ref="chatInput" v-model="enteredMessage" class="chat__input" type="text" :maxlength="maxMessageLength"
placeholder="Type your chat message here..." :disabled="sendingMessage">
:placeholder="messagePlaceholder" :disabled="sendingMessage">
<button class="chat__send" :disabled="!enteredMessage || sendingMessage">{{ messageSend }}</button>
</form>
<div v-if="loginRequired" class="chat__login" v-html="messageLogin"></div>
@ -67,9 +67,10 @@
}),
messageSend = computed(() => store.state.messages.chatSend),
messagePlaceholder = computed(() => store.state.messages.chatPlaceholder),
messageNoMessages = computed(() => store.state.messages.chatNoMessages),
messageLogin = computed(() => store.state.messages.chatLogin.replace(
'{{link}}', store.state.messages.chatLoginLink)),
'{{link}}', `<a href="login.html">${store.state.messages.chatLoginLink}</a>`)),
sendMessage = async () => {
const message = enteredMessage.value.trim().substring(0, maxMessageLength.value);
@ -117,6 +118,7 @@
messageLogin,
messageSend,
messageNoMessages,
messagePlaceholder,
}
}
})

View File

@ -18,17 +18,13 @@
<aside class="sidebar">
<header class="sidebar__buttons">
<button v-if="mapCount > 1" :class="{'button--maps': true, 'active':currentlyVisible.has('maps')}"
@click="toggleElement('maps')" title="Map list" aria-label="Map list">
@click="toggleElement('maps')" :title="messageWorlds" :aria-label="messageWorlds">
<SvgIcon name="maps"></SvgIcon>
</button>
<button :class="{'button--players': true, 'active': currentlyVisible.has('players')}"
@click="toggleElement('players')" title="Player list" aria-label="Player list">
@click="toggleElement('players')" :title="messagePlayers" :aria-label="messagePlayers">
<SvgIcon name="players"></SvgIcon>
</button>
<!-- <button :class="{'button&#45;&#45;settings': true, 'active': currentlyVisible.has('settings'))}"-->
<!-- @click="toggleElement('settings')" title="Settings" aria-label="Settings">-->
<!-- <SvgIcon name="settings"></SvgIcon>-->
<!-- </button>-->
</header>
<ServerList v-if="serverCount > 1" v-show="currentlyVisible.has('maps')"></ServerList>
<WorldList v-if="mapCount > 1" v-show="currentlyVisible.has('maps')"></WorldList>
@ -68,6 +64,9 @@ export default defineComponent({
serverCount = computed(() => store.state.servers.size),
following = computed(() => store.state.followTarget),
messageWorlds = computed(() => store.state.messages.worldsHeading),
messagePlayers = computed(() => store.state.messages.playersHeading),
toggleElement = (element: DynmapUIElement) => {
store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, element);
},
@ -86,6 +85,8 @@ export default defineComponent({
toggleElement,
followActive,
following,
messageWorlds,
messagePlayers,
}
}
})

View File

@ -61,12 +61,12 @@ export default defineComponent({
this.leaflet.getLayerManager().addLayer(
this.layerGroup,
true,
useStore().state.messages.headingPlayers,
useStore().state.messages.playersHeading,
this.componentSettings!.layerPriority);
} else {
this.leaflet.getLayerManager().addHiddenLayer(
this.layerGroup,
useStore().state.messages.headingPlayers,
useStore().state.messages.playersHeading,
this.componentSettings!.layerPriority);
}
},

View File

@ -16,17 +16,17 @@
<template>
<section class="sidebar__section following">
<h2>Following</h2>
<h2>{{ heading }}</h2>
<div :class="{'following__target': true, 'following__target--hidden': target.hidden}">
<img width="32" height="32" class="target__icon" :src="image" alt="" />
<span class="target__info">
<span class="target__name" v-html="target.name"></span>
<span class="target__status" v-show="target.hidden">Currently hidden</span>
<span class="target__status" v-show="target.hidden">{{ messageHidden }}</span>
</span>
<button class="target__unfollow" type="button" :title="`Stop following this player`"
<button class="target__unfollow" type="button" :title="messageUnfollowTitle"
@click.prevent="unfollow"
@keydown="onKeydown">Unfollow</button>
@keydown="onKeydown">{{ messageUnfollow }}</button>
</div>
</section>
</template>
@ -35,7 +35,7 @@
import {DynmapPlayer} from "@/dynmap";
import {useStore} from "@/store";
import {MutationTypes} from "@/store/mutation-types";
import {defineComponent, onMounted, ref, watch} from "@vue/runtime-core";
import {computed, defineComponent, onMounted, ref, watch} from "@vue/runtime-core";
import {getMinecraftHead} from '@/util';
import defaultImage from '@/assets/images/player_face.png';
@ -52,6 +52,11 @@ export default defineComponent({
image = ref(defaultImage),
account = ref(props.target.account),
heading = computed(() => store.state.messages.followingHeading),
messageUnfollow = computed(() => store.state.messages.followingUnfollow),
messageUnfollowTitle = computed(() => store.state.messages.followingTitleUnfollow),
messageHidden = computed(() => store.state.messages.followingHidden),
unfollow = () => {
useStore().commit(MutationTypes.CLEAR_FOLLOW_TARGET, undefined);
},
@ -79,7 +84,11 @@ export default defineComponent({
return {
image,
onKeydown,
unfollow
unfollow,
heading,
messageUnfollow,
messageUnfollowTitle,
messageHidden,
}
},
});

View File

@ -19,9 +19,7 @@
<span class="section__heading">{{ heading }} [{{ players.size }}/{{ maxPlayers }}]</span>
<ul class="section__content">
<PlayerListItem v-for="[account, player] in players" :key="account" :player="player"></PlayerListItem>
<li v-if="!players.size" class="section__skeleton">
No players are currently online
</li>
<li v-if="!players.size" class="section__skeleton">{{ skeletonPlayers }}</li>
</ul>
</section>
</template>
@ -38,7 +36,11 @@ export default defineComponent({
computed: {
heading() {
return useStore().state.messages.headingPlayers;
return useStore().state.messages.playersHeading;
},
skeletonPlayers() {
return useStore().state.messages.playersSkeleton;
},
players() {

View File

@ -50,11 +50,11 @@ export default defineComponent({
title = computed(() => {
if(props.player.hidden) {
return 'This player is currently hidden from the map\nDouble-click to follow player when they become visible';
return store.state.messages.playersTitleHidden;
} else if(otherWorld.value) {
return 'This player is in another world.\nClick to center on player\nDouble-click to follow player';
return store.state.messages.playersTitleOtherWorld;
} else {
return 'Click to center on player\nDouble-click to follow player';
return store.state.messages.playersTitle;
}
}),

View File

@ -36,7 +36,7 @@ export default defineComponent({
computed: {
heading() {
return useStore().state.messages.headingServers;
return useStore().state.messages.serversHeading;
},
servers() {

View File

@ -19,9 +19,7 @@
<span class="section__heading">{{ heading }}</span>
<ul class="section__content">
<WorldListItem :world="world" v-for="[name, world] in worlds" :key="name"></WorldListItem>
<li v-if="!worlds.size" class="section__skeleton">
No maps have been configured
</li>
<li v-if="!worlds.size" class="section__skeleton">{{ skeletonWorlds }}</li>
</ul>
</section>
</template>
@ -39,7 +37,11 @@ export default defineComponent({
computed: {
heading() {
return useStore().state.messages.headingWorlds;
return useStore().state.messages.worldsHeading;
},
skeletonWorlds() {
return useStore().state.messages.worldsSkeleton;
},
worlds() {

21
src/index.d.ts vendored
View File

@ -35,15 +35,30 @@ interface LiveAtlasMessageConfig {
chatAnonymousJoin: string;
chatAnonymousQuit: string;
chatNoMessages: string;
chatTitle: string;
chatLogin: string;
chatLoginLink: string;
chatSend: string;
chatPlaceholder: string;
chatErrorNotAllowed: string;
chatErrorRequiresLogin: string;
chatErrorCooldown: string;
chatErrorDisabled: string;
chatErrorUnknown: string;
headingServers: string;
headingWorlds: string;
headingPlayers: string;
serversHeading: string;
worldsHeading: string;
worldsSkeleton: string;
playersSkeleton: string;
playersHeading: string;
playersTitle: string;
playersTitleHidden: string;
playersTitleOtherWorld: string;
followingHeading: string;
followingUnfollow: string;
followingTitleUnfollow: string;
followingHidden: string;
linkTitle: string;
loadingTitle: string;
locationRegion: string;
locationChunk: string;
}

View File

@ -33,7 +33,7 @@ export class ChatControl extends Control {
const chatButton = DomUtil.create('button', 'leaflet-control-chat') as HTMLButtonElement;
chatButton.type = 'button';
chatButton.title = 'Chat';
chatButton.title = useStore().state.messages.chatTitle;
chatButton.innerHTML = `
<svg class="svg-icon">
<use xlink:href="#chat" />

View File

@ -61,13 +61,13 @@ export class CoordinatesControl extends Control {
if (this.options.showRegion) {
this._regionContainer.textContent = '--------------';
this._regionContainer.dataset.label = 'Region';
this._regionContainer.dataset.label = useStore().state.messages.locationRegion;
container.appendChild(this._regionContainer);
}
if (this.options.showChunk) {
this._chunkContainer.textContent = '----, ----';
this._chunkContainer.dataset.label = 'Chunk';
this._chunkContainer.dataset.label = useStore().state.messages.locationChunk;
container.appendChild(this._chunkContainer);
}

View File

@ -36,7 +36,7 @@ export class LinkControl extends Control {
const linkButton = DomUtil.create('button', 'leaflet-control-link') as HTMLButtonElement;
linkButton.type = 'button';
linkButton.title = 'Copy link to current location';
linkButton.title = useStore().state.messages.linkTitle;
linkButton.innerHTML = `
<svg class="svg-icon" aria-hidden="true">
<use xlink:href="#link" />

View File

@ -31,6 +31,7 @@ import {
Map, TileLayer,
} from 'leaflet';
import '@/assets/icons/loading.svg';
import {useStore} from "@/store";
export interface LoadingControlOptions extends ControlOptions {
delayIndicator?: number;
@ -51,7 +52,7 @@ export class LoadingControl extends Control {
}
onAdd(map: Map) {
this._loadingIndicator.title = 'Loading...';
this._loadingIndicator.title = useStore().state.messages.loadingTitle;
this._loadingIndicator.hidden = true;
this._loadingIndicator.innerHTML = `
<svg class="svg-icon">

View File

@ -98,15 +98,32 @@ export const state: State = {
chatAnonymousJoin: '',
chatAnonymousQuit: '',
chatNoMessages: '',
chatTitle: '',
chatLogin: '',
chatLoginLink: '',
chatSend: '',
chatPlaceholder: '',
chatErrorNotAllowed: '',
chatErrorCooldown: '',
chatErrorRequiresLogin: '',
chatErrorCooldown: '',
chatErrorDisabled: '',
chatErrorUnknown: '',
headingWorlds: '',
headingPlayers: '',
headingServers: '',
serversHeading: '',
worldsHeading: '',
worldsSkeleton: '',
playersSkeleton: '',
playersHeading: '',
playersTitle: '',
playersTitleHidden: '',
playersTitleOtherWorld: '',
followingHeading: '',
followingUnfollow: '',
followingTitleUnfollow: '',
followingHidden: '',
linkTitle: '',
loadingTitle: '',
locationRegion: '',
locationChunk: '',
},
loggedIn: false,