Make LiveAtlas messages translatable

This commit is contained in:
James Lyne 2021-05-20 16:13:25 +01:00
parent 17540b6279
commit 131e1dd685
12 changed files with 124 additions and 88 deletions

View File

@ -32,37 +32,37 @@
creative: { creative: {
label: 'Creative', label: 'Creative',
dynmap: { dynmap: {
configuration: 'http://dynmap.local/standalone/creative/MySQL_configuration.php', configuration: 'http://dynmap.local/creative/MySQL_configuration.php',
update: 'http://dynmap.local/standalone/creative/MySQL_update.php?world={world}&ts={timestamp}', update: 'http://dynmap.local/creative/MySQL_update.php?world={world}&ts={timestamp}',
sendmessage: 'http://dynmap.local/standalone/creative/MySQL_sendmessage.php', sendmessage: 'http://dynmap.local/creative/MySQL_sendmessage.php',
login: 'http://dynmap.local/standalone/creative/MySQL_login.php', login: 'http://dynmap.local/creative/MySQL_login.php',
register: 'http://dynmap.local/standalone/creative/MySQL_register.php', register: 'http://dynmap.local/creative/MySQL_register.php',
tiles: 'http://dynmap.local/standalone/creative/MySQL_tiles.php?tile=', tiles: 'http://dynmap.local/creative/MySQL_tiles.php?tile=',
markers: 'http://dynmap.local/standalone/creative/MySQL_markers.php?marker=' markers: 'http://dynmap.local/creative/MySQL_markers.php?marker='
} }
}, },
survival: { survival: {
label: 'Survival', label: 'Survival',
dynmap: { dynmap: {
configuration: 'http://dynmap.local/standalone/survival/MySQL_configuration.php', configuration: 'http://dynmap.local/survival/MySQL_configuration.php',
update: 'http://dynmap.local/standalone/survival/MySQL_update.php?world={world}&ts={timestamp}', update: 'http://dynmap.local/survival/MySQL_update.php?world={world}&ts={timestamp}',
sendmessage: 'http://dynmap.local/standalone/survival/MySQL_sendmessage.php', sendmessage: 'http://dynmap.local/survival/MySQL_sendmessage.php',
login: 'http://dynmap.local/standalone/survival/MySQL_login.php', login: 'http://dynmap.local/survival/MySQL_login.php',
register: 'http://dynmap.local/standalone/survival/MySQL_register.php', register: 'http://dynmap.local/survival/MySQL_register.php',
tiles: 'http://dynmap.local/standalone/survival/MySQL_tiles.php?tile=', tiles: 'http://dynmap.local/survival/MySQL_tiles.php?tile=',
markers: 'http://dynmap.local/standalone/survival/MySQL_markers.php?marker=' markers: 'http://dynmap.local/survival/MySQL_markers.php?marker='
} }
}, },
build: { build: {
label: 'Build', label: 'Build',
dynmap: { dynmap: {
configuration: 'http://dynmap.local/standalone/build/MySQL_configuration.php', configuration: 'http://dynmap.local/build/MySQL_configuration.php',
update: 'http://dynmap.local/standalone/build/MySQL_update.php?world={world}&ts={timestamp}', update: 'http://dynmap.local/build/MySQL_update.php?world={world}&ts={timestamp}',
sendmessage: 'http://dynmap.local/standalone/build/MySQL_sendmessage.php', sendmessage: 'http://dynmap.local/build/MySQL_sendmessage.php',
login: 'http://dynmap.local/standalone/build/MySQL_login.php', login: 'http://dynmap.local/build/MySQL_login.php',
register: 'http://dynmap.local/standalone/build/MySQL_register.php', register: 'http://dynmap.local/build/MySQL_register.php',
tiles: 'http://dynmap.local/standalone/build/MySQL_tiles.php?tile=', tiles: 'http://dynmap.local/build/MySQL_tiles.php?tile=',
markers: 'http://dynmap.local/standalone/build/MySQL_markers.php?marker=' markers: 'http://dynmap.local/build/MySQL_markers.php?marker='
} }
}, },
test: { test: {
@ -78,6 +78,16 @@
markers: 'http://dynmap.local:8123/tiles/' markers: 'http://dynmap.local:8123/tiles/'
} }
}, },
},
messages: {
headingServers: 'Servers',
chatNoMessages: 'No chat messages yet...',
chatLogin: 'Please {{link}} to send chat messages',
chatLoginLink: 'login',
chatSend: 'Send',
chatErrorUnknown: 'Unexpected error while sending chat message',
chatErrorDisabled: 'Chat is not enabled',
} }
}; };
</script> </script>

View File

@ -24,7 +24,6 @@ import {
DynmapMarker, DynmapMarker,
DynmapMarkerSet, DynmapMarkerSet,
DynmapMarkerSetUpdates, DynmapMarkerSetUpdates,
DynmapMessageConfig,
DynmapPlayer, DynmapPlayer,
DynmapServerConfig, DynmapServerConfig,
DynmapTileUpdate, DynmapTileUpdate,
@ -36,6 +35,7 @@ import {
} from "@/dynmap"; } from "@/dynmap";
import {useStore} from "@/store"; import {useStore} from "@/store";
import ChatError from "@/errors/ChatError"; import ChatError from "@/errors/ChatError";
import {LiveAtlasMessageConfig} from "@/index";
const titleColours = /§[0-9a-f]/ig; const titleColours = /§[0-9a-f]/ig;
@ -58,17 +58,26 @@ function buildServerConfig(response: any): DynmapServerConfig {
}; };
} }
function buildMessagesConfig(response: any): DynmapMessageConfig { function buildMessagesConfig(response: any): LiveAtlasMessageConfig {
const liveAtlasMessages = window.liveAtlasConfig ? window.liveAtlasConfig.messages || {} : {};
return { return {
chatNotAllowed: response['msg-chatnotallowed'] || '', chatPlayerJoin: response.joinmessage || '',
chatRequiresLogin: response['msg-chatrequireslogin'] || '', chatPlayerQuit: response.quitmessage || '',
chatCooldown: response.spammessage || '', chatAnonymousJoin: response['msg-hiddennamejoin'] || '',
mapTypes: response['msg-maptypes'] || '', chatAnonymousQuit: response['msg-hiddennamequit'] || '',
players: response['msg-players'] || '', chatLogin: liveAtlasMessages.chatLogin || '',
playerJoin: response.joinmessage || '', chatLoginLink: liveAtlasMessages.chatLoginLink || '',
playerQuit: response.quitmessage || '', chatNoMessages: liveAtlasMessages.chatNoMessages || '',
anonymousJoin: response['msg-hiddennamejoin'] || '', chatSend: liveAtlasMessages.chatSend || '',
anonymousQuit: response['msg-hiddennamequit'] || '', 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 || '',
} }
} }
@ -721,7 +730,7 @@ export default {
const store = useStore(); const store = useStore();
if (!store.state.components.chatSending) { if (!store.state.components.chatSending) {
return Promise.reject(new ChatError("Chat is not enabled")); return Promise.reject(store.state.messages.chatErrorDisabled);
} }
return fetch(useStore().getters.serverConfig.dynmap.sendmessage, { return fetch(useStore().getters.serverConfig.dynmap.sendmessage, {
@ -732,7 +741,7 @@ export default {
}) })
}).then((response) => { }).then((response) => {
if (response.status === 403) { //Rate limited if (response.status === 403) { //Rate limited
throw new ChatError(store.state.messages.chatCooldown throw new ChatError(store.state.messages.chatErrorCooldown
.replace('%interval%', store.state.components.chatSending!.cooldown.toString())); .replace('%interval%', store.state.components.chatSending!.cooldown.toString()));
} }
@ -743,11 +752,11 @@ export default {
return response.json(); return response.json();
}).then(response => { }).then(response => {
if (response.error !== 'none') { if (response.error !== 'none') {
throw new ChatError(store.state.messages.chatNotAllowed); throw new ChatError(store.state.messages.chatErrorNotAllowed);
} }
}).catch(e => { }).catch(e => {
if (!(e instanceof ChatError)) { if (!(e instanceof ChatError)) {
console.error('Unexpected error while sending chat message'); console.error(store.state.messages.chatErrorUnknown);
console.trace(e); console.trace(e);
} }

View File

@ -17,18 +17,16 @@
<template> <template>
<section class="chat"> <section class="chat">
<ul class="chat__messages"> <ul class="chat__messages">
<ChatMessage v-for="message in messages" :key="message.timestamp" :message="message"></ChatMessage> <ChatMessage v-for="message in chatMessages" :key="message.timestamp" :message="message"></ChatMessage>
<li v-if="!messages.length" class="message message--skeleton">No chat messages yet...</li> <li v-if="!chatMessages.length" class="message message--skeleton">{{ messageNoMessages }}</li>
</ul> </ul>
<form v-if="sendingEnabled" class="chat__form" @submit.prevent="sendMessage"> <form v-if="sendingEnabled" class="chat__form" @submit.prevent="sendMessage">
<div v-if="sendingError" class="chat__error">{{ sendingError }}</div> <div v-if="sendingError" class="chat__error">{{ sendingError }}</div>
<input ref="chatInput" v-model="enteredMessage" class="chat__input" type="text" :maxlength="messageMaxLength" <input ref="chatInput" v-model="enteredMessage" class="chat__input" type="text" :maxlength="maxMessageLength"
placeholder="Type your chat message here..." :disabled="sendingMessage"> placeholder="Type your chat message here..." :disabled="sendingMessage">
<button class="chat__send" :disabled="!enteredMessage || sendingMessage">Send</button> <button class="chat__send" :disabled="!enteredMessage || sendingMessage">{{ messageSend }}</button>
</form> </form>
<div v-if="loginRequired" class="chat__login"> <div v-if="loginRequired" class="chat__login" v-html="messageLogin"></div>
Please <a href="login.html">login</a> to send chat messages
</div>
</section> </section>
</template> </template>
@ -53,14 +51,14 @@
&& !store.state.loggedIn; && !store.state.loggedIn;
}), }),
sendingEnabled = computed(() => store.state.components.chatSending && !loginRequired.value), sendingEnabled = computed(() => store.state.components.chatSending && !loginRequired.value),
messageMaxLength = computed(() => store.state.components.chatSending?.maxLength), maxMessageLength = computed(() => store.state.components.chatSending?.maxLength),
chatInput = ref<HTMLInputElement | null>(null), chatInput = ref<HTMLInputElement | null>(null),
enteredMessage = ref<string>(""), enteredMessage = ref<string>(""),
sendingMessage = ref<boolean>(false), sendingMessage = ref<boolean>(false),
sendingError = ref<string | null>(null), sendingError = ref<string | null>(null),
messages = computed(() => { chatMessages = computed(() => {
if(componentSettings.value!.messageHistory) { if(componentSettings.value!.messageHistory) {
return store.state.chat.messages.slice(0, componentSettings.value!.messageHistory); return store.state.chat.messages.slice(0, componentSettings.value!.messageHistory);
} else { } else {
@ -68,8 +66,13 @@
} }
}), }),
messageSend = computed(() => store.state.messages.chatSend),
messageNoMessages = computed(() => store.state.messages.chatNoMessages),
messageLogin = computed(() => store.state.messages.chatLogin.replace(
'{{link}}', store.state.messages.chatLoginLink)),
sendMessage = async () => { sendMessage = async () => {
const message = enteredMessage.value.trim().substring(0, messageMaxLength.value); const message = enteredMessage.value.trim().substring(0, maxMessageLength.value);
if(!message) { if(!message) {
return; return;
@ -86,7 +89,7 @@
if(e instanceof ChatError) { if(e instanceof ChatError) {
sendingError.value = e.message; sendingError.value = e.message;
} else { } else {
sendingError.value = `An unexpected error occurred. See console for details.`; sendingError.value = store.state.messages.chatErrorUnknown;
} }
} finally { } finally {
sendingMessage.value = false; sendingMessage.value = false;
@ -105,12 +108,15 @@
chatInput, chatInput,
enteredMessage, enteredMessage,
sendMessage, sendMessage,
messages, chatMessages,
loginRequired, loginRequired,
sendingEnabled, sendingEnabled,
sendingMessage, sendingMessage,
sendingError, sendingError,
messageMaxLength maxMessageLength,
messageLogin,
messageSend,
messageNoMessages,
} }
} }
}) })

