Diacríticos Árabes: Como Amal Ensina Tashkeel, Shadda e Hamza
5 min de leituraMohammad Shaker

Diacríticos Árabes: Como Amal Ensina Tashkeel, Shadda e Hamza

Amal lida com a complexidade dos diacríticos árabes, incluindo tashkeel, variantes de alef, hamza e ligaduras Lam-Alef.

Engineering

Resposta rápida

Amal lida com a complexidade dos diacríticos árabes, incluindo tashkeel, variantes de alef, hamza e ligaduras Lam-Alef.

Amal lida com toda a complexidade dos diacríticos árabes: 8 marcas de tashkeel (fatha, damma, kasra, shadda, sukun, fathatan, dammatan, kasratan), 4 variantes de alef (padrão, madda, hamza acima, hamza abaixo, wasla), 3 variantes de hamza (isolado, sobre waw, sobre yeh) e ligaduras Lam-Alef. O reconhecimento de fala, a renderização de texto e a pontuação de similaridade do aplicativo tratam o árabe diacritizado ("كَتَبَ") de forma diferente do árabe não diacritizado ("كتب") — uma distinção crucial que muitos apps de aprendizado de árabe ignoram.

Por que os Diacríticos São Importantes para o Aprendizado

O Problema da Ambiguidade

O árabe sem diacríticos é ambíguo:

  • "كتب" pode significar:
    • "kataba" (ele escreveu) — tempo passado
    • "kutub" (livros) — substantivo plural
    • "kutiba" (foi escrito) — voz passiva

Todos são escritos da mesma forma sem os diacríticos. Os diacríticos eliminam a ambiguidade.

Progressão do Aprendizado

  1. Iniciante: Aprender a ler COM diacríticos (fácil — vogais marcadas)
  2. Intermediário: Praticar COM diacríticos até se tornar automático
  3. Avançado: Gradualmente remover diacríticos, a leitura fica mais difícil
  4. Fluente: Ler sem diacríticos fluentemente (leitura em nível nativo)

A maioria dos apps de aprendizado de árabe ignora o passo 1 — eles não ensinam os diacríticos ou os removem. Isso gera maus hábitos. A progressão do Amal é cientificamente correta.

Nossa Implementação em Nível Unicode

Marcas Diacríticas (8 no total)

