Skip to content

Instantly share code, notes, and snippets.

@balinterdi
Created March 2, 2009 22:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save balinterdi/73024 to your computer and use it in GitHub Desktop.
Save balinterdi/73024 to your computer and use it in GitHub Desktop.
require "yaml"
require "forwardable"
require "pp"
require "ruby-debug"
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)
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
class MissingT
extend Forwardable
def_delegators :@translations, :[]
# attr_reader :translations
def initialize
@translations = Hash.new
end
# NOTE: this method is needed
# because attr_reader :translations
# does not seem to be stubbable
def translations
@translations
end
def add_translations(trs)
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 hashify(strings)
h = Hash.new
strings.map { |s| s.split('.') }.
each do |segmented_string|
root = h
segmented_string.each do |segment|
root[segment] ||= {}
root = root[segment]
end
end
h
end
def translations_in_file(yaml_file)
open(yaml_file) { |f| YAML.load(f.read) }
end
def files_with_i18n_queries
[ Dir.glob("app/**/*.erb"),
Dir.glob("app/**/controllers/**/*.rb"),
Dir.glob("app/**/helpers/**/*.rb")].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 extract_i18n_queries(file)
i18n_query_pattern = /I18n\.(?:translate|t)\s*\((.*?)[,\)]/
get_content_of_file_with_i18n_queries(file).
scan(i18n_query_pattern).map { |match| match.first.gsub(/[^\w\.]/, '') }
end
def collect_translation_queries
queries = {}
files_with_i18n_queries.each do |file|
queries_in_file = extract_i18n_queries(file)
unless queries_in_file.empty?
queries[file] = queries_in_file
end
end
queries
#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 (t.respond_to?(:key?) and t.key?(segment))
t = t[segment]
end
true
end
def get_missing_translations(queries, lang=nil)
missing = {}
languages = lang.nil? ? translations.keys : [lang]
languages.each do |l|
get_missing_translations_for_lang(queries, l).each do |file, qs|
missing[file] ||= []
missing[file].concat(qs).uniq!
end
end
missing
end
def find_missing_translations(lang=nil)
collect_translations
get_missing_translations(collect_translation_queries, lang)
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| !has_translation?(lang, q) }
if queries_with_no_translation.empty?
nil
else
[file, queries_with_no_translation.map { |q| i18n_label(lang, q) }]
end
end.compact
end
def i18n_label(lang, query)
"#{lang}.#{query}"
end
end
if __FILE__ == $0
MissingT.new.find_missing_translations(ARGV[0]).each do |file, queries|
puts
puts "#{file}:"
puts
queries.each { |q| puts " #{q}" }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment