diff --git a/manifest.json b/manifest.json index a30cf1b..434eb3a 100644 --- a/manifest.json +++ b/manifest.json @@ -132,6 +132,20 @@ "themes/theme-clone/scripts/theme_audit.py" ] }, + { + "slug": "themes/rtl-localization", + "name": "RTL localization", + "description": "Localize a file-based Fluid theme for an RTL-language market (Arabic, Hebrew, Persian, Urdu). Always mirrors the layout via dir + CSS logical properties on the shared theme and fixes directional sections in place; asks the user whether the market also needs different content before creating any variant template. No section copies.", + "category": "themes", + "icon": "align-right", + "path": "themes/rtl-localization/SKILL.md", + "updated_at": "2026-07-31T00:00:00Z", + "references": [ + "themes/rtl-localization/references/direction-map.md", + "themes/rtl-localization/references/special-sections.md", + "themes/rtl-localization/references/fluid.md" + ] + }, { "slug": "themes/theme-source-inventory", "name": "Theme Source Inventory", diff --git a/themes/rtl-localization/SKILL.md b/themes/rtl-localization/SKILL.md new file mode 100644 index 0000000..68798c8 --- /dev/null +++ b/themes/rtl-localization/SKILL.md @@ -0,0 +1,126 @@ +--- +name: RTL localization +description: Use when localizing a file-based Fluid theme for an RTL-language market — Arabic, Hebrew, Persian/Farsi, Urdu, Pashto, Sindhi, Uyghur, Sorani Kurdish, Yiddish. Symptoms: onboarding a company selling into Saudi Arabia / UAE / Israel / Iran, text or layout stays left-aligned in an RTL locale, nav/carousels point the wrong way. The goal is always to localize the LAYOUT to the market; whether content also differs is asked, not assumed. Not for vertical/CJK top-to-bottom text. +icon: align-right +category: themes +--- + +# RTL localization + +Company: {{company.name}} + +Localize a **file-based** Fluid theme so an RTL-language market reads correctly right-to-left. +The invariant goal is **the layout is localized to the market** — mirrored via `dir` + CSS +logical properties on the *shared* theme, not by rebuilding or copying anything. Whether the +market also gets *different content* is a question for the user, not a default. + +**Core principles:** +- **Always localize the layout; ask about content.** Direction localization runs every time. + Different content only happens if the user asks for it in Phase 0. +- **Mirror on the shared theme.** `dir` + logical CSS in `layouts/theme.liquid` and shared + `sections/*` — this localizes every page at once. +- **Never copy sections to flip them.** Duplicating the PDP's sections into RTL copies is + thousands of lines that immediately drift. Mirroring is CSS/`dir`, not duplication. + +RTL **horizontal** only (Arabic-family). Vertical/CJK top-to-bottom is out of scope. +Surface: **file-based themes only** (`product/default/index.liquid` + `sections/*`). If the +company's theme is API-managed instead, STOP and say so — the mechanism differs and guessing +leaves an orphan template. + +## When to Use + +- A company is opening into an RTL-language market and its file-based storefront reads LTR. +- Text stays left-aligned, nav/logo sit on the wrong side, or carousels point the wrong way. + +**Do NOT use for:** vertical CJK text, a non-RTL language, API-managed themes, or pure copy +translation (that's the `translating-theme-languages` skill). + +## The Workflow + +Work one phase at a time. **Stop for a review checkpoint after each phase.** + +```dot +digraph rtl { + "0 Clarify with user" [shape=box]; + "1 Detect direction" [shape=box]; + "Is target language RTL?" [shape=diamond]; + "STOP — not RTL" [shape=box]; + "2 Localize layout (shared dir + logical CSS)" [shape=box]; + "3 Fix special sections (shared)" [shape=box]; + "Different content wanted?" [shape=diamond]; + "4 Variant template (reuse sections)" [shape=box]; + "5 Push + verify" [shape=box]; + + "0 Clarify with user" -> "1 Detect direction"; + "1 Detect direction" -> "Is target language RTL?"; + "Is target language RTL?" -> "STOP — not RTL" [label="no"]; + "Is target language RTL?" -> "2 Localize layout (shared dir + logical CSS)" [label="yes"]; + "2 Localize layout (shared dir + logical CSS)" -> "3 Fix special sections (shared)"; + "3 Fix special sections (shared)" -> "Different content wanted?"; + "Different content wanted?" -> "5 Push + verify" [label="no — mirror only"]; + "Different content wanted?" -> "4 Variant template (reuse sections)" [label="yes"]; + "4 Variant template (reuse sections)" -> "5 Push + verify"; +} +``` + +### Phase 0 — Clarify with the user (interactive flow) +Do not infer. Ask: +1. **Which market/language?** (Confirm; Phase 1 verifies it's RTL.) +2. **Same content mirrored, or different content for this market?** Make clear the layout gets + localized either way — this only decides whether content also changes. Default is + same-content-mirrored. +3. If **different content**: what specifically differs, and confirm they accept that a variant + template is a separate copy that won't auto-inherit future edits to the default. +4. Confirm the theme is **file-based** and locate the theme directory. + +### Phase 1 — Detect direction +Resolve the market's language → direction via `references/direction-map.md`. Key on **language**, +not country. Not in the RTL set → **STOP**. + +### Phase 2 — Localize the layout (always) +Edit the **shared** theme, not a copy. See `references/fluid.md`. +1. In `layouts/theme.liquid`, set `dir` on `` from the active locale + (`dir="rtl"` when `localization.language` is RTL, else `ltr`). Drive it from the locale — + never hardcode. +2. Convert physical CSS to logical properties in the theme's shared CSS so the layout mirrors: + +| Physical (LTR-only) | Logical (direction-aware) | +|---|---| +| `margin-left` / `margin-right` | `margin-inline-start` / `margin-inline-end` | +| `padding-left` / `padding-right` | `padding-inline-start` / `padding-inline-end` | +| `left:` / `right:` | `inset-inline-start` / `inset-inline-end` | +| `text-align: left` / `right` | `text-align: start` / `end` | +| `border-left` / `border-right` | `border-inline-start` / `border-inline-end` | +| `float: left` / `right` | `float: inline-start` / `inline-end` | + +### Phase 3 — Fix special sections (in place, no copies) +Hand-fix the shared components logical CSS can't flip using `references/special-sections.md` +(carousels, directional icons, image↔text splits, breadcrumbs, hardcoded positions) with +`[dir=rtl]` overrides on the existing sections. Keep numbers, prices, and Latin embeds LTR (bidi). + +### Phase 4 — Variant template (ONLY if the user chose different content) +Skip entirely for mirror-only. If different content was requested, create a variant template +folder (e.g. `product//index.liquid`) that **reuses the shared sections** with a +different order/settings, adding at most a single market section. Do not fork the section files. +See `references/fluid.md`. + +### Phase 5 — Push + verify +Push the theme, then load the storefront in the RTL locale and check: text right-aligned, +nav/logo mirrored, icons/carousels point the correct way, spacing mirrored, and numbers/prices +stay LTR (bidi). Screenshot before/after. + +## Common Mistakes + +- **Copying sections to flip them** — mirroring is `dir` + logical CSS on shared sections, not + duplication. Copies drift immediately. +- **Editing a copy instead of the shared theme for mirroring** — Phase 2 is shared-theme work. +- **Assuming different content** — always ask in Phase 0; default is same-content-mirrored. +- **Wrong surface** — this is file-based only; STOP on API-managed themes to avoid an orphan. +- **Hardcoding `dir="rtl"`** instead of driving it from the locale. +- **Forgetting bidi** — numbers/prices/URLs must stay LTR inside an RTL layout. + +## References + +- `references/direction-map.md` — language → direction lookup (the RTL set). +- `references/special-sections.md` — the exception-component checklist and fixes. +- `references/fluid.md` — file-based theme specifics: where `dir`/CSS go, `localization.language`, variant templates, and pushing. diff --git a/themes/rtl-localization/references/direction-map.md b/themes/rtl-localization/references/direction-map.md new file mode 100644 index 0000000..9b2bde7 --- /dev/null +++ b/themes/rtl-localization/references/direction-map.md @@ -0,0 +1,35 @@ +# Language → Text Direction Map + +Key text direction on the **language**, not the country. An Arabic storefront reads +right-to-left whether the market is Saudi Arabia, the UAE, or Egypt. + +## Right-to-left (horizontal) — apply RTL treatment + +| Language | ISO 639-1 / code | Primary markets | +|---|---|---| +| Arabic | `ar` | Saudi Arabia, UAE, Egypt, Qatar, Kuwait, Iraq, Jordan, Morocco, … | +| Hebrew | `he` (`iw`) | Israel | +| Persian / Farsi | `fa` | Iran | +| Urdu | `ur` | Pakistan, India | +| Pashto | `ps` | Afghanistan | +| Sindhi | `sd` | Pakistan | +| Uyghur | `ug` | — | +| Kurdish (Sorani) | `ckb` | Iraq, Iran | +| Yiddish | `yi` | — | + +Any language **not** in this table → **left-to-right**. Default to LTR when unknown. + +## Explicitly out of scope + +- **Vertical / CJK top-to-bottom** (traditional Japanese `ja`, Chinese `zh`, Korean `ko`, + Mongolian). These read **left-to-right horizontally on the modern web** — they are NOT RTL. + Do not apply RTL treatment to them. True vertical typesetting (`writing-mode: vertical-rl`) + is a separate, much larger effort and is not covered by this skill. + +## Detection notes + +- Normalize locale variants before lookup: strip region suffix (`ar-SA` → `ar`), and map + legacy codes (`iw` → `he`, `jp` → `ja`). +- If a company supports multiple languages, direction is per **active locale**, not a single + company-wide flag — an RTL company may still serve an English (LTR) page. +- The direction test in code is simply: `RTL_LANGS.include?(locale_language_code)`. diff --git a/themes/rtl-localization/references/fluid.md b/themes/rtl-localization/references/fluid.md new file mode 100644 index 0000000..552a04f --- /dev/null +++ b/themes/rtl-localization/references/fluid.md @@ -0,0 +1,63 @@ +# Fluid File-Based Theme Appendix + +This skill operates on a **file-based** Fluid theme (the `product/default/index.liquid` + +`sections/*` layout), not the API-managed template API. If the company's theme is API-managed, +STOP — creating a file-based artifact against an API theme leaves an orphan. + +## Theme layout + +``` +layouts/theme.liquid ← the shell; where `dir` goes +sections//index.liquid ← shared, reusable sections (with {% schema %}) +product/default/index.liquid ← a page template: composes sections via {% section %} +product//index.liquid← a slug/variant template (only for different structure) +``` + +A page template composes shared sections and carries a `{% schema %}` with `sections` + `order`. +It holds no page copy and does not recreate section blocks. Every RTL-locale visitor renders the +same templates — so mirroring belongs in the shared layout/CSS, applied once. + +## Phase 2 — where direction localization goes + +- **`dir` attribute:** in `layouts/theme.liquid`, on the `` tag, driven by the locale: + ```liquid + + ``` + If the theme exposes no `rtl` flag, resolve it by testing `localization.language.iso` against + the RTL set in `references/direction-map.md` (a small `{% case %}`/`{% if contains %}` check). + `localization.language`, `localization.country`, and `localization.available_countries` are + available in every template. +- **Logical CSS:** convert physical properties to logical ones in the theme's shared CSS so the + whole layout mirrors under `dir="rtl"`. Do this in the shared stylesheets/section CSS — not in + per-market copies. + +## Phase 3 — special sections + +Fix directional components in the **existing shared** `sections/*` with `[dir=rtl]` overrides +(see `references/special-sections.md`). Never fork a section into an RTL twin. + +## Phase 4 — variant template (only for different content) + +Repo rule: *"Only create a slug/variant template when the source truly uses a different +structure."* Mirroring is never a reason to create one. When the user has asked for different +content: +- Create `product//index.liquid` (or the relevant resource) that **reuses the shared + sections** with a different `order`/settings, plus at most one new market section. +- Give every section instance a unique `id`; keep blocks in the section presets, not the template. + +## Phase 5 — push + verify + +Push the theme with the project's normal mechanism (e.g. `fluid theme push`, or the Mist theme +tooling). Then load the storefront in the target locale/region and run the Phase 5 checks. Take +before/after screenshots. + +## Country routing & SEO — what to know + +- Pure mirroring needs **no** per-country routing: `dir` is locale-driven, so any RTL-locale + visitor gets the mirrored layout automatically once the shared theme is pushed. +- A *different-content* variant that must be served to a specific country relies on region + routing (a region rule keyed on country ISO). That is an API/config concern outside the theme + files — confirm the mechanism with the user before wiring it, to avoid an orphan mapping. +- **SEO limitation (don't overpromise):** there is no exposed way to emit per-country `hreflang` + alternates or path-based localized URLs; `hreflang` is per-language via a `?lang=` param. + Flag deeper per-country indexing as a backend follow-up. diff --git a/themes/rtl-localization/references/special-sections.md b/themes/rtl-localization/references/special-sections.md new file mode 100644 index 0000000..838c5f2 --- /dev/null +++ b/themes/rtl-localization/references/special-sections.md @@ -0,0 +1,37 @@ +# Special Sections — the exceptions logical CSS can't auto-mirror + +Logical properties (Phase 2) mirror the box model — margins, padding, alignment, insets. +They do **not** flip content that encodes direction in markup, JS, transforms, or fixed +geometry. Walk this checklist against the target, and for each one either fix it or flag it +for review. + +## Checklist + +| Component | What breaks in RTL | Fix | +|---|---|---| +| **Carousels / sliders** | Slides advance the wrong way; prev/next arrows point wrong; scroll/translate math assumes LTR. | Reverse slide order or the transform sign in RTL; swap prev/next; if using `scroll-snap`, verify it honors `dir`. | +| **Image ↔ text splits** | "Image left, text right" should become "image right, text left". Often driven by `flex-direction: row` + physical order. | Rely on logical order; if hardcoded, add an RTL rule to reverse `flex-direction` or the DOM order. | +| **Step / progress indicators** | Steps 1→2→3 must run right→left; connector lines point wrong. | Reverse visual order and connector direction under `[dir=rtl]`. | +| **Directional icons** | Arrows, chevrons, back/forward, "next", carets stay pointing LTR. | Mirror with `[dir=rtl] .icon { transform: scaleX(-1); }` for glyphs that encode direction. **Do NOT mirror** non-directional icons (logos, checkmarks, stars, brand marks). | +| **Breadcrumbs** | Separator direction (`Home › Shop`) points the wrong way; order stays LTR. | Reverse order and flip/replace the separator under RTL. | +| **Hardcoded position** | Elements placed with literal `left:`/`right:`, absolute badges, drawer/slide-in panels, tooltips. | Convert to `inset-inline-*`; verify off-canvas panels slide from the correct edge. | +| **Transforms / animations** | `translateX(...)`, slide-in keyframes move the wrong direction. | Negate the X translation under RTL, or express motion in logical terms. | +| **Background position / sprites** | `background-position: left` and sprite offsets don't follow `dir`. | Add RTL overrides where the asset is directional. | +| **Shadows / gradients** | Directional `box-shadow`/`linear-gradient(to right, …)` may look off. | Judgment call — mirror only if the direction is meaningful. | + +## Bidi (mixed-direction content) + +Even in an RTL layout, these stay **left-to-right** and must not reverse: +- Numbers, prices, phone numbers, dates +- Code, URLs, email addresses +- Latin-script brand names and embeds + +Wrap or isolate them (`dir="ltr"`, `unicode-bidi: isolate`, or ``) if they leak. + +## How to work this phase + +1. Grep the theme/component CSS and JS for the LTR tells: `left`, `right`, `translateX`, + `flex-direction: row`, `text-align: left`, `float`, `scaleX`, arrow/chevron icon names. +2. For each hit, decide: already handled by logical CSS? needs an `[dir=rtl]` override? + or is it a non-directional element to leave alone? +3. Fix the directional ones; list anything ambiguous for the review checkpoint.