Every "AI revolution" article assumes you write in English.
There are 500 million Urdu speakers. Until last year, none of them could open Sudowrite, NovelAI, or Jasper and write a novel in their own language. Not poorly — at all. The tools either rendered Urdu as boxes, broke right-to-left flow mid-paragraph, or refused to generate anything past 200 words.
I spent eight months building KitaabAI — an AI book-writing platform that treats Urdu as a first-class citizen, not a translation afterthought. This is what I learned about building AI products for non-English markets, and the specific technical decisions that made it work.
The problem in one screenshot
Open any major AI writing tool. Paste this:
یہ ایک کہانی ہے جو رات کے اندھیرے میں شروع ہوتی ہے۔
Three things break:
- Font rendering — most tools fall back to a default Arabic font that destroys Nastaliq aesthetics (Urdu is written in Nastaliq, not Naskh).
- RTL flow — punctuation jumps to the wrong side; mixed English-Urdu lines reverse.
- Generation quality — the LLM either responds in English, switches to Hindi-Urdu transliteration, or generates 2 sentences and stops.
For a 50,000-word novel, every one of these is fatal.
Architectural decision 1: Mehr Nastaliq, not system fonts
The first instinct is "let the OS pick a font." Don't.
Urdu has a specific aesthetic — Nastaliq script — that looks completely different from Arabic Naskh. Microsoft's default Urdu font on Windows is acceptable. Apple's is okay. Android's is terrible. Browsers on Linux give you literal boxes.
The fix: bundle the font.
// src/index.css
@font-face {
font-family: 'Mehr Nastaliq Web';
src: url('/fonts/MehrNastaliqWeb-Regular.woff2') format('woff2');
font-weight: 400;
font-display: swap;
unicode-range: U+0600-06FF, U+0750-077F, U+FB50-FDFF, U+FE70-FEFF;
}
[lang="ur"], [dir="rtl"] .urdu-text {
font-family: 'Mehr Nastaliq Web', 'Noto Nastaliq Urdu', serif;
line-height: 2.2; /* Nastaliq needs vertical breathing room */
}
Two things matter here:
-
unicode-rangeso the font only downloads when Urdu characters appear (saves ~400KB for English-only users). -
line-height: 2.2— Nastaliq characters have aggressive vertical descenders. Default 1.5 makes lines overlap.
Architectural decision 2: Logical CSS properties
The hardest bug in any RTL app is mixed-direction state. A user toggles language and half the UI mirrors, half doesn't.
The fix is brutally simple: never write margin-left or text-align: left again.
- className="ml-4 pl-2 text-left border-l-2"
+ className="ms-4 ps-2 text-start border-s-2"
Tailwind ships ms-, me-, ps-, pe-, text-start, text-end, border-s, border-e. Every directional utility has a logical equivalent. Set dir="rtl" on the document root, and the entire UI mirrors automatically — including chevrons, dropdowns, and progress bars.
The rule I now enforce with an ESLint plugin: zero physical direction utilities anywhere in the codebase.
Architectural decision 3: Language-aware prompts (not translation)
The naive approach to "Urdu support" is:
- Take English prompt.
- Translate to Urdu with GPT-4.
- Generate.
- Pray.
This produces text that is grammatically Urdu but stylistically Hindi-Urdu spoken dialect. Real Urdu literature has a specific register — formal, Persian-influenced, with vocabulary that translation-based prompts never reach.
The fix: language-native system prompts written by an actual Urdu writer.
const SYSTEM_PROMPTS = {
en: `You are a professional novelist. Write in clean, modern English prose...`,
ur: `آپ ایک تجربہ کار اردو ادیب ہیں۔ آپ کا اسلوب کلاسیکی اردو ادب جیسا ہے —
ابن صفی، انتظار حسین، اور قراۃ العین حیدر سے متاثر۔ ہندی الفاظ سے گریز کریں۔
فارسی اور عربی الاصل الفاظ کو ترجیح دیں جب مفہوم واضح ہو...`,
};
async function generateChapter(brief: ChapterBrief, lang: 'en' | 'ur') {
return openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: SYSTEM_PROMPTS[lang] },
{ role: 'user', content: lang === 'ur' ? brief.ur : brief.en },
],
max_tokens: 12000, // see next section
});
}
The reference authors (ابن صفی، انتظار حسین، قراۃ العین حیدر) anchor the model's style in actual Urdu literature. The difference is measurable — beta readers consistently rated the language-native prompt 4.2/5 vs 2.8/5 for the translation approach.
Architectural decision 4: 12k token chunks, not full books
Every "AI book writer" demo generates 2000 words and stops. Then the user paste-and-prompts another 2000. By chapter 6, the AI forgets the protagonist's name.
The fix is a three-layer memory system:
interface ChapterContext {
bookBible: string; // 500 tokens — never changes
outlineSummary: string; // 800 tokens — all chapter summaries
recentChapters: string; // 4000 tokens — last 2 chapters verbatim
currentBrief: string; // 200 tokens — what this chapter needs
}
function buildPrompt(ctx: ChapterContext): Message[] {
return [
{ role: 'system', content: SYSTEM_PROMPT },
{ role: 'system', content: `BOOK BIBLE:\n${ctx.bookBible}` },
{ role: 'system', content: `STORY SO FAR:\n${ctx.outlineSummary}` },
{ role: 'assistant', content: ctx.recentChapters },
{ role: 'user', content: ctx.currentBrief },
];
}
The book bible is generated once at book creation (characters, world rules, voice notes). The outline summary updates as chapters complete. The last 2 chapters ship verbatim so prose voice stays consistent.
Token budget: 12,000 input → 4,000–6,000 output (≈ one full chapter). That fits in a single Edge Function invocation without streaming complexity.
Architectural decision 5: RTL PDF rendering is its own circle of hell
Generation was the easy part. Exporting to PDF for Amazon KDP almost killed the project.
The problem: every JavaScript PDF library (jsPDF, pdfmake, react-pdf) treats text as left-to-right glyph sequences. Drop Urdu into them and you get one of three failure modes:
- Characters render in isolated form (not joined).
- Line breaks happen mid-word.
- Punctuation drifts to wrong side.
The solution that finally worked: a hybrid engine that detects script per paragraph and routes through different renderers.
function isRtlText(text: string): boolean {
const rtlChars = /[\u0600-\u06FF\u0750-\u077F\uFB50-\uFDFF\uFE70-\uFEFF]/;
const rtlCount = (text.match(rtlChars) || []).length;
return rtlCount / text.length > 0.3;
}
async function renderParagraph(p: Paragraph, pdf: PDFDocument) {
if (isRtlText(p.text)) {
// Use HTML-to-canvas with proper RTL font + bidi
const canvas = await renderRtlToCanvas(p.text, {
font: 'Mehr Nastaliq Web',
direction: 'rtl',
lineHeight: 2.2,
});
pdf.addImage(canvas, 'PNG', ...);
} else {
// Native PDF text path — vector, searchable, smaller file
pdf.text(p.text, ...);
}
}
Yes, RTL paragraphs become rasterized canvas blocks. They lose text-selection. But they render correctly on Kindle, Apple Books, and KDP paperback — which is the only metric that mattered.
A pure-vector RTL PDF in JavaScript remains an open problem in 2026. If you crack it, please email me.
What I got wrong (and would change)
Started with
margin-lefteverywhere. Spent two days mass-replacing withms-. Use logical properties from day 1.Trusted GPT-4 to detect language. It picked up English words in Urdu sentences and switched mid-response. I now use a deterministic character-ratio detector (the one above) before every generation call.
Underestimated font licensing. Mehr Nastaliq is free for web use. Many beautiful Nastaliq fonts are not. Read the EULA before bundling.
Built English UI first, retrofitted Urdu. The right order is: build with
diragnostic from line 1. Retrofit cost me three weeks.
What this unlocked
KitaabAI is now the only AI book writer with native Urdu support. The market that "every major AI tool ignored" turned out to be very real:
- 60% of signups in month 1 came from Pakistan, India, UK, and Gulf states.
- Average Urdu user generates 30,000+ words/month — higher engagement than English users.
- Zero competitors. Sudowrite, NovelAI, and Jasper still don't support Urdu.
The lesson isn't "Urdu is special." The lesson is that the AI tooling boom has hundreds of these gaps — Bengali (270M speakers), Indonesian (270M), Vietnamese (95M), Tamil (75M). Each is a Sudowrite-sized opportunity for someone who treats the language as a first-class citizen, not a localization checkbox.
If you're building AI tools, the next billion users don't speak English. Build accordingly.
Tech stack reference
For the curious:
- Frontend: React 18 + Vite 5 + Tailwind CSS (logical properties only)
- State: Zustand + TanStack Query
- Auth + DB: Supabase (Postgres with RLS)
- AI: OpenAI GPT-4o + Anthropic Claude 3.5 (failover)
- Edge functions: Deno on Supabase
- PDF export: Custom hybrid jsPDF + html2canvas engine
- Fonts: Mehr Nastaliq Web (Urdu), Inter (English UI)
Try it free at kitaabai.com — Urdu and English both supported, no credit card.
Questions or pushback? Drop a comment. Especially if you've solved vector RTL PDF rendering in JS — I owe you a coffee.

Top comments (0)