Module: Air18n::ClassMethods

Defined in:
lib/air18n/class_methods.rb

Instance Method Summary collapse

Instance Method Details

#available_localesObject

Return the air18n backend’s available locales. Advantages/disadvantages over asking the Rails backend: Advantage: returns only locales that we actually have translations for. Disadvantage: when all locales haven’t been sucked in from DB, won’t

return all locales we support.

Disadvantage: auto-generated locales like Singaporean English won’t be

included.


411
412
413
# File 'lib/air18n/class_methods.rb', line 411

def available_locales
  @air18n_backend.available_locales
end

#base_directionObject



124
125
126
# File 'lib/air18n/class_methods.rb', line 124

def base_direction
  base_direction_of(locale)
end

#base_direction_of(locale) ⇒ Object



120
121
122
# File 'lib/air18n/class_methods.rb', line 120

def base_direction_of(locale)
  RTL_LOCALES.include?(locale) ? 'rtl' : 'ltr'
end

#check_for_new_translationsObject



286
287
288
# File 'lib/air18n/class_methods.rb', line 286

def check_for_new_translations
  @air18n_backend.check_for_new_translations(I18n.full_locale)
end

#contextual_translationObject



142
143
144
145
146
147
148
# File 'lib/air18n/class_methods.rb', line 142

def contextual_translation
  if full_locale != default_locale
    @contextual_translation
  else
    false
  end
end

#contextual_translation=(x) ⇒ Object



150
151
152
# File 'lib/air18n/class_methods.rb', line 150

def contextual_translation= x
  @contextual_translation= x
end

#currency=(c) ⇒ Object



308
309
310
# File 'lib/air18n/class_methods.rb', line 308

def currency=(c)
  @currency = c
end

#default_languageObject

Returns the language code of I18n.default_locale, as a symbol.



14
15
16
# File 'lib/air18n/class_methods.rb', line 14

def default_language
  language_from_locale default_locale
end

#fallbacks_for(the_locale, opts = {}) ⇒ Object

Return the fallback locales for the_locale. If opts is set, the default locale, which is otherwise always the last one in the returned list, will be excluded.

For example, fallbacks_for(:“pt-BR”) is [:“pt-BR”, :pt, :en] with :exclude_default => false and [:“pt-BR”, :pt] with :exclude_default => true.



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/air18n/class_methods.rb', line 249

def fallbacks_for(the_locale, opts = {})
  # We dup() because otherwise the fallbacks hash might itself be modified
  # by the user of this method, and that would be terrible.
  ret = @fallbacks[the_locale].dup

  # We make two assumptions here:
  # 1) There is only one default locale (that is, it has no less-specific
  #    children)
  # 1) The default locale is just a language. (Like :en, and not :"en-US".)
  if opts[:exclude_default] && ret.last == self.default_locale && ret.last != language_from_locale(the_locale)
    ret.pop
  end

  ret
end

#falls_back_to?(the_locale, other_locale) ⇒ Boolean

Returns whether the_locale has other_locale in its non-default fallbacks.

Returns:

  • (Boolean)


266
267
268
# File 'lib/air18n/class_methods.rb', line 266

def falls_back_to?(the_locale, other_locale)
  fallbacks_for(the_locale, :exclude_default => true).include?(other_locale)
end

#force_cache_refresh(locale) ⇒ Object

This will cause cached translations to be updated and updates the ‘last_loaded_at’ timestamp in the cache. WARNING: You may still get stale data if another app is already in the process of updating a warm but stale cache.



281
282
283
284
# File 'lib/air18n/class_methods.rb', line 281

def force_cache_refresh(locale)
  I18n.mark_translations_as_changed(locale)
  I18n.check_for_new_translations(locale)
end

#format_date(d, locale = nil) ⇒ Object

Use when formatted string will be used by jQuery UI or calendar text fields



344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/air18n/class_methods.rb', line 344

def format_date d, locale = nil
  # temp handle iso date strings
  if d.is_a?(String)
    d = Date.parse(d)
  end

  locale ||= I18n.full_locale
  format = I18n.t('date.formats.default', :locale => locale.to_sym)
  if d.nil?
    format.gsub('%m', 'mm').gsub('%d', 'dd').gsub('%Y', 'yy')
  else
    I18n.l(d, :format => format)
  end
end

#full_localeObject

Returns full locale as a symbol, like :es, :en, :“es-419” (Latin-American Spanish) or :“pt-BR” or :“en-GB”.



28
29
30
# File 'lib/air18n/class_methods.rb', line 28

def full_locale
  config.locale
end

#full_locale=(locale) ⇒ Object

