Как я могу сортировать файлы YAML?
Я пытаюсь сортировать файл YAML для i18n с Ruby, чтобы лучше управлять и упорядочивать новые переводы, но мне было интересно, есть ли что-то, чтобы облегчить задачу.
Я нашел запись файла YAML, поэтому я могу написать хэш в файл, но моя проблема заключается в правильном сортировке хэша. Если я получил hash h
, h.sort
возвращает массив, и я до сих пор не понял простой способ сделать это.
У меня есть файлы YAML, подобные этому:
pt-br:
global:
misc:
total: "Total"
all: "Todos"
close: "Fechar"
cancel: "Cancelar"
crud:
access: "Acessar"
back: "Voltar"
edit: "Editar"
confirm: "Confirmar"
send: "Enviar"
...
(Файлы больше, чем это)
Но я хочу отсортировать их следующим образом:
pt-br:
global:
crud:
access: "Acessar"
back: "Voltar"
confirm: "Confirmar"
edit: "Editar"
send: "Enviar"
misc:
all: "Todos"
cancel: "Cancelar"
close: "Fechar"
total: "Total"
Я думал, что какой-то простой рекурсивный метод может мне помочь:
def translation_sort(h)
if h.class == Hash
h = h.sort
h.each{|item| translation_sort(item)}
end
h
end
require "yaml"
h=YAML.load_file(File.open("~/pt-br.sample.yml"))
translation_sort(h)
Ответы
Ответ 1
На самом деле это хороший вопрос. Вы хотите глубоко сортировать хэши. Поэтому мне не нравится изобретать колесо, а затем я искал хорошую реализацию, и я нашел тот, который мне нравится. Посмотрите на него https://gist.github.com/1083930. Он работает нормально.
Ответ 2
Вы не должны использовать библиотеку YAML, как предложено в других ответах. Это испортит форматирование длинных строковых значений, удалит ваши комментарии и выплюнет нечитаемые char экраны при использовании акцентов и специальных символов (что вы будете делать, так как вы делаете i18n).
Используйте этот камень, который я создал:
https://github.com/redealumni/i18n_yaml_sorter
Он будет сортировать строки только в файле, поэтому все останется таким же, как на исходном yaml (ваши акценты, конструкция YAML, которую вы использовали для ввода строк, отступов и т.д.). Он будет работать с глубоко вложенными ямлами, и результаты довольно прочные. Драгоценный камень включает тесты, и он хорош для рубина 1,8 или 1,9.
Он поставляется с пакетом TextMate Bundle (Shift + Command + S) и Rails, поэтому вы можете сортировать файлы легко и мгновенно в своем редакторе. Это очень быстро.
Чтобы проиллюстрировать разницу:
Оригинал:
pt-BR:
# Note how this is a nice way of inputing
# paragraphs of text in YAML.
apples: >
Maçãs são boas,
só não coma
seus iPods!
grapes: Não comemos elas.
bananas: |
Bananas são "legais":
- Elas são <b> doces </b>.
isto: não é chave
Por isto todos gostam de bananas!
Результаты по YAML:: dump:
pt-BR:
apples: "Ma\xC3\xA7\xC3\xA3s s\xC3\xA3o boas, s\xC3\xB3 n\xC3\xA3o coma seus iPods!\n"
bananas: "Bananas s\xC3\xA3o \"legais\":\n - Elas s\xC3\xA3o <b> doces </b>.\n isto: n\xC3\xA3o \xC3\xA9 chave\n\n\ Por isto todos gostam de bananas!\n"
grapes: "N\xC3\xA3o comemos elas."
Результаты по i18n_yaml_sorter:
pt-BR:
# Note how this is a nice way of inputing
# paragraphs of text in YAML.
apples: >
Maçãs são boas,
só não coma
seus iPods!
bananas: |
Bananas são "legais":
- Elas são <b> doces </b>.
isto: não é chave
Por isto todos gostam de bananas!
grapes: Não comemos elas.
Ответ 3
https://gist.github.com/1083930 работает не так, как я ожидал. Он глубоко сортирует НЕ ТОЛЬКО хеш-ключи, НО ТАКЖЕ хэш-значения. В моих случаях использования, когда необходима глубокая сортировка хэша, хеш всегда является деревом, где ключи являются метками, а значения - (под) деревьями (если хэши) или листьями (в противном случае). Мне нужно глубоко сортировать только метки деревьев.
Я получил это
before: {"a":[2,10,{"5":null,"1":null,"3":null}],"x":{"5":null,"1":null,"3":null},"a2":{"5":[2,10,5],"1":null,"3":null}}
after: {"a":[2,10,{"5":null,"1":null,"3":null}],"a2":{"1":null,"3":null,"5":[2,10,5]},"x":{"1":null,"3":null,"5":null}}
с этим
require 'active_support'
def deeply_sort_hash(object)
return object unless object.is_a?(Hash)
hash = RUBY_VERSION >= '1.9' ? Hash.new : ActiveSupport::OrderedHash.new
object.each { |k, v| hash[k] = deeply_sort_hash(v) }
sorted = hash.sort { |a, b| a[0].to_s <=> b[0].to_s }
hash.class[sorted]
end
Ответ 4
ОБНОВЛЕНИЕ Апрель 2014:
Использование Rails 3.2.13, Ruby 1.9.3p489:
Я только использовал жемчуг i18n_yaml_sorter (https://github.com/redealumni/i18n_yaml_sorter).
Просто добавьте в свой Gemfile:
gem 'i18n_yaml_sorter', group: :development
Затем запустите задачу rake для сортировки файлов локалей:
rake i18n:sort
Работал отлично, хотя этот камень был последним автором 2 года назад. Это заняло 5 минут максимум.
Ответ 5
В Ruby 1.8 хеши не имеют определенного порядка, поэтому вы не можете просто сортировать их.
Вы можете обезвредить патч/перезаписать метод to_yaml
Hash
следующим образом:
#!/usr/local/bin/ruby -w
require 'yaml'
class Hash
def to_yaml(opts = {})
YAML::quick_emit(self, opts) do |out|
out.map(taguri, to_yaml_style) do |map|
keys.sort.each do |k|
v = self[k]
map.add(k, v)
end
end
end
end
end
dict = YAML.load($<.read)
puts dict.to_yaml
Конечно, детали могут зависеть от вашей версии YAML/Ruby. Пример выше для Ruby 1.8.6.
Ответ 6
Вот еще одна альтернатива для всех, кто сталкивается с этим.
require 'yaml'
yaml = YAML.load(IO.read(File.join(File.dirname(__FILE__), 'example.yml')))
@yml_string = "---\n"
def recursive_hash_to_yml_string(hash, depth=0)
spacer = ""
depth.times { spacer += " "}
hash.keys.sort.each do |sorted_key|
@yml_string += spacer + sorted_key + ": "
if hash[sorted_key].is_a?(Hash)
@yml_string += "\n"
recursive_hash_to_yml_string(hash[sorted_key], depth+1)
else
@yml_string += "#{hash[sorted_key].to_s}\n"
end
end
end
recursive_hash_to_yml_string(yaml)
open(File.join(File.dirname(__FILE__), 'example.yml'), 'w') { |f|
f.write @yml_string
}
Ответ 7
Существует также пакет атомов, который также сделает это https://github.com/akfernun/yaml-sortkeys
Ответ 8
Это может быть еще один привлекательный вариант: https://github.com/redealumni/i18n_yaml_sorter
Ответ 9
К сожалению, YAML::quick_emit
устарел и больше не доступен в новых сборках самоцвета Psych. Если вы хотите, чтобы ваши ключи хеша сортировались при сериализации в yaml, вам придется использовать следующий патч обезьяны:
class Hash
def to_yaml opts={}
return Psych.dump(self.clone.sort.to_h)
end
end