Skip to main content

Locale System

How to switch language, add a translation, and where strings live (Lua and React).

This section

Locale System

Carplay V2 ships with 7 pre-translated language files and supports adding new ones. Every user-facing string in the script — Lua-side or React-side — flows through the locale system.

Built-In Locales

Located in locales/:

FileLanguage
en.luaEnglish
tr.luaTurkish
es.luaSpanish
de.luaGerman
fr.luaFrench
nl.luaDutch
pt.luaPortuguese

Choosing the Active Locale

lua
Config.Locale = "en" -- en | tr | es | de | fr | nl | pt

The script loads the matching file at boot. If the file is missing or malformed, it falls back to en.lua.

Locale File Format

Each locale file returns a flat key→string table:

lua
return {
home = "Home",
settings = "Settings",
confirm = "Confirm",
cancel = "Cancel",
-- ...

setup_welcome_title = "Welcome to Carplay",
setup_welcome_desc = "Let's set up your experience",
-- ...

music_no_music = "No music playing",
music_loading = "Loading...",
-- ...

autopilot_remaining = "Remaining Distance",
autopilot_eco = "Eco",
-- ...
}

Keys are stable identifiers — never change the keys, only the values when translating.

How Strings Are Looked Up

Lua Side

Use the global Lang(key, ...) helper:

lua
ShowNotification(Lang("notification_engine_on"))

-- with format args (string.format)
ShowNotification(Lang("notification_fuel_low", fuelPercent))
-- where the locale value is e.g. "Fuel low: %d%%"

Behavior:

  • Returns the string for key from the loaded locale
  • If the key doesn't exist, returns the key itself (as a fallback so missing strings are visible)
  • Format args are applied via string.format if provided

React Side

The React app receives the locale table on resource start via SendReactMessage("language", localeTable) and stores it in the LanguageContext. Use the useLanguage hook (or directly via context) to look up strings.

jsx
import { useLanguage } from "../hooks/useLanguage"

function MyComponent() {
const { language } = useLanguage()
return <h1>{language.home || "Home"}</h1>
}

Always provide an English fallback as the second arg in case the key isn't yet translated in the active locale.

Adding a New Language

  1. Copy locales/en.lua to locales/<your-locale>.lua — e.g. locales/it.lua for Italian
  2. Translate every value (keep keys unchanged)
  3. Set in shared/config.lua:
lua
Config.Locale = "it"
  1. Restart the resource

The new locale takes effect immediately. No code changes, no React rebuild required.

String Categories

The locale file is grouped into logical sections (the order is just for readability):

  • General — confirm, cancel, save, delete, close, edit, back, unknown, loading
  • Setup wizard — first-run wizard text (welcome, profile, avatar, theme, done)
  • Notifications — in-game notification text (engine on/off, vehicle locked, etc.)
  • Vehicle controls — dock button labels and tooltips
  • Music player — every UI label in the music app
  • Map — places, favorites, recents, autopilot, waypoints
  • Autopilot — drive modes, settings, navigation HUD
  • Weather — conditions, descriptions, units, location label
  • Cameras — security cam and dashcam labels
  • Settings app — every option label in settings
  • Errors — common error messages

Adding a New Key

If you add a feature that needs a new translatable string:

  1. Add the key to every locale file in locales/ — even if you only have an English value, add the same English text in all files (translators can pick it up later)
  2. Reference it via Lang("your_new_key") (Lua) or language.your_new_key (React)

If a key is missing in the active locale, the fallback chain is:

  1. Active locale value
  2. (Lua) the key string itself — useful as a placeholder
  3. (React) whatever fallback you provide as a second arg

Locale Tips

  • Keys should be snake_case and prefixed by area (music_, map_, autopilot_, setup_, weather_)
  • Don't use punctuation in keys — only in values
  • For format strings (%s, %d), preserve the placeholders in every translated value
  • Test your translation by setting Config.Locale and using all features end-to-end