A vueltas con la normalizacion de cadenas

Ha habido un thread largo con asunto “Seo urls: permalink_fu o
friendly_id” que ha derivado en como normalizar cadenas para obtener
una representacion ASCII de ellas. A veces he enviado una tabla de
transliteracion a la lista y creo que voy a cambiar de aproximacion.

Para normalizar, ademas de un mapping como ese hay otras dos tecnicas
usadas ampliamente:

  1. Usar Iconv.conv(“UTF-8//TRANSLIT//IGNORE”, …).

Tiene como gotcha que no todas las maquinas tienen la misma tabla de
transliteracion. Esto he tratado de averiguar por que es pero no lo he
conseguido en el tiempo empleado.

Podrian ser distintas versiones, podrian ser distintas instalaciones
(por ejemplo en los Linux que he probado iconv no conoce MacRoman).
Parece fragil en ese sentido y por eso lo he descartado. Si alguien
tuviera esto controlado y supiera como garantizar portabilidad de algo
que usa iconv lo agradeceria.

  1. Basicamente limpiar el resultado de str.chars.normalize(:kd).

Aqui mi objecion ha sido siempre que algunos caracteres que quisiera
mapear como ‘ß’ -> ‘ss’ se filtran.

Pero con un mapping explicito todo lo que no esta en el mapping se
filtra tambien. Podria ser que haya algo que no esta en la tabla de lo
que la normalizacion Unicode te puede aun sacar algo.

De manera que creo que voy a cambiar a delegar en esa normalizacion y
manualmente tratar las excepciones que quiero tener en cuenta (y que
posiblemente evolucionen con el tiempo). Ahora mismo son un poco
arbitrarias, se basan en algunos caracteres conocidos y en una pateada
que hice un dia en la tabla de caracteres especiales del Mac.

El codigo al que he llegado como primera version es el de abajo, y
salvo que haya algo que no haya anticipado me parece mejor
aproximacion. Todo lo que mapeaba la tabla explicita anterior lo mapea
igual esto (y con todas las normalizaciones posibles del input, aqui
le quiero agradecer a Adrián que apuntara ese otro gotcha).

Solo por compartirlo en la lista ya que ha salido otras veces.

– fxn

KD normalization leaves these characters untouched and would be

filtered out as non-ASCII otherwise. We substitute them by hand.

CUSTOM_NORMALIZATION = {
‘æ’ => ‘ae’,
‘ß’ => ‘ss’,
‘Ä‘’ => ‘d’,
‘Æ’’ => ‘f’,
‘ħ’ => ‘h’,
‘ı’ => ‘i’,
‘ĸ’ => ‘k’,
‘Å‚’ => ‘l’,
‘Å‹’ => ‘n’,
‘ø’ => ‘o’,
‘Å“’ => ‘oe’,
‘ŧ’ => ‘t’,
}
def self.normalize_by_kd_normalization(str)
n = str.chars.normalize(:kd).downcase.strip.to_s

This would be better based on tr!, but it is not Unicode-aware.

CUSTOM_NORMALIZATION.each {|c, s| n.gsub!(c, s)}
n.gsub!(/\s+/, ’ ‘)
n.delete!(’^ a-z0-9_/\-.’)
n
end