Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Gets default values from I18n.t() methods, replaces them, and outputs them in yml. In order to actually REMOVE :defaults from the files, uncomment the replace_content_of_file_with_i18n_queries line.
#!/usr/bin/env ruby
#NOTE: Must use Ruby 1.9+ and not 1.8.7.
# USAGE:
# Run and redirect output to a file for best results.
# example: ruby pull-defaults.rb > my-results.yml
# For now, you'll have to add the root en: element and increase
# the indent level for it to be a valid locale YML.
# Uncomment lines 160 and 161 (the ones that call
# replace_content_of_file_with_i18n_queries) to have this
# script remove :default strings from i18n tags it finds.
require "yaml"
require "optparse"
require "ostruct"
require "forwardable"
require 'active_support/all'
require 'nokogiri'
class Hash
def has_nested_key?(key)
h = self
key.to_s.split('^').each do |segment|
return false unless h.key?(segment)
h = h[segment]
end
true
end
# idea snatched from deep_merge in Rails source code
def deep_safe_merge(other_hash)
#puts "MERGEEE: "+other_hash.inspect #.first.inspect
self.merge(other_hash) do |key, oldval, newval|
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
newval = newval.to_hash if newval.respond_to?(:to_hash)
if oldval.class.to_s == 'Hash'
if newval.class.to_s == 'Hash'
oldval.deep_safe_merge(newval)
else
oldval
end
else
newval
end
end
end
def deep_safe_merge!(other_hash)
replace(deep_safe_merge(other_hash))
end
end
module Helpers
# snatched from rspec source
def colour(text, colour_code)
"#{colour_code}#{text}\e[0m"
end
def green(text); colour(text, "\e[32m"); end
def red(text); colour(text, "\e[31m"); end
def magenta(text); colour(text, "\e[35m"); end
def yellow(text); colour(text, "\e[33m"); end
def blue(text); colour(text, "\e[34m"); end
end
class MissingT
VERSION = "0.3.1"
include Helpers
extend Forwardable
def_delegators :@translations, :[]
# attr_reader :translations
def initialize
@translations = Hash.new
end
def parse_options(args)
@options = OpenStruct.new
@options.prefix = nil
opts = OptionParser.new do |opts|
opts.on("-f", "--file FILE_OR_DIR",
"look for missing translations in files under FILE_OR_DIR",
"(if a file is given, only look in that file)") do |path|
@options.path = path
end
end
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
opts.on_tail("--version", "Show version") do
puts VERSION
exit
end
opts.parse!(args)
end
# NOTE: this method is needed
# because attr_reader :translations
# does not seem to be stubbable
def translations
@translations
end
def add_translations(trs)
#puts "TRS: "+translations.inspect
#puts "TRS: "+trs.inspect
translations.deep_safe_merge!(trs)
end
def collect_translations
locales_pathes = ["config/locales/**/*.yml", "vendor/plugins/**/config/locales/**/*yml", "vendor/plugins/**/locale/**/*yml"]
locales_pathes.each do |path|
Dir.glob(path) do |file|
add_translations(translations_in_file(file))
end
end
end
def translations_in_file(yaml_file)
open(yaml_file) { |f| YAML.load(f.read) }
end
def files_with_i18n_queries
if path = @options.path
path = path[0...-1] if path[-1..-1] == '/'
[ Dir.glob("#{path}/**/*.erb"), Dir.glob("#{path}/**/*.rb") ]
else
[ Dir.glob("app/**/*.erb"),
Dir.glob("app/**/controllers/**/*.rb"),
Dir.glob("app/**/helpers/**/*.rb")]
end.flatten
end
def get_content_of_file_with_i18n_queries(file)
f = open(File.expand_path(file), "r")
content = f.read()
f.close()
content
end
def replace_content_of_file_with_i18n_queries(file, output)
File.open(file, "w") { |file|
file.puts output
}
end
def extract_i18n_queries(file)
i18n_query_pattern_paren = /(?:I18n\.t)\(["]([\w\.]*)["](?:[^"]*default[^"]*"([^"]*)")\)/#/(?:I18n\.t)\(["]([\w\.]*)["](?:.*default.*["](.*)["])?[\)|,]/#/(?:I18n\.t)\(["]([\w\.]*)["](?:.*default.*["](.*)["])?\)/ #/(?:I18n\.t)\(["]([\w\.]*)["](?:.*default.*["](.*)["])?.*\)/
i18n_query_pattern_comma = /(?:I18n\.t)\(["]([\w\.]*)["](?:[^"]*default[^"]*"([^"]*)"),/#/(?:I18n\.t)\(["]([\w\.]*)["](?:.*default.*["](.*)["])?[\)|,]/#/(?:I18n\.t)\(["]([\w\.]*)["](?:.*default.*["](.*)["])?\)/ #/(?:I18n\.t)\(["]([\w\.]*)["](?:.*default.*["](.*)["])?.*\)/
i18n_query_no_parens_pattern = /[^\w]+(?:I18n\.translate|I18n\.t|translate|t)\s+(['"])(.*?)\1/
file_content = get_content_of_file_with_i18n_queries(file)
results = file_content.scan(i18n_query_pattern_paren)
results += file_content.scan(i18n_query_pattern_comma)
#puts "RESULT: "+results.inspect
#puts "ORIG_REP: "+file_content
# replace_content_of_file_with_i18n_queries(file,file_content.gsub(i18n_query_pattern_comma, 'I18n.t("\1",'))
# replace_content_of_file_with_i18n_queries(file,file_content.gsub(i18n_query_pattern_paren, 'I18n.t("\1")'))
#puts "NEW_REP: "+file_content.gsub(i18n_query_pattern, 'I18n.t("\1")')
return results
end
def collect_translation_queries
files_with_i18n_queries.each_with_object({}) do |file, queries|
queries_in_file = extract_i18n_queries(file)
if queries_in_file.any?
#puts "DEBUG: "+queries_in_file[0][0].to_s+": "+queries_in_file[0][1].to_s
queries[file] = queries_in_file
end
end
#TODO: remove duplicate queries across files
end
def has_translation?(lang, query)
t = translations
i18n_label(lang, query).split('^').each do |segment|
return false unless segment =~ /#\{.*\}/ or (t.respond_to?(:key?) and t.key?(segment))
t = t[segment]
end
true
end
def get_missing_translations(queries, languages)
languages.each_with_object({}) do |lang, missing|
get_missing_translations_for_lang(queries, lang).each do |file, queries|
missing[file] ||= []
missing[file].concat(queries).uniq!
end
end
end
def find_missing_translations(lang=nil)
collect_translations
#puts "TRANS: "+translations.inspect
get_missing_translations(collect_translation_queries, lang ? [lang] : translations.keys)
end
private
def get_missing_translations_for_lang(queries, lang)
queries.map do |file, queries_in_file|
queries_with_no_translation = queries_in_file.select { |q|
if(!has_translation?(lang, q)) then
trans = "n"
else
trans = "y"
end
#puts "QUERY: "+q.inspect+" --- "+trans
!has_translation?(lang, q)
}
if queries_with_no_translation.empty?
nil
else
[file, queries_with_no_translation.map { |q| [i18n_label(lang, q[0]), q[1]] }]
end
end.compact
#puts "RESULT: "+queries.inspect
queries
end
def i18n_label(lang, query)
"#{lang}.#{query}"
end
end
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
#@enyml = YAML.load(File.read("config/locales/en.yml")).to_xml
def hashify(strings,value)
#puts "HASH: "+strings.inspect# .to_yaml.inspect
#puts "HASHval: "+value.inspect# .to_yaml.inspect
strings.map { |s| s.split('^') }.each_with_object({}) do |segmented_string, h|
segmented_string.each do |segment|
#puts "HASHlast: "+segment+" h: "+h.inspect
if(segment === segmented_string.last) then
h = input.split('^').inject {|a, n| {n => a}}
else
h[segment] ||= {}
h = h[segment]
end
end
end
end
def print_hash(h, level)
#puts "HHH: "+h.inspect
h.each_pair do |k,v|
#puts "HHHrrr: "+k.inspect
puts %(#{" " * (level*2)}#{k}:)
print_hash(v, level+1)
end
end
missing_t = MissingT.new
missing_t.parse_options(ARGV)
missing_values = missing_t.find_missing_translations(ARGV.first).values
#puts "VALUES: "+missing_values.inspect
missing_message_strings = missing_values.map { |ms|
ms.map { |mv|
#puts "STRINGS: "+mv.first.inspect+" - "+mv[1]
mr = mv.first.gsub(/\./, '^')
#mr.gsub(/./, '^')
unless(mv[1].nil?)
mr += "^"+mv[1]
end
mr = mr.split('^').reverse.inject {|a, n| {n => a}}
#mr = hashify([mv.first],mv[1])
#puts "STRINGShash: "+mr.inspect
mr
}
}
missing = missing_message_strings.each_with_object({}) do |h, all_message_strings|
#puts "HHH: "+h.inspect
h.each do |r|
all_message_strings.deep_safe_merge!(r)
end
end
puts missing.to_yaml
#missing.each do |language, missing_for_language|
# puts "MMM: "+language.inspect
# puts "MMMrrr: "+missing_for_language.inspect
# puts
# puts "#{language}:"
# puts missing_for_language.to_yaml
# #print_hash(missing_for_language, 1)
#end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment