PageRenderTime 61ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/rails/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb

https://github.com/adambair/spacetrader
Ruby | 193 lines | 126 code | 21 blank | 46 comment | 18 complexity | 15d6eaddf0dc18daf10f45fd4b269442 MD5 | raw file
  1. require 'strscan'
  2. module I18n
  3. module Backend
  4. class Simple
  5. # Allow client libraries to pass a block that populates the translation
  6. # storage. Decoupled for backends like a db backend that persist their
  7. # translations, so the backend can decide whether/when to yield or not.
  8. def populate(&block)
  9. yield
  10. end
  11. # Accepts a list of paths to translation files. Loads translations from
  12. # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
  13. # for details.
  14. def load_translations(*filenames)
  15. filenames.each {|filename| load_file filename }
  16. end
  17. # Stores translations for the given locale in memory.
  18. # This uses a deep merge for the translations hash, so existing
  19. # translations will be overwritten by new ones only at the deepest
  20. # level of the hash.
  21. def store_translations(locale, data)
  22. merge_translations(locale, data)
  23. end
  24. def translate(locale, key, options = {})
  25. raise InvalidLocale.new(locale) if locale.nil?
  26. return key.map{|k| translate locale, k, options } if key.is_a? Array
  27. reserved = :scope, :default
  28. count, scope, default = options.values_at(:count, *reserved)
  29. options.delete(:default)
  30. values = options.reject{|name, value| reserved.include? name }
  31. entry = lookup(locale, key, scope) || default(locale, default, options) || raise(I18n::MissingTranslationData.new(locale, key, options))
  32. entry = pluralize locale, entry, count
  33. entry = interpolate locale, entry, values
  34. entry
  35. end
  36. # Acts the same as +strftime+, but returns a localized version of the
  37. # formatted date string. Takes a key from the date/time formats
  38. # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
  39. def localize(locale, object, format = :default)
  40. raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
  41. type = object.respond_to?(:sec) ? 'time' : 'date'
  42. formats = translate(locale, :"#{type}.formats")
  43. format = formats[format.to_sym] if formats && formats[format.to_sym]
  44. # TODO raise exception unless format found?
  45. format = format.to_s.dup
  46. format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
  47. format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
  48. format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
  49. format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
  50. format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
  51. object.strftime(format)
  52. end
  53. protected
  54. def translations
  55. @translations ||= {}
  56. end
  57. # Looks up a translation from the translations hash. Returns nil if
  58. # eiher key is nil, or locale, scope or key do not exist as a key in the
  59. # nested translations hash. Splits keys or scopes containing dots
  60. # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
  61. # <tt>%w(currency format)</tt>.
  62. def lookup(locale, key, scope = [])
  63. return unless key
  64. keys = I18n.send :normalize_translation_keys, locale, key, scope
  65. keys.inject(translations){|result, k| result[k.to_sym] or return nil }
  66. end
  67. # Evaluates a default translation.
  68. # If the given default is a String it is used literally. If it is a Symbol
  69. # it will be translated with the given options. If it is an Array the first
  70. # translation yielded will be returned.
  71. #
  72. # <em>I.e.</em>, <tt>default(locale, [:foo, 'default'])</tt> will return +default+ if
  73. # <tt>translate(locale, :foo)</tt> does not yield a result.
  74. def default(locale, default, options = {})
  75. case default
  76. when String then default
  77. when Symbol then translate locale, default, options
  78. when Array then default.each do |obj|
  79. result = default(locale, obj, options.dup) and return result
  80. end and nil
  81. end
  82. rescue MissingTranslationData
  83. nil
  84. end
  85. # Picks a translation from an array according to English pluralization
  86. # rules. It will pick the first translation if count is not equal to 1
  87. # and the second translation if it is equal to 1. Other backends can
  88. # implement more flexible or complex pluralization rules.
  89. def pluralize(locale, entry, count)
  90. return entry unless entry.is_a?(Hash) and count
  91. # raise InvalidPluralizationData.new(entry, count) unless entry.is_a?(Hash)
  92. key = :zero if count == 0 && entry.has_key?(:zero)
  93. key ||= count == 1 ? :one : :other
  94. raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
  95. entry[key]
  96. end
  97. # Interpolates values into a given string.
  98. #
  99. # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X'
  100. # # => "file test.txt opened by {{user}}"
  101. #
  102. # Note that you have to double escape the <tt>\\</tt> when you want to escape
  103. # the <tt>{{...}}</tt> key in a string (once for the string and once for the
  104. # interpolation).
  105. def interpolate(locale, string, values = {})
  106. return string if !string.is_a?(String)
  107. string = string.gsub(/%d/, '{{count}}').gsub(/%s/, '{{value}}')
  108. if string.respond_to?(:force_encoding)
  109. original_encoding = string.encoding
  110. string.force_encoding(Encoding::BINARY)
  111. end
  112. s = StringScanner.new(string)
  113. while s.skip_until(/\{\{/)
  114. s.string[s.pos - 3, 1] = '' and next if s.pre_match[-1, 1] == '\\'
  115. start_pos = s.pos - 2
  116. key = s.scan_until(/\}\}/)[0..-3]
  117. end_pos = s.pos - 1
  118. raise ReservedInterpolationKey.new(key, string) if %w(scope default).include?(key)
  119. raise MissingInterpolationArgument.new(key, string) unless values.has_key? key.to_sym
  120. s.string[start_pos..end_pos] = values[key.to_sym].to_s
  121. s.unscan
  122. end
  123. result = s.string
  124. result.force_encoding(original_encoding) if original_encoding
  125. result
  126. end
  127. # Loads a single translations file by delegating to #load_rb or
  128. # #load_yml depending on the file extension and directly merges the
  129. # data to the existing translations. Raises I18n::UnknownFileType
  130. # for all other file extensions.
  131. def load_file(filename)
  132. type = File.extname(filename).tr('.', '').downcase
  133. raise UnknownFileType.new(type, filename) unless respond_to? :"load_#{type}"
  134. data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash
  135. data.each{|locale, d| merge_translations locale, d }
  136. end
  137. # Loads a plain Ruby translations file. eval'ing the file must yield
  138. # a Hash containing translation data with locales as toplevel keys.
  139. def load_rb(filename)
  140. eval IO.read(filename), binding, filename
  141. end
  142. # Loads a YAML translations file. The data must have locales as
  143. # toplevel keys.
  144. def load_yml(filename)
  145. YAML::load IO.read(filename)
  146. end
  147. # Deep merges the given translations hash with the existing translations
  148. # for the given locale
  149. def merge_translations(locale, data)
  150. locale = locale.to_sym
  151. translations[locale] ||= {}
  152. data = deep_symbolize_keys data
  153. # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
  154. merger = proc{|key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
  155. translations[locale].merge! data, &merger
  156. end
  157. # Return a new hash with all keys and nested keys converted to symbols.
  158. def deep_symbolize_keys(hash)
  159. hash.inject({}){|result, (key, value)|
  160. value = deep_symbolize_keys(value) if value.is_a? Hash
  161. result[(key.to_sym rescue key) || key] = value
  162. result
  163. }
  164. end
  165. end
  166. end
  167. end