Multi Language Template
Multi-Language Templates
Your waiting room can show up in the visitor's own language automatically. There's a ready-made Multi Language Template you can just select — nothing to build.
This guide covers what you get out of the box, and what to know if you're customising it: what the template is already doing, what has to stay intact, and how to check your design holds up in 63 languages.
Table of Contents
- What you get
- Using it
- Building a custom template
- Working with
t() - Which language a visitor sees
- Available languages
- Right-to-left languages
- Dates and times
- Changing the wording
- Every string you can use
- Testing your template
What you get
The Multi Language Template is the standard waiting room with its text translated. Same states, same layout, same behaviour — only the words change.
A visitor from Berlin sees the room in German. A visitor from Cairo sees it in Arabic, right-to-left. Dates and times follow the visitor's own regional conventions. None of that needs configuring per language or per visitor.
63 languages are covered: English plus 62 translated packs.
Using it
Select the Multi Language Template for your waiting room in your CrowdHandler dashboard. That's it — no markup to edit, no build step, no per-language setup.
Everything below this point is for customising. If the stock template suits you, you're done.
Building a custom template
On a plan that allows custom templates, download the Multi Language Template and use that as your starting point. Don't start from the standard template and try to add translation to it — the multi-language version already has the wiring in place, and copying it across by hand is the slow way round.
Two things in that download make the translation work. Keep both.
data-ch-i18n on the <html> tag
<html lang="en" data-chVersion="2" data-ch-i18n>
This is what lets CrowdHandler manage the page's language — keeping <html lang> accurate and setting <html dir="rtl"> when the visitor's language needs it. Strip it out and the page stops adapting, including the right-to-left flip.
(It's also the escape hatch, in the other direction: a custom template that's deliberately single-language, or one with its own language switcher, leaves the attribute off and CrowdHandler won't touch lang ou dir at all.)
t() calls instead of literal text
Where the standard template has English hardcoded, the multi-language one calls for a translated string:
<!-- Standard template -->
<h2>Queue Full</h2>
<p>We're at capacity. You'll be let in when a spot opens up.</p>
<!-- Multi Language Template -->
<h2>{{ t('room-full-title') }}</h2>
<p>{{ t('room-full-description') }}</p>
Restyle these freely, move them around, wrap them in your own markup — just don't replace a t() call with your own English text, or that line stops translating. If you want different wording, override the string instead so every language keeps working.
Working with t()
You'll need this when you add markup of your own, or move existing markup around. The key reference lists all 46 strings with their English wording.
In text:
<div class="ch-position-label">{{ t('position-label') }}</div>
In attributes — bind them, so nothing English gets left behind:
<input :placeholder="t('email-placeholder')" :aria-label="t('email-aria')" />
With a value dropped into the sentence:
<span>{{ t('estimate-minutes', { n: ch.estimate }) }}</span>
<span>{{ t('email-confirmation', { email: ch.email.value }) }}</span>
The second argument fills the {placeholders} in the string. estimate-minutes est "Approximately {n} minutes", so passing { n: 12 } gives "Approximately 12 minutes" — and the German "Ungefähr {n} Minuten" gets the same 12 in the right place for German word order.
Two behaviours worth knowing:
- If the value isn't ready yet —
ch.etais empty while the ETA is hidden, for instance — the placeholder just vanishes. You'll never see the word "null" on the page. - If you forget to pass a value, the raw
{n}stays visible. That's deliberate, so a missed value shows up while you're building rather than in production.
Call t() with a key that doesn't exist and the page renders the key name itself — you'll see the literal text room-full-titel where your copy should be. Handy for spotting typos.
Which language a visitor sees
CrowdHandler works through these in order and uses the first one it has a translation for:
| Order | Signal |
|---|---|
| 1 | ?lang= on the URL — outranks everything, which is how you test |
| 2 | The visitor's browser or OS language setting |
| 3 | The site they arrived from — coming in from a .de domain suggests German |
| 4 | English |
Regional variants fall back gracefully. Someone set to Brazilian Portuguese gets the Brazilian pack; if there weren't one, they'd get standard Portuguese; failing that, English. So de-AT et es-AR land on German and Spanish rather than dropping all the way to English.
If a translation can't be loaded for any reason — flaky connection, say — the room quietly renders in English rather than showing you a broken page.
Available languages
63 in total: English, plus 62 translated packs.
| Language | Code |
|---|---|
| Arabic | ar |
| Azerbaijani | az |
| Bangla | bn |
| Brazilian Portuguese | pt-BR |
| Bulgarian | bg |
| Canadian French | fr-CA |
| Catalan | ca |
| Chinese (Simplified) | zh |
| Chinese (Hong Kong) | zh-HK |
| Chinese (Taiwan) | zh-TW |
| Croatian | hr |
| Czech | cs |
| Danish | da |
| Dutch | nl |
| English | en |
| Estonian | et |
| Faroese | fo |
| Filipino | tl |
| Finnish | fi |
| French | fr |
| Galician | gl |
| Georgian | ka |
| German | de |
| Greek | el |
| Gujarati | gu |
| Haitian Creole | ht |
| Hebrew | he |
| Hindi | hi |
| Hungarian | hu |
| Icelandic | est |
| Indonesian | id |
| Italian | it |
| Japanese | ja |
| Kannada | kn |
| Kazakh | kk |
| Korean | ko |
| Latvian | lv |
| Lithuanian | lt |
| Malayalam | ml |
| Marathi | mr |
| Mexican Spanish | es-MX |
| Norwegian Bokmål | nb |
| Persian / Farsi | fa |
| Polish | pl |
| Portuguese | pt |
| Punjabi | pa |
| Romanian | ro |
| Russian | ru |
| Serbian (Cyrillic) | sr |
| Serbian (Latin) | sr-Latn |
| Slovak | sk |
| Slovenian | sl |
| Spanish | es |
| Swahili | sw |
| Swedish | sv |
| Tamil | ta |
| Telugu | te |
| Thai | th |
| Turkish | tr |
| Ukrainian | uk |
| Urdu | ur |
| Vietnamese | vi |
| Welsh | cy |
Need one that isn't here? Get in touch — adding a language is a CrowdHandler-side change, not something you do in the template.
Right-to-left languages
Arabic, Hebrew, Persian and Urdu render right-to-left. CrowdHandler sets dir="rtl" on the page; mirroring the layout is your CSS's job.
The stock stylesheet already handles it, so an unmodified template needs nothing. If you're writing your own CSS, two habits keep it working:
Use logical properties instead of left/right. They flip automatically:
/* Flips with the page direction */
margin-inline-start: 1rem;
padding-inline: 2rem;
text-align: start;
/* Stays put in RTL — avoid */
margin-left: 1rem;
text-align: left;
Give directional animations an RTL variant. Anything that visibly travels one way needs the mirror image, the way the built-in progress shimmer does:
[dir="rtl"] .ch-progress-fill {
animation-name: ch-shimmer-rtl;
}
Dates and times
Call formatDate(date, format) and leave the locale out of it:
<code>{{ formatDate(ch.requested, 'toLocaleTimeString') }}</code>
CrowdHandler picks the right regional format for you, and it follows the visitor rather than only the room's language. On an English room an American sees 2:30 PM et 1/27/2026 while someone in the UK sees 14:30 et 27/01/2026. On a French room, everyone gets French formatting regardless of where they're browsing from, so the page stays internally consistent.
Practical upshot for your design: don't assume a fixed width for a time or date. 14:30 et 2:30 PM are noticeably different lengths, and some locales are longer still.
Changing the wording
Any string can be overridden, in any language, including English. This needs a custom template, since it means adding a block to the markup — drop window.CH_languages in above the wait script:
<script>
window.CH_languages = {
"en": {
"position-label": "You are number",
"estimate-eta": "So close: {time}"
},
"fr": {
"position-label": "Vous êtes numéro"
}
};
</script>
<script src="https://wait.crowdhandler.com/js/latest/wait.js"></script>
- Only list the keys you're changing. Everything else keeps its translation.
- Your wording always wins over the built-in translation for that key.
- Keep the
{placeholders}. Dropping the{time}out ofestimate-etasilently removes the time from the sentence. - This is the right way to change wording. Replacing a
t()call with your own text breaks that line in every other language; an override keeps all 63 working.
Every string you can use
46 keys. The English column is what a visitor sees if there's no translation and you haven't overridden it.
Captcha
| Key | English |
|---|---|
captcha-message |
Please verify you're human to continue |
Bloqué
| Key | English |
|---|---|
blocked-title |
Access Denied |
blocked-description |
You've been blocked due to suspicious activity. |
Room full
| Key | English |
|---|---|
room-full-title |
Queue Full |
room-full-description |
We're at capacity. You'll be let in when a spot opens up. |
Compte à rebours
| Key | English |
|---|---|
countdown-label |
Queue opens in |
countdown-hours |
hrs |
countdown-minutes |
min |
countdown-seconds |
sec |
countdown-message |
Stay on this page. You'll get a random position when the queue opens. |
The three unit labels are the ones most likely to break a tight countdown layout — "hrs" is 3 characters in English and 8 in Finnish.
Position
| Key | English |
|---|---|
position-label |
Votre poste |
checking-in |
Checking in! |
shuffling-positions |
Shuffling positions! |
checking-in et shuffling-positions share one slot — ch.shuffling picks between them, so both need to fit the same space.
Estimates
| Key | English |
|---|---|
estimate-almost |
Almost there! |
estimate-over-hour |
More than an hour |
estimate-minutes |
Approximately {n} minutes |
estimate-eta |
ETA: {time} |
Priority code
| Key | English |
|---|---|
priority-title |
Got a priority code? |
priority-placeholder |
Enter code |
priority-apply |
Apply |
priority-success |
Code applied! |
priority-error |
Invalid code |
Stock
| Key | English |
|---|---|
stock-remaining |
items remaining |
stock-sold-out |
Sold out |
Session info
| Key | English |
|---|---|
sessions-expire-message |
Keep this window open. You'll move forward automatically. |
sessions-timeout-message |
You'll be redirected when it's your turn. You have {n} minutes to complete your transaction. |
Email notification
| Key | English |
|---|---|
email-change-title |
Change notification email |
email-notify-title |
Get notified when it's your turn |
email-placeholder |
your@email.com |
email-notify-btn |
Notify me |
email-cancel-btn |
Annuler |
email-confirmation |
We'll email {email} |
email-change-btn |
Change |
email-terms |
Your email address will be stored temporarily to notify you when your queue session ends. It will only be used for this purpose and deleted immediately after. |
Footer
| Key | English |
|---|---|
footer-id-label |
ID: |
footer-copied |
Copied! |
footer-copy-title |
Press Enter to copy |
footer-last-request |
Last check in: |
footer-powered-by |
Powered by |
footer-new-tab-sr |
(opens in new tab) |
Screen reader strings
| Key | English |
|---|---|
progress-aria |
Queue progress |
checking-in-aria |
Checking in progress |
email-aria |
Email address |
email-submitting-aria |
Submitting |
announcement-position |
Position {position} |
announcement-position-estimate |
Position {position}, estimated wait {estimate} minutes |
The two announcement-* strings are built for you and land in ch.annonce — you just need the live region in your markup:
<div class="ch-sr-only" aria-live="polite" aria-atomic="true">{{ ch.announcement }}</div>
Testing your template
Force a language
Add ?lang= to the URL. It beats every other signal, so you don't have to change your browser settings:
https://wait.example.com/my-event?lang=de
https://wait.example.com/my-event?lang=pt-BR
https://wait.example.com/my-event?lang=ar
Locally, before you upload
Your downloaded template is a standalone HTML file that loads the hosted CrowdHandler script, so you can open it straight from your desktop in a browser — no server needed.
It arrives in preview mode, which runs the room with no live queue behind it. Add ?lang= to the local file URL to switch language, and change status in the crowdhandler-data block at the top of the file to check your translated copy in each state:
status |
État |
|---|---|
1 |
Active queue |
3 |
Bloqué |
4 |
Compte à rebours |
5 |
Room full |
100 |
Captcha |
A checklist worth running
- German and Finnish for the longest words. If buttons, the stock badge or the countdown units are going to burst, it'll be here.
- Arabic or Hebrew for direction. Confirm the layout genuinely mirrors and nothing is stranded on the wrong side.
- Japanese or Thai for line breaking and font fallback, which behave differently from Latin scripts.
?lang=de-ATto confirm regional codes fall back to their base language instead of English.- The footer timestamp in a couple of languages, since its width changes with the visitor's region.