mirror of
https://github.com/CNCKitchen/stlTexturizer.git
synced 2026-04-07 22:11:32 +00:00
feat: dynamic language loading and dropdown list
This commit is contained in:
@@ -34,9 +34,6 @@
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<div class="lang-seg">
|
||||
<button class="lang-btn active" data-lang-code="en">EN</button>
|
||||
<button class="lang-btn" data-lang-code="de">DE</button>
|
||||
<button class="lang-btn" data-lang-code="it">IT</button>
|
||||
</div>
|
||||
<button id="theme-toggle" class="theme-toggle"
|
||||
data-i18n-title="theme.toggleTitle"
|
||||
|
||||
+39
-21
@@ -11,7 +11,7 @@ import { decimate } from './decimation.js';
|
||||
import { exportSTL } from './exporter.js';
|
||||
import { buildAdjacency, bucketFill,
|
||||
buildExclusionOverlayGeo, buildFaceWeights } from './exclusion.js';
|
||||
import { t, initLang, setLang, getLang, applyTranslations } from './i18n.js';
|
||||
import { t, initLang, setLang, getLang, applyTranslations, TRANSLATIONS } from './i18n.js';
|
||||
|
||||
// ── State ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -239,6 +239,9 @@ const imprintLink = document.getElementById('imprint-link');
|
||||
const imprintOverlay = document.getElementById('imprint-overlay');
|
||||
const imprintClose = document.getElementById('imprint-close');
|
||||
|
||||
// ── Language selector DOM refs ────────────────────────────────────────────────────
|
||||
const languageSelector = document.querySelector('.lang-seg');
|
||||
|
||||
// ── Scale slider log helpers ──────────────────────────────────────────────────
|
||||
// Slider stores 0–1000; actual scale spans 0.05–10 on a log axis.
|
||||
// Middle position 500 → scale ~0.71 (log midpoint between 0.05 and 10).
|
||||
@@ -265,15 +268,46 @@ initViewer(canvas);
|
||||
// Apply saved theme to 3D viewport on startup
|
||||
setViewerTheme(document.documentElement.getAttribute('data-theme') === 'light');
|
||||
|
||||
// Populate the language selector
|
||||
function populateLanguageSelector() {
|
||||
if (!languageSelector) return;
|
||||
languageSelector.innerHTML = '';
|
||||
|
||||
const select = document.createElement('select');
|
||||
select.className = 'lang-dropdown';
|
||||
|
||||
for (const langKey in TRANSLATIONS) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = langKey;
|
||||
opt.className = 'lang-option';
|
||||
opt.textContent = TRANSLATIONS[langKey]['lang.name'] || langKey.toUpperCase();
|
||||
select.appendChild(opt);
|
||||
}
|
||||
|
||||
select.addEventListener('change', (e) => {
|
||||
setLang(e.target.value);
|
||||
// Re-translate <option> elements (innerHTML won't reach these)
|
||||
document.querySelectorAll('select[id="mapping-mode"] option[data-i18n-opt]').forEach(opt => {
|
||||
opt.textContent = t(opt.dataset.i18nOpt);
|
||||
});
|
||||
// Refresh dynamic count text to current language
|
||||
if (currentGeometry) refreshExclusionOverlay();
|
||||
});
|
||||
|
||||
languageSelector.appendChild(select);
|
||||
}
|
||||
populateLanguageSelector();
|
||||
|
||||
// Initialise language (reads localStorage / browser preference, applies translations)
|
||||
initLang();
|
||||
|
||||
// Sync lang buttons to current language
|
||||
// Sync lang dropdown to current language
|
||||
(function() {
|
||||
const lang = getLang();
|
||||
document.querySelectorAll('.lang-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.langCode === lang);
|
||||
});
|
||||
const select = languageSelector.querySelector('select');
|
||||
if (select) {
|
||||
select.value = lang;
|
||||
}
|
||||
})();
|
||||
|
||||
// Theme toggle
|
||||
@@ -340,22 +374,6 @@ function selectPreset(idx, swatchEl) {
|
||||
// ── Event wiring ──────────────────────────────────────────────────────────────
|
||||
|
||||
function wireEvents() {
|
||||
// ── Language toggle ──
|
||||
document.querySelectorAll('.lang-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const lang = btn.dataset.langCode;
|
||||
setLang(lang);
|
||||
document.querySelectorAll('.lang-btn').forEach(b =>
|
||||
b.classList.toggle('active', b.dataset.langCode === lang));
|
||||
// Re-translate <option> elements (innerHTML won't reach these)
|
||||
document.querySelectorAll('select[id="mapping-mode"] option[data-i18n-opt]').forEach(opt => {
|
||||
opt.textContent = t(opt.dataset.i18nOpt);
|
||||
});
|
||||
// Refresh dynamic count text to current language
|
||||
if (currentGeometry) refreshExclusionOverlay();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Model loading ──
|
||||
stlFileInput.addEventListener('change', (e) => {
|
||||
if (e.target.files[0]) handleModelFile(e.target.files[0]);
|
||||
|
||||
@@ -47,31 +47,31 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.lang-btn {
|
||||
.lang-dropdown {
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
padding: 0 24px 0 10px; /* Add right padding for default select arrow if present */
|
||||
background: var(--surface2);
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
cursor: pointer;
|
||||
appearance: none; /* Attempt to remove default styling */
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath fill='%2366667a' d='M0 0l4 4 4-4H0z'/%3E%3C/svg%3E"); /* Custom arrow */
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 8px center;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.lang-btn:not(:last-child) {
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.lang-btn:hover {
|
||||
.lang-dropdown:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.lang-btn.active {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
.lang-dropdown:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* ── Theme toggle button ─────────────────────────────────────────────── */
|
||||
|
||||
Reference in New Issue
Block a user