=begin
  gettext/rails.rb - GetText for "Ruby on Rails"

  Copyright (C) 2005,2006  Masao Mutoh

  You may redistribute it and/or modify it under the same
  license terms as Ruby.

  $Id: rails.rb,v 1.43 2006/07/16 15:19:17 mutoh Exp $
=end

require 'gettext/cgi'
require 'active_record'
require 'active_support'

module GetText
  # GetText::Rails supports Ruby on Rails.
  # You add only 2 lines in your controller, all of the controller/view/models are
  # targeted the textdomain. 
  #
  # See <Ruby-GetText-Package HOWTO for Ruby on Rails (http://www.yotabanana.com/hiki/ruby-gettext-howto-rails.html>.
  module Rails
    include GetText

    Rails = ::Rails  #:nodoc:

    alias :_bindtextdomain :bindtextdomain #:nodoc:

    def self.included(mod)  #:nodoc:
      mod.extend self
    end

    module_function
    # call-seq:
    # bindtextdomain(domainname, options = {})
    #
    # Bind a textdomain(#{path}/#{locale}/LC_MESSAGES/#{domainname}.mo) to your program. 
    # Notes the textdomain scope becomes all of the controllers/views/models in your app. 
    # This is different from normal GetText.bindtextomain.
    #
    # Usually, you don't call this directly in your rails application.
    # Call init_gettext in ActionController::Base instead.
    #
    # On the other hand, you need to call this in helpers/plugins.
    #
    # * domainname: the textdomain name.
    # * options: options as a Hash.
    #   * :locale - the locale value such as "ja-JP". When the value is nil, 
    #     locale is searched the order by this value > "lang" value of QUERY_STRING > 
    #     params["lang"] > "lang" value of Cookie > HTTP_ACCEPT_LANGUAGE value 
    #     > Default locale(en). 
    #   * :path - the path to the mo-files. Default is "RAIL_ROOT/locale".
    #   * :charset - the charset. Generally UTF-8 is recommanded.
    #     And the charset is set order by "the argument of bindtextdomain" 
    #     > HTTP_ACCEPT_CHARSET > Default charset(UTF-8).
    #   * :with_model - false if you want to ignore ActiveRecord support.
    #   * :with_helper - false if you want to ignore ApplicationHelper support.
    #
    # Note: Don't use locale, charset, with_model argument(not in options). 
    # They are remained for backward compatibility.
    #
    def bindtextdomain(domainname, options = {}, locale = nil, charset = nil, with_model = true)
      opt = {:with_helper => true, :with_model => true}
      if options.kind_of? CGI
	# For backward compatibility
	opt.merge!(:cgi => options, :locale => locale, :charset => charset, :with_model => with_model)
      else
	opt.merge!(options)
      end
      opt[:path] ||= File.join(RAILS_ROOT, "locale")
      Locale.set_current(nil)  # IMPORTANT! current locale should be nil once(default is used).
      set_cgi(opt[:cgi]) if opt[:cgi]
      ret = _bindtextdomain(domainname, opt)
      bindtextdomain_to(ActiveRecord::Base, domainname) if opt[:with_model]
      bindtextdomain_to(ApplicationHelper, domainname) if opt[:with_helper]
      ret
    end

    def bindtextdomain_to(klass, domainname) #:nodoc:
      klass.class_eval {
	include GetText
	textdomain(domainname)
      }
    end
  end
end


