Skip to content

Instantly share code, notes, and snippets.

@ruuts
Created October 20, 2014 07:45
Show Gist options
  • Save ruuts/a6bfb0c1c06489db9701 to your computer and use it in GitHub Desktop.
Save ruuts/a6bfb0c1c06489db9701 to your computer and use it in GitHub Desktop.
i18n missing keys to gengo export/import
namespace :translator do
desc "Export missing translations for a specific locale"
task :export_keys => :environment do
from = ENV['FROM']
to = ENV['TO']
if from.present? and to.present?
translator = Translator.new(
from: from.to_sym,
to: to.to_sym
)
missing = translator.export_keys
if missing.length > 0
filename = "translate_#{from}_to_#{to}.txt"
File.open(filename, 'w') do |file|
file.write missing
end
puts "Created export file: #{filename}"
else
puts "There are no missing translations from #{from} to #{to}"
end
else
puts "Please provide locale to translate for example:"
puts "rake translator FROM=en TO=fr"
end
end
desc "Import missing translations for a specific locale"
task :import_keys => :environment do
if ENV['FROM'].present? and ENV['TO'].present? and ENV['FILE'].present?
translator = Translator.new(
from: ENV['FROM'].to_sym,
to: ENV['TO'].to_sym
)
file = File.open(ENV['FILE'])
translator.import_keys(file.read)
translator.write_locale_file
else
puts "Please provide the following arguments:"
puts "rake translator FROM=en TO=fr FILE=en_to_fr.translate"
end
end
end
require 'yaml'
class Translator
def initialize(options = {})
@from = options[:from]
@to = options[:to]
@dir = options[:dir] || "config/locales/multilingual"
end
def prepare_translations_for_missing_keys
I18n.with_locale(@from) do
result = {}
find_missing_keys.each do |key|
result[key] = I18n.t(key.gsub("#{@to}.", '')).to_s
end
result
end
end
def export_keys
prepare_translations_for_missing_keys.map do |key, value|
"[[[#{key}]]] #{value}"
end.join("\n")
end
def import_keys(import)
matches = import.scan %r{
\[\[\[ # key start brackets
([^\]]+) # key
\]\]\] # key end brackets
((.(?!\[\[\[))*) # value until brackets
}xm
@import = {}
matches.each do |match|
@import[match[0]] = match[1].to_s.lstrip
end
@import
end
def write_locale_file
old_yaml = yaml(@to)
new_yaml = deflatten_keys(@import)
merged_yaml = old_yaml ? old_yaml.deep_merge(new_yaml) : new_yaml
File.open(path(@to), 'w') do |file|
file.write merged_yaml.to_yaml
end
end
def find_missing_keys
yaml_1 = yaml(@from)
yaml_2 = yaml(@to)
keys_1 = yaml_1.present? ? flatten_keys(yaml_1[yaml_1.keys.first]) : []
keys_2 = yaml_2.present? ? flatten_keys(yaml_2[yaml_2.keys.first]) : []
(keys_1 - keys_2).map {|k| "#{@to}.#{k}" }
end
private
def yaml(locale)
YAML.load((File.open(path(locale)) rescue ''))
end
def path(locale)
File.expand_path("#{@dir}/#{locale}.yml")
end
def deflatten_keys(hash)
new_hash = {}
hash.each do |k,v|
new_hash.deep_merge!(
k.split('.').reverse.inject(v) {|a,n| { n => a } }
)
end
new_hash
end
def flatten_keys(hash, prefix="")
keys = []
hash.keys.each do |key|
if hash[key].is_a? Hash
current_prefix = prefix + "#{key}."
keys << flatten_keys(hash[key], current_prefix)
else
keys << "#{prefix}#{key}"
end
end
prefix == "" ? keys.flatten : keys
end
end
@thermistor
Copy link

FYI, I made a few changes in my fork. In my 'from' or starting locale I generally break things into multiple files for easier maintenance, so I load all translations differently and then pick that locale instead of just loading from a single file. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment