Get the language module out of spot.js

This commit is contained in:
2026-04-25 19:36:03 +02:00
parent ff4bc26381
commit 7dc2b28c44
11 changed files with 89 additions and 106 deletions

View File

@@ -9,7 +9,7 @@ export default {
SpotButton,
AdminInput
},
inject: ['spot'],
inject: ['spot', 'lang'],
data() {
return {
elems: {},
@@ -22,7 +22,7 @@ export default {
},
methods: {
l(id) {
return this.spot.lang(id);
return this.lang.get(id);
},
setEvents() {
this.spot.addPage('admin', {
@@ -57,7 +57,7 @@ export default {
for(const [iKey, oNewElem] of Object.entries(aoNewElems)) {
oNewElem.type = sType;
this.elems[sType][oNewElem.id] = oNewElem;
this.spot.onFeedback('success', this.spot.lang('admin_create_success'), {'create':sType});
this.spot.onFeedback('success', this.lang.get('admin_create_success'), {'create':sType});
}
}
})
@@ -73,7 +73,7 @@ export default {
'admin_delete',
(asData) => {
delete this.elems[asInputs.type][asInputs.id];
this.spot.onFeedback('success', this.spot.lang('admin_delete_success'), asInputs);
this.spot.onFeedback('success', this.lang.get('admin_delete_success'), asInputs);
},
asInputs,
(sError) => {
@@ -97,7 +97,7 @@ export default {
this.spot.get2('admin_set', asInputs)
.then((asData) => {
this.elems[oElem.type][oElem.id][oEvent.target.name] = sNewVal;
this.spot.onFeedback('success', this.spot.lang('admin_save_success'), asInputs);
this.spot.onFeedback('success', this.lang.get('admin_save_success'), asInputs);
})
.catch((sError) => {
oEvent.target.value = sOldVal;
@@ -232,4 +232,4 @@ export default {
<p v-for="feedback in feedbacks" :class="feedback.type">{{ feedback.msg }}</p>
</div>
</div>
</template>
</template>

View File

@@ -87,7 +87,7 @@ export default {
project: this
};
},
inject: ['spot', 'hash', 'projects', 'user'],
inject: ['spot', 'lang', 'hash', 'projects', 'user'],
beforeMount() {
if(this.hash.items.length == 0) this.hash.items[0] = this.spot.vars('default_project_codename');
},
@@ -399,7 +399,7 @@ export default {
medias: [oMedia],
project: this.currProject
});
this.popup.content.provide('spot', this.spot).mount($Popup);
this.popup.content.provide('spot', this.spot).provide('lang', this.lang).mount($Popup);
},
openMarkerPopup(oFeature) {
this.closeMarkerPopup();
@@ -434,7 +434,7 @@ export default {
medias: JSON.parse(oFeature.properties.medias || '[]'),
project: this.currProject
});
this.popup.content.provide('spot', this.spot).mount($Popup);
this.popup.content.provide('spot', this.spot).provide('lang', this.lang).mount($Popup);
},
closeMarkerPopup() {
if(this.popup.content) {
@@ -524,14 +524,14 @@ export default {
getGoogleMapsLink(asInfo) {
return $('<a>', {
href:'https://www.google.com/maps/place/'+asInfo.lat_dms+'+'+asInfo.lon_dms+'/@'+asInfo.latitude+','+asInfo.longitude+',10z',
title: this.spot.lang('see_on_google'),
title: this.lang.get('see_on_google'),
target: '_blank',
rel: 'noreferrer noopener'
}).text(asInfo.lat_dms+' '+asInfo.lon_dms);
},
async manageSubs() {
var regexEmail = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if(!regexEmail.test(this.user.email)) this.nlFeedbacks.push({type:'error', 'msg':this.spot.lang('nl_invalid_email')});
if(!regexEmail.test(this.user.email)) this.nlFeedbacks.push({type:'error', 'msg':this.lang.get('nl_invalid_email')});
else {
this.spot.get2(this.nlAction, {'email': this.user.email, 'name': this.user.name}, this.nlLoading)
.then((asUser, sDesc) => {
@@ -641,19 +641,19 @@ export default {
<div class="settings-header">
<div class="logo"><img width="289" height="72" src="images/logo_black.png" alt="Spotty" /></div>
<div id="last_update" v-if="this.currProject.mode == this.spot.consts.modes.blog && lastUpdate.unix_time > 0">
<p><span><img src="images/spot-logo-only.svg" alt="" /></span><abbr :title="lastUpdate.formatted_time">{{ spot.lang('last_update')+' '+lastUpdate.relative_time }}</abbr></p>
<p><span><img src="images/spot-logo-only.svg" alt="" /></span><abbr :title="lastUpdate.formatted_time">{{ lang.get('last_update')+' '+lastUpdate.relative_time }}</abbr></p>
</div>
</div>
<div class="settings-sections">
<Simplebar id="settings-sections-scrollbox">
<div class="settings-section">
<h1><SpotIcon :icon="'project'" :classes="'fa-fw'" :text="spot.lang('hikes')" /></h1>
<h1><SpotIcon :icon="'project'" :classes="'fa-fw'" :text="lang.get('hikes')" /></h1>
<div class="settings-section-body">
<div class="radio" v-for="project in projects" :key="'project-'+project.id">
<input type="radio" :id="'project-'+project.id" :value="project.codename" v-model="$parent.hash.items[0]" />
<label :for="'project-'+project.id">
<span>{{ project.name }}</span>
<a class="download" :href="project.gpxfilepath" :title="spot.lang('track_download')" @click.stop="()=>{}">
<a class="download" :href="project.gpxfilepath" :title="lang.get('track_download')" @click.stop="()=>{}">
<SpotIcon :icon="'download'" :classes="'push-left'" />
</a>
</label>
@@ -661,36 +661,36 @@ export default {
</div>
</div>
<div class="settings-section">
<h1><SpotIcon :icon="'map'" :classes="'fa-fw'" :text="spot.lang('maps')" /></h1>
<h1><SpotIcon :icon="'map'" :classes="'fa-fw'" :text="lang.get('maps')" /></h1>
<div class="settings-section-body">
<div class="radio" v-for="bm in baseMaps" :key="'map-'+bm.id_map">
<input type="radio" :id="'map-'+bm.id_map" :value="bm.codename" v-model="baseMap" />
<label :for="'map-'+bm.id_map">{{ this.spot.lang('map_'+bm.codename) }}</label>
<label :for="'map-'+bm.id_map">{{ lang.get('map_'+bm.codename) }}</label>
</div>
</div>
</div>
<div class="settings-section newsletter">
<h1><SpotIcon :icon="'newsletter'" :classes="'fa-fw'" :text="spot.lang('newsletter')" /></h1>
<input type="email" name="email" id="email" :placeholder="spot.lang('nl_email_placeholder')" v-model="user.email" :disabled="nlLoading || subscribed" />
<SpotButton id="nl_btn" :classes="nlClasses" :title="spot.lang('nl_'+nlAction)" @click="manageSubs" />
<h1><SpotIcon :icon="'newsletter'" :classes="'fa-fw'" :text="lang.get('newsletter')" /></h1>
<input type="email" name="email" id="email" :placeholder="lang.get('nl_email_placeholder')" v-model="user.email" :disabled="nlLoading || subscribed" />
<SpotButton id="nl_btn" :classes="nlClasses" :title="lang.get('nl_'+nlAction)" @click="manageSubs" />
<div id="settings-feedback" class="feedback">
<p v-for="feedback in nlFeedbacks" :class="feedback.type">
<SpotIcon :icon="feedback.type" :text="feedback.msg" />
</p>
</div>
{{ spot.lang(subscribed?'nl_subscribed_desc':'nl_unsubscribed_desc') }}
{{ lang.get(subscribed?'nl_subscribed_desc':'nl_unsubscribed_desc') }}
</div>
<div class="settings-section admin" v-if="spot.checkClearance(spot.consts.clearances.admin)">
<h1><SpotIcon :icon="'admin fa-fw'" :text="spot.lang('admin')" /></h1>
<a class="button" href="#admin"><SpotIcon :icon="'config'" :text="spot.lang('admin_config')" /></a>
<a class="button" href="#upload"><SpotIcon :icon="'upload'" :text="spot.lang('admin_upload')" /></a>
<h1><SpotIcon :icon="'admin fa-fw'" :text="lang.get('admin')" /></h1>
<a class="button" href="#admin"><SpotIcon :icon="'config'" :text="lang.get('admin_config')" /></a>
<a class="button" href="#upload"><SpotIcon :icon="'upload'" :text="lang.get('admin_upload')" /></a>
</div>
</Simplebar>
</div>
<div class="settings-footer">
<a href="https://git.lutran.fr/franzz/spot" :title="spot.lang('credits_git')" target="_blank" rel="noopener">
<SpotIcon :icon="'credits'" :text="spot.lang('credits_project')" />
</a> {{ spot.lang('credits_license') }}</div>
<a href="https://git.lutran.fr/franzz/spot" :title="lang.get('credits_git')" target="_blank" rel="noopener">
<SpotIcon :icon="'credits'" :text="lang.get('credits_project')" />
</a> {{ lang.get('credits_license') }}</div>
</div>
<div :class="'map-control map-control-icon settings-control map-control-'+(mobile?'bottom':'top')" @click="toggleSettingsPanel">
<SpotIcon :icon="settingsPanelOpen?'prev':'menu'" />
@@ -698,7 +698,7 @@ export default {
<div v-if="!mobile" id="legend" class="map-control settings-control map-control-bottom">
<div v-for="(color, hikeType) in hikes.colors" class="track">
<span class="line" :style="'background-color:'+color+'; height:'+hikes.width+'px;'"></span>
<span class="desc">{{ spot.lang('track_'+hikeType) }}</span>
<span class="desc">{{ lang.get('track_'+hikeType) }}</span>
</div>
</div>
<div id="title" :class="'map-control settings-control map-control-'+(mobile?'bottom':'top')">
@@ -709,7 +709,7 @@ export default {
<Simplebar id="feed-panel" class="map-panel" ref="feedSimpleBar">
<div id="feed-header">
<ProjectPost v-if="modeHisto" :options="{type: 'archived', headerless: true}" />
<ProjectPost v-else :options="{type: 'poster', relative_time: spot.lang('post_new_message')}" />
<ProjectPost v-else :options="{type: 'poster', relative_time: lang.get('post_new_message')}" />
</div>
<div id="feed-posts">
<ProjectPost v-for="post in posts" :options="post" ref="posts" />

View File

@@ -3,15 +3,15 @@ export default {
props: {
options: Object
},
inject: ['spot']
inject: ['lang']
}
</script>
<template>
<a
:href="'https://www.google.com/maps/place/'+options.lat_dms+'+'+options.lon_dms+'/@'+options.latitude+','+options.longitude+',10z'"
:title="spot.lang('see_on_google')"
:title="lang.get('see_on_google')"
target="_blank"
rel="noreferrer noopener"
>{{ options.lat_dms+' '+options.lon_dms }}</a>
</template>
</template>

View File

@@ -16,7 +16,7 @@ export default {
title:''
}
},
inject: ['spot'],
inject: ['lang'],
mounted() {
this.title =
(this.$refs.comment?this.$refs.comment.outerHTML:'') +
@@ -47,7 +47,7 @@ export default {
:src="options.thumb_path"
:width="options.width"
:height="options.height"
:title="spot.lang((options.subtype == 'video')?'click_watch':'click_zoom')"
:title="lang.get((options.subtype == 'video')?'click_watch':'click_zoom')"
class="clickable"
/>
<span class="drill-icon"><spotIcon :icon="'drill-'+options.subtype" /></span>
@@ -64,4 +64,4 @@ export default {
<projectRelTime :icon="options.subtype+'-shot'" :localTime="options.taken_on_formatted_time_local" :siteTime="options.taken_on_formatted_time" :offset="options.taken_on_day_offset" />
</span>
</div>
</template>
</template>

View File

@@ -18,7 +18,7 @@ export default {
medias: Array,
project: Object
},
inject: ['spot'],
inject: ['spot', 'lang'],
computed: {
timeIcon() {
return (this.type == 'media')?'image-shot':'time';
@@ -31,7 +31,7 @@ export default {
<div :class="'info-window '+type">
<div class="header" v-if="type=='message'">
<h1>
<spotIcon :icon="'message'" :classes="'fa-lg'" :text="spot.lang('post_message')+' '+spot.lang('counter', options.displayed_id)" />
<spotIcon :icon="'message'" :classes="'fa-lg'" :text="lang.get('post_message')+' '+lang.get('counter', options.displayed_id)" />
<span class="message-type">({{ options.type }})</span>
</h1>
<div class="separator"></div>
@@ -47,7 +47,7 @@ export default {
<projectRelTime :icon="timeIcon" :localTime="options.formatted_time_local" :siteTime="options.formatted_time" :offset="options.day_offset" />
<span v-if="project.mode==spot.consts.modes.blog"> ({{ options.relative_time }})</span>
</p>
<p class="weather" v-if="options.weather_icon && options.weather_icon!='unknown'" :title="options.weather_cond==''?'':spot.lang(options.weather_cond)">
<p class="weather" v-if="options.weather_icon && options.weather_icon!='unknown'" :title="options.weather_cond==''?'':lang.get(options.weather_cond)">
<spotIcon :icon="options.weather_icon" :classes="'fa-fw fa-lg'" :text="options.weather_temp+'°C'" />
</p>
<div v-if="medias.length > 0">
@@ -57,4 +57,4 @@ export default {
</div>
</div>
</div>
</template>
</template>

View File

@@ -29,7 +29,7 @@
absTimeLocal: this.options.formatted_time_local,
timeDiff: (this.options.formatted_time && this.options.formatted_time_local != this.options.formatted_time),
anchorVisible: ['message', 'media', 'post'].includes(this.options.type),
anchorTitle: this.spot.lang('copy_to_clipboard'),
anchorTitle: this.lang.get('copy_to_clipboard'),
anchorIcon: 'link',
popupRequested: false,
postMessage: '',
@@ -49,7 +49,7 @@
return this.options.subtype || this.options.type;
},
displayedId() {
return this.options.displayed_id?(this.spot.lang('counter', this.options.displayed_id)):'';
return this.options.displayed_id?(this.lang.get('counter', this.options.displayed_id)):'';
},
anchorLink() {
return '#'+[this.hash.page, this.hash.items[0], this.options.type, this.options.id].join(this.spot.consts.hash_sep);
@@ -69,14 +69,14 @@
}
},
inject: ['spot', 'project', 'user', 'map', 'hash'],
inject: ['spot', 'lang', 'project', 'user', 'map', 'hash'],
methods: {
copyAnchor() {
copyTextToClipboard(this.spot.consts.server+this.anchorLink);
this.anchorTitle = this.spot.lang('link_copied');
this.anchorTitle = this.lang.get('link_copied');
this.anchorIcon = 'copied';
setTimeout(()=>{ //TODO animation
this.anchorTitle = this.spot.lang('copy_to_clipboard');
this.anchorTitle = this.lang.get('copy_to_clipboard');
this.anchorIcon = 'link';
}, 5000);
},
@@ -146,9 +146,9 @@
<spotIcon :icon="anchorIcon" />
</a>
</div>
<div class="time" @mouseleave="mouseOverHeader = false" @mouseover="mouseOverHeader = true" :title="timeDiff?spot.lang('local_time', absTimeLocal):''">
<div class="time" @mouseleave="mouseOverHeader = false" @mouseover="mouseOverHeader = true" :title="timeDiff?lang.get('local_time', absTimeLocal):''">
<Transition name="fade" mode="out-in">
<span v-if="mouseOverHeader">{{ timeDiff?spot.lang('your_time', absTime):absTime }}</span>
<span v-if="mouseOverHeader">{{ timeDiff?lang.get('your_time', absTime):absTime }}</span>
<span v-else>{{ relTime }}</span>
</Transition>
</div>
@@ -163,11 +163,11 @@
<projectRelTime :icon="'time'" :iconClasses="'push'" :localTime="absTimeLocal" :siteTime="options.formatted_time" :offset="options.day_offset" />
</p>
<a class="drill" @click.prevent="executeMainAction">
<span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="spot.lang(options.weather_cond)">
<span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="lang.get(options.weather_cond)">
<spotIcon :icon="options.weather_icon" />
<span class="temperature">{{ options.weather_temp+'°C' }}</span>
</span>
<img class="staticmap clickable" :title="spot.lang('click_zoom')" :src="options.static_img_url" />
<img class="staticmap clickable" :title="lang.get('click_zoom')" :src="options.static_img_url" />
<spotIconStack :mainClasses="'message drill-icon'" :iconMain="'marker'" :iconSub="'footprint'" :iconSubClasses="'fa-rotate-270'" />
</a>
</div>
@@ -183,13 +183,13 @@
</p>
</div>
<div v-else-if="options.type == 'poster'">
<textarea ref="post" name="post" :placeholder="spot.lang('post_message')" class="autoExpand" rows="1" v-model="postMessage"></textarea>
<input type="text" name="name" :placeholder="spot.lang('post_name')" v-model="user.name" />
<spotButton name="submit" :aria-label="spot.lang('send')" :title="spot.lang('send')" :icon="'send'" @click="send()" :iconClasses="sending?'flicker':''" />
<textarea ref="post" name="post" :placeholder="lang.get('post_message')" class="autoExpand" rows="1" v-model="postMessage"></textarea>
<input type="text" name="name" :placeholder="lang.get('post_name')" v-model="user.name" />
<spotButton name="submit" :aria-label="lang.get('send')" :title="lang.get('send')" :icon="'send'" @click="send()" :iconClasses="sending?'flicker':''" />
</div>
<div v-else-if="options.type == 'archived'">
<p><spotIcon :icon="'success'" /></p>
<p>{{ spot.lang('mode_histo') }}</p>
<p>{{ lang.get('mode_histo') }}</p>
</div>
<div v-else-if="options.type == 'loading'">
<p class="flicker"><spotIcon :icon="'post'" /></p>

View File

@@ -13,10 +13,10 @@ export default {
icon: String,
iconClasses: String
},
inject: ['spot'],
inject: ['lang'],
computed: {
title() {
if(this.localTime != this.siteTime) return this.spot.lang('your_time', this.siteTime.slice(-5)) + ((this.offset != '0')?' ('+this.spot.lang('unit_d')+this.offset+')':'');
if(this.localTime != this.siteTime) return this.lang.get('your_time', this.siteTime.slice(-5)) + ((this.offset != '0')?' ('+this.lang.get('unit_d')+this.offset+')':'');
},
spotIconClasses() {
return this.iconClasses || 'fa-fw fa-lg';
@@ -28,4 +28,4 @@ export default {
<template>
<spotIcon v-if="icon" :icon="icon" :classes="spotIconClasses" :title="title" :text="localTime" />
<span v-else :class="classes" :title="title">{{ localTime }}</span>
</template>
</template>

View File

@@ -10,7 +10,7 @@ import SpotButton from './spotButton.vue';
export default {
name: 'upload',
components: { SpotButton, SpotIcon },
inject: ['spot', 'projects', 'consts', 'user'],
inject: ['spot', 'lang', 'projects', 'consts', 'user'],
data() {
return {
project: this.projects[this.spot.vars('default_project_codename')],
@@ -24,7 +24,7 @@ export default {
this.spot.addPage('upload', {});
if(!this.project.editable) {
this.logs = [this.spot.lang('upload_mode_archived', [this.project.name])];
this.logs = [this.lang.get('upload_mode_archived', [this.project.name])];
return;
}
@@ -67,13 +67,13 @@ export default {
const uploadedFiles = response?.body?.files || [];
uploadedFiles.forEach((uploadedFile) => {
const hasError = Object.prototype.hasOwnProperty.call(uploadedFile, 'error');
this.logs.push(hasError ? uploadedFile.error : this.spot.lang('upload_success', [uploadedFile.name]));
this.logs.push(hasError ? uploadedFile.error : this.lang.get('upload_success', [uploadedFile.name]));
if(!hasError) this.files.push({...uploadedFile, content: ''});
});
});
this.uppy.on('upload-error', (file, error, response) => {
const message = response?.body?.error || error?.message || this.spot.lang('error');
const message = response?.body?.error || error?.message || this.lang.get('error');
this.logs.push(message);
});
@@ -88,8 +88,8 @@ export default {
},
addComment(oFile) {
this.spot.get2('add_comment', {id: oFile.id, content: oFile.content})
.then((asData) => {this.logs.push(this.spot.lang('media_comment_update', asData.filename));})
.catch((sMsgId) => {this.logs.push(this.spot.lang(sMsgId));});
.then((asData) => {this.logs.push(this.lang.get('media_comment_update', asData.filename));})
.catch((sMsgId) => {this.logs.push(this.lang.get(sMsgId));});
},
addPosition() {
if(navigator.geolocation) {
@@ -98,8 +98,8 @@ export default {
(position) => {
this.logs.push('Sending position...');
this.spot.get2('add_position', {'latitude':position.coords.latitude, 'longitude':position.coords.longitude, 'timestamp':Math.round(position.timestamp / 1000)})
.then((asData) => {this.logs.push(this.spot.lang('success'));})
.catch((sMsgId) => {this.logs.push(this.spot.lang(sMsgId));});
.then((asData) => {this.logs.push(this.lang.get('success'));})
.catch((sMsgId) => {this.logs.push(this.lang.get(sMsgId));});
},
(error) => {
this.logs.push(error.message);
@@ -113,8 +113,8 @@ export default {
</script>
<template>
<div id="upload">
<a name="back" class="button" href="#project"><SpotIcon :icon="'back'" :text="spot.lang('nav_back')" /></a>
<h1>{{ spot.lang('upload_media_title') }}</h1>
<a name="back" class="button" href="#project"><SpotIcon :icon="'back'" :text="lang.get('nav_back')" /></a>
<h1>{{ lang.get('upload_media_title') }}</h1>
<h2>{{ this.project.name }}</h2>
<div class="section" v-if="project.editable">
<input id="fileupload" type="file" name="files[]" multiple accept=".gif,.jpg,.jpeg,.png,.mov,.mp4" @change="onFileChange" />
@@ -127,13 +127,13 @@ export default {
<div class="form">
<input class="content" name="content" type="text" v-model="file.content" />
<input class="id" name="id" type="hidden" :value="file.id" />
<SpotButton :classes="'save'" :icon="'save'" :text="spot.lang('save')" @click="addComment(file)" />
<SpotButton :classes="'save'" :icon="'save'" :text="lang.get('save')" @click="addComment(file)" />
</div>
</div>
<h1>{{ spot.lang('upload_pos_title') }}</h1>
<div class="section location">
<SpotButton :icon="'message'" :text="spot.lang('new_position')" @click="addPosition()" />
</div>
<h1>{{ lang.get('upload_pos_title') }}</h1>
<div class="section location">
<SpotButton :icon="'message'" :text="lang.get('new_position')" @click="addPosition()" />
</div>
<div class="section logs" v-if="logs.length > 0">
<p class="log" v-for="log in logs">{{ log }}.</p>
</div>

View File

@@ -1,29 +1,21 @@
import Css from './../styles/spot.scss';
//Masks
//Librairies
import Lang from './lang.js';
import Spot from './spot.js';
//import Project from './page.project.js';
//import Upload from './page.upload.js';
//import Admin from './page.admin.js';
//let oProject = new Project(oSpot);
//oSpot.addPage('project', oProject);
//let oUpload = new Upload(oSpot);
//oSpot.addPage('upload', oUpload);
//let oAdmin = new Admin(oSpot);
//oSpot.addPage('admin', oAdmin);
//$(() => {oSpot.init();});
import { createApp } from 'vue';
import SpotVue from '../Spot.vue';
const appConfigElement = document.getElementById('app-config');
const appConfig = appConfigElement ? JSON.parse(appConfigElement.textContent || '{}') : {};
const oSpot = new Spot(appConfig);
//Style
import Css from './../styles/spot.scss';
//App Configuration from PHP
const appConfig = JSON.parse(document.getElementById('app-config').textContent);
//Instances
const oLang = new Lang({translations: appConfig.consts.lang, prefix: appConfig.consts.lang_prefix});
const oSpot = new Spot(appConfig, oLang);
//Mount app
const oSpotVue = createApp(SpotVue);
oSpotVue.provide('spot', oSpot);
oSpotVue.provide('lang', oLang);
oSpotVue.mount('#container');

View File

@@ -5,7 +5,7 @@ export default class Lang {
this.prefix = prefix;
}
lang(key = '', params = []) {
get(key = '', params = []) {
if(key === '') return '';
const normalizedParams = Array.isArray(params) ? params : [params];
@@ -24,7 +24,7 @@ export default class Lang {
parse(message = '') {
if(this.prefix && typeof message === 'string' && message.startsWith(this.prefix)) {
return this.lang(message.slice(this.prefix.length));
return this.get(message.slice(this.prefix.length));
}
return message;
}

View File

@@ -1,19 +1,14 @@
import { copyArray, getElem, setElem } from './common.js';
import Lang from './lang.js';
export default class Spot {
constructor(asGlobals) {
constructor(asGlobals, oLang) {
this.consts = asGlobals.consts;
this.consts.hash_sep = '-';
this.consts.title = 'Spotty';
this.consts.default_page = 'project';
//this.consts.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || this.consts.default_timezone;
this.translator = new Lang({
translations: this.consts.lang || {},
prefix: this.consts.lang_prefix || ''
});
this.lang = oLang;
this.pages = {};
@@ -112,7 +107,7 @@ export default class Spot {
else {
let oResponse = await oRequest.json();
bLoading = false;
oResponse.desc = this.translator.parse(oResponse.desc);
oResponse.desc = this.lang.parse(oResponse.desc);
if(oResponse.result == this.consts.error) return Promise.reject(oResponse.desc);
else return oResponse.data;
@@ -124,10 +119,6 @@ export default class Spot {
}
}
lang(sKey, asParams = []) {
return this.translator.lang(sKey, asParams);
}
isMobile() {
const mobileElem = document.getElementById('mobile');
return !!mobileElem && getComputedStyle(mobileElem).display !== 'none';
@@ -295,8 +286,8 @@ export default class Spot {
iTimeMinutes = Math.floor(iHours * 4) * 15; //Round down to the closest 15 minutes
}
return '~ '
+(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'½')+' '+this.lang(iTimeDays>1?'unit_days':'unit_day')):'') //Days
+((iTimeHours>0 || iTimeDays==0)?iTimeHours+this.lang('unit_hour'):'') //Hours
+(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'½')+' '+this.lang.get(iTimeDays>1?'unit_days':'unit_day')):'') //Days
+((iTimeHours>0 || iTimeDays==0)?iTimeHours+this.lang.get('unit_hour'):'') //Hours
+((iTimeDays>0 || iTimeMinutes==0)?'':iTimeMinutes) //Minutes
}