--- /dev/null
+module I18n
+ module Backend
+ # This module contains several helpers to assist flattening translations.
+ # You may want to flatten translations for:
+ #
+ # 1) speed up lookups, as in the Memoize backend;
+ # 2) In case you want to store translations in a data store, as in ActiveRecord backend;
+ #
+ # You can check both backends above for some examples.
+ # This module also keeps all links in a hash so they can be properly resolved when flattened.
+ module Flatten
+ SEPARATOR_ESCAPE_CHAR = "\001"
+ FLATTEN_SEPARATOR = "."
+
+ # normalize_keys the flatten way. This method is significantly faster
+ # and creates way less objects than the one at I18n.normalize_keys.
+ # It also handles escaping the translation keys.
+ def self.normalize_flat_keys(locale, key, scope, separator)
+ keys = [scope, key].flatten.compact
+ separator ||= I18n.default_separator
+
+ if separator != FLATTEN_SEPARATOR
+ keys.map! do |k|
+ k.to_s.tr("#{FLATTEN_SEPARATOR}#{separator}",
+ "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}")
+ end
+ end
+
+ keys.join(".")
+ end
+
+ # Receives a string and escape the default separator.
+ def self.escape_default_separator(key) #:nodoc:
+ key.to_s.tr(FLATTEN_SEPARATOR, SEPARATOR_ESCAPE_CHAR)
+ end
+
+ # Shortcut to I18n::Backend::Flatten.normalize_flat_keys
+ # and then resolve_links.
+ def normalize_flat_keys(locale, key, scope, separator)
+ key = I18n::Backend::Flatten.normalize_flat_keys(locale, key, scope, separator)
+ resolve_link(locale, key)
+ end
+
+ # Store flattened links.
+ def links
+ @links ||= Hash.new { |h,k| h[k] = {} }
+ end
+
+ # Flatten keys for nested Hashes by chaining up keys:
+ #
+ # >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind
+ # => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }
+ #
+ def flatten_keys(hash, escape, prev_key=nil, &block)
+ hash.each_pair do |key, value|
+ key = escape_default_separator(key) if escape
+ curr_key = [prev_key, key].compact.join(FLATTEN_SEPARATOR).to_sym
+ yield curr_key, value
+ flatten_keys(value, escape, curr_key, &block) if value.is_a?(Hash)
+ end
+ end
+
+ # Receives a hash of translations (where the key is a locale and
+ # the value is another hash) and return a hash with all
+ # translations flattened.
+ #
+ # Nested hashes are included in the flattened hash just if subtree
+ # is true and Symbols are automatically stored as links.
+ def flatten_translations(locale, data, escape, subtree)
+ hash = {}
+ flatten_keys(data, escape) do |key, value|
+ if value.is_a?(Hash)
+ hash[key] = value if subtree
+ else
+ store_link(locale, key, value) if value.is_a?(Symbol)
+ hash[key] = value
+ end
+ end
+ hash
+ end
+
+ protected
+
+ def store_link(locale, key, link)
+ links[locale.to_sym][key.to_s] = link.to_s
+ end
+
+ def resolve_link(locale, key)
+ key, locale = key.to_s, locale.to_sym
+ links = self.links[locale]
+
+ if links.key?(key)
+ links[key]
+ elsif link = find_link(locale, key)
+ store_link(locale, key, key.gsub(*link))
+ else
+ key
+ end
+ end
+
+ def find_link(locale, key) #:nodoc:
+ links[locale].each do |from, to|
+ return [from, to] if key[0, from.length] == from
+ end && nil
+ end
+
+ def escape_default_separator(key) #:nodoc:
+ I18n::Backend::Flatten.escape_default_separator(key)
+ end
+
+ end
+ end
+end
\ No newline at end of file