module ActionController #:nodoc:
  class Base
    helper GetText::Rails
    include GetText::Rails

    @@gettext_domainname = nil
    @@gettext_content_type = nil

    prepend_before_filter :init_gettext
    after_filter :init_content_type
    
    def init_gettext_main #:nodoc:
      if defined? request.cgi
	cgi_ = request.cgi
	cgi_.params["lang"] = [@params["lang"]] if @params["lang"]
	bindtextdomain(@@gettext_domainname, {:cgi => cgi_})  #You need to pass CGI object first.
      else
	bindtextdomain(@@gettext_domainname, {:locale => @params["lang"]})
      end
    end

    def init_content_type #:nodoc:
      if @headers["Content-Type"] and /javascript/ =~ @headers["Content-Type"]
	@headers["Content-Type"] = "text/javascript; charset=#{GetText.output_charset}"
      elsif ! @headers["Content-Type"]
	@headers["Content-Type"] = "#{@@gettext_content_type}; charset=#{GetText.output_charset}"
      end
    end


    def init_gettext # :nodoc:
      init_gettext_main if @@gettext_domainname
      ActiveRecord::Errors.class_eval{
	# You need to call bindtextdomain here because ActiveRecord::Errors doesn't know
	# what locale is used in.
	bindtextdomain("rails")
      }
    end
    
    # Bind a 'textdomain' to all of the controllers/views/models. Call this instead of GetText.bindtextdomain.
    # * textdomain: the textdomain
    # * options: options as a Hash.
    #   * :charset - the output charset. Default is "UTF-8"
    #   * :content_type - the content type. Default is "text/html"
    #
    # locale is searched the order by @params["lang"] > "lang" value of QUERY_STRING > 
    # "lang" value of Cookie > HTTP_ACCEPT_LANGUAGE value > Default locale(en). 
    # And the charset is set order by "the argument of bindtextdomain" > HTTP_ACCEPT_CHARSET > Default charset(UTF-8).
    #
    # Note: Don't use content_type argument(not in options). 
    # They are remained for backward compatibility.
    #
    # If you want to separate the textdomain each controllers, you need to call this function in the each controllers.
    #
    # app/controller/blog_controller.rb:
    #  require 'gettext/rails'
    #  
    #  class BlogController < ApplicationController
    #    init_gettext "blog"
    #      :
    #      :
    #    end
    def self.init_gettext(domainname, options = {}, content_type = "text/html")
      opt = {:charset => "UTF-8", :content_type => content_type}
      if options.kind_of? String
	# For backward compatibility
	opt.merge!(:charset => options, :content_type => content_type)
      end
      GetText.output_charset = opt[:charset]
      @@gettext_domainname = domainname
      @@gettext_content_type = opt[:content_type]
    end

    # Gets the textdomain name of this controller.
    def self.textdomainname
      @@gettext_domainname
    end

  end

  class TestRequest < AbstractRequest  #:nodoc:
    @cgi = nil
    def cgi
      @cgi = CGI.new unless @cgi
      @cgi
    end
  end
end

