Tours are automatically discovered by language at build time using Vite's import.meta.glob. Available languages are derived from the language field in tour JSON files — no separate config needed.
src/data/tour/
├── metadata.json # Shared properties
├── en.json # "language": "en"
├── cs.json # "language": "cs"
└── de.json # "language": "de"
The language field in each JSON determines the language, not the filename.
Set the default language in metadata.json:
{
"id": "barcelona",
"defaultLanguage": "en"
}Used when the user's browser language is unavailable or no preference is saved.
?lang=URL parameter (e.g.yoursite.com/tour/barcelona?lang=de)- Saved user preference (localStorage
app-preferences) - Browser/device language (
navigator.language) defaultLanguagefrom metadata- First available language
-
- Create
src/data/tour/{code}.jsonwith the sameidas other language files
- Create
-
- Set
"language": "{code}"in the file
- Set
-
- Use matching stop
idvalues across all language files
- Use matching stop
-
- Translate all text content, update
audioFileURLs
- Translate all text content, update
-
- (Optional) Add display name and flag to
languageMetadatainsrc/services/tourDiscovery.ts
- (Optional) Add display name and flag to
-
- (Optional) Add UI translation file to
src/translations/locales/{code}.ts
- (Optional) Add UI translation file to
-
- Verify language appears in language picker and content loads correctly
If step 5 is skipped, the language code is used as the display name with a generic flag. If step 6 is skipped, the UI falls back to the default language.
Display name, flag, and country code are defined in src/services/tourDiscovery.ts:
const languageMetadata: Record<string, { name: string; flag: string; countryCode: string }> = {
en: { name: 'English', flag: '🇬🇧', countryCode: 'GB' },
cs: { name: 'Čeština', flag: '🇨🇿', countryCode: 'CZ' },
de: { name: 'Deutsch', flag: '🇩🇪', countryCode: 'DE' },
fr: { name: 'Français', flag: '🇫🇷', countryCode: 'FR' },
it: { name: 'Italiano', flag: '🇮🇹', countryCode: 'IT' },
es: { name: 'Español', flag: '🇪🇸', countryCode: 'ES' },
};countryCode is used for SVG flag icons from the country-flag-icons package.
Progress is tracked per tour id, not per language. Completed stops and playback position are preserved when switching languages. When the user changes language:
- Current audio stops immediately
- Current stop and position are saved
- Tour reloads in the new language
- Audio is paused at the same position, ready to play
Language preference is stored under key app-preferences:
{ "selectedLanguage": "cs" }All language files for the same tour must have:
- The same
id - The same stop
idvalues - The same number of stops (
totalStops)
Language not in picker — Verify language field is lowercase and the dev server has been restarted.
Tour doesn't load — Verify file exists at src/data/tour/{code}.json with valid id and language fields.
Progress not preserved — Verify all language files share the same tour id and stop id values.