Building themes
Translations
How theme code works on multi-language websites: automatic translation of settings, the t filter, the ui filter, and a language switcher.
A Plangku website can be shown in more than one language. The main language has no prefix in its addresses, and each extra language lives under its own prefix (/en/produk, /ja/produk). Up to five extra languages can be added, from Indonesian, English, Chinese, Japanese, Korean, Spanish, French, German, Dutch, Russian, Malay, Thai and Vietnamese. Only text is translated: layout, photos, colours and links are shared by all languages.
Theme code takes part in this in three ways, from least to most work for you:
- Text in section settings is translated automatically.
- Fixed words in your template are marked with the
tfilter. - Words the platform already knows (menu, contact...) come from the
uifilter.
Plus site.lang and site.languages for a language switcher.
Settings are translated for you
Sections written in Plang are translated exactly like built-in sections. Every text and textarea (and richtext) setting is collected, including those inside list settings, and shown in Settings → Languages or in the editor's language mode, where the owner can translate by hand or with AI. The template simply prints the setting and gets the visitor's language:
<h2>{{ section.settings.heading }}</h2>
<p>{{ section.settings.text }}</p>WhatsApp messages in link settings are translated too. In a link value such as wa:Halo, saya mau pesan kopi, the part after wa: is the message and is translated; the button then opens a chat with the translated message.
What is not translated:
- Text with no letters in it, such as
Rp 25.000or2026. - Settings whose id is one of
anchor,mapQuery,phone,email,address,url,categoryorformName. Those are treated as data shared by all languages. Don't use these ids for copy you want translated. - Texts over 60,000 characters.
- Text settings in the theme settings (
settings.<id>). Keep wording that must be translated in section settings instead.
append.Fixed words with t
Some words are part of your template, not a setting: "Lihat semua", "Pesan sekarang", "Kembali ke atas". Wrap a literal in the t filter:
<a href="{{ collection.url }}">{{ 'Lihat semua' | t }}</a>
<button type="button" popovertarget="menu">{{ 'Buka menu' | t }}</button>
{% assign label = 'Pesan sekarang' | t %}When you save the theme, Plangku collects every literal that starts with t (up to 2000 texts) and adds them to the website's translatable texts, next to the setting texts. The owner translates them in Settings → Languages or in the editor's language mode. On the main language t writes the text as it is. In another language it writes the translation, or the original text until a translation exists.
Rules:
- The text must be a string literal written in the template, and
tmust be the first filter applied to it.{{ 'Lihat semua' | t }}and{% assign x = 'Lihat semua' | t %}are collected;{{ s.label | t }}and{{ 'Lihat' | append: ' semua' | t }}are not (they translate only if the same text happens to be translated elsewhere). - Spaces at the start and end are ignored.
- There are no variables. For "Halo, Sari" style text, keep the parts apart:
{{ 'Halo' | t }}, {{ name }}. - Write the original text in the website's main language (Indonesian for most Plangku sites).
Platform words with ui
For words every website needs (contact, language, open menu, cookie notice, form messages), don't translate by hand: {{ 'openMenu' | ui }} writes "Buka menu" in Indonesian and "Open menu" in English, in all thirteen languages. The keys are listed on the filters page.
<nav aria-label="{{ 'mainMenu' | ui }}">...</nav>
<button type="button" popovertarget="m" aria-label="{{ 'openMenu' | ui }}">...</button>
<p>{{ 'noMatch' | ui }}</p>An unknown key is written out unchanged, so check the spelling.
Other language-aware filters
dateprints month and weekday names in the page's language:{{ 'now' | date: '%A, %e %B %Y' }}givesMinggu, 8 November 2026in Indonesian andSunday, 8 November 2026in English.moneyalways writes rupiah in Indonesian number style (Rp 25.000).- Links written as
page:slugstay in the visitor's language: on/enthey point to/en/slug.
site.lang and site.languages
site.lang is the code of the language being shown (id, en...). Use it for a lang attribute or to show something in one language only:
{% if site.lang == 'id' %}Gratis ongkir{% else %}Free shipping{% endif %}That is a last resort, since it leaves out other languages. Prefer settings and t.
site.languages is the list for a language switcher. Each entry has code, label (the language's own name: English, 日本語), href (the same page in that language) and current. The built-in header shows a switcher only when there is more than one language, and so should you:
{%- if site.languages.size > 1 %}
<nav class="langs" aria-label="{{ 'language' | ui }}">
{%- for l in site.languages %}
<a href="{{ l.href }}" hreflang="{{ l.code }}" lang="{{ l.code }}"{% if l.current %} aria-current="true"{% endif %}>{{ l.code | upcase }}</a>
{%- endfor %}
</nav>
{%- endif %}.langs { display: flex; gap: .5rem; font-size: .8125rem; font-weight: 600; }
.langs a[aria-current] { text-decoration: underline; text-underline-offset: 4px; }Put long names (l.label) in a mobile menu and short codes next to the logo, as the built-in header does.
Data in other languages
On a detail page, entry is translated into the visitor's language (title, short texts, article text). Items listed through a collection setting are translated the same way, together with the collection's name, just like the built-in lists.
Checklist
- Put copy in settings, so the owner can translate it.
- Put fixed words in
{{ '...' | t }}. - Use
uifor platform words. - Use
site.languagesfor the switcher, only when it has more than one entry. - Avoid building sentences from pieces.
- Test with a second language: switch the editor to language mode and read the page.
See also theme settings for what the theme can offer owners, and objects for the site object.