i18n-js 3.2.1 → 4.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) 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 +45 -386
  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 +191 -0
  18. data/README.md +439 -791
  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 +157 -0
  32. data/lib/i18n-js/cli/lint_translations_command.rb +155 -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 -181
  49. data/.editorconfig +0 -24
  50. data/.npmignore +0 -27
  51. data/.travis.yml +0 -36
  52. data/Appraisals +0 -32
  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 -1077
  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/lib/i18n/js/dependencies.rb +0 -59
  68. data/lib/i18n/js/engine.rb +0 -87
  69. data/lib/i18n/js/fallback_locales.rb +0 -70
  70. data/lib/i18n/js/formatters/base.rb +0 -23
  71. data/lib/i18n/js/formatters/js.rb +0 -31
  72. data/lib/i18n/js/formatters/json.rb +0 -13
  73. data/lib/i18n/js/middleware.rb +0 -82
  74. data/lib/i18n/js/private/hash_with_symbol_keys.rb +0 -36
  75. data/lib/i18n/js/segment.rb +0 -75
  76. data/lib/i18n/js/utils.rb +0 -65
  77. data/lib/i18n/js/version.rb +0 -7
  78. data/lib/i18n/js.rb +0 -259
  79. data/lib/rails/generators/i18n/js/config/config_generator.rb +0 -19
  80. data/lib/rails/generators/i18n/js/config/templates/i18n-js.yml +0 -27
  81. data/lib/tasks/export.rake +0 -8
  82. data/spec/fixtures/custom_path.yml +0 -5
  83. data/spec/fixtures/default.yml +0 -5
  84. data/spec/fixtures/erb.yml +0 -5
  85. data/spec/fixtures/except_condition.yml +0 -7
  86. data/spec/fixtures/js_export_dir_custom.yml +0 -7
  87. data/spec/fixtures/js_export_dir_none.yml +0 -6
  88. data/spec/fixtures/js_extend_parent.yml +0 -6
  89. data/spec/fixtures/js_extend_segment.yml +0 -6
  90. data/spec/fixtures/js_file_per_locale.yml +0 -7
  91. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml +0 -4
  92. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_hash.yml +0 -6
  93. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale.yml +0 -4
  94. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml +0 -4
  95. data/spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml +0 -4
  96. data/spec/fixtures/js_file_per_locale_without_fallbacks.yml +0 -4
  97. data/spec/fixtures/js_file_with_namespace_and_pretty_print.yml +0 -7
  98. data/spec/fixtures/js_sort_translation_keys_false.yml +0 -6
  99. data/spec/fixtures/js_sort_translation_keys_true.yml +0 -6
  100. data/spec/fixtures/json_only.yml +0 -18
  101. data/spec/fixtures/locales.yml +0 -95
  102. data/spec/fixtures/merge_plurals.yml +0 -6
  103. data/spec/fixtures/multiple_conditions.yml +0 -7
  104. data/spec/fixtures/multiple_conditions_per_locale.yml +0 -7
  105. data/spec/fixtures/multiple_files.yml +0 -7
  106. data/spec/fixtures/no_config.yml +0 -2
  107. data/spec/fixtures/no_scope.yml +0 -4
  108. data/spec/fixtures/simple_scope.yml +0 -5
  109. data/spec/js/currency.spec.js +0 -62
  110. data/spec/js/current_locale.spec.js +0 -19
  111. data/spec/js/dates.spec.js +0 -265
  112. data/spec/js/defaults.spec.js +0 -31
  113. data/spec/js/extend.spec.js +0 -110
  114. data/spec/js/interpolation.spec.js +0 -124
  115. data/spec/js/jasmine/MIT.LICENSE +0 -20
  116. data/spec/js/jasmine/jasmine-html.js +0 -190
  117. data/spec/js/jasmine/jasmine.css +0 -166
  118. data/spec/js/jasmine/jasmine.js +0 -2476
  119. data/spec/js/jasmine/jasmine_favicon.png +0 -0
  120. data/spec/js/locales.spec.js +0 -31
  121. data/spec/js/localization.spec.js +0 -48
  122. data/spec/js/numbers.spec.js +0 -170
  123. data/spec/js/placeholder.spec.js +0 -24
  124. data/spec/js/pluralization.spec.js +0 -211
  125. data/spec/js/prepare_options.spec.js +0 -41
  126. data/spec/js/require.js +0 -2083
  127. data/spec/js/specs.html +0 -49
  128. data/spec/js/specs_requirejs.html +0 -72
  129. data/spec/js/translate.spec.js +0 -284
  130. data/spec/js/translations.js +0 -163
  131. data/spec/js/utility_functions.spec.js +0 -20
  132. data/spec/ruby/i18n/js/fallback_locales_spec.rb +0 -84
  133. data/spec/ruby/i18n/js/segment_spec.rb +0 -219
  134. data/spec/ruby/i18n/js/utils_spec.rb +0 -106
  135. data/spec/ruby/i18n/js_spec.rb +0 -663
  136. data/spec/spec_helper.rb +0 -79
  137. data/yarn.lock +0 -131
