i18n-js 4.0.0 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b2fdace9fca14d471ff34c3b24055d4093d82ce2a1f72f2df06364a98454a31
4
- data.tar.gz: 9ea6bd71626357c8fce24c28e991b9edc96f8a3230e55c4d55cf1dec3813c94e
3
+ metadata.gz: fb5f60568907f90a59b7d19a6b2f61cf9a3fff95ea419b1b8b31deab16529dc6
4
+ data.tar.gz: ca65b2bf6717b93218092e8c994cdbac8af6bc69fddab421ebef976c87dc5503
5
5
  SHA512:
6
- metadata.gz: 2c8da0348d02f04d805a013dadd24beb08fb7de555bfdded51e4e6c7c29ba392978a723b7e95be9d752dda2b35bbe6d6aefe4d4cf9166c1ddca09f003712533f
7
- data.tar.gz: 882af1975b1f2e479ad4d8bdcbe812175fae165c80a2f915223e14fc71fb0b3236774254e103754169eab5a3d577e219e10803fd05e14fa3bac82471e4c770e1
6
+ metadata.gz: 3e33c8605b5197cbdc6a8f30ef1cb7e5f911f84dd177d488489fcfc82e5dd2b8fb3e387b0fe89683c3e71c6025ff7b47685163317540c4e2c5c756936660be55
7
+ data.tar.gz: b4367db58b89392c42c94121ccdde7179e1ea455de6c20685ded2c034304ce4c8c44cbfa1a4967610443e4f91116058ee86bc14d2e913a2c6006ee648c189dd9
data/CHANGELOG.md CHANGED
@@ -11,6 +11,13 @@ Prefix your message with one of the following:
11
11
  - [Security] in case of vulnerabilities.
12
12
  -->
13
13
 
