Skip to main content

arabizi

Moroccan Arabizi transliteration, Arabic normalization, and word-level code-switching detection.

CI PyPI Python License Typed

No dependencies. Deterministic. Says "I don't know" when it doesn't.


Why a Moroccan one

Arabizi — Arabic typed in Latin letters — uses digits for the sounds Latin has no letter for. Those digits are near-universal. The letters are not.

Moroccan arabizi is built on French orthography, because that is the second language its writers were schooled in. Levantine and Gulf arabizi are built on English:

sound Moroccan Levantine / Gulf
ش ch sh
و (long) ou oo / u
ق 9 or q 2 (glottal stop)
گ g → ڭ g → ج (Egypt)

A transliterator tuned on Levantine data reads chkoun (شكون, "who") with an English ch and gets it wrong. Every existing package I could find is tuned that way. This one is not.

Installation

pip install arabizi

Three things it does

1. Transliterate

from arabizi import transliterate

transliterate("salam 3lik")  # 'سلام عليك'
transliterate("wach kayn chi 7aja")  # 'واش كاين شي حاجة'
transliterate("mabghitch")  # 'ما بغيتش'
transliterate("labas hamdullah")  # 'لاباس الحمد لله'

Arabizi omits short vowels, so a pure character mapping turns salam into سالام — the a is a fatha, not an alef, and nothing in the input says which. No rule recovers that. A lookup table does, for the words where it matters: a few hundred function words and greetings cover a large share of any Darija text, and their spellings are settled. The transliterator checks that first and falls back to characters for everything else.

URLs, emails and @handles pass through untouched.

2. Normalize

from arabizi import normalize

normalize("أَحْمَد")  # 'احمد'   — diacritics dropped, alef unified
normalize("مـــرحبا")  # 'مرحبا'  — tatweel removed
normalize("١٢٣")  # '123'    — Arabic-Indic digits converted

Every step is separately switchable, because the right amount of normalization depends on the task: collapsing ta marbuta (ة → ه) helps a search index and hurts a morphological analyser, so it is off by default.

3. Detect code-switching, word by word

A single Moroccan sentence routinely carries three languages. Document-level language identification returns one label for that and is useless.

$ arabizi detect "salam, wach kayn chi rendez-vous dispo today?"
  salam        arabizi      function word
  wach         arabizi      function word
  kayn         arabizi      function word
  chi          arabizi      function word
  rendez-vous  unknown      Latin word, no decisive signal
  dispo        unknown      Latin word, no decisive signal
  today        unknown      Latin word, no decisive signal

  dominant: arabizi
  code-switched: False
from arabizi import detect, dominant_script, is_code_switched

for token in detect("salam خويا, wach l rendez-vous dyal today?"):
    print(token.text, token.script.value, token.evidence)

Evidence is applied strongest-first:

flowchart TD
    T["token"] --> A{"Arabic script?"}
    A -->|yes| AR["ARABIC"]
    A -->|no| N{"digits or<br/>punctuation only?"}
    N -->|yes| NUM["NUMBER /<br/>PUNCTUATION"]
    N -->|no| D{"2 3 5 6 7 8 9<br/>inside a Latin word?"}
    D -->|yes| AZ["ARABIZI<br/><i>near-certain</i>"]
    D -->|no| F{"French-only<br/>diacritic?"}
    F -->|yes| FR["FRENCH"]
    F -->|no| L{"in how many<br/>function-word lists?"}
    L -->|exactly one| ONE["that language"]
    L -->|several| AMB["UNKNOWN<br/><i>ambiguous</i>"]
    L -->|none| UNK["UNKNOWN<br/><i>no decisive signal</i>"]

    style AZ fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px
    style AMB fill:#fef7e0,stroke:#f9ab00,stroke-width:2px
    style UNK fill:#fef7e0,stroke:#f9ab00,stroke-width:2px

What it will not do

It does not guess. salam is Darija and salut is French, but normal is both and taxi is everyone's. A Latin word with no diacritic, no arabizi digit and no function-word entry cannot be resolved by rules, so it comes back UNKNOWN with the reason attached — not a coin flip dressed up as a label.

That is a deliberate trade. Use these labels as features for a classifier, not as the classifier. Every token carries its evidence string so a surprising result can be traced.

Two further limits worth knowing:

  • Transliteration is lossy and not reversible. Round-tripping will not give you the input back. Use it to widen recall, not as ground truth.
  • is_code_switched under-reports. It only counts languages it has real evidence for, so a sentence whose French words are all content words reads as single-language. Raising recall here means adding content words, which costs precision — the current setting favours precision.

Why it exists

Because Darija is badly served by general-purpose Arabic NLP, and because the gap is specific and fixable: the conventions differ from the Levant, the code-switching is constant, and no existing package handles either. This is the brick the rest of that work sits on.

Contributing

Spelling variants are the most useful contribution. Arabizi is not standardised — if you write a word differently than this package expects, that is a bug report. See CONTRIBUTING.md.

git clone https://github.com/novagiosAI/arabizi.git
cd arabizi
python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest

License

Apache License 2.0 — free for commercial use.


Built and maintained by Novagios — IT services, AI and ITSM.

Metadata

Release files for arabizi 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for arabizi 0.1.0
File Size Uploaded Provenance
arabizi-0.1.0.tar.gz 25.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for arabizi 0.1.0
File Interpreter ABI Platform
arabizi-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.6 kB

Release files / arabizi-0.1.0.tar.gz

Download URL arabizi-0.1.0.tar.gz
Size 25.3 kB
Tags Source
SHA-256 checksum
How to use checksums
dc0e5e719f16a3f2e198237e227ba3da75841fc4623856781f73f42c9a4fc7d0
BLAKE2b-256 checksum
How to use checksums
e8d3c298c390910fac267d5e15dcc77ee7b54643526f8f5165f8bf17001d9e9c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / arabizi-0.1.0-py3-none-any.whl

Download URL arabizi-0.1.0-py3-none-any.whl
Size 22.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
95699836b2449dcd2c5b0d31e3a4fd4e45dcfff88f27c1057fe1865a39a4d076
BLAKE2b-256 checksum
How to use checksums
c89ae0a6c45df433841ce5814fffe2ffc5b823de293de5fb00c668a0b42faade
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Security for arabizi 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for arabizi-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page