// lib/src/utils/arabic_extension.dart
class ArabicExtension {
  static const Map<String, String> tashkeelMarks = {
    'FATHA': '\u064E',      // َ (vogal 'a')
    'DAMMA': '\u064F',      // ُ (vogal 'u')
    'KASRA': '\u0650',      // ِ (vogal 'i')
    'SUKUN': '\u0652',      // ْ (sem vogal)
    'SHADDA': '\u0651',     // ّ (letra duplicada)
    'FATHATAN': '\u064B',   // ً (tanween 'an')
    'DAMMATAN': '\u064C',   // ٌ (tanween 'un')
    'KASRATAN': '\u064D',   // ٍ (tanween 'in')
  };

static const Map<String, String> alefVariants = { 'ALEF_STANDARD': 'ا', // ا 'ALEF_WITH_MADDA': 'آ', // آ (alongado) 'ALEF_WITH_HAMZA_ABOVE': 'أ', // أ 'ALEF_WITH_HAMZA_BELOW': 'إ', // إ 'ALEF_WASLA': 'ٰ', // ٰ (alef conectivo) };

static const Map<String, String> hamzaVariants = { 'HAMZA_ISOLATED': 'ء', // Hamza isolado 'HAMZA_ON_WAW': 'ؤ', // Hamza sobre waw (و + hamza) 'HAMZA_ON_YEH': 'ئ', // Hamza sobre yeh (ي + hamza) }; }

Diacríticos Corânicos e Paradas Uthmani

Para Thurayya, suportamos marcas específicas do Alcorão:

static const Map<String, String> quranicMarks = {
  'STOP_FULL': 'ۖ',         // Parada completa (‖)
  'STOP_HALF': 'ۗ',         // Parada meia
  'STOP_QUA': 'ۙ',          // Parada Qua
  'STOP_NECESSARY': 'ۚ',     // Parada necessária
  'TAJWEED_ELONGATION': '۝', // Indicador de alongamento
};

Reconhecimento de Fala Compatível com Diacríticos

Viés de Contexto com Diacríticos

Quando uma criança está aprendendo "كَتَبَ" (ele escreveu, passado), direcionamos o reconhecimento de fala para essa vocalização exata:

# src/services/stt_client.py
def recognize_with_diacritical_context(audio_bytes, expected_text):
    # expected_text = "كَتَبَ" (com diacríticos)
# Criar dica de contexto de fala
speech_context = {
    'phrases': [expected_text],
    'boost': 20.0  # Grande ênfase para o texto esperado
}

# Enviar para o Google Cloud STT
response = google_stt_client.recognize(
    audio=audio_bytes,
    language_code='ar-SA',
    speech_contexts=[speech_context]
)

# Resultado: Google STT é direcionado à pronúncia "kataba"
return response

Pontuação de Similaridade Consciente de Diacríticos

def compare_pronunciations(expected, actual):
    """
    expected: "كَتَبَ" (com diacríticos)
    actual: "كتب" (tentativa da criança, possivelmente sem diacríticos)
    """
# Remover diacríticos para comparação grosseira
expected_base = strip_diacritics(expected)  # "كتب"
actual_base = strip_diacritics(actual)      # "كتب"

# Similaridade base (ignorando diacríticos)
base_similarity = string_similarity(expected_base, actual_base)  # 1.0 (perfeito)

# Bônus de diacríticos (se tentativa inclui diacríticos)
diacritic_bonus = 0.0
if has_diacritics(actual):
    diacritic_accuracy = diacritics_match_ratio(expected, actual)
    diacritic_bonus = diacritic_accuracy * 0.15  # Até +15% por diacríticos corretos

# Pontuação final
final_score = min(base_similarity + diacritic_bonus, 1.0)

return {
    'base_score': base_similarity,
    'diacritic_bonus': diacritic_bonus,
    'final_score': final_score,
    'feedback': 'Ótimo! A pronúncia está perfeita. Agora, pratique as marcas diacríticas.'
}

Isso significa:

  • Criança diz "كتب" (sem diacríticos) → pontuação 85-90% (base correta, faltando diacríticos)
  • Criança diz "كَتَبَ" (totalmente diacritizado) → pontuação 98%+ (perfeito)
  • Progressão clara: primeiro dominar pronúncia base, depois detalhes diacríticos

Desafios na Renderização RTL

Gerenciamento de Direção do Texto

// lib/src/screens/lesson_screen.dart
Column(
  children: [
    Directionality(
      textDirection: TextDirection.rtl,  // Para texto árabe
      child: Text(
        'كَتَبَ',
        textAlign: TextAlign.right,      // Alinhado à direita para RTL
        style: TextStyle(
          fontFamily: 'IBMPlexSansArabic',
          fontSize: 36,
          height: 1.8,  // Altura extra para diacríticos
        ),
      ),
    ),
    // Instruções em inglês abaixo
    Directionality(
      textDirection: TextDirection.ltr,  // Para inglês
      child: Text(
        'Pronounce: "he wrote"',
        textAlign: TextAlign.left,       // Alinhado à esquerda para LTR
      ),
    ),
  ],
)

Formação das Letras Conectadas

Letras árabes mudam de forma dependendo da posição:

  • Isolada: "ك" (Kaf)
  • Inicial: "كَـــ" (Kaf no início da palavra)
  • Medial: "ـــكَـــ" (Kaf no meio)
  • Final: "ـــكَ" (Kaf no fim)

A fonte IBMPlexSansArabic forma automaticamente as letras, mas precisamos das sequências Unicode corretas:

// Correto: usa caracteres de ligação Unicode
String word = 'ك' + '\u0640' + 'ت' + '\u0640' + 'ب';  // Kashida (caractere de extensão)

// Incorreto: concatenação direta String word = 'ك' + 'ت' + 'ب'; // Pode não formar corretamente em alguns dispositivos

Mixagem de Texto Bidirecional

Quando inglês e árabe aparecem juntos:

RichText(
  textDirection: TextDirection.rtl,  // RTL geral
  text: TextSpan(
    children: [
      TextSpan(text: 'means ', style: englishStyle),  // LTR
      TextSpan(text: 'كتاب', style: arabicStyle),    // RTL
      TextSpan(text: ' (book)', style: englishStyle), // LTR
    ],
  ),
)

Resultado: "means كتاب (book)" exibido com fluxo bidirecional correto.

Perguntas Frequentes

P: Por que forçar diacríticos para iniciantes? Isso não dificulta?
A: Inicialmente, sim. Mas aprender com diacríticos cria associações mais fortes entre letra e som. Pesquisas mostram que o aprendizado com diacríticos acelera a fluência. Depois de dominar com diacríticos, ler sem eles é uma progressão natural.

P: E se o teclado do meu filho não suporta digitar diacríticos?
A: O app nunca pede para as crianças digitarem diacríticos. O reconhecimento e pronúncia são baseados em fala. Apenas adultos (professores, criadores de conteúdo) precisam inserir diacríticos, usando teclados árabes especializados.

P: Amal suporta combinações diacríticas não padrão?
A: Suportamos todas as combinações padronizadas pelo Unicode. Combinações raras ou customizadas podem não renderizar corretamente, mas o árabe moderno e o corânico padrão são totalmente suportados.

Artigos Relacionados