-
Notifications
You must be signed in to change notification settings - Fork 0
How It Works
mmaher88 edited this page Mar 9, 2026
·
1 revision
Laren is structured as a 4-layer stack. Only the outermost layer (Engine) depends on Fcitx5 — everything else is a standalone C++ library.
graph TB
subgraph "Fcitx5 Process"
E["🔌 Engine Layer<br/><i>laren_engine + laren_state</i><br/>Fcitx5 addon, key events, UI"]
end
subgraph "Laren Core (standalone C++)"
C["⚙️ Core Layer<br/><i>transliterator + rule_engine + ranker</i><br/>Transliteration pipeline"]
D["📖 Dictionary Layer<br/><i>dictionary + trie</i><br/>375k Arabic words"]
U["🔧 Util Layer<br/><i>unicode + mmap_file</i><br/>UTF-8/UTF-32, memory mapping"]
end
E --> C
C --> D
D --> U
style E fill:#fff3e0,stroke:#e65100
style C fill:#e3f2fd,stroke:#1565c0
style D fill:#e8f5e9,stroke:#2e7d32
style U fill:#f3e5f5,stroke:#6a1b9a
When you type a character, it flows through this pipeline:
flowchart TD
A["⌨️ Key Press"] --> B["Buffer: 'salam'"]
B --> C["1. Normalize<br/><i>lowercase, collapse repeats</i>"]
C --> D["2. DFS Expand<br/><i>generate all possible Arabic forms</i>"]
D --> E{"3. Dictionary Filter"}
E -->|"Match found"| F["📖 Dictionary Candidates<br/><i>ranked by frequency</i>"]
E -->|"No match"| G["🔤 Skeletal Fallback<br/><i>top expansions as-is</i>"]
F --> H["4. Rank & Sort"]
G --> H
H --> I["5. History Boost<br/><i>user's preferred choice → top</i>"]
I --> J["📋 Candidate List<br/><i>up to 50 results, paginated</i>"]
style A fill:#e1f5fe
style J fill:#c8e6c9
style F fill:#fff9c4
style G fill:#ffecb3
The Rule Engine uses depth-first search to generate every possible Arabic interpretation of the input. Each Latin character can map to multiple Arabic letters.
graph TD
S["s"] -->|"س (seen)"| A1["a"]
S -->|"ص (sad)"| A2["a"]
A1 -->|"SKIP<br/><i>short vowel</i>"| L1["l → ل"]
A1 -->|"ا (alef)"| L2["l → ل"]
A2 -->|"SKIP"| L3["l → ل"]
A2 -->|"ا (alef)"| L4["l → ل"]
L1 --> A3["a → SKIP"] --> M1["m → م"]
L2 --> A4["a → SKIP"] --> M2["m → م"]
M1 --> R1["سلم"]
M2 --> R2["سالم"]
style R1 fill:#c8e6c9
style R2 fill:#c8e6c9
style S fill:#e1f5fe
The tree branches at every character. With max_expansions = 4096, Laren explores thousands of paths, then filters through the dictionary to find real Arabic words.
Arabic script doesn't write short vowels. The SKIP sentinel (value 0) represents this:
"salam" with SKIP vowels → سلام (correct Arabic)
"salam" with explicit alef → سالاام (over-vowelized, not a real word)
graph LR
subgraph "SKIP path (correct)"
A1["s→س"] --> A2["a→∅"] --> A3["l→ل"] --> A4["a→ا"] --> A5["m→م"]
A5 --> R1["سلام ✅"]
end
subgraph "Explicit path (wrong)"
B1["s→س"] --> B2["a→ا"] --> B3["l→ل"] --> B4["a→ا"] --> B5["m→م"]
B5 --> R2["سالام ❌"]
end
style R1 fill:#c8e6c9
style R2 fill:#ffcdd2
sequenceDiagram
participant App as Application
participant KWin as KWin / Compositor
participant Fcitx as Fcitx5
participant Laren as Laren Engine
App->>KWin: Key press event
KWin->>Fcitx: Forward to input method
Fcitx->>Laren: processKeyEvent()
alt Printable character
Laren->>Laren: Append to buffer
Laren->>Laren: transliterate(buffer)
Laren-->>Fcitx: Update candidate list
Fcitx-->>App: Show preedit + candidates
else Space / Enter / Number
Laren->>Laren: Commit selected candidate
Laren->>Laren: Save to history
Laren-->>Fcitx: commitString(arabic + " ")
Fcitx-->>App: Insert Arabic text
else Escape
Laren->>Laren: Clear buffer
Laren-->>Fcitx: Reset input panel
end
graph LR
subgraph "No external deps"
util["util/<br/>unicode.cpp<br/>mmap_file.cpp"]
end
subgraph "No external deps"
dict["dict/<br/>dictionary.cpp<br/>trie.cpp"]
end
subgraph "No external deps"
core["core/<br/>transliterator.cpp<br/>rule_engine.cpp<br/>ranker.cpp"]
end
subgraph "Fcitx5 dependency"
engine["engine/<br/>laren_engine.cpp<br/>laren_state.cpp"]
end
engine --> core --> dict --> util
style engine fill:#fff3e0
style core fill:#e3f2fd
style dict fill:#e8f5e9
style util fill:#f3e5f5
Only engine/ links against Fcitx5. The core transliteration logic can be used independently.