data/README.md CHANGED
@@ -1,925 +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
- %d - Day of the month (01..31)
599
- %-d - Day of the month (1..31)
600
- %H - Hour of the day, 24-hour clock (00..23)
601
- %-H - Hour of the day, 24-hour clock (0..23)
602
- %I - Hour of the day, 12-hour clock (01..12)
603
- %-I - Hour of the day, 12-hour clock (1..12)
604
- %m - Month of the year (01..12)
605
- %-m - Month of the year (1..12)
606
- %M - Minute of the hour (00..59)
607
- %-M - Minute of the hour (0..59)
608
- %p - Meridian indicator (AM or PM)
609
- %S - Second of the minute (00..60)
610
- %-S - Second of the minute (0..60)
611
- %w - Day of the week (Sunday is 0, 0..6)
612
- %y - Year without a century (00..99)
613
- %-y - Year without a century (0..99)
614
- %Y - Year with century
615
- %z - Timezone offset (+0545)
616
-
617
- Check out `spec/*.spec.js` files for more examples!
618
-
619
- #### Using pluralization and number formatting together
620
- Sometimes you might want to display translation with formatted number, like adding thousand delimiters to displayed number
621
- You can do this:
622
- ```json
623
- {
624
- "en": {
625
- "point": {
626
- "one": "1 Point",
627
- "other": "{{formatted_number}} Points",
628
- "zero": "0 Points"
629
- }
630
- }
631
- }
632
- ```
633
- ```js
634
- var point_in_number = 1000;
635
- 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
636
471
  ```