14
+ ## v4.0.1
15
+
16
+ - [Fixed] Shell out export to avoid handling I18n reloading heuristics.
17
+ - [Changed] `I18nJS.listen` now accepts a directories list to watch.
18
+ - [Changed] `I18nJS.listen` now accepts
19
+ [listen](https://rubygems.org/gems/listen) options via `:options`.
20
+
14
21
  ## Jul 29, 2022
15
22
 
16
23
  - Official release of i18n-js v4.0.0.
@@ -0,0 +1,191 @@
1
+ # Migrating from v3 to v4
2
+
3
+ I18n-js v4 is a breaking change release and diverges quite a lot from how the
4
+ previous version worked. This guides summarizes the process of upgrading an app
5
+ that uses i18n-js v3 to v4.
6
+
7
+ ## Development
8
+
9
+ Previously, you could use a middleware to export translations (some people even
10
+ used this in production 😬). In development, you can now use whatever your want,
11
+ because i18n-js doesn't make any assumptions. All you need to do is running
12
+ `i18n export`, either manually or by using something that listens to file
13
+ changes.
14
+
15
+ If you like watchman, you can use something like this:
16
+
17
+ ```bash
18
+ #!/usr/bin/env bash
19
+
20
+ root=`pwd`
21
+
22
+ watchman watch-del "$root"
23
+ watchman watch-project "$root"
24
+ watchman trigger-del "$root" i18n
25
+
26
+ watchman -j <<-JSON
27
+ [
28
+ "trigger",
29
+ "$root",
30
+ {
31
+ "name": "i18n",
32
+ "expression": [
33
+ "anyof",
34
+ ["match", "config/locales/**/*.yml", "wholename"],
35
+ ["match", "config/locales/**/*.po", "wholename"],
36
+ ["match", "config/i18n.yml", "wholename"]
37
+ ],
38
+ "command": ["i18n", "export"]
39
+ }
40
+ ]
41
+ JSON
42
+
43
+ # If you're running this through Foreman, then uncomment the following lines:
44
+ # while true; do
45
+ # sleep 1
46
+ # done
47
+ ```
48
+
49
+ You can also use guard. Make sure you have both
50
+ [guard](https://rubygems.org/packages/guard) and
51
+ [guard-compat](https://rubygems.org/packages/guard-compat) installed and use
52
+ Guardfile file with the following contents:
53
+
54
+ ```ruby
55
+ guard(:"i18n-js",
56
+ run_on_start: true,
57
+ config_file: "./config/i18n.yml",
58
+ require_file: "./config/environment.rb") do
59
+ watch(%r{^config/locales/.+\.(yml|po)$})
60
+ watch(%r{^config/i18n.yml$})
61
+ watch("Gemfile")
62
+ end
63
+ ```
64
+
65
+ To run guard, use `guard start -i`.
66
+
67
+ Finally, you can use [listen](https://rubygems.org/gems/listen). Create the file
68
+ `config/initializers/i18n.rb` with the following content:
69
+
70
+ ```ruby
71
+ Rails.application.config.after_initialize do
72
+ require "i18n-js/listen"
73
+ # This will only run in development.
74
+ I18nJS.listen
75
+ end
76
+ ```
77
+
78
+ > **Warning**:
79
+ >
80
+ > No matter which approach you choose, the idea is that you _precompile_ your
81
+ > translations when going to production. DO NOT RUN any of the above in
82
+ > production.
83
+
84
+ ## Exporting translations
85
+
86
+ The build process for i18n now relies on an external CLI called `i18n`. All you
87
+ need to do is executing `i18n export` in your build step to generate the json
88
+ files for your translations.
89
+
90
+ ## Using your translations
91
+
92
+ The JavaScript package is now a separate thing and need to be installed using
93
+ your favorite tooling (e.g. yarn, npm, pnpm, etc).
94
+
95
+ ```console
96
+ $ yarn add i18n-js@latest
97
+ $ npm i --save-dev i18n-js@latest
98
+ ```
99
+
100
+ From now on, the way you load translations and set up I18n-js is totally up to
101
+ you, but means you need to load the json files and attach to the I18n-js
102
+ instance. This is how I do it in a project I'm doing right now (Rails 7 +
103
+ esbuild + TypeScript). First, we need to load the I18n-js configuration from the
104
+ main JavaScript file:
105
+
106
+ ```typescript
107
+ // app/javascript/application.ts
108
+ import { i18n } from "./config/i18n";
109
+ ```
110
+
111
+ Then we need to load our translations and instantiate the I18n-js class.
112
+
113
+ ```typescript
114
+ // app/javascript/config/i18n.ts
115
+ import { I18n } from "i18n-js";
116
+ import translations from "translations.json";
117
+
118
+ // Fetch user locale from html#lang.
119
+ // This value is being set on `app/views/layouts/application.html.erb` and
120
+ // is inferred from `ACCEPT-LANGUAGE` header.
121
+ const userLocale = document.documentElement.lang;
122
+
123
+ export const i18n = new I18n();
124
+ i18n.store(translations);
125
+ i18n.defaultLocale = "en";
126
+ i18n.enableFallback = true;
127
+ i18n.locale = userLocale;
128
+ ```
129
+
130
+ The best thing about the above is that it is a pretty straightforward pattern in
131
+ the JavaScript community. It doesn't rely on specific parts from Sprockets (I'm
132
+ not even using it on my projects) or eRb files.
133
+
134
+ ## Ruby on Rails
135
+
136
+ ### Upgrading the configuration file
137
+
138
+ The configuration file loaded from `config/i18nyml` has changed. Given the v3
139
+ configuration below
140
+
141
+ ```yaml
142
+ ---
143
+ translations:
144
+ - file: "app/assets/javascripts/date_formats.js"
145
+ only: "*.date.formats"
146
+ - file: "app/assets/javascripts/other.js"
147
+ only: ["*.activerecord", "*.admin.*.title"]
148
+ - file: "app/assets/javascripts/everything_else.js"
149
+ except:
150
+ - "*.activerecord"
151
+ - "*.admin.*.title"
152
+ - "*.date.formats"
153
+ ```
154
+
155
+ the equivalent configuration file for v4 would be
156
+
157
+ ```yaml
158
+ ---
159
+ translations:
160
+ - file: "app/assets/javascripts/date_formats.js"
161
+ patterns:
162
+ - "*.date.formats"
163
+ - file: "app/assets/javascripts/other.js"
164
+ patterns:
165
+ - "*.activerecord"
166
+ - "*.admin.*.title"
167
+ - file: "app/assets/javascripts/everything_else.js"
168
+ patterns:
169
+ # Notice the exclamation mark.
170
+ - "!*.activerecord"
171
+ - "!*.admin.*.title"
172
+ - "!*.date.formats"
173
+ ```
174
+
175
+ Other configuration options:
176
+
177
+ - `export_i18n_js`: removed without an equivalent
178
+ - `fallbacks`: removed without an equivalent
179
+ - `js_available_locales`: removed (on v4 you can use groups, like in
180
+ `{pt-BR,en}.*`)
181
+ - `namespace`: removed without an equivalent
182
+ - `sort_translation_keys`: removed (on v4 keys will always be sorted)
183
+ - `translations[].prefix`: removed without an equivalent
184
+ - `translations[].pretty_print`: removed (on v4 files will always be exported in
185
+ a readable format)
186
+
187
+ ### Placeholders
188
+
189
+ Previously, v3 had the `%{locale}` placeholder, which can be used as part of the
190
+ directory and/or file name. Now, the syntax is just `:locale`. Additionally, you
191
+ can also use `:digest`, which uses a MD5 hex digest of the exported file.
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img width="250" height="58" src="https://github.com/fnando/i18n-js/raw/main/i18njs.png" alt="i18n.js">
2
+ <img width="250" height="58" src="https://github.com/fnando/i18n-js/raw/main/images/i18njs.png" alt="i18n.js">
3
3
  </p>
4
4
 
5
5
  <p align="center">
@@ -12,14 +12,14 @@
12
12
  <small>
13
13
  Oh, you don't use Ruby? No problem! You can still use i18n-js
14
14
  <br>
15
- and the
16
- <a href="https://www.npmjs.com/package/i18n-js/v/next">companion JavaScript package</a>.
15
+ and the
16
+ <a href="https://www.npmjs.com/package/i18n-js/v/latest">companion JavaScript package</a>.
17
17
  </small>
18
18
  </p>
19
19
 
20
20
  <p align="center">
21
21
  <a href="https://github.com/fnando/i18n-js"><img src="https://github.com/fnando/i18n-js/workflows/ruby-tests/badge.svg" alt="Tests"></a>
22
- <a href="https://rubygems.org/gems/i18n-js"><img src="https://img.shields.io/gem/v/i18n-js.svg?include_prereleases" alt="Gem"></a>
22
+ <a href="https://rubygems.org/gems/i18n-js"><img src="https://img.shields.io/gem/v/i18n-js.svg" alt="Gem"></a>
23
23
  <a href="https://rubygems.org/gems/i18n-js"><img src="https://img.shields.io/gem/dt/i18n-js.svg" alt="Gem"></a>
24
24
  <a href="https://tldrlegal.com/license/mit-license"><img src="https://img.shields.io/:License-MIT-blue.svg" alt="MIT License"></a>
25
25
  </p>
@@ -45,6 +45,14 @@ About patterns:
45
45
  - `*.messages.*`
46
46
  - Patterns starting with `!` are excluded.
47
47
  - `!*.activerecord.*` will exclude all ActiveRecord translations.
48
+ - You can use groups:
49
+ - `{pt-BR,en}.js.*` will include only `pt-BR` and `en` translations, even if
50
+ more languages are available.
51
+
52
+ > **Note**:
53
+ >
54
+ > Patterns use [glob](https://rubygems.org/gems/glob), so check it out for the
55
+ > most up-to-date documentation about what's available.
48
56
 
49
57
  The config file:
50
58
 
@@ -131,13 +139,15 @@ check:
131
139
  - en.mailer.login.body
132
140
  ```
133
141
 
134
- > **Note**: In order to avoid mistakenly ignoring keys, this configuration
135
- > option only accepts the full translation scope, rather than accepting a
136
- > pattern like `pt.ignored.scope.*`.
142
+ > **Note**:
143
+ >
144
+ > In order to avoid mistakenly ignoring keys, this configuration option only
145
+ > accepts the full translation scope, rather than accepting a pattern like
146
+ > `pt.ignored.scope.*`.
137
147
 
138
148
  ## Automatically export translations
139
149
 
140
- ### Using watchman
150
+ ### Using [watchman](https://facebook.github.io/watchman/)
141
151
 
142
152
  Create a script at `bin/i18n-watch`.
143
153
 
@@ -182,11 +192,11 @@ line to your Procfile:
182
192
  i18n: ./bin/i18n-watch
183
193
  ```
184
194
 
185
- ### Using guard
195
+ ### Using [guard](https://rubygems.org/gems/guard)
186
196
 
187
- Install [guard](https://rubygems.org/packages/guard) and
188
- [guard-compat](https://rubygems.org/packages/guard-compat). Then create a
189
- Guardfile with the following configuration:
197
+ Install [guard](https://rubygems.org/gems/guard) and
198
+ [guard-compat](https://rubygems.org/gems/guard-compat). Then create a Guardfile
199
+ with the following configuration:
190
200
 
191
201
  ```ruby
192
202
  guard(:"i18n-js",
@@ -204,7 +214,7 @@ accordingly.
204
214
 
205
215
  Now you can run `guard start -i`.
206
216
 
207
- ### Using listen
217
+ ### Using [listen](https://rubygems.org/gems/listen)
208
218
 
209
219
  Create a file under `config/initializers/i18n.rb` with the following content:
210
220
 
@@ -216,8 +226,21 @@ end
216
226
  ```
217
227
 
218
228
  The code above will watch for changes based on `config/i18n.yml` and
219
- `config/locales`. You can customize these options with
220
- `I18nJS.listen(config_file: "config/i18n.yml", locales_dir: "config/locales")`.
229
+ `config/locales`. You can customize these options:
230
+
231
+ - `config_file` - i18n-js configuration file
232
+ - `locales_dir` - one or multiple directories to watch for locales changes
233
+ - `options` - passed directly to [listen](https://github.com/guard/listen/#options)
234
+
235
+ Example:
236
+
237
+ ```ruby
238
+ I18nJS.listen(
239
+ config_file: "config/i18n.yml",
240
+ locales_dir: ["config/locales", "app/views"],
241
+ options: {only: %r{.yml$}
242
+ )
243
+ ```
221
244
 
222
245
  ### Integrating with your frontend
223
246
 
@@ -227,6 +250,13 @@ that loads all the exported translation.
227
250
 
228
251
  ### FAQ
229
252
 
253
+ #### I'm running v3. Is there a migration plan?
254
+
255
+ [There's a document](https://github.com/fnando/i18n-js/tree/main/MIGRATING_FROM_V3_TO_V4.md)
256
+ outlining some of the things you need to do to migrate from v3 to v4. It may not
257
+ be as complete as we'd like it to be, so let's know if you face any issues
258
+ during the migration is not outline is that document.
259
+
230
260
  #### How can I export translations without having a database around?
231
261
 
232
262
  Some people may have a build process using something like Docker that don't
@@ -246,9 +276,11 @@ require "action_view/railtie"
246
276
  I18n.load_path += Dir["./config/locales/**/*.yml"]
247
277
  ```
248
278
 
249
- Notice that you may not need to load ActiveSupport and ActionView lines, or even
250
- may need to add additional requires for other libs. With this approach you have
251
- full control on what's going to be loaded.
279
+ > **Note**:
280
+ >
281
+ > You may not need to load ActiveSupport and ActionView lines, or even may need
282
+ > to add additional requires for other libs. With this approach you have full
283
+ > control on what's going to be loaded.
252
284
 
253
285
  ## Maintainer
254
286
 
data/i18n-js.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
29
29
  `git ls-files -z`
30
30
  .split("\x0")
31
- .reject {|f| f.match(%r{^(test|spec|features)/}) }
31
+ .reject {|f| f.match(%r{^(test|spec|features|images)/}) }
32
32
  end
33
33
 
34
34
  spec.bindir = "exe"
data/lib/guard/i18n-js.rb CHANGED
@@ -53,13 +53,30 @@ module Guard
53
53
  info("Changes detected: #{changes.join(', ')}") if changes
54
54
 
55
55
  @current_thread = Thread.new do
56
- require @require_file
57
- ::I18nJS.call(config_file: @config_file)
56
+ capture do
57
+ system "i18n",
58
+ "export",
59
+ "--config",
60
+ config_file.to_s,
61
+ "--require",
62
+ require_file.to_s,
63
+ "--quiet"
64
+ end
58
65
  end
59
66
 
60
67
  current_thread.join
61
68
  end
62
69
 
70
+ def capture
71
+ original = $stdout
72
+ $stdout = StringIO.new
73
+ yield
74
+ rescue StandardError
75
+ # noop
76
+ ensure
77
+ $stdout = original
78
+ end
79
+
63
80
  def validate_file(key, file)
64
81
  return true if file && File.file?(file)
65
82
 
@@ -68,11 +85,11 @@ module Guard
68
85
  end
69
86
 
70
87
  def error(message)
71
- ::Guard::UI.error "[guard-i18n-js] #{message}"
88
+ ::Guard::UI.error "[i18n-js] #{message}"
72
89
  end
73
90
 
74
91
  def info(message)
75
- ::Guard::UI.info "[guard-i18n-js] #{message}"
92
+ ::Guard::UI.info "[i18n-js] #{message}"
76
93
  end
77
94
  end
78
95
  end
@@ -27,6 +27,14 @@ module I18nJS
27
27
  options[:require_file] = require_file
28
28
  end
29
29
 
30
+ opts.on(
31
+ "-q",
32
+ "--quiet",
33
+ "A Ruby file that must be loaded"
34
+ ) do |quiet|
35
+ options[:quiet] = quiet
36
+ end
37
+
30
38
  opts.on("-h", "--help", "Prints this help") do
31
39
  ui.exit_with opts.to_s
32
40
  end
@@ -39,11 +47,11 @@ module I18nJS
39
47
  ui.fail_with("=> ERROR: you need to specify the config file")
40
48
  end
41
49
 
42
- ui.stdout_print("=> Config file:", options[:config_file].inspect)
50
+ log("=> Config file:", options[:config_file].inspect)
43
51
  config_file = File.expand_path(options[:config_file])
44
52
 
45
53
  if options[:require_file]
46
- ui.stdout_print("=> Require file:", options[:require_file].inspect)
54
+ log("=> Require file:", options[:require_file].inspect)
47
55
  require_file = File.expand_path(options[:require_file])
48
56
  end
49
57
 
@@ -66,7 +74,13 @@ module I18nJS
66
74
  I18nJS.call(config_file: config_file)
67
75
  end
68
76
 
69
- ui.stdout_print("=> Done in #{time.round(2)}s")
77
+ log("=> Done in #{time.round(2)}s")
78
+ end
79
+
80
+ private def log(*args)
81
+ return if options[:quiet]
82
+
83
+ ui.stdout_print(*args)
70
84
  end
71
85
 
72
86
  private def set_defaults!
@@ -7,7 +7,8 @@ module I18nJS
7
7
 
8
8
  def self.listen(
9
9
  config_file: Rails.root.join("config/i18n.yml"),
10
- locales_dir: Rails.root.join("config/locales")
10
+ locales_dir: Rails.root.join("config/locales"),
11
+ options: {}
11
12
  )
12
13
  return unless Rails.env.development?
13
14
  return if started
@@ -18,12 +19,14 @@ module I18nJS
18
19
 
19
20
  self.started = true
20
21
 
22
+ locales_dirs = Array(locales_dir)
23
+
21
24
  relative_paths =
22
- [config_file, locales_dir].map {|path| relative_path(path) }
25
+ [config_file, *locales_dirs].map {|path| relative_path(path) }
23
26
 
24
27
  debug("Watching #{relative_paths.inspect}")
25
28
 
26
- listener(config_file, locales_dir.to_s).start
29
+ listener(config_file, locales_dirs.map(&:to_s), options).start
27
30
  I18nJS.call(config_file: config_file)
28
31
  end
29
32
 
@@ -43,12 +46,12 @@ module I18nJS
43
46
  @logger ||= ActiveSupport::TaggedLogging.new(Rails.logger)
44
47
  end
45
48
 
46
- def self.listener(config_file, locales_dir)
47
- paths = [File.dirname(config_file), locales_dir]
49
+ def self.listener(config_file, locales_dirs, options)
50
+ paths = [File.dirname(config_file), *locales_dirs]
48
51
 
49
- Listen.to(*paths) do |changed, added, removed|
52
+ Listen.to(*paths, options) do |changed, added, removed|
50
53
  changes = compute_changes(
51
- [config_file, locales_dir],
54
+ [config_file, *locales_dirs],
52
55
  changed,
53
56
  added,
54
57
  removed
@@ -58,11 +61,22 @@ module I18nJS
58
61
 
59
62
  debug(changes.map {|key, value| "#{key}=#{value.inspect}" }.join(", "))
60
63
 
61
- ::I18n.backend.reload!
62
- I18nJS.call(config_file: config_file)
64
+ capture do
65
+ system "i18n", "export", "--config", config_file.to_s
66
+ end
63
67
  end
64
68
  end
65
69
 
70
+ def self.capture
71
+ original = $stdout
72
+ $stdout = StringIO.new
73
+ yield
74
+ rescue StandardError
75
+ # noop
76
+ ensure
77
+ $stdout = original
78
+ end
79
+
66
80
  def self.compute_changes(paths, changed, added, removed)
67
81
  paths = paths.map {|path| relative_path(path) }
68
82
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nJS
4
- VERSION = "4.0.0"
4
+ VERSION = "4.0.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n-js
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Vieira
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-29 00:00:00.000000000 Z
11
+ date: 2022-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glob
@@ -173,12 +173,11 @@ files:
173
173
  - CONTRIBUTING.md
174
174
  - Gemfile
175
175
  - LICENSE.md
176
+ - MIGRATING_FROM_V3_TO_V4.md
176
177
  - README.md
177
178
  - Rakefile
178
179
  - exe/i18n
179
180
  - i18n-js.gemspec
180
- - i18njs.png
181
- - images/i18njs-check.gif
182
181
  - lib/guard/i18n-js.rb
183
182
  - lib/guard/i18n-js/templates/Guardfile
184
183
  - lib/guard/i18n-js/version.rb
@@ -200,10 +199,10 @@ metadata:
200
199
  rubygems_mfa_required: 'true'
201
200
  homepage_uri: https://github.com/fnando/i18n-js
202
201
  bug_tracker_uri: https://github.com/fnando/i18n-js/issues
203
- source_code_uri: https://github.com/fnando/i18n-js/tree/v4.0.0
204
- changelog_uri: https://github.com/fnando/i18n-js/tree/v4.0.0/CHANGELOG.md
205
- documentation_uri: https://github.com/fnando/i18n-js/tree/v4.0.0/README.md
206
- license_uri: https://github.com/fnando/i18n-js/tree/v4.0.0/LICENSE.md
202
+ source_code_uri: https://github.com/fnando/i18n-js/tree/v4.0.1
203
+ changelog_uri: https://github.com/fnando/i18n-js/tree/v4.0.1/CHANGELOG.md
204
+ documentation_uri: https://github.com/fnando/i18n-js/tree/v4.0.1/README.md
205
+ license_uri: https://github.com/fnando/i18n-js/tree/v4.0.1/LICENSE.md
207
206
  post_install_message:
208
207
  rdoc_options: []
209
208
  require_paths:
data/i18njs.png DELETED
Binary file
Binary file