module ActiveRecord #:nodoc:
  class Migration
    extend GetText
    include GetText
  end
  
  module ConnectionAdapters #:nodoc:
    # An abstract definition of a column in a table.
    class Column
      attr_accessor :table_class

      def human_name
        table_class.human_attribute_name(@name)
      end
    end
  end

  module Validations # :nodoc:
    def self.append_features(base) # :nodoc:
      super
      base.extend ClassMethods
      base.class_eval{
	def gettext(str)  #:nodoc:
	  _(str)
	end
	def self.human_attribute_name(attribute_key_name) #:nodoc:
	  s_("#{self}|#{attribute_key_name.humanize}")
	end

	def self.human_attribute_table_name_for_error(table_name) #:nodoc:
	  _(table_name.gsub(/_/, " "))
	end
      }
    end

    module ClassMethods #:nodoc:
      @@custom_error_messages_d = {}
      
      # Very ugly but...
      def validates_length_of(*attrs)  #:nodoc:
	if attrs.last.is_a?(Hash)
	  msg = attrs.last[:message] 
	  if msg
	    @@custom_error_messages_d[msg] = /#{Regexp.escape(msg).sub(/%d/, '(\d+)')}/ 
	  end
	end
	validates_size_of(*attrs)
      end
      
      def custom_error_messages_d  #:nodoc:
	@@custom_error_messages_d
      end
    end
    def custom_error_messages_d  #:nodoc:
      self.class.custom_error_messages_d
    end
  end
  
  class Base
    include GetText::Rails
    include Validations

    @@gettext_untranslate = false
    @@gettext_untranslate_columns = []

    # Untranslate all of the tablename/fieldnames in this model class.
    def self.untranslate_all
      @@gettext_untranslate = true
    end

    # Returns true if "untranslate_all" is called. Otherwise false.
    def self.untranslate_all?
      @@gettext_untranslate
    end

    # Sets the untranslate columns.
    # (e.g.) untranslate :foo, :bar, :baz
    def self.untranslate(*w)
      @@gettext_untranslate_columns += w.collect{|v| v.to_s}
    end

    # Returns true if the column is set "untranslate".
    # (e.g.) untranslate? :foo
    def self.untranslate?(columnname)
      @@gettext_untranslate_columns.include?(columnname)
    end

    def self.untranslate_data #:nodoc:
      [@@gettext_untranslate, @@gettext_untranslate_columns]
    end

    def self.columns
      unless @columns
	@columns = connection.columns(table_name, "#{name} Columns")
	@columns.each {|column| 
	  column.table_class = self
	  column.primary = column.name == primary_key
	}
      end
      @columns
    end
    
    # call-seq:
    # set_error_message_title(msg)
    #
    # ((*Deprecated*)) 
    # Use ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_title
    # instead.
    #
    # Sets a your own title of error message dialog.
    # * msg: [single_msg, plural_msg]. Usually you need to call this with Nn_().
    # * Returns: [single_msg, plural_msg]
    def self.set_error_message_title(msg, plural_msg = nil)
      ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_title(msg, plural_msg)
    end

    # call-seq:
    # set_error_message_explanation(msg)
    #
    # ((*Deprecated*)) 
    # Use ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_explanation
    # instead.
    #
    # Sets a your own explanation of the error message dialog.
    # * msg: [single_msg, plural_msg]. Usually you need to call this with Nn_().
    # * Returns: [single_msg, plural_msg]
    def self.set_error_message_explanation(msg, plural_msg = nil)
      ActionView::Helpers::ActiveRecordHelper::L10n.set_error_message_explanation(msg, plural_msg)
    end
  end
  
  # activerecord-1.14.3/lib/active_record/validations.rb
  class Errors #:nodoc:
    include GetText

    RE_FIELD_NAME = /%\{fn\}/  #:nodoc:
    
    # You need to define this here, because this values will be updated by application.
    default_error_messages.update(
				  :inclusion => N_("%{fn} is not included in the list"),
				  :exclusion => N_("%{fn} is reserved"),
				  :invalid => N_("%{fn} is invalid"),
				  :confirmation => N_("%{fn} doesn't match confirmation"),
				  :accepted  => N_("%{fn} must be accepted"),
				  :empty => N_("%{fn} can't be empty"),
				  :blank => N_("%{fn} can't be blank"),
				  :too_long => N_("%{fn} is too long (max is %d characters)"),  
				  :too_short => N_("%{fn} is too short (min is %d characters)"), 
				  :wrong_length => N_("%{fn} is the wrong length (should be %d characters)"),
				  :taken => N_("%{fn} has already been taken"),
				  :not_a_number => N_("%{fn} is not a number")
				  )
    @@default_error_messages_d = {
      default_error_messages[:too_long] => /#{Regexp.escape(default_error_messages[:too_long]).sub(/%d/, '(\d+)')}/,
      default_error_messages[:too_short] =>/#{Regexp.escape(default_error_messages[:too_short]).sub(/%d/, '(\d+)')}/,
      default_error_messages[:wrong_length] =>/#{Regexp.escape(default_error_messages[:wrong_length]).sub(/%d/, '(\d+)')}/,
    }
    cattr_accessor :default_error_messages_d

    def localize_error_messages # :nodoc:
      errors = {}
      @errors.each_key do |attr|
        @errors[attr].each do |msg|
          next if msg.nil?
          custom_msg = nil
          #Ugly but... :-<
          @@default_error_messages_d.merge(@base.custom_error_messages_d).each do |key, regexp|
            if regexp =~ msg
              custom_msg = @base.gettext(key)
              custom_msg = _(msg) if custom_msg == msg 
	      custom_msg = _(custom_msg) % $1.to_i
	      break
            end
          end
          unless custom_msg
            custom_msg = @base.gettext(msg)
            custom_msg = _(msg) if custom_msg == msg 
          end
          if attr == "base"
            full_message = custom_msg
          elsif  RE_FIELD_NAME =~ custom_msg
            full_message = custom_msg % {:fn => @base.class.human_attribute_name(attr)}
          else
            full_message = @base.class.human_attribute_name(attr) + " " + custom_msg
          end
	  errors[attr] = full_message
        end
      end
      errors
    end

    def on(attribute) #:nodoc:
      errors = localize_error_messages
      if errors[attribute.to_s].nil?
        nil
      elsif errors[attribute.to_s].length == 1
        errors[attribute.to_s].first
      else
        errors[attribute.to_s]
      end
    end

    def full_messages #:nodoc:
      full_messages = []
      errors = localize_error_messages
      errors.each do |attr, msg|
	next if msg.nil?
	full_messages << msg
      end
      full_messages
    end
  end
end