637
- Output should be `1,000 points`
638
-
639
-
640
- ## Using multiple exported translation files on a page.
641
- 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.
642
-
643
- ### Rails without asset pipeline
644
- 1. Setup your `config/i18n-js.yml` to have multiple files and try to minimize any overlap.
645
-
646
- ```yaml
647
- sort_translation_keys: true
648
- fallbacks: false
649
-
650
- translations:
651
- + file: "app/assets/javascript/nls/welcome.js"
652
- only:
653
- + '*.welcome.*'
654
-
655
- + file: "app/assets/javascript/nls/albums.js"
656
- only:
657
- + '*.albums.*'
658
-
659
- + file: "app/assets/javascript/nls/global.js"
660
- only:
661
- + '*'
662
- # Exempt any routes specific translations from being
663
- # included in the global translation file
664
- except:
665
- + '*.welcome.*'
666
- + '*.albums.*'
667
- ```
668
- When `rake i18n:js:export` is executed it will create 3 translations files that can be loaded via the `javascript_include_tag`
669
-
670
- 2. Add the `javascript_include_tag` to your layout and to any route specific files that will require it.
671
- ```ruby
672
- # views/layouts/application.html.erb
673
- <%= javascript_include_tag(
674
- "i18n"
675
- "nls/global"
676
- ) %>
677
- ```
678
- and in the route specific
679
-
680
- ```ruby
681
- # views/welcome/index.html.erb
682
- <%= javascript_include_tag(
683
- "nls/welcome"
684
- ) %>
685
- ```
686
-
687
- 3. Make sure that you add these files to your `config/application.rb`
688
-
689
- ```ruby
690
- config.assets.precompile += %w(
691
- i18n
692
- nls/*
693
- )
694
- ```
695
-
696
- ### Using require.js / r.js
697
-
698
- To use this with require.js we are only going to change a few things from above.
699
-
700
- 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.
701
-
702
- ```yaml
703
- export_i18n_js: "app/assets/javascript/nls"
704
- ```
705
-
706
- 2. In your `config/require.yml` we need to add a map, shim all the translations, and include them into the appropriate modules
707
-
708
- ```yaml
709
- # In your maps add (if you do not have this you will need to add it)
710
- map:
711
- '*':
712
- i18n: 'nls/i18n'
713
-
714
- # In your shims
715
- shims:
716
- nls/welcome:
717
- deps:
718
- + i18n
719
-
720
- nls/global:
721
- deps:
722
- + i18n
723
-
724
- # Finally in your modules
725
- modules:
726
- + name: 'application'
727
- include:
728
- + i18n
729
- + 'nls/global'
730
-
731
- + name: 'welcome'
732
- exclude:
733
- + application
734
- include:
735
- + 'nls/welcome'
736
- ```
737
- 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.
738
-
739
- ```javascript
740
- define(['welcome/other_asset','nls/welcome'], function (otherAsset){
741
- // ...
742
- });
743
- ```
744
- 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
745
-
746
- ```rake
747
- # lib/tasks/i18n.rake
748
- Rake::Task[:'i18n:js:export'].prerequisites.clear
749
- task :'i18n:js:export' => :'i18n:js:before_export'
750
- task :'requirejs:precompile:external' => :'i18n:js:export'
751
-
752
- namespace :i18n do
753
- namespace :js do
754
- task :before_export => :'assets:environment' do
755
- I18n.load_path += Dir[Rails.root.join('config', 'locales', '*.{yml,rb}')]
756
- I18n.backend.load_translations
757
- end
758
- end
759
- end
760
- ```
761
472
 
762
- ## 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.
763
475
 
764
- The JavaScript library is language agnostic; so you can use it with PHP, Python, [your favorite language here].
765
- The only requirement is that you need to set the `translations` attribute like following:
476
+ Now you can run `guard start -i`.
766
477
 
767
- ```javascript
768
- I18n.translations = {};
478
+ ### Using [listen](https://rubygems.org/gems/listen)
769
479
 
770
- I18n.translations["en"] = {
771
- message: "Some special message for you"
772
- }
480
+ Create a file under `config/initializers/i18n.rb` with the following content:
773
481
 
774
- I18n.translations["pt-BR"] = {
775
- message: "Uma mensagem especial para você"
776
- }
482
+ ```ruby
483
+ Rails.application.config.after_initialize do
484
+ require "i18n-js/listen"
485
+ I18nJS.listen
486
+ end
777
487
  ```
778
488
 
489
+ The code above will watch for changes based on `config/i18n.yml` and
490
+ `config/locales`. You can customize these options:
779
491
 
780
- ## Known Issues
781
-
782
- ### 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.
783
498
 
784
- Due to the design of `sprockets`:
499
+ Example:
785
500
 
786
- - `depend_on` only takes file paths, not directory paths
787
- - registered `preprocessors` are only run when the fingerprint of any asset file, including `.erb` files, is changed
788
-
789
- 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:
790
-
791
- 1. You can force i18n-js to update its translations by completely clearing the assets cache. Use one of the following:
792
-
793
- ```bash
794
- $ rake assets:clobber
795
- # Or, with older versions of Rails:
796
- $ rake tmp:cache:clear
797
- ```
798
-
799
- These commands will remove *all* fingerprinted assets, and you will have to recompile them with
800
-
801
- ```bash
802
- $ 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
+ )
803
508
  ```
804
509
 
805
- 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.
806
-
807
- 2. You can change something in a different locale file.
808
-
809
- 3. Finally, you can change `config.assets.version`.
810
-
811
- **Note:** See issue [#213](https://github.com/fnando/i18n-js/issues/213) for more details and discussion of this issue.
812
-
813
- ### Translations in JS are not updated when Sprockets not loaded before this gem
510
+ ### Integrating with your frontend
814
511
 
815
- The "rails engine" declaration will try to detect existence of "sprockets" before adding the initailizer
816
- If sprockets is loaded after this gem, the preprocessor for
817
- making JS translations file cache to depend on content of locale files will not be hooked.
818
- 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.
819
515
 
820
- **Note:** See issue [#404](https://github.com/fnando/i18n-js/issues/404) for more details and discussion of this issue.
516
+ ### FAQ
821
517
 
822
- ### JS `I18n.toCurrency` & `I18n.toNumber` cannot handle large integers
518
+ #### I'm running v3. Is there a migration plan?
823
519
 
824
- The above methods use `toFixed` and it only supports 53 bit integers.
825
- 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.
826
524
 
827
- 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?
828
526
 
829
- ### Only works with `Simple` backend
830
-
831
- If you set `I18n.backend` to something other than the default `Simple` backend, you will likely get an exception like this:
832
-
833
- ```
834
- Undefined method 'initialized?' for <your backend class>
835
- ```
836
-
837
- For now, i18n-js is only compatible with the `Simple` backend.
838
- 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:
839
-
840
- ```ruby
841
- I18n::JS.backend = I18n.backend
842
- I18n.backend = I18n::Backend::Chain.new(<your other backend(s)>, I18n.backend)
843
- ```
844
-
845
- This will use your backend with the default `Simple` backend as fallback, while i18n-js only sees and uses the simple backend.
846
- This means however, that only translations from your static locale files will be present in JavaScript.
847
-
848
- 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:
849
532
 
850
533
  ```ruby
