# Normalización de búsqueda en árabe: Falta compatibilidad con variantes de Hamza, formas de Ya/Kaf y equivalencia ortográfica

**URL:** <https://meta.discourse.org/t/arabic-search-normalization-missing-support-for-hamza-variants-ya-kaf-forms-and-orthographic-equivalence/384253>\
**Category:** Feature\
**Tags:** search, pr-welcome, localization, rtl\
**Created:** [29 Septiembre, 2025 08:27 UTC](https://meta.discourse.org/t/arabic-search-normalization-missing-support-for-hamza-variants-ya-kaf-forms-and-orthographic-equivalence/384253 "2025-09-29T08:27:57Z")\
**Posts on this page:** 1\
**Showing post:** 10

<div class="post-metadata">

**Author:** ![serkhelesheyi](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/serkhelesheyi/32/497726_2.png) [@serkhelesheyi](https://meta.discourse.org/u/serkhelesheyi)\
**Post date:** [2 Octubre, 2025 07:31 UTC](https://meta.discourse.org/t/arabic-search-normalization-missing-support-for-hamza-variants-ya-kaf-forms-and-orthographic-equivalence/384253/10 "2025-10-02T07:31:45Z")

</div>

No soy un experto técnico, pero he estado investigando este problema porque quiero asegurarme de que no se pierdan consultas de búsqueda en un foro bilingüe persa-árabe de Discourse. Dado que Discourse utiliza **PostgreSQL** , la normalización se vuelve esencial: un usuario podría buscar usando caracteres persas, mientras que la misma palabra se almacena usando caracteres árabes, o viceversa. Sin una normalización adecuada, la búsqueda fallará.  
Según lo que he aprendido, usar la normalización NFKC de Unicode es un buen punto de partida: maneja muchos casos de compatibilidad como ligaduras, formas de presentación y dígitos árabes/persas.

Sin embargo, para el texto persa y árabe, **NFKC por sí solo no es suficiente**. No normaliza varias variantes de caracteres críticas que son visual y semánticamente equivalentes pero difieren a nivel binario.

**A continuación, describo los procedimientos y las ideas a las que he llegado a través de mi investigación y exploración.**

* * *

## 🔧 Estrategia General de Diseño

1. **Aplicar primero la normalización NFKC de Unicode** para manejar ligaduras, formas de presentación y unificación de dígitos.
2. **Luego aplicar mapeos de caracteres personalizados** en un orden definido (por ejemplo, normalizar variantes de Hamza antes que Ya árabe).
3. **Separar las políticas de normalización para almacenamiento vs. búsqueda** :
  - Usar un perfil **Conservador** para el almacenamiento canónico (preservar ZWNJ, evitar cambios semánticos).
  - Usar un perfil **Permisivo** para la búsqueda (ignorar ZWNJ, unificar variantes de Hamza, normalizar dígitos).

4. Todos los mapeos deben ser **configurables** a través de una tabla de mapeo centralizada en la base de datos o un hash de Ruby en la aplicación.

* * *

## 1️⃣ Perfiles de Normalización

### 🟢 Conservador (para almacenamiento)

- Transformación mínima
- Aplicar NFKC
- Normalizar Kaf/Ya árabe a equivalentes persas
- Eliminar diacríticos
- Preservar ZWNJ
- Almacenar como `texto_original` + `normalizado_conservador`

### 🔵 Permisivo (para búsqueda)

- Coincidencia agresiva
- Aplicar todas las reglas Conservadoras
- Eliminar/ignorar ZWNJ
- Normalizar variantes de Hamza a letras base
- Convertir todos los dígitos a ASCII
- Opcionalmente unificar Taa Marbuta → Heh
- Usado para preprocesamiento de consultas

* * *

## 2️⃣ Tabla de Mapeo Integral

| Fuente | Destino | Unicode | Notas |
| --- | --- | --- | --- |
| `ك` | `ک` | U+0643 → U+06A9 | Kaf árabe → Kaf persa |
| `ي` | `ی` | U+064A → U+06CC | Ya árabe → Ya persa |
| `ى` | `ی` | U+0649 → U+06CC | Variante final de Ya |
| `أ`, `إ`, `ٱ` | `ا` | Varios → U+0627 | Formas de Hamza → Alef |
| `ؤ` | `و` | U+0624 → U+0648 | Hamza Waw |
| `ئ` | `ی` | U+0626 → U+06CC | Hamza Ya |
| `ء` | — | U+0621 | Eliminar o preservar (configurable) |
| `ة` | `ه` | U+0629 → U+0647 | Taa Marbuta → Heh (opcional) |
| `ۀ` | `هٔ` | U+06C0 ↔ U+0647+U+0654 | Normalizar forma compuesta |
| `ڭ` | `گ` | U+06AD → U+06AF | Variantes regionales |
| `‌` | — | U+200C | ZWNJ: preservar en Conservador, eliminar en Permisivo |
| `٤`, `۴` | `4` | U+0664, U+06F4 → ASCII | Normalizar dígitos |
| Diacríticos | — | U+064B–U+0652 | Eliminar todos los harakat |
| ZWJ | — | U+200D | Eliminar uniones invisibles |
| Espacios múltiples | Espacio simple | — | Normalizar espaciado |

* * *

## 3️⃣ Fragmento de Mapeo Rápido (para SQL o Ruby)

```plaintext
ك → ک
ي → ی
ى → ی
أ → ا
إ → ا
ؤ → و
ئ → ی
ة → ه
ۀ → هٔ
ٱ → ا
٤, ۴ → 4
ZWNJ (U+200C) → (eliminado en permisivo)
Harakat (U+064B..U+0652) → eliminado
ZWJ (U+200D) → eliminado

```

* * *

## 4️⃣ Implementación en PostgreSQL

- Crear una tabla `text_normalization_map`
- Usar `regexp_replace` o cadenas `TRANSLATE` para rendimiento
- Opcionalmente implementar en PL/Python o PL/v8 para soporte Unicode
- Normalizar tanto el contenido almacenado como las consultas entrantes utilizando la misma lógica

### Estrategia de Indexación

- Almacenar `normalized_conservative` para indexación canónica
- Normalizar consultas con `normalize_persian_arabic(query, 'permissive')`
- Si se usa búsqueda permisiva, el índice debe coincidir con el mismo perfil
- Opcionalmente almacenar ambas versiones para comparación cruzada

* * *

## 5️⃣ Ejemplo de Hash de Ruby (para Discourse)

```ruby
NORMALIZATION_MAP = {
  "ك" => "ک",
  "ي" => "ی",
  "ى" => "ی",
  "أ" => "ا",
  "إ" => "ا",
  "ٱ" => "ا",
  "ؤ" => "و",
  "ئ" => "ی",
  "ة" => "ه",
  "ۀ" => "هٔ",
  "۴" => "4",
  "٤" => "4",
  "\u200C" => "", # ZWNJ
  "\u200D" => "", # ZWJ
}

```

* * *

## 6️⃣ Notas de Rendimiento y Prácticas

1. Aplicar **NFKC en la capa de aplicación** (por ejemplo, `unicode_normalize(:nfkc)` de Ruby)
2. Usar **índices separados** para perfiles conservadores vs. permisivos
3. Evitar mapeos forzados de caracteres semánticamente sensibles (por ejemplo, Hamza, Taa Marbuta) a menos que se configuren explícitamente
4. Ejecutar **pruebas A/B** con datos reales del foro para medir la tasa de aciertos y los falsos positivos
5. Documentar cada mapeo con justificación y ejemplos
6. Definir **pruebas unitarias** tanto en Ruby como en SQL para cada mapeo

* * *

## 7️⃣ Recomendación Final

- Usar **Unicode NFKC** como base
- Extenderlo con una **capa de mapeo personalizada**
- Mantener **perfiles duales** para almacenamiento y búsqueda
- Implementar la normalización tanto en la capa de la aplicación como en la de la base de datos
- Documentar y probar cada mapeo
- Construir índices apropiados (`GIN + to_tsvector`) en columnas normalizadas

---

_[View the full topic](https://meta.discourse.org/t/arabic-search-normalization-missing-support-for-hamza-variants-ya-kaf-forms-and-orthographic-equivalence/384253)._
