Arabic first
Direction and language, locale files and the t filter, dates and numerals, typography, and the RTL checklist.
This page is for anyone designing a theme that sells in an Arabic market. Dukkan storefronts are Arabic first and RTL by default; English is a second layer enabled on request. By the end you know how to build correct direction, translated strings, local numerals and dates, and sound Arabic typography.
Direction and language#
request.locale is ar or en, and request.direction is rtl or ltr. Bind both to the document root and never hardcode a direction in CSS:
<html dir="{{ request.direction }}" lang="{{ request.locale }}">Design RTL first and check LTR second. Use logical properties exclusively (margin-inline-start, padding-inline, inset-inline-end, text-align: start) so the mirrored layout needs no second stylesheet.
Locale files and the t filter#
Every string a visitor sees goes through {{ 'key' | t }} with its value in locales/ar.json (and locales/en.json if you support English). Never write Arabic in templates.
{
"nav": {
"home": "الرئيسية",
"cart": "السلة"
},
"product": {
"add_to_cart": "أضف إلى السلة",
"sold_out": "نفد المخزون"
},
"footer": {
"powered_by": "مدعوم من دكان"
}
}<button type="button" data-dukkan-add-to-cart data-variant-id="{{ variant.id }}">
{{ 'product.add_to_cart' | t }}
</button>- Files are flattened to dotted keys (
product.add_to_cart), so nesting is free. - The current locale's table is selected at render; a missing key returns the key itself and never throws, so you spot it easily in preview.
- Merchant text settings carrying
localized: truearrive with their English value automatically when the reader visits in English and a translation exists in the settings document; see Settings schema.
Numerals#
moneyvalues display with Western digits by default, the currency symbol before the amount, and bidi wrapping so a price never flips inside an Arabic line.- Declare a checkbox setting with the id
arabic_numeralsto offer the merchant Eastern Arabic digits: when enabled, the money filters render٠١٢٣٤٥٦٧٨٩with Arabic separators, whilemoney_amountstays Western for machine consumers. Stamp the root with the attribute so the browser-side currency converter matches:
<html dir="{{ request.direction }}" lang="{{ request.locale }}"{% if theme_settings.arabic_numerals %} data-dukkan-numerals="arabic"{% endif %}>- Other numbers (
product.sold_count,pagination.total) arrive as raw numbers; display them as is or format them with standard Liquid filters.
Dates#
Platform dates (such as review.created_at) are YYYY-MM-DD. Turn them into readable text in the current locale:
{{ review.created_at | date_localized: request.locale }}In Arabic you get Levantine month names ("٢٧ آب ٢٠٢٦"); in English, plain English formatting. Any value not in the date shape passes through untouched.
Typography#
- The recommended typeface is IBM Plex Sans Arabic at weights 400, 500 and 600; bundle it as
woff2files underassets/or rely on the system's Arabic fonts. - Line height 1.7 to 1.9 for Arabic text; never use
letter-spacingwith Arabic, it breaks the joins. - Arabic headings read one step smaller than their Latin counterparts at the same weight; tune the scale on Arabic glyphs first.
- Technical identifiers (tracking code, discount code, phone number) belong in an element with
unicode-bidi: plaintextordir="ltr"so they never flip.
RTL checklist#
-
dirandlangcome fromrequest, never hardcoded. - No
left/rightin CSS except for what must not mirror (such as images). - Directional icons (arrows, chevrons) mirror in RTL.
- Prices never flip inside Arabic sentences (use
money, never manual formatting). - Forms and fields align to the start, with numbers inside them
dir="ltr". - Every string goes through
t; no Arabic text in templates or CSS. - The page reads on a 360 px wide phone first.