Docs/Translations/Tour content translations

Tour content translations

Creating and structuring translated tour files

This guide covers how to create translated versions of your tour. We'll use Barcelona as an example, adding English, Spanish, and German versions.

File structure

Each language gets its own JSON file:

src/data/tour/
├── metadata.json     # Shared settings (no translation needed)
├── en.json           # English content
├── es.json           # Spanish content
└── de.json           # German content

The golden rules

Same tour ID

All language files must have the same id value as metadata.json

Same stop IDs

Stop IDs must match across all languages (stop "3" = stop "3")

Same number of stops

All languages should have the same stops in the same order

Unique Language Code

Each file needs a different language value

Step-by-step example

1. Start with English (en.json)

{
  "id": "barcelona",
  "language": "en",
  "title": "Unlimited Barcelona",
  "description": "Discover the rich history and hidden gems of Barcelona's Gothic Quarter.",
  "totalDuration": "45 mins",
  "totalStops": 3,
  "stops": [
    {
      "id": "1",
      "type": "audio",
      "title": "Welcome to Barcelona",
      "duration": "3 min audio",
      "image": "https://images.unsplash.com/photo-xxx",
      "audioFile": "https://storage.example.com/barcelona/en/01-welcome.mp3",
      "transcription": "Welcome to Barcelona! Today we'll explore the Gothic Quarter..."
    },
    {
      "id": "2",
      "type": "audio",
      "title": "The Cathedral",
      "duration": "5 min audio",
      "image": "https://images.unsplash.com/photo-yyy",
      "audioFile": "https://storage.example.com/barcelona/en/02-cathedral.mp3"
    },
    {
      "id": "3",
      "type": "audio",
      "title": "La Rambla",
      "duration": "4 min audio",
      "image": "https://images.unsplash.com/photo-zzz",
      "audioFile": "https://storage.example.com/barcelona/en/03-rambla.mp3"
    }
  ]
}

2. Create Spanish (es.json)

{
  "id": "barcelona",
  "language": "es",
  "title": "Barcelona Sin Límites",
  "description": "Descubre la rica historia y los tesoros ocultos del Barrio Gótico de Barcelona.",
  "totalDuration": "45 mins",
  "totalStops": 3,
  "stops": [
    {
      "id": "1",
      "type": "audio",
      "title": "Bienvenido a Barcelona",
      "duration": "3 min audio",
      "image": "https://images.unsplash.com/photo-xxx",
      "audioFile": "https://storage.example.com/barcelona/es/01-welcome.mp3",
      "transcription": "¡Bienvenido a Barcelona! Hoy exploraremos el Barrio Gótico..."
    },
    {
      "id": "2",
      "type": "audio",
      "title": "La Catedral",
      "duration": "5 min audio",
      "image": "https://images.unsplash.com/photo-yyy",
      "audioFile": "https://storage.example.com/barcelona/es/02-cathedral.mp3"
    },
    {
      "id": "3",
      "type": "audio",
      "title": "La Rambla",
      "duration": "4 min audio",
      "image": "https://images.unsplash.com/photo-zzz",
      "audioFile": "https://storage.example.com/barcelona/es/03-rambla.mp3"
    }
  ]
}

3. Create German (de.json)

{
  "id": "barcelona",
  "language": "de",
  "title": "Barcelona Grenzenlos",
  "description": "Entdecken Sie die reiche Geschichte und verborgenen Schätze des Gotischen Viertels von Barcelona.",
  "totalDuration": "45 mins",
  "totalStops": 3,
  "stops": [
    {
      "id": "1",
      "type": "audio",
      "title": "Willkommen in Barcelona",
      "duration": "3 min audio",
      "image": "https://images.unsplash.com/photo-xxx",
      "audioFile": "https://storage.example.com/barcelona/de/01-welcome.mp3",
      "transcription": "Willkommen in Barcelona! Heute erkunden wir das Gotische Viertel..."
    },
    {
      "id": "2",
      "type": "audio",
      "title": "Die Kathedrale",
      "duration": "5 min audio",
      "image": "https://images.unsplash.com/photo-yyy",
      "audioFile": "https://storage.example.com/barcelona/de/02-cathedral.mp3"
    },
    {
      "id": "3",
      "type": "audio",
      "title": "La Rambla",
      "duration": "4 min audio",
      "image": "https://images.unsplash.com/photo-zzz",
      "audioFile": "https://storage.example.com/barcelona/de/03-rambla.mp3"
    }
  ]
}

What to translate

FieldTranslate?Notes
titleYesTour title
descriptionYesTour description
totalDurationMaybe"45 mins" vs "45 minutos" vs "45 Minuten"
stops[].titleYesStop titles
stops[].durationMaybe"3 min audio" might need translation
stops[].transcriptionYesIf using transcriptions
stops[].audioFileDifferent URLPoints to language-specific audio

What NOT to translate

FieldKeep SameWhy
idSame across allLinks language versions together
stops[].idSame across allEnables position preservation
stops[].typeSame across allMust match
stops[].imageUsually sameImages don't need translation

Audio file organization

Organize your audio files by language:

your-storage.com/barcelona/
├── en/
│   ├── 01-welcome.mp3
│   ├── 02-cathedral.mp3
│   └── 03-rambla.mp3
├── es/
│   ├── 01-welcome.mp3
│   ├── 02-cathedral.mp3
│   └── 03-rambla.mp3
└── de/
    ├── 01-welcome.mp3
    ├── 02-cathedral.mp3
    └── 03-rambla.mp3

This keeps things organized and makes the URL pattern predictable.

Per-language overrides

You can override metadata properties in individual language files:

{
  "id": "barcelona",
  "language": "es",
  "title": "Barcelona Sin Límites",
  "themeId": "warm-theme"
}

This Spanish version would use "warm-theme" while other languages use whatever's in metadata.json.

This is useful for language-specific branding or when a different color scheme works better for certain markets.

Common mistakes

Different stop ids

// en.json
{ "id": "1", "title": "Welcome" }
 
// es.json - WRONG!
{ "id": "welcome", "title": "Bienvenido" }

Fix: Use "id": "1" in both files.

Missing stops

// en.json has 5 stops
// es.json only has 4 stops - WRONG!

Fix: All languages must have all stops translated.

Different tour ids

// en.json
{ "id": "barcelona-en" }
 
// es.json - WRONG!
{ "id": "barcelona-es" }

Fix: Use "id": "barcelona" in all files.

Testing translations

  1. Run bun run dev
  2. Open the tour
  3. Click the language flag
  4. Switch between languages
  5. Verify:
    • Content changes
    • Position is preserved if mid-stop
    • No console errors
    • All images load