Skip to main content

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

KeyUsage
langLabelLabel before the EN / RU buttons
timeLabelLabel before the time slider
lightsOffHeadlights button label when lights are off
lightsOnHeadlights button label when lights are on
exhaustOffExhaust button label when exhaust is off
exhaustOnExhaust button label when exhaust is on
musicOffMusic button label when music is stopped
musicOnMusic button label when music is playing
addTrack”Add” button label
placeholderPlaceholder text for the custom URL input
trackPrefixPrefix for preset track labels in the <select> (e.g. “Track 1”)
customTrackPrefixPrefix for user-added track labels (e.g. “Custom track:“)
alertNoTrackAlert when Play is pressed but no track is selected
alertInvalidUrlAlert when the custom URL does not end with .mp3
alertTrackAddedAlert confirming a custom track was added
alertPlayErrorAlert when the browser fails to play the audio
alertEnterUrlAlert 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.
let currentLang = 'en';

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.
updateUI();
Adding a third language is a four-step process:
  1. Add a new key to translations with all 17 message strings translated — e.g. translations.de = { langLabel: 'Sprache:', … }.
  2. Add a button to the #langRow div in the HTML: <button id="langDe" class="lang-btn">DE</button>.
  3. Add a DOM reference: const langDeBtn = document.getElementById('langDe');.
  4. 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.

Build docs developers (and LLMs) love