translate-rails3 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README +62 -0
- data/Rakefile +33 -0
- data/VERSION +1 -0
- data/app/controllers/translate_controller.rb +165 -0
- data/app/helpers/translate_helper.rb +45 -0
- data/app/views/layouts/translate.html.erb +359 -0
- data/app/views/translate/_pagination.html.erb +24 -0
- data/app/views/translate/index.html.erb +114 -0
- data/config/routes.rb +5 -0
- data/init.rb +1 -0
- data/lib/tasks/translate.rake +178 -0
- data/lib/translate.rb +8 -0
- data/lib/translate/file.rb +35 -0
- data/lib/translate/keys.rb +152 -0
- data/lib/translate/log.rb +35 -0
- data/lib/translate/routes.rb +11 -0
- data/lib/translate/storage.rb +28 -0
- data/spec/controllers/translate_controller_spec.rb +130 -0
- data/spec/file_spec.rb +54 -0
- data/spec/files/translate/app/models/article.rb +12 -0
- data/spec/files/translate/app/views/category.erb +1 -0
- data/spec/files/translate/app/views/category.html +1 -0
- data/spec/files/translate/app/views/category.html.erb +1 -0
- data/spec/files/translate/app/views/category.rhtml +5 -0
- data/spec/files/translate/public/javascripts/application.js +1 -0
- data/spec/keys_spec.rb +179 -0
- data/spec/log_spec.rb +47 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/storage_spec.rb +33 -0
- data/translate-rails3.gemspec +77 -0
- metadata +118 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
<%
|
2
|
+
# Expects locals:
|
3
|
+
#
|
4
|
+
# total_entries
|
5
|
+
# per_page
|
6
|
+
|
7
|
+
n_pages = total_entries/per_page + (total_entries % per_page > 0 ? 1 : 0)
|
8
|
+
current_page = (params[:page] || 1).to_i
|
9
|
+
%>
|
10
|
+
|
11
|
+
<% if n_pages > 1 %>
|
12
|
+
<h2>Pages:</h2>
|
13
|
+
<div class="clearfix">
|
14
|
+
<ul class="paging">
|
15
|
+
<% (1..n_pages).each do |page_number| %>
|
16
|
+
<% if current_page == page_number %>
|
17
|
+
<li class="selected"><%= link_to(page_number, params.merge(:page => page_number), :title => "Page #{page_number}" ) %></li>
|
18
|
+
<% else %>
|
19
|
+
<li><%= link_to(page_number, params.merge(:page => page_number), :title => "Page #{page_number}") %></li>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
</ul>
|
23
|
+
</div>
|
24
|
+
<% end %>
|
@@ -0,0 +1,114 @@
|
|
1
|
+
<%
|
2
|
+
@page_title = "Translate"
|
3
|
+
show_filters = ["all", "untranslated", "translated"]
|
4
|
+
show_filters << "changed" if @from_locale != @to_locale
|
5
|
+
%>
|
6
|
+
|
7
|
+
<fieldset>
|
8
|
+
<legend>Search filter</legend>
|
9
|
+
<div id="show-sort">
|
10
|
+
<p>
|
11
|
+
<label>Show:</label> <%= raw simple_filter(show_filters) %>
|
12
|
+
</p>
|
13
|
+
<p>
|
14
|
+
<label>Sort by:</label> <%= raw simple_filter(["key", "text"], 'sort_by') %>
|
15
|
+
</p>
|
16
|
+
</div>
|
17
|
+
<%= form_tag(params, :method => :get) do %>
|
18
|
+
<div id="languages">
|
19
|
+
<p>
|
20
|
+
<%= hidden_field_tag(:filter, params[:filter]) %>
|
21
|
+
<%= hidden_field_tag(:sort_by, params[:sort_by]) %>
|
22
|
+
<label>Translate from</label>
|
23
|
+
<%= select_tag(:from_locale, options_for_select(I18n.available_locales, @from_locale.to_sym)) %> <span>to</span>
|
24
|
+
<%= select_tag(:to_locale, options_for_select(I18n.available_locales, @to_locale.to_sym)) %>
|
25
|
+
<%= submit_tag "Display" %>
|
26
|
+
</p>
|
27
|
+
</div>
|
28
|
+
<div id="filter-pattern">
|
29
|
+
<p>
|
30
|
+
<label for="key_pattern_value">Key</label>
|
31
|
+
<%= select_tag(:key_type, options_for_select([["contains", 'contains'], ["starts with", 'starts_with']], params[:key_type])) %>
|
32
|
+
<%= text_field_tag(:key_pattern, params[:key_pattern], :size => 50, :id => "key_pattern_value", :class => "text-default") %>
|
33
|
+
</p>
|
34
|
+
<p>
|
35
|
+
<label for="text_pattern_value">Text</label>
|
36
|
+
<%= select_tag(:text_type, options_for_select(['contains', 'equals'], params[:text_type])) %>
|
37
|
+
<%= text_field_tag(:text_pattern, params[:text_pattern], :size => 50, :id => "text_pattern_value", :class => "text-default") %>
|
38
|
+
</p>
|
39
|
+
<p>
|
40
|
+
<%= submit_tag "Search" %>
|
41
|
+
<%= link_to "clear", params.merge({:text_pattern => nil, :key_pattern => nil}) %>
|
42
|
+
</p>
|
43
|
+
</div>
|
44
|
+
<% end %>
|
45
|
+
<p class="hits">
|
46
|
+
Found <strong><%= @total_entries %></strong> messages
|
47
|
+
</p>
|
48
|
+
<p>
|
49
|
+
<%= link_to "Reload messages", translate_reload_path %>
|
50
|
+
</p>
|
51
|
+
</fieldset>
|
52
|
+
|
53
|
+
|
54
|
+
<div class="paging">
|
55
|
+
<%= render :partial => 'pagination', :locals => {:total_entries => @total_entries, :per_page => per_page} %>
|
56
|
+
</div>
|
57
|
+
|
58
|
+
<% if @total_entries > 0 %>
|
59
|
+
<%= form_tag(translate_path) do %>
|
60
|
+
<div>
|
61
|
+
<%= hidden_field_tag(:filter, params[:filter], :id => "hid_filter") %>
|
62
|
+
<%= hidden_field_tag(:sort_by, params[:sort_by], :id => "hid_sort_by") %>
|
63
|
+
<%= hidden_field_tag(:key_type, params[:key_type], :id => "hid_key_type") %>
|
64
|
+
<%= hidden_field_tag(:key_pattern, params[:key_pattern], :id => "hid_key_pattern") %>
|
65
|
+
<%= hidden_field_tag(:text_type, params[:text_type], :id => "hid_text_type") %>
|
66
|
+
<%= hidden_field_tag(:text_pattern, params[:text_pattern], :id => "hid_text_pattern") %>
|
67
|
+
</div>
|
68
|
+
<div class="translations">
|
69
|
+
<h2>Translations from <%= @from_locale %> to <%= @to_locale %></h2>
|
70
|
+
<p class="translate">
|
71
|
+
<%= submit_tag "Save Translations" %>
|
72
|
+
</p>
|
73
|
+
<% @paginated_keys.each do |key|
|
74
|
+
from_text = lookup(@from_locale, key)
|
75
|
+
to_text = lookup(@to_locale, key)
|
76
|
+
line_size = 100
|
77
|
+
n_lines = n_lines(from_text, line_size)
|
78
|
+
field_name = "key[#{key}]"
|
79
|
+
%>
|
80
|
+
<div class="translation">
|
81
|
+
<% if from_text.present? %>
|
82
|
+
<p class="translation-text">
|
83
|
+
<%= simple_format(h(from_text)) %>
|
84
|
+
</p>
|
85
|
+
<% end %>
|
86
|
+
<p class="edit-form">
|
87
|
+
<% if n_lines > 1 %>
|
88
|
+
<%= text_area_tag(field_name, to_text, :size => "#{line_size}x#{n_lines}", :id => key) %>
|
89
|
+
<% else %>
|
90
|
+
<%= text_field_tag(field_name, to_text, :size => line_size, :id => key) %>
|
91
|
+
<% end %>
|
92
|
+
</p>
|
93
|
+
<p>
|
94
|
+
<em>
|
95
|
+
<%= link_to_function 'Auto Translate', "getGoogleTranslation('#{key}', \"#{escape_javascript(from_text)}\", '#{@from_locale}', '#{@to_locale}')", :style => 'padding: 0; margin: 0;' %>
|
96
|
+
<br/>
|
97
|
+
<strong>Key:</strong><%=h key %><br/>
|
98
|
+
<% if @files[key] %>
|
99
|
+
<strong>File:</strong><%= @files[key].join("<br/>") %>
|
100
|
+
<% end %>
|
101
|
+
</em>
|
102
|
+
</p>
|
103
|
+
</div>
|
104
|
+
<% end %>
|
105
|
+
<p class="translate">
|
106
|
+
<%= submit_tag "Save Translations" %>
|
107
|
+
</p>
|
108
|
+
</div>
|
109
|
+
<% end %>
|
110
|
+
<% end %>
|
111
|
+
|
112
|
+
<div class="paging">
|
113
|
+
<%= render :partial => 'pagination', :locals => {:total_entries => @total_entries, :per_page => per_page} %>
|
114
|
+
</div>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
match 'translate' => 'translate#index', :as => :translate_list
|
3
|
+
match 'translate/translate' => 'translate#translate', :as => :translate
|
4
|
+
match 'translate/reload' => 'translate#reload', :as => :translate_reload
|
5
|
+
end if Rails.env.development?
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'translate'
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
def deep_merge(other)
|
5
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
6
|
+
merger = proc { |key, v1, v2| (Hash === v1 && Hash === v2) ? v1.merge(v2, &merger) : v2 }
|
7
|
+
merge(other, &merger)
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(keys, value)
|
11
|
+
key = keys.shift
|
12
|
+
if keys.empty?
|
13
|
+
self[key] = value
|
14
|
+
else
|
15
|
+
self[key] ||= {}
|
16
|
+
self[key].set keys, value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if ENV['SORT']
|
21
|
+
# copy of ruby's to_yaml method, prepending sort.
|
22
|
+
# before each so we get an ordered yaml file
|
23
|
+
def to_yaml( opts = {} )
|
24
|
+
YAML::quick_emit( self, opts ) do |out|
|
25
|
+
out.map( taguri, to_yaml_style ) do |map|
|
26
|
+
sort.each do |k, v| #<- Adding sort.
|
27
|
+
map.add( k, v )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
namespace :translate do
|
36
|
+
desc "Show untranslated keys for locale LOCALE"
|
37
|
+
task :untranslated => :environment do
|
38
|
+
from_locale = I18n.default_locale
|
39
|
+
untranslated = Translate::Keys.new.untranslated_keys
|
40
|
+
|
41
|
+
messages = []
|
42
|
+
untranslated.each do |locale, keys|
|
43
|
+
keys.each do |key|
|
44
|
+
from_text = I18n.backend.send(:lookup, from_locale, key)
|
45
|
+
messages << "#{locale}.#{key} (#{from_locale}.#{key}='#{from_text}')"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if messages.present?
|
50
|
+
messages.each { |m| puts m }
|
51
|
+
else
|
52
|
+
puts "No untranslated keys"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "Show I18n keys that are missing in the config/locales/default_locale.yml YAML file"
|
57
|
+
task :missing => :environment do
|
58
|
+
missing = Translate::Keys.new.missing_keys.inject([]) do |keys, (key, filename)|
|
59
|
+
keys << "#{key} in \t #{filename} is missing"
|
60
|
+
end
|
61
|
+
puts missing.present? ? missing.join("\n") : "No missing translations in the default locale file"
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "Remove all translation texts that are no longer present in the locale they were translated from"
|
65
|
+
task :remove_obsolete_keys => :environment do
|
66
|
+
I18n.backend.send(:init_translations)
|
67
|
+
master_locale = ENV['LOCALE'] || I18n.default_locale
|
68
|
+
Translate::Keys.translated_locales.each do |locale|
|
69
|
+
texts = {}
|
70
|
+
Translate::Keys.new.i18n_keys(locale).each do |key|
|
71
|
+
if I18n.backend.send(:lookup, master_locale, key).to_s.present?
|
72
|
+
texts[key] = I18n.backend.send(:lookup, locale, key)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
I18n.backend.send(:translations)[locale] = nil # Clear out all current translations
|
76
|
+
I18n.backend.store_translations(locale, Translate::Keys.to_deep_hash(texts))
|
77
|
+
Translate::Storage.new(locale).write_to_file
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "Merge I18n keys from log/translations.yml into config/locales/*.yml (for use with the Rails I18n TextMate bundle)"
|
82
|
+
task :merge_keys => :environment do
|
83
|
+
I18n.backend.send(:init_translations)
|
84
|
+
new_translations = YAML::load(IO.read(File.join(Rails.root, "log", "translations.yml")))
|
85
|
+
raise("Can only merge in translations in single locale") if new_translations.keys.size > 1
|
86
|
+
locale = new_translations.keys.first
|
87
|
+
|
88
|
+
overwrites = false
|
89
|
+
Translate::Keys.to_shallow_hash(new_translations[locale]).keys.each do |key|
|
90
|
+
new_text = key.split(".").inject(new_translations[locale]) { |hash, sub_key| hash[sub_key] }
|
91
|
+
existing_text = I18n.backend.send(:lookup, locale.to_sym, key)
|
92
|
+
if existing_text && new_text != existing_text
|
93
|
+
puts "ERROR: key #{key} already exists with text '#{existing_text.inspect}' and would be overwritten by new text '#{new_text}'. " +
|
94
|
+
"Set environment variable OVERWRITE=1 if you really want to do this."
|
95
|
+
overwrites = true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if !overwrites || ENV['OVERWRITE']
|
100
|
+
I18n.backend.store_translations(locale, new_translations[locale])
|
101
|
+
Translate::Storage.new(locale).write_to_file
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "Apply Google translate to auto translate all texts in locale ENV['FROM'] to locale ENV['TO']"
|
106
|
+
task :google => :environment do
|
107
|
+
raise "Please specify FROM and TO locales as environment variables" if ENV['FROM'].blank? || ENV['TO'].blank?
|
108
|
+
|
109
|
+
# Depends on httparty gem
|
110
|
+
# http://www.robbyonrails.com/articles/2009/03/16/httparty-goes-foreign
|
111
|
+
class GoogleApi
|
112
|
+
include HTTParty
|
113
|
+
base_uri 'ajax.googleapis.com'
|
114
|
+
def self.translate(string, to, from)
|
115
|
+
tries = 0
|
116
|
+
begin
|
117
|
+
get("/ajax/services/language/translate",
|
118
|
+
:query => {:langpair => "#{from}|#{to}", :q => string, :v => 1.0},
|
119
|
+
:format => :json)
|
120
|
+
rescue
|
121
|
+
tries += 1
|
122
|
+
puts("SLEEPING - retrying in 5...")
|
123
|
+
sleep(5)
|
124
|
+
retry if tries < 10
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
I18n.backend.send(:init_translations)
|
130
|
+
|
131
|
+
start_at = Time.now
|
132
|
+
translations = {}
|
133
|
+
Translate::Keys.new.i18n_keys(ENV['FROM']).each do |key|
|
134
|
+
from_text = I18n.backend.send(:lookup, ENV['FROM'], key).to_s
|
135
|
+
to_text = I18n.backend.send(:lookup, ENV['TO'], key)
|
136
|
+
if !from_text.blank? && to_text.blank?
|
137
|
+
print "#{key}: '#{from_text[0, 40]}' => "
|
138
|
+
if !translations[from_text]
|
139
|
+
response = GoogleApi.translate(from_text, ENV['TO'], ENV['FROM'])
|
140
|
+
translations[from_text] = response["responseData"] && response["responseData"]["translatedText"]
|
141
|
+
end
|
142
|
+
if !(translation = translations[from_text]).blank?
|
143
|
+
translation.gsub!(/\(\(([a-z_.]+)\)\)/i, '{{\1}}')
|
144
|
+
# Google translate sometimes replaces {{foobar}} with (()) foobar. We skip these
|
145
|
+
if translation !~ /\(\(\)\)/
|
146
|
+
puts "'#{translation[0, 40]}'"
|
147
|
+
I18n.backend.store_translations(ENV['TO'].to_sym, Translate::Keys.to_deep_hash({key => translation}))
|
148
|
+
else
|
149
|
+
puts "SKIPPING since interpolations were messed up: '#{translation[0,40]}'"
|
150
|
+
end
|
151
|
+
else
|
152
|
+
puts "NO TRANSLATION - #{response.inspect}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
puts "\nTime elapsed: #{(((Time.now - start_at) / 60) * 10).to_i / 10.to_f} minutes"
|
158
|
+
Translate::Storage.new(ENV['TO'].to_sym).write_to_file
|
159
|
+
end
|
160
|
+
|
161
|
+
desc "List keys that have changed I18n texts between YAML file ENV['FROM_FILE'] and YAML file ENV['TO_FILE']. Set ENV['VERBOSE'] to see changes"
|
162
|
+
task :changed => :environment do
|
163
|
+
from_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['FROM_FILE']).read)
|
164
|
+
to_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['TO_FILE']).read)
|
165
|
+
from_hash.each do |key, from_value|
|
166
|
+
if (to_value = to_hash[key]) && to_value != from_value
|
167
|
+
key_without_locale = key[/^[^.]+\.(.+)$/, 1]
|
168
|
+
if ENV['VERBOSE']
|
169
|
+
puts "KEY: #{key_without_locale}"
|
170
|
+
puts "FROM VALUE: '#{from_value}'"
|
171
|
+
puts "TO VALUE: '#{to_value}'"
|
172
|
+
else
|
173
|
+
puts key_without_locale
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/lib/translate.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class Translate::File
|
4
|
+
attr_accessor :path
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
self.path = path
|
8
|
+
end
|
9
|
+
|
10
|
+
def write(keys)
|
11
|
+
FileUtils.mkdir_p File.dirname(path)
|
12
|
+
File.open(path, "w") do |file|
|
13
|
+
file.puts keys_to_yaml(Translate::File.deep_stringify_keys(keys))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def read
|
18
|
+
File.exists?(path) ? YAML::load(IO.read(path)) : {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Stringifying keys for prettier YAML
|
22
|
+
def self.deep_stringify_keys(hash)
|
23
|
+
hash.inject({}) { |result, (key, value)|
|
24
|
+
value = deep_stringify_keys(value) if value.is_a? Hash
|
25
|
+
result[(key.to_s rescue key) || key] = value
|
26
|
+
result
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def keys_to_yaml(keys)
|
32
|
+
# Using ya2yaml, if available, for UTF8 support
|
33
|
+
keys.respond_to?(:ya2yaml) ? keys.ya2yaml(:escape_as_utf8 => true) : keys.to_yaml
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
class Translate::Keys
|
4
|
+
# Allows keys extracted from lookups in files to be cached
|
5
|
+
def self.files
|
6
|
+
@@files ||= Translate::Keys.new.files
|
7
|
+
end
|
8
|
+
|
9
|
+
# Allows flushing of the files cache
|
10
|
+
def self.files=(files)
|
11
|
+
@@files = files
|
12
|
+
end
|
13
|
+
|
14
|
+
def files
|
15
|
+
@files ||= extract_files
|
16
|
+
end
|
17
|
+
alias_method :to_hash, :files
|
18
|
+
|
19
|
+
def keys
|
20
|
+
files.keys
|
21
|
+
end
|
22
|
+
alias_method :to_a, :keys
|
23
|
+
|
24
|
+
def i18n_keys(locale)
|
25
|
+
I18n.backend.send(:init_translations) unless I18n.backend.initialized?
|
26
|
+
Translate::Keys.to_shallow_hash(I18n.backend.send(:translations)[locale.to_sym]).keys.sort
|
27
|
+
end
|
28
|
+
|
29
|
+
def untranslated_keys
|
30
|
+
Translate::Keys.translated_locales.inject({}) do |missing, locale|
|
31
|
+
missing[locale] = i18n_keys(I18n.default_locale).map do |key|
|
32
|
+
I18n.backend.send(:lookup, locale, key).nil? ? key : nil
|
33
|
+
end.compact
|
34
|
+
missing
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def missing_keys
|
39
|
+
locale = I18n.default_locale; yaml_keys = {}
|
40
|
+
yaml_keys = Translate::Storage.file_paths(locale).inject({}) do |keys, path|
|
41
|
+
keys = keys.deep_merge(Translate::File.new(path).read[locale.to_s])
|
42
|
+
end
|
43
|
+
files.reject { |key, file| self.class.contains_key?(yaml_keys, key) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.translated_locales
|
47
|
+
I18n.available_locales.reject { |locale| [:root, I18n.default_locale.to_sym].include?(locale) }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Checks if a nested hash contains the keys in dot separated I18n key.
|
51
|
+
#
|
52
|
+
# Example:
|
53
|
+
#
|
54
|
+
# hash = {
|
55
|
+
# :foo => {
|
56
|
+
# :bar => {
|
57
|
+
# :baz => 1
|
58
|
+
# }
|
59
|
+
# }
|
60
|
+
# }
|
61
|
+
#
|
62
|
+
# contains_key?("foo", key) # => true
|
63
|
+
# contains_key?("foo.bar", key) # => true
|
64
|
+
# contains_key?("foo.bar.baz", key) # => true
|
65
|
+
# contains_key?("foo.bar.baz.bla", key) # => false
|
66
|
+
#
|
67
|
+
def self.contains_key?(hash, key)
|
68
|
+
keys = key.to_s.split(".")
|
69
|
+
return false if keys.empty?
|
70
|
+
!keys.inject(HashWithIndifferentAccess.new(hash)) do |memo, key|
|
71
|
+
memo.is_a?(Hash) ? memo.try(:[], key) : nil
|
72
|
+
end.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Convert something like:
|
76
|
+
#
|
77
|
+
# {
|
78
|
+
# :pressrelease => {
|
79
|
+
# :label => {
|
80
|
+
# :one => "Pressmeddelande"
|
81
|
+
# }
|
82
|
+
# }
|
83
|
+
# }
|
84
|
+
#
|
85
|
+
# to:
|
86
|
+
#
|
87
|
+
# {'pressrelease.label.one' => "Pressmeddelande"}
|
88
|
+
#
|
89
|
+
def self.to_shallow_hash(hash)
|
90
|
+
hash.inject({}) do |shallow_hash, (key, value)|
|
91
|
+
if value.is_a?(Hash)
|
92
|
+
to_shallow_hash(value).each do |sub_key, sub_value|
|
93
|
+
shallow_hash[[key, sub_key].join(".")] = sub_value
|
94
|
+
end
|
95
|
+
else
|
96
|
+
shallow_hash[key.to_s] = value
|
97
|
+
end
|
98
|
+
shallow_hash
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Convert something like:
|
103
|
+
#
|
104
|
+
# {'pressrelease.label.one' => "Pressmeddelande"}
|
105
|
+
#
|
106
|
+
# to:
|
107
|
+
#
|
108
|
+
# {
|
109
|
+
# :pressrelease => {
|
110
|
+
# :label => {
|
111
|
+
# :one => "Pressmeddelande"
|
112
|
+
# }
|
113
|
+
# }
|
114
|
+
# }
|
115
|
+
def self.to_deep_hash(hash)
|
116
|
+
hash.inject({}) do |deep_hash, (key, value)|
|
117
|
+
keys = key.to_s.split('.').reverse
|
118
|
+
leaf_key = keys.shift
|
119
|
+
key_hash = keys.inject({leaf_key.to_sym => value}) { |hash, key| {key.to_sym => hash} }
|
120
|
+
deep_merge!(deep_hash, key_hash)
|
121
|
+
deep_hash
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
126
|
+
def self.deep_merge!(hash1, hash2)
|
127
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
128
|
+
hash1.merge!(hash2, &merger)
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def extract_files
|
134
|
+
files_to_scan.inject(HashWithIndifferentAccess.new) do |files, file|
|
135
|
+
IO.read(file).scan(i18n_lookup_pattern).flatten.map(&:to_sym).each do |key|
|
136
|
+
files[key] ||= []
|
137
|
+
path = Pathname.new(File.expand_path(file)).relative_path_from(Pathname.new(Rails.root)).to_s
|
138
|
+
files[key] << path if !files[key].include?(path)
|
139
|
+
end
|
140
|
+
files
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def i18n_lookup_pattern
|
145
|
+
/\b(?:I18n\.t|I18n\.translate|t)(?:\s|\():?'([a-z0-9_]+.[a-z0-9_.]+)'\)?/
|
146
|
+
end
|
147
|
+
|
148
|
+
def files_to_scan
|
149
|
+
Dir.glob(File.join(Translate::Storage.root_dir, "{app,config,lib}", "**","*.{rb,erb,rhtml}")) +
|
150
|
+
Dir.glob(File.join(Translate::Storage.root_dir, "public", "javascripts", "**","*.js"))
|
151
|
+
end
|
152
|
+
end
|