~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
require 'yaml'
2
 
 
3
 
module I18n
4
 
  module Backend
5
 
    class Simple
6
 
      INTERPOLATION_RESERVED_KEYS = %w(scope default)
7
 
      MATCH = /(\\\\)?\{\{([^\}]+)\}\}/
8
 
 
9
 
      # Accepts a list of paths to translation files. Loads translations from
10
 
      # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
11
 
      # for details.
12
 
      def load_translations(*filenames)
13
 
        filenames.each { |filename| load_file(filename) }
14
 
      end
15
 
 
16
 
      # Stores translations for the given locale in memory.
17
 
      # This uses a deep merge for the translations hash, so existing
18
 
      # translations will be overwritten by new ones only at the deepest
19
 
      # level of the hash.
20
 
      def store_translations(locale, data)
21
 
        merge_translations(locale, data)
22
 
      end
23
 
 
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
 
 
28
 
        reserved = :scope, :default
29
 
        count, scope, default = options.values_at(:count, *reserved)
30
 
        options.delete(:default)
31
 
        values = options.reject { |name, value| reserved.include?(name) }
32
 
 
33
 
        entry = lookup(locale, key, scope)
34
 
        if entry.nil?
35
 
          entry = default(locale, default, options)
36
 
          if entry.nil?
37
 
            raise(I18n::MissingTranslationData.new(locale, key, options))
38
 
          end
39
 
        end
40
 
        entry = pluralize(locale, entry, count)
41
 
        entry = interpolate(locale, entry, values)
42
 
        entry
43
 
      end
44
 
 
45
 
      # Acts the same as +strftime+, but returns a localized version of the
46
 
      # formatted date string. Takes a key from the date/time formats
47
 
      # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
48
 
      def localize(locale, object, format = :default)
49
 
        raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
50
 
 
51
 
        type = object.respond_to?(:sec) ? 'time' : 'date'
52
 
        # TODO only translate these if format is a String?
53
 
        formats = translate(locale, :"#{type}.formats")
54
 
        format = formats[format.to_sym] if formats && formats[format.to_sym]
55
 
        # TODO raise exception unless format found?
56
 
        format = format.to_s.dup
57
 
 
58
 
        # TODO only translate these if the format string is actually present
59
 
        # TODO check which format strings are present, then bulk translate then, then replace them
60
 
        format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
61
 
        format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
62
 
        format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
63
 
        format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
64
 
        format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
65
 
        object.strftime(format)
66
 
      end
67
 
 
68
 
      def initialized?
69
 
        @initialized ||= false
70
 
      end
71
 
 
72
 
      # Returns an array of locales for which translations are available
73
 
      def available_locales
74
 
        init_translations unless initialized?
75
 
        translations.keys
76
 
      end
77
 
 
78
 
      def reload!
79
 
        @initialized = false
80
 
        @translations = nil
81
 
      end
82
 
 
83
 
      protected
84
 
        def init_translations
85
 
          load_translations(*I18n.load_path.flatten)
86
 
          @initialized = true
87
 
        end
88
 
 
89
 
        def translations
90
 
          @translations ||= {}
91
 
        end
92
 
 
93
 
        # Looks up a translation from the translations hash. Returns nil if
94
 
        # eiher key is nil, or locale, scope or key do not exist as a key in the
95
 
        # nested translations hash. Splits keys or scopes containing dots
96
 
        # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
97
 
        # <tt>%w(currency format)</tt>.
98
 
        def lookup(locale, key, scope = [])
99
 
          return unless key
100
 
          init_translations unless initialized?
101
 
          keys = I18n.send(:normalize_translation_keys, locale, key, scope)
102
 
          keys.inject(translations) do |result, k|
103
 
            if (x = result[k.to_sym]).nil?
104
 
              return nil
105
 
            else
106
 
              x
107
 
            end
108
 
          end
109
 
        end
110
 
 
111
 
        # Evaluates a default translation.
112
 
        # If the given default is a String it is used literally. If it is a Symbol
113
 
        # it will be translated with the given options. If it is an Array the first
114
 
        # translation yielded will be returned.
115
 
        #
