LITHOS / getting started / languages

Configuring languages

The language list is the one setting that decides both what the room can hear and what you pay. Every language you turn on is a live, per-minute translation session the moment the service starts — whether or not anyone is listening to it. This page is how to build that list deliberately.

Updated for v0.6.0-beta.6, with screenshots captured from that release. Room names and settings shown are examples.

The idea

Two kinds of "language"

Two different things are called a language here, and they are set in two different places:

  • Translation languages — what the speaker's words are turned into. You choose these on the operator page, under Languages (global). Each one is a paid session while it is on. This page is about these.
  • The phone's own wording — the buttons and messages on the listener page ("Preferred language", "Tap to listen"). The phone picks this by itself from its OS language, out of the eight the page is written in: English, Spanish, French, Haitian Creole, Portuguese, Swahili, Khmer and Lao. Nothing to configure, nothing billed. When someone picks a preferred translation language that is one of those eight, the page wording follows it too.

There is no per-language on/off on the phone. A phone can only offer what you have turned on.

Operator page

Add a language

Languages ship off. A fresh install has an empty list and the phones say "No languages are available yet — the service has not been set up." until you add one.

  1. Open the operator page (/operator, with your PIN) and find the Languages (global) card, just under API keys.
  2. Tap + Add language. Two menus appear: the provider that will do the translating (Gemini is the default; OpenAI; or your own Local stack) and the language. The list shows the English name, the language's own name and its code, sorted by English name; languages you have already added are hidden from it.
  3. Tap Add. The new row appears with its box ticked — added languages are on straight away. Untick it if you are only preparing for later.
The Languages card with Add language open and enabled English, Spanish and French rows using Gemini.
The Languages card with the Add form open. Each row shows the language, its code and a provider tag.

Every row carries a small tag — gemini, openai or local — because the provider is chosen per language, not for the whole room. Spanish on OpenAI and Portuguese on Gemini in the same service is perfectly normal. The tag cannot be edited: to move a language to another provider, remove the row (it asks first) and add it again.

The last entry in the language menu is ➕ Custom…. Type a language code and the language's native name, and the row is created like any other. This is for a language that is not in the built-in list; whether the provider can actually speak it is between you and the provider — see below.

Choosing

Which languages, which provider

The built-in menu has 45 languages. Not every provider handles all of them, and the menu tells you where it can.

  • Gemini (default) — offered for every language in the list. It is also the provider the service prefers for the transcript of what the speaker said, so if you run a mix, at least one Gemini language is a good idea.
  • OpenAI — the realtime translation model officially covers a shorter list: Arabic, Chinese, English, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Portuguese, Russian, Spanish and Vietnamese. Pick OpenAI as the provider and the language menu shrinks to those fourteen. Anything else on OpenAI has to go in as a Custom code — allowed, because the provider's list changes faster than ours, but check it with real speech before Sunday.
  • Local stack — your own translation server on the LAN or a rented GPU box. Its translator knows several hundred languages, so anything in the menu is accepted; the limit is which voices the stack has installed (the reference build ships voices for en, es, fr, pt and sw, with ht, km and lo as operator-supplied add-ons). Test in Service settings only proves the stack is reachable — it does not check voices.

The provider's own Test button, next to its key in the API keys card, is the ground truth. A language the menu is unsure about, or a Custom code, is proven by hearing it, not by the list.

Live behaviour

Turning one on or off

The tick box on each row is not a preference that waits for the next service. It acts immediately, and it acts on the bill:

  • While the engine is running, ticking a language opens its translation session within seconds, in the main service and in every running group whose menu includes it. Unticking closes those sessions everywhere at once. So does ✕ Remove.
  • While the engine is stopped, the boxes only decide what ▶ Start translating will open: one session per ticked language.
  • A ticked language is billed from the moment its session is live until it closes — whether or not any phone has chosen it. The main service does not wait for a listener. Four ticked languages and nobody in the room is still four sessions.

The card's own help (the ? in its corner) puts the arithmetic bluntly: four languages and two running groups can mean twelve parallel sessions. How that turns into dollars, and the auto-stop that guards against a forgotten engine, is on the cost & charging page. The short version for this page: the list should be the languages this room needs today, not every language you might ever need. Untick, don't remove — an unticked row costs nothing and is one tap away next time.

If you tap Start and a language's provider has no key, the language does not silently fail: it is listed under the sessions as "fr · didn't start — missing gemini API key" with a note to add the key and Restart. The other languages start normally.

Groups

Languages in groups

A group — a class, a youth room, a breakout — has a language menu of its own, but it is always a subset of the global list. When you create a group, its language boxes start all ticked, which means something specific: the group follows the service list. Add a language globally next month and the group offers it too, without anyone editing the group.

Untick a box in the group and it stops following: it now has a fixed menu of exactly what you left ticked. That is the right choice for a class that only ever needs Spanish while the main hall runs five languages, because each running group opens a session per language on its menu — a follow-all group under a five-language service is five more sessions.

Attendee groups (the ones people create from their phones) are gentler: they open a session for a language only when someone in the group actually picks it. Operator-made groups open theirs at start. Both are covered in setting up and managing groups and private groups.

Service settings

The speaker's language

Everything above is about the target. The language the speaker actually talks in is the Speaker’s language row in Service settings, and its default, auto, is right for almost everyone: the provider works out what it is hearing.

Speaker’s language set to auto beside Save transcripts in Service settings.
Service settings → Speaker’s language. Leave it on auto unless detection keeps guessing wrong.

Set it to a code (es, pt-BR, ko…) when a service is entirely in one language and auto-detection keeps drifting — a heavy accent, lots of proper names, a bilingual speaker who codeswitches. A fixed source is passed to every provider session, so it applies to every target language at once. It takes effect when sessions next start; press Restart if the engine is already running.

