Skip to content

Commit ffacf2c

Browse files
committed
Automatically generate javascript files for react-intl when the server starts up.
1 parent 13cff3f commit ffacf2c

File tree

8 files changed

+257
-11
lines changed

8 files changed

+257
-11
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Contributors: please follow the recommendations outlined at [keepachangelog.com]
66
## [Unreleased]
77
*Please add entries here for your pull requests.*
88

9+
##### Changed
10+
- 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).
11+
912
## [6.3.4] - 2016-12-25
1013
##### Fixed
1114
- Disable Turbolinks support when not supported. [#650](https://github.com/shakacode/react_on_rails/pull/650) by [ka2n](https://github.com/ka2n).

README.md

+99-11
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
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.
6-
75
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!
86

97
Whether you have a new project or need help on an existing project, feel free to contact me directly at [[email protected]](mailto:[email protected]) and thanks in advance for any referrals!
@@ -50,6 +48,7 @@ React on Rails integrates Facebook's [React](https://github.com/facebook/react)
5048
- [Installation Summary](#installation-summary)
5149
- [Initializer Configuration: config/initializers/react_on_rails.rb](#initializer-configuration)
5250
- [Including your React Component in your Rails Views](#including-your-react-component-in-your-rails-views)
51+
- [I18n](#i18n)
5352
+ [How it Works](#how-it-works)
5453
- [Client-Side Rendering vs. Server-Side Rendering](#client-side-rendering-vs-server-side-rendering)
5554
- [Building the Bundles](#building-the-bundles)
@@ -168,7 +167,104 @@ Configure the `config/initializers/react_on_rails.rb`. You can adjust some neces
168167
// inside your React component
169168
this.props.name // "Stranger"
170169
```
171-
170+
171+
### I18n
172+
173+
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.
174+
175+
For more detail, you can refer to [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial)
176+
177+
1. Add `react-intl` to `client/package.json`, and remember to `bundle && npm install`
178+
179+
```js
180+
"dependencies": {
181+
...
182+
"intl": "^1.2.5",
183+
"react-intl": "^2.1.5",
184+
...
185+
}
186+
```
187+
188+
2. In `client/webpack.client.base.config.js`, set `react-intl` as an entry point.
189+
190+
```js
191+
module.exports = {
192+
...
193+
entry: {
194+
...
195+
vendor: [
196+
...
197+
'react-intl',
198+
],
199+
...
200+
```
201+
202+
3. ReactOnRails uses locale files as you did before in Rails: `config/locales/*.yml`. Therefore, you don't need to create additional local files.
203+
204+
4. Update settings in `config/initializers/react_on_rails.rb` to what you need:
205+
206+
```ruby
207+
# Replace the following line to the location where you keep translation.js & default.js.
208+
config.i18n_dir = Rails.root.join("PATH_TO", "YOUR_JS_I18N_FOLDER")
209+
# Default locale
210+
config.default_locale = "en"
211+
# The location of rails locales
212+
config.rails_locales_path = Rails.application.config.i18n.load_path
213+
```
214+
215+
5. Add following lines to `config/application.rb`, this will help you to generate `translations.js` & `default.js` automatically when you starts the server.
216+
217+
```js
218+
module YourModule
219+
class Application < Rails::Application
220+
...
221+
config.after_initialize do
222+
ReactOnRails::LocalesToJs.new.convert
223+
end
224+
end
225+
end
226+
```
227+
228+
6. In React, you need to initialize `react-intl`, and set parameters for it.
229+
230+
For more detail, you can refer to [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial)
231+
232+
> `translations.js`: All your locales in json format.
233+
>
234+
> `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.
235+
>
236+
> There is no need to track and lint `translations.js` & `default.js`, and you can add them to `.gitignore` and `.eslintignore`.
237+
238+
```js
239+
...
240+
import { addLocaleData } from 'react-intl';
241+
import en from 'react-intl/locale-data/en';
242+
import de from 'react-intl/locale-data/de';
243+
import { translations } from 'path_to/i18n/translations';
244+
import { defaultLocale } from 'path_to/i18n/default';
245+
...
246+
// Initizalize all locales for react-intl.
247+
addLocaleData([...en, ...de]);
248+
...
249+
// set locale and messages for IntlProvider.
250+
const locale = method_to_get_current_locale() || defaultLocale;
251+
const messages = translations[locale];
252+
...
253+
return (
254+
<IntlProvider locale={locale} key={locale} messages={messages}>
255+
<CommentScreen {...{ actions, data }} />
256+
</IntlProvider>
257+
)
258+
```
259+
```js
260+
// In your component.
261+
import { defaultMessages } from 'path_to/i18n/default';
262+
...
263+
return (
264+
{ formatMessage(defaultMessages.yourLocaleKeyInCamelCase) }
265+
)
266+
```
267+
172268
## NPM
173269
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:
174270

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

244-
# Locale settings
245-
i18nLocale: I18n.locale,
246-
i18nDefaultLocale: I18n.default_locale,
247-
httpAcceptLanguage: request.env["HTTP_ACCEPT_LANGUAGE"],
248-
249340
# Other
250341
serverSide: boolean # Are we being called on the server or client? NOTE, if you conditionally
251342
# render something different on the server than the client, then React will only show the
@@ -257,9 +348,6 @@ The `railsContext` has: (see implementation in file [react_on_rails_helper.rb](a
257348
##### Needing the current url path for server rendering
258349
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.
259350

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-
263351
##### Configuring different code for server side rendering
264352
Suppose you want to turn off animation when doing server side rendering. The `serverSide` value is just what you need.
265353

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
@@ -72,6 +72,9 @@ def self.configuration
7272
server_render_method: "",
7373
symlink_non_digested_assets_regex: /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg|map)/,
7474
npm_build_test_command: "",
75+
i18n_dir: "",
76+
default_locale: "",
77+
rails_locales_path: "",
7578
npm_build_production_command: ""
7679
)
7780
end
@@ -84,6 +87,7 @@ class Configuration
8487
:skip_display_none, :generated_assets_dirs, :generated_assets_dir,
8588
:webpack_generated_files, :rendering_extension, :npm_build_test_command,
8689
:npm_build_production_command,
90+
:i18n_dir, :default_locale, :rails_locales_path,
8791
:server_render_method, :symlink_non_digested_assets_regex
8892

8993
def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil,
@@ -94,12 +98,16 @@ def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil,
9498
generated_assets_dir: nil, webpack_generated_files: nil,
9599
rendering_extension: nil, npm_build_test_command: nil,
96100
npm_build_production_command: nil,
101+
i18n_dir: nil, default_locale: nil, rails_locales_path: nil,
97102
server_render_method: nil, symlink_non_digested_assets_regex: nil)
98103
self.server_bundle_js_file = server_bundle_js_file
99104
self.generated_assets_dirs = generated_assets_dirs
100105
self.generated_assets_dir = generated_assets_dir
101106
self.npm_build_test_command = npm_build_test_command
102107
self.npm_build_production_command = npm_build_production_command
108+
self.i18n_dir = i18n_dir
109+
self.default_locale = default_locale
110+
self.rails_locales_path = rails_locales_path
103111

104112
self.prerender = prerender
105113
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 "generates 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)