Reference
Options
slug(text, {
script: 'latin', // or 'keep'
lang: 'ar', // 'ar' | 'fa' | 'ur'
separator: '-',
lower: true,
maxLength: undefined,
article: true, // a leading ال becomes al- when romanising
stopWords: [],
keep: '', // extra characters to allow, such as '_'
});
| Option | Default | |
|---|---|---|
script |
'latin' |
'keep' leaves the Arabic letters in place. |
lang |
'ar' |
Which reading to use, and which spelling to unify towards. |
separator |
'-' |
|
lower |
true |
|
maxLength |
— | Cuts on a separator, never mid-word. |
article |
true |
المقالة → al-mqala. |
stopWords |
[] |
Dropped — unless they are the whole title, which would leave nothing. |
keep |
'' |
Characters that would otherwise be stripped. |
uniqueSlug(text, taken, options)
import { uniqueSlug } from '@devix-labs/arabic-slugify';
uniqueSlug('مرحبا', ['mrhba']); // 'mrhba-2'
uniqueSlug('مرحبا', new Set(['mrhba'])); // 'mrhba-2'
Worth more here than in English. Romanised Arabic collides often, because the
short vowels that would tell two words apart are not written — كتب (books) and
كاتب (writer) both reduce towards the same consonants. taken is anything
iterable, so a Set or the result of a pluck('slug') both work.
prefersScript(text)
prefersScript('مرحبا بالعالم'); // true
prefersScript('Hello world'); // false
prefersScript('Devix متجر'); // true
True when the text is more than half Arabic-script letters. Digits and
punctuation do not count, so '2025' is false.
The pieces on their own
import { normalise, transliterate, westernDigits, stripInvisible, hasTashkeel } from '@devix-labs/arabic-slugify';
normalise('مَرْحـــبا'); // 'مرحبا' — marks, kashida and joiners gone
normalise(text, { towards: 'fa' }); // unify towards ی and ک instead of ي and ك
stripInvisible('ab'); // 'ab' — joiners and direction marks only
westernDigits('٢٠٢٥'); // '2025'
hasTashkeel('مَرحبا'); // true
transliterate('مُحَمَّد'); // 'muhammad'
normalise is idempotent — normalising a normalised string changes nothing —
which is what makes a slug stable across a save, a re-import and a migration.
Searching, not just slugs
The same normalisation is what makes Arabic search work. Store a normalised copy
of the field and match against a normalised query, and a search for دنيا finds
دنیا, مرحبا finds مَرْحَبًا, and a stray kashida stops mattering:
const searchable = normalise(title); // alongside the real title
Without it, a user typing a Persian yeh will not find a record stored with an Arabic one, which is the commonest "search is broken" report on Arabic sites.