116
 
        # <em>I.e.</em>, <tt>default(locale, [:foo, 'default'])</tt> will return +default+ if
117
 
        # <tt>translate(locale, :foo)</tt> does not yield a result.
118
 
        def default(locale, default, options = {})
119
 
          case default
120
 
            when String then default
121
 
            when Symbol then translate locale, default, options
122
 
            when Array  then default.each do |obj|
123
 
              result = default(locale, obj, options.dup) and return result
124
 
            end and nil
125
 
          end
126
 
        rescue MissingTranslationData
127
 
          nil
128
 
        end
129
 
 
130
 
        # Picks a translation from an array according to English pluralization
131
 
        # rules. It will pick the first translation if count is not equal to 1
132
 
        # and the second translation if it is equal to 1. Other backends can
133
 
        # implement more flexible or complex pluralization rules.
134
 
        def pluralize(locale, entry, count)
135
 
          return entry unless entry.is_a?(Hash) and count
136
 
          # raise InvalidPluralizationData.new(entry, count) unless entry.is_a?(Hash)
137
 
          key = :zero if count == 0 && entry.has_key?(:zero)
138
 
          key ||= count == 1 ? :one : :other
139
 
          raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
140
 
          entry[key]
141
 
        end
142
 
 
143
 
        # Interpolates values into a given string.
144
 
        #
145
 
        #   interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X'
146
 
        #   # => "file test.txt opened by {{user}}"
147
 
        #
148
 
        # Note that you have to double escape the <tt>\\</tt> when you want to escape
149
 
        # the <tt>{{...}}</tt> key in a string (once for the string and once for the
150
 
        # interpolation).
151
 
        def interpolate(locale, string, values = {})
152
 
          return string unless string.is_a?(String)
153
 
 
154
 
          string.gsub(MATCH) do
155
 
            escaped, pattern, key = $1, $2, $2.to_sym
156
 
 
157
 
            if escaped
158
 
              pattern
159
 
            elsif INTERPOLATION_RESERVED_KEYS.include?(pattern)
160
 
              raise ReservedInterpolationKey.new(pattern, string)
161
 
            elsif !values.include?(key)
162
 
              raise MissingInterpolationArgument.new(pattern, string)
163
 
            else
164
 
              values[key].to_s
165
 
            end
166
 
          end
167
 
        end
168
 
 
169
 
        # Loads a single translations file by delegating to #load_rb or
170
 
        # #load_yml depending on the file extension and directly merges the
171
 
        # data to the existing translations. Raises I18n::UnknownFileType
172
 
        # for all other file extensions.
173
 
        def load_file(filename)
174
 
          type = File.extname(filename).tr('.', '').downcase
175
 
          raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}")
176
 
          data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash
177
 
          data.each { |locale, d| merge_translations(locale, d) }
178
 
        end
179
 
 
180
 
        # Loads a plain Ruby translations file. eval'ing the file must yield
181
 
        # a Hash containing translation data with locales as toplevel keys.
182
 
        def load_rb(filename)
183
 
          eval(IO.read(filename), binding, filename)
184
 
        end
185
 
 
186
 
        # Loads a YAML translations file. The data must have locales as
187
 
        # toplevel keys.
188
 
        def load_yml(filename)
189
 
          YAML::load(IO.read(filename))
190
 
        end
191
 
 
192
 
        # Deep merges the given translations hash with the existing translations
193
 
        # for the given locale
194
 
        def merge_translations(locale, data)
195
 
          locale = locale.to_sym
196
 
          translations[locale] ||= {}
197
 
          data = deep_symbolize_keys(data)
198
 
 
199
 
          # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
200
 
          merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
201
 
          translations[locale].merge!(data, &merger)
202
 
        end
203
 
 
204
 
        # Return a new hash with all keys and nested keys converted to symbols.
205
 
        def deep_symbolize_keys(hash)
206
 
          hash.inject({}) { |result, (key, value)|
207
 
            value = deep_symbolize_keys(value) if value.is_a? Hash
208
 
            result[(key.to_sym rescue key) || key] = value
209
 
            result
210
 
          }
211
 
        end
212
 
    end
213
 
  end
214
 
end
 
 
b'\\ No newline at end of file'