aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/gems/gettext-2.1.0/lib/gettext/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gems/gettext-2.1.0/lib/gettext/runtime')
-rw-r--r--vendor/gems/gettext-2.1.0/lib/gettext/runtime/class_info.rb67
-rw-r--r--vendor/gems/gettext-2.1.0/lib/gettext/runtime/locale_path.rb122
-rw-r--r--vendor/gems/gettext-2.1.0/lib/gettext/runtime/mofile.rb330
-rw-r--r--vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain.rb177
-rw-r--r--vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_group.rb24
-rw-r--r--vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_manager.rb211
6 files changed, 931 insertions, 0 deletions
diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/runtime/class_info.rb b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/class_info.rb
new file mode 100644
index 000000000..c7acca267
--- /dev/null
+++ b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/class_info.rb
@@ -0,0 +1,67 @@
+require 'locale/util/memoizable'
+
+module GetText
+ # For normalize/finding the related classes/modules.
+ # This is used for realizing the scope of TextDomain.
+ # (see: http://www.yotabanana.com/hiki/ruby-gettext-scope.html)
+ module ClassInfo
+ extend self
+ include Locale::Util::Memoizable
+
+ # normalize the class name
+ # klass should kind of the class, not object.
+ def normalize_class(klass)
+ ret = (klass.kind_of? Module) ? klass : klass.class
+ if ret.name =~ /^\#<|^$/ or ret == GetText
+ ret = Object
+ end
+ ret
+ end
+
+ def root_ancestors # :nodoc:
+ Object.ancestors
+ end
+ memoize :root_ancestors
+
+ # Internal method for related_classes.
+ def related_classes_internal(klass, all_classes = [], analyzed_classes = [] )
+ ret = []
+ klass = normalize_class(klass)
+
+ return [Object] if root_ancestors.include? klass
+
+ ary = klass.name.split(/::/)
+ while(v = ary.shift)
+ ret.unshift(((ret.size == 0) ? Object.const_get(v) : ret[0].const_get(v)))
+ end
+ ret -= analyzed_classes
+ if ret.size > 1
+ ret += related_classes_internal(ret[1], all_classes, analyzed_classes)
+ ret.uniq!
+ end
+ analyzed_classes << klass unless analyzed_classes.include? klass
+
+ klass.ancestors[1..-1].each do |a|
+ ret += related_classes_internal(a, all_classes, analyzed_classes)
+ ret.uniq!
+ end
+
+ if all_classes.size > 0
+ (ret & all_classes).uniq
+ else
+ ret.uniq
+ end
+ end
+
+ # Returns the classes which related to klass
+ # (klass's ancestors, included modules and nested modules)
+ def related_classes(klass, all_classes = [])
+ ret = related_classes_internal(klass, all_classes)
+ unless ret.include? Object
+ ret += [Object]
+ end
+ ret
+ end
+ memoize :related_classes
+ end
+end
diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/runtime/locale_path.rb b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/locale_path.rb
new file mode 100644
index 000000000..5500bca20
--- /dev/null
+++ b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/locale_path.rb
@@ -0,0 +1,122 @@
+=begin
+ locale_path.rb - GetText::LocalePath
+
+ Copyright (C) 2001-2009 Masao Mutoh
+
+ You may redistribute it and/or modify it under the same
+ license terms as Ruby or LGPL.
+
+=end
+
+require 'rbconfig'
+require 'gettext/core_ext/string'
+
+module GetText
+ # Treats locale-path for mo-files.
+ class LocalePath
+ include Locale::Util::Memoizable
+
+ # The default locale paths.
+ CONFIG_PREFIX = Config::CONFIG['prefix'].gsub(/\/local/, "")
+ DEFAULT_RULES = [
+ "./locale/%{lang}/LC_MESSAGES/%{name}.mo",
+ "./locale/%{lang}/%{name}.mo",
+ "#{Config::CONFIG['datadir']}/locale/%{lang}/LC_MESSAGES/%{name}.mo",
+ "#{Config::CONFIG['datadir'].gsub(/\/local/, "")}/locale/%{lang}/LC_MESSAGES/%{name}.mo",
+ "#{CONFIG_PREFIX}/share/locale/%{lang}/LC_MESSAGES/%{name}.mo",
+ "#{CONFIG_PREFIX}/local/share/locale/%{lang}/LC_MESSAGES/%{name}.mo"
+ ].uniq
+
+ class << self
+ include Locale::Util::Memoizable
+
+ # Add default locale path. Usually you should use GetText.add_default_locale_path instead.
+ # * path: a new locale path. (e.g.) "/usr/share/locale/%{lang}/LC_MESSAGES/%{name}.mo"
+ # ('locale' => "ja_JP", 'name' => "textdomain")
+ # * Returns: the new DEFAULT_LOCALE_PATHS
+ def add_default_rule(path)
+ DEFAULT_RULES.unshift(path)
+ end
+
+ # Returns path rules as an Array.
+ # (e.g.) ["/usr/share/locale/%{lang}/LC_MESSAGES/%{name}.mo", ...]
+ def default_path_rules
+ default_path_rules = []
+
+ if ENV["GETTEXT_PATH"]
+ ENV["GETTEXT_PATH"].split(/,/).each {|i|
+ default_path_rules = ["#{i}/%{lang}/LC_MESSAGES/%{name}.mo", "#{i}/%{lang}/%{name}.mo"]
+ }
+ end
+
+ default_path_rules += DEFAULT_RULES
+
+ load_path = $LOAD_PATH
+ if defined? ::Gem
+ load_path += Gem.all_load_paths
+ end
+ load_path.map!{|v| v.match(/(.*?)(\/lib)*?$/); $1}
+ load_path.each {|path|
+ default_path_rules += [
+ "#{path}/data/locale/%{lang}/LC_MESSAGES/%{name}.mo",
+ "#{path}/data/locale/%{lang}/%{name}.mo",
+ "#{path}/locale/%{lang}/%{name}.mo"]
+ }
+ # paths existed only.
+ default_path_rules = default_path_rules.select{|path|
+ Dir.glob(path % {:lang => "*", :name => "*"}).size > 0}.uniq
+ default_path_rules
+ end
+ memoize_dup :default_path_rules
+ end
+
+ attr_reader :locale_paths, :supported_locales
+
+ # Creates a new GetText::TextDomain.
+ # * name: the textdomain name.
+ # * topdir: the locale path ("%{topdir}/%{lang}/LC_MESSAGES/%{name}.mo") or nil.
+ def initialize(name, topdir = nil)
+ @name = name
+
+ if topdir
+ path_rules = ["#{topdir}/%{lang}/LC_MESSAGES/%{name}.mo", "#{topdir}/%{lang}/%{name}.mo"]
+ else
+ path_rules = self.class.default_path_rules
+ end
+
+ @locale_paths = {}
+ path_rules.each do |rule|
+ this_path_rules = rule % {:lang => "([^\/]+)", :name => name}
+ Dir.glob(rule %{:lang => "*", :name => name}).each do |path|
+ if /#{this_path_rules}/ =~ path
+ @locale_paths[$1] = path unless @locale_paths[$1]
+ end
+ end
+ end
+ @supported_locales = @locale_paths.keys.sort
+ end
+
+ # Gets the current path.
+ # * lang: a Locale::Tag.
+ def current_path(lang)
+ lang_candidates = lang.to_posix.candidates
+ search_files = []
+
+ lang_candidates.each do |tag|
+ path = @locale_paths[tag.to_s]
+ warn "GetText::TextDomain#load_mo: mo-file is #{path}" if $DEBUG
+ return path if path
+ end
+
+ if $DEBUG
+ warn "MO file is not found in"
+ @locale_paths.each do |path|
+ warn " #{path[1]}"
+ end
+ end
+ nil
+ end
+ memoize :current_path
+
+ end
+end
diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/runtime/mofile.rb b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/mofile.rb
new file mode 100644
index 000000000..dd8158d01
--- /dev/null
+++ b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/mofile.rb
@@ -0,0 +1,330 @@
+=begin
+ mofile.rb - A simple class for operating GNU MO file.
+
+ Copyright (C) 2003-2009 Masao Mutoh
+ Copyright (C) 2002 Masahiro Sakai, Masao Mutoh
+ Copyright (C) 2001 Masahiro Sakai
+
+ Masahiro Sakai <s01397ms at sfc.keio.ac.jp>
+ Masao Mutoh <mutomasa at gmail.com>
+
+ You can redistribute this file and/or modify it under the same term
+ of Ruby. License of Ruby is included with Ruby distribution in
+ the file "README".
+
+=end
+
+require 'gettext/core_ext/iconv'
+require 'stringio'
+
+module GetText
+ class MOFile < Hash
+ class InvalidFormat < RuntimeError; end;
+
+ attr_reader :filename
+
+ Header = Struct.new(:magic,
+ :revision,
+ :nstrings,
+ :orig_table_offset,
+ :translated_table_offset,
+ :hash_table_size,
+ :hash_table_offset)
+
+ # The following are only used in .mo files
+ # with minor revision >= 1.
+ class HeaderRev1 < Header
+ attr_accessor :n_sysdep_segments,
+ :sysdep_segments_offset,
+ :n_sysdep_strings,
+ :orig_sysdep_tab_offset,
+ :trans_sysdep_tab_offset
+ end
+
+ MAGIC_BIG_ENDIAN = "\x95\x04\x12\xde"
+ MAGIC_LITTLE_ENDIAN = "\xde\x12\x04\x95"
+
+ def self.open(arg = nil, output_charset = nil)
+ result = self.new(output_charset)
+ result.load(arg)
+ end
+
+ def initialize(output_charset = nil)
+ @filename = nil
+ @last_modified = nil
+ @little_endian = true
+ @output_charset = output_charset
+ @plural_proc = nil
+ super()
+ end
+
+ def update!
+ if FileTest.exist?(@filename)
+ st = File.stat(@filename)
+ load(@filename) unless (@last_modified == [st.ctime, st.mtime])
+ else
+ warn "#{@filename} was lost." if $DEBUG
+ clear
+ end
+ self
+ end
+
+ def load(arg)
+ if arg.kind_of? String
+ begin
+ st = File.stat(arg)
+ @last_modified = [st.ctime, st.mtime]
+ rescue Exception
+ end
+ load_from_file(arg)
+ else
+ load_from_stream(arg)
+ end
+ @filename = arg
+ self
+ end
+
+ def load_from_stream(io)
+ magic = io.read(4)
+ case magic
+ when MAGIC_BIG_ENDIAN
+ @little_endian = false
+ when MAGIC_LITTLE_ENDIAN
+ @little_endian = true
+ else
+ raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
+ end
+
+ endian_type6 = @little_endian ? 'V6' : 'N6'
+ endian_type_astr = @little_endian ? 'V*' : 'N*'
+
+ header = HeaderRev1.new(magic, *(io.read(4 * 6).unpack(endian_type6)))
+
+ if header.revision == 1
+ # FIXME: It doesn't support sysdep correctly.
+ header.n_sysdep_segments = io.read(4).unpack(endian_type6)
+ header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
+ header.n_sysdep_strings = io.read(4).unpack(endian_type6)
+ header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
+ header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
+ elsif header.revision > 1
+ raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
+ end
+ io.pos = header.orig_table_offset
+ orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
+
+ io.pos = header.translated_table_offset
+ trans_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
+
+ original_strings = Array.new(header.nstrings)
+ for i in 0...header.nstrings
+ io.pos = orig_table_data[i * 2 + 1]
+ original_strings[i] = io.read(orig_table_data[i * 2 + 0])
+ end
+
+ clear
+ for i in 0...header.nstrings
+ io.pos = trans_table_data[i * 2 + 1]
+ str = io.read(trans_table_data[i * 2 + 0])
+
+ if (! original_strings[i]) || original_strings[i] == ""
+ if str
+ @charset = nil
+ @nplurals = nil
+ @plural = nil
+ str.each_line{|line|
+ if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
+ @charset = $1
+ elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
+ @nplurals = $1
+ @plural = $2
+ end
+ break if @charset and @nplurals
+ }
+ @nplurals = "1" unless @nplurals
+ @plural = "0" unless @plural
+ end
+ else
+ if @output_charset
+ begin
+ str = Iconv.conv(@output_charset, @charset, str) if @charset
+ rescue Iconv::Failure
+ if $DEBUG
+ warn "@charset = ", @charset
+ warn"@output_charset = ", @output_charset
+ warn "msgid = ", original_strings[i]
+ warn "msgstr = ", str
+ end
+ end
+ end
+ end
+ self[original_strings[i]] = str.freeze
+ end
+ self
+ end
+
+ def prime?(number)
+ ('1' * number) !~ /^1?$|^(11+?)\1+$/
+ end
+
+ begin
+ require 'prime'
+ def next_prime(seed)
+ Prime.instance.find{|x| x > seed }
+ end
+ rescue LoadError
+ def next_prime(seed)
+ require 'mathn'
+ prime = Prime.new
+ while current = prime.succ
+ return current if current > seed
+ end
+ end
+ end
+
+ HASHWORDBITS = 32
+ # From gettext-0.12.1/gettext-runtime/intl/hash-string.h
+ # Defines the so called `hashpjw' function by P.J. Weinberger
+ # [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
+ # 1986, 1987 Bell Telephone Laboratories, Inc.]
+ def hash_string(str)
+ hval = 0
+ i = 0
+ str.each_byte do |b|
+ break if b == '\0'
+ hval <<= 4
+ hval += b.to_i
+ g = hval & (0xf << (HASHWORDBITS - 4))
+ if (g != 0)
+ hval ^= g >> (HASHWORDBITS - 8)
+ hval ^= g
+ end
+ end
+ hval
+ end
+
+ #Save data as little endian format.
+ def save_to_stream(io)
+ header_size = 4 * 7
+ table_size = 4 * 2 * size
+
+ hash_table_size = next_prime((size * 4) / 3)
+ hash_table_size = 3 if hash_table_size <= 2
+ header = Header.new(
+ MAGIC_LITTLE_ENDIAN, # magic
+ 0, # revision
+ size, # nstrings
+ header_size, # orig_table_offset
+ header_size + table_size, # translated_table_offset
+ hash_table_size, # hash_table_size
+ header_size + table_size * 2 # hash_table_offset
+ )
+ io.write(header.to_a.pack('a4V*'))
+
+ ary = to_a
+ ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
+
+ pos = header.hash_table_size * 4 + header.hash_table_offset
+
+ orig_table_data = Array.new()
+ ary.each{|item, _|
+ orig_table_data.push(item.bytesize)
+ orig_table_data.push(pos)
+ pos += item.bytesize + 1 # +1 is <NUL>
+ }
+ io.write(orig_table_data.pack('V*'))
+
+ trans_table_data = Array.new()
+ ary.each{|_, item|
+ trans_table_data.push(item.bytesize)
+ trans_table_data.push(pos)
+ pos += item.bytesize + 1 # +1 is <NUL>
+ }
+ io.write(trans_table_data.pack('V*'))
+
+ hash_tab = Array.new(hash_table_size)
+ j = 0
+ ary[0...size].each {|key, _|
+ hash_val = hash_string(key)
+ idx = hash_val % hash_table_size
+ if hash_tab[idx] != nil
+ incr = 1 + (hash_val % (hash_table_size - 2))
+ begin
+ if (idx >= hash_table_size - incr)
+ idx -= hash_table_size - incr
+ else
+ idx += incr
+ end
+ end until (hash_tab[idx] == nil)
+ end
+ hash_tab[idx] = j + 1
+ j += 1
+ }
+ hash_tab.collect!{|i| i ? i : 0}
+
+ io.write(hash_tab.pack('V*'))
+
+ ary.each{|item, _| io.write(item); io.write("\0") }
+ ary.each{|_, item| io.write(item); io.write("\0") }
+
+ self
+ end
+
+ def load_from_file(filename)
+ @filename = filename
+ begin
+ File.open(filename, 'rb'){|f| load_from_stream(f)}
+ rescue => e
+ e.set_backtrace("File: #{@filename}")
+ raise e
+ end
+ end
+
+ def save_to_file(filename)
+ File.open(filename, 'wb'){|f| save_to_stream(f)}
+ end
+
+ def set_comment(msgid_or_sym, comment)
+ #Do nothing
+ end
+
+ def plural_as_proc
+ unless @plural_proc
+ @plural_proc = Proc.new{|n| eval(@plural)}
+ begin
+ @plural_proc.call(1)
+ rescue
+ @plural_proc = Proc.new{|n| 0}
+ end
+ end
+ @plural_proc
+ end
+
+ attr_accessor :little_endian, :path, :last_modified
+ attr_reader :charset, :nplurals, :plural
+ end
+
+end
+
+# Test
+
+if $0 == __FILE__
+ if (ARGV.include? "-h") or (ARGV.include? "--help")
+ STDERR.puts("mo.rb [filename.mo ...]")
+ exit
+ end
+
+ ARGV.each{ |item|
+ mo = GetText::MOFile.open(item)
+ puts "------------------------------------------------------------------"
+ puts "charset = \"#{mo.charset}\""
+ puts "nplurals = \"#{mo.nplurals}\""
+ puts "plural = \"#{mo.plural}\""
+ puts "------------------------------------------------------------------"
+ mo.each do |key, value|
+ puts "original message = #{key.inspect}"
+ puts "translated message = #{value.inspect}"
+ puts "--------------------------------------------------------------------"
+ end
+ }
+end
diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain.rb b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain.rb
new file mode 100644
index 000000000..ce52fab3a
--- /dev/null
+++ b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain.rb
@@ -0,0 +1,177 @@
+=begin
+ textdomain.rb - GetText::Textdomain
+
+ Copyright (C) 2001-2009 Masao Mutoh
+ Copyright (C) 2001-2003 Masahiro Sakai
+
+ Masahiro Sakai <s01397ms@sfc.keio.ac.jp>
+ Masao Mutoh <mutomasa at gmail.com>
+
+ You may redistribute it and/or modify it under the same
+ license terms as Ruby or LGPL.
+=end
+
+require 'gettext/core_ext/string'
+require 'gettext/runtime/mofile'
+require 'gettext/runtime/locale_path'
+
+module GetText
+ # GetText::TextDomain class manages mo-files of a textdomain.
+ #
+ # Usually, you don't need to use this class directly.
+ #
+ # Notice: This class is unstable. APIs will be changed.
+ class TextDomain
+
+ attr_reader :output_charset
+ attr_reader :mofiles
+ attr_reader :name
+
+ @@cached = ! $DEBUG
+ # Cache the mo-file or not.
+ # Default is true. If $DEBUG is set then false.
+ def self.cached?
+ @@cached
+ end
+
+ # Set to cache the mo-file or not.
+ # * val: true if cached, otherwise false.
+ def self.cached=(val)
+ @@cached = val
+ end
+
+ # Add default locale path. Usually you should use GetText.add_default_locale_path instead.
+ # * path: a new locale path. (e.g.) "/usr/share/locale/%{lang}/LC_MESSAGES/%{name}.mo"
+ # ('locale' => "ja_JP", 'name' => "textdomain")
+ # * Returns: the new DEFAULT_LOCALE_PATHS
+ def self.add_default_locale_path(path)
+ warn "Deprecated. Use GetText::LocalePath.add_default_rule instead."
+ LocalePath.add_default_rule(path)
+ end
+
+ # Creates a new GetText::TextDomain.
+ # * name: the textdomain name.
+ # * topdir: the locale path ("%{topdir}/%{lang}/LC_MESSAGES/%{name}.mo") or nil.
+ # * output_charset: output charset.
+ # * Returns: a newly created GetText::TextDomain object.
+ def initialize(name, topdir = nil, output_charset = nil)
+ @name, @output_charset = name, output_charset
+
+ @locale_path = LocalePath.new(@name, topdir)
+ @mofiles = {}
+ end
+
+ # Translates the translated string.
+ # * lang: Locale::Tag::Simple's subclass.
+ # * msgid: the original message.
+ # * Returns: the translated string or nil.
+ def translate_singluar_message(lang, msgid)
+ return "" if msgid == "" or msgid.nil?
+
+ lang_key = lang.to_s
+
+ mofile = nil
+ if self.class.cached?
+ mofile = @mofiles[lang_key]
+ end
+ unless mofile
+ mofile = load_mo(lang)
+ end
+
+ if (! mofile) or (mofile ==:empty)
+ return nil
+ end
+
+ msgstr = mofile[msgid]
+ if msgstr and (msgstr.size > 0)
+ msgstr
+ elsif msgid.include?("\000")
+ # Check "aaa\000bbb" and show warning but return the singluar part.
+ ret = nil
+ msgid_single = msgid.split("\000")[0]
+ mofile.each{|key, val|
+ if key =~ /^#{Regexp.quote(msgid_single)}\000/
+ # Usually, this is not caused to make po-files from rgettext.
+ warn %Q[Warning: n_("#{msgid_single}", "#{msgid.split("\000")[1]}") and n_("#{key.gsub(/\000/, '", "')}") are duplicated.]
+ ret = val
+ break
+ end
+ }
+ ret
+ else
+ ret = nil
+ mofile.each{|key, val|
+ if key =~ /^#{Regexp.quote(msgid)}\000/
+ ret = val.split("\000")[0]
+ break
+ end
+ }
+ ret
+ end
+ end
+
+ DEFAULT_PLURAL_CALC = Proc.new{|n| n != 1}
+ DEFAULT_SINGLE_CALC = Proc.new{|n| 0}
+
+ # Translates the translated string.
+ # * lang: Locale::Tag::Simple's subclass.
+ # * msgid: the original message.
+ # * msgid_plural: the original message(plural).
+ # * Returns: the translated string as an Array ([[msgstr1, msgstr2, ...], cond]) or nil.
+ def translate_plural_message(lang, msgid, msgid_plural) #:nodoc:
+ key = msgid + "\000" + msgid_plural
+ msg = translate_singluar_message(lang, key)
+ ret = nil
+ if ! msg
+ ret = nil
+ elsif msg.include?("\000")
+ # [[msgstr[0], msgstr[1], msgstr[2],...], cond]
+ mofile = @mofiles[lang.to_posix.to_s]
+ cond = (mofile and mofile != :empty) ? mofile.plural_as_proc : DEFAULT_PLURAL_CALC
+ ret = [msg.split("\000"), cond]
+ else
+ ret = [[msg], DEFAULT_SINGLE_CALC]
+ end
+ ret
+ end
+
+ # Clear cached mofiles.
+ def clear
+ @mofiles = {}
+ end
+
+ # Set output_charset.
+ # * charset: output charset.
+ def output_charset=(charset)
+ @output_charset = charset
+ clear
+ end
+
+ private
+ # Load a mo-file from the file.
+ # lang is the subclass of Locale::Tag::Simple.
+ def load_mo(lang)
+ lang = lang.to_posix unless lang.kind_of? Locale::Tag::Posix
+ lang_key = lang.to_s
+
+ mofile = @mofiles[lang_key]
+ if mofile
+ if mofile == :empty
+ return :empty
+ elsif ! self.class.cached?
+ mofile.update!
+ end
+ return mofile
+ end
+
+ path = @locale_path.current_path(lang)
+
+ if path
+ charset = @output_charset || lang.charset || Locale.charset || "UTF-8"
+ @mofiles[lang_key] = MOFile.open(path, charset)
+ else
+ @mofiles[lang_key] = :empty
+ end
+ end
+ end
+end
diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_group.rb b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_group.rb
new file mode 100644
index 000000000..b2ffd2dd2
--- /dev/null
+++ b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_group.rb
@@ -0,0 +1,24 @@
+=begin
+ gettext/textdomain_group - GetText::TextDomainGroup class
+
+ Copyright (C) 2009 Masao Mutoh
+
+ You may redistribute it and/or modify it under the same
+ license terms as Ruby or LGPL.
+
+=end
+
+module GetText
+
+ class TextDomainGroup
+ attr_reader :textdomains
+
+ def initialize
+ @textdomains = []
+ end
+
+ def add(textdomain)
+ @textdomains.unshift(textdomain) unless @textdomains.include? textdomain
+ end
+ end
+end
diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_manager.rb b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_manager.rb
new file mode 100644
index 000000000..09d879521
--- /dev/null
+++ b/vendor/gems/gettext-2.1.0/lib/gettext/runtime/textdomain_manager.rb
@@ -0,0 +1,211 @@
+=begin
+ gettext/textdomain_manager - GetText::TextDomainManager class
+
+ Copyright (C) 2009 Masao Mutoh
+
+ You may redistribute it and/or modify it under the same
+ license terms as Ruby or LGPL.
+
+=end
+
+require 'gettext/runtime/class_info'
+require 'gettext/runtime/textdomain'
+require 'gettext/runtime/textdomain_group'
+
+module GetText
+
+ module TextDomainManager
+
+ @@textdomain_pool = {}
+ @@textdomain_group_pool = {}
+
+ @@output_charset = nil
+ @@gettext_classes = []
+
+ @@singular_message_cache = {}
+ @@plural_message_cache = {}
+ @@cached = ! $DEBUG
+
+ extend self
+
+ # Find textdomain by name
+ def textdomain_pool(domainname)
+ @@textdomain_pool[domainname]
+ end
+
+ # Set the value whether cache messages or not.
+ # true to cache messages, otherwise false.
+ #
+ # Default is true. If $DEBUG is false, messages are not checked even if
+ # this value is true.
+ def cached=(val)
+ @@cached = val
+ TextDomain.cached = val
+ end
+
+ # Return the cached value.
+ def cached?
+ TextDomain.cached?
+ end
+
+ # Gets the output charset.
+ def output_charset
+ @@output_charset
+ end
+
+ # Sets the output charset.The program can have a output charset.
+ def output_charset=(charset)
+ @@output_charset = charset
+ @@textdomain_pool.each do |key, textdomain|
+ textdomain.output_charset = charset
+ end
+ end
+
+ # bind textdomain to the class.
+ def bind_to(klass, domainname, options = {})
+ warn "Bind the domain '#{domainname}' to '#{klass}'. " if $DEBUG
+
+ charset = options[:output_charset] || self.output_charset
+ textdomain = create_or_find_textdomain(domainname,options[:path],charset)
+ target_klass = ClassInfo.normalize_class(klass)
+ create_or_find_textdomain_group(target_klass).add(textdomain)
+ @@gettext_classes << target_klass unless @@gettext_classes.include? target_klass
+
+ textdomain
+ end
+
+ def each_textdomains(klass) #:nodoc:
+ lang = Locale.candidates[0]
+ ClassInfo.related_classes(klass, @@gettext_classes).each do |target|
+ msg = nil
+ if group = @@textdomain_group_pool[target]
+ group.textdomains.each do |textdomain|
+ yield textdomain, lang
+ end
+ end
+ end
+ end
+
+ # Translates msgid, but if there are no localized text,
+ # it returns a last part of msgid separeted "div" or whole of the msgid with no "div".
+ #
+ # * msgid: the message id.
+ # * div: separator or nil.
+ # * Returns: the localized text by msgid. If there are no localized text,
+ # it returns a last part of msgid separeted "div".
+ def translate_singluar_message(klass, msgid, div = nil)
+ klass = ClassInfo.normalize_class(klass)
+ key = [Locale.current, klass, msgid, div].hash
+ msg = @@singular_message_cache[key]
+ return msg if msg and @@cached
+ # Find messages from related classes.
+ each_textdomains(klass) do |textdomain, lang|
+ msg = textdomain.translate_singluar_message(lang, msgid)
+ break if msg
+ end
+
+ # If not found, return msgid.
+ msg ||= msgid
+ if div and msg == msgid
+ if index = msg.rindex(div)
+ msg = msg[(index + 1)..-1]
+ end
+ end
+ @@singular_message_cache[key] = msg
+ end
+
+ # This function is similar to the get_singluar_message function
+ # as it finds the message catalogs in the same way.
+ # But it takes two extra arguments for plural form.
+ # The msgid parameter must contain the singular form of the string to be converted.
+ # It is also used as the key for the search in the catalog.
+ # The msgid_plural parameter is the plural form.
+ # The parameter n is used to determine the plural form.
+ # If no message catalog is found msgid1 is returned if n == 1, otherwise msgid2.
+ # And if msgid includes "div", it returns a last part of msgid separeted "div".
+ #
+ # * msgid: the singular form with "div". (e.g. "Special|An apple", "An apple")
+ # * msgid_plural: the plural form. (e.g. "%{num} Apples")
+ # * n: a number used to determine the plural form.
+ # * div: the separator. Default is "|".
+ # * Returns: the localized text which key is msgid_plural if n is plural(follow plural-rule) or msgid.
+ # "plural-rule" is defined in po-file.
+ #
+ # or
+ #
+ # * [msgid, msgid_plural] : msgid and msgid_plural an Array
+ # * n: a number used to determine the plural form.
+ # * div: the separator. Default is "|".
+ def translate_plural_message(klass, arg1, arg2, arg3 = "|", arg4 = "|")
+ klass = ClassInfo.normalize_class(klass)
+ # parse arguments
+ if arg1.kind_of?(Array)
+ msgid = arg1[0]
+ msgid_plural = arg1[1]
+ n = arg2
+ if arg3 and arg3.kind_of? Numeric
+ raise ArgumentError, _("3rd parmeter is wrong: value = %{number}") % {:number => arg3}
+ end
+ div = arg3
+ else
+ msgid = arg1
+ msgid_plural = arg2
+ n = arg3
+ div = arg4
+ end
+
+ key = [Locale.current, klass, msgid, msgid_plural, div].hash
+ msgs = @@plural_message_cache[key]
+ unless (msgs and @@cached)
+ # Find messages from related classes.
+ msgs = nil
+ each_textdomains(klass) do |textdomain, lang|
+ msgs = textdomain.translate_plural_message(lang, msgid, msgid_plural)
+ break if msgs
+ end
+
+ msgs = [[msgid, msgid_plural], TextDomain::DEFAULT_PLURAL_CALC] unless msgs
+
+ msgstrs = msgs[0]
+ if div and msgstrs[0] == msgid and index = msgstrs[0].rindex(div)
+ msgstrs[0] = msgstrs[0][(index + 1)..-1]
+ end
+ @@plural_message_cache[key] = msgs
+ end
+
+ # Return the singular or plural message.
+ msgstrs = msgs[0]
+ plural = msgs[1].call(n)
+ return msgstrs[plural] if plural.kind_of?(Numeric)
+ return plural ? msgstrs[1] : msgstrs[0]
+ end
+
+ # for testing.
+ def clear_all_textdomains
+ @@textdomain_pool = {}
+ @@textdomain_group_pool = {}
+ @@gettext_classes = []
+ clear_caches
+ end
+
+ # for testing.
+ def clear_caches
+ @@singular_message_cache = {}
+ @@plural_message_cache = {}
+ end
+
+ def create_or_find_textdomain_group(klass) #:nodoc:
+ group = @@textdomain_group_pool[klass]
+ return group if group
+
+ @@textdomain_group_pool[klass] = TextDomainGroup.new
+ end
+
+ def create_or_find_textdomain(name, path, charset)#:nodoc:
+ textdomain = @@textdomain_pool[name]
+ return textdomain if textdomain
+
+ @@textdomain_pool[name] = TextDomain.new(name, path, charset)
+ end
+ end
+end