Ruby on Rails Inflector en español

lunes, junio 22, 2009

El módulo Inflectors de Ruby on Rails transforma palabras de singular a plural, nombres de clases a nombres de tablas, nombres de clases a claves foráneas, entre otros. Sin embargo éste módulo no soporta pluralización en español.

Si quieremos reemplazar la pluralización que trae por defecto Rails (inglés) y crear la nuestra en español solo debemos agregar estas reglas al final del archivo config/environment.rb:

# Limpiamos todas las inflecciones existentes
Inflector.inflections.clear

# Agregamos las reglas de inflección
Inflector.inflections do |inflect|
inflect.plural /([taeiou])([A-Z]|_|\$)/, '\1s\2'
inflect.plural /([rlnd])([A-Z]|_|$)/, '\1es\2'
inflect.singular /([taeiou])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([rlnd])es([A-Z]|_|$)/, '\1\2'
end

La primera regla dice algo como: "Todas las palabras que terminen en t, a, e, i, o, u y que luego tengan una letra mayúscula, un underscore o un fin de línea entonces se pluralizan con S", mientras que la segunda regla dice: "Todas las palabras que terminen en r, l, n, d y que luego tengan una letra mayúscula, un underscore o un fin de línea entonces se pluralizan con ES". Las reglas tres y cuatro se interpretan siguiendo el mismo criterio pero de forma inversa. Así, "casa" se pluraliza como "casas" y "canción" se pluraliza como "canciones".

Dejaré como tarea para el lector estudiar de manera detallada las expresiones regulares usadas en las reglas, pero les dejaré una pista:
Es necesario recordar que en una expresión regular los paréntesis son signos de agrupación y cada expresión entre paréntesis corresponde a un grupo. Se puede hacer referencia a la expresión asociada a un grupo mediante una barra invertida "\" y el número del grupo. Es por eso que en la expresión regular del Inflector se usa el \1 y el \2

Con el código anterior ya tenemos configurada nuestra pluralización en español. Podemos probarla agregando el código a un proyecto existente y abriendo una consola de Rails:
$ cd proyecto_existente_de_rails
$ script/console
Loading development environment.
>>'prueba'.pluralize
=> "pruebas"

Observamos que ahora el string 'prueba' está en plural. Sin embargo existe un bug en la versión 1.2.6 de Rails (quizás esté corregido en las versiones posteriores pero aún no lo he confirmado) y es que si intentamos pluralizar un string que contenga expresiones que cumplan ambos patrones del inflector entonces obtendremos una sola palabra pluralizada, por ejemplo:
>>'prueba_cancion'.pluralize
=> "prueba_canciones"

Se debe extender la clase Inflector y modificar los métodos pluralize y singularize. El código lo podemos insertar en el mismo archivo config/environment.rb y sería algo como:

# Extender la clase Inflector
module Inflector
def pluralize(word)
result = word.to_s.dup

if word.empty? || inflections.uncountables.include?(result.downcase)
result
else
inflections.plurals.each { |(rule, replacement)| result.gsub!(rule, replacement) }
result
end
end

def singularize(word)
result = word.to_s.dup
if inflections.uncountables.include?(result.downcase)
result
else
inflections.singulars.each { |(rule, replacement)| result.gsub!(rule, replacement) }
result
end
end
end

Si revisamos el código fuente de los métodos pluralize y singularize veremos que la diferencia está en la instrucción break if en las líneas 112 y 132 respectivamente:
112:   inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
...
132: inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }

Solo debemos eliminar ese par de instrucciones para que entonces la regla sea aplicable a más de un patrón al mismo tiempo.

Por los momentos es todo. Espero sea de utilidad este pequeño pero interesante tip

5 comentarios:

Alfredo Rico dijo...

