diff options
Diffstat (limited to 'vendor/gems/gettext-2.1.0/lib/gettext/runtime')
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 |