Merging Rails’ i18n files
Thursday, January 8th, 2009Lately I’ve been in a hassle to translate our application Quassum into English. My collegues continously modify the german i18n file for Rails, then the old structure of the other language files are left unmodified. Doing this by hand is close to impossible.
So what I came up with was a rake task that merges a give structure (YAML source file) into an existing target structure while overwriting every key that changed and adding new keys. Old keys and values are left as they are.
I didn’t find anything on the internet doing this task, so I hope this task will save you the time I spent last saturday. Good luck!
$KCODE = 'UTF8'
require 'ya2yaml'
namespace :quassum do
desc "Copy all missing lang identifiers from [src].yml to [dest].yml"
task :fill_lang do
if ENV['src'].blank? or ENV['dest'].blank?
puts "Give all parameters: src and dest. " +
"E.g.: rake quassum:fill_lang src='de' dest='en'"
exit
end
src_path = File.join(RAILS_ROOT,'config','locales', "#{ENV['src']}.yml")
dest_path = File.join(RAILS_ROOT,'config','locales', "#{ENV['dest']}.yml")
unless File.readable?(src_path)
puts "File #{src_path} not readable!"
exit
end
unless File.exist?(dest_path)
puts "File #{dest_path} does not exist. Creating it..."
File.new(dest_path, "w")
end
# We assume that the src file is correct...
yaml_src = YAML::load_file(src_path)
struct_src = yaml_src[ENV['src']]
# ...but the src not necessarily. So, in case, we create a new lang file:
@yaml_dest = YAML::load_file(dest_path)
@yaml_dest ||= Hash.new
@struct_dest = @yaml_dest[ENV['dest']]
@struct_dest ||= Hash.new
# merge all unknown changes to the dest struct:
merge_recursively struct_src
@yaml_dest[ENV['dest']] = @struct_dest
File.open(dest_path, "w") do |file|
file.puts @yaml_dest.ya2yaml
end
puts File.new(dest_path).read
puts "Everything done."
end
#
# SOME HELPER FUNCTIONS
#
def merge_recursively(pairs, parents = [])
pairs.each_pair do |k,v|
# copy the parents path and add the current element key:
current_path = Array.new(parents) << k
if v.is_a?(Hash)
merge_recursively(v, current_path)
else
ensure_yaml_contains(@struct_dest, current_path, v.to_s)
end
end
end
def ensure_yaml_contains(element, path, val)
#puts " ensure_yaml_contains(#{element}, #{path}, #{val})"
if path.length == 1
if element[path.first].is_a?(Hash) and not val.blank?
puts "Hash found instead of key. In favor of '#{val}' '#{element.to_s}' will be deleted!"
element[path.first] = val
end
if element[path.first].nil?
puts "Missing key '#{path.first}'! Pre-filled for editing with: '#{val}'. element was #{element.class}"
element[path.first] = val + " [[TODO]] "
end
# if none of these replaced anything - we're fine, the key exists!
#
elsif path.length > 1
# walk down if possible:
if element[path.first].is_a?(Hash) and element.has_key?(path.first)
ensure_yaml_contains(element[path.first], path[1..-1], val)
else
# We don't have a hash in the dest file, so we create the hash
# and fill in all subsequent children:
element[path.first] = Hash.new
ensure_yaml_contains(element[path.first], path[1..-1], val)
end
else
puts "REPLACING FAILED"
# should stop here
end
end
end