The one true way to set the locale.



33
34
35
# File 'lib/air18n/class_methods.rb', line 33

def full_locale=(locale)
  config.locale = locale.to_sym
end

#has_translation?(key, locale) ⇒ Boolean

If there is a translation of given key in given, or a less-specific fallback locale, returns the most specific locale that has a translation. Returns nil otherwise.

For “guessed” specific locales like British English, returns the specific locale only if there is a manually-written translation for that locale. Otherwise, if the given locale falls back to the default locale, returns the default locale.

This is useful for knowing whether or not there is a translation for a key in a locale. It can also be used to determine whether a translation comes from a fallback locale or not.

Returns:

  • (Boolean)


401
402
403
# File 'lib/air18n/class_methods.rb', line 401

def has_translation?(key, locale)
  @air18n_backend.has_translation?(key, locale)
end

#languageObject

Returns only the language part of the current locale, as a symbol. E.g. “pt” if the locale is pt-BR.



9
10
11
# File 'lib/air18n/class_methods.rb', line 9

def language
  language_from_locale full_locale
end

#language_from_locale(locale) ⇒ Object

For given locale (like :en, :en-GB, :pt-PT, or “en”, “en-GB”, “pt-PT”), returns the “language” part (like :en or :pt). If locale is invalid or nil, returns the default language.



115
116
117
118
# File 'lib/air18n/class_methods.rb', line 115

def language_from_locale locale
  tags = I18n::Locale::Tag.tag(locale)
  tags ? tags.language.to_sym : language_from_locale(I18n.default_locale)
end

#locale=(locale) ⇒ Object

Deprecated. Sets locale. Using full_locale= is preferable.



39
40
41
42
# File 'lib/air18n/class_methods.rb', line 39

def locale=(locale)
  LoggingHelper.info "I18n.locale=() is deprecated. Use I18n.full_locale=()."
  self.full_locale = locale
end

#locale_with_language_hackObject

Deprecated. This returns the same as language(). TODO(jason) Switch all use of locale() to either language() or full_locale().



22
23
24
# File 'lib/air18n/class_methods.rb', line 22

def locale_with_language_hack
  language
end

#mark_translations_as_changed(locale) ⇒ Object

Call this when translations have been updated. Next time ‘check_for_new_translations’ is called, it will do a database read and update the cache.



273
274
275
# File 'lib/air18n/class_methods.rb', line 273

def mark_translations_as_changed(locale)
  I18n.cache.write(Air18n::Backend::T_LAST_UPDATED_AT % locale, Time.now) if I18n.cache
end

#needs_translation?(key) ⇒ Boolean

Returns true if key should be translated. A key needs to be translated if it is either UGC or was looked up by t() today or in the last week.

Returns:

  • (Boolean)


214
215
216
# File 'lib/air18n/class_methods.rb', line 214

def needs_translation?(key)
  phrase_key_is_ugc?(key) || still_used?(key)
end

#override_fallbacks(fully_specified_locale, fallbacks) ⇒ Object

Call after reset() to override fallbacks. For example,

override_fallbacks(:es, [:es, :"es-419", :en])

to make :es’s fallbacks include :“es-419”.



239
240
241
# File 'lib/air18n/class_methods.rb', line 239

def override_fallbacks(fully_specified_locale, fallbacks)
  @fallbacks[fully_specified_locale] = fallbacks
end

#parse_date(s, locale = nil) ⇒ Object

Parses a date using the current locale setting. 2-digit years are interpreted as 19XX (>= 69) or 20XX (< 69).



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/air18n/class_methods.rb', line 314

def parse_date s, locale = nil
  return s if s.nil?

  # `to_str` is needed for ActiveSupport::SafeBuffer coming in from controllers.
  s = s.to_str

  if /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\Z/.match s
    # If this looks like an ISO date, YYYY-mm-dd, let [Date.parse] handle
    # parsing since [Date.strptime] varies by platform (darwin vs. linux)
    Date.parse(s, true)
  else
    locale ||= I18n.full_locale
    format = I18n.t('date.formats.default', :locale => locale.to_sym)
    begin
      date = Date.strptime(s, format)
      if date.year < 100
        date = Date.strptime(s, format.gsub("%Y", "%y"))
      end
    rescue
      # When date is not parsable in user's locale attempt default
      # date handling for cases like booking where dates can be
      # in the international format "%Y-%m-%d". Failure to parse
      # after this will raise an error.
      date = Date.parse(s, true)
    end
    date
  end
end

#phrase_key_is_ugc?(key) ⇒ Boolean

Phrases that are from user-generated content (like hosting names) have a key prefixed with “ugc.”.

