diff options
author | Robin Houston <robin@lenny.robin> | 2011-06-09 12:54:05 +0100 |
---|---|---|
committer | Robin Houston <robin@lenny.robin> | 2011-06-09 12:54:05 +0100 |
commit | 4d077dc48fb0589dbf401a131d524b23ab0d2258 (patch) | |
tree | 680b12febc448827ee937039d38e97020b4c5df9 /vendor/gems/gettext-2.1.0/lib | |
parent | cb67f4d0ef9feae96a90ea5bba63c939268b1224 (diff) |
Bundle the locale and gettext gems
Diffstat (limited to 'vendor/gems/gettext-2.1.0/lib')
24 files changed, 3631 insertions, 0 deletions
diff --git a/vendor/gems/gettext-2.1.0/lib/gettext.rb b/vendor/gems/gettext-2.1.0/lib/gettext.rb new file mode 100644 index 000000000..30a0e83b8 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext.rb @@ -0,0 +1,310 @@ +=begin + gettext.rb - GetText module + + Copyright (C) 2001-2009 Masao Mutoh + Copyright (C) 2001-2003 Masahiro Sakai + + Masao Mutoh <mutomasa at gmail.com> + Masahiro Sakai <s01397ms@sfc.keio.ac.jp> + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +if Object.constants.include? "Gem" + begin + begin + gem 'locale', '>=2.0.4' + rescue Gem::LoadError + end + rescue NoMethodError + else LoadError + end +end + +require 'locale' +raise "Install locale as gem or uninstall old gettext" unless Locale.respond_to? :candidates + +require 'gettext/version' +require 'gettext/runtime/textdomain_manager' + +module GetText + # If the textdomain isn't bound when calling GetText.textdomain, this error is raised. + class NoboundTextDomainError < RuntimeError + def initialize(domainname) + @domainname = domainname + end + def message + "#{@domainname} is not bound." + end + end + + extend self + + def self.included(mod) #:nodoc: + mod.extend self + end + + # bindtextdomain(domainname, options = {}) + # + # Bind a textdomain(%{path}/%{locale}/LC_MESSAGES/%{domainname}.mo) to + # your program. + # Normally, the texdomain scope becomes the class/module(and parent + # classes/included modules). + # + # * domainname: the textdomain name. + # * options: options as an Hash. + # * :path - the path to the mo-files. When the value is nil, it will search default paths such as + # /usr/share/locale, /usr/local/share/locale) + # * :output_charset - The output charset. Same with GetText.set_output_charset. Usually, L10n + # library doesn't use this option. Application may use this once. + # * Returns: the GetText::TextDomainManager. + # + def bindtextdomain(domainname, *options) + bindtextdomain_to(self, domainname, *options) + end + + # Includes GetText module and bind a textdomain to a class. + # * klass: the target ruby class. + # * domainname: the textdomain name. + # * options: options as an Hash. See GetText.bindtextdomain. + def bindtextdomain_to(klass, domainname, *options) + if options[0].kind_of? Hash + opts = options[0] + else + # for backward compatibility. + opts = {} + opts[:path] = options[0] if options[0] + opts[:output_charset] = options[2] if options[2] + end + unless (klass.kind_of? GetText or klass.include? GetText) + klass.__send__(:include, GetText) + end + TextDomainManager.bind_to(klass, domainname, opts) + end + + # Binds a existed textdomain to your program. + # This is the same function with GetText.bindtextdomain but simpler(and faster) than bindtextdomain. + # Note that you need to call GetText.bindtextdomain first. If the domainname hasn't bound yet, + # raises GetText::NoboundTextDomainError. + # * domainname: a textdomain name. + # * Returns: the GetText::TextDomainManager. + def textdomain(domainname) #:nodoc: + textdomain_to(self, domainname) + end + + # Includes GetText module and bind an exsited textdomain to a class. + # See textdomain for more detail. + # * klass: the target ruby class. + # * domainname: the textdomain name. + + def textdomain_to(klass, domainname) #:nodoc: + domain = TextDomainManager.textdomain_pool(domainname) + raise NoboundTextDomainError.new(domainname) unless domain + bindtextdomain_to(klass, domainname) + end + + # call-seq: + # gettext(msgid) + # _(msgid) + # + # Translates msgid and return the message. + # This doesn't make a copy of the message. + # + # You need to use String#dup if you want to modify the return value + # with destructive functions. + # + # (e.g.1) _("Hello ").dup << "world" + # + # But e.g.1 should be rewrite to: + # + # (e.g.2) _("Hello %{val}") % {:val => "world"} + # + # Because the translator may want to change the position of "world". + # + # * msgid: the message id. + # * Returns: localized text by msgid. If there are not binded mo-file, it will return msgid. + def gettext(msgid) + TextDomainManager.translate_singluar_message(self, msgid) + end + + # call-seq: + # sgettext(msgid, div = '|') + # s_(msgid, div = '|') + # + # Translates msgid, but if there are no localized text, + # it returns a last part of msgid separeted "div". + # + # * msgid: the message id. + # * separator: separator or nil for no seperation. + # * Returns: the localized text by msgid. If there are no localized text, + # it returns a last part of the msgid separeted by "seperator". + # <tt>Movie|Location -> Location</tt> + # See: http://www.gnu.org/software/gettext/manual/html_mono/gettext.html#SEC151 + def sgettext(msgid, seperator = "|") + TextDomainManager.translate_singluar_message(self, msgid, seperator) + end + + # call-seq: + # pgettext(msgctxt, msgid) + # p_(msgctxt, msgid) + # + # Translates msgid with msgctxt. This methods is similer with s_(). + # e.g.) p_("File", "New") == s_("File|New") + # p_("File", "Open") == s_("File|Open") + # + # * msgctxt: the message context. + # * msgid: the message id. + # * Returns: the localized text by msgid. If there are no localized text, + # it returns msgid. + # See: http://www.gnu.org/software/autoconf/manual/gettext/Contexts.html + def pgettext(msgctxt, msgid) + TextDomainManager.translate_singluar_message(self, "#{msgctxt}\004#{msgid}", "\004") + end + + # call-seq: + # ngettext(msgid, msgid_plural, n) + # ngettext(msgids, n) # msgids = [msgid, msgid_plural] + # n_(msgid, msgid_plural, n) + # n_(msgids, n) # msgids = [msgid, msgid_plural] + # + # The ngettext is similar to the gettext function as it finds the message catalogs in the same way. + # But it takes two extra arguments for plural form. + # + # * msgid: the singular form. + # * msgid_plural: the plural form. + # * n: a number used to determine the plural form. + # * 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. + def ngettext(msgid, msgid_plural, n = nil) + TextDomainManager.translate_plural_message(self, msgid, msgid_plural, n) + end + + # call-seq: + # nsgettext(msgid, msgid_plural, n, div = "|") + # nsgettext(msgids, n, div = "|") # msgids = [msgid, msgid_plural] + # ns_(msgid, msgid_plural, n, div = "|") + # ns_(msgids, n, div = "|") # msgids = [msgid, msgid_plural] + # + # The nsgettext is similar to the ngettext. + # But if there are no localized text, + # it returns a last part of msgid separeted "div". + # + # * msgid: the singular form with "div". (e.g. "Special|An apple") + # * msgid_plural: the plural form. (e.g. "%{num} Apples") + # * n: a number used to determine the plural form. + # * 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. + def nsgettext(msgid, msgid_plural, n="|", seperator = "|") + TextDomainManager.translate_plural_message(self, msgid, msgid_plural, n, seperator) + end + + # call-seq: + # npgettext(msgctxt, msgid, msgid_plural, n) + # npgettext(msgctxt, msgids, n) # msgids = [msgid, msgid_plural] + # np_(msgctxt, msgid, msgid_plural, n) + # np_(msgctxt, msgids, n) # msgids = [msgid, msgid_plural] + # + # The npgettext is similar to the nsgettext function. + # e.g.) np_("Special", "An apple", "%{num} Apples", num) == ns_("Special|An apple", "%{num} Apples", num) + # * msgctxt: the message context. + # * msgid: the singular form. + # * msgid_plural: the plural form. + # * n: a number used to determine the plural form. + # * 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. + def npgettext(msgctxt, msgids, arg2 = nil, arg3 = nil) + if msgids.kind_of?(Array) + msgid = msgids[0] + msgid_ctxt = "#{msgctxt}\004#{msgid}" + msgid_plural = msgids[1] + opt1 = arg2 + opt2 = arg3 + else + msgid = msgids + msgid_ctxt = "#{msgctxt}\004#{msgid}" + msgid_plural = arg2 + opt1 = arg3 + opt2 = nil + end + + msgstr = TextDomainManager.translate_plural_message(self, msgid_ctxt, msgid_plural, opt1, opt2) + if msgstr == msgid_ctxt + msgid + else + msgstr + end + end + + # makes dynamic translation messages readable for the gettext parser. + # <tt>_(fruit)</tt> cannot be understood by the gettext parser. To help the parser find all your translations, + # you can add <tt>fruit = N_("Apple")</tt> which does not translate, but tells the parser: "Apple" needs translation. + # * msgid: the message id. + # * Returns: msgid. + def N_(msgid) + msgid + end + + # This is same function as N_ but for ngettext. + # * msgid: the message id. + # * msgid_plural: the plural message id. + # * Returns: msgid. + def Nn_(msgid, msgid_plural) + [msgid, msgid_plural] + end + + # Sets charset(String) such as "euc-jp", "sjis", "CP932", "utf-8", ... + # You shouldn't use this in your own Libraries. + # * charset: an output_charset + # * Returns: self + def set_output_charset(charset) + TextDomainManager.output_charset = charset + self + end + + # Gets the current output_charset which is set using GetText.set_output_charset. + # * Returns: output_charset. + def output_charset + TextDomainManager.output_charset + end + + # Set the locale. This value forces the locale whole the programs. + # This method calls Locale.set_app_language_tags, Locale.default, Locale.current. + # Use Locale methods if you need to handle locales more flexible. + def set_locale(lang) + Locale.set_app_language_tags(lang) + Locale.default = lang + Locale.current = lang + end + + # Set the locale to the current thread. + # Note that if #set_locale is set, this value is ignored. + # If you need, set_locale(nil); set_current_locale(lang) + def set_current_locale(lang) + Locale.current = lang + end + + def locale + Locale.current[0] + end + + alias :locale= :set_locale #:nodoc: + alias :current_locale= :set_current_locale #:nodoc: + alias :_ :gettext #:nodoc: + alias :n_ :ngettext #:nodoc: + alias :s_ :sgettext #:nodoc: + alias :ns_ :nsgettext #:nodoc: + alias :np_ :npgettext #:nodoc: + + alias :output_charset= :set_output_charset #:nodoc: + +unless defined? XX + # This is the workaround to conflict p_ methods with the xx("double x") library. + # http://rubyforge.org/projects/codeforpeople/ + alias :p_ :pgettext #:nodoc: +end + + # for backward compatibility + alias :set_locale_all :set_locale #:nodoc: + alias :setlocale :set_locale #:nodoc: +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/cgi.rb b/vendor/gems/gettext-2.1.0/lib/gettext/cgi.rb new file mode 100644 index 000000000..78868fc10 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/cgi.rb @@ -0,0 +1,37 @@ +=begin + gettext/cgi.rb - GetText for CGI + + Copyright (C) 2005-2009 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +require 'cgi' +require 'gettext' + +Locale.init(:driver => :cgi) + +module GetText + + # Sets a CGI object. This methods is appeared when requiring "gettext/cgi". + # * cgi_: CGI object + # * Returns: self + def set_cgi(cgi_) + Locale.set_cgi(cgi_) + end + + # Same as GetText.set_cgi. This methods is appeared when requiring "gettext/cgi". + # * cgi_: CGI object + # * Returns: cgi_ + def cgi=(cgi_) + set_cgi(cgi_) + cgi_ + end + + # Gets the CGI object. If it is nil, returns new CGI object. This methods is appeared when requiring "gettext/cgi". + # * Returns: the CGI object + def cgi + Locale.cgi + end +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/core_ext/iconv.rb b/vendor/gems/gettext-2.1.0/lib/gettext/core_ext/iconv.rb new file mode 100644 index 000000000..bc3aab0eb --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/core_ext/iconv.rb @@ -0,0 +1,110 @@ +# encoding: utf-8 +=begin + iconv.rb - Pseudo Iconv class. Supports Iconv.iconv, Iconv.conv. + + For Matz Ruby: + If you don't have iconv but glib2, this library uses glib2 iconv functions. + + For JRuby: + Use Java String class to convert strings. + + Copyright (C) 2004-2009 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +begin + require 'iconv.so' +rescue LoadError + # Pseudo Iconv class + # + # ==== For Matz Ruby: + # If you don't have iconv but Ruby/GLib2, this library uses Ruby/GLib2's + # iconv functions. + # + # Ruby/GLib is a module which is provided from Ruby-GNOME2 Project. + # You can get binaries for Win32(One-Click Ruby Installer). + # <URL: http://ruby-gnome2.sourceforge.jp/> + # ==== For JRuby: + # Use Java String class to convert strings. + class Iconv + module Failure; end + class InvalidEncoding < ArgumentError; include Failure; end + class IllegalSequence < ArgumentError; include Failure; end + class InvalidCharacter < ArgumentError; include Failure; end + + if RUBY_PLATFORM =~ /java/ + def self.conv(to, from, str) + raise InvalidCharacter, "the 3rd argument is nil" unless str + begin + str = java.lang.String.new(str.unpack("C*").to_java(:byte), from) + str.getBytes(to).to_ary.pack("C*") + rescue java.io.UnsupportedEncodingException + raise InvalidEncoding + end + end + else + begin + require 'glib2' + + def self.check_glib_version?(major, minor, micro) # :nodoc: + (GLib::BINDING_VERSION[0] > major || + (GLib::BINDING_VERSION[0] == major && + GLib::BINDING_VERSION[1] > minor) || + (GLib::BINDING_VERSION[0] == major && + GLib::BINDING_VERSION[1] == minor && + GLib::BINDING_VERSION[2] >= micro)) + end + + if check_glib_version?(0, 11, 0) + # This is a function equivalent of Iconv.iconv. + # * to: encoding name for destination + # * from: encoding name for source + # * str: strings to be converted + # * Returns: Returns an Array of converted strings. + def self.conv(to, from, str) + begin + GLib.convert(str, to, from) + rescue GLib::ConvertError => e + case e.code + when GLib::ConvertError::NO_CONVERSION + raise InvalidEncoding.new(str) + when GLib::ConvertError::ILLEGAL_SEQUENCE + raise IllegalSequence.new(str) + else + raise InvalidCharacter.new(str) + end + end + end + else + def self.conv(to, from, str) # :nodoc: + begin + GLib.convert(str, to, from) + rescue + raise IllegalSequence.new(str) + end + end + end + rescue LoadError + def self.conv(to, from, str) # :nodoc: + warn "Iconv was not found." if $DEBUG + str + end + end + end + def self.iconv(to, from, str) + conv(to, from, str).split(//) + end + end +end + +if __FILE__ == $0 + puts Iconv.iconv("EUC-JP", "UTF-8", "ほげ").join + begin + puts Iconv.iconv("EUC-JP", "EUC-JP", "ほげ").join + rescue Iconv::Failure + puts $! + puts $!.class + end +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/core_ext/string.rb b/vendor/gems/gettext-2.1.0/lib/gettext/core_ext/string.rb new file mode 100644 index 000000000..1cbd91567 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/core_ext/string.rb @@ -0,0 +1,84 @@ +=begin + string.rb - Extension for String. + + Copyright (C) 2005-2009 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +# Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError. +# +# String#% method which accept "named argument". The translator can know +# the meaning of the msgids using "named argument" instead of %s/%d style. +class String + + unless instance_methods.find {|m| m.to_s == 'bytesize'} + # For older ruby (such as ruby-1.8.5) + alias :bytesize :size + end + + alias :_old_format_m :% # :nodoc: + + PERCENT_MATCH_RE = Regexp.union( + /%%/, + /%\{(.+?)\}/, + /%<(.+?)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ + ) + + # call-seq: + # %(arg) + # %(hash) + # + # Format - Uses str as a format specification, and returns the result of applying it to arg. + # If the format specification contains more than one substitution, then arg must be + # an Array containing the values to be substituted. See Kernel::sprintf for details of the + # format string. This is the default behavior of the String class. + # * arg: an Array or other class except Hash. + # * Returns: formatted String + # + # (e.g.) "%s, %s" % ["Masao", "Mutoh"] + # + # Also you can use a Hash as the "named argument". This is recommanded way for Ruby-GetText + # because the translators can understand the meanings of the msgids easily. + # * hash: {:key1 => value1, :key2 => value2, ... } + # * Returns: formatted String + # + # (e.g.) + # For strings. + # "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"} + # + # With field type to specify format such as d(decimal), f(float),... + # "%<age>d, %<weight>.1f" % {:age => 10, :weight => 43.4} + def %(args) + if args.kind_of?(Hash) + ret = dup + ret.gsub!(PERCENT_MATCH_RE) {|match| + if match == '%%' + '%' + elsif $1 + key = $1.to_sym + args.has_key?(key) ? args[key] : match + elsif $2 + key = $2.to_sym + args.has_key?(key) ? sprintf("%#{$3}", args[key]) : match + end + } + ret + else + ret = gsub(/%([{<])/, '%%\1') + begin + ret._old_format_m(args) + rescue ArgumentError => e + if $DEBUG + $stderr.puts " The string:#{ret}" + $stderr.puts " args:#{args.inspect}" + puts e.backtrace + else + raise ArgumentError, e.message + end + end + end + end +end + diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/parser/erb.rb b/vendor/gems/gettext-2.1.0/lib/gettext/parser/erb.rb new file mode 100644 index 000000000..eea0ea828 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/parser/erb.rb @@ -0,0 +1,3 @@ +warn "DEPRECATED: Use 'gettext/tools/parser/erb' instead." + +require 'gettext/tools/parser/erb' diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/parser/glade.rb b/vendor/gems/gettext-2.1.0/lib/gettext/parser/glade.rb new file mode 100644 index 000000000..7b61fedad --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/parser/glade.rb @@ -0,0 +1,3 @@ +warn "DEPRECATED: Use 'gettext/tools/parser/glade' instead." + +require 'gettext/tools/parser/glade' diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/parser/ruby.rb b/vendor/gems/gettext-2.1.0/lib/gettext/parser/ruby.rb new file mode 100644 index 000000000..cf8fae47a --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/parser/ruby.rb @@ -0,0 +1,172 @@ +#!/usr/bin/ruby +=begin + parser/ruby.rb - parser for ruby script + + Copyright (C) 2003-2005 Masao Mutoh + Copyright (C) 2005 speakillof + Copyright (C) 2001,2002 Yasushi Shoji, Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby. + +=end + +require 'irb/ruby-lex.rb' +require 'stringio' +warn "DEPRECATED: Use 'gettext/tools/parser/ruby' instead." + +class RubyLexX < RubyLex # :nodoc: all + # Parser#parse resemlbes RubyLex#lex + def parse + until ( (tk = token).kind_of?(RubyToken::TkEND_OF_SCRIPT) && !@continue or tk.nil? ) + s = get_readed + if RubyToken::TkSTRING === tk + def tk.value + @value + end + + def tk.value=(s) + @value = s + end + + if @here_header + s = s.sub(/\A.*?\n/, '').sub(/^.*\n\Z/, '') + else + begin + s = eval(s) + rescue Exception + # Do nothing. + end + end + + tk.value = s + end + + if $DEBUG + if tk.is_a? TkSTRING + $stderr.puts("#{tk}: #{tk.value}") + elsif tk.is_a? TkIDENTIFIER + $stderr.puts("#{tk}: #{tk.name}") + else + $stderr.puts(tk) + end + end + + yield tk + end + return nil + end + +end + +module GetText + module RubyParser + extend self + + unless defined? ID + ID = ['gettext', '_', 'N_', 'sgettext', 's_'] + PLURAL_ID = ['ngettext', 'n_', 'Nn_', 'ns_', 'nsgettext'] + MSGCTXT_ID = ['pgettext', 'p_'] + MSGCTXT_PLURAL_ID = ['npgettext', 'np_'] + end + + def parse(file, targets = []) # :nodoc: + lines = IO.readlines(file) + parse_lines(file, lines, targets) + end + + def parse_lines(file_name, lines, targets) # :nodoc: + file = StringIO.new(lines.join + "\n") + rl = RubyLexX.new + rl.set_input(file) + rl.skip_space = true + #rl.readed_auto_clean_up = true + + target = nil + msgid = nil + line_no = nil + rl.parse do |tk| + begin + case tk + when RubyToken::TkIDENTIFIER, RubyToken::TkCONSTANT + if ID.include?(tk.name) + target = :normal + elsif PLURAL_ID.include?(tk.name) + target = :plural + elsif MSGCTXT_ID.include?(tk.name) + target = :msgctxt + elsif MSGCTXT_PLURAL_ID.include?(tk.name) + target = :msgctxt_plural + else + target = nil + end + line_no = tk.line_no.to_s + when RubyToken::TkSTRING + if target + if msgid + msgid += tk.value + else + msgid = tk.value + end + end + when RubyToken::TkPLUS, RubyToken::TkNL + #do nothing + when RubyToken::TkCOMMA + if msgid + case target + when :plural + msgid += "\000" + target = :normal + when :msgctxt + msgid += "\004" + target = :normal + when :msgctxt_plural + msgid += "\004" + target = :plural + else + target = :normal + end + end + else + if msgid + key_existed = targets.assoc(msgid.gsub(/\n/, '\n')) + if key_existed + targets[targets.index(key_existed)] = key_existed << + file_name + ":" + line_no + else + targets << [msgid.gsub(/\n/, '\n'), file_name + ":" + line_no] + end + msgid = nil + target = nil + end + end + targets + rescue + $stderr.print "\n\nError: #{$!.inspect} " + $stderr.print " in #{file_name}:#{tk.line_no}\n\t #{lines[tk.line_no - 1]}" if tk + $stderr.print "\n" + exit 1 + end + end + targets + end + + def target?(file) # :nodoc: + true # always true, as default parser. + end + end +end + + + +if __FILE__ == $0 + require 'pp' + ARGV.each do |file| + pp GetText::RubyParser.parse(file) + end + + #rl = RubyLexX.new; rl.set_input(ARGF) + #rl.parse do |tk| + #p tk + #end +end 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 diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools.rb new file mode 100644 index 000000000..d0ccd8dd9 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools.rb @@ -0,0 +1,195 @@ +=begin + tools.rb - Utility functions + + Copyright (C) 2005-2008 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +require 'rbconfig' +if /mingw|mswin|mswin32/ =~ RUBY_PLATFORM + ENV['PATH'] = %w(bin lib).collect{|dir| + "#{Config::CONFIG["prefix"]}\\lib\\GTK\\#{dir};" + }.join('') + ENV['PATH'] +end + +require 'gettext/tools/rgettext' +require 'gettext/tools/rmsgfmt' +require 'gettext/runtime/mofile' +require 'fileutils' + +module GetText + bindtextdomain "rgettext" + + BOM_UTF8 = [0xef, 0xbb, 0xbf].pack("c3") + + # Currently, GNU msgmerge doesn't accept BOM. + # This mesthod remove the UTF-8 BOM from the po-file. + def remove_bom(path) #:nodoc: + bom = IO.read(path, 3) + if bom == BOM_UTF8 + data = IO.read(path)[3..-1] + File.open(path, "w") {|f| f.write(data)} + end + end + + # Merges two Uniforum style .po files together. + # + # *Note* This function requires "msgmerge" tool included in GNU GetText. So you need to install GNU GetText. + # + # The def.po file is an existing PO file with translations which will be taken + # over to the newly created file as long as they still match; comments will be preserved, + # but extracted comments and file positions will be discarded. + # + # The ref.pot file is the last created PO file with up-to-date source references but + # old translations, or a PO Template file (generally created by rgettext); + # any translations or comments in the file will be discarded, however dot + # comments and file positions will be preserved. Where an exact match + # cannot be found, fuzzy matching is used to produce better results. + # + # Usually you don't need to call this function directly. Use GetText.update_pofiles instead. + # + # * defpo: a po-file. translations referring to old sources + # * refpo: a po-file. references to new sources + # * app_version: the application information which appears "Project-Id-Version: #{app_version}" in the pot/po-files. + # * Returns: self + def msgmerge(defpo, refpo, app_version, options={}) + verbose = options.delete(:verbose) + puts "msgmerge called" if verbose + $stderr.print defpo + " " + + content = merge_po_files(defpo,refpo,options.delete(:msgmerge),verbose) + + if content.empty? + # report failure + failed_filename = refpo + "~" + FileUtils.cp(refpo, failed_filename) + $stderr.puts _("Failed to merge with %{defpo}") % {:defpo => defpo} + $stderr.puts _("New .pot was copied to %{failed_filename}") %{:failed_filename => failed_filename} + raise _("Check these po/pot-files. It may have syntax errors or something wrong.") + else + # update version and save merged data + content.sub!(/(Project-Id-Version\:).*$/, "\\1 #{app_version}\\n\"") + File.open(defpo, "w") {|f|f.write(content)} + end + + self + end + + # Creates mo-files using #{po_root}/#{lang}/*.po an put them to + # #{targetdir}/#{targetdir_rule}/. + # + # This is a convenience function of GetText.rmsgfmt for multiple target files. + # * options: options as a Hash. + # * verbose: true if verbose mode, otherwise false + # * po_root: the root directory of po-files. + # * mo_root: the target root directory where the mo-files are stored. + # * mo_path_rule: the target directory for each mo-files. + def create_mofiles(options = {}) + options = {:po_root => "./po"}.merge(options) + + Dir.glob(File.join(options[:po_root], "*/*.po")) do |po_file| + mo_file = mo_file_from_po_file(po_file,options) + $stderr.print %Q[#{po_file} -> #{mo_file} ... ] if options[:verbose] + FileUtils.mkdir_p(File.dirname(mo_file)) + rmsgfmt(po_file, mo_file) + $stderr.puts "Done." if options[:verbose] + end + end + + + # At first, this creates the #{po_root}/#{domainname}.pot file using GetText.rgettext. + # In the second step, this updates(merges) the #{po_root}/#{domainname}.pot and all of the + # #{po_root}/#{lang}/#{domainname}.po files under "po_root" using "msgmerge". + # + # *Note* "msgmerge" tool is included in GNU GetText. So you need to install GNU GetText. + # + # See <HOWTO maintain po/mo files(http://www.yotabanana.com/hiki/ruby-gettext-howto-manage.html)> for more detals. + # * domainname: the textdomain name. + # * targetfiles: An Array of target files, that should be parsed for messages (See GetText.rgettext for more details). + # * app_version: the application information which appears "Project-Id-Version: #{app_version}" in the pot/po-files. + # * options: a hash with following possible settings + # :lang - update files only for one language - the language specified by this option + # :po_root - the root directory of po-files + # :msgmerge - an array with the options, passed through to the gnu msgmerge tool + # symbols are automatically translated to options with dashes, + # example: [:no_wrap, :no_fuzzy_matching, :sort_output] translated to '--no-fuzzy-matching --sort-output' + # :verbose - true to show verbose messages. default is false. + # + # Example: GetText.update_pofiles("myapp", Dir.glob("lib/*.rb"), "myapp 1.0.0", :verbose => true) + def update_pofiles(textdomain, files, app_version, options = {}) + puts options.inspect if options[:verbose] + + #write found messages to tmp.pot + temp_pot = "tmp.pot" + rgettext(files, temp_pot) + + #merge tmp.pot and existing pot + po_root = options.delete(:po_root) || "po" + FileUtils.mkdir_p(po_root) + msgmerge("#{po_root}/#{textdomain}.pot", temp_pot, app_version, options.dup) + + #update local po-files + only_one_language = options.delete(:lang) + if only_one_language + msgmerge("#{po_root}/#{only_one_language}/#{textdomain}.po", temp_pot, app_version, options.dup) + else + Dir.glob("#{po_root}/*/#{textdomain}.po") do |po_file| + msgmerge(po_file, temp_pot, app_version, options.dup) + end + end + + File.delete(temp_pot) + end + + private + + # Merge 2 po files, using msgmerge + def merge_po_files(po_a,po_b,msgmerge_options=[],verbose=false) + return File.read(po_b) unless FileTest.exist? po_a + + cmd = ENV["MSGMERGE_PATH"] || "msgmerge" + ensure_command_exists(cmd) + + remove_bom(po_a) + + cmd_params = array_to_cli_options(msgmerge_options) + to_run = "#{cmd} #{cmd_params} #{po_a} #{po_b}" + puts "\nrunning #{to_run}" if verbose + `#{to_run}` + end + + # convert an array of String/Symbol to cli options + def array_to_cli_options(array) + [*array].map do |o| + o.kind_of?(Symbol) ? "--#{o}".gsub('_','-') : o.to_s + end.join(' ') + end + + def ensure_command_exists(cmd) + `#{cmd} --help` + unless $? && $?.success? + raise _("`%{cmd}' can not be found. \nInstall GNU Gettext then set PATH or MSGMERGE_PATH correctly.") % {:cmd => cmd} + end + end + + # where lies the mo file for a given po_file + # generare directory unless it exists + def mo_file_from_po_file(po_file,options) + options = { + :mo_root => "./data/locale", + :mo_path_rule => "%{lang}/LC_MESSAGES" + }.merge(options) + + lang, textdomain = %r[/([^/]+?)/(.*)\.po].match(po_file[options[:po_root].size..-1]).to_a[1,2] + + mo_dir_rule = File.join(options[:mo_root], options[:mo_path_rule]) + mo_dir = mo_dir_rule % {:lang => lang} + File.join(mo_dir, "#{textdomain}.mo") + end +end + +if __FILE__ == $0 + GetText.update_pofiles("foo", ARGV, "foo 1.1.0") +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/erb.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/erb.rb new file mode 100644 index 000000000..0aee9d461 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/erb.rb @@ -0,0 +1,52 @@ +=begin + parser/erb.rb - parser for ERB + + Copyright (C) 2005-2009 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +require 'erb' +require 'gettext/tools/parser/ruby' + +module GetText + module ErbParser + extend self + + @config = { + :extnames => ['.rhtml', '.erb'] + } + + # Sets some preferences to parse ERB files. + # * config: a Hash of the config. It can takes some values below: + # * :extnames: An Array of target files extension. Default is [".rhtml"]. + def init(config) + config.each{|k, v| + @config[k] = v + } + end + + def parse(file, targets = []) # :nodoc: + src = ERB.new(IO.readlines(file).join).src + # Remove magic comment prepended by erb in Ruby 1.9. + src.sub!(/\A#.*?coding[:=].*?\n/, '') if src.respond_to?(:encode) + erb = src.split(/$/) + RubyParser.parse_lines(file, erb, targets) + end + + def target?(file) # :nodoc: + @config[:extnames].each do |v| + return true if File.extname(file) == v + end + false + end + end +end + +if __FILE__ == $0 + # ex) ruby glade.rhtml foo.rhtml bar.rhtml + ARGV.each do |file| + p GetText::ErbParser.parse(file) + end +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/glade.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/glade.rb new file mode 100644 index 000000000..ab77beede --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/glade.rb @@ -0,0 +1,98 @@ +=begin + parser/glade.rb - parser for Glade-2 + + Copyright (C) 2004,2005 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +require 'cgi' +require 'gettext' + +module GetText + module GladeParser + extend GetText + extend self + + bindtextdomain("rgettext") + + TARGET1 = /<property.*translatable="yes">(.*)/ + TARGET2 = /(.*)<\/property>/ + + def parse(file, targets = []) # :nodoc: + lines = IO.readlines(file) + parse_lines(file, lines, targets) + end + + #from ary of lines. + def parse_lines(file, lines, targets) # :nodoc: + cnt = 0 + target = false + line_no = 0 + val = nil + + loop do + line = lines.shift + break unless line + + cnt += 1 + if TARGET1 =~ line + line_no = cnt + val = $1 + "\n" + target = true + if TARGET2 =~ $1 + val = $1 + add_target(val, file, line_no, targets) + val = nil + target = false + end + elsif target + if TARGET2 =~ line + val << $1 + add_target(val, file, line_no, targets) + val = nil + target = false + else + val << line + end + end + end + targets + end + + XML_RE = /<\?xml/ + GLADE_RE = /glade-2.0.dtd/ + + def target?(file) # :nodoc: + data = IO.readlines(file) + if XML_RE =~ data[0] and GLADE_RE =~ data[1] + true + else + if File.extname(file) == '.glade' + raise _("`%{file}' is not glade-2.0 format.") % {:file => file} + end + false + end + end + + def add_target(val, file, line_no, targets) # :nodoc: + return unless val.size > 0 + assoc_data = targets.assoc(val) + val = CGI.unescapeHTML(val) + if assoc_data + targets[targets.index(assoc_data)] = assoc_data << "#{file}:#{line_no}" + else + targets << [val.gsub(/\n/, '\n'), "#{file}:#{line_no}"] + end + targets + end + end +end + +if __FILE__ == $0 + # ex) ruby glade.rb foo.glade bar.glade + ARGV.each do |file| + p GetText::GladeParser.parse(file) + end +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/ruby.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/ruby.rb new file mode 100644 index 000000000..9d16401ce --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/parser/ruby.rb @@ -0,0 +1,226 @@ +#!/usr/bin/ruby +=begin + parser/ruby.rb - parser for ruby script + + Copyright (C) 2003-2009 Masao Mutoh + Copyright (C) 2005 speakillof + Copyright (C) 2001,2002 Yasushi Shoji, Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. + +=end + +require 'irb/ruby-lex.rb' +require 'stringio' +require 'gettext/tools/pomessage' + +module GetText + class RubyLexX < RubyLex # :nodoc: all + # Parser#parse resemlbes RubyLex#lex + def parse + until ( (tk = token).kind_of?(RubyToken::TkEND_OF_SCRIPT) && !@continue or tk.nil? ) + s = get_readed + if RubyToken::TkSTRING === tk + def tk.value + @value + end + + def tk.value=(s) + @value = s + end + + if @here_header + s = s.sub(/\A.*?\n/, '').sub(/^.*\n\Z/, '') + else + begin + s = eval(s) + rescue Exception + # Do nothing. + end + end + + tk.value = s + end + + if $DEBUG + if tk.is_a? TkSTRING + $stderr.puts("#{tk}: #{tk.value}") + elsif tk.is_a? TkIDENTIFIER + $stderr.puts("#{tk}: #{tk.name}") + else + $stderr.puts(tk) + end + end + + yield tk + end + return nil + end + + # Original parser does not keep the content of the comments, + # so monkey patching this with new token type and extended + # identify_comment implementation + RubyToken.def_token :TkCOMMENT_WITH_CONTENT, TkVal + + def identify_comment + @ltype = "#" + get_readed # skip the hash sign itself + + while ch = getc + if ch == "\n" + @ltype = nil + ungetc + break + end + end + return Token(TkCOMMENT_WITH_CONTENT, get_readed) + end + + end + + # Extends PoMessage for RubyParser. + # Implements a sort of state machine to assist the parser. + module PoMessageForRubyParser + # Supports parsing by setting attributes by and by. + def set_current_attribute(str) + param = @param_type[@param_number] + raise ParseError, 'no more string parameters expected' unless param + set_value(param, str) + end + + def init_param + @param_number = 0 + self + end + + def advance_to_next_attribute + @param_number += 1 + end + end + class PoMessage + include PoMessageForRubyParser + alias :initialize_old :initialize + def initialize(type) + initialize_old(type) + init_param + end + end + + module RubyParser + extend self + + ID = ['gettext', '_', 'N_', 'sgettext', 's_'] + PLURAL_ID = ['ngettext', 'n_', 'Nn_', 'ns_', 'nsgettext'] + MSGCTXT_ID = ['pgettext', 'p_'] + MSGCTXT_PLURAL_ID = ['npgettext', 'np_'] + + # (Since 2.1.0) the 2nd parameter is deprecated + # (and ignored here). + # And You don't need to keep the pomessages as unique. + + def parse(path, deprecated = []) # :nodoc: + lines = IO.readlines(path) + parse_lines(path, lines, deprecated) + end + + def parse_lines(path, lines, deprecated = []) # :nodoc: + pomessages = deprecated + file = StringIO.new(lines.join + "\n") + rl = RubyLexX.new + rl.set_input(file) + rl.skip_space = true + #rl.readed_auto_clean_up = true + + pomessage = nil + line_no = nil + last_comment = '' + reset_comment = false + rl.parse do |tk| + begin + case tk + when RubyToken::TkIDENTIFIER, RubyToken::TkCONSTANT + store_pomessage(pomessages, pomessage, path, line_no, last_comment) + if ID.include?(tk.name) + pomessage = PoMessage.new(:normal) + elsif PLURAL_ID.include?(tk.name) + pomessage = PoMessage.new(:plural) + elsif MSGCTXT_ID.include?(tk.name) + pomessage = PoMessage.new(:msgctxt) + elsif MSGCTXT_PLURAL_ID.include?(tk.name) + pomessage = PoMessage.new(:msgctxt_plural) + else + pomessage = nil + end + line_no = tk.line_no.to_s + when RubyToken::TkSTRING + pomessage.set_current_attribute tk.value if pomessage + when RubyToken::TkPLUS, RubyToken::TkNL + #do nothing + when RubyToken::TkCOMMA + pomessage.advance_to_next_attribute if pomessage + else + if store_pomessage(pomessages, pomessage, path, line_no, last_comment) + pomessage = nil + end + end + rescue + $stderr.print "\n\nError" + $stderr.print " parsing #{path}:#{tk.line_no}\n\t #{lines[tk.line_no - 1]}" if tk + $stderr.print "\n #{$!.inspect} in\n" + $stderr.print $!.backtrace.join("\n") + $stderr.print "\n" + exit 1 + end + + case tk + when RubyToken::TkCOMMENT_WITH_CONTENT + last_comment = "" if reset_comment + if last_comment.empty? + # new comment from programmer to translator? + comment1 = tk.value.lstrip + if comment1 =~ /^TRANSLATORS\:/ + last_comment = $' + end + else + last_comment += "\n" + last_comment += tk.value + end + reset_comment = false + when RubyToken::TkNL + else + reset_comment = true + end + end + pomessages + end + + def target?(file) # :nodoc: + true # always true, as the default parser. + end + + private + def store_pomessage(pomessages, pomessage, file_name, line_no, last_comment) #:nodoc: + if pomessage && pomessage.msgid + pomessage.sources << file_name + ":" + line_no + pomessage.add_comment(last_comment) unless last_comment.empty? + pomessages << pomessage + true + else + false + end + end + end +end + +if __FILE__ == $0 + require 'pp' + ARGV.each do |path| + pp GetText::RubyParser.parse(path) + end + + #rl = GetText::RubyLexX.new; rl.set_input(ARGF) + #rl.parse do |tk| + #p tk + #end +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/pomessage.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/pomessage.rb new file mode 100644 index 000000000..b5efe7221 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/pomessage.rb @@ -0,0 +1,197 @@ +module GetText + class ParseError < StandardError + end + + # Contains data related to the expression or sentence that + # is to be translated. + class PoMessage + PARAMS = { + :normal => [:msgid], + :plural => [:msgid, :msgid_plural], + :msgctxt => [:msgctxt, :msgid], + :msgctxt_plural => [:msgctxt, :msgid, :msgid_plural] + } + + @@max_line_length = 70 + + # Sets the max line length. + def self.max_line_length=(len) + @@max_line_length = len + end + + # Gets the max line length. + def self.max_line_length + @@max_line_length + end + + # Required + attr_accessor :type # :normal, :plural, :msgctxt, :msgctxt_plural + attr_accessor :msgid + # Options + attr_accessor :msgid_plural + attr_accessor :msgctxt + attr_accessor :sources # ["file1:line1", "file2:line2", ...] + attr_accessor :comment + + # Create the object. +type+ should be :normal, :plural, :msgctxt or :msgctxt_plural. + def initialize(type) + @type = type + @sources = [] + @param_type = PARAMS[@type] + end + + # Support for extracted comments. Explanation s. + # http://www.gnu.org/software/gettext/manual/gettext.html#Names + def add_comment(new_comment) + if (new_comment and ! new_comment.empty?) + @comment ||= "" + @comment += new_comment + end + to_s + end + + # Returns a parameter representation suitable for po-files + # and other purposes. + def escaped(param_name) + orig = self.send param_name + orig.gsub(/"/, '\"').gsub(/\r/, '') + end + + # Checks if the other translation target is mergeable with + # the current one. Relevant are msgid and translation context (msgctxt). + def ==(other) + other.msgid == self.msgid && other.msgctxt == self.msgctxt + end + + # Merges two translation targets with the same msgid and returns the merged + # result. If one is declared as plural and the other not, then the one + # with the plural wins. + def merge(other) + return self unless other + raise ParseError, "Translation targets do not match: \n" \ + " self: #{self.inspect}\n other: '#{other.inspect}'" unless self == other + if other.msgid_plural && !self.msgid_plural + res = other + unless (res.sources.include? self.sources[0]) + res.sources += self.sources + res.add_comment(self.comment) + end + else + res = self + unless (res.sources.include? other.sources[0]) + res.sources += other.sources + res.add_comment(other.comment) + end + end + res + end + + # Output the po message for the po-file. + def to_po_str + raise "msgid is nil." unless @msgid + raise "sources is nil." unless @sources + + str = "" + # extracted comments + if comment + comment.split("\n").each do |comment_line| + str << "\n#. #{comment_line.strip}" + end + end + + # references + curr_pos = @@max_line_length + sources.each do |e| + if curr_pos + e.size > @@max_line_length + str << "\n#:" + curr_pos = 3 + else + curr_pos += (e.size + 1) + end + str << " " << e + end + + # msgctxt, msgid, msgstr + str << "\nmsgctxt \"" << msgctxt << "\"" if msgctxt? + str << "\nmsgid \"" << escaped(:msgid) << "\"\n" + if plural? + str << "msgid_plural \"" << escaped(:msgid_plural) << "\"\n" + str << "msgstr[0] \"\"\n" + str << "msgstr[1] \"\"\n" + else + str << "msgstr \"\"\n" + end + str + end + + # Returns true if the type is kind of msgctxt. + # And if this is a kind of msgctxt and msgctxt property + # is nil, then raise an RuntimeException. + def msgctxt? + if [:msgctxt, :msgctxt_plural].include? @type + raise "This PoMessage is a kind of msgctxt but the msgctxt property is nil. msgid: #{msgid}" unless @msgctxt + true + end + end + + # Returns true if the type is kind of plural. + # And if this is a kind of plural and msgid_plural property + # is nil, then raise an RuntimeException. + def plural? + if [:plural, :msgctxt_plural].include? @type + raise "This PoMessage is a kind of plural but the msgid_plural property is nil. msgid: #{msgid}" unless @msgid_plural + true + end + end + + private + + # sets or extends the value of a translation target params like msgid, + # msgctxt etc. + # param is symbol with the name of param + # value - new value + def set_value(param, value) + send "#{param}=", (send(param) || '') + value.gsub(/\n/, '\n') + end + + public + # For backward comatibility. This doesn't support "comment". + # ary = [msgid1, "file1:line1", "file2:line"] + def self.new_from_ary(ary) + ary = ary.dup + msgid = ary.shift + sources = ary + type = :normal + msgctxt = nil + msgid_plural = nil + + if msgid.include? "\004" + msgctxt, msgid = msgid.split(/\004/) + type = :msgctxt + end + if msgid.include? "\000" + ids = msgid.split(/\000/) + msgid = ids[0] + msgid_plural = ids[1] + if type == :msgctxt + type = :msgctxt_plural + else + type = :plural + end + end + ret = self.new(type) + ret.msgid = msgid + ret.sources = sources + ret.msgctxt = msgctxt + ret.msgid_plural = msgid_plural + ret + end + + def [](number) + param = @param_type[number] + raise ParseError, 'no more string parameters expected' unless param + send param + end + end + +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/poparser.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/poparser.rb new file mode 100644 index 000000000..827a99c2f --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/poparser.rb @@ -0,0 +1,356 @@ +=begin + poparser.rb - Generate a .mo + + Copyright (C) 2003-2009 Masao Mutoh <mutomasa at gmail.com> + + You may redistribute it and/or modify it under the same + license terms as Ruby. +=end + +# +# DO NOT MODIFY!!!! +# This file is automatically generated by Racc 1.4.6 +# from Racc grammer file "". +# + +require 'racc/parser.rb' +module GetText + class PoParser < Racc::Parser + +module_eval(<<'...end poparser.ry/module_eval...', 'poparser.ry', 108) + include GetText + GetText.bindtextdomain("rgettext") + + def unescape(orig) + ret = orig.gsub(/\\n/, "\n") + ret.gsub!(/\\t/, "\t") + ret.gsub!(/\\r/, "\r") + ret.gsub!(/\\"/, "\"") + ret + end + + def parse(str, data, ignore_fuzzy = true) + @comments = [] + @data = data + @fuzzy = false + @msgctxt = "" + $ignore_fuzzy = ignore_fuzzy + + str.strip! + @q = [] + until str.empty? do + case str + when /\A\s+/ + str = $' + when /\Amsgctxt/ + @q.push [:MSGCTXT, $&] + str = $' + when /\Amsgid_plural/ + @q.push [:MSGID_PLURAL, $&] + str = $' + when /\Amsgid/ + @q.push [:MSGID, $&] + str = $' + when /\Amsgstr/ + @q.push [:MSGSTR, $&] + str = $' + when /\A\[(\d+)\]/ + @q.push [:PLURAL_NUM, $1] + str = $' + when /\A\#~(.*)/ + $stderr.print _("Warning: obsolete msgid exists.\n") + $stderr.print " #{$&}\n" + @q.push [:COMMENT, $&] + str = $' + when /\A\#(.*)/ + @q.push [:COMMENT, $&] + str = $' + when /\A\"(.*)\"/ + @q.push [:STRING, $1] + str = $' + else + #c = str[0,1] + #@q.push [:STRING, c] + str = str[1..-1] + end + end + @q.push [false, '$end'] + if $DEBUG + @q.each do |a,b| + puts "[#{a}, #{b}]" + end + end + @yydebug = true if $DEBUG + do_parse + + if @comments.size > 0 + @data.set_comment(:last, @comments.join("\n")) + end + @data + end + + def next_token + @q.shift + end + + def on_message(msgid, msgstr) + if msgstr.size > 0 + @data[msgid] = msgstr + @data.set_comment(msgid, @comments.join("\n")) + end + @comments.clear + @msgctxt = "" + end + + def on_comment(comment) + @fuzzy = true if (/fuzzy/ =~ comment) + @comments << comment + end + + def parse_file(po_file, data, ignore_fuzzy = true) + args = [ po_file ] + # In Ruby 1.9, we must detect proper encoding of a PO file. + if String.instance_methods.include?(:encode) + encoding = detect_file_encoding(po_file) + args << "r:#{encoding}" + end + @po_file = po_file + parse(File.open(*args) {|io| io.read }, data, ignore_fuzzy) + end + + def detect_file_encoding(po_file) + open(po_file, :encoding => 'ASCII-8BIT') do |input| + input.lines.each do |line| + return Encoding.find($1) if %r["Content-Type:.*\scharset=(.*)\\n"] =~ line + end + end + Encoding.default_external + end + private :detect_file_encoding +...end poparser.ry/module_eval... +##### State transition tables begin ### + +racc_action_table = [ + 3, 13, 5, 7, 9, 15, 16, 17, 20, 17, + 13, 17, 13, 13, 11, 17, 23, 20, 13, 17 ] + +racc_action_check = [ + 1, 16, 1, 1, 1, 12, 12, 12, 18, 18, + 7, 14, 15, 9, 3, 19, 20, 21, 23, 25 ] + +racc_action_pointer = [ + nil, 0, nil, 14, nil, nil, nil, 3, nil, 6, + nil, nil, 0, nil, 4, 5, -6, nil, 2, 8, + 8, 11, nil, 11, nil, 12 ] + +racc_action_default = [ + -1, -16, -2, -16, -3, -13, -4, -16, -6, -16, + -7, 26, -16, -15, -5, -16, -16, -14, -16, -8, + -16, -9, -11, -16, -10, -12 ] + +racc_goto_table = [ + 12, 22, 14, 4, 24, 6, 2, 8, 18, 19, + 10, 21, 1, nil, nil, nil, 25 ] + +racc_goto_check = [ + 5, 9, 5, 3, 9, 4, 2, 6, 5, 5, + 7, 8, 1, nil, nil, nil, 5 ] + +racc_goto_pointer = [ + nil, 12, 5, 2, 4, -7, 6, 9, -7, -17 ] + +racc_goto_default = [ + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ] + +racc_reduce_table = [ + 0, 0, :racc_error, + 0, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 12, :_reduce_5, + 1, 13, :_reduce_none, + 1, 13, :_reduce_none, + 4, 15, :_reduce_8, + 5, 16, :_reduce_9, + 2, 17, :_reduce_10, + 1, 17, :_reduce_none, + 3, 18, :_reduce_12, + 1, 11, :_reduce_13, + 2, 14, :_reduce_14, + 1, 14, :_reduce_15 ] + +racc_reduce_n = 16 + +racc_shift_n = 26 + +racc_token_table = { + false => 0, + :error => 1, + :COMMENT => 2, + :MSGID => 3, + :MSGCTXT => 4, + :MSGID_PLURAL => 5, + :MSGSTR => 6, + :STRING => 7, + :PLURAL_NUM => 8 } + +racc_nt_base = 9 + +racc_use_result_var = true + +Racc_arg = [ + racc_action_table, + racc_action_check, + racc_action_default, + racc_action_pointer, + racc_goto_table, + racc_goto_check, + racc_goto_default, + racc_goto_pointer, + racc_nt_base, + racc_reduce_table, + racc_token_table, + racc_shift_n, + racc_reduce_n, + racc_use_result_var ] + +Racc_token_to_s_table = [ + "$end", + "error", + "COMMENT", + "MSGID", + "MSGCTXT", + "MSGID_PLURAL", + "MSGSTR", + "STRING", + "PLURAL_NUM", + "$start", + "msgfmt", + "comment", + "msgctxt", + "message", + "string_list", + "single_message", + "plural_message", + "msgstr_plural", + "msgstr_plural_line" ] + +Racc_debug_parser = true + +##### State transition tables end ##### + +# reduce 0 omitted + +# reduce 1 omitted + +# reduce 2 omitted + +# reduce 3 omitted + +# reduce 4 omitted + +module_eval(<<'.,.,', 'poparser.ry', 23) + def _reduce_5(val, _values, result) + @msgctxt = unescape(val[1]) + "\004" + + result + end +.,., + +# reduce 6 omitted + +# reduce 7 omitted + +module_eval(<<'.,.,', 'poparser.ry', 35) + def _reduce_8(val, _values, result) + if @fuzzy and $ignore_fuzzy + if val[1] != "" + $stderr.print _("Warning: fuzzy message was ignored.\n") + $stderr.print " #{@po_file}: msgid '#{val[1]}'\n" + else + on_message('', unescape(val[3])) + end + @fuzzy = false + else + on_message(@msgctxt + unescape(val[1]), unescape(val[3])) + end + result = "" + + result + end +.,., + +module_eval(<<'.,.,', 'poparser.ry', 52) + def _reduce_9(val, _values, result) + if @fuzzy and $ignore_fuzzy + if val[1] != "" + $stderr.print _("Warning: fuzzy message was ignored.\n") + $stderr.print "msgid = '#{val[1]}\n" + else + on_message('', unescape(val[3])) + end + @fuzzy = false + else + on_message(@msgctxt + unescape(val[1]) + "\000" + unescape(val[3]), unescape(val[4])) + end + result = "" + + result + end +.,., + +module_eval(<<'.,.,', 'poparser.ry', 70) + def _reduce_10(val, _values, result) + if val[0].size > 0 + result = val[0] + "\000" + val[1] + else + result = "" + end + + result + end +.,., + +# reduce 11 omitted + +module_eval(<<'.,.,', 'poparser.ry', 82) + def _reduce_12(val, _values, result) + result = val[2] + + result + end +.,., + +module_eval(<<'.,.,', 'poparser.ry', 89) + def _reduce_13(val, _values, result) + on_comment(val[0]) + + result + end +.,., + +module_eval(<<'.,.,', 'poparser.ry', 97) + def _reduce_14(val, _values, result) + result = val.delete_if{|item| item == ""}.join + + result + end +.,., + +module_eval(<<'.,.,', 'poparser.ry', 101) + def _reduce_15(val, _values, result) + result = val[0] + + result + end +.,., + +def _reduce_none(val, _values, result) + val[0] +end + + end # class PoParser + end # module GetText + + diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/rgettext.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/rgettext.rb new file mode 100644 index 000000000..a66ed2fe0 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/rgettext.rb @@ -0,0 +1,226 @@ +#! /usr/bin/env ruby +=begin + rgettext.rb - Generate a .pot file. + + Copyright (C) 2003-2009 Masao Mutoh + Copyright (C) 2001,2002 Yasushi Shoji, Masao Mutoh + + Yasushi Shoji <yashi at atmark-techno.com> + 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 'optparse' +require 'gettext' +require 'rbconfig' + +module GetText + + module RGetText #:nodoc: + extend GetText + + bindtextdomain("rgettext") + + # constant values + VERSION = GetText::VERSION + + @ex_parsers = [] + [ + ["glade.rb", "GladeParser"], + ["erb.rb", "ErbParser"], +# ["ripper.rb", "RipperParser"], + ["ruby.rb", "RubyParser"] # Default parser. + ].each do |f, klass| + begin + require "gettext/tools/parser/#{f}" + @ex_parsers << GetText.const_get(klass) + rescue + $stderr.puts _("'%{klass}' is ignored.") % {:klass => klass} + $stderr.puts $! if $DEBUG + end + end + + module_function + + # Add an option parser + # the option parser module requires to have target?(file) and parser(file, ary) method. + # + # require 'gettext/tools/rgettext' + # module FooParser + # module_function + # def target?(file) + # File.extname(file) == '.foo' # *.foo file only. + # end + # def parse(file, ary) + # : + # return ary # [["msgid1", "foo.rb:200"], ["msgid2", "bar.rb:300", "baz.rb:400"], ...] + # end + # end + # + # GetText::RGetText.add_parser(FooParser) + def add_parser(klass) + @ex_parsers.insert(0, klass) + end + + def generate_pot_header # :nodoc: + time = Time.now.strftime("%Y-%m-%d %H:%M") + off = Time.now.utc_offset + sign = off <= 0 ? '-' : '+' + time += sprintf('%s%02d%02d', sign, *(off.abs / 60).divmod(60)) + + <<TITLE +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\\n" +"POT-Creation-Date: #{time}\\n" +"PO-Revision-Date: #{time}\\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n" +"Language-Team: LANGUAGE <LL@li.org>\\n" +"MIME-Version: 1.0\\n" +"Content-Type: text/plain; charset=UTF-8\\n" +"Content-Transfer-Encoding: 8bit\\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n" +TITLE + end + + def generate_pot(paths) # :nodoc: + pomessages = parse(paths) + str = "" + pomessages.each do |target| + str << target.to_po_str + end + str + end + + def parse(paths) # :nodoc: + pomessages = [] + paths.each do |path| + begin + @ex_parsers.each do |klass| + if klass.target?(path) + if klass.method(:parse).arity == 1 + targets = klass.parse(path) + else + # For backward compatibility. + targets = klass.parse(path, []) + end + + targets.each{|pomessage| + if pomessage.kind_of? Array + pomessage = PoMessage.new_from_ary(pomessage) + end + + # Save the previous target. + existing = pomessages.find_index {|t| t == pomessage} + if existing + pomessage = pomessages[existing].merge(pomessage) + pomessages[existing] = pomessage + else + pomessages << pomessage + end + } + break + end + end + rescue + puts _("Error parsing %{path}") % {:path => path} + raise + end + end + pomessages + end + + def check_options # :nodoc: + output = STDOUT + + opts = OptionParser.new + opts.banner = _("Usage: %s input.rb [-r parser.rb] [-o output.pot]") % $0 + opts.separator("") + opts.separator(_("Extract translatable strings from given input files.")) + opts.separator("") + opts.separator(_("Specific options:")) + + opts.on("-o", "--output=FILE", _("write output to specified file")) do |out| + unless FileTest.exist? out + output = File.new(File.expand_path(out), "w+") + else + $stderr.puts(_("File '%s' already exists.") % out) + exit 1 + end + end + + opts.on("-r", "--require=library", _("require the library before executing rgettext")) do |out| + require out + end + + opts.on("-d", "--debug", _("run in debugging mode")) do + $DEBUG = true + end + + opts.on_tail("--version", _("display version information and exit")) do + puts "#{$0} #{VERSION}" + puts "#{File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"])} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" + exit + end + + opts.parse!(ARGV) + + if ARGV.size == 0 + puts opts.help + exit 1 + end + + [ARGV, output] + end + + def run(paths = nil, out = STDOUT) # :nodoc: + if paths.is_a? String + paths = [paths] + elsif ! paths + paths, out = check_options + end + + if paths.size == 0 + raise ArgumentError, _("no input files") + end + + if out.is_a? String + File.open(File.expand_path(out), "w+") do |file| + file.puts generate_pot_header + file.puts generate_pot(paths) + end + else + out.puts generate_pot_header + out.puts generate_pot(paths) + end + self + end + end + extend self + # Creates a po-file from targetfiles(ruby-script-files, .rhtml files, glade-2 XML files), + # then output the result to out. If no parameter is set, it behaves same as command line tools(rgettet). + # + # This function is a part of GetText.create_pofiles. + # Usually you don't need to call this function directly. + # + # * paths: An Array of po-file paths or nil. + # * out: output IO or output path. + # * Returns: self + def rgettext(paths = nil, out = STDOUT) + RGetText.run(paths, out) + self + end +end + +if $0 == __FILE__ + GetText.rgettext +# GetText.rgettext($0, "tmp.txt") +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/rmsgfmt.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/rmsgfmt.rb new file mode 100644 index 000000000..2aa428c55 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/rmsgfmt.rb @@ -0,0 +1,84 @@ +=begin + rmsgfmt.rb - Generate a .mo + + Copyright (C) 2003-2009 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +require 'optparse' +require 'fileutils' +require 'gettext' +require 'gettext/tools/poparser' +require 'rbconfig' + +module GetText + + module RMsgfmt #:nodoc: + extend GetText + extend self + + bindtextdomain "rgettext" + + def run(targetfile = nil, output_path = nil) # :nodoc: + unless targetfile + targetfile, output_path = check_options + end + unless targetfile + raise ArgumentError, _("no input files") + end + unless output_path + output_path = "messages.mo" + end + + parser = PoParser.new + data = MOFile.new + + parser.parse_file(targetfile, data) + data.save_to_file(output_path) + end + + def check_options # :nodoc: + output = nil + + opts = OptionParser.new + opts.banner = _("Usage: %s input.po [-o output.mo]" % $0) + opts.separator("") + opts.separator(_("Generate binary message catalog from textual translation description.")) + opts.separator("") + opts.separator(_("Specific options:")) + + opts.on("-o", "--output=FILE", _("write output to specified file")) do |out| + output = out + end + + opts.on_tail("--version", _("display version information and exit")) do + puts "#{$0} #{GetText::VERSION}" + puts "#{File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"])} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" + exit + end + opts.parse!(ARGV) + + if ARGV.size == 0 + puts opts.help + exit 1 + end + + [ARGV[0], output] + end + end + + # Creates a mo-file from a targetfile(po-file), then output the result to out. + # If no parameter is set, it behaves same as command line tools(rmsgfmt). + # * targetfile: An Array of po-files or nil. + # * output_path: output path. + # * Returns: the MOFile object. + def rmsgfmt(targetfile = nil, output_path = nil) + RMsgfmt.run(targetfile, output_path) + end +end + +if $0 == __FILE__ then + GetText.rmsgfmt +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/tools/rmsgmerge.rb b/vendor/gems/gettext-2.1.0/lib/gettext/tools/rmsgmerge.rb new file mode 100644 index 000000000..802ba20bc --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/tools/rmsgmerge.rb @@ -0,0 +1,498 @@ +=begin + rmsgmerge.rb - Merge old .po to new .po + + Copyright (C) 2005-2009 Masao Mutoh + Copyright (C) 2005,2006 speakillof + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +require 'optparse' +require 'gettext' +require 'gettext/tools/poparser' +require 'rbconfig' + +module GetText + + module RMsgMerge + + class PoData #:nodoc: + + attr_reader :msgids + + def initialize + @msgid2msgstr = {} + @msgid2comment = {} + @msgids = [] + end + + def set_comment(msgid_or_sym, comment) + @msgid2comment[msgid_or_sym] = comment + end + + def msgstr(msgid) + @msgid2msgstr[msgid] + end + + def comment(msgid) + @msgid2comment[msgid] + end + + def [](msgid) + @msgid2msgstr[msgid] + end + + def []=(msgid, msgstr) + # Retain the order + unless @msgid2msgstr[msgid] + @msgids << msgid + end + + @msgid2msgstr[msgid] = msgstr + end + + def each_msgid + arr = @msgids.delete_if{|i| Symbol === i or i == ''} + arr.each do |i| + yield i + end + end + + def msgid?(msgid) + !(Symbol === msgid) and @msgid2msgstr[msgid] and (msgid != '') + end + + # Is it necessary to implement this method? + def search_msgid_fuzzy(msgid, used_msgids) + nil + end + + def nplural + unless @msgid2msgstr[''] + return 0 + else + if /\s*nplural\s*=\s*(\d+)/ =~ @msgid2msgstr[''] + return $1.to_i + else + return 0 + end + + end + end + + def generate_po + str = '' + str << generate_po_header + + self.each_msgid do |id| + str << self.generate_po_entry(id) + end + + str << @msgid2comment[:last] + str + end + + def generate_po_header + str = "" + + str << @msgid2comment[''].strip << "\n" + str << 'msgid ""' << "\n" + str << 'msgstr ""' << "\n" + msgstr = @msgid2msgstr[''].gsub(/"/, '\"').gsub(/\r/, '') + msgstr = msgstr.gsub(/^(.*)$/, '"\1\n"') + str << msgstr + str << "\n" + + str + end + + def generate_po_entry(msgid) + str = "" + str << @msgid2comment[msgid] + if str[-1] != "\n"[0] + str << "\n" + end + + id = msgid.gsub(/"/, '\"').gsub(/\r/, '') + msgstr = @msgid2msgstr[msgid].gsub(/"/, '\"').gsub(/\r/, '') + + if id.include?("\000") + ids = id.split(/\000/) + str << "msgid " << __conv(ids[0]) << "\n" + ids[1..-1].each do |single_id| + str << "msgid_plural " << __conv(single_id) << "\n" + end + + msgstr.split("\000").each_with_index do |m, n| + str << "msgstr[#{n}] " << __conv(m) << "\n" + end + else + str << "msgid " << __conv(id) << "\n" + str << "msgstr " << __conv(msgstr) << "\n" + end + + str << "\n" + str + end + + def __conv(str) + s = '' + + if str.count("\n") > 1 + s << '""' << "\n" + s << str.gsub(/^(.*)$/, '"\1\n"') + else + s << '"' << str.sub("\n", "\\n") << '"' + end + + s.rstrip + end + + end + + class Merger #:nodoc: + + # From GNU gettext source. + # + # Merge the reference with the definition: take the #. and + # #: comments from the reference, take the # comments from + # the definition, take the msgstr from the definition. Add + # this merged entry to the output message list. + DOT_COMMENT_RE = /\A#\./ + SEMICOLON_COMMENT_RE = /\A#\:/ + FUZZY_RE = /\A#\,/ + NOT_SPECIAL_COMMENT_RE = /\A#([^:.,]|\z)/ + + CRLF_RE = /\r?\n/ + POT_DATE_EXTRACT_RE = /POT-Creation-Date:\s*(.*)?\s*$/ + POT_DATE_RE = /POT-Creation-Date:.*?$/ + + def merge(definition, reference) + # deep copy + result = Marshal.load( Marshal.dump(reference) ) + + used = [] + merge_header(result, definition) + + result.each_msgid do |msgid| + if definition.msgid?(msgid) + used << msgid + merge_message(msgid, result, msgid, definition) + elsif other_msgid = definition.search_msgid_fuzzy(msgid, used) + used << other_msgid + merge_fuzzy_message(msgid, result, other_msgid, definition) + elsif msgid.index("\000") and ( reference.msgstr(msgid).gsub("\000", '') == '' ) + # plural + result[msgid] = "\000" * definition.nplural + else + change_reference_comment(msgid, result) + end + end + + ################################################################### + # msgids which are not used in reference are handled as obsolete. # + ################################################################### + last_comment = result.comment(:last) || '' + definition.each_msgid do |msgid| + unless used.include?(msgid) + last_comment << "\n" + last_comment << definition.generate_po_entry(msgid).strip.gsub(/^/, '#. ') + last_comment << "\n" + end + end + result.set_comment(:last, last_comment) + + result + end + + def merge_message(msgid, target, def_msgid, definition) + merge_comment(msgid, target, def_msgid, definition) + + ############################################ + # check mismatch of msgid and msgid_plural # + ############################################ + def_msgstr = definition[def_msgid] + if msgid.index("\000") + if def_msgstr.index("\000") + # OK + target[msgid] = def_msgstr + else + # NG + s = '' + definition.nplural.times { + s << def_msgstr + s << "\000" + } + target[msgid] = s + end + else + if def_msgstr.index("\000") + # NG + target[msgid] = def_msgstr.split("\000")[0] + else + # OK + target[msgid] = def_msgstr + end + end + end + + # for the future + def merge_fuzzy_message(msgid, target, def_msgid, definition) + merge_message(msgid, target, def_msgid, definition) + end + + def merge_comment(msgid, target, def_msgid, definition) + ref_comment = target.comment(msgid) + def_comment = definition.comment(def_msgid) + + normal_comment = [] + dot_comment = [] + semi_comment = [] + is_fuzzy = false + + def_comment.split(CRLF_RE).each do |l| + if NOT_SPECIAL_COMMENT_RE =~ l + normal_comment << l + end + end + + ref_comment.split(CRLF_RE).each do |l| + if DOT_COMMENT_RE =~ l + dot_comment << l + elsif SEMICOLON_COMMENT_RE =~ l + semi_comment << l + elsif FUZZY_RE =~ l + is_fuzzy = true + end + end + + str = format_comment(normal_comment, dot_comment, semi_comment, is_fuzzy) + target.set_comment(msgid, str) + end + + def change_reference_comment(msgid, podata) + normal_comment = [] + dot_comment = [] + semi_comment = [] + is_fuzzy = false + + podata.comment(msgid).split(CRLF_RE).each do |l| + if DOT_COMMENT_RE =~ l + dot_comment << l + elsif SEMICOLON_COMMENT_RE =~ l + semi_comment << l + elsif FUZZY_RE =~ l + is_fuzzy = true + else + normal_comment << l + end + end + + str = format_comment(normal_comment, dot_comment, semi_comment, is_fuzzy) + podata.set_comment(msgid, str) + end + + def format_comment(normal_comment, dot_comment, semi_comment, is_fuzzy) + str = '' + + str << normal_comment.join("\n").gsub(/^#(\s*)/){|sss| + if $1 == "" + "# " + else + sss + end + } + if normal_comment.size > 0 + str << "\n" + end + + str << dot_comment.join("\n").gsub(/^#.(\s*)/){|sss| + if $1 == "" + "#. " + else + sss + end + } + if dot_comment.size > 0 + str << "\n" + end + + str << semi_comment.join("\n").gsub(/^#:\s*/, "#: ") + if semi_comment.size > 0 + str << "\n" + end + + if is_fuzzy + str << "#, fuzzy\n" + end + + str + end + + def merge_header(target, definition) + merge_comment('', target, '', definition) + + msg = target.msgstr('') + def_msg = definition.msgstr('') + if POT_DATE_EXTRACT_RE =~ msg + time = $1 + def_msg = def_msg.sub(POT_DATE_RE, "POT-Creation-Date: #{time}") + end + + target[''] = def_msg + end + + end + + end + +end + +module GetText::RMsgMerge #:nodoc: + + class Config #:nodoc: + + attr_accessor :defpo, :refpot, :output, :fuzzy, :update + + # update mode options + attr_accessor :backup, :suffix + +=begin +The result is written back to def.po. + --backup=CONTROL make a backup of def.po + --suffix=SUFFIX override the usual backup suffix +The version control method may be selected via the --backup option or through +the VERSION_CONTROL environment variable. Here are the values: + none, off never make backups (even if --backup is given) + numbered, t make numbered backups + existing, nil numbered if numbered backups exist, simple otherwise + simple, never always make simple backups +The backup suffix is `~', unless set with --suffix or the SIMPLE_BACKUP_SUFFIX +environment variable. +=end + + def initialize + @output = STDOUT + @fuzzy = nil + @update = nil + @backup = ENV["VERSION_CONTROL"] + @suffix= ENV["SIMPLE_BACKUP_SUFFIX"] || "~" + @input_dirs = ["."] + end + + end + +end + +module GetText + + module RMsgMerge + extend GetText + extend self + + bindtextdomain("rgettext") + + # constant values + VERSION = GetText::VERSION + DATE = %w($Date: 2007/07/21 15:03:05 $)[1] + + def check_options(config) + opts = OptionParser.new + opts.banner = _("Usage: %s def.po ref.pot [-o output.pot]") % $0 + #opts.summary_width = 80 + opts.separator("") + opts.separator(_("Merges two Uniforum style .po files together. The def.po file is an existing PO file with translations. The ref.pot file is the last created PO file with up-to-date source references. ref.pot is generally created by rgettext.")) + opts.separator("") + opts.separator(_("Specific options:")) + + opts.on("-o", "--output=FILE", _("write output to specified file")) do |out| + unless FileTest.exist? out + config.output = out + else + #$stderr.puts(_("File '%s' has already existed.") % out) + #exit 1 + end + end + + #opts.on("-F", "--fuzzy-matching") + + opts.on_tail("--version", _("display version information and exit")) do + puts "#{$0} #{VERSION} (#{DATE})" + puts "#{File.join(::Config::CONFIG["bindir"], ::Config::CONFIG["RUBY_INSTALL_NAME"])} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" + exit + end + + opts.parse!(ARGV) + + if ARGV.size != 2 + puts opts.help + exit 1 + end + + config.defpo = ARGV[0] + config.refpot = ARGV[1] + end + + def run(reference = nil, definition = nil, out = STDOUT) + config = GetText::RMsgMerge::Config.new + config.refpot = reference + config.defpo = definition + config.output = out + + check_options(config) + + if config.defpo.nil? + raise ArgumentError, _("definition po is not given.") + elsif config.refpot.nil? + raise ArgumentError, _("reference pot is not given.") + end + + parser = PoParser.new + defpo = parser.parse_file(config.defpo, PoData.new, false) + refpot = parser.parse_file(config.refstrrefstr, PoData.new, false) + + m = Merger.new + result = m.merge(defpo, refpot) + p result if $DEBUG + print result.generate_po if $DEBUG + + begin + if out.is_a? String + File.open(File.expand_path(out), "w+") do |file| + file.write(result.generate_po) + end + else + out.puts(result.generate_po) + end + ensure + out.close + end + end + + end + +end + + + +module GetText + + # Experimental + def rmsgmerge(reference = nil, definition = nil, out = STDOUT) + RMsgMerge.run(reference, definition, out) + end + +end + + + +if $0 == __FILE__ then + require 'pp' + + #parser = GetText::RMsgMerge::PoParser.new; + #parser = GetText::PoParser.new; + #pp parser.parse(ARGF.read) + + GetText.rmsgmerge +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/utils.rb b/vendor/gems/gettext-2.1.0/lib/gettext/utils.rb new file mode 100644 index 000000000..e4f1be4e8 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/utils.rb @@ -0,0 +1,37 @@ +=begin + utils.rb - Utility functions + + Copyright (C) 2005,2006 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end + +require 'gettext/tools' + +warn "'gettext/utils.rb' is deprecated. Use gettext/tools.rb." + +module GetText + + alias :create_mofiles_org :create_mofiles #:nodoc: + alias :update_pofiles_org :update_pofiles #:nodoc: + + + # Deprecated. Use gettext/tools instead. + def create_mofiles(verbose = false, + podir = "./po", targetdir = "./data/locale", + targetpath_rule = "%s/LC_MESSAGES") # :nodoc: + warn "'gettext/utils.rb' is deprecated. Use gettext/tools.rb." + create_mofiles_org(:verbose => verbose, + :po_root => podir, + :mo_root => targetdir, + :mo_root_rule => targetpath_rule) + end + + # Deprecated. Use gettext/tools instead. + def update_pofiles(textdomain, files, app_version, po_root = "po", refpot = "tmp.pot") # :nodoc: + warn "'gettext/utils.rb' is deprecated. Use gettext/tools.rb." + options = {:po_root => po_root} + update_pofiles_org(textdomain, files, app_version, options) + end +end diff --git a/vendor/gems/gettext-2.1.0/lib/gettext/version.rb b/vendor/gems/gettext-2.1.0/lib/gettext/version.rb new file mode 100644 index 000000000..c1de5bc07 --- /dev/null +++ b/vendor/gems/gettext-2.1.0/lib/gettext/version.rb @@ -0,0 +1,12 @@ +#! /usr/bin/ruby +=begin + version - version information of Ruby-GetText-Package + + Copyright (C) 2005-2009 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby or LGPL. +=end +module GetText + VERSION = "2.1.0" +end |