Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sort YAML files?

I've been trying to sort an i18n translations YAML file with Ruby so I can manage new translations in a better and organized way, but I've been wondering if there is something to ease the task.

I found a YAML file writer so I can write a hash into a file, but my problem is to sort the hash correctly. If I got hash h, h.sort returns an array and I still haven't figured a simple way to do that.

I have YAML files like this:

pt-br:    
  global:
    misc:
      total: "Total"
      all: "Todos"
      close: "Fechar"
      cancel: "Cancelar"

    crud:
      access: "Acessar"
      back: "Voltar"
      edit: "Editar"
      confirm: "Confirmar"
      send: "Enviar"

...

(The files are way larger than this)

But I want to sort them this way:

pt-br:    
  global:
    crud:
      access: "Acessar"
      back: "Voltar"
      confirm: "Confirmar"
      edit: "Editar"
      send: "Enviar"

    misc:
      all: "Todos"
      cancel: "Cancelar"
      close: "Fechar"          
      total: "Total"

I thought that some simple recursive method could help me like this:

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)
like image 612
AndreDurao Avatar asked Sep 01 '11 20:09

AndreDurao


3 Answers

You shouldn't use the YAML library like suggested in the other answers. It will screw up the formatting of long string values, remove your comments and spit unreadable char escapes when you use accents and special characters (which you will, since you are doing i18n). Use this gem I created:

https://github.com/redealumni/i18n_yaml_sorter

It will only sort the lines on the file, so everything will remain the same way it was on the original yaml (your accents, the YAML construct you used to enter the strings, indentation, etc). It will work with deeply nested yamls and results are pretty solid. The gem includes tests and it's good for ruby 1.8 or 1.9.

It comes with a TextMate Bundle (Shift + Command + S) and a Rails rake task so you can sort the files easily and instantly in your editor. It's really fast.

To illustrate the difference:

Original:

  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!

Results by 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."

Results by 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.
like image 182
Bernardo Pádua Avatar answered Oct 13 '22 00:10

Bernardo Pádua


In my use cases where deep sorting a hash is needed, the hash is always a tree where keys are labels and values are (sub)trees (if hashes) or leaves (otherwise). I need to deep-sort only the labels of trees (not the values).

I got this

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}}

with this

    def deeply_sort_hash(object)
      return object unless object.is_a?(Hash)
      hash = Hash.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
like image 26
aercolino Avatar answered Oct 12 '22 23:10

aercolino


UPDATE April 2014:

Using Rails 3.2.13, Ruby 1.9.3p489:

I just used the i18n_yaml_sorter gem ( https://github.com/redealumni/i18n_yaml_sorter ).

Simply add to your Gemfile:

gem 'i18n_yaml_sorter', group: :development

Then run the rake task to sort your locales' files:

rake i18n:sort

Worked perfectly, even though the gem has been last authored 2 years ago. It took 5 minutes max.

like image 22
MrYoshiji Avatar answered Oct 13 '22 00:10

MrYoshiji