Theme Translations & Internationalization
SitePack includes native support for multi-language storefront translations. By utilizing localized translation dictionaries in JSON format, you can easily adapt your theme's headings, form labels, interface actions, and checkout messages for any country, region, or custom language profile.
This guide explains the translation folder structure, strict naming conventions, dynamic fallback hierarchy, translating keys inside templates, and rendering interpolation parameters.
The translations/ Folder
All translation file mappings must reside within the /translations folder at the root directory of your theme:
your-theme-directory/
├── translations/
│ ├── en.json # Base English translation
│ ├── nl.json # Base Dutch translation
│ ├── nl_be.json # Regional Dutch (Belgium) overrides
│ └── fr_ca.json # Regional French (Canada) overrides
Strict File-Name Conventions
SitePack is deployed across highly fast, case-sensitive Linux servers. To guarantee consistency and prevent template compilation lookup errors:
- Strict Lowercase Rule: All translation filenames MUST be formatted in strict lowercase.
- Correct:
nl_be.json,en_us.json - Incorrect:
nl_BE.json,en_US.json,NL_BE.json
- Correct:
- Valid File Extension: Every translation file must end in the
.jsonextension. - Supported Formats: SitePack supports both two-letter primary language-level codes (
en.json,nl.json,fr.json) and specific five-character regional/country codes containing an underscore divider (nl_be.json,en_gb.json,de_at.json).
Smart Translation Resolution & Fallbacks
SitePack implements a hierarchical resolution fallback algorithm. This is designed to maximize developer efficiency: you do not have to write hundreds of repetitive keys across multiple files. Instead, you only define base translations once and override specific localized regional terms in sub-files.
When a customer visits a store using a regional locale like nl_be (Dutch - Belgium), the SitePack compilation engine resolves keys in this strict order:
Customer Locale: "nl_be"
│
▼
┌──────────────────────────────────────────────────┐
│ Step 1: Check Specific Regional File │ ──> Is the key in "nl_be.json"?
└──────────────────────────────────────────────────┘
│ (If not found or file missing)
▼
┌──────────────────────────────────────────────────┐
│ Step 2: Check Base Language File │ ──> Is the key in "nl.json"?
└──────────────────────────────────────────────────┘
│ (If still not found)
▼
┌──────────────────────────────────────────────────┐
│ Step 3: Render Raw Key String │ ──> Output the literal path (e.g. "cart.checkout")
└──────────────────────────────────────────────────┘
Fallback Practical Example
Consider the following two dictionary files configured inside your theme workspace:
translations/nl.json (Base Language)
{
"cart": {
"title": "Mijn Winkelwagen",
"checkout": "Afrekenen",
"empty": "Uw winkelwagen is momenteel leeg."
}
}
translations/nl_be.json (Regional Overrides)
{
"cart": {
"checkout": "Bestellen"
}
}
Here is how SitePack resolves and outputs translation strings for a customer whose session locale is nl_be:
{{ 'cart.title' | trans }}- Resolution: Not found in
nl_be.json. Checks base filenl.json. - Result: "Mijn Winkelwagen"
- Resolution: Not found in
{{ 'cart.checkout' | trans }}- Resolution: Match found in
nl_be.json. - Result: "Bestellen" (custom Belgian terminology override).
- Resolution: Match found in
{{ 'cart.empty' | trans }}- Resolution: Not found in
nl_be.json. Checks base filenl.json. - Result: "Uw winkelwagen is momenteel leeg."
- Resolution: Not found in
Formatting Dictionary Files (Nested Objects)
Translation keys are stored in standard JSON format. To keep your localization clean and readable, group related keys into nested objects.
{
"storefront": {
"welcome": "Welcome back!",
"contact_us": "Get in Touch"
},
"search": {
"placeholder": "Search catalog...",
"no_results": "No items matched your query."
}
}
Translating Keys inside Twig Templates
To render a translated string inside your storefront templates, use the built-in Twig filter trans applied to your key path.
1. Simple Key Lookup (Dot-Notation)
Use standard dot-notation to navigate nested objects inside your JSON translation dictionary:
{# Maps to the "placeholder" key inside the "search" object #}
<input type="text" placeholder="{{ 'search.placeholder' | trans }}">
{# Output: <input type="text" placeholder="Search catalog..."> #}
2. Parameter Interpolation Variables
To output dynamic, data-driven content (such as customer names, dates, or order statuses) within a static sentence, define interpolation placeholders wrapped in percent signs (%placeholder%) inside your JSON dictionaries. This follows the same convention used by the Symfony Translator that powers SitePack under the hood.
Then, pass the variables as a hashed array argument directly to the trans filter in your Twig file. The array keys must match the %placeholder% string exactly, percent signs included.
translations/en.json
{
"account": {
"welcome_customer": "Welcome, %name%",
"order_item": "Order #%id% - %date% - Status: %status%"
}
}
templates/account.twig
{# Safely rendering parameters inside translations #}
<h2>{{ 'account.welcome_customer' | trans({'%name%': customer.first_name ~ ' ' ~ customer.last_name}) }}</h2>
<p>{{ 'account.order_item' | trans({'%id%': order.id, '%date%': order.created_at | date('M d, Y'), '%status%': order.status}) }}</p>
{# Output: <h2>Welcome, Alex Johnson</h2> #}
{# Output: <p>Order #1042 - Jun 12, 2026 - Status: Shipped</p> #}
:::caution Common Mistake
Do not use Twig's own {{ variable }} interpolation syntax inside translation strings — SitePack translations are resolved server-side by the Symfony Translator, not by the Twig template engine, so only the %placeholder% format is recognized. A JSON string like "Hello, {{ name }}" will be rendered literally, curly braces and all.
:::
Exploring Further:
Now that you have configured multi-lingual translations, explore the built-in vector assets available for your UI designs in the Theme Icons Reference Guide!