react_railsとi18n-jsをwebpackerで動かす

この記事は最終更新日から1年以上が経過しています。

はじめに

先日、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 をします。

terminal
$ rails new i18n-sandbox -d mysql 

必要なGemの追加

Gemfileに webpacker,i18n-js を追加します。

Gemfile
gem 'webpacker'
gem 'react-rails'
gem 'i18n-js'

react_componentの作成

bundle installをして、react_railsの設定を行います。その後、言語切り替えを試すためのサンプルのコンポーネントを作成します。

terminal
$ bundle install
$ rails webpacker:install 
$ rails webpacker:install:react
$ rails g react:install 
$ rails g react:component HelloWorld greeting:string

これを行うと、次のようなファイルができます。

app/javascript/components/HelloWorld.js
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を設定します。

terminal
$ rails g controller posts
$ touch app/views/posts/index.html.erb

作成したviewファイルから、先ほどのreact componentを呼び出します。
ruby:app/views/posts/index.html.erb
<%= react_component("HelloWorld", { greeting: "Hello" }) %>

ルーティングを設定します。

config/routes.rb
root 'posts#index'

application.html.erbにwebpackerによってコンパイルされたJavascriptファイルを読み込むような設定を追加します。
ruby:app/views/layouts/application.html.erb
<%= javascript_pack_tag 'application' %>

サーバーの立ち上げ

terminal
$ rails s
$ ./bin/webpack-dev-server

これらを立ち上げて、 localhost:3000にアクセスすると、Hello の表示が確認できるはずです。

i18n-js をwebpackerで動かす設定

さて、ここから、i18n-jsをwebpacker上で動かす設定をしていきます。このGistがとても参考になりました。

terminal
$ 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が参考になりました。

app/javascript/src/i18n-js/index.js.erb
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に次のように編集します。

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_console
$ 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を設定します。

config/routes.rb
  scope '(:locale)', locale: /#{I18n.available_locales.map(&:to_s).join('|')}/ do
    root 'posts#index'
  end

この記述によって、http://localhost:3000/ja/postshttp://localhost:3000/en/postsというURLが生成され、URL単位で言語を管理することが可能になります。

コントローラで言語を切り替えるロジックを追加する。

app/controllers/application_controller.rb
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

実際に言語を切り替えるボタンを追加する

app/views/posts/index.html.erb
    <% 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を渡して完成です。

app/javascript/components/HelloWorld.js
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
app/views/posts/index.html.erb
<%= react_component("HelloWorld", { locale: I18n.locale, greeting: "Hello" }) %>

おわりに

i18n-jsをasset_pipelineに乗っ取らないでwebpackerだけで実装するのにはかなり苦労しました。そんな中、本文中のこのGisti18n-js/issues514にはかなり助けられました。自分より前に詰まっていた先人たちに感謝をしつつ、この文章が今後誰かの助けになることを願っております。

Atsuyoshi-N
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした