# How to build a multilingual app in Bubble.io: Step-by-Step Guide

- Tool: Bubble
- Difficulty: Beginner
- Time required: 25-30 min
- Compatibility: All Bubble plans
- Last updated: March 2026

## TL;DR

Building a multilingual app in Bubble involves using Option Sets for static text translations, translating dynamic database content with a Translation Data Type, implementing a language switcher that persists across sessions, and structuring your app so adding new languages requires minimal rework. This tutorial covers the complete multilingual architecture from data design through UI implementation.

## Overview: Building a Multilingual App in Bubble

This tutorial shows how to build app-level multilingual support in Bubble. You will create a translation system for both static UI text and dynamic database content, with a language switcher that remembers user preferences.

## Before you start

- A Bubble app with content to translate
- An understanding of Bubble Option Sets and Data Types
- The translated text for at least two languages
- Basic familiarity with Bubble custom states and conditions

## Step-by-step guide

### 1. Set up the language infrastructure

Create an Option Set called Language with options: English, Spanish, French (or your target languages). Each option has attributes: code (text — 'en', 'es', 'fr'), name (text — 'English', 'Español', 'Français'), flag_emoji (text — optional). Add a preferred_language field (type: Language Option Set) to the User Data Type, defaulting to English. Create a page-level custom state called 'active_language' (type: Language) that loads from Current User's preferred_language on page load.

**Expected result:** The language infrastructure is in place with Language Option Set and user preference tracking.

### 2. Create translations for static UI text using Option Sets

Create an Option Set called UIText with an option for each static text element in your app (e.g., btn_login, nav_home, label_email, msg_welcome). Add an attribute for each language: en_text, es_text, fr_text. Populate each option with the translated strings. In your UI, instead of typing static text, use expressions: Get an option of UIText where Display = 'btn_login's [active_language code]_text. This approach loads instantly (Option Sets are cached) and costs zero workload units.

> Pro tip: Use Bubble's built-in App Text / Translation feature (Settings → Languages tab) as an alternative. It provides a dedicated translation interface that may be simpler for apps with many text strings.

**Expected result:** All static UI text dynamically switches between languages using Option Set lookups.

### 3. Handle dynamic database content translations

For user-generated or admin-managed content that needs translation (product descriptions, blog posts), create a Translation Data Type with fields: content_type (text), content_id (text), language (Language), field_name (text), translated_text (text). When displaying dynamic content, search for a Translation matching the content ID, field name, and active language. If found, show the translated text. If not found, fall back to the original language. For admin content, build a translation editor where admins enter translations for each language.

**Expected result:** Dynamic database content displays in the user's selected language with fallback to the default.

### 4. Build the language switcher

Add a Dropdown or clickable flag icons in your header (reusable element). The Dropdown data source is All Languages from the Option Set. When the user selects a language, the workflow: (1) Sets the page's active_language state. (2) If the user is logged in, makes changes to Current User's preferred_language. This persists their choice across sessions. All text elements and content displays on the page update immediately since they reference the active_language state. For non-logged-in users, store the preference in a cookie or local storage via a plugin.

**Expected result:** Users can switch languages from a header dropdown, with their preference saved for future sessions.

## Complete code example

File: `Workflow summary`

```text
MULTILINGUAL APP ARCHITECTURE
===============================

LANGUAGE OPTION SET:
  English: code='en', name='English'
  Spanish: code='es', name='Español'
  French: code='fr', name='Français'

STATIC TEXT (Option Set approach):
  UIText Option Set:
    btn_login: en_text='Log In', es_text='Iniciar Sesión', fr_text='Connexion'
    nav_home: en_text='Home', es_text='Inicio', fr_text='Accueil'
    msg_welcome: en_text='Welcome!', es_text='¡Bienvenido!', fr_text='Bienvenue!'

  Usage in elements:
    Text = UIText btn_login's [active_language's code]_text

DYNAMIC CONTENT (Data Type approach):
  Translation:
    content_type, content_id, language, field_name, translated_text

  Usage:
    Search Translations where content_id = item's ID
      AND language = active_language AND field_name = 'description'
    If found: show translated_text
    If not: show original content

LANGUAGE SWITCHER:
  Header dropdown: All Languages
  On change:
    1. Set page state active_language = selected
    2. If logged in: Update User preferred_language
    3. If not logged in: Save to cookie/local storage

PAGE LOAD:
  Set active_language = Current User's preferred_language
  Fallback: English (default)
```

## Common mistakes

- **Hardcoding text in UI elements instead of using a translation system** — Hardcoded text requires finding and changing every element when adding a new language, which is error-prone in apps with hundreds of text elements Fix: Use Option Sets for static text or Bubble's built-in Translation feature so all text is managed centrally
- **Creating separate pages for each language** — Duplicating pages per language doubles or triples your maintenance burden — every change must be made on every language version Fix: Use a single set of pages with dynamic text that changes based on the active language selection
- **Not providing a fallback when a translation is missing** — If a translation record does not exist for a specific language and content, the user sees empty text Fix: Always fall back to the default language (English) when a translation is not found

## Best practices

- Use Option Sets for static UI text — they load instantly with zero WU cost
- Use a Translation Data Type for dynamic content that needs per-language versions
- Always provide a fallback to the default language for missing translations
- Store language preference on the User record for logged-in users
- Build the language switcher in a reusable header for app-wide access
- Use Bubble's built-in Languages tab for simpler translation management

## Frequently asked questions

### Should I use Option Sets or Bubble's built-in Translation feature?

Bubble's Languages tab (Settings → Languages) is simpler for apps with many text strings. Option Sets give you more control and work well for smaller apps. Both approaches are valid.

### How do I handle right-to-left (RTL) languages like Arabic?

Add a direction attribute to the Language Option Set (ltr/rtl). Apply a Conditional on the page: when active_language's direction is 'rtl', add an HTML element that sets dir='rtl' on the body.

### Can I auto-detect the user's language?

Yes. Use JavaScript to read navigator.language and match it to your supported languages. Set the active_language to the detected language on first visit.

### How do I translate content generated by users?

For automatic translation, use the Google Translate API via the API Connector. For curated translations, build a translation editor where admins enter translations for each piece of content.

### Can RapidDev help build a multilingual Bubble app?

Yes. RapidDev can implement complete multilingual systems with translation management, language switching, RTL support, and SEO optimization for each language in your Bubble app.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/build-a-multilingual-app-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/build-a-multilingual-app-in-bubble
