Today, I am open sourcing my Ruby LocaleTranslator; the translator uses google’s translator API to translate a primary seed locale into various other languages. This eases the creation of multi-lingual sites. Not only can the LocaleTranslator translate your main seed locale into different languages but it can also recursively merge in differences, this comes in handy if you have hand-optimized your translated locales.

Viva Localization!

My projects that use the LocaleTranslator; UploadBooth, PasteBooth and ShrinkBooth.

LocaleTranslator Examples

en.yml


site:
  hello_world: Hello World!
  home: Home
  statement: Localization should be simple!

Batch Conversion of your English locale.

[code lang=”ruby”]
#!/opt/local/bin/ruby

require ‘monkey-patches.rb’
require ‘locale_translator.rb’

en_yml = YAML::load(File.open(‘en.yml’))

[:de,:ru].each do |lang|
lang_yml = LocaleTranslator.translate(en_yml,
:to=>lang,
:html=>true,
:key=>’GOOGLE API KEY’)
f = File.new(“#{lang.to_s.downcase}.yml”,”w”)
f.puts(lang_yml.ya2yaml(:syck_compatible => true))
f.close
p “Translated to #{lang.to_s}”
end
[/code]

Merge in new locale keys from your English Locale into your already translated Russian locale.

[code lang=”ruby”]
#!/opt/local/bin/ruby

require ‘monkey-patches.rb’
require ‘locale_translator.rb’

en_yml = YAML::load(File.open(‘en.yml’))
ru_yml = YAML::load(File.open(‘ru.yml’))

ru_new_yml = LocaleTranslator.translate(en_yml,
:to=>:ru,
:html=>true,
:merge=>ru_yml,
:key=>’GOOGLE API KEY’)
puts ru_new_yml.ya2yaml(:syck_compatible => true)
[/code]

The Implementation Code

Support Monkey Patches

monkey-patches.rb
[code lang=”ruby”]
class Hash
def to_list
h2l(self)
end

def diff(hash)
hsh = {}
this = self
hash.each do |k,v|
if v.kind_of?Hash and this.key?k
tmp = this[k].diff(v)
hsh[k] = tmp if tmp.size > 0
else
hsh[k] = v unless this.key?k
end
end
hsh
end

def merge_r(hash)
hsh = {}
this = self
hash.each do |k,v|
if v.kind_of?Hash
hsh[k] = this[k].merge_r(v)
else
hsh[k] = v
end
end
self.merge(hsh)
end

private
def h2l(hash)
list = []
hash.each {|k,v| list = (v.kind_of?Hash) ? list.merge_with_dups(h2l(v)) : list << v } list end end class Array def chunk(p=2) return [] if p.zero? p_size = (length.to_f / p).ceil [first(p_size), *last(length - p_size).chunk(p - 1)] end def to_hash(hash) l2h(hash,self) end def merge(arr) self | arr end def merge_with_dups(arr) temp = [] self.each {|a| temp << a } arr.each {|a| temp << a } temp end def merge!(arr) temp = self.clone self.clear temp.each {|a| self << a } arr.each {|a| self << a unless temp.include?a } true end def merge_with_dups!(arr) temp = self.clone self.clear temp.each {|a| self << a } arr.each {|a| self << a } true end private def l2h(hash,lst) hsh = {} hash.each {|k,v| hsh[k] = (v.kind_of?Hash) ? l2h(v,lst) : lst.shift } hsh end end [/code] The LocaleTranslator Implementation

You need the ya2yaml and easy_translate gems. Ya2YAML can export locales in UTF-8 unlike the standard yaml implementation that can only export in binary for non-standard ascii.

locale-translator.rb
[code lang=”ruby”]
$KCODE = ‘UTF8’ if RUBY_VERSION < '1.9.0' require 'rubygems' require 'ya2yaml' require 'yaml' require 'easy_translate' class LocaleTranslator def self.translate(text,opts) opts[:to] = [opts[:to]] if opts[:to] and !opts[:to].kind_of?Array if opts[:merge].kind_of?Hash and text.kind_of?Hash diff = opts[:merge].diff(text) diff_hsh = LocaleTranslator.translate(diff,:to=>opts[:to],:html=>true)
return opts[:merge].merge_r(diff_hsh)
end

if text.kind_of?Hash
t_arr = text.to_list
t_arr = t_arr.first if t_arr.size == 1
tout_arr = LocaleTranslator.translate(t_arr,:to=>opts[:to],:html=>true)
tout_arr = [tout_arr] if tout_arr.kind_of?String
tout_arr.to_hash(text)
elsif text.kind_of?Array
if text.size > 50
out = []
text.chunk.each {|l| out.merge_with_dups!(EasyTranslate.translate(l,opts).first) }
out
else
text = text.first if text.size == 1
EasyTranslate.translate(text,opts).first
end
else
EasyTranslate.translate(text,opts).first
end
end
end
[/code]