i18n-js 3.3.0 → 4.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +4 -0
  3. data/.github/FUNDING.yml +3 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
  5. data/.github/ISSUE_TEMPLATE/config.yml +5 -0
  6. data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  7. data/.github/PULL_REQUEST_TEMPLATE.md +38 -0
  8. data/.github/dependabot.yml +15 -0
  9. data/.github/workflows/ruby-tests.yml +73 -0
  10. data/.gitignore +13 -7
  11. data/.rubocop.yml +19 -0
  12. data/CHANGELOG.md +47 -410
  13. data/CODE_OF_CONDUCT.md +74 -0
  14. data/CONTRIBUTING.md +79 -0
  15. data/Gemfile +3 -0
  16. data/LICENSE.md +20 -0
  17. data/MIGRATING_FROM_V3_TO_V4.md +192 -0
  18. data/README.md +439 -793
  19. data/Rakefile +10 -20
  20. data/bin/release +81 -0
  21. data/exe/i18n +5 -0
  22. data/i18n-js.gemspec +51 -29
  23. data/lib/guard/i18n-js/templates/Guardfile +10 -0
  24. data/lib/guard/i18n-js/version.rb +13 -0
  25. data/lib/guard/i18n-js.rb +95 -0
  26. data/lib/i18n-js/clean_hash.rb +13 -0
  27. data/lib/i18n-js/cli/check_command.rb +17 -0
  28. data/lib/i18n-js/cli/command.rb +79 -0
  29. data/lib/i18n-js/cli/export_command.rb +95 -0
  30. data/lib/i18n-js/cli/init_command.rb +52 -0
  31. data/lib/i18n-js/cli/lint_scripts_command.rb +159 -0
  32. data/lib/i18n-js/cli/lint_translations_command.rb +157 -0
  33. data/lib/i18n-js/cli/plugins_command.rb +67 -0
  34. data/lib/i18n-js/cli/ui.rb +64 -0
  35. data/lib/i18n-js/cli/version_command.rb +18 -0
  36. data/lib/i18n-js/cli.rb +66 -0
  37. data/lib/i18n-js/embed_fallback_translations_plugin.rb +70 -0
  38. data/lib/i18n-js/export_files_plugin.rb +103 -0
  39. data/lib/i18n-js/lint.js +150645 -0
  40. data/lib/i18n-js/lint.ts +196 -0
  41. data/lib/i18n-js/listen.rb +96 -0
  42. data/lib/i18n-js/plugin.rb +103 -0
  43. data/lib/i18n-js/schema.rb +216 -0
  44. data/lib/i18n-js/sort_hash.rb +12 -0
  45. data/lib/i18n-js/version.rb +5 -0
  46. data/lib/i18n-js.rb +107 -1
  47. data/package.json +5 -20
  48. metadata +153 -182
  49. data/.editorconfig +0 -24
  50. data/.npmignore +0 -27
  51. data/.travis.yml +0 -37
  52. data/Appraisals +0 -36
  53. data/app/assets/javascripts/i18n/filtered.js.erb +0 -23
  54. data/app/assets/javascripts/i18n/shims.js +0 -208
  55. data/app/assets/javascripts/i18n/translations.js +0 -3
  56. data/app/assets/javascripts/i18n.js +0 -1091
  57. data/gemfiles/i18n_0_6.gemfile +0 -7
  58. data/gemfiles/i18n_0_7.gemfile +0 -7
  59. data/gemfiles/i18n_0_8.gemfile +0 -7
  60. data/gemfiles/i18n_0_9.gemfile +0 -7
  61. data/gemfiles/i18n_1_0.gemfile +0 -7
  62. data/gemfiles/i18n_1_1.gemfile +0 -7
  63. data/gemfiles/i18n_1_2.gemfile +0 -7
  64. data/gemfiles/i18n_1_3.gemfile +0 -7
  65. data/gemfiles/i18n_1_4.gemfile +0 -7
  66. data/gemfiles/i18n_1_5.gemfile +0 -7
  67. data/gemfiles/i18n_1_6.gemfile +0 -7
  68. data/lib/i18n/js/dependencies.rb +0 -63
  69. data/lib/i18n/js/engine.rb +0 -87
  70. data/lib/i18n/js/fallback_locales.rb +0 -70
  71. data/lib/i18n/js/formatters/base.rb +0 -23
  72. data/lib/i18n/js/formatters/js.rb +0 -31
  73. data/lib/i18n/js/formatters/json.rb +0 -13
  74. data/lib/i18n/js/middleware.rb +0 -82
  75. data/lib/i18n/js/private/hash_with_symbol_keys.rb +0 -36
  76. data/lib/i18n/js/segment.rb +0 -75
  77. data/lib/i18n/js/utils.rb +0 -65
  78. data/lib/i18n/js/version.rb +0 -7
  79. data/lib/i18n/js.rb +0 -259
  80. data/lib/rails/generators/i18n/js/config/config_generator.rb +0 -19
  81. data/lib/rails/generators/i18n/js/config/templates/i18n-js.yml +0 -27
  82. data/lib/tasks/export.rake +0 -8
  83. data/spec/fixtures/custom_path.yml +0 -5
  84. data/spec/fixtures/default.yml +0 -5
  85. data/spec/fixtures/erb.yml +0 -5
  86. data/spec/fixtures/except_condition.yml +0 -7
  87. data/spec/fixtures/js_export_dir_custom.yml +0 -7
  88. data/spec/fixtures/js_export_dir_none.yml +0 -6
  89. data/spec/fixtures/js_extend_parent.yml +0 -6
  90. data/spec/fixtures/js_extend_segment.yml +0 -6
  91. data/spec/fixtures/js_file_per_locale.yml +0 -7
  92. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml +0 -4
  93. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_hash.yml +0 -6
  94. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale.yml +0 -4
  95. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml +0 -4
  96. data/spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml +0 -4
  97. data/spec/fixtures/js_file_per_locale_without_fallbacks.yml +0 -4
  98. data/spec/fixtures/js_file_with_namespace_and_pretty_print.yml +0 -7
  99. data/spec/fixtures/js_sort_translation_keys_false.yml +0 -6
  100. data/spec/fixtures/js_sort_translation_keys_true.yml +0 -6
  101. data/spec/fixtures/json_only.yml +0 -18
  102. data/spec/fixtures/locales.yml +0 -95
  103. data/spec/fixtures/merge_plurals.yml +0 -6
  104. data/spec/fixtures/multiple_conditions.yml +0 -7
  105. data/spec/fixtures/multiple_conditions_per_locale.yml +0 -7
  106. data/spec/fixtures/multiple_files.yml +0 -7
  107. data/spec/fixtures/no_config.yml +0 -2
  108. data/spec/fixtures/no_scope.yml +0 -4
  109. data/spec/fixtures/simple_scope.yml +0 -5
  110. data/spec/js/currency.spec.js +0 -62
  111. data/spec/js/current_locale.spec.js +0 -19
  112. data/spec/js/dates.spec.js +0 -275
  113. data/spec/js/defaults.spec.js +0 -31
  114. data/spec/js/extend.spec.js +0 -110
  115. data/spec/js/interpolation.spec.js +0 -124
  116. data/spec/js/jasmine/MIT.LICENSE +0 -20
  117. data/spec/js/jasmine/jasmine-html.js +0 -190
  118. data/spec/js/jasmine/jasmine.css +0 -166
  119. data/spec/js/jasmine/jasmine.js +0 -2476
  120. data/spec/js/jasmine/jasmine_favicon.png +0 -0
  121. data/spec/js/locales.spec.js +0 -31
  122. data/spec/js/localization.spec.js +0 -54
  123. data/spec/js/numbers.spec.js +0 -170
  124. data/spec/js/placeholder.spec.js +0 -24
  125. data/spec/js/pluralization.spec.js +0 -211
  126. data/spec/js/prepare_options.spec.js +0 -41
  127. data/spec/js/require.js +0 -2083
  128. data/spec/js/specs.html +0 -49
  129. data/spec/js/specs_requirejs.html +0 -72
  130. data/spec/js/translate.spec.js +0 -284
  131. data/spec/js/translations.js +0 -163
  132. data/spec/js/utility_functions.spec.js +0 -20
  133. data/spec/ruby/i18n/js/fallback_locales_spec.rb +0 -84
  134. data/spec/ruby/i18n/js/segment_spec.rb +0 -219
  135. data/spec/ruby/i18n/js/utils_spec.rb +0 -106
  136. data/spec/ruby/i18n/js_spec.rb +0 -663
  137. data/spec/spec_helper.rb +0 -79
  138. data/yarn.lock +0 -131
