Add i18n dictionary and dynamic UI translations
This commit is contained in:
1 parent
f726b76b7d
commit
d546124359
3 files changed
+89
-17
No files matched your search
@@ -4,6 +4,7 @@ COPY ash.css /usr/share/nginx/html/ash.css
|
|||||||
COPY gruvbox.css /usr/share/nginx/html/gruvbox.css
|
COPY gruvbox.css /usr/share/nginx/html/gruvbox.css
|
||||||
COPY nord.css /usr/share/nginx/html/nord.css
|
COPY nord.css /usr/share/nginx/html/nord.css
|
||||||
COPY config.js /usr/share/nginx/html/config.js
|
COPY config.js /usr/share/nginx/html/config.js
|
||||||
|
COPY i18n.js /usr/share/nginx/html/i18n.js
|
||||||
COPY index.html /usr/share/nginx/html/index.html
|
COPY index.html /usr/share/nginx/html/index.html
|
||||||
COPY noise.png /usr/share/nginx/html/noise.png
|
COPY noise.png /usr/share/nginx/html/noise.png
|
||||||
EXPOSE 58881
|
EXPOSE 58881
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/* Internationalization dictionary
|
||||||
|
*
|
||||||
|
* Each key maps to an array of translations indexed by language code.
|
||||||
|
* To add a new language, add an entry with the language code as the key
|
||||||
|
* and translations for each existing key.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* TRANSLATIONS.title = { de: 'Titel', en: 'Title' };
|
||||||
|
*
|
||||||
|
* The current language is taken from window.CONFIG.language.
|
||||||
|
*/
|
||||||
|
|
||||||
|
window.TRANSLATIONS = {
|
||||||
|
'Aufgang': { de: 'Aufgang', en: 'Sunrise' },
|
||||||
|
'Untergang': { de: 'Untergang', en: 'Sunset' },
|
||||||
|
'Zenit Sonne': { de: 'Zenit Sonne', en: 'Sun Zenith' },
|
||||||
|
'Mondphase': { de: 'Mondphase', en: 'Moon Phase' },
|
||||||
|
'Mond auf': { de: 'Mond auf', en: 'Moonrise' },
|
||||||
|
'Mond unter': { de: 'Mond unter', en: 'Moonset' },
|
||||||
|
|
||||||
|
'heute': { de: 'heute', en: 'today' },
|
||||||
|
'morgen': { de: 'morgen', en: 'tomorrow' },
|
||||||
|
|
||||||
|
'Neumond': { de: 'Neumond', en: 'New Moon' },
|
||||||
|
'Zunehm. Sichel': { de: 'Zunehm. Sichel', en: 'Waxing Crescent' },
|
||||||
|
'Erstes Viertel': { de: 'Erstes Viertel', en: 'First Quarter' },
|
||||||
|
'Zunehm. Gibbous': { de: 'Zunehm. Gibbous', en: 'Waxing Gibbous' },
|
||||||
|
'Vollmond': { de: 'Vollmond', en: 'Full Moon' },
|
||||||
|
'Abnehm. Gibbous': { de: 'Abnehm. Gibbous', en: 'Waning Gibbous' },
|
||||||
|
'Letztes Viertel': { de: 'Letztes Viertel', en: 'Last Quarter' },
|
||||||
|
'Abnehm. Sichel': { de: 'Abnehm. Sichel', en: 'Waning Crescent' },
|
||||||
|
|
||||||
|
'Mo.': { de: 'Mo.', en: 'Mo.' },
|
||||||
|
'Di.': { de: 'Di.', en: 'Tu.' },
|
||||||
|
'Mi.': { de: 'Mi.', en: 'We.' },
|
||||||
|
'Do.': { de: 'Do.', en: 'Th.' },
|
||||||
|
'Fr.': { de: 'Fr.', en: 'Fr.' },
|
||||||
|
'Sa.': { de: 'Sa.', en: 'Sa.' },
|
||||||
|
'So.': { de: 'So.', en: 'Su.' }
|
||||||
|
};
|
||||||
|
|
||||||
|
window.getTranslation = function(key) {
|
||||||
|
var lang = (window.CONFIG && window.CONFIG.language) || 'de';
|
||||||
|
var entry = window.TRANSLATIONS[key];
|
||||||
|
if (!entry) return key;
|
||||||
|
return entry[lang] || entry.de || key;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.t = window.getTranslation;
|
||||||
+39
-17
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Vanta Clouds</title>
|
<title>Vanta Clouds</title>
|
||||||
<script src="config.js"></script>
|
<script src="config.js"></script>
|
||||||
|
<script src="i18n.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var theme = (window.CONFIG && window.CONFIG.theme) || 'ash';
|
var theme = (window.CONFIG && window.CONFIG.theme) || 'ash';
|
||||||
@@ -411,37 +412,37 @@
|
|||||||
<div class="info-icon">
|
<div class="info-icon">
|
||||||
<svg viewBox="0 0 24 24" fill="var(--orange)"><circle cx="12" cy="12" r="4"/><g stroke="var(--orange)" stroke-width="2" stroke-linecap="round"><line x1="12" y1="2" x2="12" y2="5"/><line x1="12" y1="19" x2="12" y2="22"/><line x1="2" y1="12" x2="5" y2="12"/><line x1="19" y1="12" x2="22" y2="12"/><line x1="4.9" y1="4.9" x2="6.9" y2="6.9"/><line x1="17.1" y1="17.1" x2="19.1" y2="19.1"/><line x1="4.9" y1="19.1" x2="6.9" y2="17.1"/><line x1="17.1" y1="6.9" x2="19.1" y2="4.9"/></g></svg>
|
<svg viewBox="0 0 24 24" fill="var(--orange)"><circle cx="12" cy="12" r="4"/><g stroke="var(--orange)" stroke-width="2" stroke-linecap="round"><line x1="12" y1="2" x2="12" y2="5"/><line x1="12" y1="19" x2="12" y2="22"/><line x1="2" y1="12" x2="5" y2="12"/><line x1="19" y1="12" x2="22" y2="12"/><line x1="4.9" y1="4.9" x2="6.9" y2="6.9"/><line x1="17.1" y1="17.1" x2="19.1" y2="19.1"/><line x1="4.9" y1="19.1" x2="6.9" y2="17.1"/><line x1="17.1" y1="6.9" x2="19.1" y2="4.9"/></g></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-content"><span class="info-label">Aufgang</span><span><span class="info-value" id="sunrise-value">--:--</span><span class="info-sub" id="sunrise-when"></span></span></div>
|
<div class="info-content"><span class="info-label" data-i18n="Aufgang">Aufgang</span><span><span class="info-value" id="sunrise-value">--:--</span><span class="info-sub" id="sunrise-when"></span></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<div class="info-icon">
|
<div class="info-icon">
|
||||||
<svg viewBox="0 0 24 24" fill="var(--orange)"><circle cx="12" cy="12" r="4"/><line x1="12" y1="12" x2="12" y2="3" stroke="var(--orange)" stroke-width="2"/></svg>
|
<svg viewBox="0 0 24 24" fill="var(--orange)"><circle cx="12" cy="12" r="4"/><line x1="12" y1="12" x2="12" y2="3" stroke="var(--orange)" stroke-width="2"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-content"><span class="info-label">Zenit Sonne</span><span><span class="info-value" id="sun-zenith-value">--:--</span><span class="info-sub" id="sun-zenith-when"></span></span></div>
|
<div class="info-content"><span class="info-label" data-i18n="Zenit Sonne">Zenit Sonne</span><span><span class="info-value" id="sun-zenith-value">--:--</span><span class="info-sub" id="sun-zenith-when"></span></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<div class="info-icon">
|
<div class="info-icon">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="var(--orange)" stroke-width="2"><path d="M3 12 A9 9 0 0 1 21 12 Z"/></svg>
|
<svg viewBox="0 0 24 24" fill="none" stroke="var(--orange)" stroke-width="2"><path d="M3 12 A9 9 0 0 1 21 12 Z"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-content"><span class="info-label">Untergang</span><span><span class="info-value" id="sunset-value">--:--</span><span class="info-sub" id="sunset-when"></span></span></div>
|
<div class="info-content"><span class="info-label" data-i18n="Untergang">Untergang</span><span><span class="info-value" id="sunset-value">--:--</span><span class="info-sub" id="sunset-when"></span></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<div class="info-icon">
|
<div class="info-icon">
|
||||||
<svg id="moon-phase-icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="var(--dark)"/></svg>
|
<svg id="moon-phase-icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="var(--dark)"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-content"><span class="info-label">Mondphase</span><span class="info-value" id="moon-phase-text">--</span></div>
|
<div class="info-content"><span class="info-label" data-i18n="Mondphase">Mondphase</span><span class="info-value" id="moon-phase-text">--</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<div class="info-icon">
|
<div class="info-icon">
|
||||||
<svg viewBox="0 0 24 24" fill="var(--icon)"><path d="M12 2 a10 10 0 1 0 0 20 a8 8 0 1 1 0 -16 a6 6 0 1 1 0 8"/></svg>
|
<svg viewBox="0 0 24 24" fill="var(--icon)"><path d="M12 2 a10 10 0 1 0 0 20 a8 8 0 1 1 0 -16 a6 6 0 1 1 0 8"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-content"><span class="info-label">Mond auf</span><span><span class="info-value" id="moonrise-value">--:--</span><span class="info-sub" id="moonrise-when"></span></span></div>
|
<div class="info-content"><span class="info-label" data-i18n="Mond auf">Mond auf</span><span><span class="info-value" id="moonrise-value">--:--</span><span class="info-sub" id="moonrise-when"></span></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<div class="info-icon">
|
<div class="info-icon">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="var(--icon)" stroke-width="2"><path d="M3 12 A9 9 0 0 1 21 22 Z"/></svg>
|
<svg viewBox="0 0 24 24" fill="none" stroke="var(--icon)" stroke-width="2"><path d="M3 12 A9 9 0 0 1 21 22 Z"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-content"><span class="info-label">Mond unter</span><span><span class="info-value" id="moonset-value">--:--</span><span class="info-sub" id="moonset-when"></span></span></div>
|
<div class="info-content"><span class="info-label" data-i18n="Mond unter">Mond unter</span><span><span class="info-value" id="moonset-value">--:--</span><span class="info-sub" id="moonset-when"></span></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="weather-divider" class="weather-divider"></div>
|
<div id="weather-divider" class="weather-divider"></div>
|
||||||
<div id="weather-current" class="weather-current"></div>
|
<div id="weather-current" class="weather-current"></div>
|
||||||
@@ -833,7 +834,7 @@ var effect = (VANTA.CLOUDS2 || VANTA.CLOUDS)({
|
|||||||
var times = type.indexOf('moon') === 0 ? SunCalc.getMoonTimes(nextDate, lat, lon) : SunCalc.getTimes(nextDate, lat, lon);
|
var times = type.indexOf('moon') === 0 ? SunCalc.getMoonTimes(nextDate, lat, lon) : SunCalc.getTimes(nextDate, lat, lon);
|
||||||
var ev = type === 'moonrise' ? times.rise : type === 'moonset' ? times.set : times[type];
|
var ev = type === 'moonrise' ? times.rise : type === 'moonset' ? times.set : times[type];
|
||||||
if (ev && !isNaN(ev.getTime())) {
|
if (ev && !isNaN(ev.getTime())) {
|
||||||
var dayLabel = d === 1 ? 'morgen' : formatDateLong(nextDate);
|
var dayLabel = d === 1 ? t('morgen') : formatDateLong(nextDate);
|
||||||
return { date: ev, label: dayLabel };
|
return { date: ev, label: dayLabel };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -842,14 +843,18 @@ var effect = (VANTA.CLOUDS2 || VANTA.CLOUDS)({
|
|||||||
|
|
||||||
function getMoonPhaseText(phase) {
|
function getMoonPhaseText(phase) {
|
||||||
var p = phase;
|
var p = phase;
|
||||||
if (p < 0.0625 || p >= 0.9375) return 'Neumond';
|
var keys = ['Neumond', 'Zunehm. Sichel', 'Erstes Viertel', 'Zunehm. Gibbous',
|
||||||
if (p < 0.1875) return 'Zunehm. Sichel';
|
'Vollmond', 'Abnehm. Gibbous', 'Letztes Viertel', 'Abnehm. Sichel'];
|
||||||
if (p < 0.3125) return 'Erstes Viertel';
|
var idx;
|
||||||
if (p < 0.4375) return 'Zunehm. Gibbous';
|
if (p < 0.0625 || p >= 0.9375) idx = 0;
|
||||||
if (p < 0.5625) return 'Vollmond';
|
else if (p < 0.1875) idx = 1;
|
||||||
if (p < 0.6875) return 'Abnehm. Gibbous';
|
else if (p < 0.3125) idx = 2;
|
||||||
if (p < 0.8125) return 'Letztes Viertel';
|
else if (p < 0.4375) idx = 3;
|
||||||
return 'Abnehm. Sichel';
|
else if (p < 0.5625) idx = 4;
|
||||||
|
else if (p < 0.6875) idx = 5;
|
||||||
|
else if (p < 0.8125) idx = 6;
|
||||||
|
else idx = 7;
|
||||||
|
return t(keys[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var CENTER_X = 140;
|
var CENTER_X = 140;
|
||||||
@@ -1276,6 +1281,21 @@ window.weatherThemes = {
|
|||||||
{ value: 'en', label: 'EN' }
|
{ value: 'en', label: 'EN' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function applyI18n() {
|
||||||
|
var els = document.querySelectorAll('[data-i18n]');
|
||||||
|
for (var i = 0; i < els.length; i++) {
|
||||||
|
var key = els[i].getAttribute('data-i18n');
|
||||||
|
els[i].textContent = t(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDebugLanguage(lang) {
|
||||||
|
window.CONFIG.language = lang;
|
||||||
|
applyI18n();
|
||||||
|
// Trigger weather refresh to update day labels
|
||||||
|
if (typeof fetchWeather === 'function') fetchWeather();
|
||||||
|
}
|
||||||
|
|
||||||
function populateDebugSelects() {
|
function populateDebugSelects() {
|
||||||
// Theme select (from window.THEMES)
|
// Theme select (from window.THEMES)
|
||||||
var themeSelect = document.getElementById('dbg-theme-select');
|
var themeSelect = document.getElementById('dbg-theme-select');
|
||||||
@@ -1579,9 +1599,10 @@ window.weatherThemes = {
|
|||||||
var dayNames = ['Heute', 'Morgen'];
|
var dayNames = ['Heute', 'Morgen'];
|
||||||
for (var d = 0; d < 4; d++) {
|
for (var d = 0; d < 4; d++) {
|
||||||
var dayDate = new Date(daily.time[d]);
|
var dayDate = new Date(daily.time[d]);
|
||||||
var dayLabel = d === 0 ? 'Heute' : dayNames[1] || dayDate.toLocaleDateString('de-DE', { weekday: 'short' });
|
var dayLabel = d === 0 ? t('heute') : t('morgen');
|
||||||
if (d > 1) {
|
if (d > 1) {
|
||||||
dayLabel = dayDate.toLocaleDateString('de-DE', { weekday: 'short' }) + ' ' +
|
var weekdayKey = ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'][dayDate.getDay()];
|
||||||
|
dayLabel = t(weekdayKey) + ' ' +
|
||||||
(dayDate.getDate() < 10 ? '0' : '') + dayDate.getDate() + '.' +
|
(dayDate.getDate() < 10 ? '0' : '') + dayDate.getDate() + '.' +
|
||||||
(dayDate.getMonth() + 1 < 10 ? '0' : '') + (dayDate.getMonth() + 1) + '.';
|
(dayDate.getMonth() + 1 < 10 ? '0' : '') + (dayDate.getMonth() + 1) + '.';
|
||||||
}
|
}
|
||||||
@@ -1620,6 +1641,7 @@ window.weatherThemes = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update weather every 15 minutes
|
// Update weather every 15 minutes
|
||||||
|
applyI18n();
|
||||||
populateDebugSelects();
|
populateDebugSelects();
|
||||||
animate();
|
animate();
|
||||||
tick();
|
tick();
|
||||||
|
|||||||
Reference in new issue
Block a user