O problema dos digrafos que ninguém conta
Trabalhar com digrafos em processamento de texto é um daqueles detalhes que parecem simples até você encontrar um bug estranho no meio de uma produção. Na prática, um digrafo é uma sequência de dois caracteres que representa um único fonema — o "nh", o "lh", o "ch" do português são os exemplos mais óbvios. Mas a complexidade aparece quando você tenta fazer ordenação alfabética, busca, normalização ou segmentação de palavras e o sistema não reconhece essas combinações como unidades. Já perdi horas tentando entender por que uma listagem de nomes em um relatório ficava totalmente bagunçada. "Ninho" aparecia depois de "nicho" e antes de "nitrogênio", quando na verdade deveria seguir uma lógica diferente. O problema não era o algoritmo de em si — era que eu estava comparando caractere por caractere sem considerar que "nh" funciona como uma letra própria no contexto collation do português.
Como funciona a ordenação de texto com digrafos
O conceito central é que o sistema de collation precisa tratar sequências como "nh", "lh" e "ch" como unidades indivisíveis durante a comparação. No padrão Unicode, isso se relaciona com os rules de ordination do UCA (Unicode Collation Algorithm). Para o português brasileiro, a versão mais recente das colações do CLDR já inclui regras específicas para esses digrafos, mas elas nem sempre são o padrão ativo das bibliotecas que você instala por padrão. Na prática, se você estiver usando Java, o caminho mais direto é o Collator com a locale pt_BR. Se estiver em Python, o pacote pyuca ou o uso de icu4c via pyicu são as opções que realmente funcionam. Em JavaScript, o Intl.Collator com localesspecifier adequado resolve para a maioria dos casos, mas testei em Node.js antigo e encontrei compportamento inconsistente com digrafos em certas versões — atualizar para Node 18+ resolveu sem dor de cabeça.
O que a maioria dos tutoriais não menciona é que a ordenação por digrafo depende fortemente do contexto. "Chega" e "chiappa" vão se comportar de formas diferentes dependendo se o "ch" é tratado como digrafo ou como dois caracteres separados. No português, "ch" é considerado digrafo, então "che" vem antes de "chi". Em espanhol também, mas em catalão a coisa muda um pouco. Se seu sistema precisa lidar com múltiplos idiomas, ter uma regra fixa para digrafos já é simplificação demais.
Normalização e a armadilha do NFD
Outro ponto onde as pessoas tropeçam é na normalização de strings. Quando você aplica NFD (Normalization Form Decomposition), digrafos como "ç" podem ser decompostos em "c" + "cedilha combinante", mas "nh" permanece como dois caracteres separados mesmo na decomposição. Isso significa que uma pipeline de normalização que funciona bem para acentuação simplesmente quebra para digrafos. Minha experiência com isso veio de um projeto de indexação de documentos onde estávamos normalizando tudo para NFD antes de calcular hashes. Palavras com "nh" geravam hashes diferentes das mesmas palavras escritas com "n" e "h" normais, o que gerava duplicatas fantasmas no índice. A solução foi aplicar uma etapa adicional de identificação de digrafos pós-normalização, mapeando "n" + "h" de volta para um token único antes da hash.
Se você está apenas lendo e exibindo texto, provavelmente nunca vai se deparar com isso. Mas assim que entra segmentação, fuzzy matching ou deduplicação, o custo de ignorar digrafos cresce rápido.
Implementação prática: texto com digrafos no dia a dia
Vou mostrar um exemplo funcional em Python porque é a stack mais comum que vejo nesse tipo de problema. A ideia é criar uma função que reconheça digrafos e os trate como unidades durante comparação e ordenação. O código abaixo usa o PyICU para aproveitar o motor de collation do ICU, que já entende digrafos do português de forma nativa:
import icu
Criando um collator específico para português brasileiro collator = icu.Collator.createInstance(icu.Locale("pt_BR"))
Definindo o nível de força para considerar digrafos corretamente
collator.setStrength(icu.Collator.PRIMARY)
palavras = ["ninho", "nicho", "nitrogênio", "chave", "charuto"] palavras_ordenadas = sorted(palavras, key=lambda x: collator.getSortKey(x))
print(palavras_ordenadas) Esse script produz a ordem correta considerando "nh" e "ch" como digrafos. Sem o PyICU e usando comparação simples de strings, a saída seria diferente porque o Python compara bytes Unicode ponto a ponto.
Se você não pode instalar dependências nativas como o pyicu, existe uma abordagem puramente em Python que usa um mapeamento de prioridade. A ideia é transformar cada palavra em uma tupla onde digrafos recebem valores especiais:
👉 Clique no botão abaixo para saber mais sobre o assunto!
DIGRAFOS = ["ch", "lh", "nh"]
def chave_ordenação(texto): resultado = []
i = 0 while i < len(texto):
par = texto[i:i+2] if par in DIGRAFOS:
resultado.append((1, par)) i += 2
else: resultado.append((0, texto[i]))
i += 1 return resultado
palavras = ["ninho", "nicho", "nitrogênio", "chave", "charuto"]
print(sorted(palavras, key=chave_ordenação)) Essa função manual é suficiente para cases simples, mas tem limitações sérias. Ela não lida com maiúsculas e minúsculas, não considera variantes diacríticas e quebra se o digrafo aparecer no final da string de forma ambígua. Para produção, o PyICU ou equivalente em outras linguagens é muito mais confiável.
Fuzziness e pesquisa com digrafos
Um problema que apareceu diretamente no meu trabalho foi implementar busca aproximada (fuzzy search) em um banco de registros com nomes próprios. O sistema de edit distance padrão conta "n" + "h" como duas substituições diferentes de "nh", o que inflaciona erroneamente a distância entre "anho" e "anhelo". A solução que adotei foi pré-processar tanto o termo de busca quanto os documentos da coleção, substituindo cada digrafo por um caractere Unicode privado da área U+Fxxx (usa-se o range U+E000-U+F8FF, os Private Use Areas do Unicode). Depois disso, a distância de edição funciona normalmente porque o digrafo é tratado como um único símbolo.
O detalhe importante é que esse mapeamento precisa ser feito de forma consistente em todos os estágios do pipeline — entrada, índice e consulta. Se um deles pular a substituição, a correspondência falha silenciosamente. Eu configurei uma função de transformação em um único ponto da codebase e importei ela em todos os módulos que precisavam, o que evitou inconsistências.
Limitações e quando essa abordagem não funciona
O que eu diria para quem está começando com isso é: fique atento aos casos onde digrafos simplesmente não se aplicam. Em nomes próprios estrangeiros, por exemplo, "nh" pode ser apenas duas letras adjacentes sem função de digrafo — "John Smith" não tem digrafo nenhum. Um collator configurado para português vai tratar o "nh" de "Smith" como unidade, o que distorce a ordenação. Outro ponto: digrafos não são o único fenômeno fonológico que causa problemas. Encontros consonantais como "rr", "ss", "sc", "sç" e até trigravas como "tlh" em nahuatl também precisam de tratamento especial em alguns contextos. Se seu sistema precisa lidar com textos multilingues de verdade, a lista de exceções cresce rápido e a manutenção manual vira um inferno.
A alternativa mais robusta para cenários complexos é delegar todo o tratamento de collation para bibliotecas mature como o ICU (usado em Java, C++, Python via pyicu, e disponível via Intl API no JavaScript moderno). Essas bibliotecas já embutem centenas de regras linguísticas e são atualizadas regularmente pelo CLDR. O custo é a dependência de bibliotecas nativas, o que pode ser um problema em ambientes com restrições de deployment como Lambda functions ou containers minimalistas. Em resumo, o texto com digrafos é um problema real e recorrente em processamento de linguagem. A boa notícia é que as ferramentas existentes resolvem a maior parte dos casos. A má notícia é que saber que elas existem e saber quando usá-las corretamente é o que diferencia um sistema que ordena nomes de forma aceitável de um que ordena de forma silenciosamente errada.