i18n_backend_database_rails3 0.2.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.
Files changed (71) hide show
  1. data/Gemfile +4 -0
  2. data/LICENSE +21 -0
  3. data/README.textile +125 -0
  4. data/Rakefile +1 -0
  5. data/data/locales.yml +4 -0
  6. data/generators/i18n_backend_database/i18n_backend_database_generator.rb +8 -0
  7. data/generators/i18n_backend_database/templates/migrate/create_i18n_tables.rb +19 -0
  8. data/i18n_backend_database.gemspec +24 -0
  9. data/init.rb +1 -0
  10. data/lib/controllers/locales_controller.rb +86 -0
  11. data/lib/controllers/translations_controller.rb +141 -0
  12. data/lib/ext/i18n.rb +68 -0
  13. data/lib/google_language.rb +30 -0
  14. data/lib/i18n_backend_database.rb +9 -0
  15. data/lib/i18n_backend_database/database.rb +263 -0
  16. data/lib/i18n_backend_database/version.rb +7 -0
  17. data/lib/i18n_util.rb +148 -0
  18. data/lib/models/locale.rb +67 -0
  19. data/lib/models/translation.rb +46 -0
  20. data/lib/models/translation_option.rb +26 -0
  21. data/lib/public/images/custom1_bar.gif +0 -0
  22. data/lib/public/images/custom1_box.gif +0 -0
  23. data/lib/public/images/percentImage.png +0 -0
  24. data/lib/public/images/percentImage_back.png +0 -0
  25. data/lib/public/images/percentImage_back1.png +0 -0
  26. data/lib/public/images/percentImage_back2.png +0 -0
  27. data/lib/public/images/percentImage_back3.png +0 -0
  28. data/lib/public/images/percentImage_back4.png +0 -0
  29. data/lib/public/javascripts/jsProgressBarHandler.js +509 -0
  30. data/lib/routing.rb +15 -0
  31. data/lib/views/layouts/translations.html.haml +16 -0
  32. data/lib/views/locales/edit.html.haml +16 -0
  33. data/lib/views/locales/index.html.haml +14 -0
  34. data/lib/views/locales/new.html.haml +14 -0
  35. data/lib/views/locales/show.html.haml +10 -0
  36. data/lib/views/translations/asset_translations.html.haml +23 -0
  37. data/lib/views/translations/edit.html.haml +24 -0
  38. data/lib/views/translations/index.html.haml +21 -0
  39. data/lib/views/translations/new.html.haml +14 -0
  40. data/lib/views/translations/show.html.haml +21 -0
  41. data/lib/views/translations/translations.html.haml +20 -0
  42. data/lib/views/translations/update.rjs +7 -0
  43. data/routes.rb +3 -0
  44. data/spec/assets/public/es/favicons/favicon1.gif +0 -0
  45. data/spec/assets/public/es/images/icons/icon1.gif +0 -0
  46. data/spec/assets/public/es/images/image1.gif +0 -0
  47. data/spec/assets/public/favicons/favicon1.gif +0 -0
  48. data/spec/assets/public/favicons/favicon2.gif +0 -0
  49. data/spec/assets/public/images/icons/icon1.gif +0 -0
  50. data/spec/assets/public/images/icons/icon2.gif +0 -0
  51. data/spec/assets/public/images/image1.gif +0 -0
  52. data/spec/assets/public/images/image2.gif +0 -0
  53. data/spec/assets/public/images/promo/sfc08_140x400_3.gif +0 -0
  54. data/spec/assets/views/test_view1.html.erb +1 -0
  55. data/spec/assets/views/test_view2.html.erb +30 -0
  56. data/spec/caching_spec.rb +87 -0
  57. data/spec/controllers/locales_controller_spec.rb +173 -0
  58. data/spec/controllers/locales_routing_spec.rb +59 -0
  59. data/spec/controllers/translations_controller_spec.rb +189 -0
  60. data/spec/controllers/translations_routing_spec.rb +59 -0
  61. data/spec/database_spec.rb +199 -0
  62. data/spec/i18n_ext_spec.rb +40 -0
  63. data/spec/localize_spec.rb +66 -0
  64. data/spec/localize_text_spec.rb +76 -0
  65. data/spec/models/locale_spec.rb +111 -0
  66. data/spec/models/translation_spec.rb +44 -0
  67. data/spec/spec_helper.rb +16 -0
  68. data/spec/translate_asset_spec.rb +57 -0
  69. data/spec/translate_spec.rb +546 -0
  70. data/tasks/i18n.rake +60 -0
  71. metadata +143 -0
