Skip to content

Commit 95d8a08

Browse files
committed
Automatically generate javascript files for react-intl when the server starts up.
1 parent 6370447 commit 95d8a08

File tree

8 files changed

+258
-12
lines changed

8 files changed

+258
-12
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ All notable changes to this project's source code will be documented in this fil
44
Contributors: please follow the recommendations outlined at [keepachangelog.com](http://keepachangelog.com/). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare to the latest release version.
55

66
## [Unreleased]
7-
*Please add entries here for your pull requests.*
7+
##### Changed
8+
- Automatically generate __i18n__ javascript files for `react-intl` when the serve starts up. [#642](https://github.com/shakacode/react_on_rails/pull/642) by [JasonYCHuang](https://github.com/JasonYCHuang).
89

910
## [6.3.2] - 2016-12-5
1011
##### Fixed

README.md

+101-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**For a complete example of this gem, see our live demo at [www.reactrails.com](http://www.reactrails.com). ([Source Code](https://github.com/shakacode/react-webpack-rails-tutorial))**
44

5-
Aloha from Justin Gordon ([bio](http://www.railsonmaui.com/about)) and the [ShakaCode](http://www.shakacode.com) Team! We're actively looking for new projects involving React, React-Native, and Rails. Please contact me at [[email protected]](mailto:[email protected]) if we could potentially help you in any way. Besides consulting on bigger projects, [ShakaCode](http://www.shakacode.com) is doing ScreenHero plus Slack/Github based coaching for React on Rails. See our blog post [Can ShakaCode Help You?](https://blog.shakacode.com/can-shakacode-help-you-4a5b1e5a8a63#.jex6tg9w9) for more information.
5+
Aloha from Justin Gordon ([bio](http://www.railsonmaui.com/about)) and the [ShakaCode](http://www.shakacode.com) Team! We're actively looking for new projects involving React, React-Native, and Rails. Please contact me at [[email protected]](mailto:[email protected]) if we could potentially help you in any way. Besides consulting on bigger projects, [ShakaCode](http://www.shakacode.com) is doing ScreenHero plus Slack/Github based coaching for React on Rails. See our blog post [Can ShakaCode Help You?](https://blog.shakacode.com/can-shakacode-help-you-4a5b1e5a8a63#.jex6tg9w9) for more information.
66

77
We're offering a free half-hour project consultation, on anything from React on Rails to any aspect of web application development for both consumer and enterprise products. In addition to React.js and Rails, we're doing React-Native iOS and Android apps!
88

@@ -50,6 +50,7 @@ React on Rails integrates Facebook's [React](https://github.com/facebook/react)
5050
- [Installation Summary](#installation-summary)
5151
- [Initializer Configuration: config/initializers/react_on_rails.rb](#initializer-configuration)
5252
- [Including your React Component in your Rails Views](#including-your-react-component-in-your-rails-views)
53+
- [I18n](#i18n)
5354
+ [How it Works](#how-it-works)
5455
- [Client-Side Rendering vs. Server-Side Rendering](#client-side-rendering-vs-server-side-rendering)
5556
- [Building the Bundles](#building-the-bundles)
@@ -168,7 +169,104 @@ Configure the `config/initializers/react_on_rails.rb`. You can adjust some neces
168169
// inside your React component
169170
this.props.name // "Stranger"
170171
```
171-
172+
173+
### I18n
174+
175+
You can enable the i18n functionality with [react-intl](https://github.com/yahoo/react-intl). ReactOnRails also converts traditional Rails locale files, `*.yml`, to required javascript files, `translations.js` & `default.js`, automatically.
176+
177+
For more detail, you can refer to [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial)
178+
179+
1. Add `react-intl` to `client/package.json`, and remember to `bundle && npm install`
180+
181+
```js
182+
"dependencies": {
183+
...
184+
"intl": "^1.2.5",
185+
"react-intl": "^2.1.5",
186+
...
187+
}
188+
```
189+
190+
2. In `client/webpack.client.base.config.js`, set `react-intl` as an entry point.
191+
192+
```js
193+
module.exports = {
194+
...
195+
entry: {
196+
...
197+
vendor: [
198+
...
199+
'react-intl',
200+
],
201+
...
202+
```
203+
204+
3. ReactOnRails uses locale files as you did before in Rails: `config/locales/*.yml`. Therefore, you don't need to create additional local files.
205+
206+
4. Update settings in `config/initializers/react_on_rails.rb` to what you need:
207+
208+
```ruby
209+
# Replace the following line to the location where you keep translation.js & default.js.
210+
config.i18n_dir = Rails.root.join("PATH_TO", "YOUR_JS_I18N_FOLDER")
211+
# Default locale
212+
config.default_locale = "en"
213+
# The location of rails locales
214+
config.rails_locales_path = Rails.application.config.i18n.load_path
215+
```
216+
217+
5. Add following lines to `config/application.rb`, this will help you to generate `translations.js` & `default.js` automatically when you starts the server.
218+
219+
```js
220+
module YourModule
221+
class Application < Rails::Application
222+
...
223+
config.after_initialize do
224+
ReactOnRails::LocalesToJs.new.convert
225+
end
226+
end
227+
end
228+
```
229+
230+
6. In React, you need to initialize `react-intl`, and set parameters for it.
231+
232+
For more detail, you can refer to [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial)
233+
234+
> `translations.js`: All your locales in json format.
235+
>
236+
> `default.js`: [1] `defaultLocale` is your default locale, like "en". [2] `defaultMessages` is the place where you can get your local values with localeKeyInCamelForm, and it also contains fallback when something went wrong.
237+
>
238+
> There is no need to track and lint `translations.js` & `default.js`, and you can add them to `.gitignore` and `.eslintignore`.
239+
240+
```js
241+
...
242+
import { addLocaleData } from 'react-intl';
243+
import en from 'react-intl/locale-data/en';
244+
import de from 'react-intl/locale-data/de';
245+
import { translations } from 'path_to/i18n/translations';
246+
import { defaultLocale } from 'path_to/i18n/default';
247+
...
248+
// Initizalize all locales for react-intl.
249+
addLocaleData([...en, ...de]);
250+
...
251+
// set locale and messages for IntlProvider.
252+
const locale = method_to_get_current_locale() || defaultLocale;
253+
const messages = translations[locale];
254+
...
255+
return (
256+
<IntlProvider locale={locale} key={locale} messages={messages}>
257+
<CommentScreen {...{ actions, data }} />
258+
</IntlProvider>
259+
)
260+
```
261+
```js
262+
// In your component.
263+
import { defaultMessages } from 'path_to/i18n/default';
264+
...
265+
return (
266+
{ formatMessage(defaultMessages.yourLocaleKeyInCamelCase) }
267+
)
268+
```
269+
172270
## NPM
173271
All JavaScript in React On Rails is loaded from npm: [react-on-rails](https://www.npmjs.com/package/react-on-rails). To manually install this (you did not use the generator), assuming you have a standard configuration, run this command:
174272

@@ -241,11 +339,6 @@ The `railsContext` has: (see implementation in file [react_on_rails_helper.rb](a
241339
pathname: uri.path, # /posts
242340
search: uri.query, # id=30&limit=5
243341

244-
# Locale settings
245-
i18nLocale: I18n.locale,
246-
i18nDefaultLocale: I18n.default_locale,
247-
httpAcceptLanguage: request.env["HTTP_ACCEPT_LANGUAGE"],
248-
249342
# Other
250343
serverSide: boolean # Are we being called on the server or client? NOTE, if you conditionally
251344
# render something different on the server than the client, then React will only show the
@@ -257,9 +350,6 @@ The `railsContext` has: (see implementation in file [react_on_rails_helper.rb](a
257350
##### Needing the current url path for server rendering
258351
Suppose you want to display a nav bar with the current navigation link highlighted by the URL. When you server render the code, you will need to know the current URL/path if that is what you want your logic to be based on. The new `railsContext` has this information so the application of an "active" class can be done server side.
259352

260-
##### Needing the I18n.locale
261-
Suppose you want to server render your react components with localization applied given the current Rails locale. The `railsContext` contains the I18n.locale.
262-
263353
##### Configuring different code for server side rendering
264354
Suppose you want to turn off animation when doing server side rendering. The `serverSide` value is just what you need.
265355

@@ -473,7 +563,7 @@ If you are using [jquery-ujs](https://github.com/rails/jquery-ujs) for AJAX call
473563
## Integration with Node
474564
Node.js can be used as the backend for server-side rendering instead of [execJS](https://github.com/rails/execjs). Before you try this, consider the tradeoff of extra complexity with your deployments versus *potential* performance gains. We've found that using ExecJS with [mini_racer](https://github.com/discourse/mini_racer) to be "fast enough" so far. That being said, we've heard of other large websites using Node.js for better server rendering performance. See [Node.js for Server Rendering](docs/additional-reading/node-server-rendering.md) for more information.
475565

476-
## Additional Documentation
566+
## Additional Documentation
477567
**Try out our new [Documentation Gitbook](https://shakacode.gitbooks.io/react-on-rails/content/) for improved readability & reference!**
478568
+ **Rails**
479569
+ [Rails Assets](docs/additional-reading/rails-assets.md)

lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt

+10
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ ReactOnRails.configure do |config|
5959
config.server_renderer_pool_size = 1 # increase if you're on JRuby
6060
config.server_renderer_timeout = 20 # seconds
6161

62+
################################################################################
63+
# I18N OPTIONS
64+
################################################################################
65+
# Replace the following line to the location where you keep translation.js & default.js.
66+
config.i18n_dir = Rails.root.join("client", "app", "libs", "i18n")
67+
# Default locale
68+
config.default_locale = "en"
69+
# The location of rails locales
70+
config.rails_locales_path = Rails.application.config.i18n.load_path
71+
6272
################################################################################
6373
# MISCELLANEOUS OPTIONS
6474
################################################################################

lib/react_on_rails.rb

+1
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
require "react_on_rails/test_helper/webpack_assets_status_checker"
1717
require "react_on_rails/test_helper/ensure_assets_compiled"
1818
require "react_on_rails/test_helper/node_process_launcher"
19+
require "react_on_rails/locales_to_js"

lib/react_on_rails/configuration.rb

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ def self.configuration
5959
server_render_method: "",
6060
symlink_non_digested_assets_regex: /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg|map)/,
6161
npm_build_test_command: "",
62+
i18n_dir: "",
63+
default_locale: "",
64+
rails_locales_path: "",
6265
npm_build_production_command: ""
6366
)
6467
end
@@ -71,6 +74,7 @@ class Configuration
7174
:skip_display_none, :generated_assets_dirs, :generated_assets_dir,
7275
:webpack_generated_files, :rendering_extension, :npm_build_test_command,
7376
:npm_build_production_command,
77+
:i18n_dir, :default_locale, :rails_locales_path,
7478
:server_render_method, :symlink_non_digested_assets_regex
7579

7680
def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil,
@@ -81,12 +85,16 @@ def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil,
8185
generated_assets_dir: nil, webpack_generated_files: nil,
8286
rendering_extension: nil, npm_build_test_command: nil,
8387
npm_build_production_command: nil,
88+
i18n_dir: nil, default_locale: nil, rails_locales_path: nil,
8489
server_render_method: nil, symlink_non_digested_assets_regex: nil)
8590
self.server_bundle_js_file = server_bundle_js_file
8691
self.generated_assets_dirs = generated_assets_dirs
8792
self.generated_assets_dir = generated_assets_dir
8893
self.npm_build_test_command = npm_build_test_command
8994
self.npm_build_production_command = npm_build_production_command
95+
self.i18n_dir = i18n_dir
96+
self.default_locale = default_locale
97+
self.rails_locales_path = rails_locales_path
9098

9199
self.prerender = prerender
92100
self.replay_console = replay_console

lib/react_on_rails/locales_to_js.rb

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
require "erb"
2+
3+
module ReactOnRails
4+
class LocalesToJs
5+
def initialize
6+
@locale_files = set_locale_files
7+
@default_locale = set_default_locale
8+
@translations, @defaults = generate_translations
9+
end
10+
11+
def convert
12+
i18n_js_files.each do |f|
13+
template = send("template_#{f}")
14+
path = send("path_#{f}")
15+
create_js_file(template, path)
16+
end
17+
end
18+
19+
private
20+
21+
def i18n_js_files
22+
%w(translations_js default_js)
23+
end
24+
25+
def create_js_file(template, path)
26+
result = ERB.new(template).result()
27+
File.open(path, "w") do |f|
28+
f.write(result)
29+
end
30+
end
31+
32+
def generate_translations
33+
translations = {}
34+
defaults = {}
35+
@locale_files.each do |f|
36+
translation = YAML.load(File.open(f))
37+
key = translation.keys[0]
38+
val = flatten(translation[key])
39+
translations = translations.deep_merge(key => val)
40+
defaults = defaults.deep_merge(flatten_defaults(val)) if key == @default_locale
41+
end
42+
[translations.to_json, defaults.to_json]
43+
end
44+
45+
def format(input)
46+
input.to_s.tr(".", "_").camelize(:lower).to_sym
47+
end
48+
49+
def flatten_defaults(val)
50+
flatten(val).each_with_object({}) do |(k, v), h|
51+
key = format(k)
52+
h[key] = { id: k, defaultMessage: v }
53+
end
54+
end
55+
56+
def flatten(translations)
57+
translations.each_with_object({}) do |(k, v), h|
58+
if v.is_a? Hash
59+
flatten(v).map { |hk, hv| h["#{k}.#{hk}".to_sym] = hv }
60+
else
61+
h[k] = v
62+
end
63+
end
64+
end
65+
66+
def i18n_dir
67+
ReactOnRails.configuration.i18n_dir
68+
end
69+
70+
def set_locale_files
71+
ReactOnRails.configuration.rails_locales_path
72+
end
73+
74+
def set_default_locale
75+
ReactOnRails.configuration.default_locale || "en"
76+
end
77+
78+
def path_translations_js
79+
i18n_dir + "translations.js"
80+
end
81+
82+
def path_default_js
83+
i18n_dir + "default.js"
84+
end
85+
86+
def template_translations_js
87+
<<-JS
88+
export const translations = #{@translations};
89+
JS
90+
end
91+
92+
def template_default_js
93+
<<-JS
94+
import { defineMessages } from 'react-intl';
95+
96+
const defaultLocale = \'#{@default_locale}\';
97+
98+
const defaultMessages = defineMessages(#{@defaults});
99+
100+
export { defaultMessages, defaultLocale };
101+
JS
102+
end
103+
end
104+
end

spec/dummy/config/initializers/react_on_rails.rb

+10
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ def self.custom_context(view_context)
7171
config.server_renderer_pool_size = 1 # increase if you're on JRuby
7272
config.server_renderer_timeout = 20 # seconds
7373

74+
################################################################################
75+
# I18N OPTIONS
76+
################################################################################
77+
# Replace the following line to the location where you keep translation.js & default.js.
78+
config.i18n_dir = Rails.root.join("client", "app", "libs", "i18n")
79+
# Default locale
80+
config.default_locale = "en"
81+
# The location of rails locales
82+
config.rails_locales_path = Rails.application.config.i18n.load_path
83+
7484
################################################################################
7585
# MISCELLANEOUS OPTIONS
7686
################################################################################
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require_relative "spec_helper"
2+
require "tmpdir"
3+
4+
module ReactOnRails
5+
RSpec.describe LocalesToJs do
6+
let(:i18n_dir) { Pathname.new(Dir.mktmpdir) }
7+
let(:locale_dir) { File.expand_path("../../dummy/config/locales/", __FILE__) }
8+
9+
it "be able to generate translations.js & default.js" do
10+
ReactOnRails.configure do |config|
11+
config.i18n_dir = i18n_dir
12+
config.default_locale = "en"
13+
config.rails_locales_path = Dir["#{locale_dir}/*"]
14+
end
15+
16+
ReactOnRails::LocalesToJs.new.convert
17+
18+
files = Dir["#{i18n_dir.to_path}/*"].map { |p| Pathname.new(p).basename.to_s }
19+
expect(files).to include("translations.js", "default.js")
20+
end
21+
end
22+
end

0 commit comments

Comments
 (0)