View File

@ -47,17 +47,17 @@
return props.message.message; return props.message.message;
case 'playerjoin': case 'playerjoin':
if(props.message.playerName) { if(props.message.playerName) {
return store.state.messages.playerJoin return store.state.messages.chatPlayerJoin
.replace('%playername%', props.message.playerName); .replace('%playername%', props.message.playerName);
} else { } else {
return store.state.messages.anonymousJoin; return store.state.messages.chatAnonymousJoin;
} }
case 'playerleave': case 'playerleave':
if(props.message.playerName) { if(props.message.playerName) {
return store.state.messages.playerQuit return store.state.messages.chatPlayerQuit
.replace('%playername%', props.message.playerName); .replace('%playername%', props.message.playerName);
} else { } else {
return store.state.messages.anonymousQuit; return store.state.messages.chatAnonymousQuit;
} }
} }
}) })

View File

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

View File

@ -38,7 +38,7 @@ export default defineComponent({
computed: { computed: {
heading() { heading() {
return useStore().state.messages.players; return useStore().state.messages.headingPlayers;
}, },
players() { players() {

View File

@ -16,7 +16,7 @@
<template> <template>
<section class="sidebar__section" v-if="servers.size > 1"> <section class="sidebar__section" v-if="servers.size > 1">
<span class="section__heading">Servers</span> <span class="section__heading">{{ heading }}</span>
<ul class="section__content"> <ul class="section__content">
<ServerListItem :server="server" v-for="[name, server] in servers" :key="name"></ServerListItem> <ServerListItem :server="server" v-for="[name, server] in servers" :key="name"></ServerListItem>
</ul> </ul>
@ -36,7 +36,7 @@ export default defineComponent({
computed: { computed: {
heading() { heading() {
return 'Servers'; return useStore().state.messages.headingServers;
}, },
servers() { servers() {

View File

@ -39,7 +39,7 @@ export default defineComponent({
computed: { computed: {
heading() { heading() {
return useStore().state.messages.mapTypes; return useStore().state.messages.headingWorlds;
}, },
worlds() { worlds() {

15
src/dynmap.d.ts vendored
View File

@ -18,6 +18,7 @@ import {PathOptions, PointTuple, PolylineOptions} from "leaflet";
import {CoordinatesControlOptions} from "@/leaflet/control/CoordinatesControl"; import {CoordinatesControlOptions} from "@/leaflet/control/CoordinatesControl";
import {LogoControlOptions} from "@/leaflet/control/LogoControl"; import {LogoControlOptions} from "@/leaflet/control/LogoControl";
import {ClockControlOptions} from "@/leaflet/control/ClockControl"; import {ClockControlOptions} from "@/leaflet/control/ClockControl";
import {LiveAtlasMessageConfig} from "@/index";
declare global { declare global {
interface Window { interface Window {
@ -56,18 +57,6 @@ interface DynmapServerConfig {
hash: number; hash: number;
} }
interface DynmapMessageConfig {
chatNotAllowed: string;
chatRequiresLogin: string;
chatCooldown: string;
mapTypes: string;
players: string;
playerJoin: string;
playerQuit: string;
anonymousJoin: string;
anonymousQuit: string;
}
interface DynmapComponentConfig { interface DynmapComponentConfig {
markers: DynmapMarkersConfig; markers: DynmapMarkersConfig;
playerMarkers?: DynmapPlayerMarkersConfig; playerMarkers?: DynmapPlayerMarkersConfig;
@ -156,7 +145,7 @@ interface DynmapLocation {
interface DynmapConfigurationResponse { interface DynmapConfigurationResponse {
config: DynmapServerConfig, config: DynmapServerConfig,
messages: DynmapMessageConfig, messages: LiveAtlasMessageConfig,
worlds: Array<DynmapWorld>, worlds: Array<DynmapWorld>,
components: DynmapComponentConfig, components: DynmapComponentConfig,
loggedIn: boolean, loggedIn: boolean,

19
src/index.d.ts vendored
View File

@ -27,4 +27,23 @@ interface LiveAtlasServerDefinition {
interface LiveAtlasDynmapServerDefinition extends LiveAtlasServerDefinition { interface LiveAtlasDynmapServerDefinition extends LiveAtlasServerDefinition {
type: 'dynmap', type: 'dynmap',
dynmap: DynmapUrlConfig, dynmap: DynmapUrlConfig,
}
interface LiveAtlasMessageConfig {
chatPlayerJoin: string;
chatPlayerQuit: string;
chatAnonymousJoin: string;
chatAnonymousQuit: string;
chatNoMessages: string;
chatLogin: string;
chatLoginLink: string;
chatSend: string;
chatErrorNotAllowed: string;
chatErrorRequiresLogin: string;
chatErrorCooldown: string;
chatErrorDisabled: string;
chatErrorUnknown: string;
headingServers: string;
headingWorlds: string;
headingPlayers: string;
} }

View File

@ -25,14 +25,13 @@ import {
DynmapMarker, DynmapMarker,
DynmapMarkerSet, DynmapMarkerSet,
DynmapMarkerSetUpdates, DynmapMarkerSetUpdates,
DynmapMessageConfig,
DynmapPlayer, DynmapPlayer,
DynmapServerConfig, DynmapTileUpdate, DynmapServerConfig, DynmapTileUpdate,
DynmapWorld, DynmapWorld,
DynmapWorldState, DynmapParsedUrl, DynmapChat, DynmapUIElement DynmapWorldState, DynmapParsedUrl, DynmapChat, DynmapUIElement
} from "@/dynmap"; } from "@/dynmap";
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection"; import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
import {LiveAtlasServerDefinition} from "@/index"; import {LiveAtlasMessageConfig, LiveAtlasServerDefinition} from "@/index";
export type CurrentMapPayload = { export type CurrentMapPayload = {
worldName: string; worldName: string;
@ -44,7 +43,7 @@ export type Mutations<S = State> = {
[MutationTypes.SET_CONFIGURATION](state: S, config: DynmapServerConfig): void [MutationTypes.SET_CONFIGURATION](state: S, config: DynmapServerConfig): void
[MutationTypes.SET_CONFIGURATION_HASH](state: S, hash: number): void [MutationTypes.SET_CONFIGURATION_HASH](state: S, hash: number): void
[MutationTypes.CLEAR_CONFIGURATION_HASH](state: S): void [MutationTypes.CLEAR_CONFIGURATION_HASH](state: S): void
[MutationTypes.SET_MESSAGES](state: S, messages: DynmapMessageConfig): void [MutationTypes.SET_MESSAGES](state: S, messages: LiveAtlasMessageConfig): void
[MutationTypes.SET_WORLDS](state: S, worlds: Array<DynmapWorld>): void [MutationTypes.SET_WORLDS](state: S, worlds: Array<DynmapWorld>): void
[MutationTypes.CLEAR_WORLDS](state: S): void [MutationTypes.CLEAR_WORLDS](state: S): void
[MutationTypes.SET_COMPONENTS](state: S, worlds: DynmapComponentConfig): void [MutationTypes.SET_COMPONENTS](state: S, worlds: DynmapComponentConfig): void
@ -114,7 +113,7 @@ export const mutations: MutationTree<State> & Mutations = {
}, },
//Set messsages from the initial config fetch //Set messsages from the initial config fetch
[MutationTypes.SET_MESSAGES](state: State, messages: DynmapMessageConfig) { [MutationTypes.SET_MESSAGES](state: State, messages: LiveAtlasMessageConfig) {
state.messages = Object.assign(state.messages, messages); state.messages = Object.assign(state.messages, messages);
}, },

View File

@ -17,20 +17,19 @@
import { import {
DynmapComponentConfig, DynmapComponentConfig,
DynmapWorldMap, DynmapMarkerSet, DynmapMarkerSetUpdates, DynmapWorldMap, DynmapMarkerSet, DynmapMarkerSetUpdates,
DynmapMessageConfig,
DynmapPlayer, DynmapPlayer,
DynmapServerConfig, DynmapTileUpdate, DynmapServerConfig, DynmapTileUpdate,
DynmapWorld, DynmapWorldState, Coordinate, DynmapParsedUrl, DynmapChat, DynmapUIElement DynmapWorld, DynmapWorldState, Coordinate, DynmapParsedUrl, DynmapChat, DynmapUIElement
} from "@/dynmap"; } from "@/dynmap";
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection"; import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
import {LiveAtlasServerDefinition} from "@/index"; import {LiveAtlasMessageConfig, LiveAtlasServerDefinition} from "@/index";
export type State = { export type State = {
version: string; version: string;
servers: Map<string, LiveAtlasServerDefinition>; servers: Map<string, LiveAtlasServerDefinition>;
configuration: DynmapServerConfig; configuration: DynmapServerConfig;
configurationHash: number | undefined; configurationHash: number | undefined;
messages: DynmapMessageConfig; messages: LiveAtlasMessageConfig;
components: DynmapComponentConfig; components: DynmapComponentConfig;
loggedIn: boolean; loggedIn: boolean;
@ -94,15 +93,20 @@ export const state: State = {
configurationHash: undefined, configurationHash: undefined,
messages: { messages: {
chatNotAllowed: '', chatPlayerJoin: '',
chatRequiresLogin: '', chatPlayerQuit: '',
chatCooldown: '', chatAnonymousJoin: '',
mapTypes: '', chatAnonymousQuit: '',
players: '', chatNoMessages: '',
playerJoin: '', chatLogin: '',
playerQuit: '', chatErrorNotAllowed: '',
anonymousJoin: '', chatErrorCooldown: '',
anonymousQuit: '', chatErrorRequiresLogin: '',
chatErrorDisabled: '',
chatErrorUnknown: '',
headingWorlds: '',
headingPlayers: '',
headingServers: '',
}, },
loggedIn: false, loggedIn: false,