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

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.eta is 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 of estimate-eta silently 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-AT to 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.