=begin gettext.rb - GetText module Copyright (C) 2001-2006 Masao Mutoh Copyright (C) 2001-2003 Masahiro Sakai Masao Mutoh Masahiro Sakai You may redistribute it and/or modify it under the same license terms as Ruby. $Id: gettext.rb,v 1.14 2006/06/07 15:56:04 mutoh Exp $ =end require 'rbconfig' require 'gettext/version' require 'gettext/mo' require 'gettext/locale' require 'gettext/textdomainmanager' require 'gettext/string' module GetText # If the textdomain isn't bound when calling GetText.textdomain, this error is raised. class NoboundTextDomainError < RuntimeError end def self.included(mod) #:nodoc: mod.extend self end @@__textdomainmanagers = Hash.new # Bind a textdomain(%{path}/%{locale}/LC_MESSAGES/%{domainname}.mo) to your program. # Normally, the texdomain scope becomes a ruby-script-file. # So you need to call this function each ruby-script-files. # On the other hand, if you call this function under GetText::Container #(gettext/container, gettext/erb, gettext/rails), the textdomain scope becomes a Class/Module. # # * 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) # * :locale - the locale string such as "ja_JP.UTF-8". Generally, you should use GetText.set_locale instead. # The value is searched order by: # # the value of this value > System default language. # # * :charset - output charset. This affect the current textdomain only. Generally, you should use GetText.set_output_charset instead. # The value is searched order by: # # the value of Locale.set_output_charset > ENV["OUTPUT_CHARSET"] > this value > System default charset. # * Returns: the GetText::TextDomain. # # Note: Don't use locale_, charset argument(not in options). # They are remained for backward compatibility. # def bindtextdomain(domainname, options = {}, locale_ = nil, charset = nil) opt = {} if options.kind_of? String # For backward compatibility opt = {:path => options, :locale => locale_, :charset => charset} elsif options opt = options end opt[:locale] = opt[:locale] ? Locale::Object.new(opt[:locale]) : Locale.get opt[:charset] = output_charset if output_charset locale.charset = opt[:charset] if opt[:charset] Locale.set_current(opt[:locale]) manager = @@__textdomainmanagers[bound_target] if manager manager.set_locale(opt[:locale]) else manager = TextDomainManager.new(bound_target, opt[:locale]) @@__textdomainmanagers[bound_target] = manager end manager.add_textdomain(domainname, opt) manager end # Binds a existed textdomain to your program. # This is the same function with GetText.bindtextdomain but simpler than bindtextdomain. # Notice 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::TextDomain. def textdomain(domainname) domain = TextDomainManager.textdomain(domainname) raise NoboundTextDomainError, "#{domainname} is not bound." unless domain manager = @@__textdomainmanagers[bound_target] unless manager manager = TextDomainManager.new(bound_target, Locale.get) @@__textdomainmanagers[bound_target] = manager end manager.add_textdomain(domainname) manager.set_locale(Locale.get) end TARGET_REGEXP = /\:\:/ # :nodoc: # Iterates bound textdomains. # * klass: a class/module to find. Default is the class of self. # * ignore_targets: Ignore tragets. # * Returns: a bound GetText::TextDomain or nil. def each_textdomain(klass = bound_target, ignore_targets = []) (klass.ancestors + [GetText]).each do |target| unless ignore_targets.include? target ignore_targets << target manager = @@__textdomainmanagers[target] if manager manager.each{ |textdomain| yield textdomain } end if target.to_s =~ TARGET_REGEXP each_textdomain(Module.const_get($`), ignore_targets){|domain| yield domain } end end end self end def bound_target # :nodoc: if self.kind_of? Class or self.kind_of? Module self else self.class end end # call-seq: # gettext(msgid) # _(msgid) # # Translates msgid and return the message. # * msgid: the message id. # * Returns: localized text by msgid. If there are not binded mo-file, it will return msgid. def gettext(msgid) ret = nil each_textdomain {|textdomain| ret = textdomain.gettext(msgid) break if ret } ret ? ret : msgid 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(arg1, arg2, arg3 = nil) if arg1.kind_of?(Array) msgid = arg1[0] msgid_plural = arg1[1] n = arg2 else msgid = arg1 msgid_plural = arg2 n = arg3 end ret = nil each_textdomain {|textdomain| ret = textdomain.ngettext(msgid, msgid_plural, n) break if ret } ret ? ret : (n == 1 ? msgid : msgid_plural) end # This function does nothing. But it is required in order to recognize the msgid by rgettext. # * 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 # 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. # * 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". # See: http://www.gnu.org/software/gettext/manual/html_mono/gettext.html#SEC151 def sgettext(msgid, div = '|') msg = gettext(msgid) if msg == msgid if index = msg.rindex(div) msg = msg[(index + 1)..-1] end end msg end # Sets locale to the current class/module # # Notice that you shouldn't use this for your own Libraries. # * locale: a locale string or Locale::Object. # * this_target_only: true if you want to change the current class/module only. # Otherwise, this changes the locale of the current class/module and its ancestors. # Default is false. # * Returns: self def set_locale(locale, this_target_only = false) ret = nil if locale if locale.kind_of? Locale::Object ret = locale else ret = Locale::Object.new(locale.to_s) end ret.charset = output_charset if output_charset Locale.set(ret) else Locale.clear ret = Locale.get end if this_target_only manager = @@__textdomainmanagers[bound_target] if manager manager.set_locale(ret) end else each_textdomain {|textdomain| textdomain.set_locale(ret) } end self end # Sets locale to the all textdomains. # # Notice that you shouldn't use this for your own Libraries. # * locale: a locale string or Locale::Object. # * Returns: self def set_locale_all(locale) ret = nil if locale if locale.kind_of? Locale::Object ret = locale else ret = Locale::Object.new(locale.to_s) end ret.charset = output_charset if output_charset Locale.set(ret) else Locale.set(nil) ret = Locale.get end TextDomainManager.each_all {|textdomain| textdomain.set_locale(ret) } self end # Same as GetText.set_locale. # * locale: a locale string # * src: internal usage only. You shouldn't use this. # * Returns: a locale string def locale=(locale) set_locale(locale) locale 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: charset def set_output_charset(charset) TextDomainManager.output_charset = charset self end # Same as GetText.set_output_charset # * charset: an output_charset # * Returns: charset def output_charset=(charset) TextDomainManager.output_charset = charset end # Gets the current output_charset which is set using GetText.set_output_charset. # * Returns: output_charset. def output_charset TextDomainManager.output_charset end # Gets the current locale. # * Returns: a current Locale::Object def locale Locale.current end # Deprecated. Now this function do nothing. Use GetText.output_charset= instead. def set_charset(cs) $stderr.puts "Deprecated. Now this function do nothing. Use GetText.output_charset= instead." if $DEBUG self end def charset=(cs) #:nodoc: set_charset(cs) cs end # Add default locale path. # * path: a new locale path. (e.g.) "/usr/share/locale/%{locale}/LC_MESSAGES/%{name}.mo" # ('locale' => "ja_JP", 'name' => "textdomain") # * Returns: the new DEFAULT_LOCALE_PATHS def add_default_locale_path(path) TextDomain.add_default_locale_path(path) end alias :setlocale :locale= #:nodoc: alias :_ :gettext #:nodoc: alias :n_ :ngettext #:nodoc: alias :s_ :sgettext #:nodoc: module_function :bindtextdomain, :textdomain, :each_textdomain, :N_, :gettext, :_, :ngettext, :n_, :sgettext, :s_, :bound_target, :setlocale, :set_locale, :locale=, :set_locale_all, :locale, :charset=, :set_charset, :set_output_charset, :output_charset=, :output_charset end