=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.33 2006/05/06 14:59:08 mutoh Exp $ =end require 'gettext/cgi' require 'active_record' require 'active_support' module GetText #:nodoc: # = GetText for Ruby on Rails # 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 . module Rails include GetText extend GetText Rails = ::Rails #:nodoc: alias :_bindtextdomain :bindtextdomain #:nodoc: module_function # 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. # * locale: the locale value such as "ja-JP". When the value is nil, # * charset: the charset. Generally UTF-8 is recommanded. # * with_model: false if you want to ignore model support. # # locale is searched the order by this value > "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). # def bindtextdomain(domainname, _cgi = nil, locale = nil, charset = nil, with_model = true) Locale.set_current(nil) # IMPORTANT! current locale should be nil once(default is used). set_cgi(_cgi) if _cgi @gettext_container_domainname = domainname path = File.join(RAILS_ROOT, "locale") _bindtextdomain(domainname, path, locale, charset) bindtextdomain_to(ActiveRecord::Base, domainname) if with_model end def bindtextdomain_to(klass, domainname) #:nodoc: klass.class_eval { textdomain(domainname) 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 def callersrc #:nodoc: @gettext_container_domainname = nil unless defined? @gettext_container_domainname @gettext_container_domainname end # true to use localized templates such as list.rhtml, list_ja.rhtml, list_ja_JP.rhtml. # * setting: true if localized, otherwise false. Default is true. # * Returns: setting def use_localized_templates(setting) @@gettext_use_localized_templates = setting end # true if it use localized templates. See GetText::Rails.use_localized_templates # for more details. # * Returns: true or false. def use_localized_templates? if defined? @@gettext_use_localized_templates @@gettext_use_localized_templates else true end end end end module ActionController #:nodoc: class Base helper GetText::Rails include GetText::Rails extend 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_) #You need to pass CGI object first. else bindtextdomain(@@gettext_domainname, nil, @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. GetText.bindtextdomain("rails") } end # Bind a 'textdomain' to all of the controllers/views/models. Call this instead of GetText.bindtextdomain. # * textdomain: the textdomain # * 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). # # 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, charset = "UTF-8", content_type = "text/html") GetText.output_charset = charset @@gettext_domainname = domainname @@gettext_content_type = content_type end end class TestRequest < AbstractRequest #:nodoc: @cgi = nil def cgi @cgi = CGI.new unless @cgi @cgi end end end module ActiveRecord #:nodoc: 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 class Base include GetText::Rails # Important extend GetText::Rails # Important @@error_message_title = nil @@error_message_explanation = nil # call-seq: # set_error_message_title(msg) # # 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) 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 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_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 self.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 gettext(str) #:nodoc: _(str) end end class Errors #:nodoc: include GetText extend 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 = { :too_long => /#{Regexp.escape(default_error_messages[:too_long]).sub(/%d/, '(\d+)')}/, :too_short => /#{Regexp.escape(default_error_messages[:too_short]).sub(/%d/, '(\d+)')}/, :wrong_length => /#{Regexp.escape(default_error_messages[:wrong_length]).sub(/%d/, '(\d+)')}/, } def full_messages #:nodoc: full_messages = [] @errors.each_key do |attr| @errors[attr].each do |msg| next if msg.nil? #Ugly but... :-< @@default_error_messages_d.each do |key, regexp| if regexp =~ msg msg = _(default_error_messages[key]) % $1.to_i break end end custom_msg = @base.gettext(msg) custom_msg = _(msg) if custom_msg == msg if attr == "base" full_messages << custom_msg elsif RE_FIELD_NAME =~ msg full_messages << custom_msg % {:fn => @base.class.human_attribute_name(attr)} else full_messages << @base.class.human_attribute_name(attr) + " " + custom_msg end end 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 = {}) if GetText::Rails.use_localized_templates? 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 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 extend GetText @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 def render_error_message(object, klass, record, count, options) message_title = object.error_message_title || @error_message_title message_explanation = object.error_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 = {}) textdomain("rails") 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 end end 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 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