class Utopia::Localization

A middleware which attempts to find localized content.

Constants

CURRENT_LOCALE_KEY
HTTP_ACCEPT_LANGUAGE
LOCALIZATION_KEY
RESOURCE_NOT_FOUND

Attributes

all_locales[R]
default_locale[R]

Public Class Methods

[](request) click to toggle source
# File lib/utopia/localization.rb, line 62
def self.[] request
        Wrapper.new(request.env)
end
new(app, locales:, default_locale: nil, default_locales: nil, hosts: {}, ignore: []) click to toggle source

@param locales [Array<String>] An array of all supported locales. @param default_locale [String] The default locale if none is provided. @param default_locales [String] The locales to try in order if none is provided. @param hosts [Hash<Pattern, String>] Specify a mapping of the HTTP_HOST header to a given locale. @param ignore [Array<Pattern>] A list of patterns matched against PATH_INFO which will not be localized.

# File lib/utopia/localization.rb, line 77
def initialize(app, locales:, default_locale: nil, default_locales: nil, hosts: {}, ignore: [])
        @app = app
        
        @all_locales = HTTP::Accept::Languages::Locales.new(locales)
        
        # Locales here are represented as an array of strings, e.g. ['en', 'ja', 'cn', 'de'] and are used in order if no locale is specified by the user.
        unless @default_locales = default_locales
                if default_locale
                        @default_locales = [default_locale, nil]
                else
                        # We append nil, i.e. no localization.
                        @default_locales = @all_locales.names + [nil]
                end
        end
        
        @default_locale = default_locale || @default_locales.first
        
        unless @default_locales.include? @default_locale
                @default_locales.unshift(@default_locale)
        end
        
        # Select a localization based on a request host name:
        @hosts = hosts
        
        @ignore = ignore || options[:nonlocalized]
        
        @methods = methods
end

Public Instance Methods

browser_preferred_locales(env) click to toggle source
# File lib/utopia/localization.rb, line 167
def browser_preferred_locales(env)
        accept_languages = env[HTTP_ACCEPT_LANGUAGE]
        
        # No user prefered languages:
        return [] unless accept_languages
        
        # Extract the ordered list of languages:
        languages = HTTP::Accept::Languages.parse(accept_languages)
        
        # Returns available languages based on the order languages:
        return @all_locales & languages
rescue HTTP::Accept::ParseError
        # If we fail to parse the browser Accept-Language header, we ignore it (silently).
        return []
end
call(env) click to toggle source
# File lib/utopia/localization.rb, line 207
def call(env)
        # Pass the request through if it shouldn't be localized:
        return @app.call(env) unless localized?(env)
        
        env[LOCALIZATION_KEY] = self
        
        response = nil
        
        # We have a non-localized request, but there might be a localized resource. We return the best localization possible:
        preferred_locales(env) do |localized_env|
                # puts "Trying locale: #{localized_env[CURRENT_LOCALE_KEY]}: #{localized_env[Rack::PATH_INFO]}..."
                
                response = @app.call(localized_env)
                
                break unless response[0] >= 400
        end
        
        return vary(env, response)
end
freeze() click to toggle source
Calls superclass method
# File lib/utopia/localization.rb, line 106
def freeze
        return self if frozen?
        
        @all_locales.freeze
        @default_locales.freeze
        @default_locale.freeze
        @hosts.freeze
        @ignore.freeze
        
        super
end
host_preferred_locales(env) { |locale| ... } click to toggle source
# File lib/utopia/localization.rb, line 147
def host_preferred_locales(env)
        http_host = env[Rack::HTTP_HOST]
        
        # Yield all hosts which match the incoming http_host:
        @hosts.each do |pattern, locale|
                yield locale if http_host[pattern]
        end
end
localized?(env) click to toggle source
# File lib/utopia/localization.rb, line 183
def localized?(env)
        # Ignore requests which match the ignored paths:
        path_info = env[Rack::PATH_INFO]
        return false if @ignore.any? { |pattern| path_info[pattern] != nil }
        
        return true
end
preferred_locales(env) { |merge(CURRENT_LOCALE_KEY => locale)| ... } click to toggle source
# File lib/utopia/localization.rb, line 121
def preferred_locales(env)
        return to_enum(:preferred_locales, env) unless block_given?
        
        # Keep track of what locales have been tried:
        locales = Set.new
        
        host_preferred_locales(env) do |locale|
                yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
        end
        
        request_preferred_locale(env) do |locale, path|
                # We have extracted a locale from the path, so from this point on we should use the updated path:
                env = env.merge(Rack::PATH_INFO => path.to_s)
                
                yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
        end
        
        browser_preferred_locales(env).each do |locale|
                yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
        end
        
        @default_locales.each do |locale|
                yield env.merge(CURRENT_LOCALE_KEY => locale) if locales.add? locale
        end
end
request_preferred_locale(env) { |request_locale, path| ... } click to toggle source
# File lib/utopia/localization.rb, line 156
def request_preferred_locale(env)
        path = Path[env[Rack::PATH_INFO]]
        
        if request_locale = @all_locales.patterns[path.first]
                # Remove the localization prefix:
                path.delete_at(0)
                
                yield request_locale, path
        end
end
vary(env, response) click to toggle source

Set the Vary: header on the response to indicate that this response should include the header in the cache key.

# File lib/utopia/localization.rb, line 192
def vary(env, response)
        headers = response[1].to_a
        
        # This response was based on the Accept-Language header:
        headers << ['Vary', 'Accept-Language']
        
        # Althought this header is generally not supported, we supply it anyway as it is useful for debugging:
        if locale = env[CURRENT_LOCALE_KEY]
                # Set the Content-Location to point to the localized URI as requested:
                headers['Content-Location'] = "/#{locale}" + env[Rack::PATH_INFO]
        end
        
        return response
end