はじめに
先日、Railsのerbテンプレートのページに必要な部分だけreact_railsを使って、react化する機会がありました。
asset_pipelineを使用しないでwebpackerだけでi18n-js
というライブラリを使用したかったのですが、案外まとまっていなかったのでまとめます。
少しでも、これからこの実装を行う人の助けになれば嬉しいです。また、この部分に関して、それほど知識があるわけではないので、フィードバックお待ちしております!
実際の動きはこんな感じになります。一番上の曜日の部分がreact_componentによるもの、仕様言語はという部分はRailsのerbによるものとなっております。
今回の環境(2019/05/09時点)
- Ruby 2.6.2
- Rails5.2.3
- Yarn1.6.0
- MySQL 5.7.25
翻訳で使用する言語は日本語
と英語
といった構成になっています。
手順
Rails new
rails new をします。
$ rails new i18n-sandbox -d mysql
必要なGemの追加
Gemfileに webpacker
,i18n-js
を追加します。
gem 'webpacker'
gem 'react-rails'
gem 'i18n-js'
react_componentの作成
bundle installをして、react_railsの設定を行います。その後、言語切り替えを試すためのサンプルのコンポーネントを作成します。
$ bundle install
$ rails webpacker:install
$ rails webpacker:install:react
$ rails g react:install
$ rails g react:component HelloWorld greeting:string
これを行うと、次のようなファイルができます。
import React from "react"
import PropTypes from "prop-types"
class HelloWorld extends React.Component {
render () {
return (
<React.Fragment>
Greating: {this.props.greating}
</React.Fragment>
);
}
}
HelloWorld.propTypes = {
greating: PropTypes.string
};
export default HelloWorld
React componentを表示
適当にコントローラとviewファイルを作成し、rootを設定します。
$ rails g controller posts
$ touch app/views/posts/index.html.erb
作成したviewファイルから、先ほどのreact componentを呼び出します。
<%= react_component("HelloWorld", { greeting: "Hello" }) %>
ルーティングを設定します。
root 'posts#index'
application.html.erbにwebpackerによってコンパイルされたJavascriptファイルを読み込むような設定を追加します。
<%= javascript_pack_tag 'application' %>
サーバーの立ち上げ
$ rails s
$ ./bin/webpack-dev-server
これらを立ち上げて、 localhost:3000にアクセスすると、Hello
の表示が確認できるはずです。
i18n-js をwebpackerで動かす設定
さて、ここから、i18n-jsをwebpacker上で動かす設定をしていきます。このGistがとても参考になりました。
$ yarn add i18n-js
$ rails webpacker:install:erb
$ mkdir -p app/javascript/src/i18n-js
$ touch app/javascript/src/i18n-js/index.js.erb
上で作ったindex.js.erbファイルを次のように編集します。
コメントアウトの部分は、RailsのI18nと同様にconfig/locales/に配置した翻訳ファイルを参照するために追加しています。i18n-js/issue514が参考になりました。
import I18n from "i18n-js"
/* rails-erb-loader-dependencies ./../config/locales/ */
I18n.translations = <%= I18n::JS.filtered_translations.to_json %>;
export default I18n
これで、react_component側でI18n.t('翻訳ファイルのキー')
と呼び出すことができるようになりました。
この後、RailsのI18nとの連携をしていきます。
I18nの設定(Rails側)
defaultのlocaleと使用するlocaleを指定する
今回のサンプルでは、日本語と英語のみを使用し、基本の言語は、日本語という指定があったため、その設定をします。config/application.rbに次のように編集します。
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module I18nSandbox
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
# I18n `追記部分`
config.i18n.default_locale = :ja
config.i18n.available_locales = %i(ja en)
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
end
end
本当に必要な言語のみが使用できるようになったか不安な方は、コンソールを立ち上げて確認することができます。
$ rails c
Running via Spring preloader in process 92876
Loading development environment (Rails 5.2.3)
irb(main):001:0> I18n.default_locale
=> :ja
irb(main):002:0> I18n.available_locales
=> [:ja, :en]
言語の切り替え
RailsGuidesを参考にして、localeごとのURLを設定します。
scope '(:locale)', locale: /#{I18n.available_locales.map(&:to_s).join('|')}/ do
root 'posts#index'
end
この記述によって、http://localhost:3000/ja/posts
とhttp://localhost:3000/en/posts
というURLが生成され、URL単位で言語を管理することが可能になります。
コントローラで言語を切り替えるロジックを追加する。
class ApplicationController < ActionController::Base
before_action :set_locale
def set_locale
locale_param = Rails.configuration.i18n.available_locales&.include?(params[:locale].to_s.to_sym) && params[:locale]
I18n.locale = locale_param || cookies[:locale] || I18n.default_locale
cookies[:locale] = I18n.locale.to_s
@locale_path = I18n.locale==I18n.default_locale ? nil : I18n.locale
end
実際に言語を切り替えるボタンを追加する
<% I18n.available_locales.reject{|l| l == I18n.locale.to_sym}.each do |locale| %>
<p>使用言語は<%= I18n.locale %>です</p>
<%= link_to "#{locale}に切り替える", url_for(params.permit().merge(locale: (locale == :ja ? :ja : locale))) %>
<% end %>
翻訳ファイルを用意する
ここから、ja.ymlをダウンロードしてきて、config/locales/ja.yml
に追記します。
実際にreact_componentにlocaleを渡して完成です。
import React from "react"
import PropTypes from "prop-types"
import I18n from 'src/i18n-js/index.js.erb'
class HelloWorld extends React.Component {
constructor(props) {
super(props);
I18n.locale = this.props.locale;
}
render () {
console.log(I18n.t('date.abbr_day_names'));
return (
<React.Fragment>
<p>{I18n.t('date.abbr_day_names')}</p>
Greating: {this.props.greating}
</React.Fragment>
);
}
}
HelloWorld.propTypes = {
greating: PropTypes.string
};
export default HelloWorld
<%= react_component("HelloWorld", { locale: I18n.locale, greeting: "Hello" }) %>
おわりに
i18n-jsをasset_pipelineに乗っ取らないでwebpackerだけで実装するのにはかなり苦労しました。そんな中、本文中のこのGistとi18n-js/issues514にはかなり助けられました。自分より前に詰まっていた先人たちに感謝をしつつ、この文章が今後誰かの助けになることを願っております。