module ActionView #:nodoc:
  class Base #:nodoc:
    alias render_file_without_locale render_file #:nodoc:
    # This provides to find localized template files such as foo_ja.rhtml, foo_ja_JP.rhtml
    # instead of foo.rhtml. If the file isn't found, foo.rhtml is used.
    def render_file(template_path, use_full_path = true, local_assigns = {})
      locale = GetText.locale
      [locale.to_general, locale.to_s, locale.language, Locale.default.language].each do |v|
	localized_path = "#{template_path}_#{v}"
	return render_file_without_locale(localized_path, use_full_path, local_assigns) if file_exists? localized_path
      end
      render_file_without_locale(template_path, use_full_path, local_assigns)
    end
    
  end

  module Helpers  #:nodoc:
    module ActiveRecordHelper #:nodoc: all
      module L10n
	# Separate namespace for textdomain
	include GetText

	bindtextdomain("rails")

	@error_message_title = Nn_("%{num} error prohibited this %{record} from being saved", 
				   "%{num} errors prohibited this %{record} from being saved")
	@error_message_explanation = Nn_("There was a problem with the following field:", 
					 "There were problems with the following fields:")

	module_function
	# call-seq:
	# set_error_message_title(msgs)
	#
	# Sets a your own title of error message dialog.
	# * msgs: [single_msg, plural_msg]. Usually you need to call this with Nn_().
	# * Returns: [single_msg, plural_msg]
	def set_error_message_title(msg, plural_msg = nil)
	  if msg.kind_of? Array
	    single_msg = msg[0]
	    plural_msg = msg[1]
	  else
	    single_msg = msg
	  end
	  @error_message_title = [single_msg, plural_msg]
	end
	
	# call-seq:
	# set_error_message_explanation(msg)
	#
	# Sets a your own explanation of the error message dialog.
	# * msg: [single_msg, plural_msg]. Usually you need to call this with Nn_().
	# * Returns: [single_msg, plural_msg]
	def set_error_message_explanation(msg, plural_msg = nil)
	  if msg.kind_of? Array
	    single_msg = msg[0]
	    plural_msg = msg[1]
	  else
	    single_msg = msg
	  end
	  @error_message_explanation = [single_msg, plural_msg]
	end
	
	def error_message_title #:nodoc:
	  if @error_message_title
	    [_(@error_message_title[0]), _(@error_message_title[1])]
	  else
	    nil
	  end
	end
	def error_message_explanation #:nodoc:
	  if @error_message_explanation
	    [_(@error_message_explanation[0]), _(@error_message_explanation[1])]
	  else
	    nil
	  end
	end

	def render_error_message(object, klass, record, count, options)
	  message_title = @error_message_title
	  message_explanation = @error_message_explanation
	  
	  klass.content_tag("div",
			    klass.content_tag(
					      options[:header_tag] || "h2",
					      n_(message_title, count) % {:num => count, :record => record}
					      ) +
			    klass.content_tag("p", n_(message_explanation, count) % {:num => count}) +
			    klass.content_tag("ul", object.errors.full_messages.collect { |msg| klass.content_tag("li", msg) }),
			    "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
			    )
	end

	def error_messages_for(object, object_name, klass, options = {})
	  options = options.symbolize_keys

	  if object && ! object.errors.empty?
	    count = object.errors.count
	    record = ActiveRecord::Base.human_attribute_table_name_for_error(object_name.to_s)
	    render_error_message(object, klass, record, count, options)
	  end
	end
      end
      def error_messages_for(object_name, options = {})
	object = instance_variable_get("@#{object_name}")
	L10n.error_messages_for(object, object_name, self, options)
      end
    end

    module DateHelper #:nodoc: all
      include GetText
      alias distance_of_time_in_words_without_locale distance_of_time_in_words #:nodoc:

      bindtextdomain("rails")

      # This is FAKE constant. The messages are found by rgettext as the msgid. 
      MESSAGESS = [N_('less than 5 seconds'), N_('less than 10 seconds'), N_('less than 20 seconds'),
	N_('half a minute'), N_('less than a minute')]
      MINUTES = [/^(\d+) minutes?$/, Nn_('1 minute', '%{num} minutes')]
      HOURS   = [/^about (\d+) hours?$/, Nn_('about 1 hour', 'about %{num} hours')]
      DAYS    = [/^(\d+) days?$/, Nn_('1 day', '%{num} days')]

      def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
	msg = distance_of_time_in_words_without_locale(from_time, to_time, include_seconds)
	match = false
	[MINUTES, HOURS, DAYS].each do |regexp, nn|
	  if regexp =~ msg
	    match = true
	    msg = n_(nn, $1.to_i) % {:num => $1}
	    break
	  end
	end
	match ? msg : _(msg)
      end
    end
  end
end

if defined? ActionMailer
  module ActionMailer #:nodoc:
    class Base #:nodoc:
      helper GetText::Rails
      include GetText::Rails 
      extend GetText::Rails
      
      alias :_create! :create! #:nodoc:
      
      def base64(text, charset="iso-2022-jp", convert=true)
	if convert
	  if charset == "iso-2022-jp"
	    text = NKF.nkf('-j -m0', text)
        end
	end
	text = TMail::Base64.folding_encode(text)
	"=?#{charset}?B?#{text}?="
      end
      
      def create!(*arg) #:nodoc:
	_create!(*arg)
	if Locale.get.language == "ja"
	  require 'nkf'
	  @mail.subject = base64(@mail.subject)
	  part = @mail.parts.empty? ? @mail : @mail.parts.first
	  if part.content_type == 'text/plain'
	    part.charset = 'iso-2022-jp'
	    part.body = NKF.nkf('-j', part.body)
	  end
	end
      @mail
      end
    end
  end
end

begin
  Rails::Info.property("GetText version") do 
    GetText::VERSION 
  end
rescue Exception
  $stderr.puts "GetText: #{GetText::VERSION} Rails::Info is not found." if $DEBUG
end


if ::RAILS_ENV == "development"
  GetText::TextDomain.check_mo = true
end
