Module: Controllers::Localizable
- Extended by:
- ActiveSupport::Concern
- Included in:
- ApplicationController
- Defined in:
- app/concerns/controllers/localizable.rb
Instance Method Summary collapse
- #cloudflare_country_locale ⇒ Object
- #determine_request_locale ⇒ Object
- #geocoder_locale(location = nil) ⇒ Object
-
#guest_user_locale_check(param_locale) ⇒ Object
Check if the guest user's locale matches the current set locale If not then we will attempt to switch catalog Method returns the locale to use for the guest user If a change of catalog was not possible for instance the returned value will be the original locale otherwise it will be the new locale.
-
#locale_optional_www_auth_path? ⇒ Boolean
True when the request uses www locale-optional auth paths without a /en-US/ prefix.
- #param_locale ⇒ Object
-
#set_locale ⇒ Object
Phase 2: User-aware locale finalization (runs AFTER init_current_user) Handles logged-in user locale preferences, guest locale switching, and redirects.
-
#set_request_locale ⇒ Object
Phase 1: Set basic locale from request before user initialization This runs BEFORE init_current_user so guest user creation can use I18n.locale Only sets I18n.locale based on URL params, Cloudflare geo, or geocoder Does NOT do any user-specific logic or redirects.
- #skip_localization? ⇒ Boolean
- #warmlyyours_ip_locale ⇒ Object
Instance Method Details
#cloudflare_country_locale ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 |
# File 'app/concerns/controllers/localizable.rb', line 136 def cloudflare_country_locale return nil unless request.env['HTTP_CF_IPCOUNTRY'].present? return nil if request.env['HTTP_CF_IPCOUNTRY'] == 'XX' case request.env['HTTP_CF_IPCOUNTRY'].upcase when 'CA', 'CANADA' :'en-CA' else :'en-US' end end |
#determine_request_locale ⇒ Object
177 178 179 180 181 182 183 184 185 |
# File 'app/concerns/controllers/localizable.rb', line 177 def determine_request_locale # In case one day you have french vs english in canada you might need this. # browser_locale = request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}-[A-Z]{2}/).first.try(:to_sym) if request.env['HTTP_ACCEPT_LANGUAGE'].present? request_locale = param_locale request_locale ||= warmlyyours_ip_locale if is_crm_request? # Ramie 072622 just skip this nanny featurefor non-CRM requests request_locale ||= cloudflare_country_locale request_locale ||= geocoder_locale request_locale end |
#geocoder_locale(location = nil) ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'app/concerns/controllers/localizable.rb', line 148 def geocoder_locale(location = nil) location ||= request.location return unless location country = location.country return unless country case country.upcase when 'CA', 'CANADA' :'en-CA' else :'en-US' end end |
#guest_user_locale_check(param_locale) ⇒ Object
Check if the guest user's locale matches the current set locale
If not then we will attempt to switch catalog
Method returns the locale to use for the guest user
If a change of catalog was not possible for instance
the returned value will be the original locale
otherwise it will be the new locale
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'app/concerns/controllers/localizable.rb', line 193 def guest_user_locale_check(param_locale) # Only check if context user is a guest and if its not an exception # This method is just for party typpes responding to guest logger.tagged('guest_user_locale_check') do # For bots, always honor the URL locale if provided to avoid cross-locale locking if bot_request? && param_locale.present? logger.debug "Bot request detected, honoring param locale #{param_locale}" return param_locale end # Never change catalog for non-guest active customers (e.g., Prospects). Always keep their # existing locale and let set_locale redirect to the matching URL. unless @context_user.state.to_s == 'guest' logger.debug "Non-guest user (state=#{@context_user.state}) detected; preserving locale #{@context_user.locale}" return @context_user.locale end unless @context_user.can_change_country? logger.debug "Context user can't change country, returning #{@context_user.locale}" return @context_user.locale end unless param_locale.present? logger.debug "No param locale set, returning guest's native locale" return @context_user.locale end param_locale_catalog = Catalog.locale_to_catalog(param_locale) if Catalog.locale_to_catalog(@context_user.locale) != param_locale_catalog logger.debug "Guest wants a different locale/catalog than previously set, guest user locale: #{@context_user.locale}, requested locale #{param_locale}" # Guest user gets converted, others get redirected new_catalog = param_locale_catalog result = @context_user.change_catalog(new_catalog) if result.catalog_assigned? logger.debug "Successfully switched to requested catalog #{new_catalog.id}" @context_user.reload else logger.error "Failed to switch to requested catalog #{new_catalog.id} failed #{result..to_sentence}" flash[:warning] = "Your account could not be switched to #{param_locale}. #{result..to_sentence}" end end return @context_user.locale end end |
#locale_optional_www_auth_path? ⇒ Boolean
True when the request uses www locale-optional auth paths without a /en-US/ prefix.
256 257 258 259 260 261 262 263 264 |
# File 'app/concerns/controllers/localizable.rb', line 256 def locale_optional_www_auth_path? return false unless is_www_request? return false if params[:locale].present? # /masquerade/* is whitelisted here so the signed, single-use handoff token # is NOT re-issued through a locale redirect, which would cause the token # to be consumed twice (failing the second request with a ReplayError). request.path =~ %r{\A/(accounts|customer_sessions|masquerade)(/|\z)}i end |
#param_locale ⇒ Object
173 174 175 |
# File 'app/concerns/controllers/localizable.rb', line 173 def param_locale params[:locale].presence&.to_sym end |
#set_locale ⇒ Object
Phase 2: User-aware locale finalization (runs AFTER init_current_user)
Handles logged-in user locale preferences, guest locale switching, and redirects
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'app/concerns/controllers/localizable.rb', line 44 def set_locale logger.tagged 'set_locale' do return true if skip_localization? logger.debug "request.fullpath: #{request.fullpath}" logger.debug "params[:locale]: #{params[:locale]}" valid_locales = LocaleUtility.service_locales request_locale = determine_request_locale if valid_locales.include?(request_locale) logger.debug "Request locale valid: #{request_locale || 'undeclared'}" param_locale = params[:locale]&.to_sym else logger.error "Request locale invalid: #{request_locale || 'undeclared'}" request_locale = nil param_locale = nil end # If URL carries a locale, honor it for bots/guests to keep server locale aligned with the path # This prevents cases where a previously stored guest locale forces CAD on en-US URLs for crawlers # # Use the already-validated `param_locale` (nil when params[:locale] # was absent or rejected by the valid_locales check above) so an # unsupported URL locale doesn't bypass that gate and reach the # locale writers. if param_locale.present? && (bot_request? || current_user.nil?) apply_locale(param_locale) end # Is there a current logged in user? we always use that user's locale and there's no switching reason_for_switch = nil if current_user previous_context_user_locale = current_user.locale apply_locale(current_user.locale) # which is catalog based logger.debug "Current user detected with locale #{current_user.locale}, setting locale to #{I18n.locale}" reason_for_switch = "params locale #{param_locale} is different from the logged in user's current_user.locale of #{current_user.locale} which must stay in effect" if previous_context_user_locale&.to_sym != param_locale # Guest user or previously logged in user elsif @context_user && param_locale.present? previous_context_user_locale = @context_user.locale apply_locale(guest_user_locale_check(param_locale)) # Check if this locale switch is possible reason_for_switch = "params locale #{param_locale} is different from the user's previously set locale/catalog #{previous_context_user_locale}" if previous_context_user_locale&.to_sym != param_locale # Unlikely but a visit without a context user elsif request_locale apply_locale(request_locale) else # Fallback default when all else fails since most users are US apply_locale(:'en-US') end logger.debug "Final Locale: #{I18n.locale}" # Suppress reason logging for CRM requests reason_for_switch = nil if is_crm_request? logger.debug "User locale switched to #{I18n.locale}, reason: #{reason_for_switch}" if reason_for_switch.present? return true if request.xhr? # No redirection needed # CRM locale is driven by the logged-in user, not the URL. CRM routes # have no locale prefix, so param_locale is always nil there. Skip the # locale-mismatch redirect entirely for CRM to avoid an infinite loop. return true if is_crm_request? # Devise routes live under scope '(/:locale)' in www routes, so /accounts/… and # /en-US/accounts/… are both valid. When the locale segment is omitted, # params[:locale] is nil while I18n.locale is still set (e.g. en-US) from # set_request_locale — the mismatch branch below would redirect POST # /accounts/password to GET /en-US/accounts/password and halt before the action # (no email sent, CMS 404 on follow-up). return true if locale_optional_www_auth_path? if param_locale == I18n.locale # This instance variable is a default country switcher, in some circumstances, another controller action # might want to modify this, for instance, if a product is not available in the other locale, then you # would want to change the href to a generic product page. @country_switch_options = { 'USA' => { href: cms_link(request.url, :'en-US', strip_params: true), locale: :'en-US', flag_image_src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', svg_src: 'svgs/custom/us.svg' }, 'Canada' => { href: cms_link(request.url, :'en-CA', strip_params: true), locale: :'en-CA', flag_image_src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', svg_src: 'svgs/custom/ca.svg' } } return true else logger.debug "User locale switched to #{I18n.locale}" # flash[:info] = "Your locale was set to #{I18n.locale}" redirect_to(cms_link(request.fullpath)) # The locale redirection should be a 302 by default not 301 end end end |
#set_request_locale ⇒ Object
Phase 1: Set basic locale from request before user initialization
This runs BEFORE init_current_user so guest user creation can use I18n.locale
Only sets I18n.locale based on URL params, Cloudflare geo, or geocoder
Does NOT do any user-specific logic or redirects
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'app/concerns/controllers/localizable.rb', line 10 def set_request_locale # Unconditional baseline write BEFORE any early return. Guarantees every # request that touches this concern starts at a known-good locale, # regardless of: # - what the previous request on this Puma thread set (Thread.current # leak), # - whether CurrentScope's before_reset hook fired (it always does # between requests, but defending in depth here is cheap), # - whether skip_localization? returns true and bails out below # (the API path needs a sane I18n.locale too if any code reads it). apply_locale(:'en-US') return if skip_localization? # Priority: # 1. URL locale param (e.g., /en-US/products) # 2. Cloudflare geo header # 3. Geocoder lookup # 4. Default to en-US locale = param_locale locale ||= cloudflare_country_locale locale ||= geocoder_locale locale ||= :'en-US' # Validate locale before setting valid_locales = LocaleUtility.service_locales locale = :'en-US' unless valid_locales.include?(locale) apply_locale(locale) logger.debug "[set_request_locale] Set I18n.locale to #{I18n.locale}" end |
#skip_localization? ⇒ Boolean
249 250 251 252 253 |
# File 'app/concerns/controllers/localizable.rb', line 249 def skip_localization? request.path =~ %r{^/api} || request.path =~ %r{/accounts/auth/} || request.subdomain =~ /^api/ || request.path =~ %r{\.(js|css|map|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|avif|webp)(\?|$)} || request.path =~ %r{^/dist/|^/packs/|^/assets/|^/media/|^/secure-media/} end |
#warmlyyours_ip_locale ⇒ Object
163 164 165 166 167 168 169 170 171 |
# File 'app/concerns/controllers/localizable.rb', line 163 def warmlyyours_ip_locale if warmlyyours_canada_ip? :'en-CA' elsif warmlyyours_ip? :'en-US' else nil end end |