Как я могу отсортировать файлы YAML? - PullRequest
18 голосов
/ 02 сентября 2011

Я пытался отсортировать YAML-файл переводов i18n с помощью Ruby, чтобы я мог лучше и организованно управлять новыми переводами, но мне было интересно, есть ли что-то, что облегчит задачу.

Я нашел средство записи в файл YAML, чтобы я мог записать хеш в файл, но моя проблема в том, чтобы правильно отсортировать хеш. Если я получил хэш 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)

Ответы [ 9 ]

10 голосов
/ 02 сентября 2011

На самом деле это хороший вопрос.Вы хотите глубоко сортировать хэши.Поэтому я не люблю заново изобретать колесо, а потом я искал хорошую реализацию и нашел ту, которая мне нравится.Взгляните на это https://gist.github.com/1083930. Работает нормально.

9 голосов
/ 29 ноября 2011

Вы не должны использовать библиотеку YAML, как предложено в других ответах.Он испортит форматирование длинных строковых значений, удалит ваши комментарии и выплюнет нечитаемые экранированные символы, когда вы используете акценты и специальные символы (что вы и сделаете, поскольку вы делаете i18n).Используйте этот драгоценный камень, который я создал:

https://github.com/redealumni/i18n_yaml_sorter

Он будет сортировать только строки в файле, поэтому все останется таким же, как и на исходном yaml (ваши акценты, YAMLконструкция, которую вы использовали для ввода строк, отступов и т. д.).Это будет работать с глубоко вложенными ямлами, и результаты довольно солидны.Драгоценный камень включает в себя тесты и подходит для ruby ​​1.8 или 1.9.

Он поставляется с пакетом TextMate (Shift + Command + S) и рейк-рейком, так что вы можете легко и быстро сортировать файлы в редакторе.Это действительно быстро.

Чтобы проиллюстрировать разницу:

Оригинал:

  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.
7 голосов
/ 14 ноября 2012

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
6 голосов
/ 08 апреля 2014

ОБНОВЛЕНИЕ Апрель 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 для сортировки ваших локалей 'files:

rake i18n:sort

Отлично сработало , несмотря на то, что драгоценный камень последний раз создавался 2 года назад.Это заняло максимум 5 минут.

1 голос
/ 23 мая 2016

Существует также пакет атомов, который будет делать это также https://github.com/akfernun/yaml-sortkeys

1 голос
/ 06 октября 2011

Вот еще один вариант для тех, кто сталкивается с этим ..

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
}
1 голос
/ 02 сентября 2011

В 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.

0 голосов
/ 14 мая 2019

К сожалению YAML::quick_emit устарела и больше не доступна в новых сборках самоцвета Psych.Если вы хотите, чтобы ваши ключи хеша сортировались при сериализации в yaml, вам придется использовать следующий патч обезьяны:

class Hash
    def to_yaml opts={}
        return Psych.dump(self.clone.sort.to_h)
    end
end
0 голосов
/ 09 января 2014

Это может быть еще один привлекательный вариант: https://github.com/redealumni/i18n_yaml_sorter

...