Returns:

  • (Boolean)


207
208
209
# File 'lib/air18n/class_methods.rb', line 207

def phrase_key_is_ugc?(key)
  key.starts_with?('ugc.')
end

#phrase_needs_screenshot(routes_context, phrase) ⇒ Object



137
138
139
140
# File 'lib/air18n/class_methods.rb', line 137

def phrase_needs_screenshot(routes_context, phrase)
  @phrases_needing_screenshot ||= Hash.new { |h, routes_context| h[routes_context] = {} }
  @phrases_needing_screenshot[routes_context].merge!(phrase)
end

#pluralize(count, singular, plural = nil) ⇒ Object

Like ActionView::Helpers::TextHelper#pluralize, except requires that ‘count’ be a real number, not a string.

ActionView::Helpers::TextHelper#pluralize is inherently English-centric. So this method is too, and assumes that the default locale’s plural forms are like “%smart_count count-is-one-form;%smart_count everything-else-form”.

Pass in nil for the count if you want to get the default text (for example, if you will be interpolating the count into the text in the front-end.) For example:

I18n.pluralize(nil, 'Review')
  => '%{smart_count} Review||||%{smart_count} Reviews'

Raises:

  • (TypeError)


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/air18n/class_methods.rb', line 91

def pluralize(count, singular, plural=nil)
  raise TypeError if count.is_a?(String)
  default_text = "%{smart_count} #{singular}#{SmartCount::DELIMITER}" +
                 "%{smart_count} #{plural || singular.pluralize}"
  if count == nil
    translate(pluralize_key(singular),
              :default => default_text,
              :suppress_ct => true)
  else
    translate(pluralize_key(singular),
              :default => default_text,
              :smart_count => count,
              :suppress_ct => true)
  end
end

#pluralize_key(singular) ⇒ Object

Returns the phrase key used by pluralize().



108
109
110
# File 'lib/air18n/class_methods.rb', line 108

def pluralize_key(singular)
  "shared.pluralize.#{singular}"
end

#reload_translationsObject

Reloads translations for the current locale.



291
292
293
# File 'lib/air18n/class_methods.rb', line 291

def reload_translations
  reload_translations_in(I18n.full_locale)
end

#reload_translations_in(locale) ⇒ Object

This is unaware of the cache and ignores it.



296
297
298
# File 'lib/air18n/class_methods.rb', line 296

def reload_translations_in(locale)
  @air18n_backend.reload_translations([locale])
end

#rescreenshot(routes_context) ⇒ Object



300
301
302
# File 'lib/air18n/class_methods.rb', line 300

def rescreenshot(routes_context)
  @air18n_backend.rescreenshot(routes_context)
end

#reset(default_locale) ⇒ Object

Create Air18n backend with given default_locale and translation prioritizer.



219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/air18n/class_methods.rb', line 219

def reset(default_locale)
  self.default_locale = default_locale

  # Use locale fallbacks that always include default locale as the last
  # fallback.
  @fallbacks ||= I18n::Locale::Fallbacks.new
  @fallbacks.defaults = [default_locale]

  # Make the single instance of Air18n::Backend.
  @air18n_backend = Air18n::Backend.new

  # This activates Air18n by putting Air18n::Backend before I18n.backend.
  self.backend = Air18n::LessSillyChain.new(@air18n_backend, I18n::Backend::Simple.new)
end

#reset_notion_of_still_used_phrasesObject

The notion of which keys are still used are cached and never reloaded, except after this method is called.



156
157
158
159
# File 'lib/air18n/class_methods.rb', line 156

def reset_notion_of_still_used_phrases
  @priority.reset
  @still_used_phrase_ids = nil
end

#retrieve_phrases_needing_screenshot(routes_context) ⇒ Object

Gets a hash containing phrases that need to be screenshot in the specified routes_context. After this method returns the phrases, it forgets about them, so you can only retrieve them once.



132
133
134
135
# File 'lib/air18n/class_methods.rb', line 132

def retrieve_phrases_needing_screenshot(routes_context)
  return {} unless @phrases_needing_screenshot && @phrases_needing_screenshot.include?(routes_context)
  @phrases_needing_screenshot.delete(routes_context)
end

#run_in_locale(locale) ⇒ Object

Sets I18n.full_locale to specified locale, yields, then sets I18n.full_locale back to what it was.

Returns what the block returns.

If locale is blank, leaves I18n.full_locale unaffected.

Useful for tests or where you want to render something in a certain language without the side-effect of changing I18n.full_locale.



54
55
56
57
58
59
60
61
62
63
# File 'lib/air18n/class_methods.rb', line 54

