Ответ 1
http://github.com/grosser/sort_alphabetical
Этот камень должен помочь. Он добавляет методы sort_alphabetical
и sort_alphabetical_by
в Enumberable.
Я пытаюсь выяснить "правильный" способ сортировки строк UTF-8 в Ruby on Rails.
В моем приложении у меня есть поле выбора, которое заполняется странами. Поскольку мое приложение локализовано, каждый существующий язык имеет файл country.yml, который связывает идентификатор страны с локализованным именем для этой страны. Я не могу сортировать строки вручную в файле yml, потому что мне нужно, чтобы ID был согласован во всех локалях.
Что я сделал, создайте метод ascii_name
, который использует unidecode
для преобразования акцентированных и нелатинских символов в свои ascii эквивалент (например, "Afeganistão" станет "Afeganistao" ), а затем сортировать по этому:
require 'unidecode'
class Country
def ascii_name
Unidecoder.decode(name).gsub("[?]", "").gsub(/`/, "'").strip
end
end
Country.all.sort_by(:&ascii_name)
Однако есть очевидные проблемы с этим:
Кто-нибудь знает, как лучше я могу сортировать строки?
http://github.com/grosser/sort_alphabetical
Этот камень должен помочь. Он добавляет методы sort_alphabetical
и sort_alphabetical_by
в Enumberable.
Сравнение строк в Ruby, основанных на байтовых значениях символов:
%w[à a e].sort
# => ["a", "e", "à"]
Чтобы правильно сортировать строки в соответствии с локалью, можно использовать ffi-icu драгоценный камень:
require "ffi-icu"
ICU::Collation.collate("it_IT", %w[à a e])
# => ["a", "à", "e"]
ICU::Collation.collate("de", %w[a s x ß])
# => ["a", "s", "ß", "x"]
В качестве альтернативы:
collator = ICU::Collation::Collator.new("it_IT")
%w[à a e].sort { |a, b| collator.compare(a, b) }
# => %w[a à e]
Обновить. Чтобы проверить, как строки должны сортироваться в соответствии с языковыми правилами, проект ICU предоставляет этот хороший инструмент.
Единственным решением, которое я нашел до сих пор, является использование ActiveSupport::Inflector.transliterate(string)
для замены символов Unicode на ASCII и сортировки:
Country.all.sort_by do |country|
ActiveSupport::Inflector.transliterate country.name
end
Теперь единственная проблема заключается в том, что это выравнивает "ä" с "a" (DIN 5007-1), и я заканчиваю "Ägypten" перед "Albanien", в то время как я ожидаю, что это будет наоборот. К счастью, транслитерация настраивается как заменить символы.
См. документацию: http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate
Есть несколько способов пойти. Вы можете конвертировать строки UTF в шестнадцатеричные строки, а затем сортировать их:
s.split(//).collect { |x| x.unpack('U').to_s }.join
или вы можете использовать библиотеку iconv. Прочитайте его и используйте по мере необходимости (от dzone):
#add this to environment.rb
#call to_iso on any UTF8 string to get a ISO string back
#example : "Cédez le passage aux français".to_iso
class String
require 'iconv' #this line is not needed in rails !
def to_iso
Iconv.conv('ISO-8859-1', 'utf-8', self)
end
end
Единственное работающее решение, которое я нашел до сих пор (по крайней мере, для Ruby 1.8, потому что Ruby 1.9 лучше обрабатывать Unicode) Unicode от Yoshida Masato, Здесь вы можете найти метод Unicode.strcmp.
РЕДАКТИРОВАТЬ: Извините, это решение также использует разложение NFD со всеми его ограничениями.
То, что вы пытаетесь сделать, - очень грязное предложение. Нет никакого способа сделать прозрачную транслитерацию для всех символов Юникода, потому что значение орграфов изменяется от локали к языку, а строки могут расти ОГРОМНЫМ (если вы хотите заменить 10 китайских символов их фонетическими эквивалентами). Не ходите туда.
Почему вы хотите транслитерировать имена в первую очередь? Для URL-адресов? Браузеры обрабатывают URL-адреса Unicode прилично сейчас, поэтому вы изобретаете огромную проблему из воздуха. Если вам нужны идентификаторы, предварительно подготовьте свои списки, чтобы включить стабильный числовой идентификатор для каждой страны и использовать его в качестве идентификатора. Или сохраните английское имя страны в качестве идентификатора (вы можете бесплатно загрузить списки стран, в которых существует справочная система ISO).
Если вам действительно нужна хорошая транслитерация для Unicode (и в этом случае это не то, что вам нужно), см. библиотеки IBM ICU, для них есть дремлющий камень.
Вы пытались получить доступ к методу mb_chars
для каждой из строк вашей страны? mb_chars
- это прокси-сервер, который добавляет ActiveSupport, который определяет безопасные версии Unicode всех методов String
. Если компаратор поддерживает Unicode, сортировка должна работать правильно.