data/README.md CHANGED
@@ -1,927 +1,573 @@
1
- # I18n.js
1
+ <p align="center">
2
+ <img width="250" height="58" src="https://github.com/fnando/i18n-js/raw/main/images/i18njs.png" alt="i18n.js">
3
+ </p>
4
+
5
+ <p align="center">
6
+ Export <a href="https://rubygems.org/gems/i18n">i18n</a> translations to JSON.
7
+ <br>
8
+ A perfect fit if you want to export translations to JavaScript.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <small>
13
+ Oh, you don't use Ruby? No problem! You can still use i18n-js
14
+ <br>
15
+ and the
16
+ <a href="https://www.npmjs.com/package/i18n-js/v/latest">companion JavaScript package</a>.
17
+ </small>
18
+ </p>
19
+
20
+ <p align="center">
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" alt="Gem"></a>
23
+ <a href="https://rubygems.org/gems/i18n-js"><img src="https://img.shields.io/gem/dt/i18n-js.svg" alt="Gem"></a>
24
+ <a href="https://tldrlegal.com/license/mit-license"><img src="https://img.shields.io/:License-MIT-blue.svg" alt="MIT License"></a>
25
+ </p>
26
+
27
+ ## Installation
2
28
 
3
- [![Gem Version](http://img.shields.io/gem/v/i18n-js.svg?style=flat-square)](http://badge.fury.io/rb/i18n-js)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
5
-
6
- [![Build Status](http://img.shields.io/travis/fnando/i18n-js.svg?style=flat-square)](https://travis-ci.org/fnando/i18n-js)
7
- [![Coverage Status](http://img.shields.io/coveralls/fnando/i18n-js.svg?style=flat-square)](https://coveralls.io/r/fnando/i18n-js)
8
-
9
- [![Gitter](https://img.shields.io/badge/gitter-join%20chat-1dce73.svg?style=flat-square)](https://gitter.im/fnando/i18n-js)
10
-
11
- It's a small library to provide the Rails I18n translations on the JavaScript.
12
-
13
- Features:
14
-
15
- - Pluralization
16
- - Date/Time localization
17
- - Number localization
18
- - Locale fallback
19
- - Asset pipeline support
20
- - Lots more! :)
21
-
22
- ## Version Notice
23
- The `master` branch (including this README) is for latest `3.0.0` instead of `2.x`.
24
-
25
-
26
- ## Usage
27
-
28
- ### Installation
29
+ ```bash
30
+ gem install i18n-js
31
+ ```
29
32
 
30
- #### Rails app
33
+ Or add the following line to your project's Gemfile:
31
34
 
32
- Add the gem to your Gemfile.
33
35
  ```ruby
34
36
  gem "i18n-js"
35
37
  ```
36
38
 
37
- #### Rails app with [Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html)
38
-
39
- If you're using the [asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html),
40
- then you must add the following line to your `app/assets/javascripts/application.js`.
41
-
42
- ```javascript
43
- //
44
- // This is optional (in case you have `I18n is not defined` error)
45
- // If you want to put this line, you must put it BEFORE `i18n/translations`
46
- //= require i18n
47
- // Some people even need to add the extension to make it work, see https://github.com/fnando/i18n-js/issues/283
48
- //= require i18n.js
49
- //
50
- // This is a must
51
- //= require i18n/translations
52
- ```
53
-
54
- #### Rails app without [Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html)
55
-
56
-
57
- First, put this in your `application.html` (layout file).
58
- Then get the JS files following the instructions below.
59
- ```erb
60
- <%# This is just an example, you can put `i18n.js` and `translations.js` anywhere you like %>
61
- <%# Unlike the Asset Pipeline example, you need to require both **in order** %>
62
- <%= javascript_include_tag "i18n" %>
63
- <%= javascript_include_tag "translations", skip_pipeline: true %>
64
- ```
39
+ ## Usage
65
40
 
66
- **There are two ways to get `translations.js` (For Rails app without Asset Pipeline).**
41
+ About patterns:
67
42
 
68
- 1. This `translations.js` file can be automatically generated by the `I18n::JS::Middleware`.
69
- Just add `config.middleware.use I18n::JS::Middleware` to your `config/application.rb` file.
70
- 2. If you can't or prefer not to generate this file,
71
- you can move the middleware line to your `config/environments/development.rb` file
72
- and run `rake i18n:js:export` before deploying.
73
- This will export all translation files, including the custom scopes
74
- you may have defined on `config/i18n-js.yml`.
75
- If `I18n.available_locales` is set (e.g. in your Rails `config/application.rb` file)
76
- then only the specified locales will be exported.
77
- Current version of `i18n.js` will also be exported to avoid version mismatching by downloading.
43
+ - Patterns can use `*` as a wildcard and can appear more than once.
44
+ - `*` will include everything
45
+ - `*.messages.*`
46
+ - Patterns starting with `!` are excluded.
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.
78
51
 
79
- #### Export Configuration (For translations)
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.
80
56
 
81
- Exported translation files generated by `I18n::JS::Middleware` or `rake i18n:js:export` can be customized with config file `config/i18n-js.yml`
82
- (use `rails generate i18n:js:config` to create it).
83
- You can even get more files generated to different folders and with different translations to best suit your needs.
84
- The config file also affects developers using Asset Pipeline to require translations.
85
- Except the option `file`, since all translations are required by adding `//= require i18n/translations`.
57
+ The config file:
86
58
 
87
- Examples:
88
- ```yaml
59
+ ```yml
60
+ ---
89
61
  translations:
90
- - file: 'public/javascripts/path-to-your-messages-file.js'
91
- only: '*.date.formats'
92
- - file: 'public/javascripts/path-to-your-second-file.js'
93
- only: ['*.activerecord', '*.admin.*.title']
94
- ```
62
+ - file: app/frontend/locales/en.json
63
+ patterns:
64
+ - "*"
65
+ - "!*.activerecord"
66
+ - "!*.errors"
67
+ - "!*.number.nth"
95
68
 
96
- If `only` is omitted all the translations will be saved. Also, make sure you add that initial `*`; it specifies that all languages will be exported. If you want to export only one language, you can do something like this:
97
- ```yaml
98
- translations:
99
- - file: 'public/javascripts/en.js'
100
- only: 'en.*'
101
- - file: 'public/javascripts/pt-BR.js'
102
- only: 'pt-BR.*'
69
+ - file: app/frontend/locales/:locale.:digest.json
70
+ patterns:
71
+ - "*"
103
72
  ```
104
73
 
105
- Optionally, you can auto generate a translation file per available locale if you specify the `%{locale}` placeholder.
106
- ```yaml
107
- translations:
108
- - file: "public/javascripts/i18n/%{locale}.js"
109
- only: '*'
110
- - file: "public/javascripts/frontend/i18n/%{locale}.js"
111
- only: ['*.frontend', '*.users.*']
112
- ```
113
-
114
- You can also include ERB in your config file.
115
- ```yaml
116
- translations:
117
- <% Widgets.each do |widget| %>
118
- - file: <%= "'#{widget.file}'" %>
119
- only: <%= "'#{widget.only}'" %>
120
- <% end %>
121
- ```
122
-
123
- You are able to exclude certain phrases or whole groups of phrases by
124
- specifying the YAML key(s) in the `except` configuration option. The outputted
125
- JS translations file (exported or generated by the middleware) will omit any
126
- keys listed in `except` configuration param:
127
-
128
- ```yaml
129
- translations:
130
- - except: ['*.active_admin', '*.ransack', '*.activerecord.errors']
131
- ```
132
-
133
-
134
- #### Export Configuration (For other things)
74
+ The output path can use the following placeholders:
135
75
 
136
- - `I18n::JS.config_file_path`
137
- Expected Type: `String`
138
- Default: `config/i18n-js.yml`
139
- Behaviour: Try to read the config file from that location
76
+ - `:locale`: the language that's being exported.
77
+ - `:digest`: the MD5 hex digest of the exported file.
140
78
 
141
- - `I18n::JS.export_i18n_js_dir_path`
142
- Expected Type: `String`
143
- Default: `public/javascripts`
144
- Behaviour:
145
- - Any `String`: considered as a relative path for a folder to `Rails.root` and export `i18n.js` to that folder for `rake i18n:js:export`
146
- - Any non-`String` (`nil`, `false`, `:none`, etc): Disable `i18n.js` exporting
79
+ The config file is processed as erb, so you can have dynamic content on it if
80
+ you want. The following example shows how to use groups from a variable.
147
81
 
148
- - `I18n::JS.sort_translation_keys`
149
- Expected Type: `Boolean`
150
- Default: `true`
151
- Behaviour:
152
- - Sets whether or not to deep sort all translation keys in order to generate identical output for the same translations
153
- - Set to true to ensure identical asset fingerprints for the asset pipeline
154
-
155
- - You may also set `export_i18n_js` and `sort_translation_keys` in your config file, e.g.:
156
-
157
- ```yaml
158
- export_i18n_js: false
159
- # OR
160
- export_i18n_js: "my/path"
161
-
162
- sort_translation_keys: false
82
+ ```yml
83
+ ---
84
+ <% group = "{en,pt}" %>
163
85
 
164
86
  translations:
165
- - ...
87
+ - file: app/frontend/translations.json
88
+ patterns:
89
+ - "<%= group %>.*"
90
+ - "!<%= group %>.activerecord"
91
+ - "!<%= group %>.errors"
92
+ - "!<%= group %>.number.nth"
166
93
  ```
167
94
 
168
- To find more examples on how to use the configuration file please refer to the tests.
169
-
170
- #### Fallbacks
171
-
172
- If you specify the `fallbacks` option, you will be able to fill missing translations with those inside fallback locale(s).
173
- Default value is `true`.
95
+ The Ruby API:
174
96
 
175
- Examples:
176
- ```yaml
177
- fallbacks: true
178
-
179
- translations:
180
- - file: "public/javascripts/i18n/%{locale}.js"
181
- only: '*'
182
- ```
183
- This will enable merging fallbacks into each file. (set to `false` to disable).
184
- If you use `I18n` with fallbacks, the fallbacks defined there will be used.
185
- Otherwise `I18n.default_locale` will be used.
186
-
187
- ```yaml
188
- fallbacks: :de
97
+ ```ruby
98
+ require "i18n-js"
189
99
 
190
- translations:
191
- - file: "public/javascripts/i18n/%{locale}.js"
192
- only: '*'
100
+ I18nJS.call(config_file: "config/i18n.yml")
101
+ I18nJS.call(config: config)
193
102
  ```
194
- Here, the specified locale `:de` will be used as fallback for all locales.
195
103
 
196
- ```yaml
197
- fallbacks:
198
- fr: ["de", "en"]
199
- de: "en"
104
+ The CLI API:
200
105
 
201
- translations:
202
- - file: "public/javascripts/i18n/%{locale}.js"
203
- only: '*'
204
- ```
205
- Fallbacks defined will be used, if not defined (e.g. `:pl`) `I18n.fallbacks` or `I18n.default_locale` will be used.
106
+ ```console
107
+ $ i18n --help
108
+ Usage: i18n COMMAND FLAGS
206
109
 
207
- ```yaml
208
- fallbacks: :default_locale
110
+ Commands:
209
111
 
210
- translations:
211
- - file: "public/javascripts/i18n/%{locale}.js"
212
- only: '*'
213
- ```
214
- Setting the option to `:default_locale` will enforce the fallback to use the `I18n.default_locale`, ignoring `I18n.fallbacks`.
215
-
216
- Examples:
217
- ```yaml
218
- fallbacks: false
112
+ - init: Initialize a project
113
+ - export: Export translations as JSON files
114
+ - version: Show package version
115
+ - plugins: List plugins that will be activated
116
+ - lint:translations: Check for missing translations
117
+ - lint:scripts: Lint files using TypeScript
219
118
 
220
- translations:
221
- - file: "public/javascripts/i18n/%{locale}.js"
222
- only: '*'
119
+ Run `i18n COMMAND --help` for more information on specific commands.
223
120
  ```
224
- You must disable this feature by setting the option to `false`.
225
121
 
226
- To find more examples on how to use the configuration file please refer to the tests.
122
+ By default, `i18n` will use `config/i18n.yml` and `config/environment.rb` as the
123
+ configuration files. If you don't have these files, then you'll need to specify
124
+ both `--config` and `--require`.
227
125
 
126
+ ### Plugins
228
127
 
229
- #### Namespace
128
+ #### Built-in plugins:
230
129
 
231
- Setting the `namespace` option will change the namespace of the output Javascript file to something other than `I18n`.
232
- This can be useful in no-conflict scenarios. Example:
233
-
234
- ```yaml
235
- translations:
236
- - file: "public/javascripts/i18n/translations.js"
237
- namespace: "MyNamespace"
238
- ```
239
-
240
- will create:
241
-
242
- ```
243
- MyNamespace.translations || (MyNamespace.translations = {});
244
- MyNamespace.translations["en"] = { ... }
245
- ```
130
+ ##### `embed_fallback_translations`:
246
131
 
132
+ Embed fallback translations inferred from the default locale. This can be useful
133
+ in cases where you have multiple large translation files and don't want to load
134
+ the default locale together with the target locale.
247
135
 
248
- #### Pretty Print
249
-
250
- Set the `pretty_print` option if you would like whitespace and indentation in your output file (default: false)
136
+ To use it, add the following to your configuration file:
251
137
 
252
138
  ```yaml
253
- translations:
254
- - file: "public/javascripts/i18n/translations.js"
255
- pretty_print: true
139
+ embed_fallback_translations:
140
+ enabled: true
256
141
  ```
257
142
 
143
+ ##### `export_files`:
258
144
 
259
- #### Javascript Deep Merge (:js_extend option)
260
-
261
- By default, the output file Javascript will call the `I18n.extend` method to ensure that newly loaded locale
262
- files are deep-merged with any locale data already in memory. To disable this either globally or per-file,
263
- set the `js_extend` option to false
145
+ By default, i18n-js will export only JSON files out of your translations. This
146
+ plugin allows exporting other file formats. To use it, add the following to your
147
+ configuration file:
264
148
 
265
149
  ```yaml
266
- js_extend: false # this will disable Javascript I18n.extend globally
267
- translations:
268
- - file: "public/javascripts/i18n/translations.js"
269
- js_extend: false # this will disable Javascript I18n.extend for this file
150
+ export_files:
151
+ enabled: true
152
+ files:
153
+ - template: path/to/template.erb
154
+ output: "%{dir}/%{base_name}.ts"
270
155
  ```
271
156
 
157
+ You can export multiple files by define more entries.
272
158
 
273
- #### Vanilla JavaScript
274
-
275
- Just add the `i18n.js` file to your page. You'll have to build the translations object
276
- by hand or using your favorite programming language. More info below.
277
-
159
+ The output name can use the following placeholders:
278
160
 
279
- #### Via NPM with webpack and CommonJS
161
+ - `%{dir}`: the directory where the translation file is.
162
+ - `%{name}`: file name with extension.
163
+ - `%{base_name}`: file name without extension.
164
+ - `%{digest}`: MD5 hexdigest from the generated file.
280
165
 
281
-
282
- Add the following line to your package.json dependencies
283
- where version is the version you want
284
- ```javascript
285
- "i18n-js": "{version_constraint}"
286
-
287
- // Or if you want unreleased version
288
- // npm install requires it to be the gzipped tarball, see [npm install](https://www.npmjs.org/doc/cli/npm-install.html)
289
- "i18n-js": "https://github.com/fnando/i18n-js/archive/{tag_name_or_branch_name_or_commit_sha}.tar.gz"
290
- ```
291
- Run npm install then use via
292
- ```javascript
293
- var i18n = require("i18n-js");
294
- ```
295
-
296
-
297
- ### Setting up
298
-
299
- You **don't** need to set up a thing. The default settings will work just okay. But if you want to split translations into several files or specify specific contexts, you can follow the rest of this setting up section.
300
-
301
- Set your locale is easy as
302
- ```javascript
303
- I18n.defaultLocale = "pt-BR";
304
- I18n.locale = "pt-BR";
305
- I18n.currentLocale();
306
- // pt-BR
307
- ```
308
-
309
- **NOTE:** You can now apply your configuration **before I18n** is loaded like this:
310
- ```javascript
311
- I18n = {} // You must define this object in top namespace, which should be `window`
312
- I18n.defaultLocale = "pt-BR";
313
- I18n.locale = "pt-BR";
314
-
315
- // Load I18n from `i18n.js`, `application.js` or whatever
316
-
317
- I18n.currentLocale();
318
- // pt-BR
319
- ```
320
-
321
- In practice, you'll have something like the following in your `application.html.erb`:
166
+ The template file must be a valid eRB template. You can execute arbitrary Ruby
167
+ code, so be careful. An example of how you can generate a file can be seen
168
+ below:
322
169
 
323
170
  ```erb
324
- <script type="text/javascript">
325
- I18n.defaultLocale = "<%= I18n.default_locale %>";
326
- I18n.locale = "<%= I18n.locale %>";
327
- </script>
328
- ```
329
-
330
- You can use translate your messages:
331
-
332
- ```javascript
333
- I18n.t("some.scoped.translation");
334
- // or translate with explicit setting of locale
335
- I18n.t("some.scoped.translation", {locale: "fr"});
336
- ```
337
-
338
- You can also interpolate values:
339
-
340
- ```javascript
341
- // You need the `translations` object setup first
342
- I18n.translations["en"] = {
343
- greeting: "Hello %{name}"
344
- }
345
-
346
- I18n.t("greeting", {name: "John Doe"});
347
- ```
348
- You can set default values for missing scopes:
349
- ```javascript
350
- // simple translation
351
- I18n.t("some.missing.scope", {defaultValue: "A default message"});
352
-
353
- // with interpolation
354
- I18n.t("noun", {defaultValue: "I'm a {{noun}}", noun: "Mac"});
355
- ```
171
+ /* eslint-disable */
172
+ <%= banner %>
356
173
 
357
- You can also provide a list of default fallbacks for missing scopes:
174
+ import { i18n } from "config/i18n";
358
175
 
359
- ```javascript
360
- // As a scope
361
- I18n.t("some.missing.scope", {defaults: [{scope: "some.existing.scope"}]});
362
-
363
- // As a simple translation
364
- I18n.t("some.missing.scope", {defaults: [{message: "Some message"}]});
176
+ i18n.store(<%= JSON.pretty_generate(translations) %>);
365
177
  ```
366
178
 
367
- Default values must be provided as an array of hashs where the key is the
368
- type of translation desired, a `scope` or a `message`. The translation returned
369
- will be either the first scope recognized, or the first message defined.
179
+ This template is loading the instance from `config/i18n` and storing the
180
+ translations that have been loaded. The
181
+ `banner(comment: "// ", include_time: true)` method is built-in. The generated
182
+ file will look something like this:
370
183
 
371
- The translation will fallback to the `defaultValue` translation if no scope
372
- in `defaults` matches and if no default of type `message` is found.
184
+ ```typescript
185
+ /* eslint-disable */
186
+ // File generated by i18n-js on 2022-12-10 15:37:00 +0000
373
187
 
374
- Translation fallback can be enabled by enabling the `I18n.fallbacks` option:
188
+ import { i18n } from "config/i18n";
375
189
 
376
- ```erb
377
- <script type="text/javascript">
378
- I18n.fallbacks = true;
379
- </script>
190
+ i18n.store({
191
+ en: {
192
+ "bunny rabbit adventure": "bunny rabbit adventure",
193
+ "hello sunshine!": "hello sunshine!",
194
+ "time for bed!": "time for bed!",
195
+ },
196
+ es: {
197
+ "bunny rabbit adventure": "conejito conejo aventura",
198
+ bye: "adios",
199
+ "time for bed!": "hora de acostarse!",
200
+ },
201
+ pt: {
202
+ "bunny rabbit adventure": "a aventura da coelhinha",
203
+ bye: "tchau",
204
+ "time for bed!": "hora de dormir!",
205
+ },
206
+ });
380
207
  ```
381
208
 
382
- By default missing translations will first be looked for in less
383
- specific versions of the requested locale and if that fails by taking
384
- them from your `I18n.defaultLocale`.
385
-
386
- ```javascript
387
- // if I18n.defaultLocale = "en" and translation doesn't exist
388
- // for I18n.locale = "de-DE" this key will be taken from "de" locale scope
389
- // or, if that also doesn't exist, from "en" locale scope
390
- I18n.t("some.missing.scope");
391
- ```
209
+ #### Plugin API
392
210
 
393
- Custom fallback rules can also be specified for a particular language. There
394
- are three different ways of doing it so:
211
+ You can transform the exported translations by adding plugins. A plugin must
212
+ inherit from `I18nJS::Plugin` and can have 4 class methods (they're all optional
213
+ and will default to a noop implementation). For real examples, see [lib/i18n-js/embed_fallback_translations_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/embed_fallback_translations_plugin.rb) and [lib/i18n-js/export_files_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/export_files_plugin.rb)
395
214
 
396
- ```javascript
397
- I18n.locales.no = ["nb", "en"];
398
- I18n.locales.no = "nb";
399
- I18n.locales.no = function(locale){ return ["nb"]; };
400
- ```
401
-
402
- By default a missing translation will be displayed as
215
+ ```ruby
216
+ # frozen_string_literal: true
217
+
218
+ module I18nJS
219
+ class SamplePlugin < I18nJS::Plugin
220
+ # This method is responsible for transforming the translations. The
221
+ # translations you'll receive may be already be filtered by other plugins
222
+ # and by the default filtering itself. If you need to access the original
223
+ # translations, use `I18nJS.translations`.
224
+ def transform(translations:)
225
+ # transform `translations` here…
226
+
227
+ translations
228
+ end
403
229
 
404
- [missing "name of scope" translation]
230
+ # In case your plugin accepts configuration, this is where you must validate
231
+ # the configuration, making sure only valid keys and type is provided.
232
+ # If the configuration contains invalid data, then you must raise an
233
+ # exception using something like
234
+ # `raise I18nJS::Schema::InvalidError, error_message`.
235
+ #
236
+ # Notice the validation will only happen when the plugin configuration is
237
+ # set (i.e. the configuration contains your config key).
238
+ def validate_schema
239
+ # validate plugin schema here…
240
+ end
405
241
 
406
- While you are developing or if you do not want to provide a translation
407
- in the default language you can set
242
+ # This method must set up the basic plugin configuration, like adding the
243
+ # config's root key in case your plugin accepts configuration (defined via
244
+ # the config file).
245
+ #
246
+ # If you don't add this key, the linter will prevent non-default keys from
247
+ # being added to the configuration file.
248
+ def setup
249
+ # If you plugin has configuration, uncomment the line below
250
+ # I18nJS::Schema.root_keys << config_key
251
+ end
408
252
 
409
- ```javascript
410
- I18n.missingBehaviour='guess';
253
+ # This method is called whenever `I18nJS.call(**kwargs)` finishes exporting
254
+ # JSON files based on your configuration.
255
+ #
256
+ # You can use it to further process exported files, or generate new files
257
+ # based on the translations that have been exported.
258
+ def after_export(files:)
259
+ # process exported files here…
260
+ end
261
+ end
262
+ end
411
263
  ```
412
264
 
413
- this will take the last section of your scope and guess the intended value.
414
- Camel case becomes lower cased text and underscores are replaced with space
265
+ The class `I18nJS::Plugin` implements some helper methods that you can use:
415
266
 
416
- questionnaire.whatIsYourFavorite_ChristmasPresent
267
+ - `I18nJS::Plugin#config_key`: the configuration key that was inferred out of
268
+ your plugin's class name.
269
+ - `I18nJS::Plugin#config`: the plugin configuration.
270
+ - `I18nJS::Plugin#enabled?`: whether the plugin is enabled or not based on the
271
+ plugin's configuration.
417
272
 
418
- becomes "what is your favorite Christmas present"
273
+ To distribute this plugin, you need to create a gem package that matches the
274
+ pattern `i18n-js/*_plugin.rb`. You can test whether your plugin will be found by
275
+ installing your gem, opening a iRB session and running
276
+ `Gem.find_files("i18n-js/*_plugin.rb")`. If your plugin is not listed, then you
277
+ need to double check your gem load path and see why the file is not being
278
+ loaded.
419
279
 
420
- In order to still detect untranslated strings, you can
421
- i18n.missingTranslationPrefix to something like:
422
- ```javascript
423
- I18n.missingTranslationPrefix = 'EE: ';
424
- ```
425
-
426
- And result will be:
427
- ```javascript
428
- "EE: what is your favorite Christmas present"
429
- ```
280
+ ### Listing missing translations
430
281
 
431
- This will help you doing automated tests against your localisation assets.
282
+ To list missing and extraneous translations, you can use
283
+ `i18n lint:translations`. This command will load your translations similarly to
284
+ how `i18n export` does, but will output the list of keys that don't have a
285
+ matching translation against the default locale. Here's an example:
432
286
 
433
- Some people prefer returning `null` for missing translation:
434
- ```javascript
435
- I18n.missingTranslation = function () { return undefined; };
287
+ ```console
288
+ $ i18n lint:translations
289
+ => Config file: "./config/i18n.yml"
290
+ => Require file: "./config/environment.rb"
291
+ => Check "./config/i18n.yml" for ignored keys.
292
+ => en: 232 translations
293
+ => pt-BR: 5 missing, 1 extraneous, 1 ignored
294
+ - pt-BR.actors.github.metrics (missing)
295
+ - pt-BR.actors.github.metrics_hint (missing)
296
+ - pt-BR.actors.github.repo_metrics (missing)
297
+ - pt-BR.actors.github.repository (missing)
298
+ - pt-BR.actors.github.user_metrics (missing)
299
+ - pt-BR.github.repository (extraneous)
436
300
  ```
437
301
 
438
- Pluralization is possible as well and by default provides English rules:
302
+ This command will exist with status 1 whenever there are missing translations.
303
+ This way you can use it as a CI linting.
439
304
 
440
- ```javascript
441
- I18n.t("inbox.counting", {count: 10}); // You have 10 messages
442
- ```
305
+ You can ignore keys by adding a list to the config file:
443
306
 
444
- The sample above expects the following translation:
307
+ ```yml
308
+ ---
309
+ translations:
310
+ - file: app/frontend/locales/en.json
311
+ patterns:
312
+ - "*"
313
+ - "!*.activerecord"
314
+ - "!*.errors"
315
+ - "!*.number.nth"
316
+
317
+ - file: app/frontend/locales/:locale.:digest.json
318
+ patterns:
319
+ - "*"
320
+
321
+ lint_translations:
322
+ ignore:
323
+ - en.mailer.login.subject
324
+ - en.mailer.login.body
325
+ ```
326
+
327
+ > **Note**:
328
+ >
329
+ > In order to avoid mistakenly ignoring keys, this configuration option only
330
+ > accepts the full translation scope, rather than accepting a pattern like
331
+ > `pt.ignored.scope.*`.
332
+
333
+ ### Linting your JavaScript/TypeScript files
334
+
335
+ To lint your script files and check for missing translations (which can signal
336
+ that you're either using wrong scopes or forgot to add the translation), use
337
+ `i18n lint:scripts`. This command will parse your JavaScript/TypeScript files
338
+ and extract all scopes being used. This command requires a Node.js runtime. You
339
+ can either specify one via `--node-path`, or let the plugin infer a binary from
340
+ your `$PATH`.
341
+
342
+ The comparison will be made against the export JSON files, which means it'll
343
+ consider transformations performed by plugins (e.g. the output files may be
344
+ affected by `embed_fallback_translations` plugin).
345
+
346
+ The translations that will be extract must be called as one of the following
347
+ ways:
348
+
349
+ - `i18n.t(scope, options)`
350
+ - `i18n.translate(scope, options)`
351
+ - `t(scope, options)`
352
+
353
+ Notice that only literal strings can be used, as in `i18n.t("message")`. If
354
+ you're using dynamic scoping through variables (e.g.
355
+ `const scope = "message"; i18n.t(scope)`), they will be skipped.
356
+
357
+ ```console
358
+ $ i18n lint:scripts
359
+ => Config file: "./config/i18n.yml"
360
+ => Require file: "./config/environment.rb"
361
+ => Node: "/Users/fnando/.asdf/shims/node"
362
+ => Available locales: [:en, :es, :pt]
363
+ => Patterns: ["!(node_modules)/**/*.js", "!(node_modules)/**/*.ts", "!(node_modules)/**/*.jsx", "!(node_modules)/**/*.tsx"]
364
+ => 9 translations, 11 missing, 4 ignored
365
+ - test/scripts/lint/file.js:1:1: en.js.missing
366
+ - test/scripts/lint/file.js:1:1: es.js.missing
367
+ - test/scripts/lint/file.js:1:1: pt.js.missing
368
+ - test/scripts/lint/file.js:2:8: en.base.js.missing
369
+ - test/scripts/lint/file.js:2:8: es.base.js.missing
370
+ - test/scripts/lint/file.js:2:8: pt.base.js.missing
371
+ - test/scripts/lint/file.js:4:8: en.js.missing
372
+ - test/scripts/lint/file.js:4:8: es.js.missing
373
+ - test/scripts/lint/file.js:4:8: pt.js.missing
374
+ - test/scripts/lint/file.js:6:1: en.another_ignore_scope
375
+ - test/scripts/lint/file.js:6:1: es.another_ignore_scope
376
+ ```
377
+
378
+ This command will list all locales and their missing translations. Avoid listing
379
+ a particular translation, you can set `lint.ignore` on your config file.
445
380
 
446
381
  ```yaml
447
- en:
448
- inbox:
449
- counting:
450
- one: You have 1 new message
451
- other: You have {{count}} new messages
452
- zero: You have no messages
453
- ```
454
-
455
- **NOTE:** Rails I18n recognizes the `zero` option.
456
-
457
- If you need special rules just define them for your language, for example Russian, just add a new pluralizer:
458
-
459
- ```javascript
460
- I18n.pluralization["ru"] = function (count) {
461
- var key = count % 10 == 1 && count % 100 != 11 ? "one" : [2, 3, 4].indexOf(count % 10) >= 0 && [12, 13, 14].indexOf(count % 100) < 0 ? "few" : count % 10 == 0 || [5, 6, 7, 8, 9].indexOf(count % 10) >= 0 || [11, 12, 13, 14].indexOf(count % 100) >= 0 ? "many" : "other";
462
- return [key];
463
- };
464
- ```
465
-
466
- You can find all rules on <http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html>.
467
-
468
- If you're using the same scope over and over again, you may use the `scope` option.
469
-
470
- ```javascript
471
- var options = {scope: "activerecord.attributes.user"};
472
-
473
- I18n.t("name", options);
474
- I18n.t("email", options);
475
- I18n.t("username", options);
476
- ```
477
-
478
- You can also provide an array as scope.
479
-
480
- ```javascript
481
- // use the greetings.hello scope
482
- I18n.t(["greetings", "hello"]);
483
- ```
484
-
485
- #### Number formatting
486
-
487
- Similar to Rails helpers, you have localized number and currency formatting.
488
-
489
- ```javascript
490
- I18n.l("currency", 1990.99);
491
- // $1,990.99
492
-
493
- I18n.l("number", 1990.99);
494
- // 1,990.99
495
-
496
- I18n.l("percentage", 123.45);
497
- // 123.450%
498
- ```
499
-
500
- To have more control over number formatting, you can use the
501
- `I18n.toNumber`, `I18n.toPercentage`, `I18n.toCurrency` and `I18n.toHumanSize`
502
- functions.
382
+ ---
383
+ translations:
384
+ - file: app/frontend/translations.json
385
+ patterns:
386
+ - "*"
503
387
 
504
- ```javascript
505
- I18n.toNumber(1000); // 1,000.000
506
- I18n.toCurrency(1000); // $1,000.00
507
- I18n.toPercentage(100); // 100.000%
388
+ lint_scripts:
389
+ ignore:
390
+ - ignore_scope # will ignore this scope on all languages
391
+ - pt.another_ignore_scope # will ignore this scope only on `pt`
508
392
  ```
509
393
 
510
- The `toNumber` and `toPercentage` functions accept the following options:
511
-
512
- - `precision`: defaults to `3`
513
- - `separator`: defaults to `.`
514
- - `delimiter`: defaults to `,`
515
- - `strip_insignificant_zeros`: defaults to `false`
394
+ You can also set the patterns that will be looked up. By default, it scans all
395
+ JavaScript and TypeScript files that don't live on `node_modules`.
516
396
 
517
- See some number formatting examples:
397
+ ```yaml
398
+ ---
399
+ translations:
400
+ - file: app/frontend/translations.json
401
+ patterns:
402
+ - "*"
518
403
 
519
- ```javascript
520
- I18n.toNumber(1000, {precision: 0}); // 1,000
521
- I18n.toNumber(1000, {delimiter: ".", separator: ","}); // 1.000,000
522
- I18n.toNumber(1000, {delimiter: ".", precision: 0}); // 1.000
404
+ lint:
405
+ patterns:
406
+ - "app/assets/**/*.ts"
523
407
  ```
524
408
 
525
- The `toCurrency` function accepts the following options:
526
-
527
- - `precision`: sets the level of precision
528
- - `separator`: sets the separator between the units
529
- - `delimiter`: sets the thousands delimiter
530
- - `format`: sets the format of the output string
531
- - `unit`: sets the denomination of the currency
532
- - `strip_insignificant_zeros`: defaults to `false`
533
- - `sign_first`: defaults to `true`
409
+ ## Automatically export translations
534
410
 
535
- You can provide only the options you want to override:
411
+ ### Using [watchman](https://facebook.github.io/watchman/)
536
412
 
537
- ```javascript
538
- I18n.toCurrency(1000, {precision: 0}); // $1,000
539
- ```
540
-
541
- The `toHumanSize` function accepts the following options:
542
-
543
- - `precision`: defaults to `1`
544
- - `separator`: defaults to `.`
545
- - `delimiter`: defaults to `""`
546
- - `strip_insignificant_zeros`: defaults to `false`
547
- - `format`: defaults to `%n%u`
413
+ Create a script at `bin/i18n-watch`.
548
414
 
549
- <!---->
415
+ ```bash
416
+ #!/usr/bin/env bash
417
+
418
+ root=`pwd`
419
+
420
+ watchman watch-del "$root"
421
+ watchman watch-project "$root"
422
+ watchman trigger-del "$root" i18n
423
+
424
+ watchman -j <<-JSON
425
+ [
426
+ "trigger",
427
+ "$root",
428
+ {
429
+ "name": "i18n",
430
+ "expression": [
431
+ "anyof",
432
+ ["match", "config/locales/**/*.yml", "wholename"],
433
+ ["match", "config/i18n.yml", "wholename"]
434
+ ],
435
+ "command": ["i18n", "export"]
436
+ }
437
+ ]
438
+ JSON
550
439
 
551
- ```javascript
552
- I18n.toHumanSize(1234); // 1KB
553
- I18n.toHumanSize(1234 * 1024); // 1MB
440
+ # If you're running this through Foreman,
441
+ # the uncomment the following lines:
442
+ # while true; do
443
+ # sleep 1
444
+ # done
554
445
  ```
555
446
 
447
+ Make it executable with `chmod +x bin/i18n-watch`. To watch for changes, run
448
+ `./bin/i18n-watch`. If you're using Foreman, make sure you uncommented the lines
449
+ that keep the process running (`while..`), and add something like the following
450
+ line to your Procfile:
556
451
 
557
- #### Date formatting
558
-
559
- ```javascript
560
- // accepted formats
561
- I18n.l("date.formats.short", "2009-09-18"); // yyyy-mm-dd
562
- I18n.l("time.formats.short", "2009-09-18 23:12:43"); // yyyy-mm-dd hh:mm:ss
563
- I18n.l("time.formats.short", "2009-11-09T18:10:34"); // JSON format with local Timezone (part of ISO-8601)
564
- I18n.l("time.formats.short", "2009-11-09T18:10:34Z"); // JSON format in UTC (part of ISO-8601)
565
- I18n.l("date.formats.short", 1251862029000); // Epoch time
566
- I18n.l("date.formats.short", "09/18/2009"); // mm/dd/yyyy
567
- I18n.l("date.formats.short", (new Date())); // Date object
568
452
  ```
569
-
570
- You can also add placeholders to the date format:
571
-
572
- ```javascript
573
- I18n.translations["en"] = {
574
- date: {
575
- formats: {
576
- ordinal_day: "%B %{day}"
577
- }
578
- }
579
- }
580
- I18n.l("date.formats.ordinal_day", "2009-09-18", { day: '18th' }); // Sep 18th
453
+ i18n: ./bin/i18n-watch
581
454
  ```
582
455
 
583
- If you prefer, you can use the `I18n.toTime` and `I18n.strftime` functions to format dates.
456
+ ### Using [guard](https://rubygems.org/gems/guard)
584
457
 
585
- ```javascript
586
- var date = new Date();
587
- I18n.toTime("date.formats.short", "2009-09-18");
588
- I18n.toTime("date.formats.short", date);
589
- I18n.strftime(date, "%d/%m/%Y");
590
- ```
458
+ Install [guard](https://rubygems.org/gems/guard) and
459
+ [guard-compat](https://rubygems.org/gems/guard-compat). Then create a Guardfile
460
+ with the following configuration:
591
461
 
592
- The accepted formats for `I18n.strftime` are:
593
-
594
- %a - The abbreviated weekday name (Sun)
595
- %A - The full weekday name (Sunday)
596
- %b - The abbreviated month name (Jan)
597
- %B - The full month name (January)
598
- %c - The preferred local date and time representation
599
- %d - Day of the month (01..31)
600
- %-d - Day of the month (1..31)
601
- %H - Hour of the day, 24-hour clock (00..23)
602
- %-H - Hour of the day, 24-hour clock (0..23)
603
- %I - Hour of the day, 12-hour clock (01..12)
604
- %-I/%l - Hour of the day, 12-hour clock (1..12)
605
- %m - Month of the year (01..12)
606
- %-m - Month of the year (1..12)
607
- %M - Minute of the hour (00..59)
608
- %-M - Minute of the hour (0..59)
609
- %p - Meridian indicator (AM or PM)
610
- %P - Meridian indicator (am or pm)
611
- %S - Second of the minute (00..60)
612
- %-S - Second of the minute (0..60)
613
- %w - Day of the week (Sunday is 0, 0..6)
614
- %y - Year without a century (00..99)
615
- %-y - Year without a century (0..99)
616
- %Y - Year with century
617
- %z/%Z - Timezone offset (+0545)
618
-
619
- Check out `spec/*.spec.js` files for more examples!
620
-
621
- #### Using pluralization and number formatting together
622
- Sometimes you might want to display translation with formatted number, like adding thousand delimiters to displayed number
623
- You can do this:
624
- ```json
625
- {
626
- "en": {
627
- "point": {
628
- "one": "1 Point",
629
- "other": "{{formatted_number}} Points",
630
- "zero": "0 Points"
631
- }
632
- }
633
- }
634
- ```
635
- ```js
636
- var point_in_number = 1000;
637
- I18n.t('point', { count: point_in_number, formatted_number: I18n.toNumber(point_in_number) });
462
+ ```ruby
463
+ guard(:"i18n-js",
464
+ run_on_start: true,
465
+ config_file: "./config/i18n.yml",
466
+ require_file: "./config/environment.rb") do
467
+ watch(%r{^(app|config)/locales/.+\.(yml|po)$})
468
+ watch(%r{^config/i18n.yml$})
469
+ watch("Gemfile")
470
+ end
638
471
  ```
639
- Output should be `1,000 points`
640
-
641
-
642
- ## Using multiple exported translation files on a page.
643
- This method is useful for very large apps where a single contained translations.js file is not desirable. Examples would be a global translations file and a more specific route translation file.
644
-
645
- ### Rails without asset pipeline
646
- 1. Setup your `config/i18n-js.yml` to have multiple files and try to minimize any overlap.
647
-
648
- ```yaml
649
- sort_translation_keys: true
650
- fallbacks: false
651
-
652
- translations:
653
- + file: "app/assets/javascript/nls/welcome.js"
654
- only:
655
- + '*.welcome.*'
656
-
657
- + file: "app/assets/javascript/nls/albums.js"
658
- only:
659
- + '*.albums.*'
660
-
661
- + file: "app/assets/javascript/nls/global.js"
662
- only:
663
- + '*'
664
- # Exempt any routes specific translations from being
665
- # included in the global translation file
666
- except:
667
- + '*.welcome.*'
668
- + '*.albums.*'
669
- ```
670
- When `rake i18n:js:export` is executed it will create 3 translations files that can be loaded via the `javascript_include_tag`
671
-
672
- 2. Add the `javascript_include_tag` to your layout and to any route specific files that will require it.
673
- ```ruby
674
- # views/layouts/application.html.erb
675
- <%= javascript_include_tag(
676
- "i18n"
677
- "nls/global"
678
- ) %>
679
- ```
680
- and in the route specific
681
-
682
- ```ruby
683
- # views/welcome/index.html.erb
684
- <%= javascript_include_tag(
685
- "nls/welcome"
686
- ) %>
687
- ```
688
-
689
- 3. Make sure that you add these files to your `config/application.rb`
690
-
691
- ```ruby
692
- config.assets.precompile += %w(
693
- i18n
694
- nls/*
695
- )
696
- ```
697
-
698
- ### Using require.js / r.js
699
-
700
- To use this with require.js we are only going to change a few things from above.
701
-
702
- 1. In your `config/i18n-js.yml` we need to add a better location for the i18n to be exported. You want to use this location so that it can be properly precompiled by r.js.
703
-
704
- ```yaml
705
- export_i18n_js: "app/assets/javascript/nls"
706
- ```
707
-
708
- 2. In your `config/require.yml` we need to add a map, shim all the translations, and include them into the appropriate modules
709
-
710
- ```yaml
711
- # In your maps add (if you do not have this you will need to add it)
712
- map:
713
- '*':
714
- i18n: 'nls/i18n'
715
-
716
- # In your shims
717
- shims:
718
- nls/welcome:
719
- deps:
720
- + i18n
721
-
722
- nls/global:
723
- deps:
724
- + i18n
725
-
726
- # Finally in your modules
727
- modules:
728
- + name: 'application'
729
- include:
730
- + i18n
731
- + 'nls/global'
732
-
733
- + name: 'welcome'
734
- exclude:
735
- + application
736
- include:
737
- + 'nls/welcome'
738
- ```
739
- 3. When `rake assets:precompile` is executed it will optimize the translations into the correct modules so they are loaded with their assigned module, and loading them with requirejs is as simple as requiring any other shim.
740
-
741
- ```javascript
742
- define(['welcome/other_asset','nls/welcome'], function (otherAsset){
743
- // ...
744
- });
745
- ```
746
- 4. (optional) As an additional configuration we can make a task to be run before the requirejs optimizer. This will allow any automated scripts that run the requirejs optimizer to export the strings before we run r.js
747
-
748
- ```rake
749
- # lib/tasks/i18n.rake
750
- Rake::Task[:'i18n:js:export'].prerequisites.clear
751
- task :'i18n:js:export' => :'i18n:js:before_export'
752
- task :'requirejs:precompile:external' => :'i18n:js:export'
753
-
754
- namespace :i18n do
755
- namespace :js do
756
- task :before_export => :'assets:environment' do
757
- I18n.load_path += Dir[Rails.root.join('config', 'locales', '*.{yml,rb}')]
758
- I18n.backend.load_translations
759
- end
760
- end
761
- end
762
- ```
763
472
 
764
- ## Using I18n.js with other languages (Python, PHP, ...)
473
+ If your files are located in a different path, remember to configure file paths
474
+ accordingly.
765
475
 
766
- The JavaScript library is language agnostic; so you can use it with PHP, Python, [your favorite language here].
767
- The only requirement is that you need to set the `translations` attribute like following:
476
+ Now you can run `guard start -i`.
768
477
 
769
- ```javascript
770
- I18n.translations = {};
478
+ ### Using [listen](https://rubygems.org/gems/listen)
771
479
 
772
- I18n.translations["en"] = {
773
- message: "Some special message for you"
774
- }
480
+ Create a file under `config/initializers/i18n.rb` with the following content:
775
481
 
776
- I18n.translations["pt-BR"] = {
777
- message: "Uma mensagem especial para você"
778
- }
482
+ ```ruby
483
+ Rails.application.config.after_initialize do
484
+ require "i18n-js/listen"
485
+ I18nJS.listen
486
+ end
779
487
  ```
780
488
 
489
+ The code above will watch for changes based on `config/i18n.yml` and
490
+ `config/locales`. You can customize these options:
781
491
 
782
- ## Known Issues
783
-
784
- ### Missing translations in precompiled file(s) after adding any new locale file
492
+ - `config_file` - i18n-js configuration file
493
+ - `locales_dir` - one or multiple directories to watch for locales changes
494
+ - `options` - passed directly to
495
+ [listen](https://github.com/guard/listen/#options)
496
+ - `run_on_start` - export files on start. Defaults to `true`. When disabled,
497
+ files will be exported only when there are file changes.
785
498
 
786
- Due to the design of `sprockets`:
499
+ Example:
787
500
 
788
- - `depend_on` only takes file paths, not directory paths
789
- - registered `preprocessors` are only run when the fingerprint of any asset file, including `.erb` files, is changed
790
-
791
- This means that new locale files will not be detected, and so they will not trigger a i18n-js refresh. There are a few approaches to work around this:
792
-
793
- 1. You can force i18n-js to update its translations by completely clearing the assets cache. Use one of the following:
794
-
795
- ```bash
796
- $ rake assets:clobber
797
- # Or, with older versions of Rails:
798
- $ rake tmp:cache:clear
799
- ```
800
-
801
- These commands will remove *all* fingerprinted assets, and you will have to recompile them with
802
-
803
- ```bash
804
- $ rake assets:precompile
501
+ ```ruby
502
+ I18nJS.listen(
503
+ config_file: "config/i18n.yml",
504
+ locales_dir: ["config/locales", "app/views"],
505
+ options: {only: %r{.yml$}},
506
+ run_on_start: false
507
+ )
805
508
  ```
806
509
 
807
- or similar commands. If you are precompiling assets on the target machine(s), cached pages may be broken by this, so they will need to be refreshed.
808
-
809
- 2. You can change something in a different locale file.
810
-
811
- 3. Finally, you can change `config.assets.version`.
812
-
813
- **Note:** See issue [#213](https://github.com/fnando/i18n-js/issues/213) for more details and discussion of this issue.
814
-
815
- ### Translations in JS are not updated when Sprockets not loaded before this gem
510
+ ### Integrating with your frontend
816
511
 
817
- The "rails engine" declaration will try to detect existence of "sprockets" before adding the initailizer
818
- If sprockets is loaded after this gem, the preprocessor for
819
- making JS translations file cache to depend on content of locale files will not be hooked.
820
- So ensure sprockets is loaded before this gem like moving entry of sprockets in Gemfile or adding "require" statements for sprockets somewhere.
512
+ You're done exporting files, now what? Well, go to
513
+ [i18n](https://github.com/fnando/i18n) to discover how to use the NPM package
514
+ that loads all the exported translation.
821
515
 
822
- **Note:** See issue [#404](https://github.com/fnando/i18n-js/issues/404) for more details and discussion of this issue.
516
+ ### FAQ
823
517
 
824
- ### JS `I18n.toCurrency` & `I18n.toNumber` cannot handle large integers
518
+ #### I'm running v3. Is there a migration plan?
825
519
 
826
- The above methods use `toFixed` and it only supports 53 bit integers.
827
- Ref: http://2ality.com/2012/07/large-integers.html
520
+ [There's a document](https://github.com/fnando/i18n-js/tree/main/MIGRATING_FROM_V3_TO_V4.md)
521
+ outlining some of the things you need to do to migrate from v3 to v4. It may not
522
+ be as complete as we'd like it to be, so let us know if you face any issues
523
+ during the migration is not outline is that document.
828
524
 
829
- Feel free to find & discuss possible solution(s) at issue [#511](https://github.com/fnando/i18n-js/issues/511)
525
+ #### How can I export translations without having a database around?
830
526
 
831
- ### Only works with `Simple` backend
832
-
833
- If you set `I18n.backend` to something other than the default `Simple` backend, you will likely get an exception like this:
834
-
835
- ```
836
- Undefined method 'initialized?' for <your backend class>
837
- ```
838
-
839
- For now, i18n-js is only compatible with the `Simple` backend.
840
- If you need a more sophisticated backend for your rails application, like `I18n::Backend::ActiveRecord`, you can setup i18n-js to get translations from a separate `Simple` backend, by adding the following in an initializer:
841
-
842
- ```ruby
843
- I18n::JS.backend = I18n.backend
844
- I18n.backend = I18n::Backend::Chain.new(<your other backend(s)>, I18n.backend)
845
- ```
846
-
847
- This will use your backend with the default `Simple` backend as fallback, while i18n-js only sees and uses the simple backend.
848
- This means however, that only translations from your static locale files will be present in JavaScript.
849
-
850
- If you do cannot use a `Chain`-Backend for some reason, you can also set
527
+ Some people may have a build process using something like Docker that don't
528
+ necessarily have a database available. In this case, you may define your own
529
+ loading file by using something like
530
+ `i18n export --require ./config/i18n_export.rb`, where `i18n_export.rb` may look
531
+ like this:
851
532
 
852
533
  ```ruby
853
- I18n::JS.backend = I18n::Backend::Simple.new
854
- I18n.backend = <something different>
855
- ```
534
+ # frozen_string_literal: true
856
535
 
857
- However, the automatic reloading of translations in developement will not work in this case.
858
- This is because Rails calls `I18n.reload!` for each request in development, but `reload!` will not be called on `I18n::JS.backend`, since it is a different object.
859
- One option would be to patch `I18n.reload!` in an initializer:
536
+ require "bundler/setup"
537
+ require "rails"
538
+ require "active_support/railtie"
539
+ require "action_view/railtie"
860
540
 
861
- ```ruby
862
- module I18n
863
- def self.reload!
864
- I18n::JS.backend.reload!
865
- super
866
- end
867
- end
541
+ I18n.load_path += Dir["./config/locales/**/*.yml"]
868
542
  ```
869
543
 
870
- See issue [#428](https://github.com/fnando/i18n-js/issues/428) for more details and discussion of this issue.
544
+ > **Note**:
545
+ >
546
+ > You may not need to load ActiveSupport and ActionView lines, or even may need
547
+ > to add additional requires for other libs. With this approach you have full
548
+ > control on what's going to be loaded.
871
549
 
872
550
  ## Maintainer
873
551
 
874
- - Nando Vieira - <http://nandovieira.com.br>
875
-
876
- ## Contributing
877
-
878
- Once you've made your great commits:
879
-
880
- 1. [Fork](http://help.github.com/forking/) I18n.js
881
- 2. Create a branch with a clear name
882
- 3. Make your changes (Please also add/change spec, README and CHANGELOG if applicable)
883
- 4. Push changes to the created branch
884
- 5. [Create an Pull Request](http://github.com/fnando/i18n-js/pulls)
885
- 6. That's it!
552
+ - [Nando Vieira](https://github.com/fnando)
886
553
 
887
- Please respect the indentation rules and code style.
888
- And use 2 spaces, not tabs. And don't touch the versioning thing.
554
+ ## Contributors
889
555
 
890
- ## Running tests
556
+ - https://github.com/fnando/i18n-js/contributors
891
557
 
892
- You can run I18n tests using Node.js or your browser.
893
-
894
- To use Node.js, install the `jasmine-node` library:
895
-
896
- $ npm install jasmine-node
897
-
898
- Then execute the following command from the lib's root directory:
558
+ ## Contributing
899
559
 
900
- $ npm test
560
+ For more details about how to contribute, please read
561
+ https://github.com/fnando/i18n-js/blob/main/CONTRIBUTING.md.
901
562
 
902
- To run using your browser, just open the `spec/js/specs.html` file.
563
+ ## License
903
564
 
904
- You can run both Ruby and JavaScript specs with `rake spec`.
565
+ The gem is available as open source under the terms of the
566
+ [MIT License](https://opensource.org/licenses/MIT). A copy of the license can be
567
+ found at https://github.com/fnando/i18n-js/blob/main/LICENSE.md.
905
568
 
906
- ## License
569
+ ## Code of Conduct
907
570
 
908
- (The MIT License)
909
-
910
- Permission is hereby granted, free of charge, to any person obtaining
911
- a copy of this software and associated documentation files (the
912
- 'Software'), to deal in the Software without restriction, including
913
- without limitation the rights to use, copy, modify, merge, publish,
914
- distribute, sublicense, and/or sell copies of the Software, and to
915
- permit persons to whom the Software is furnished to do so, subject to
916
- the following conditions:
917
-
918
- The above copyright notice and this permission notice shall be
919
- included in all copies or substantial portions of the Software.
920
-
921
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
922
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
923
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
924
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
925
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
926
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
927
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
571
+ Everyone interacting in the i18n-js project's codebases, issue trackers, chat
572
+ rooms and mailing lists is expected to follow the
573
+ [code of conduct](https://github.com/fnando/i18n-js/blob/main/CODE_OF_CONDUCT.md).