Hola.. Uno de mis socios creo este archivo de pluralizació para nombres de tablas con 1 o 2 palabras conformando el nombre. El tuyo sirve para nombres de tabla con una sola palabra. Aquí va por si alguien le funciona. Si alguien lo puede hacer para cualaquier cantidad de palabras bienvenido es..
ActiveSupport::Inflector.inflections do |inflect|
inflect.plural /([aeiou])([A-Z]|_|$)/, '\1s\2'
inflect.plural /([rlnd])([A-Z]|_|$)/, '\1es\2'
inflect.plural /([aeiou])([A-Z]|_|$)([a-z]+)([rlnd])($)/, '\1s\2\3\4es\5'
inflect.plural /([rlnd])([A-Z]|_|$)([a-z]+)([aeiou])($)/, '\1es\2\3\4s\5'
inflect.singular /([aeiou])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([rlnd])es([A-Z]|_|$)/, '\1\2'
inflect.singular /([aeiou])s([A-Z]|_)([a-z]+)([rlnd])es($)/, '\1\2\3\4\5'
inflect.singular /([rlnd])es([A-Z]|_)([a-z]+)([aeiou])s($)/, '\1\2\3\4\5'
inflect.irregular 'user', 'users'
inflect.irregular 'account', 'accounts'
inflect.irregular 'password', 'passwords'
inflect.irregular 'session', 'sessions'
end

Unknown dijo...

Hola, aqui yo utilice estas reglas y agregue unas dos para pluralizar y singularizar las palabras terminadas en "is" como "pais" y en "iz" como "raiz" => "raices"


ActiveSupport::Inflector.inflections do |inflect|
#inflect.plural /^(ox)$/i, '\1en'
#inflect.singular /^(ox)en/i, '\1'
#inflect.irregular 'person', 'people'
#inflect.uncountable %w( fish sheep )
inflect.plural /([taeiou])([A-Z]|_|\$)/, '\1s\2'
inflect.plural /([rlnd])([A-Z]|_|$)/, '\1es\2'
inflect.plural /(is)([A-Z]|_|$)/, '\1es'
inflect.plural /(i)(z)([A-Z]|_|$)/, '\1ces'
inflect.singular /([taeiou])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([rlnd])es([A-Z]|_|$)/, '\1\2'
inflect.singular /ises([A-Z]|_|$)/, '\1is'
inflect.singular /ices([A-Z]|_|$)/, '\1iz'
end

Unknown dijo...

Hola,

Gracias por este aporte!. Me fue de gran ayuda hace algún tiempo en un proyecto que desarrollé en ROR.

Estuve viendo este poyecto en Github
(https://github.com/bermi/Python-Inflector). Se trata de un inflector para inglés y español hecho en Python. Las reglas para el español son bastante completas y tal vez no sea muy difícil portarlas a Ruby.

Lástima que no tengo el tiempo suficiente para colaborar con esto, pero al menos pongo la información por si le interesa a alguien más.

Hasta pronto,

Héctor

SamuelVega dijo...

Hola, yo utilizo esto y me funciona bien tanto con una o mas palabras, underscore y CamelCase. Espero que os sirva de ayuda :)


ActiveSupport::Inflector.inflections do |inflect|
inflect.plural /([^djlnrs])([A-Z]|_|$)/, '\1s\2'
inflect.plural /([djlnrs])([A-Z]|_|$)/, '\1es\2'
inflect.plural /(.*)z([A-Z]|_|$)$/i, '\1ces\2'

inflect.singular /([^djlnrs])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([djlnrs])es([A-Z]|_|$)/, '\1\2'
inflect.singular /(.*)ces([A-Z]|_|$)$/i, '\1z\2'

inflect.irregular 'user', 'users'
inflect.irregular 'account', 'accounts'
inflect.irregular 'password', 'passwords'
inflect.irregular 'session', 'sessions'
inflect.irregular 'ud', 'uds'
end

Offshore Software Development Company dijo...

I was seeking for rails development company and set down up on your mail and i should state thanks for distributing such helpful information.