9

For example if I have YAML file with

en:
  questions:
    new: 'New Question'
    other:
      recent: 'Recent'
      old: 'Old'

This would end up as a json object like

{
  'questions.new': 'New Question',
  'questions.other.recent': 'Recent',
  'questions.other.old': 'Old'
}
koonse
  • 1,468
  • 1
  • 14
  • 14
  • 2
    The answer would be 'not easily'. Why do you want this hash? – Ryan Bigg Oct 22 '13 at 00:51
  • I'm using YAML files for i18n on a Rails app. But I am also using Polyglot to do i18n in JavaScript, which needs JSON. https://github.com/airbnb/polyglot.js – koonse Oct 22 '13 at 06:40

3 Answers3

9
require 'yaml'

yml = %Q{
en:
  questions:
    new: 'New Question'
    other:
      recent: 'Recent'
      old: 'Old'
}

yml = YAML.load(yml)
translations = {}

def process_hash(translations, current_key, hash)
  hash.each do |new_key, value|
    combined_key = [current_key, new_key].delete_if { |k| k.blank? }.join('.')
    if value.is_a?(Hash)
      process_hash(translations, combined_key, value)
    else
      translations[combined_key] = value
    end
  end
end

process_hash(translations, '', yml['en'])
p translations
Walerian Sobczak
  • 869
  • 2
  • 13
  • 21
Ryan Bigg
  • 102,687
  • 22
  • 224
  • 252
8

Since the question is about using YAML files for i18n on a Rails app, it's worth noting that the i18n gem provides a helper module I18n::Backend::Flatten that flattens translations exactly like this:

test.rb:

require 'yaml'
require 'json'
require 'i18n'

yaml = YAML.load <<YML
en:
  questions:
    new: 'New Question'
    other:
      recent: 'Recent'
      old: 'Old'
YML
include I18n::Backend::Flatten
puts JSON.pretty_generate flatten_translations(nil, yaml, nil, false)

Output:

$ ruby test.rb
{
  "en.questions.new": "New Question",
  "en.questions.other.recent": "Recent",
  "en.questions.other.old": "Old"
}
wjordan
  • 17,115
  • 2
  • 72
  • 91
5

@Ryan's recursive answer is the way to go, I just made it a little more Rubyish:

yml = YAML.load(yml)['en']

def flatten_hash(my_hash, parent=[])
  my_hash.flat_map do |key, value|
    case value
      when Hash then flatten_hash( value, parent+[key] )
      else [(parent+[key]).join('.'), value]
    end
  end
end

p flatten_hash(yml) #=> ["questions.new", "New Question", "questions.other.recent", "Recent", "questions.other.old", "Old"]
p Hash[*flatten_hash(yml)] #=> {"questions.new"=>"New Question", "questions.other.recent"=>"Recent", "questions.other.old"=>"Old"}

Then to get it into json format you just need to require 'json' and call the to_json method on the hash.

hirolau
  • 11,838
  • 7
  • 29
  • 43