サイトを多言語化するとき、HTTP_ACCEPT_LANGUAGE
で動的に書き換えるんじゃなくてURLをごと変える設計を採用する場合、Railsでうまいことやれるのかが気になった。
言語や地域の URL に hreflang を使用する - Search Console ヘルプ
前提
仮にこんな感じのルーティングをしてる設定で考えてみる。
config/routes.rb
Rails.application.routes.draw do
scope "(:locale)", locale: /en|ja/ do
resources :articles
end
end
守るべき仕様は、
- canonicalは表示中のURLにlocaleが含まれるなら含み、含まれないなら含まない
x-default
もちゃんと設定する- 下層ページも対応する
- パラメータは維持する
こんな感じ。
だめなパターン
単純に考えてこんな実装にしたかった。
<% I18n.available_locales.each do |locale| %>
<link rel="alternate" href="<%= url_for(**params.symbolize_keys, locale: locale, only_path: false) %>" hreflang="<%= locale %>">
<% end %>
しかしsymbolize_keys
はdeprecatedになってるので使うべきじゃない・・・。
DEPRECATION WARNING: Method symbolize_keys is deprecated and will be removed in Rails 5.1, as `ActionController::Parameters` no longer inherits from hash. Using this deprecated
behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.0.1/classes/ActionController/Parameters.html (called from block in _app_views_layouts_application_html_erb___3051065901541225498_34487080 at /myapp/app/views/layouts/application.html.erb:21)
だし、そもそもpermit
してないActionController::Parameters
を使うのも気持ち悪い。
Viewのyieldを使うパターン
とはいえ下層ページでもパラメータを考慮して設定しなきゃいけないので、各Viewで実装していくしかないんだろうか・・・。
とりあえずこういう実装はどうだろう。
application.html.erb
<% if yield(:alternate_locales).present? %>
<%= yield(:alternate_locales) %>
<% else %>
<link rel="alternate" href="<%= url_for(only_path: false) %>" hreflang="x-default">
<% I18n.available_locales.each do |locale| %>
<link rel="alternate" href="<%= url_for(locale: locale, only_path: false) %>" hreflang="<%= locale %>">
<% end %>
<% end %>
articles/index.html.erb
<% content_for :alternate_locales do %>
<link rel="alternate" href="<%= articles_url(q: @params_article.to_param) %>" hreflang="x-default">
<% I18n.available_locales.each do |locale| %>
<link rel="alternate" href="<%= articles_url(q: @params_article.to_param, locale: locale) %>" hreflang="<%= locale %>">
<% end %>
<% end %>
うーん。
Controllerにメソッドを作るパターン
Viewにやらせるパターンだとちょっと冗長すぎるのでControllerにcanonicalのURLを返す & localeも設定できるようなメソッドを作って、 書き換える必要のあるディレクトリではオーバーライドするのはどうだろう。
application.html.erb
<link rel="canonical" href="<%= canonical(locale: params[:locale]) %>">
<link rel="alternate" href="<%= canonical %>" hreflang="x-default">
<% I18n.available_locales.each do |l| %>
<link rel="alternate" href="<%= canonical(locale: l) %>" hreflang="<%= l %>">
<% end %>
application_controller.rb
helper_method :canonical
def canonical(locale: nil)
url_for(locale: locale, only_path: false)
end
articles_controller.rb
def canonical(locale: nil)
articles_url(q: @params_article.to_param, locale: locale)
end
パッと見イケてそう。
params[:locale]
がちょっと嫌だけど。