def run_in_locale(locale)
  # Be robust to people passing in nil locale.
  locale ||= I18n.default_locale

  old_locale = I18n.full_locale
  I18n.full_locale = locale
  ret = yield
  I18n.full_locale = old_locale
  ret
end

#set_default_text_change_observer(observer) ⇒ Object



304
305
306
# File 'lib/air18n/class_methods.rb', line 304

def set_default_text_change_observer(observer)
  @air18n_backend.default_text_change_observer = observer
end

#still_used?(key) ⇒ Boolean

Returns whether or not a phrase key is still used. If we don’t have data on phrase usage, then assumes all keys are still used.

Returns:

  • (Boolean)


201
202
203
# File 'lib/air18n/class_methods.rb', line 201

def still_used?(key)
  still_used_keys_hash.empty? || still_used_keys_hash.include?(key)
end

#still_used_keysObject

Returns list of keys looked up today or in the last week. If we don’t have data on phrase usage, returns all keys.



175
176
177
# File 'lib/air18n/class_methods.rb', line 175

def still_used_keys
  still_used_keys_hash.keys
end

#still_used_keys_hashObject

Returns OrderedHash mapping key => times looked up today or in the last week.



163
164
165
166
167
168
169
170
171
# File 'lib/air18n/class_methods.rb', line 163

def still_used_keys_hash
  # If you want to test with the full allotment of still-used phrases from
  # production, do this on mainframe:
  # File.open('/tmp/still_used', 'w') { |f| f.write Counter.retrieve_counts_from_today_and_last_week("t").to_json }
  # Then copy /tmp/still_used to your local machine, and uncomment the next line.
  # @still_used_keys ||= File.open('/tmp/still_used') { |f| JSON.parse(f.read()) }

  @priority ? @priority.key_usage : {}
end

#still_used_phrase_idsObject

Returns list of phrase IDs that are still used. Returns empty list if we don’t have data on phrase usage.



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/air18n/class_methods.rb', line 181

def still_used_phrase_ids
  @still_used_phrase_ids ||= begin
    key_to_phrase_id = {}
    # Uses raw SQL for speed.
    Phrase.connection.select_all("SELECT `id`, `key` FROM phrases").each do |record|
      key_to_phrase_id[record['key']] = record['id']
    end

    raw_phrase_ids = still_used_keys.map do |key|
      LoggingHelper.error "Mystery phrase key: #{key}" if !key_to_phrase_id.include? key
      key_to_phrase_id[key]
    end

    raw_phrase_ids.compact.uniq
  end
end

#translate_with_routes_context(key, options = {}) ⇒ Object

Add :routes_context option, which is used for translation screenshotting. I18n.fallback_routes_context is set by an ApplicationController before_filter.



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/air18n/class_methods.rb', line 362

def translate_with_routes_context(key, options = {})
  if !options.include?(:routes_context) && I18n.fallback_routes_context
    options = options.merge(:routes_context => I18n.fallback_routes_context)
  end
  result = translate_without_routes_context(key, options)

  # Handle contextual translation.
  if result && I18n.contextual_translation && (!options[:suppress_ct])
    phrase = Phrase.by_key(key)

    if phrase
      # Provides styling to wrap a string into a div so that it can be
      # clicked on for easy translation.
      result = "<phrase id='#{Digest::MD5.hexdigest(result + rand(1000).to_s)[0..8]}' data-phrase-id='#{phrase.id}'>#{result}</phrase>"
    end
  end

  if @priority.present? && key.is_a?(String) && options[:disable_bookkeeping].blank?
    # Handle bookkeeping of which translation keys are still used.
    @priority.key_used(key)
  end

  result = result.html_safe if result && result.respond_to?(:html_safe)

  result
end

#uses_imperial_lengths?Boolean

Returns whether or not imperial lengths are appropriate under I18n.full_locale and I18n.tld_country.

Returns:

  • (Boolean)


67
68
69
# File 'lib/air18n/class_methods.rb', line 67

def uses_imperial_lengths?
  uses_imperial_lengths_in_locale_and_country?(I18n.full_locale, I18n.tld_country)
end

#uses_imperial_lengths_in_locale_and_country?(locale, country) ⇒ Boolean

Returns whether or not imperial lengths are appropriate for given locale and country. Logic is: true in the US and UK when language is English, and false otherwise.

Returns:

  • (Boolean)


75
76
77
# File 'lib/air18n/class_methods.rb', line 75

def uses_imperial_lengths_in_locale_and_country?(locale, country)
  language_from_locale(locale) == :en && [:US, :GB].include?(country)
end