Right-to-left layout, so Arabic and Hebrew can be read #67

Closed
opened 2026-09-06 16:21:47 +00:00 by tiagoagueda · 1 comment
Owner

Observation

target to 0.5.0
enable R-to-L writing to support arabic

What exists today

One line of it, in base.html:

<html lang="{{ LANGUAGE_CODE|default:'en-gb' }}" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr' }}">

The hook is there and nothing has ever exercised it. Every one of the 24 languages
Postulo currently offers is written left to right, so dir has been ltr on every page
ever rendered, and whatever the interface does when it is rtl is unknown rather than
known-good.

What actually has to change

dir="rtl" flips text and inline layout. It does not touch a stylesheet that names a
side, and the interface names sides in 59 places across the templates:

What Count
ml-auto (pushes an action group to one edge) 29
text-left 11
text-right 10
ml-* / pl-* 9
left-* / right-* 3

Tailwind v4 has the logical equivalents — ms/me, ps/pe, start/end,
text-start/text-end — which resolve against the document direction. Most of this is
that substitution, and ml-auto is the single biggest group: it is what puts Record an
application
at the far end of a heading row, and in Arabic the far end is the other one.

Then the parts a substitution does not reach:

  • Icons that point. The sort arrows, the move up and move down controls, the
    chevrons on menus and the pagination arrows. Vertical ones stay; horizontal ones flip.
    A "next page" arrow pointing the wrong way is worse than no arrow.
  • The board. Columns are laid out in a horizontally scrolling row, so the earliest
    status must sit at the reading-start edge and the scroll must begin there. Drag and
    drop needs the same treatment.
  • Mixed direction, which is the hard part. An Arabic interface listing Aperture
    Science
    — a Latin name inside an Arabic sentence — needs the name isolated, or the
    punctuation around it jumps to the wrong end. Same for a URL, an email address, a
    version number and anything else that stays Latin. <bdi> around user-entered text in
    the templates is the mechanism.
  • Rendered documents. A CV or a letter in Arabic is a WeasyPrint page of its own, and
    documents/themes/base_cv.html sets no direction. That needs to follow the document's
    language — which is the same field #65 is about for letters.
  • The dashboard and Insights. Bars grow from a side, and #44 is about to rebuild both
    from widgets; whatever it builds should be direction-agnostic from the start rather than
    fixed afterwards.

What this issue does not include

Arabic itself. #43 sets the order languages arrive in — the European Union, then the
rest of Europe, then Africa, Asia and the world — and Arabic belongs to a later phase of
it. This is the layout work that has to exist before any right-to-left language can be
added without the interface falling apart, and it can land and be tested with none.

How it gets tested

There is no language to test with, which is the whole difficulty. Two answers, both worth
having:

  1. A pseudo-locale. A right-to-left code with a catalogue that mirrors the source
    text, so the browser suite can visit the same pages under dir="rtl" and the
    screenshots can be looked at. Test-only, never offered in the picker.
  2. A lint. Refuse a physical direction utility in a template, the way
    tests/test_template_lint.py already refuses a broken comment. That is what stops it
    drifting back one heading row at a time.

Classification

Enhancement, 0.5.0. Not breaking: dir is already emitted, and every logical utility
resolves to today's behaviour under ltr.

Depends on

Nothing hard. It should land before Arabic or Hebrew is offered, and #44 should be built
with it in mind rather than after it.

## Observation > target to 0.5.0 > enable R-to-L writing to support arabic ## What exists today One line of it, in `base.html`: ```django <html lang="{{ LANGUAGE_CODE|default:'en-gb' }}" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr' }}"> ``` The hook is there and **nothing has ever exercised it**. Every one of the 24 languages Postulo currently offers is written left to right, so `dir` has been `ltr` on every page ever rendered, and whatever the interface does when it is `rtl` is unknown rather than known-good. ## What actually has to change `dir="rtl"` flips text and inline layout. It does not touch a stylesheet that names a side, and the interface names sides in 59 places across the templates: | What | Count | |---|---| | `ml-auto` (pushes an action group to one edge) | 29 | | `text-left` | 11 | | `text-right` | 10 | | `ml-*` / `pl-*` | 9 | | `left-*` / `right-*` | 3 | Tailwind v4 has the logical equivalents — `ms`/`me`, `ps`/`pe`, `start`/`end`, `text-start`/`text-end` — which resolve against the document direction. Most of this is that substitution, and `ml-auto` is the single biggest group: it is what puts *Record an application* at the far end of a heading row, and in Arabic the far end is the other one. Then the parts a substitution does not reach: - **Icons that point.** The sort arrows, the *move up* and *move down* controls, the chevrons on menus and the pagination arrows. Vertical ones stay; horizontal ones flip. A "next page" arrow pointing the wrong way is worse than no arrow. - **The board.** Columns are laid out in a horizontally scrolling row, so the earliest status must sit at the reading-start edge and the scroll must begin there. Drag and drop needs the same treatment. - **Mixed direction, which is the hard part.** An Arabic interface listing *Aperture Science* — a Latin name inside an Arabic sentence — needs the name isolated, or the punctuation around it jumps to the wrong end. Same for a URL, an email address, a version number and anything else that stays Latin. `<bdi>` around user-entered text in the templates is the mechanism. - **Rendered documents.** A CV or a letter in Arabic is a WeasyPrint page of its own, and `documents/themes/base_cv.html` sets no direction. That needs to follow the document's language — which is the same field #65 is about for letters. - **The dashboard and Insights.** Bars grow from a side, and #44 is about to rebuild both from widgets; whatever it builds should be direction-agnostic from the start rather than fixed afterwards. ## What this issue does not include **Arabic itself.** #43 sets the order languages arrive in — the European Union, then the rest of Europe, then Africa, Asia and the world — and Arabic belongs to a later phase of it. This is the layout work that has to exist before any right-to-left language can be added without the interface falling apart, and it can land and be tested with none. ## How it gets tested There is no language to test with, which is the whole difficulty. Two answers, both worth having: 1. **A pseudo-locale.** A right-to-left code with a catalogue that mirrors the source text, so the browser suite can visit the same pages under `dir="rtl"` and the screenshots can be looked at. Test-only, never offered in the picker. 2. **A lint.** Refuse a physical direction utility in a template, the way `tests/test_template_lint.py` already refuses a broken comment. That is what stops it drifting back one heading row at a time. ## Classification Enhancement, 0.5.0. Not breaking: `dir` is already emitted, and every logical utility resolves to today's behaviour under `ltr`. ## Depends on Nothing hard. It should land before Arabic or Hebrew is offered, and #44 should be built with it in mind rather than after it.
tiagoagueda added this to the 0.5.0 milestone 2026-09-06 16:21:47 +00:00
tiagoagueda modified the milestone from 0.5.0 to 0.3.0 2026-09-06 20:15:52 +00:00
Author
Owner

Landed on 0.3.0 as 0fee264.

Landed on `0.3.0` as 0fee264.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
Postulo/postulo#67
No description provided.