feat: i18n locales support

This commit is contained in:
2026-04-12 22:51:11 +02:00
parent f614157259
commit 0a88d958da
12 changed files with 162 additions and 28 deletions
+1
View File
@@ -15,6 +15,7 @@
"pinia": "^3.0.4", "pinia": "^3.0.4",
"sass": "^1.87.0", "sass": "^1.87.0",
"vue": "^3.3.11", "vue": "^3.3.11",
"vue-i18n": "11",
"vue-router": "5.0.4" "vue-router": "5.0.4"
}, },
"devDependencies": { "devDependencies": {
+20 -1
View File
@@ -40,6 +40,26 @@ export default defineComponent({
(this.mainStore.filters as any)[key] = settingsObj[key]; (this.mainStore.filters as any)[key] = settingsObj[key];
}); });
} }
},
loadLang() {
const storageLang = window.localStorage.getItem('language');
if (storageLang) {
this.mainStore.locale = storageLang;
this.$i18n.locale = storageLang;
return;
}
if (!window.navigator.language) return;
const naviLanguage = window.navigator.language.toString();
if (!naviLanguage.startsWith('pl')) {
this.mainStore.locale = 'en';
this.$i18n.locale = 'en';
return;
}
} }
}, },
@@ -68,7 +88,6 @@ export default defineComponent({
grid-template-rows: auto 1fr; grid-template-rows: auto 1fr;
min-height: 100vh; min-height: 100vh;
overflow-x: hidden;
} }
main { main {
+19 -15
View File
@@ -6,37 +6,29 @@
<transition name="dropdown-anim"> <transition name="dropdown-anim">
<div class="dropdown-body" v-if="store.optionsOpen"> <div class="dropdown-body" v-if="store.optionsOpen">
<h3>Opcje</h3> <h3>{{ $t('options.header') }}</h3>
<hr /> <hr />
<div <div class="dropdown-checkboxes">
style="
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5em;
margin: 0.5em 0;
"
>
<label> <label>
<input type="checkbox" v-model="store.filters.nonPassenger" /> <input type="checkbox" v-model="store.filters.nonPassenger" />
Relacje niepasażerskie {{ $t('options.checkbox-non-passenger') }}
</label> </label>
<label> <label>
<input type="checkbox" v-model="store.filters.terminating" /> <input type="checkbox" v-model="store.filters.terminating" />
Relacje kończące bieg {{ $t('options.checkbox-terminating') }}
</label> </label>
<label> <label>
<input type="checkbox" v-model="store.filters.soundsEnabled" /> <input type="checkbox" v-model="store.filters.soundsEnabled" />
Dźwięki {{ $t('options.checkbox-sounds') }}
</label> </label>
</div> </div>
<div v-if="isPragotronOpen" style="margin: 0.5em 0"> <div class="dropdown-checkpoints" v-if="isPragotronOpen">
<label for="checkpoint"> <label for="checkpoint">
Posterunek: {{ $t('options.checkpoint-name') }}
<select id="checkpoint" v-model="store.selectedCheckpointName"> <select id="checkpoint" v-model="store.selectedCheckpointName">
<option v-for="cp in store.selectedStation?.stationCheckpoints" :value="cp" :key="cp"> <option v-for="cp in store.selectedStation?.stationCheckpoints" :value="cp" :key="cp">
{{ cp }} {{ cp }}
@@ -110,6 +102,18 @@ h3 {
background-color: #000000e1; background-color: #000000e1;
} }
.dropdown-checkboxes {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5em;
margin: 0.5em 0;
}
.dropdown-checkpoints {
margin: 0.5em 0;
}
.dropdown-anim { .dropdown-anim {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
+2
View File
@@ -54,6 +54,8 @@ nav.navbar {
margin: 0 auto; margin: 0 auto;
max-width: 1400px; max-width: 1400px;
font-weight: bold;
} }
.brand { .brand {
+20
View File
@@ -0,0 +1,20 @@
import enLang from './locales/en.json';
import plLang from './locales/pl.json';
import { createI18n } from 'vue-i18n';
const i18n = createI18n({
locale: 'pl',
legacy: false,
warnHtmlMessage: false,
fallbackLocale: 'pl',
messages: {
en: enLang,
pl: plLang
},
enableLegacy: false
});
export default i18n;
+21
View File
@@ -0,0 +1,21 @@
{
"home": {
"header": "Choose region and scenery to open the pragotron view",
"data-loading": "Loading active sceneries list...",
"no-available-data": "No active sceneries"
},
"pragotron": {
"header-1": "HOUR",
"header-2": "TRAIN",
"header-3": "VIA",
"header-4": "TERMINATING",
"header-5": "DELAYED"
},
"options": {
"header": "Opcje",
"checkbox-non-passenger": "Relacje niepasażerskie",
"checkbox-terminating": "Relacje kończące bieg",
"checkbox-sounds": "Dźwięki",
"checkpoint-name": "Posterunek:"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"home": {
"header": "Wybierz region i scenerię, aby otworzyć widok pragotronu",
"data-loading": "Ładowanie listy aktywnych scenerii...",
"no-available-data": "Brak aktywnych scenerii"
},
"pragotron": {
"header-1": "GODZ.",
"header-2": "POCIĄG",
"header-3": "PRZEZ",
"header-4": "DO STACJI",
"header-5": "OPÓŹNIONY"
},
"options": {
"header": "Opcje",
"checkbox-non-passenger": "Relacje niepasażerskie",
"checkbox-terminating": "Relacje kończące bieg",
"checkbox-sounds": "Dźwięki",
"checkpoint-name": "Posterunek:"
}
}
+2
View File
@@ -2,6 +2,7 @@ import { createApp, type Directive } from 'vue';
import App from './App.vue'; import App from './App.vue';
import router from './router'; import router from './router';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import i18n from './i18n';
const pinia = createPinia(); const pinia = createPinia();
@@ -20,5 +21,6 @@ const clickOutsideDirective: Directive = {
createApp(App) createApp(App)
.use(router) .use(router)
.use(pinia) .use(pinia)
.use(i18n)
.directive('click-outside', clickOutsideDirective) .directive('click-outside', clickOutsideDirective)
.mount('#app'); .mount('#app');
+2 -1
View File
@@ -30,7 +30,8 @@ export const useMainStore = defineStore('main', {
soundsEnabled: false soundsEnabled: false
}, },
selectedStationName: '', selectedStationName: '',
selectedCheckpointName: '' selectedCheckpointName: '',
locale: 'pl'
}; };
}, },
+3 -5
View File
@@ -1,7 +1,7 @@
<template> <template>
<div class="home-view"> <div class="home-view">
<div> <div>
<h1 style="margin: 0">Wybierz region i scenerię, aby otworzyć widok pragotronu</h1> <h1 style="margin: 0">{{ $t('home.header') }}</h1>
<div class="region-selector g-selector"> <div class="region-selector g-selector">
<label v-for="region in regions" :key="region"> <label v-for="region in regions" :key="region">
@@ -17,14 +17,12 @@
</div> </div>
<div class="scenery-selector"> <div class="scenery-selector">
<!-- <p style="margin: 0.5em; color: #ccc">Widoczne jedynie scenerie aktywne na serwerze PL1</p> -->
<transition name="list-anim" tag="div" mode="out-in"> <transition name="list-anim" tag="div" mode="out-in">
<h3 v-if="apiStore.dataStatuses.activeData == DataStatus.LOADING"> <h3 v-if="apiStore.dataStatuses.activeData == DataStatus.LOADING">
Ładowanie listy aktywnych scenerii... {{ $t('home.data-loading') }}
</h3> </h3>
<h3 v-else-if="sceneriesOnline.length == 0">Brak aktywnych scenerii</h3> <h3 v-else-if="sceneriesOnline.length == 0">{{ $t('home.no-available-data') }}</h3>
<ul v-else class="scenery-list" :key="mainStore.region"> <ul v-else class="scenery-list" :key="mainStore.region">
<li v-for="station in sceneriesOnline" :key="station.stationName"> <li v-for="station in sceneriesOnline" :key="station.stationName">
+5 -5
View File
@@ -7,11 +7,11 @@
<div>{{ mainStore.selectedCheckpointName.toUpperCase() }}</div> <div>{{ mainStore.selectedCheckpointName.toUpperCase() }}</div>
</span> </span>
<div class="headers"> <div class="headers">
<span>GODZ.</span> <span>{{ $t('pragotron.header-1') }}</span>
<span>POCIĄG</span> <span>{{ $t('pragotron.header-2') }}</span>
<span>PRZEZ</span> <span>{{ $t('pragotron.header-3') }}</span>
<span>DO STACJI</span> <span>{{ $t('pragotron.header-4') }}</span>
<span>OPÓŹNIONY</span> <span>{{ $t('pragotron.header-5') }}</span>
</div> </div>
</div> </div>
<div class="table"> <div class="table">
+46 -1
View File
@@ -60,6 +60,36 @@
dependencies: dependencies:
tslib "^2.4.0" tslib "^2.4.0"
"@intlify/core-base@11.3.2":
version "11.3.2"
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-11.3.2.tgz#185d0e3259be5afc1888c2d2e1a636be09ad2dae"
integrity sha512-cgsUaV/dyD6aS49UPgerIblrWeXAZHNaDWqm4LujOGC7IafSyhghGXEiSVvuDYaDPiQTP+tSFSTM1HIu7Yp1nA==
dependencies:
"@intlify/devtools-types" "11.3.2"
"@intlify/message-compiler" "11.3.2"
"@intlify/shared" "11.3.2"
"@intlify/devtools-types@11.3.2":
version "11.3.2"
resolved "https://registry.yarnpkg.com/@intlify/devtools-types/-/devtools-types-11.3.2.tgz#983e3c12340b36a41648fda451aeebc4f3ade812"
integrity sha512-q96G2ZZw0FNoXzejbjIf9dbfgz1xyYBZu6ZT4b5TE/55j8d1O9X5jv0k+U+L3fVe7uebPcqRQFD0ffm30i5mJA==
dependencies:
"@intlify/core-base" "11.3.2"
"@intlify/shared" "11.3.2"
"@intlify/message-compiler@11.3.2":
version "11.3.2"
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-11.3.2.tgz#3e53e5f71995a2ef7d385554185852f5139d4eb3"
integrity sha512-d/awyHUkNSaGPxBxT/qlUpfRizxHX9dt55CnW03xx5p1KmMyfYHKupCnvzINX+Na8JR8LAR7y32lPKjoeQGmzA==
dependencies:
"@intlify/shared" "11.3.2"
source-map-js "^1.0.2"
"@intlify/shared@11.3.2":
version "11.3.2"
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-11.3.2.tgz#589d0a7d5448fb042b9553be28aca4467898368d"
integrity sha512-x66fjdH6i+lNYPae5URSQGTjBL68Av6hi09jvC5Ci96iTkwfqrPhCj46aylQZmgMaG89rOZCIKqS7ApC8ZDVjg==
"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
version "0.3.13" version "0.3.13"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
@@ -384,6 +414,11 @@
"@vue/compiler-dom" "3.5.32" "@vue/compiler-dom" "3.5.32"
"@vue/shared" "3.5.32" "@vue/shared" "3.5.32"
"@vue/devtools-api@^6.5.0":
version "6.6.4"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343"
integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
"@vue/devtools-api@^7.7.7": "@vue/devtools-api@^7.7.7":
version "7.7.9" version "7.7.9"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz#999dbea50da6b00cf59a1336f11fdc2b43d9e063" resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz#999dbea50da6b00cf59a1336f11fdc2b43d9e063"
@@ -879,7 +914,7 @@ scule@^1.3.0:
resolved "https://registry.yarnpkg.com/scule/-/scule-1.3.0.tgz#6efbd22fd0bb801bdcc585c89266a7d2daa8fbd3" resolved "https://registry.yarnpkg.com/scule/-/scule-1.3.0.tgz#6efbd22fd0bb801bdcc585c89266a7d2daa8fbd3"
integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g== integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1: "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2, source-map-js@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
@@ -959,6 +994,16 @@ vscode-uri@^3.0.8:
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c"
integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==
vue-i18n@11:
version "11.3.2"
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-11.3.2.tgz#8f774a4e680be7e0c7a939ff04663cf26886fb85"
integrity sha512-gmFrvM+iuf2AH4ygligw/pC7PRJ63AdRNE68E0GPlQ83Mzfyck6g6cRQC3KzkYXr+ZidR91wq+5YBmAMpkgE1A==
dependencies:
"@intlify/core-base" "11.3.2"
"@intlify/devtools-types" "11.3.2"
"@intlify/shared" "11.3.2"
"@vue/devtools-api" "^6.5.0"
vue-router@5.0.4: vue-router@5.0.4:
version "5.0.4" version "5.0.4"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-5.0.4.tgz#41ffc00bab448c406447115bbb29d49144cc1e7d" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-5.0.4.tgz#41ffc00bab448c406447115bbb29d49144cc1e7d"