@@ -0,0 +1,68 @@
1
+ module I18n
2
+ APP_DIRECTORY = 'app/views'
3
+
4
+ class << self
5
+
6
+ def locale_segment
7
+ I18n.locale.to_s == I18n.default_locale.to_s ? "" : "/#{I18n.locale}"
8
+ end
9
+ alias :ls :locale_segment
10
+
11
+ def localize_text(text, options = {})
12
+ locale = options[:locale] || I18n.locale
13
+ backend.localize_text(locale, text)
14
+ end
15
+ alias :lt :localize_text
16
+
17
+ def tag_localized_text(text)
18
+ backend.localize_text_tag + text + backend.localize_text_tag
19
+ end
20
+ alias :tlt :tag_localized_text
21
+
22
+ def translate_asset(asset, options={})
23
+ locale = options.delete(:locale) || I18n.locale
24
+ if locale_asset = locale_asset(asset, locale)
25
+ locale_asset
26
+ else
27
+ asset
28
+ end
29
+ end
30
+ alias ta translate_asset
31
+
32
+ def untranslated_assets(locale)
33
+ return [] if locale.to_s == I18n.default_locale.to_s #default locale assets are assumed to exist
34
+ assets = asset_translations
35
+ assets.reject! {|asset| locale_asset_exists?(locale, asset) }
36
+ assets
37
+ end
38
+
39
+ def asset_translations(dir=APP_DIRECTORY)
40
+ assets = []
41
+ Dir.glob("#{dir}/*").each do |item|
42
+ if File.directory?(item)
43
+ assets += asset_translations(item)
44
+ else
45
+ File.readlines(item).each do |l|
46
+ assets += l.scan(/I18n.ta\(["'](.*?)["']\)/).flatten
47
+ end
48
+ end
49
+ end
50
+ assets.uniq
51
+ end
52
+
53
+ protected
54
+
55
+ def locale_asset_exists?(locale, asset)
56
+ File.exists?("#{ActionView::Helpers::AssetTagHelper::ASSETS_DIR}/#{locale}#{asset_path(asset)}")
57
+ end
58
+
59
+ def locale_asset(asset, locale)
60
+ locale_asset_exists?(locale, asset) ? "/#{locale}#{asset_path(asset)}" : nil
61
+ end
62
+
63
+ def asset_path(asset)
64
+ asset[0] == ?/ ? asset : "/images/#{asset}"
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,30 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'json'
4
+
5
+ class GoogleLanguage
6
+
7
+ # Thanks http://ruby.geraldbauer.ca/google-translation-api.html
8
+ def self.translate( text, to, from='en' )
9
+ base = 'https://www.googleapis.com/language/translate/v2'
10
+
11
+ # assemble query params
12
+ params = {
13
+ :source => "#{from}",
14
+ :target => "#{to}",
15
+ :q => text,
16
+ :key => # add your google app key
17
+ }
18
+
19
+ query = params.map{ |k,v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
20
+
21
+ # send get request
22
+ # response = Net::HTTP.get_response( URI.parse( "#{base}?#{query}" ) )
23
+ puts "URI >> #{URI.parse( "#{base}?#{query}")}"
24
+ response = JSON.parse(open(URI.parse( "#{base}?#{query}" )).read)
25
+
26
+ response['data']['translations'].first['translatedText']
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/models/locale'
2
+ require File.dirname(__FILE__) + '/models/translation'
3
+ require File.dirname(__FILE__) + '/models/translation_option'
4
+ require File.dirname(__FILE__) + '/routing'
5
+ require File.dirname(__FILE__) + '/controllers/locales_controller'
6
+ require File.dirname(__FILE__) + '/controllers/translations_controller'
7
+ require File.dirname(__FILE__) + '/i18n_backend_database/database'
8
+ require File.dirname(__FILE__) + '/ext/i18n'
9
+ ActionController::Routing::RouteSet::Mapper.send(:include, I18n::BackendDatabase::Routing)
@@ -0,0 +1,263 @@
1
+
2
+ module I18n
3
+ module Backend
4
+ class Database
5
+ INTERPOLATION_RESERVED_KEYS = %w(scope default)
6
+ MATCH = /(\\\\)?\{\{([^\}]+)\}\}/
7
+
8
+ attr_accessor :locale
9
+ attr_accessor :cache_store
10
+ attr_accessor :localize_text_tag
11
+
12
+ def initialize(options = {})
13
+ store = options.delete(:cache_store)
14
+ text_tag = options.delete(:localize_text_tag)
15
+ @cache_store = store ? ActiveSupport::Cache.lookup_store(store) : Rails.cache
16
+ @localize_text_tag = text_tag ? text_tag : '^^'
17
+ end
18
+
19
+ def locale=(code)
20
+ @locale = Locale.find_by_code(code)
21
+ end
22
+
23
+ def cache_store=(store)
24
+ @cache_store = ActiveSupport::Cache.lookup_store(store)
25
+ end
26
+
27
+ # Handles the lookup and addition of translations to the database
28
+ #
29
+ # On an initial translation, the locale is checked to determine if
30
+ # this is the default locale. If it is, we'll create a complete
31
+ # translation record for this locale with both the key and value.
32
+ #
33
+ # If the current locale is checked, and it differs from the default
34
+ # locale, we'll create a translation record with a nil value. This
35
+ # allows for the lookup of untranslated records in a given locale.
36
+ def translate(locale, key, options = {})
37
+ @locale = locale_in_context(locale)
38
+
39
+ options[:scope] = [options[:scope]] unless options[:scope].is_a?(Array) || options[:scope].blank?
40
+ key = "#{options[:scope].join('.')}.#{key}".to_sym if options[:scope] && key.is_a?(Symbol)
41
+ count = options[:count]
42
+ # pull out values for interpolation
43
+ values = options.reject { |name, value| [:scope, :default].include?(name) }
44
+
45
+ entry = lookup(@locale, key)
46
+ cache_lookup = true unless entry.nil?
47
+
48
+ # if no entry exists for the current locale and the current locale is not the default locale then lookup translations for the default locale for this key
49
+ unless entry || @locale.default_locale?
50
+ entry = use_and_copy_default_locale_translations_if_they_exist(@locale, key)
51
+ end
52
+
53
+ # if we have no entry and some defaults ... start looking them up
54
+ unless entry || key.is_a?(String) || options[:default].blank?
55
+ default = options[:default].is_a?(Array) ? options[:default].shift : options.delete(:default)
56
+ return translate(@locale.code, default, options.dup)
57
+ end
58
+
59
+ # this needs to be folded into the above at some point.
60
+ # this handles the case where the default of the string key is a space
61
+ if !entry && key.is_a?(String) && options[:default] == " "
62
+ default = options[:default].is_a?(Array) ? options[:default].shift : options.delete(:default)
63
+ return translate(@locale.code, default, options.dup)
64
+ end
65
+
66
+ # The requested key might not be a parent node in a hierarchy of keys instead of a regular 'leaf' node
67
+ # that would simply result in a string return. If so, check the database for possible children
68
+ # and return them in a nested hash if we find them.
69
+ # We can safely ignore pluralization indeces here since they should never apply to a hash return
70
+ if !entry && (key.is_a?(String) || key.is_a?(Symbol))
71
+ #We need to escape % and \. Rails will handle the rest.
72
+ escaped_key = key.to_s.gsub('\\', '\\\\\\\\').gsub(/%/, '\%')
73
+ children = @locale.translations.find :all, :conditions => ["raw_key like ?", "#{escaped_key}.%"]
74
+ if children.size > 0
75
+ entry = hashify_record_array(key.to_s, children)
76
+ @cache_store.write(Translation.ck(@locale, key), entry) unless cache_lookup == true
77
+ return entry
78
+ end
79
+ end
80
+
81
+ # we check the database before creating a translation as we can have translations with nil values
82
+ # if we still have no blasted translation just go and create one for the current locale!
83
+ unless entry
84
+ pluralization_index = (options[:count].nil? || options[:count] == 1) ? 1 : 0
85
+ translation = @locale.translations.find_by_key_and_pluralization_index(Translation.hk(key), pluralization_index) ||
86
+ @locale.create_translation(key, key, pluralization_index)
87
+ entry = translation.value_or_default
88
+ end
89
+
90
+ # write to cache unless we've already had a successful cache hit
91
+ @cache_store.write(Translation.ck(@locale, key), entry) unless cache_lookup == true
92
+
93
+ entry = pluralize(@locale, entry, count)
94
+ entry = interpolate(@locale.code, entry, values)
95
+ entry.is_a?(Array) ? entry.dup : entry # array's can get frozen with cache writes
96
+ end
97
+
98
+ # Acts the same as +strftime+, but returns a localized version of the
99
+ # formatted date string. Takes a key from the date/time formats
100
+ # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
101
+ def localize(locale, object, format = :default)
102
+ raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
103
+
104
+ type = object.respond_to?(:sec) ? 'time' : 'date'
105
+ format = translate(locale, "#{type}.formats.#{format.to_s}") unless format.to_s.index('%') # lookup keyed formats unless a custom format is passed
106
+
107
+ format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
108
+ format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
109
+ format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
110
+ format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
111
+ format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
112
+
113
+ object.strftime(format)
114
+ end
115
+
116
+ # Returns the text string with the text within the localize text tags translated.
117
+ def localize_text(locale, text)
118
+ text_tag = Regexp.escape(localize_text_tag).to_s
119
+ expression = Regexp.new(text_tag + "(.*?)" + text_tag)
120
+ tagged_text = text[expression, 1]
121
+ while tagged_text do
122
+ text = text.sub(expression, translate(locale, tagged_text))
123
+ tagged_text = text[expression, 1]
124
+ end
125
+ return text
126
+ end
127
+
128
+ def available_locales
129
+ Locale.available_locales
130
+ end
131
+
132
+ def reload!
133
+ # get's called on initialization
134
+ # let's not do anything yet
135
+ end
136
+
137
+ protected
138
+ # keep a local copy of the locale in context for use within the translation
139
+ # routine, and also accept an arbitrary locale for one time locale lookups
140
+ def locale_in_context(locale)
141
+ return @locale if @locale && @locale.code == locale.to_s
142
+ #Locale.find_by_code(locale.to_s) rescue nil && (raise InvalidLocale.new(locale))
143
+ locale = Locale.find_by_code(locale.to_s)
144
+ raise InvalidLocale.new(locale) unless locale
145
+ locale
146
+ end
147
+
148
+ # lookup key in cache and db, if the db is hit the value is cached
149
+ def lookup(locale, key)
150
+ cache_key = Translation.ck(locale, key)
151
+ if @cache_store.exist?(cache_key) && value = @cache_store.read(cache_key)
152
+ return value
153
+ else
154
+ translations = locale.translations.find_all_by_key(Translation.hk(key))
155
+ case translations.size
156
+ when 0
157
+ value = nil
158
+ when 1
159
+ value = translations.first.value_or_default
160
+ else
161
+ value = translations.inject([]) do |values, t|
162
+ values[t.pluralization_index] = t.value_or_default
163
+ values
164
+ end
165
+ end
166
+
167
+ @cache_store.write(cache_key, (value.nil? ? nil : value))
168
+ return value
169
+ end
170
+ end
171
+
172
+ # looks up translations for the default locale, and if they exist untranslated records are created for the locale and the default locale values are returned
173
+ def use_and_copy_default_locale_translations_if_they_exist(locale, key)
174
+ default_locale_entry = lookup(Locale.default_locale, key)
175
+ return unless default_locale_entry
176
+
177
+ if default_locale_entry.is_a?(Array)
178
+ default_locale_entry.each_with_index do |entry, index|
179
+ locale.create_translation(key, nil, index) if entry
180
+ end
181
+ else
182
+ locale.create_translation(key, nil)
183
+ end
184
+
185
+ return default_locale_entry
186
+ end
187
+
188
+ def pluralize(locale, entry, count)
189
+ return entry unless entry.is_a?(Array) and count
190
+ count = count == 1 ? 1 : 0
191
+ entry.compact[count]
192
+ end
193
+
194
+ # Interpolates values into a given string.
195
+ #
196
+ # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X'
197
+ # # => "file test.txt opened by {{user}}"
198
+ #
199
+ # Note that you have to double escape the <tt>\\</tt> when you want to escape
200
+ # the <tt>{{...}}</tt> key in a string (once for the string and once for the
201
+ # interpolation).
202
+ def interpolate(locale, string, values = {})
203
+ return string unless string.is_a?(String)
204
+
205
+ if string.respond_to?(:force_encoding)
206
+ original_encoding = string.encoding
207
+ string.force_encoding(Encoding::BINARY)
208
+ end
209
+
210
+ result = string.gsub(MATCH) do
211
+ escaped, pattern, key = $1, $2, $2.to_sym
212
+
213
+ if escaped
214
+ pattern
215
+ elsif INTERPOLATION_RESERVED_KEYS.include?(pattern)
216
+ raise ReservedInterpolationKey.new(pattern, string)
217
+ elsif !values.include?(key)
218
+ raise MissingInterpolationArgument.new(pattern, string)
219
+ else
220
+ values[key].to_s
221
+ end
222
+ end
223
+
224
+ result.force_encoding(original_encoding) if original_encoding
225
+ result
226
+ end
227
+
228
+ def strip_root_key(root_key, key)
229
+ return nil if key.nil?
230
+ return key.gsub(/^#{root_key}\./, '')
231
+ end
232
+
233
+ def hashify_record_array(root_key, record_array)
234
+ return nil if record_array.nil? || record_array.empty?
235
+
236
+ #Make sure that all of our records have raw_keys
237
+ record_array.reject! {|record| record.raw_key.nil?}
238
+
239
+ # Start building our return hash
240
+ result = {}
241
+ record_array.each { |record|
242
+ key = strip_root_key(root_key, record.raw_key)
243
+ next unless key.present?
244
+
245
+ # If we contain a period delimiter, we need to add a sub-hash.
246
+ # Otherwise, we just insert the value at this level.
247
+ if key.index(".")
248
+ internal_node = key.slice(0, key.index('.'))
249
+ new_root = root_key + '.' + internal_node
250
+ new_record_array = record_array.select {|record| record.raw_key.starts_with? new_root}
251
+ result[internal_node.to_sym] = hashify_record_array(new_root, new_record_array)
252
+ else
253
+ value = record.value
254
+ value = value.to_i if value == "0" || value.to_i != 0 #simple integer cast
255
+ result[key.to_sym] = value
256
+ end
257
+ }
258
+ result
259
+ end
260
+
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,7 @@
1
+ module I18n
2
+ module Backend
3
+ module Database
4
+ VERSION = "0.2.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,148 @@
1
+ class I18nUtil
2
+
3
+ # Create tanslation records from the YAML file. Will create the required locales if they do not exist.
4
+ def self.load_from_yml(file_name)
5
+ data = YAML::load(IO.read(file_name))
6
+ data.each do |code, translations|
7
+ locale = Locale.find_or_create_by_code(code)
8
+ backend = I18n::Backend::Simple.new
9
+ keys = extract_i18n_keys(translations)
10
+ keys.each do |key|
11
+ value = backend.send(:lookup, code, key)
12
+
13
+ pluralization_index = 1
14
+
15
+ if key.ends_with?('.one')
16
+ key.gsub!('.one', '')
17
+ end
18
+
19
+ if key.ends_with?('.other')
20
+ key.gsub!('.other', '')
21
+ pluralization_index = 0
22
+ end
23
+
24
+ if value.is_a?(Array)
25
+ value.each_with_index do |v, index|
26
+ create_translation(locale, "#{key}", index, v) unless v.nil?
27
+ end
28
+ else
29
+ create_translation(locale, key, pluralization_index, value)
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+
36
+ # Finds or creates a translation record and updates the value
37
+ def self.create_translation(locale, key, pluralization_index, value)
38
+ translation = locale.translations.find_by_key_and_pluralization_index(Translation.hk(key), pluralization_index) # find existing record by hash key
39
+ unless translation # or build new one with raw key
40
+ translation = locale.translations.build(:key =>key, :pluralization_index => pluralization_index)
41
+ puts "from yaml create translation for #{locale.code} : #{key} : #{pluralization_index}" unless RAILS_ENV['test']
42
+ end
43
+ translation.value = value
44
+ translation.save!
45
+ end
46
+
47
+ def self.extract_i18n_keys(hash, parent_keys = [])
48
+ hash.inject([]) do |keys, (key, value)|
49
+ full_key = parent_keys + [key]
50
+ if value.is_a?(Hash)
51
+ # Nested hash
52
+ keys += extract_i18n_keys(value, full_key)
53
+ elsif !value.nil?
54
+ # String leaf node
55
+ keys << full_key.join(".")
56
+ end
57
+ keys
58
+ end
59
+ end
60
+
61
+ # Create translation records for all existing locales from translation calls with the application. Ignores errors from tranlations that require objects.
62
+ def self.seed_application_translations(dir='app')
63
+ translated_objects(dir).each do |object|
64
+ interpolation_arguments= object.scan(/\{\{(.*?)\}\}/).flatten
65
+ object = object[/'(.*?)'/, 1] || object[/"(.*?)"/, 1]
66
+ options = {}
67
+ interpolation_arguments.each { |arg| options[arg.to_sym] = nil }
68
+ next if object.nil?
69
+
70
+ puts "translating for #{object} with options #{options.inspect}" unless RAILS_ENV['test']
71
+ I18n.t(object, options) # default locale first
72
+ locales = Locale.available_locales
73
+ locales.delete(I18n.default_locale)
74
+ # translate for other locales
75
+ locales.each do |locale|
76
+ I18n.t(object, options.merge(:locale => locale))
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ def self.translated_objects(dir='app')
83
+ assets = []
84
+ Dir.glob("#{dir}/*").each do |item|
85
+ if File.directory?(item)
86
+ assets += translated_objects(item) unless item.ends_with?('i18n_backend_database') # ignore self
87
+ else
88
+ File.readlines(item).each do |l|
89
+ assets += l.scan(/I18n.t\((.*?)\)/).flatten
90
+ end
91
+ end
92
+ end
93
+ assets.uniq
94
+ end
95
+
96
+ # Populate translation records from the default locale to other locales if no record exists.
97
+ def self.synchronize_translations
98
+ non_default_locales = Locale.non_defaults
99
+ Locale.default_locale.translations.each do |t|
100
+ non_default_locales.each do |locale|
101
+ unless locale.translations.exists?(:key => t.key, :pluralization_index => t.pluralization_index)
102
+ value = t.value =~ /^---(.*)\n/ ? t.value : nil # well will copy across YAML, like symbols
103
+ locale.translations.create!(:key => t.raw_key, :value => value, :pluralization_index => t.pluralization_index)
104
+ puts "synchronizing has created translation for #{locale.code} : #{t.raw_key} : #{t.pluralization_index}" unless RAILS_ENV['test']
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def self.google_translate
111
+ Locale.non_defaults.each do |locale|
112
+ locale.translations.untranslated.each do |translation|
113
+ default_locale_value = translation.default_locale_value
114
+ unless needs_human_eyes?(default_locale_value)
115
+ interpolation_arguments= default_locale_value.scan(/\{\{(.*?)\}\}/).flatten
116
+
117
+ if interpolation_arguments.empty?
118
+ translation.value = GoogleLanguage.translate(default_locale_value, locale.code, Locale.default_locale.code)
119
+ translation.save!
120
+ else
121
+ placeholder_value = 990 # at least in :es it seems to leave a 3 digit number in the postion on the string
122
+ placeholders = {}
123
+
124
+ # replace {{interpolation_arguments}} with a numeric place holder
125
+ interpolation_arguments.each do |interpolation_argument|
126
+ default_locale_value.gsub!("{{#{interpolation_argument}}}", "#{placeholder_value}")
127
+ placeholders[placeholder_value] = interpolation_argument
128
+ placeholder_value += 1
129
+ end
130
+
131
+ # translate string
132
+ translated_value = GoogleLanguage.translate(default_locale_value, locale.code, Locale.default_locale.code)
133
+
134
+ # replace numeric place holders with {{interpolation_arguments}}
135
+ placeholders.each {|placeholder_value,interpolation_argument| translated_value.gsub!("#{placeholder_value}", "{{#{interpolation_argument}}}") }
136
+ translation.value = translated_value
137
+ translation.save!
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ def self.needs_human_eyes?(value)
145
+ return true if value.index('%') # date formats
146
+ return true if value =~ /^---(.*)\n/ # YAML
147
+ end
148
+ end