# Copyright (c) 2005-2006 David Barri require 'gloc' module ActionController #:nodoc: class Base #:nodoc: include GLoc end module Filters #:nodoc: module ClassMethods # This filter attempts to auto-detect the clients desired language. # It first checks the params, then a cookie and then the HTTP_ACCEPT_LANGUAGE # request header. If a language is found to match or be similar to a currently # valid language, then it sets the current_language of the controller. # # class ExampleController < ApplicationController # set_language :en # autodetect_language_filter :except => 'monkey', :on_no_lang => :lang_not_autodetected_callback # autodetect_language_filter :only => 'monkey', :check_cookie => 'monkey_lang', :check_accept_header => false # ... # def lang_not_autodetected_callback # redirect_to somewhere # end # end # # The args for this filter are exactly the same the arguments of # before_filter with the following exceptions: # * :check_params -- If false, then params will not be checked for a language. # If a String, then this will value will be used as the name of the param. # * :check_cookie -- If false, then the cookie will not be checked for a language. # If a String, then this will value will be used as the name of the cookie. # * :check_accept_header -- If false, then HTTP_ACCEPT_LANGUAGE will not be checked for a language. # * :on_set_lang -- You can specify the name of a callback function to be called when the language # is successfully detected and set. The param must be a Symbol or a String which is the name of the function. # The callback function must accept one argument (the language) and must be instance level. # * :on_no_lang -- You can specify the name of a callback function to be called when the language # couldn't be detected automatically. The param must be a Symbol or a String which is the name of the function. # The callback function must be instance level. # # You override the default names of the param or cookie by calling GLoc.set_config :default_param_name => 'new_param_name' # and GLoc.set_config :default_cookie_name => 'new_cookie_name'. def autodetect_language_filter(*args) options= args.last.is_a?(Hash) ? args.last : {} x= 'Proc.new { |c| l= nil;' # :check_params unless (v= options.delete(:check_params)) == false name= v ? ":#{v}" : 'GLoc.get_config(:default_param_name)' x << "l ||= GLoc.similar_language(c.params[#{name}]);" end # :check_cookie unless (v= options.delete(:check_cookie)) == false name= v ? ":#{v}" : 'GLoc.get_config(:default_cookie_name)' x << "l ||= GLoc.similar_language(c.send(:cookies)[#{name}]);" end # :check_accept_header unless options.delete(:check_accept_header) == false x << %< unless l a= c.request.env['HTTP_ACCEPT_LANGUAGE'].split(/,|;/) rescue nil a.each {|x| l ||= GLoc.similar_language(x)} if a end; > end # Set language x << 'ret= true;' x << 'if l; c.set_language(l); c.headers[\'Content-Language\']= l.to_s; ' if options.has_key?(:on_set_lang) x << "ret= c.#{options.delete(:on_set_lang)}(l);" end if options.has_key?(:on_no_lang) x << "else; ret= c.#{options.delete(:on_no_lang)};" end x << 'end; ret }' # Create filter block= eval x before_filter(*args, &block) end end end end # ============================================================================== module ActionMailer #:nodoc: # In addition to including GLoc, render_message is also overridden so # that mail templates contain the current language at the end of the file. # Eg. deliver_hello will render hello_en.rhtml. class Base include GLoc private alias :render_message_without_gloc :render_message def render_message(method_name, body) template = File.exist?("#{template_path}/#{method_name}_#{current_language}.rhtml") ? "#{method_name}_#{current_language}" : "#{method_name}" render_message_without_gloc(template, body) end end end # ============================================================================== module ActionView #:nodoc: # initialize is overridden so that new instances of this class inherit # the current language of the controller. class Base include GLoc alias :initialize_without_gloc :initialize def initialize(base_path = nil, assigns_for_first_render = {}, controller = nil) initialize_without_gloc(base_path, assigns_for_first_render, controller) set_language controller.current_language unless controller.nil? end end module Helpers #:nodoc: class InstanceTag include GLoc # Inherits the current language from the template object. def current_language @template_object.current_language end end end end # ============================================================================== module ActiveRecord #:nodoc: class Base #:nodoc: include GLoc end # class Errors # include GLoc # alias :add_without_gloc :add # # The GLoc version of this method provides two extra features # # * If msg is a string, it will be considered a GLoc string key. # # * If msg is an array, the first element will be considered # # the string and the remaining elements will be considered arguments for the # # string. Eg. ['Hi %s.','John'] # def add(attribute, msg= @@default_error_messages[:invalid]) # if msg.is_a?(Array) # args= msg.clone # msg= args.shift # args= nil if args.empty? # end # msg= ltry(msg) # msg= msg % args unless args.nil? # add_without_gloc(attribute, msg) # end # # Inherits the current language from the base record. # def current_language # @base.current_language # end # end module Validations #:nodoc: module ClassMethods # The default Rails version of this function creates an error message and then # passes it to ActiveRecord.Errors. # The GLoc version of this method, sends an array to ActiveRecord.Errors that will # be turned into a string by ActiveRecord.Errors which in turn allows for the message # of this validation function to be a GLoc string key. def validates_length_of(*attrs) # Merge given options with defaults. options = { :too_long => ActiveRecord::Errors.default_error_messages[:too_long], :too_short => ActiveRecord::Errors.default_error_messages[:too_short], :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length] }.merge(DEFAULT_VALIDATION_OPTIONS) options.update(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash) # Ensure that one and only one range option is specified. range_options = ALL_RANGE_OPTIONS & options.keys case range_options.size when 0 raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.' when 1 # Valid number of options; do nothing. else raise ArgumentError, 'Too many range options specified. Choose only one.' end # Get range option and value. option = range_options.first option_value = options[range_options.first] case option when :within, :in raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range) too_short = [options[:too_short] , option_value.begin] too_long = [options[:too_long] , option_value.end ] validates_each(attrs, options) do |record, attr, value| if value.nil? or value.split(//).size < option_value.begin record.errors.add(attr, too_short) elsif value.split(//).size > option_value.end record.errors.add(attr, too_long) end end when :is, :minimum, :maximum raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0 # Declare different validations per option. validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" } message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long } message = [(options[:message] || options[message_options[option]]) , option_value] validates_each(attrs, options) do |record, attr, value| if value.kind_of?(String) record.errors.add(attr, message) unless !value.nil? and value.split(//).size.method(validity_checks[option])[option_value] else record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value] end end end end alias_method :validates_size_of, :validates_length_of end end end # ============================================================================== module ApplicationHelper #:nodoc: include GLoc end