Displaying Thaana well
Thaana is written right to left, its vowels are marks above and below the letters, and most Dhivehi fonts have no Latin letters. Here's everything that goes wrong because of that, with a live example and the fix. The @yashau/thaana packages and thaana.css handle most of it for you.
1. Set direction and language
Without dir="rtl" the browser lays Thaana out left to right: the text hugs the wrong edge and the
full stop lands on the right. lang="dv" lets the browser and screen readers pick the right font and voice.
ދިވެހިރާއްޖެއަކީ ރީތި ޤައުމެކެވެ.
ދިވެހިރާއްޖެއަކީ ރީތި ޤައުމެކެވެ.
<html lang="dv" dir="rtl">
<!-- or on any element -->
<p dir="rtl" lang="dv">…</p>2. Text people type
dir="auto" picks the direction from the first letter, so a Dhivehi sentence that starts with a brand
name turns left to right. If the text contains any Thaana, render it right to left (resolveDirection()), and use unicode-bidi: plaintext so each paragraph of a longer text decides for itself.
Apple ގެ އާ ފޯނު ނެރެފިއެވެ!
Apple ގެ އާ ފޯނު ނެރެފިއެވެ!
import { resolveDirection } from '@yashau/thaana';
el.dir = resolveDirection(text); // 'rtl' if there's any Thaana, else 'ltr'3. Numbers and phone numbers
Inside right-to-left text, a phone number's groups swap places and a minus sign jumps to the wrong end. Wrap them
in an isolate: <bdi dir="ltr"> in HTML, or isolateNumbers() for plain strings.
ގުޅާލައްވާ: +960 333-1234
މިއަދުގެ ފިނިމިން -5 ޑިގްރީ
ގުޅާލައްވާ: +960 333-1234
މިއަދުގެ ފިނިމިން -5 ޑިގްރީ
<p dir="rtl">ގުޅާލައްވާ: <bdi dir="ltr">+960 333-1234</bdi></p>
import { isolateNumbers, stripBidiControls } from '@yashau/thaana';
label.textContent = isolateNumbers(text); // display
save(stripBidiControls(label.textContent)); // store without the invisible marks4. Brackets
Store brackets in logical order: ( before the enclosed text. Browsers mirror brackets in right-to-left
text, so they come out right. That is why Dhivehi keyboards (Microsoft's, and the one here) swap the bracket keys: you
press the key that looks right on screen and the logical character is stored. Text that stores the brackets reversed
shows them facing outward and breaks copy-paste.
ދިވެހިރާއްޖެ )ރާއްޖެ( ގައި
ދިވެހިރާއްޖެ (ރާއްޖެ) ގައި
5. Punctuation
Dhivehi uses the Arabic comma ،, semicolon ؛ and question mark ؟; the full
stop is the ordinary one. No space before punctuation. normalizePunctuation() fixes both.
ކިހިނެއް ? ރަނގަޅު , ޝުކުރިއްޔާ .
ކިހިނެއް؟ ރަނގަޅު، ޝުކުރިއްޔާ.
6. Line height
Fili sit above and below the letters. A tight line height or overflow: hidden cuts them off, which shows
up in buttons, badges and table cells. Give Thaana about 1.75.
7. Latin fallback
Most Dhivehi fonts have no Latin letters. Name only the Dhivehi font and English words fall back to the browser's
default serif. Always add a Latin font after it; fontStack() also adds installed Thaana fonts.
ދިވެހި Fonts ގެ 2026 ވަނަ އަހަރު
ދިވެހި Fonts ގެ 2026 ވަނަ އަހަރު
font-family: 'MV Iyyu', system-ui, 'Noto Sans Thaana', 'MV Boli', sans-serif;8. No italics
Thaana has no italics, and many Thaana fonts already lean. A browser-faked slant leans the wrong way for
right-to-left text. Emphasise with weight or colour, and set font-synthesis: none.
މިއީ މުހިންމު ބަހެކެވެ.
މިއީ މުހިންމު ބަހެކެވެ.
9. Inputs
Most people have no Dhivehi keyboard installed. Give fields the phonetic keyboard, right-to-left direction, and
turn off autocorrect and autocapitalise (they mangle Thaana on phones). Try typing divehi in both:
<script type="module" src="https://dvfonts.yashau.com/thaana.js"></script>
<input data-thaana>React, Svelte and Vue: see the packages.
10. Canvas and SVG
Canvas draws with whatever font is loaded right now: wait for the font first, and set
the direction, or the text comes out in a fallback font, left to right. In SVG set direction="rtl" and text-anchor="start".
import { loadFonts } from '@yashau/thaana';
await loadFonts({ family: 'Faruma' });
ctx.font = "32px 'Faruma'";
ctx.direction = 'rtl';
ctx.textAlign = 'start';
ctx.fillText('ދިވެހިރާއްޖެ', canvas.width - 16, 48);11. Old fonts and PDFs
Documents typed with the old A_ fonts store Latin letters (ihevid) and only look like Thaana in that
font. Text copied out of a PDF comes back with reversed dates and stray spaces. Both can be repaired: convertLegacy() and repairPdfText(), or paste it into the converter.
cswb ihevid
ތާރީޚް 2026/1/14
ދިވެހި ބަސް
ތާރީޚް 14/1/2026
12. Generating PDFs
PDF libraries' simple text functions don't shape Thaana: fili land in the wrong place. Shape the text with
HarfBuzz (script Thaa, direction RTL), draw the resulting glyphs, and embed the full font. To keep
the text selectable and searchable, write each run in logical order. For form fields, provide the appearance
yourself rather than letting the viewer regenerate it; Acrobat and Preview reverse Thaana or drop marks. rihaPDF does all of this and documents
why.
13. Storing and searching
- Store text in logical order (the order it's typed), never pre-reversed. Strip bidi control characters before
saving or comparing (
stripBidiControls()). - Keep the display-tidied version and the stored text separate: don't save what you only changed for display.
- Thaana has no case and no Unicode composition variants, so plain string comparison works for search.