如何在更改区域设置时保留URL参数?

时间:2017-05-19 09:34:09

标签: ruby-on-rails ruby

在我的Rails应用程序中,我有这种方法,用户可以通过该方法更改其语言环境:

# locales_controller.rb

class LocalesController < ApplicationController

  def change_locale
    new_locale = params[:set_locale]
    if new_locale
      session[:locale] = new_locale
      url_hash = Rails.application.routes.recognize_path URI(request.referer).path
      url_hash[:locale] = new_locale
      redirect_to url_hash
    end
  end

end
# routes.rb

MyApp::Application.routes.draw do

  scope "(:locale)", locale: /#{DEFINED_LANGUAGES.join("|")}/ do

    get 'change_locale', :to => 'locales#change_locale'

    ...

end
# application_controller.rb

class ApplicationController < ActionController::Base

  before_action :set_locale

  def set_locale
    I18n.locale = params[:locale] || session[:locale] || extract_locale_from_accept_language_header || I18n.default_locale
    session[:locale] = I18n.locale
  end

  def extract_locale_from_accept_language_header
    http_accept_language.compatible_language_from(DEFINED_LANGUAGES) # => e.g. 'de'
  end

  ...

end
# application_helper.rb

def locale_switcher
  form_tag url_for(:controller => 'locales', :action => 'change_locale'), :method => 'get', :id => 'locale_switcher' do
    select_tag 'set_locale', options_for_select(LANGUAGES, I18n.locale.to_s)
  end
end

不幸的是,当用户选择另一个区域设置时,此时URL中存在的所有其他参数都将丢失。有办法规避吗?

1 个答案:

答案 0 :(得分:2)

目前,您的区域设置切换器会将所需的区域设置发布到新路线。这使得很难将用户重定向回发布请求的页面,同时更改URL的部分内容。

此外,无法使表单操作指向包含查询参数的URL,并使此表单通过get方法发送其内容。表单submit只会删除所有现有查询参数,只添加在表单中定义的键/值对。

要解决这个问题,我们需要做两件事:

  1. 将表单发送到当前网址而不是专用的区域设置控制器
  2. 将所有现有查询参数作为隐藏标记复制到表单
  3. 为了避免在Rails帮助程序中嵌套内容标记中使用已清理的HTML,我建议在应用程序中添加一个新的部分。请注意,我内联onchange JavaScript以删除外部依赖项。

    # in app/views/shared/_locale_switcher.html.erb
    <%= form_tag url_for(params), method: 'get' do %>
      <% request.query_parameters.each do |key, value| %>
        <%= hidden_field_tag(key, value)
      <% end %>
    
      <%= select_tag(:new_locale, 
                     options_for_select(LANGUAGES, I18n.locale.to_s), 
                     onchange: 'this.form.submit()') %>
    <% end %>
    

    您可以使用以下行将新的locale_switcher部分呈现到每个其他视图或布局文件中:

    <%= render 'shared/locale_switcher' %>
    

    最后一步:向before_action添加ApplicationContoller方法,如果新的和当前的区域设置不匹配,则触发重定向:

    before_action :check_for_locale_change
    before_action :set_locale
    
    def check_for_locale_change
      new_locale = params[:new_locale]
    
      if new_locale.present? && new_locale != params[:locale]
        redirect_to url_for(params.merge(locale: new_locale, set_locale: nil))
      end
    end
    
    def set_locale
      I18n.locale = params[:locale] || session[:locale] || extract_locale_from_accept_language_header || I18n.default_locale
      session[:locale] = I18n.locale
    end