Multi-Language Sites

Last updated on October 10, 2026.

Translate your interface with translation files, switch language per visitor, and send emails in the right language.

Nothing on Insites requires you to hardcode English. Put every piece of text in translation files, one file per language, and look each piece up by its key with the t filter. The Insites modules look up their own messages the same way.

This page shows the whole path: the files, the lookups, choosing the language for each request, remembering the choice, and sending email in the visitor's language.

What You Need Before You Start

  • An Insites instance you can deploy to, through Insites CloudShell or the Insites GitHub App.
  • The list of languages you support, and a code for each one. See Language Codes below.

Step 1: Add One Translation File per Language

Each file is named after its language code, and its first key is the same code.

app/translations/en.yml

en:
  sign_in:
    title: Sign in
    code_sent: We sent a code to %{email}.
  email:
    subject: Your sign-in code
    code_intro: Your code is %{code}. It expires in 10 minutes.

app/translations/de.yml

de:
  sign_in:
    title: Anmelden
    code_sent: Wir haben einen Code an %{email} gesendet.
  email:
    subject: Ihr Anmeldecode
    code_intro: Ihr Code lautet %{code}. Er ist 10 Minuten gültig.

English is the default language. When a key is missing in another language, the English text is used.

Step 2: Look Up Text With t

Pass the key to t. To fill in a %{name} placeholder, pass a value with the same name.

<h1>{{ 'sign_in.title' | t }}</h1>
<p>{{ 'sign_in.code_sent' | t_escape: email: email }}</p>

t treats the result as safe HTML. When a value you pass in comes from a visitor, such as an email address they typed, use t_escape. It escapes the values before it puts them in the text.

Step 3: Set the Language for Each Request

The context tag sets the language for the current request. After that, every t lookup uses it, and context.language holds the code.

Put this at the top of every page, before any text is looked up:

{% liquid
  assign supported = 'en,bn,de,es,fr,hi,it,ja,fil,pt,ru,ta,uk,zh' | split: ','
  assign lang = context.params.language | default: context.session.language | default: 'en'
  unless supported contains lang
    assign lang = 'en'
  endunless
  if context.params.language != blank
    session language = lang
  endif
  context language: lang
%}

Only accept codes from your own list. The language comes from the visitor, so never pass it on unchecked.

Step 4: Remember the Choice

The code in Step 3 saves the language in the session whenever a request carries a language parameter. Later requests from the same visitor read it from context.session.language.

A language switcher is a link that adds the parameter:

<a href='?language=de'>Deutsch</a>

If your front end runs in the browser, send the chosen code as a language field with each request to your JSON endpoints. JSON fields arrive in context.params, the same as form fields, so Step 3 picks it up.

Step 5: Send Email in the Visitor's Language

Choose the language in the page that sends the email, where Step 3 has already set it. Then pass the result to the email as template data. The values you send arrive in the email as data.

The simplest way is to look up the text in the page and send the finished text. The email then shows whatever it is given.

app/emails/sign_in_code.liquid

---
to: '{{ data.to }}'
from: no-reply@example.com
subject: '{{ data.subject }}'
---
<p>{{ data.intro }}</p>

app/graphql/emails/send.graphql

mutation send($template: String!, $data: HashObject) {
  email_send(template: { name: $template }, data: $data) {
    is_scheduled_to_send
    errors { message }
  }
}

Send it from your page, after Step 3 has set the language:

{% liquid
  assign data = {}
  assign data.to = email
  assign data.subject = 'email.subject' | t
  assign data.intro = 'email.code_intro' | t: code: code
  graphql sent = 'emails/send', template: 'sign_in_code', data: data
%}

Build data as one Liquid variable, as above. An object written inside the GraphQL query, with variables in it, sends nothing. See Passwordless Sign-In.

If an email needs a different layout per language, keep one template per language instead, such as sign_in_code_en and sign_in_code_de, and choose the template name from the language:

{% liquid
  assign template_name = 'sign_in_code_' | append: context.language
  graphql sent = 'emails/send', template: template_name, data: data
%}

email_send queues the email. It is not sent while the page is still running.

Note: A staging instance sends email only to the test email recipients set in your instance settings. See Staging vs. Production.

Language Codes

Use a language code, not a country code. Two codes often get mixed up:

  • Ukrainian is uk. ua is the country code for Ukraine.
  • Filipino is fil. Filipino has no two-letter language code. ph is the country code for the Philippines.

Whatever codes you choose, use the same code in three places: the file name, the first key in the file, and the list in Step 3. After you deploy, open one page in each language to check it.

Next Steps

Have a suggestion for this page?

Didn't quite find what you are looking for or have feedback on how we can make the content better then we would love to hear from you. Please provide us feedback and we will get back to you shortly.