3amia · AI — C'est quoi ?

3amia · AI est un dictionnaire collaboratif des dialectes arabes, enrichi par l'intelligence artificielle. Chaque mot est documenté avec exemples, schèmes et traductions — grâce à la communauté et à l'IA.

3 692

mots

9

dialectes

0

contributions

3amia · AI doit son nom à deux racines arabes : زاد (zaada, «augmenter», «enrichir») et نا (na, «nous»). Littéralement : « Enrichissons ensemble ».

La 3amiya (l'arabe dialectal) est la langue du quotidien pour plus de 400 millions de personnes. Pourtant, elle est rarement documentée de façon structurée. Ce projet cherche à changer ça, un mot à la fois.

Chaque mot peut être accompagné d'une traduction française, d'un exemple d'utilisation, de ses variantes morphologiques et de ses équivalents dans d'autres dialectes.

Chaque contribution est analysée et enrichie automatiquement par l'intelligence artificielle — schème, exemple, traduction, type grammatical — pour garantir la qualité et la richesse de chaque fiche.

Pourquoi ce dictionnaire ?

Plus de 400 millions de personnes parlent l'arabe dialectal au quotidien : tunisien, marocain, algérien, égyptien, levantin… Pourtant, aucune ressource structurée n'existait pour documenter ces langues vivantes de façon ouverte et collaborative.

L'arabe classique (MSA) est bien documenté, mais personne ne l'utilise à la maison. Les dialectes, eux, évoluent, se mélangent, s'inventent — et disparaissent si on ne les note pas. Ce projet est une tentative de changer ça, un mot à la fois.

Ajouter un mot en 30 secondes

  1. 1

    Ouvre le formulaire

    Va sur /ajouter — pas besoin de compte ni d'inscription.

  2. 2

    Remplis les champs

    Le mot en arabe, sa traduction française, le dialecte. Un exemple d'usage si tu l'as.

  3. 3

    Envoie

    La contribution est examinée par l'équipe et publiée sous 24–48h.

Ce que fait (et ne fait pas) l'IA

À chaque contribution, Gemini 2.5 Flash analyse automatiquement le mot soumis. Il propose un schème morphologique et une racine consonantique, les deux piliers de la grammaire arabe.

L'IA ne publie rien toute seule. Ses suggestions arrivent dans l'interface d'administration, où un humain les valide, les corrige si besoin, et décide de la publication. L'IA complète, l'humain valide.

De la soumission à la publication

01

Détection des doublons

La contribution est comparée aux mots déjà en base — insensible aux diacritiques arabes.

02

Vérification de la translittération

Le système Maghreb chiffré (3=ع, 7=ح, 9=ق…) est validé automatiquement.

03

Analyse morphologique IA

Gemini propose le schème et la racine. L'admin les valide ou les corrige.

04

Publication

Une fois validé, le mot est publié et visible dans le dictionnaire sous 24–48h.

Recherche par racine consonantique

Explore la famille morphologique complète d'un mot.

Module QCM

Teste ton vocabulaire dialectal avec des quiz thématiques.

Expansion des dialectes

Algérien, marocain, égyptien… enrichissement en cours.

Audio & prononciation

Enregistrements natifs pour chaque mot.

Application mobile

Accès hors-ligne et notification "mot du jour".

Voir tout l'historique →
v1.22.11Juillet 2026 — FIX CRITIQUE (prod figée)
  • Boucle infinie sur /articles/[slug] — introduite en v1.22.10 (commit bd041f0) — ouvrir une fiche article gelait l'onglet (boucle infinie sur le thread principal). Cause confirmée : INLINE_RE, la regex /g du tokenizer inline, était déclarée au niveau module et réutilisée en RÉ-ENTRANCE (le parsing récursif sur le label d'un lien / l'intérieur d'un gras/italique appelait renderInline() récursivement avec le MÊME objet RegExp). Le dernier exec() d'un appel imbriqué qui retourne null réinitialise automatiquement lastIndex à 0 côté moteur JS — la boucle while de l'appel PARENT reprenait alors sa recherche depuis le début du texte au lieu de continuer où elle en était, rejouant indéfiniment le premier lien/gras rencontré. Reproduit isolément : sur une ligne aussi simple qu'un gras + un lien, l'ancienne logique ne termine jamais (confirmé — abandon manuel après 20 000 itérations en test isolé, sans le fix).
  • Tokenizing extrait en fonction pure — src/lib/articleInline.ts (nouveau, aucune dépendance React/JSX) : tokenizeInline(text, depth) instancie une regex fraîche (new RegExp(...)) à CHAQUE appel, y compris chaque appel récursif — jamais d'objet partagé. La branche "run arabe" est strictement une feuille (jamais de récursion dessus, 2ᵉ cause potentielle identifiée). Récursion sur lien/gras/italique bornée à MAX_INLINE_DEPTH = 3 (au-delà, contenu traité en feuille via tokenizeArabicOnly()). Garde-fou dur MAX_INLINE_ITERATIONS = 5000 dans la boucle while (throw silencieux → coupe et retourne, ne devrait jamais se déclencher avec les protections ci-dessus mais protège contre toute régression future).
  • ArticleContent.tsx — renderInline()/INLINE_RE (module-level) remplacés par un appel à tokenizeInline() (logique pure) + renderTokens() (mapping JSX pur, aucune regex). parseBlocks(), la détection des paires AR/FR (système bilingue des articles) et le composant a des blocs raw (liens /mot/... → next/link, ajouté en v1.22.10) sont inchangés — l'objectif de v1.22.10 reste acquis : gras rend du gras, [texte](/mot/x) rend un lien cliquable, dans les blocs section/bilingual comme dans les blocs raw.
  • Nouveau test src/lib/articleInline.test.ts (npm run test:article-inline) — 6 cas (ligne 100% arabe vocalisée, arabe+lien interne, terme arabe en gras, FR avec lien+gras mélangés, ligne mixte AR+FR, cas adversarial avec formations répétées denses) + vérification du garde-fou. Chaque cas doit se terminer sans exception en moins de 50ms. Le npm run build de ce projet ne peut pas détecter ce type de bug (pas de Supabase dans le sandbox → génération statique des pages articles jamais exercée, le rendu réel n'est jamais exercé) : ce test runtime est donc la seule garde-fou de non-régression pour ce composant. Vérifié : le test échoue (boucle non-terminante) contre l'ancienne implémentation, passe contre la nouvelle.
  • CLAUDE.md — 2 nouvelles règles permanentes (§ Gotchas développement) : regex /g jamais partagée en ré-entrance récursive (toujours new RegExp(...) local à la fonction) ; tout parsing récursif basé sur une regex/boucle while doit avoir un test runtime de terminaison dédié, tsc --noEmit/npm run build ne suffisent pas dans cet environnement.
v1.22.10Juillet 2026
  • Fix ArticleContent.tsx — syntaxe markdown affichée littéralement — gras et [lien](/mot/...) s'affichaient tels quels (astérisques/crochets visibles) dans les paires de lignes AR/FR (blocs section/bilingual de parseBlocks(), ex: titres arabes + sous-titre français, lignes séparées par <br>). Cause confirmée en base (article la-chaleur-en-arabe-classique) : ces blocs ne passent jamais par ReactMarkdown — seuls les blocs raw en bénéficiaient — et étaient rendus via un simple <p>{texte}</p>/renderFrLine() (isolation bidi arabe uniquement, aucun parsing markdown).
  • renderFrLine() remplacée par renderInline() — même isolation bidi des séquences arabes (dir="rtl", mêmes plages Unicode qu'avant), plus parsing inline pour lien [texte](url), gras texte, italique *texte* (récursif sur le contenu imbriqué). Appliquée aux lignes ar ET fr des blocs section/bilingual (le AR peut aussi contenir un lien, cf. l'article test). Aucun changement à parseBlocks() ni à la logique de détection des paires AR/FR (système bilingue des articles non touché).
  • Liens internes /mot/... → <Link> Next.js — renderInline() route les hrefs commençant par / vers next/link, le reste vers <a target="_blank" rel="noopener noreferrer">. Même traitement ajouté aux blocs raw existants via un nouveau composant a dans les components de ReactMarkdown (AnchorComponent, absent auparavant — seul strong était overridé pour le raccourci "mot en gras = lien vers sa fiche").
  • Vérifié : regex testée directement contre la ligne réelle en base (نَقُولُ [صَحْرَاءُ](/mot/sa7raa2-tn) عَنْ...) — lien et gras correctement extraits, séquences arabes isolées individuellement.
v1.22.9Juillet 2026
  • Fix autocomplete — mot anglais tapé directement introuvable — le mode field='fr' de autocompleteSearch() (api/search/route.ts) ne cherchait (via la RPC search_df_trad) que translation_fr : taper "know"/"help"/"dishes" directement ne remontait rien (il fallait taper la traduction française pour voir apparaître le mot). Ajout d'un ILIKE séparé sur translation_en (jamais .or() avec variable — règle PostgREST virgule), en parallèle de la RPC, fusionné par id via le Set de dédup existant. La RPC search_df_trad elle-même n'est pas touchée (v1.22.8 documentait déjà pourquoi : RETURNS TABLE(...) explicite, risque d'overloads dupliqués si modifiée — cf. v1.14.2/v1.14.3) ; l'enrichissement translation_en pour l'affichage des lignes RPC (déjà en place depuis v1.22.8) est conservé tel quel.
  • scoreRow() étendu à translation_en — paramètre optionnel translationEn (7ᵉ arg, rétro-compatible) propagé à scoreWordRelevance() (déjà capable de le prendre en compte depuis v1.22.7) ; matchType='trad' déclenché aussi sur exact/préfixe/contenu de translation_en, même priorité que translation_fr. Appliqué à tous les appels de scoreRow() (secondarySearch, dfRowToSuggestion, RPC MSA) pour cohérence, sans changer le ranking existant (arabe/latin > traduction > exemple).
  • Pas de priorisation par locale côté serveur — les deux champs sont cherchés sans distinction (le fallback explicitement accepté par la demande : détecter la locale serveur aurait nécessité un paramètre supplémentaire côté client non nécessaire ici). Tabs Transliterated/Arabic/French-English inchangés.
v1.22.8Juillet 2026
  • Fix autocomplete — traduction affichée figée en français — les suggestions de SearchBar.tsx affichaient toujours translation_fr, même en locale EN. Suggestion (api/search/route.ts) gagne un champ translation_en : ajouté aux select(...) explicites (secondarySearch, mode arab, variantes phonétiques, pool fallback 200-lignes) et lu directement sur les lignes RPC search_df_fuzzy/search_words_fuzzy (RETURNS SETOF dialect_forms/words — colonne déjà présente, aucun changement RPC). Seule search_df_trad (mode fr, RETURNS TABLE(...) explicite sans translation_en) ne l'expose pas : enrichissement par un lookup id séparé après l'appel RPC plutôt que modifier sa signature — évite le bug des overloads dupliqués sur RETURNS TABLE déjà documenté (v1.14.2/v1.14.3). Aucune RPC ni logique de matching touchée.
  • SearchBar.tsx : helper translationFor(s) — retourne translation_en si locale==='en' et non vide, sinon fallback translation_fr. Utilisé dans les 3 modes d'affichage (arabe/français/latin) y compris pour le texte passé à <HighlightText> — le surlignage matche donc désormais le bon champ selon la locale (ex: taper "know" surligne dans translation_en, pas translation_fr).
  • Tab "Français"/"English" adaptatif — le pill de mode fr (recherche par traduction) affiche désormais t('search.fieldEn') ("English"/"Anglais") en locale EN au lieu de traduire littéralement le mot "French". Nouvelle clé search.fieldEn dans fr.json/en.json. Aucune auto-détection de langue de la query n'existait déjà côté mode — non ajoutée (hors-scope, RPC backend inchangée).
v1.22.7Juillet 2026
  • Fix recherche par translation_en — le mode field='fr' de searchWords() (db.ts, mode par défaut de /dictionnaire) ne cherchait que translation_fr + example_fr : un mot tapé en anglais ne remontait jamais. Ajout d'un 3ᵉ ILIKE parallèle sur translation_en (jamais .or() — même règle PostgREST que les autres colonnes), dans searchDf() (dialect_forms) et searchMsa() (words MSA), fusionné dans l'ordre translation_fr → translation_en → example_fr. translation_en était déjà retourné par les select('*') existants, aucun changement de SELECT nécessaire.
  • Tri de pertinence étendu à translation_en — scoreWordRelevance() (search-utils.ts) accepte un 7ᵉ paramètre optionnel translationEn (rétro-compatible, tous les appels existants inchangés) : exact/préfixe/contenu sur translation_en évalués au même niveau de priorité que translation_fr (0.77/0.75/0.72). Ranking existant (arabe/latin > traduction > exemple) inchangé. Appel de tri dans searchWords() mis à jour pour passer translation_en.
  • Placeholder barre de recherche — mention des 3 langues — search.placeholderFr (fr.json/en.json), affiché par défaut sur /dictionnaire (mode fr initial), disait uniquement "Chercher une traduction française…" / "Search a French translation…". Corrigé en "Chercher en arabe, français ou anglais…" / "Search in Arabic, French or English…" — déjà conditionné par useLocale()/t(), aucun changement de code dans SearchBar.tsx.
v1.22.6Juillet 2026
  • Fiche article — bloc de partage remonté sous le header — le bloc "Partager cet article" (src/app/articles/[slug]/page.tsx) était placé après le contenu Markdown et la navigation série, tout en bas de page. Déplacé juste après le bloc Auteur/date, avant ArticleContent — visible sans scroller jusqu'au bas de l'article. Déplacement JSX pur, logique de partage (liens Twitter/WhatsApp) inchangée.
  • Icône WhatsApp officielle — public/icons/whatsapp.png (128×128, généré à partir du path officiel simple-icons — fond vert #25D366, glyphe blanc — via sharp, aucun asset copié depuis un CDN externe non accessible depuis ce réseau). Remplace le texte seul "WhatsApp" par <img src="/icons/whatsapp.png" width={24} height={24} /> + libellé, cohérent avec le bouton Twitter à côté.
v1.22.5Juillet 2026
  • Fiche mot — "Je l'utilise" remonté au-dessus du pli mobile — la section WordContribution (bouton "Je l'utilise" + 3 autres tabs) est déplacée juste après le bloc hero (forme arabe + translittération + traduction), avant "Exemple d'utilisation"/"Équivalents dans d'autres dialectes" (grille Details), Morphologie, Famille de mots, Même schème, Déclinaisons, Régions d'usage et Action links. Objectif : sur mobile, "Je l'utilise" est visible sans scroller jusqu'en bas de la fiche. Aucun changement de logique de soumission — déplacement JSX pur dans src/app/mot/[slug]/page.tsx.
v1.22.4Juillet 2026
  • WordCard — exemple arabe affiché en permanence — le prop showExample (n'affichait l'exemple que si !!query, donc seulement en résultats de recherche) est supprimé ; l'exemple arabe (word.example_phrase) s'affiche désormais dans toutes les situations où WordCard est utilisé dès qu'il existe : /dictionnaire (browse ET recherche), page d'accueil ("Mots récents"), /racine/[racine], section mots liés de /mot/[slug]. Condition word.example_phrase && déjà en place — aucun espace vide laissé pour les mots sans exemple.