RailsアプリケーションにURLパラメータを使用してロケールを渡すことでI18Nを追加しています。私のURLは http://example.com/en/users と- http://example.com/ar/users (それぞれ英語とアラビア語のロケール用)。
ルートファイルで、:path_prefixオプションを使用してルートを定義しました。
map.resources :users, :path_prefix => '/:locale'
そしてロケールはApplicationControllerで定義されたbefore_filterを使用して設定されています
def set_locale
I18n.locale = params[:locale]
end
また、ApplicationController#default_url_optionsを定義して、アプリケーションによって生成されたすべてのURLにロケールを追加しました。
def default_url_options(options={})
{:locale => I18n.locale}
end
私が欲しいのは、同じページにリンクするが他のロケールにリンクするレイアウトヘッダー(すべてのページに表示される)にリンクを追加することです。
たとえば、アラビア語のロケールを参照している場合、ヘッダーに「英語」リンクを追加すると、現在のページにリダイレクトされ、ロケールが英語に設定されます。これをレールで行う方法はありますか?
これを見つけるのにしばらく時間がかかりましたが、これが私の解決策です:
link_to 'English', url_for( :locale => 'en' )
link_to 'Deutch', url_for( :locale => 'de' )
ここのドキュメントから: http://api.rubyonrails.org/classes/ActionController/Base.html#M000649
新しいURLを生成するときに、現在のリクエストのパラメーターから欠損値が入力される場合があります。たとえば、url_for:action => ‘some_action‘は、期待どおりに現在のコントローラーを保持します。この動作は、:controller、:id、およびルートのパスに配置されているその他のパラメーターを含む、その他のパラメーターにまで及びます。
したがって、url_forを使用すると、デフォルトで現在のリクエストのパラメータが使用されます。コードで必要なパラメータを変更するだけです。この場合、私が変更したのは:localeだけだったので、他はすべて同じままです。
これは「非表示」の:パラメータにも機能することに注意してください。だからあなたが持っている場合:
map.my_map ':locale/my_map', :controller => 'home', :action => 'my_map'
/ en/my_mapページで上記のurl_forを使用すると、URLに「home」が含まれません(つまり、/ en/home/my_map)。ボーナス。
そこで、Railsマジックに依存することなく、より明示的にこれを行う方法を見つけました。
url_for(params.merge({:your_new_parameter => value}))
これはlink_to
で機能するはずです。
すべての処理は、現在のリクエストのパラメータを取得し、それらに新しい目的のハッシュをマージして、そのための新しいURLを作成することです。
異なるロケールの現在のページへのリンク
Rails 4でテスト済み
こんにちは皆さん。しばらく研究した後、私はこのための独自のソリューションを書くことにしました。
link_to 'English', url_for( :locale => 'en' )
link_to 'Deutch', url_for( :locale => 'de' )
これは完璧に機能しますが、以下のようにXSS Vulnerabilityを使用して、URLにパラメータを渡すだけです。
http://localhost:3000/en/about?host=www.fishingsiteorbadurl.com/%23&port=80
または最悪の場合:
http://localhost:3000/en/about?host=%D0%BE%D1%87%D0%B5%D0%BD%D1%8C%D0%BF%D0%BB%D0%BE%D1%85%D0%BE%D0%B9%D1%81%D0%B0%D0%B9%D1%82.%D1%80%D1%84
アプリケーションでこのリンクを通過した後に取得されるURLを確認してください。
My production solution。メソッド「言語を変更」は、リクエストオブジェクトでHTTP_REFERERを使用するだけで、適切なロケールのページにリダイレクトします。 注意してください:URL全体ではなく、パスのみを取得するためのURI.pathメソッド
任意のコントローラーで「言語の変更」メソッドを作成します。
def change_lang
if request.referer.nil?
refer = root_url
else
uri = URI(request.referer)
refer = uri.path
end
lang = params[:lang]
cookies[:locale] = lang
redirect_to refer
end
application_controller.rb
before_action :set_locale
def set_locale
# -- Get lang from cookies or url parameter locale
user_locale = cookies[:locale] || params[:locale]
# -- If present
if user_locale.present?
# -- If it is has 2 symbols
user_locale = user_locale.scan(/[a-zA-Z]{2}/)
else
# -- If no - use default en locale
user_locale = 'en'
end
# -- Check, is this locale available for using.
# Please note: this needed for disable invalid locale warning.
if I18n.available_locales.include?(user_locale[0].to_sym)
I18n.locale = user_locale[0]
else
I18n.locale = "en"
end
end
これをレイアウトに追加します
<%= link_to 'English', change_lang_path('en') %> <%= link_to 'Russian', change_lang_path('ru') %>
config/routes.rb
scope "(:locale)", locale: /[a-zA-Z]{2}/ do
get "change_lang/:lang" => "users#change_lang", :as => "change_lang"
end
Params.mergeやサルパッチソリューションを使用する必要はありません。
私が個人的にそれを解決するために多くの時間を費やしたので、これが役に立てば幸いです。
Request_uriを解析し、パス内のロケールを正規表現で置き換えることができます
さて、ここにヘルパーの例があります。目標を正しく理解していれば
def locale_url(url, locale)
url.gsub(/\/\w*$/, "/#{locale}")
end
url = "http://www.domain.com/products/1/ru" # or request.request_uri
locale = "en"
locale_url(url, locale) #=> "http://www.domain.com/products/1/en"
これは出発点なので、必要なものをいくつか作成できます
DRYと適切なものではないかもしれませんが、私にとっては完全に機能します。ロケールEXのURLだけを置き換えて、指定したすべてのパラメータを読み取ります。 http ://example.com:3000/us/users?t = 123&m = 34 など
def us_link
link_to "US", form_locale_url("/us")
end
def jp_link
link_to "Japan",form_locale_url("/jp")
end
def form_locale_url(locale)
new_url = request.request_uri
new_locale_url = new_us_url = new_jp_url = new_url
if new_url == "/"
new_locale_url.sub!(/\//,locale)
elsif (new_url =~/\/us/) == 0
new_us_url.sub!(/\/us/,locale)
elsif (new_url =~/\/jp/) == 0
new_jp_url.sub!(/\/jp/,locale)
end
end
はるかに迅速な手段-さまざまな場所で変化する多くのパラメーターがある場合に便利です...新しいロケールパラメーターを既存のものにマージする(そして実際には古いロケールパラメーターを強制終了する)アンカータグで混乱を避けます。
<%= link_to "ру", request.params.merge( locale: 'ru' ) %>
しかし、はい、アプリケーションのコンテキストに従って、その時点でパラメーターをホワイトリストに登録する必要があります。
安全にurl_for
を設定すると、only_path: true
を使用して、ロケールをparams
で切り替えることができます。
<%= link_to I18n.t('language_name', locale: I18n.locale), url_for( params.clone.permit!.merge(locale: locale, only_path: true ) %>
.clone
the params
すべてを許可する前に(.permit!
)、強力なパラメーターを他の場所に保存します。私が見つけることができる唯一のより安全なソリューションは、代わりにすべてのパラメータを時間をかけてホワイトリストに登録することです...
堅牢なI18n実装:
locales.rb
初期化子を追加して、サポートするI18n.available_locales
を定義します。
# config/initializers/locales.rb
# Permitted locales available for the application
I18n.available_locales = [:en, :fr]
各言語のロケールファイルにlanguage_name
値を設定します(例:fr.yml
):
fr:
language_name: "Français"
言語を追加すると、このERBにより、一般的にそれらを切り替えることができます。
// app/views/layouts/_languages.html.erb
<span class="languages">
<% I18n.available_locales.each do |locale| %>
<% if I18n.locale == locale %>
<%= link_to I18n.t('language_name', locale: locale), url_for( params.clone.permit!.merge(locale: locale, only_path: true ), {style: "display:none" } %>
<% else %>
<%= link_to I18n.t('language_name', locale: locale), url_for( params.clone.permit!.merge(locale: locale, only_path: true ) %>
<% end %>
<% end %>
</span>
コントローラーについては、ブラウザーのAccept-Language
HTTPヘッダーを検出することにより、ユーザーの正しい言語を自動的に検出します( http_accept_language gemを使用)。
session
Cookieを設定して、リクエスト間でロケールを保持します。
または、必要に応じて、default_url_options
を使用して?locale=
パラメータをアプリのURLに挿入します。私は両方を行います。
コントローラ:
class ApplicationController < ActionController::Base
before_action :set_locale
private
def set_locale
I18n.locale = begin
extract_locale ||
session[:locale] ||
http_accept_language.compatible_language_from(I18n.available_locales) ||
I18n.default_locale
end
session[:locale] = I18n.locale
end
def extract_locale
parsed_locale = params[:locale].dup
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end
def default_url_options
{ locale: I18n.locale }
end
end