Documentation Index
Fetch the complete documentation index at: https://mintlify.com/JustADev1024/threejs-car/llms.txt
Use this file to discover all available pages before exploring further.
Dream Car ships with a lightweight, hand-rolled internationalization system. All user-visible strings are stored in a single translations object keyed first by language code ('en' or 'ru'), then by message key. Switching languages calls setLanguage(), which flips a module-level variable and immediately re-renders every label, button, and placeholder in the UI — no page reload, no external i18n library, and no asset fetches.
The translations Object
Every translatable string in the application lives here. The keys are shared across both languages; the values differ.
const translations = {
en: {
langLabel: 'Language:',
timeLabel: 'Time of day',
lightsOff: '💡 Headlights Off',
lightsOn: '💡 Headlights On',
exhaustOff: '💨 Exhaust Off',
exhaustOn: '💨 Exhaust On',
musicOff: '🎵 Play',
musicOn: '🔇 Stop',
addTrack: '➕ Add',
placeholder: '➕ Direct MP3 link (from Newgrounds or other site)',
trackPrefix: 'Track',
customTrackPrefix: 'Custom track:',
alertNoTrack: 'No tracks available. Add your own MP3 link.',
alertInvalidUrl: 'Link must point directly to an MP3 file (usually ends with .mp3)',
alertTrackAdded: 'Track added to list. Select it and press "Play".',
alertPlayError: 'Failed to play track. Check the link (must be direct .mp3) and CORS availability.',
alertEnterUrl: 'Enter a link to an MP3 file',
},
ru: {
langLabel: 'Язык:',
timeLabel: 'Время суток',
lightsOff: '💡 Фары Выкл',
lightsOn: '💡 Фары Вкл',
exhaustOff: '💨 Выхлоп Выкл',
exhaustOn: '💨 Выхлоп Вкл',
musicOff: '🎵 Вкл',
musicOn: '🔇 Выкл',
addTrack: '➕ Добавить',
placeholder: '➕ Прямая ссылка на MP3 (с Newgrounds или другого сайта)',
trackPrefix: 'Трек',
customTrackPrefix: 'Свой трек:',
alertNoTrack: 'Нет доступных треков. Добавьте свою MP3-ссылку.',
alertInvalidUrl: 'Ссылка должна вести напрямую на MP3 файл (обычно заканчивается на .mp3)',
alertTrackAdded: 'Трек добавлен в список. Выберите его и нажмите "Вкл".',
alertPlayError: 'Не удалось воспроизвести трек. Проверьте ссылку (должна быть прямой на .mp3) и доступность CORS.',
alertEnterUrl: 'Введите ссылку на MP3 файл',
}
};
Translation Key Reference
| Key | Usage |
|---|
langLabel | Label before the EN / RU buttons |
timeLabel | Label before the time slider |
lightsOff | Headlights button label when lights are off |
lightsOn | Headlights button label when lights are on |
exhaustOff | Exhaust button label when exhaust is off |
exhaustOn | Exhaust button label when exhaust is on |
musicOff | Music button label when music is stopped |
musicOn | Music button label when music is playing |
addTrack | ”Add” button label |
placeholder | Placeholder text for the custom URL input |
trackPrefix | Prefix for preset track labels in the <select> (e.g. “Track 1”) |
customTrackPrefix | Prefix for user-added track labels (e.g. “Custom track:“) |
alertNoTrack | Alert when Play is pressed but no track is selected |
alertInvalidUrl | Alert when the custom URL does not end with .mp3 |
alertTrackAdded | Alert confirming a custom track was added |
alertPlayError | Alert when the browser fails to play the audio |
alertEnterUrl | Alert when Add is clicked with an empty URL field |
currentLang State
A single module-level variable tracks the active language. It is initialised to 'en' on page load.
t(key) Helper
All translatable strings are retrieved through the t() helper function. It looks up the current language in translations and falls back to the raw key string if a translation is missing — handy during development when a key exists in one language but not yet in another.
function t(key) {
return translations[currentLang][key] || key;
}
setLanguage(lang)
The public entry point for changing the active language. Guards against no-op calls (clicking the already-active language button), then updates currentLang and calls updateUI().
function setLanguage(lang) {
if (lang === currentLang) return;
currentLang = lang;
updateUI();
}
langEnBtn.addEventListener('click', () => setLanguage('en'));
langRuBtn.addEventListener('click', () => setLanguage('ru'));
updateUI()
Re-renders every affected DOM node synchronously. Static text nodes are updated directly; stateful buttons delegate to their own helper functions so they always reflect the current toggle state in the new language.
function updateUI() {
// Static labels
langLabel.textContent = t('langLabel');
timeLabel.textContent = t('timeLabel');
customUrlInput.placeholder = t('placeholder');
addTrackBtn.textContent = t('addTrack');
// Stateful buttons (reflect current on/off state in new language)
updateLightsButton();
updateExhaustButton();
updateMusicButton();
// Rebuild track list labels
buildTrackOptions();
// Highlight the active language button
langEnBtn.classList.toggle('active', currentLang === 'en');
langRuBtn.classList.toggle('active', currentLang === 'ru');
}
buildTrackOptions()
Regenerates all <option> elements in #trackSelect using translated prefixes. Preset tracks receive labels like “Track 1” / “Трек 1”; custom tracks added by the user receive labels like “Custom track: https://…” / “Свой трек: https://…”. The previously selected URL is preserved across rebuilds.
function buildTrackOptions() {
const currentVal = trackSelect.value;
trackSelect.innerHTML = '';
trackList.forEach((item, idx) => {
const opt = document.createElement('option');
opt.value = item.url;
let label;
if (item.isCustom) {
label = t('customTrackPrefix') + ' ' +
(item.customLabel || item.url.substring(0, 30) + '…');
} else {
label = t('trackPrefix') + ' ' + (item.index + 1);
}
opt.textContent = label;
trackSelect.appendChild(opt);
});
// Restore previous selection if still valid
if (currentVal && trackList.some(t => t.url === currentVal)) {
trackSelect.value = currentVal;
} else if (trackList.length > 0) {
trackSelect.value = trackList[0].url;
}
}
Initialisation
updateUI() is called once at the bottom of the script, after all DOM references and event listeners are set up, to apply the default 'en' translations on first render.
Adding a third language is a four-step process:
- Add a new key to
translations with all 17 message strings translated — e.g. translations.de = { langLabel: 'Sprache:', … }.
- Add a button to the
#langRow div in the HTML: <button id="langDe" class="lang-btn">DE</button>.
- Add a DOM reference:
const langDeBtn = document.getElementById('langDe');.
- Wire the click handler:
langDeBtn.addEventListener('click', () => setLanguage('de')); and extend updateUI() to include langDeBtn.classList.toggle('active', currentLang === 'de').
No other changes are needed — t(), setLanguage(), and updateUI() are all language-agnostic once the translation entry exists.