851
- I18n::JS.backend = I18n::Backend::Simple.new
852
- I18n.backend = <something different>
853
- ```
534
+ # frozen_string_literal: true
854
535
 
855
- However, the automatic reloading of translations in developement will not work in this case.
856
- 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.
857
- 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"
858
540
 
859
- ```ruby
860
- module I18n
861
- def self.reload!
862
- I18n::JS.backend.reload!
863
- super
864
- end
865
- end
541
+ I18n.load_path += Dir["./config/locales/**/*.yml"]
866
542
  ```
867
543
 
868
- 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.
869
549
 
870
550
  ## Maintainer
871
551
 
872
- - Nando Vieira - <http://nandovieira.com.br>
873
-
874
- ## Contributing
875
-
876
- Once you've made your great commits:
877
-
878
- 1. [Fork](http://help.github.com/forking/) I18n.js
879
- 2. Create a branch with a clear name
880
- 3. Make your changes (Please also add/change spec, README and CHANGELOG if applicable)
881
- 4. Push changes to the created branch
882
- 5. [Create an Pull Request](http://github.com/fnando/i18n-js/pulls)
883
- 6. That's it!
552
+ - [Nando Vieira](https://github.com/fnando)
884
553
 
885
- Please respect the indentation rules and code style.
886
- And use 2 spaces, not tabs. And don't touch the versioning thing.
554
+ ## Contributors
887
555
 
888
- ## Running tests
556
+ - https://github.com/fnando/i18n-js/contributors
889
557
 
890
- You can run I18n tests using Node.js or your browser.
891
-
892
- To use Node.js, install the `jasmine-node` library:
893
-
894
- $ npm install jasmine-node
895
-
896
- Then execute the following command from the lib's root directory:
558
+ ## Contributing
897
559
 
898
- $ npm test
560
+ For more details about how to contribute, please read
561
+ https://github.com/fnando/i18n-js/blob/main/CONTRIBUTING.md.
899
562
 
900
- To run using your browser, just open the `spec/js/specs.html` file.
563
+ ## License
901
564
 
902
- 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.
903
568
 
904
- ## License
569
+ ## Code of Conduct
905
570
 
906
- (The MIT License)
907
-
908
- Permission is hereby granted, free of charge, to any person obtaining
909
- a copy of this software and associated documentation files (the
910
- 'Software'), to deal in the Software without restriction, including
911
- without limitation the rights to use, copy, modify, merge, publish,
912
- distribute, sublicense, and/or sell copies of the Software, and to
913
- permit persons to whom the Software is furnished to do so, subject to
914
- the following conditions:
915
-
916
- The above copyright notice and this permission notice shall be
917
- included in all copies or substantial portions of the Software.
918
-
919
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
920
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
921
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
922
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
923
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
924
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
925
- 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).