Local language detection

Local language detection in Service settings.
Service settings → Local language detection.

Normally the app learns which language is being spoken from the provider's transcript — which needs a billed session open and arrives a few seconds late. Local language detection runs a small model on the operator machine instead: it reacts in about a second and works before the engine is started, so the "hearing: es" tag on the operator page can be right from the first sentence. It needs Python with torch and speechbrain installed on that machine; without them the box does nothing harmful — the app quietly falls back to the transcripts. It does not change what is translated, only how quickly the app knows what it is hearing.

Service settings

Voices & models

Each provider has one row in Service settings: model, voice, and the $/min rate the cost meter uses. The voice is one voice for every language on that provider — you cannot pick a different voice per language.

OpenAI and Gemini model, voice and per-minute estimate fields in Service settings.
Service settings → OpenAI and Gemini. Model, voice, and the per-minute rate for the meter.
  • OpenAI — model gpt-realtime-translate, voice alloy by default. Any voice name the model accepts can go in the box.
  • Gemini — model gemini-3.5-live-translate-preview, voice Kore by default. Gemini's prebuilt voices have names like Kore, Puck, Charon, Aoede; the voice's gender is fixed by the name.
  • Local stack — no voice setting here; the stack uses whatever voice it has installed for each language. Its row is the ws:// endpoint and a Test button, plus Simultaneous: translate and speak while the person is still talking (about two seconds behind instead of five to nine, at the cost of choppier phrasing). Local stack only.

Model and voice changes apply the next time the engine or a group starts, not to sessions already open. Change them, save, then Restart. The $/min box changes nothing about the audio; it only feeds the running cost estimate on the operator page — set it to what your account is actually billed.

Listener page

What the phone shows

The phone's first screen has a Preferred language menu at the top. It lists exactly the languages that are currently ticked — unticked and removed languages never reach the phone — using each language's own name (Español, not Spanish). The choice is remembered on the phone, carries into whichever group the person joins, and is pre-selected there if the group offers it. Below the menu come the groups and the main service. The phone reads the list when the page loads, so after you change it, phones that are already open need a reload to see the difference.

The listener room picker in French after choosing French as the preferred language.
The first screen with French picked as the preferred language — the page's own wording switched to French with it.

Once inside a service, the page is captions first. Audio is off until the person taps "🔇 Audio off — captions only. Tap to listen (use earbuds)"; the earbuds line is there because a phone speaker playing translation into a room feeds the microphone and the translator starts hearing itself. If the service has not been started yet the page says "You're in — waiting for the service to begin…" and picks up on its own when you press Start.

Under the captions there can be a Speak section — "your language will be projected". It appears on the main service only while Allow participants speaking from phones is ticked on the presenter page for that room. Classes have their own saved speaking switch too. What the person says is translated into the same language list and shown on the projector, so a question from the floor reaches everyone; there is nothing extra to enable per language.

Screen

The projector

The projector page (/projector) shows captions without audio. Register a browser display using the corner gear, or enable an attached screen on This computer. On Playout devices, choose the display’s room, the languages it shows, its layout and whether the join QR is visible. It can show the hall or a class, and two screens can split the languages between them. Its language choices come from its assigned room.

Fixes

When something's wrong

A language is missing from the phone
The phone only lists ticked languages, and only from the service or group it is looking at. Check the box on the operator page (or, in a group, in that group's language boxes), then pull the phone page down to refresh. If the phone says "No languages are available yet", the whole list is off. A language that is on the global list but missing inside one group means that group has stopped following the list — tick it in the group too.
The voice is wrong — wrong gender, wrong sound
The voice is set per provider, in Service settings (OpenAI voice, Gemini voice), and it applies to every language on that provider. Change it, save, and Restart — a voice change does not reach sessions that are already open. If one language sounds different from the others, look at its provider tag: it is probably on a different provider with a different voice setting.
It's translating into the wrong language
Almost always a mismatch between what the phone chose and what you think it chose. Tap Change ⌄ on the phone and look at the highlighted button. If the audio is right but the text is wrong, or the other way round, the language is fine and the problem is the source — see the next item. If a Custom language comes out in a neighbour language (Norwegian as Danish, say), the provider does not really support the code; try the other provider for that language.
It's mis-detecting what the speaker is saying
With Speaker’s language on auto, the provider decides what it hears, and a strong accent, a bilingual speaker or a run of names can tip it. If the whole service is in one language, set Source lang to that code and Restart — every session then knows what to expect. Turning on Local language detection makes the operator page's "hearing:" tag react faster, but it does not tell the provider what to translate from; Speaker’s language does.
One language says "connecting" and never goes live
Look at the session list on the operator page. "didn't start — missing … API key" means the language's provider has no key: add it under API keys and Restart. A session that stays on connecting while the others are live is being refused by the provider — usually a language that provider does not support, or a rejected key (the page tells you when a key is rejected, and stops the engine rather than leave it burning). Move the language to the other provider, or remove it. For a Local stack language, Test the endpoint first — "Unreachable" or "No hello after 6s" means the stack itself is down or its port is not exposed, and every local language will sit on connecting until it is.
Captions arrive but there's no audio
On the phone, audio is off until tapped — look for "🔇 Audio off — captions only. Tap to listen" at the top and tap it; iPhones in particular will not play sound the page did not ask for. If that is already on and the phone is on silent, iOS keeps web audio muted — flip the ring switch. If every phone on one language has captions but no sound while other languages are fine, that language is on the Local stack without a voice installed for it — translation works without a voice, so captions still flow. Add the voice on the stack, or move the language to Gemini or OpenAI.

Something not covered here? Write to support@lithos.community with the language, its provider tag, and what the session list on the operator page says, and we'll sort it out.