🇺🇦Допомогти Україні
Сортування тексту здається простою задачею — поки не натрапляєш на український алфавіт у Ruby.
["ґ", "г", "є", "е", "і", "и", "ї", "й"].sort

=> ["г", "е", "и", "й", "є", "і", "ї", "ґ"]
Як бачимо, "ґ" потрапила в самий кінець. Але в українській абетці має бути навпаки: "ґ" стоїть після "г".

Unicode ≠ Алфавіт

Ruby сортує рядки за замовчуванням за Unicode codepoints, тобто за технічним порядком символів у таблиці Unicode, а не за граматичним порядком літер в українській мові.
uk_alphabet = "АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ"
puts "Великі літери:"
uk_alphabet.each_char do |char|
  dec = char.ord
  hex = dec.to_s(16).upcase.rjust(4, '0')
  puts "#{char} -> dec: #{dec}, hex: U+#{hex}"
end

puts "\nМалі літери:"
uk_alphabet.downcase.each_char do |char|
  dec = char.ord
  hex = dec.to_s(16).upcase.rjust(4, '0')
  puts "#{char} -> dec: #{dec}, hex: U+#{hex}"
end
Великі літери:
А -> dec: 1040, hex: U+0410
Б -> dec: 1041, hex: U+0411
В -> dec: 1042, hex: U+0412
Г -> dec: 1043, hex: U+0413
Ґ -> dec: 1168, hex: U+0490
Д -> dec: 1044, hex: U+0414
Е -> dec: 1045, hex: U+0415
Є -> dec: 1028, hex: U+0404
Ж -> dec: 1046, hex: U+0416
З -> dec: 1047, hex: U+0417
И -> dec: 1048, hex: U+0418
І -> dec: 1030, hex: U+0406
Ї -> dec: 1031, hex: U+0407
Й -> dec: 1049, hex: U+0419
К -> dec: 1050, hex: U+041A
Л -> dec: 1051, hex: U+041B
М -> dec: 1052, hex: U+041C
Н -> dec: 1053, hex: U+041D
О -> dec: 1054, hex: U+041E
П -> dec: 1055, hex: U+041F
Р -> dec: 1056, hex: U+0420
С -> dec: 1057, hex: U+0421
Т -> dec: 1058, hex: U+0422
У -> dec: 1059, hex: U+0423
Ф -> dec: 1060, hex: U+0424
Х -> dec: 1061, hex: U+0425
Ц -> dec: 1062, hex: U+0426
Ч -> dec: 1063, hex: U+0427
Ш -> dec: 1064, hex: U+0428
Щ -> dec: 1065, hex: U+0429
Ь -> dec: 1068, hex: U+042C
Ю -> dec: 1070, hex: U+042E
Я -> dec: 1071, hex: U+042F

Малі літери:
а -> dec: 1072, hex: U+0430
б -> dec: 1073, hex: U+0431
в -> dec: 1074, hex: U+0432
г -> dec: 1075, hex: U+0433
ґ -> dec: 1169, hex: U+0491
д -> dec: 1076, hex: U+0434
е -> dec: 1077, hex: U+0435
є -> dec: 1108, hex: U+0454
ж -> dec: 1078, hex: U+0436
з -> dec: 1079, hex: U+0437
и -> dec: 1080, hex: U+0438
і -> dec: 1110, hex: U+0456
ї -> dec: 1111, hex: U+0457
й -> dec: 1081, hex: U+0439
к -> dec: 1082, hex: U+043A
л -> dec: 1083, hex: U+043B
м -> dec: 1084, hex: U+043C
н -> dec: 1085, hex: U+043D
о -> dec: 1086, hex: U+043E
п -> dec: 1087, hex: U+043F
р -> dec: 1088, hex: U+0440
с -> dec: 1089, hex: U+0441
т -> dec: 1090, hex: U+0442
у -> dec: 1091, hex: U+0443
ф -> dec: 1092, hex: U+0444
х -> dec: 1093, hex: U+0445
ц -> dec: 1094, hex: U+0446
ч -> dec: 1095, hex: U+0447
ш -> dec: 1096, hex: U+0448
щ -> dec: 1097, hex: U+0449
ь -> dec: 1100, hex: U+044C
ю -> dec: 1102, hex: U+044E
я -> dec: 1103, hex: U+044F

Наслідки Unicode порядку

Через те, що Unicode-коди розташовані не в алфавітному порядку, стандартне сортування Ruby видає неправильний порядок для українських слів, особливо для літер "ґ", "є", "і", "ї".

Моє рішення: ukrainian_sort - Ruby-gem для правильного сортування українських слів

Щоб уникнути цієї проблеми в своїх особистих проєктах, я створив бібліотеку ukrainian_sort, яка реалізує сортування за офіційним українським алфавітом. Робив для використання в своїх сайд-проєктах. 
Вона порівнює слова літеру за літерою, використовуючи власний порядок:
["г", "ґ", "е", "є", "и", "і", "ї", "й"]
require 'ukrainian_sort'

words = ["ґава", "груша", "єнот", "яблуко"]
sorted_words = UkrainianSort.sort(words)
puts sorted_words
# => ["груша", "ґава", "єнот", "яблуко"]
А так сортує Ruby "з коробки":
words.sort
=> ["груша", "яблуко", "єнот", "ґава"]

Чому в англійській все працює "нормально"?

Англійський алфавіт — це простий набір латинських літер від A до Z у безперервному діапазоні Unicode (U+0041–U+005A для великих і U+0061–U+007A для малих літер). Це означає, що порядок символів у Unicode збігається з алфавітним порядком англійської мови.
Тому стандартне сортування Ruby (Array#sort або String#<=>), яке порівнює символи за їх Unicode кодами, працює коректно для англійських слів.

В яких ще мовах існує проблема сортування?

Відповідь проста - у багатьох. Багато інших мов мають складніші алфавіти, де порядок літер у Unicode не відповідає лінгвістичному порядку, наприклад:
  • Німецька — де ä, ö, ü, ß мають особливий порядок;
  • Шведська — додає літери å, ä, ö після z;
  • Чеська, словацька, польська — мають діакритичні літери, які за Unicode йдуть не послідовно;
  • Французька, іспанська, португальська — різні правила сортування з апострофами, тильдами, акцентами.

Складність питання

Unicode задає універсальний номер символу, але не визначає лінгвістичний порядок сортування. Для коректного сортування мов часто потрібні спеціальні правила — collation rules, які враховують:
  • Порядок літер,
  • Особливі символи,
  • Акценти
  • Чи вважати великі і малі літери однаковими,
  • Лігатури та інші мовні особливості.

Як це зазвичай вирішують?

  • Через локалізовані сортувальники (наприклад, ICU — International Components for Unicode). Але якогось рішення для себе я швидко знайти не зміг.
  • Спеціальні бібліотеки для сортування (gem’и, пакети).
  • Ручне визначення порядку для конкретної мови.
Для англійської мови Unicode-послідовність символів збігається з алфавітним порядком, тож sort працює без проблем. Але для більшості інших мов треба додатково враховувати лінгвістичні правила сортування, бо Unicode — це лише кодова таблиця, а не алгоритм сортування.
ukrainian_sort (GitHub / RubyGems) - один з перших gem'ів які я викатив публічно. Зазвичай роблю приватні репозиторії з рішеннями під себе. Тож можете сміливо робити Issues та Pull-реквести. Скоріш за все там є ще над чим поправцювати.

Цей допис поки що не має жодних доповнень від автора/ки.