Considering only the implementation of the algorithm I have a function adapted to Mysql in which the phonemes have been analyzed and improved as time goes by to reflect the searches performed in some systems. It may contain some inconsistencies (as in the case of "W" which has no defined rule), but solves the vast majority of cases. The details of the implementation are commented on in the course of the code:
DROP FUNCTION IF EXISTS transformar_fonetica;
DELIMITER $
CREATE FUNCTION transformar_fonetica(ptexto TEXT)
RETURNS TEXT
BEGIN
DECLARE vtexto TEXT;
DECLARE vtexto_apoio TEXT;
DECLARE vposicao_atual INT;
DECLARE vcaracter_anterior VARCHAR(1);
DECLARE vcaracter_atual VARCHAR(1);
DECLARE vcaracter_seguinte VARCHAR(1);
DECLARE vsom VARCHAR(2);
DECLARE com_acentos VARCHAR(65);
DECLARE sem_acentos VARCHAR(65);
SET vtexto = UPPER(ptexto);
SET com_acentos = 'ŠšŽžÀÁÂÃÄÅÆÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝŸÞàáâãäåæèéêëìíîïñòóôõöøùúûüýÿþƒ';
SET sem_acentos = 'SsZzAAAAAAAEEEEIIIINOOOOOOUUUUYYBaaaaaaaeeeeiiiinoooooouuuuyybf';
SET vposicao_atual = LENGTH(com_acentos);
-- Remove acentos
WHILE vposicao_atual > 0 DO
SET vtexto = REPLACE(vtexto, SUBSTRING(com_acentos, vposicao_atual, 1), SUBSTRING(sem_acentos, vposicao_atual, 1));
SET vposicao_atual = vposicao_atual - 1;
end while;
-- Remove caracteres inválido
SET vposicao_atual = 1;
WHILE vposicao_atual <= LENGTH(vtexto) DO
SET vcaracter_atual = SUBSTRING(vtexto, vposicao_atual, 1);
IF INSTR('ABCÇDEFGHIJKLMNOPQRSTUVWXYZ ', vcaracter_atual) <> 0 THEN
SET vtexto_apoio = CONCAT(IFNULL(vtexto_apoio, ''), vcaracter_atual);
END IF;
SET vposicao_atual = vposicao_atual + 1;
END WHILE;
SET vtexto = vtexto_apoio;
-- Substitui os mais simples
SET vtexto = REPLACE(vtexto, 'Ç', 'S');
SET vtexto = REPLACE(vtexto, 'SH', 'X');
SET vtexto = REPLACE(vtexto, 'XC', 'S');
SET vtexto = REPLACE(vtexto, 'QU', 'K');
SET vtexto = REPLACE(vtexto, 'CH', 'X');
SET vtexto = REPLACE(vtexto, 'PH', 'F');
SET vtexto = REPLACE(vtexto, 'LH', 'LI');
SET vtexto = REPLACE(vtexto, 'NH', 'NI');
-- Remove duplicados. Menos o S que altera o som da sílaba
SET vposicao_atual = 1;
SET vtexto_apoio = '';
WHILE vposicao_atual <= LENGTH(vtexto) DO
SET vcaracter_atual = SUBSTRING(vtexto, vposicao_atual, 1);
IF vposicao_atual < LENGTH(vtexto) THEN
SET vcaracter_seguinte = SUBSTRING(vtexto, vposicao_atual + 1, 1);
ELSE -- Último caracter não tem motivo para ser verificado
SET vcaracter_seguinte = '';
END IF;
IF vcaracter_atual <> vcaracter_seguinte OR vcaracter_atual <> 'S' THEN
SET vtexto_apoio = CONCAT(vtexto_apoio, vcaracter_atual);
END IF;
SET vposicao_atual = vposicao_atual + 1;
END WHILE;
SET vtexto = vtexto_apoio;
-- Troca caracteres pelo som
SET vposicao_atual = 1;
SET vtexto_apoio = '';
WHILE vposicao_atual <= LENGTH(vtexto) DO
SET vcaracter_atual = SUBSTRING(vtexto, vposicao_atual, 1);
IF vposicao_atual < LENGTH(vtexto) THEN
SET vcaracter_seguinte = SUBSTRING(vtexto, vposicao_atual + 1, 1);
ELSE
SET vcaracter_seguinte = '';
END IF;
-- "B" seguindo de qualquer caracter que não seja "A", "E", "I", "O", "U", "R" ou "Y"
IF vcaracter_atual = 'B' AND INSTR('AEIOURY', vcaracter_seguinte) = 0 THEN
SET vsom = 'BI';
-- "C" seguindo de "E", "I" ou "Y"
ELSEIF vcaracter_atual = 'C' AND INSTR('EIY', vcaracter_seguinte) <> 0 THEN
SET vsom = 'S';
ELSEIF vcaracter_atual = 'C' THEN
SET vsom = 'K';
ELSEIF vcaracter_atual = 'D' AND INSTR('AEIOURY', vcaracter_seguinte) = 0 THEN
SET vsom = 'DI';
ELSEIF vcaracter_atual = 'G' AND INSTR('EIY', vcaracter_seguinte) <> 0 THEN -- GE, GI OU GY
SET vsom = 'J';
ELSEIF vcaracter_atual = 'G' AND vcaracter_seguinte = 'T' THEN -- GT
SET vsom = '';
ELSEIF vcaracter_atual = 'H' THEN -- O H é a única letra do nosso alfabeto sem valor fonético, ou seja, sem som.
SET vsom = '';
ELSEIF vcaracter_atual = 'N' AND INSTR('AEIOUY', vcaracter_seguinte) = 0 THEN -- Quando for seguida de uma consoante, recebe o som fechado "M"
SET vsom = 'M';
ELSEIF vcaracter_atual = 'P' AND INSTR('AEIOURY', vcaracter_seguinte) = 0 THEN
SET vsom = 'PI';
ELSEIF vcaracter_atual = 'Q' THEN
SET vsom = 'K';
-- QUA, QUE, QUI, QUO ou QUY
ELSEIF IFNULL(vcaracter_anterior, '') = 'Q' AND vcaracter_atual = 'U' AND INSTR('AEIOY', vcaracter_seguinte) <> 0 THEN
SET vsom = '';
-- Quando se localiza entre duas vogais, tem sempre o valor da sonora "Z". Exemplo: Coisa, faisão, mausoléu, lousa, Neusa, Brasil, Sousa, cheiroso, manhoso, gasoso, etc.
ELSEIF (IFNULL(vcaracter_anterior, '') <> '' AND INSTR('AEIOUY', IFNULL(vcaracter_anterior, '')) <> 0) AND vcaracter_atual = 'S' AND INSTR('AEIOUY', vcaracter_seguinte) <> 0 THEN
SET vsom = 'Z';
ELSEIF vcaracter_atual = 'S' AND vcaracter_seguinte = 'C' THEN -- "S" seguido de "C" não tem som
SET vsom = '';
ELSEIF vcaracter_atual = 'W' THEN -- O "W" não tem uma regra definida, podendo ser "V" ou "U" dependendo da palavra
SET vsom = 'V';
ELSEIF vcaracter_atual = 'X' AND INSTR('AEIOUY', vcaracter_seguinte) <> 0 THEN -- "X "seguido de vogal Exemplo: Exemplo
SET vsom = 'Z';
ELSEIF vcaracter_atual = 'X' AND INSTR('AEIOUY', vcaracter_seguinte) = 0 THEN -- "X" seguido de consoante. Exemplo: Exceção
SET vsom = 'S';
ELSEIF vcaracter_atual = 'Y' THEN
SET vsom = 'I';
ELSEIF vcaracter_atual = 'Z' AND INSTR('AEIOUY', vcaracter_seguinte) = 0 THEN
SET vsom = 'S';
ELSE
SET vsom = vcaracter_atual;
END IF;
SET vcaracter_anterior = vcaracter_atual;
SET vposicao_atual = vposicao_atual + 1;
SET vtexto_apoio = CONCAT(vtexto_apoio, vsom);
END WHILE;
SET vtexto_apoio = REPLACE(vtexto_apoio, 'SS', 'S'); -- Remove o "SS" que foi utilizado para decidir se continuava como "S" ou virava "Z"
SET vtexto = vtexto_apoio;
RETURN vtexto;
END
$
Some sample outputs:
NEUSA | NEUZA
HERESIA | EREZIA
PALHA | PALIA
QUERO | KERO
EXIGIR | EZIJIR
HESITAR | EZITAR
Some with misspellings:
ÇAPO | SAPO
EREZIA | EREZIA
ESITAR | EZITAR
LAGOZTA | LAGOSTA
HORIENTAR | ORIEMTAR
EDIT
Revising the rules I decided to run a similar algorithm but in Java. I noticed that the "X" also does not have a defined rule, so it is only possible to treat some cases. I also added the treatment for the ~. The result was as follows:
import java.text.Normalizer;
import java.util.LinkedHashSet;
public class Fonetica {
public String converterFrase(String frase) {
LinkedHashSet<String> palavras;
palavras = this.converter(frase.split(" "));
return String.join(" ", palavras);
}
public LinkedHashSet<String> converter(String... palavras) {
LinkedHashSet<String> resultado = new LinkedHashSet<>();
for (String palavra : palavras) {
resultado.add(this.converter(palavra));
}
return resultado;
}
public String converter(String palavra) {
palavra = palavra.toUpperCase();
palavra = palavra.replace("Ç", "SS");
palavra = palavra.replace("Y", "I");
palavra = palavra.replace("W", "V"); // "W" não tem uma regra definida, as vezes é "V", as vezes é "U"
palavra = palavra.replace("GT", "");
palavra = palavra.replace("Q", "K");
palavra = palavra.replace("SH", "X");
palavra = palavra.replace("CH", "X");
palavra = palavra.replace("PH", "F");
palavra = palavra.replace("LH", "LI");
palavra = palavra.replace("NH", "NI");
palavra = palavra.replace("H", ""); // O "H" é a única letra do nosso alfabeto sem valor fonético.
palavra = this.removerDuplicadas(palavra);
// Acentuações
palavra = palavra.replaceAll("([ÃÕ])([EO])", "$1-$2"); // Separa as sílabas
palavra = palavra.replaceAll("([ÃÕ])", "$1M");
palavra = this.removerAcentos(palavra);
palavra = palavra.replaceAll("([BDP])([^AEIOU]|$)", "$1I$2"); // "B", "D" e "P" mudos
palavra = palavra.replaceAll("C([AOUR])", "K$1"); // "CA", "CO" e "CU" viram "KA", "KO" e "KU" respectivamente
palavra = palavra.replaceAll("C([EI])", "SS$1"); // "CE" e "CI" viram "SSE" e "SSI" respecivamente
palavra = palavra.replaceAll("C([^AEIOU]|$)", "KI$1"); // "C" mudo tem som de "KI"
palavra = palavra.replaceAll("G([EI])", "J$1"); // "GE" e "GI" tem som de "JE" e "JI" respectivamente
palavra = palavra.replaceAll("L([^AEIOU]|$)", "U$1"); // Quando o "L" vem antes de consoante
palavra = palavra.replaceAll("N([^AEIOU]|$)", "M$1"); // Quando "N" for seguida de uma consoante, recebe o som fechado "M"
palavra = palavra.replaceAll("X([^AEIOU]|$)", "SS$1"); // Quando o "X" é seguido por uma vogal, tem som de "SS"
palavra = palavra.replaceAll("([AEIOU])S([AEIOU])", "$1Z$2");
palavra = palavra.replaceAll("Z([^AEIOU]|$)", "SS$1"); // "Z" seguido de vogal tem som de "SS"
palavra = palavra.replaceAll("S+", "S"); // Mais de 1 "S" vira apenas 1
palavra = palavra.replace("OU", "O"); // Quando o "U" segue o "O" não tem som
return palavra;
}
private String removerDuplicadas(String texto) {
String[] letras = "ABCDEFGHIJKLMNOPQRTUVWYXZ".split("");
for (String letra : letras) {
texto = texto.replaceAll(letra + "+", letra);
}
return texto;
}
private String removerAcentos(String texto) {
texto = Normalizer.normalize(texto, Normalizer.Form.NFD);
texto = texto.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
return texto;
}
}
Functioning in the IDEONE
The piracicabano algorithm must obligatorily turn on the speaker and release a: "Pamonhas! Pamonhas! Pamonhas!" :) (derived audio without the best part and to Desmitification)
– Maniero
I found an interesting study of text conversion system in the respective phonetic transcription. It can be useful to think of a different approach from the traditional ones (soundex/metaphone). It is a doctoral thesis with UFSC, which is an important center in the studies of computational linguistics in Brazil. https://repositorio.ufsc.br/bitstream/handle/123456789/91849/254656.pdf?sequence=1
– bfavaretto
@bfavaretto too I think. I was saving it for later, but also didn’t want to miss the opportunity. I like UFSC. To be a very interesting thesis.
– Maniero
I don’t know much about it (and I don’t dare to answer), but apparently there are some studies/projects related to Metaphone: http://link.springer.com/chapter/10.1007%2F978-3-642-28601-8_25 e http://sourceforge.net/projects/metaphoneptbr/
– Luiz Vieira
@Luizvieira very interesting. It is helping a lot.
– Maniero
@Bigown: I’m happy to help. :)
– Luiz Vieira
When I worked at Prodesp (I left there in 2006), an algorithm like this was developed to register people in the system of the Court of Justice of the State of São Paulo. Unfortunately I couldn’t find anything related to this openly on the internet, but this article gives a general idea of an algorithm that I consider similar to that developed at Prodesp.
– Jordão
... note that the algorithm I mentioned was actually optimized for the name of people.
– Jordão
The number of distinct accents in Brazil is impressive. Surely the algorithm needs to be differentiated for certain regions. Where I live one speaks practically a dialect apart. Good luck.
– Oralista de Sistemas