i18n-js 3.8.2 → 4.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +4 -0
- data/.github/FUNDING.yml +1 -1
- data/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- data/.github/ISSUE_TEMPLATE/config.yml +5 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +38 -0
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/ruby-tests.yml +73 -0
- data/.gitignore +13 -7
- data/.rubocop.yml +19 -0
- data/CHANGELOG.md +47 -512
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +3 -0
- data/LICENSE.md +20 -0
- data/MIGRATING_FROM_V3_TO_V4.md +192 -0
- data/README.md +425 -921
- data/Rakefile +10 -20
- data/bin/release +81 -0
- data/exe/i18n +5 -0
- data/i18n-js.gemspec +51 -29
- data/lib/guard/i18n-js/templates/Guardfile +10 -0
- data/lib/guard/i18n-js/version.rb +13 -0
- data/lib/guard/i18n-js.rb +95 -0
- data/lib/i18n-js/clean_hash.rb +13 -0
- data/lib/i18n-js/cli/check_command.rb +17 -0
- data/lib/i18n-js/cli/command.rb +79 -0
- data/lib/i18n-js/cli/export_command.rb +95 -0
- data/lib/i18n-js/cli/init_command.rb +52 -0
- data/lib/i18n-js/cli/lint_scripts_command.rb +159 -0
- data/lib/i18n-js/cli/lint_translations_command.rb +157 -0
- data/lib/i18n-js/cli/plugins_command.rb +67 -0
- data/lib/i18n-js/cli/ui.rb +64 -0
- data/lib/i18n-js/cli/version_command.rb +18 -0
- data/lib/i18n-js/cli.rb +66 -0
- data/lib/i18n-js/embed_fallback_translations_plugin.rb +70 -0
- data/lib/i18n-js/export_files_plugin.rb +103 -0
- data/lib/i18n-js/lint.js +150645 -0
- data/lib/i18n-js/lint.ts +196 -0
- data/lib/i18n-js/listen.rb +96 -0
- data/lib/i18n-js/plugin.rb +103 -0
- data/lib/i18n-js/schema.rb +216 -0
- data/lib/i18n-js/sort_hash.rb +12 -0
- data/lib/i18n-js/version.rb +5 -0
- data/lib/i18n-js.rb +107 -1
- data/package.json +5 -20
- metadata +149 -189
- data/.editorconfig +0 -24
- data/.github/workflows/tests.yaml +0 -100
- data/.npmignore +0 -27
- data/Appraisals +0 -44
- data/app/assets/javascripts/i18n/filtered.js.erb +0 -23
- data/app/assets/javascripts/i18n/shims.js +0 -240
- data/app/assets/javascripts/i18n/translations.js +0 -3
- data/app/assets/javascripts/i18n.js +0 -1095
- data/gemfiles/i18n_0_6.gemfile +0 -7
- data/gemfiles/i18n_0_7.gemfile +0 -7
- data/gemfiles/i18n_0_8.gemfile +0 -7
- data/gemfiles/i18n_0_9.gemfile +0 -7
- data/gemfiles/i18n_1_0.gemfile +0 -7
- data/gemfiles/i18n_1_1.gemfile +0 -7
- data/gemfiles/i18n_1_2.gemfile +0 -7
- data/gemfiles/i18n_1_3.gemfile +0 -7
- data/gemfiles/i18n_1_4.gemfile +0 -7
- data/gemfiles/i18n_1_5.gemfile +0 -7
- data/gemfiles/i18n_1_6.gemfile +0 -7
- data/gemfiles/i18n_1_7.gemfile +0 -7
- data/gemfiles/i18n_1_8.gemfile +0 -7
- data/i18njs.png +0 -0
- data/lib/i18n/js/dependencies.rb +0 -63
- data/lib/i18n/js/engine.rb +0 -87
- data/lib/i18n/js/fallback_locales.rb +0 -70
- data/lib/i18n/js/formatters/base.rb +0 -25
- data/lib/i18n/js/formatters/js.rb +0 -32
- data/lib/i18n/js/formatters/json.rb +0 -13
- data/lib/i18n/js/middleware.rb +0 -82
- data/lib/i18n/js/private/config_store.rb +0 -31
- data/lib/i18n/js/private/hash_with_symbol_keys.rb +0 -36
- data/lib/i18n/js/segment.rb +0 -80
- data/lib/i18n/js/utils.rb +0 -78
- data/lib/i18n/js/version.rb +0 -7
- data/lib/i18n/js.rb +0 -264
- data/lib/rails/generators/i18n/js/config/config_generator.rb +0 -19
- data/lib/rails/generators/i18n/js/config/templates/i18n-js.yml +0 -27
- data/lib/tasks/export.rake +0 -8
- data/spec/fixtures/custom_path.yml +0 -5
- data/spec/fixtures/default.yml +0 -5
- data/spec/fixtures/erb.yml +0 -5
- data/spec/fixtures/except_condition.yml +0 -7
- data/spec/fixtures/js_export_dir_custom.yml +0 -7
- data/spec/fixtures/js_export_dir_none.yml +0 -6
- data/spec/fixtures/js_extend_parent.yml +0 -6
- data/spec/fixtures/js_extend_segment.yml +0 -6
- data/spec/fixtures/js_file_per_locale.yml +0 -7
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml +0 -4
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_hash.yml +0 -6
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale.yml +0 -4
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml +0 -4
- data/spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml +0 -4
- data/spec/fixtures/js_file_per_locale_without_fallbacks.yml +0 -4
- data/spec/fixtures/js_file_with_namespace_prefix_and_pretty_print.yml +0 -9
- data/spec/fixtures/js_sort_translation_keys_false.yml +0 -6
- data/spec/fixtures/js_sort_translation_keys_true.yml +0 -6
- data/spec/fixtures/json_only.yml +0 -18
- data/spec/fixtures/locales.yml +0 -133
- data/spec/fixtures/merge_plurals.yml +0 -6
- data/spec/fixtures/merge_plurals_with_no_overrides.yml +0 -4
- data/spec/fixtures/merge_plurals_with_partial_overrides.yml +0 -4
- data/spec/fixtures/millions.yml +0 -4
- data/spec/fixtures/multiple_conditions.yml +0 -7
- data/spec/fixtures/multiple_conditions_per_locale.yml +0 -7
- data/spec/fixtures/multiple_files.yml +0 -7
- data/spec/fixtures/no_config.yml +0 -2
- data/spec/fixtures/no_scope.yml +0 -4
- data/spec/fixtures/simple_scope.yml +0 -5
- data/spec/js/currency.spec.js +0 -62
- data/spec/js/current_locale.spec.js +0 -19
- data/spec/js/dates.spec.js +0 -276
- data/spec/js/defaults.spec.js +0 -31
- data/spec/js/extend.spec.js +0 -110
- data/spec/js/interpolation.spec.js +0 -124
- data/spec/js/jasmine/MIT.LICENSE +0 -20
- data/spec/js/jasmine/jasmine-html.js +0 -190
- data/spec/js/jasmine/jasmine.css +0 -166
- data/spec/js/jasmine/jasmine.js +0 -2476
- data/spec/js/jasmine/jasmine_favicon.png +0 -0
- data/spec/js/locales.spec.js +0 -31
- data/spec/js/localization.spec.js +0 -78
- data/spec/js/numbers.spec.js +0 -174
- data/spec/js/placeholder.spec.js +0 -24
- data/spec/js/pluralization.spec.js +0 -219
- data/spec/js/prepare_options.spec.js +0 -41
- data/spec/js/require.js +0 -2083
- data/spec/js/specs.html +0 -49
- data/spec/js/specs_requirejs.html +0 -72
- data/spec/js/translate.spec.js +0 -304
- data/spec/js/translations.js +0 -188
- data/spec/js/utility_functions.spec.js +0 -20
- data/spec/ruby/i18n/js/fallback_locales_spec.rb +0 -84
- data/spec/ruby/i18n/js/segment_spec.rb +0 -219
- data/spec/ruby/i18n/js/utils_spec.rb +0 -106
- data/spec/ruby/i18n/js_spec.rb +0 -748
- data/spec/spec_helper.rb +0 -80
- data/yarn.lock +0 -131
data/README.md
CHANGED
@@ -1,1069 +1,573 @@
|
|
1
1
|
<p align="center">
|
2
|
-
<img width="250" height="58" src="https://github.com/fnando/i18n-js/raw/main/i18njs.png" alt="i18n.js">
|
2
|
+
<img width="250" height="58" src="https://github.com/fnando/i18n-js/raw/main/images/i18njs.png" alt="i18n.js">
|
3
3
|
</p>
|
4
4
|
|
5
5
|
<p align="center">
|
6
|
-
|
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.
|
7
9
|
</p>
|
8
10
|
|
9
11
|
<p align="center">
|
10
|
-
<
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
<a href="https://gitter.im/fnando/i18n-js"><img src="https://img.shields.io/badge/gitter-join%20chat-1dce73.svg" alt="Gitter"></a>
|
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>
|
17
18
|
</p>
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
-
|
24
|
-
|
25
|
-
- Number localization
|
26
|
-
- Locale fallback
|
27
|
-
- Asset pipeline support
|
28
|
-
- Lots more! :)
|
29
|
-
|
30
|
-
## Version Notice
|
31
|
-
|
32
|
-
The `main` branch (including this README) is for latest `3.0.0` instead of
|
33
|
-
`2.x`.
|
34
|
-
|
35
|
-
## Usage
|
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>
|
36
26
|
|
37
|
-
|
27
|
+
## Installation
|
38
28
|
|
39
|
-
|
29
|
+
```bash
|
30
|
+
gem install i18n-js
|
31
|
+
```
|
40
32
|
|
41
|
-
|
33
|
+
Or add the following line to your project's Gemfile:
|
42
34
|
|
43
35
|
```ruby
|
44
36
|
gem "i18n-js"
|
45
37
|
```
|
46
38
|
|
47
|
-
|
48
|
-
|
49
|
-
If you're using `webpacker`, you may need to add the dependencies to your client
|
50
|
-
with:
|
51
|
-
|
52
|
-
```
|
53
|
-
yarn add i18n-js
|
54
|
-
# or, if you're using npm,
|
55
|
-
npm install i18n-js
|
56
|
-
```
|
57
|
-
|
58
|
-
For more details, see:
|
59
|
-
- [this gist](https://gist.github.com/bazzel/ecdff4718962e57c2d5569cf01d332fe)
|
60
|
-
- https://github.com/fnando/i18n-js/issues/597
|
61
|
-
|
62
|
-
#### Rails app with [Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html)
|
63
|
-
|
64
|
-
If you're using the
|
65
|
-
[asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html), then you
|
66
|
-
must add the following line to your `app/assets/javascripts/application.js`.
|
67
|
-
|
68
|
-
```javascript
|
69
|
-
//
|
70
|
-
// This is optional (in case you have `I18n is not defined` error)
|
71
|
-
// If you want to put this line, you must put it BEFORE `i18n/translations`
|
72
|
-
//= require i18n
|
73
|
-
// Some people even need to add the extension to make it work, see https://github.com/fnando/i18n-js/issues/283
|
74
|
-
//= require i18n.js
|
75
|
-
//
|
76
|
-
// This is a must
|
77
|
-
//= require i18n/translations
|
78
|
-
```
|
79
|
-
|
80
|
-
#### Rails app without [Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html)
|
81
|
-
|
82
|
-
First, put this in your `application.html` (layout file). Then get the JS files
|
83
|
-
following the instructions below.
|
84
|
-
|
85
|
-
```erb
|
86
|
-
<%# This is just an example, you can put `i18n.js` and `translations.js` anywhere you like %>
|
87
|
-
<%# Unlike the Asset Pipeline example, you need to require both **in order** %>
|
88
|
-
<%= javascript_include_tag "i18n" %>
|
89
|
-
<%= javascript_include_tag "translations", skip_pipeline: true %>
|
90
|
-
```
|
91
|
-
|
92
|
-
**There are two ways to get `translations.js` (For Rails app without Asset
|
93
|
-
Pipeline).**
|
94
|
-
|
95
|
-
1. This `translations.js` file can be automatically generated by the
|
96
|
-
`I18n::JS::Middleware`. Just add `config.middleware.use I18n::JS::Middleware`
|
97
|
-
to your `config/application.rb` file.
|
98
|
-
2. If you can't or prefer not to generate this file, you can move the middleware
|
99
|
-
line to your `config/environments/development.rb` file and run
|
100
|
-
`rake i18n:js:export` before deploying. This will export all translation
|
101
|
-
files, including the custom scopes you may have defined on
|
102
|
-
`config/i18n-js.yml`. If `I18n.available_locales` is set (e.g. in your Rails
|
103
|
-
`config/application.rb` file) then only the specified locales will be
|
104
|
-
exported. Current version of `i18n.js` will also be exported to avoid version
|
105
|
-
mismatching by downloading.
|
106
|
-
|
107
|
-
#### Export Configuration (For translations)
|
108
|
-
|
109
|
-
Exported translation files generated by `I18n::JS::Middleware` or
|
110
|
-
`rake i18n:js:export` can be customized with config file `config/i18n-js.yml`
|
111
|
-
(use `rails generate i18n:js:config` to create it). You can even get more files
|
112
|
-
generated to different folders and with different translations to best suit your
|
113
|
-
needs. The config file also affects developers using Asset Pipeline to require
|
114
|
-
translations. Except the option `file`, since all translations are required by
|
115
|
-
adding `//= require i18n/translations`.
|
116
|
-
|
117
|
-
Examples:
|
118
|
-
|
119
|
-
```yaml
|
120
|
-
translations:
|
121
|
-
- file: "public/javascripts/path-to-your-messages-file.js"
|
122
|
-
only: "*.date.formats"
|
123
|
-
- file: "public/javascripts/path-to-your-second-file.js"
|
124
|
-
only: ["*.activerecord", "*.admin.*.title"]
|
125
|
-
```
|
126
|
-
|
127
|
-
If `only` is omitted all the translations will be saved. Also, make sure you add
|
128
|
-
that initial `*`; it specifies that all languages will be exported. If you want
|
129
|
-
to export only one language, you can do something like this:
|
130
|
-
|
131
|
-
```yaml
|
132
|
-
translations:
|
133
|
-
- file: "public/javascripts/en.js"
|
134
|
-
only: "en.*"
|
135
|
-
- file: "public/javascripts/pt-BR.js"
|
136
|
-
only: "pt-BR.*"
|
137
|
-
```
|
138
|
-
|
139
|
-
Optionally, you can auto generate a translation file per available locale if you
|
140
|
-
specify the `%{locale}` placeholder.
|
141
|
-
|
142
|
-
```yaml
|
143
|
-
translations:
|
144
|
-
- file: "public/javascripts/i18n/%{locale}.js"
|
145
|
-
only: "*"
|
146
|
-
- file: "public/javascripts/frontend/i18n/%{locale}.js"
|
147
|
-
only: ["*.frontend", "*.users.*"]
|
148
|
-
```
|
149
|
-
|
150
|
-
You can also include ERB in your config file.
|
151
|
-
|
152
|
-
```yaml
|
153
|
-
translations:
|
154
|
-
<% Widgets.each do |widget| %>
|
155
|
-
- file: <%= "'#{widget.file}'" %>
|
156
|
-
only: <%= "'#{widget.only}'" %>
|
157
|
-
<% end %>
|
158
|
-
```
|
159
|
-
|
160
|
-
You are able to exclude certain phrases or whole groups of phrases by specifying
|
161
|
-
the YAML key(s) in the `except` configuration option. The outputted JS
|
162
|
-
translations file (exported or generated by the middleware) will omit any keys
|
163
|
-
listed in `except` configuration param:
|
164
|
-
|
165
|
-
```yaml
|
166
|
-
translations:
|
167
|
-
- except: ["*.active_admin", "*.ransack", "*.activerecord.errors"]
|
168
|
-
```
|
169
|
-
|
170
|
-
#### Export Configuration (For other things)
|
171
|
-
|
172
|
-
- `I18n::JS.config_file_path` Expected Type: `String` Default:
|
173
|
-
`config/i18n-js.yml` Behaviour: Try to read the config file from that location
|
174
|
-
|
175
|
-
- `I18n::JS.export_i18n_js_dir_path` Expected Type: `String` Default:
|
176
|
-
`public/javascripts` Behaviour:
|
177
|
-
|
178
|
-
- Any `String`: considered as a relative path for a folder to `Rails.root` and
|
179
|
-
export `i18n.js` to that folder for `rake i18n:js:export`
|
180
|
-
- Any non-`String` (`nil`, `false`, `:none`, etc): Disable `i18n.js` exporting
|
181
|
-
|
182
|
-
- `I18n::JS.sort_translation_keys` Expected Type: `Boolean` Default: `true`
|
183
|
-
Behaviour:
|
184
|
-
|
185
|
-
- Sets whether or not to deep sort all translation keys in order to generate
|
186
|
-
identical output for the same translations
|
187
|
-
- Set to true to ensure identical asset fingerprints for the asset pipeline
|
188
|
-
|
189
|
-
- You may also set `export_i18n_js` and `sort_translation_keys` in your config
|
190
|
-
file, e.g.:
|
191
|
-
|
192
|
-
```yaml
|
193
|
-
export_i18n_js: false
|
194
|
-
# OR
|
195
|
-
export_i18n_js: "my/path"
|
196
|
-
|
197
|
-
sort_translation_keys: false
|
198
|
-
|
199
|
-
translations:
|
200
|
-
- ...
|
201
|
-
```
|
202
|
-
|
203
|
-
To find more examples on how to use the configuration file please refer to the
|
204
|
-
tests.
|
39
|
+
## Usage
|
205
40
|
|
206
|
-
|
41
|
+
About patterns:
|
207
42
|
|
208
|
-
|
209
|
-
|
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.
|
210
51
|
|
211
|
-
|
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.
|
212
56
|
|
213
|
-
|
214
|
-
fallbacks: true
|
57
|
+
The config file:
|
215
58
|
|
59
|
+
```yml
|
60
|
+
---
|
216
61
|
translations:
|
217
|
-
- file:
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
Otherwise `I18n.default_locale` will be used.
|
224
|
-
|
225
|
-
```yaml
|
226
|
-
fallbacks: :de
|
62
|
+
- file: app/frontend/locales/en.json
|
63
|
+
patterns:
|
64
|
+
- "*"
|
65
|
+
- "!*.activerecord"
|
66
|
+
- "!*.errors"
|
67
|
+
- "!*.number.nth"
|
227
68
|
|
228
|
-
|
229
|
-
|
230
|
-
|
69
|
+
- file: app/frontend/locales/:locale.:digest.json
|
70
|
+
patterns:
|
71
|
+
- "*"
|
231
72
|
```
|
232
73
|
|
233
|
-
|
74
|
+
The output path can use the following placeholders:
|
234
75
|
|
235
|
-
|
236
|
-
|
237
|
-
fr: ["de", "en"]
|
238
|
-
de: "en"
|
76
|
+
- `:locale`: the language that's being exported.
|
77
|
+
- `:digest`: the MD5 hex digest of the exported file.
|
239
78
|
|
240
|
-
|
241
|
-
|
242
|
-
only: "*"
|
243
|
-
```
|
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.
|
244
81
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
```yaml
|
249
|
-
fallbacks: :default_locale
|
82
|
+
```yml
|
83
|
+
---
|
84
|
+
<% group = "{en,pt}" %>
|
250
85
|
|
251
86
|
translations:
|
252
|
-
- file:
|
253
|
-
|
87
|
+
- file: app/frontend/translations.json
|
88
|
+
patterns:
|
89
|
+
- "<%= group %>.*"
|
90
|
+
- "!<%= group %>.activerecord"
|
91
|
+
- "!<%= group %>.errors"
|
92
|
+
- "!<%= group %>.number.nth"
|
254
93
|
```
|
255
94
|
|
256
|
-
|
257
|
-
`I18n.default_locale`, ignoring `I18n.fallbacks`.
|
258
|
-
|
259
|
-
Examples:
|
95
|
+
The Ruby API:
|
260
96
|
|
261
|
-
```
|
262
|
-
|
97
|
+
```ruby
|
98
|
+
require "i18n-js"
|
263
99
|
|
264
|
-
|
265
|
-
|
266
|
-
only: "*"
|
100
|
+
I18nJS.call(config_file: "config/i18n.yml")
|
101
|
+
I18nJS.call(config: config)
|
267
102
|
```
|
268
103
|
|
269
|
-
|
270
|
-
|
271
|
-
To find more examples on how to use the configuration file please refer to the
|
272
|
-
tests.
|
104
|
+
The CLI API:
|
273
105
|
|
274
|
-
|
106
|
+
```console
|
107
|
+
$ i18n --help
|
108
|
+
Usage: i18n COMMAND FLAGS
|
275
109
|
|
276
|
-
|
277
|
-
Javascript file to something other than `I18n`. This can be useful in
|
278
|
-
no-conflict scenarios. Example:
|
279
|
-
|
280
|
-
```yaml
|
281
|
-
translations:
|
282
|
-
- file: "public/javascripts/i18n/translations.js"
|
283
|
-
namespace: "MyNamespace"
|
284
|
-
```
|
110
|
+
Commands:
|
285
111
|
|
286
|
-
|
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
|
287
118
|
|
119
|
+
Run `i18n COMMAND --help` for more information on specific commands.
|
288
120
|
```
|
289
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
290
|
-
MyNamespace.translations["en"] = { ... }
|
291
|
-
```
|
292
|
-
|
293
|
-
### Adding prefix & suffix to the translations file(s)
|
294
121
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
which is quite useful to use it with webpack. The user should provide the
|
299
|
-
semi-colon and the newline character if needed.
|
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`.
|
300
125
|
|
301
|
-
|
126
|
+
### Plugins
|
302
127
|
|
303
|
-
|
304
|
-
translations:
|
305
|
-
- file: "public/javascripts/i18n/translations.js"
|
306
|
-
prefix: "import I18n from 'i18n-js';\n"
|
307
|
-
```
|
308
|
-
|
309
|
-
will create:
|
310
|
-
|
311
|
-
```
|
312
|
-
import I18n from 'i18n-js';
|
313
|
-
I18n.translations || (I18n.translations = {});
|
314
|
-
```
|
128
|
+
#### Built-in plugins:
|
315
129
|
|
316
|
-
`
|
317
|
-
It's similar to `prefix` so won't explain it in details.
|
130
|
+
##### `embed_fallback_translations`:
|
318
131
|
|
319
|
-
|
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.
|
320
135
|
|
321
|
-
|
322
|
-
your output file (default: false)
|
136
|
+
To use it, add the following to your configuration file:
|
323
137
|
|
324
138
|
```yaml
|
325
|
-
|
326
|
-
|
327
|
-
pretty_print: true
|
139
|
+
embed_fallback_translations:
|
140
|
+
enabled: true
|
328
141
|
```
|
329
142
|
|
330
|
-
|
143
|
+
##### `export_files`:
|
331
144
|
|
332
|
-
By default,
|
333
|
-
|
334
|
-
|
335
|
-
`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:
|
336
148
|
|
337
149
|
```yaml
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
150
|
+
export_files:
|
151
|
+
enabled: true
|
152
|
+
files:
|
153
|
+
- template: path/to/template.erb
|
154
|
+
output: "%{dir}/%{base_name}.ts"
|
342
155
|
```
|
343
156
|
|
344
|
-
|
345
|
-
|
346
|
-
Just add the `i18n.js` file to your page. You'll have to build the translations
|
347
|
-
object by hand or using your favorite programming language. More info below.
|
348
|
-
|
349
|
-
#### Via NPM with webpack and CommonJS
|
157
|
+
You can export multiple files by define more entries.
|
350
158
|
|
351
|
-
|
352
|
-
version you want:
|
159
|
+
The output name can use the following placeholders:
|
353
160
|
|
354
|
-
|
355
|
-
|
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.
|
356
165
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
```
|
361
|
-
|
362
|
-
Run npm install then use via
|
363
|
-
|
364
|
-
```javascript
|
365
|
-
var i18n = require("i18n-js");
|
366
|
-
```
|
367
|
-
|
368
|
-
### Setting up
|
369
|
-
|
370
|
-
You **don't** need to set up a thing. The default settings will work just okay.
|
371
|
-
But if you want to split translations into several files or specify contexts,
|
372
|
-
you can follow the rest of this setting up section.
|
373
|
-
|
374
|
-
Set your locale is easy as
|
375
|
-
|
376
|
-
```javascript
|
377
|
-
I18n.defaultLocale = "pt-BR";
|
378
|
-
I18n.locale = "pt-BR";
|
379
|
-
I18n.currentLocale();
|
380
|
-
// pt-BR
|
381
|
-
```
|
382
|
-
|
383
|
-
**NOTE:** You can now apply your configuration **before I18n** is loaded like
|
384
|
-
this:
|
385
|
-
|
386
|
-
```javascript
|
387
|
-
I18n = {}; // You must define this object in top namespace, which should be `window`
|
388
|
-
I18n.defaultLocale = "pt-BR";
|
389
|
-
I18n.locale = "pt-BR";
|
390
|
-
|
391
|
-
// Load I18n from `i18n.js`, `application.js` or whatever
|
392
|
-
|
393
|
-
I18n.currentLocale();
|
394
|
-
// pt-BR
|
395
|
-
```
|
396
|
-
|
397
|
-
In practice, you'll have something like the following in your
|
398
|
-
`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:
|
399
169
|
|
400
170
|
```erb
|
401
|
-
|
402
|
-
|
403
|
-
I18n.locale = "<%= I18n.locale %>";
|
404
|
-
</script>
|
405
|
-
```
|
406
|
-
|
407
|
-
You can use translate your messages:
|
171
|
+
/* eslint-disable */
|
172
|
+
<%= banner %>
|
408
173
|
|
409
|
-
|
410
|
-
I18n.t("some.scoped.translation");
|
411
|
-
// or translate with explicit setting of locale
|
412
|
-
I18n.t("some.scoped.translation", { locale: "fr" });
|
413
|
-
```
|
414
|
-
|
415
|
-
You can also interpolate values:
|
174
|
+
import { i18n } from "config/i18n";
|
416
175
|
|
417
|
-
|
418
|
-
// You need the `translations` object setup first
|
419
|
-
I18n.translations["en"] = {
|
420
|
-
greeting: "Hello %{name}",
|
421
|
-
};
|
422
|
-
|
423
|
-
I18n.t("greeting", { name: "John Doe" });
|
176
|
+
i18n.store(<%= JSON.pretty_generate(translations) %>);
|
424
177
|
```
|
425
178
|
|
426
|
-
|
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:
|
427
183
|
|
428
|
-
```
|
429
|
-
|
430
|
-
|
184
|
+
```typescript
|
185
|
+
/* eslint-disable */
|
186
|
+
// File generated by i18n-js on 2022-12-10 15:37:00 +0000
|
431
187
|
|
432
|
-
|
433
|
-
I18n.t("noun", { defaultValue: "I'm a {{noun}}", noun: "Mac" });
|
434
|
-
```
|
188
|
+
import { i18n } from "config/i18n";
|
435
189
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
Translation fallback can be enabled by enabling the `I18n.fallbacks` option:
|
454
|
-
|
455
|
-
```erb
|
456
|
-
<script type="text/javascript">
|
457
|
-
I18n.fallbacks = true;
|
458
|
-
</script>
|
459
|
-
```
|
460
|
-
|
461
|
-
By default missing translations will first be looked for in less specific
|
462
|
-
versions of the requested locale and if that fails by taking them from your
|
463
|
-
`I18n.defaultLocale`.
|
464
|
-
|
465
|
-
```javascript
|
466
|
-
// if I18n.defaultLocale = "en" and translation doesn't exist
|
467
|
-
// for I18n.locale = "de-DE" this key will be taken from "de" locale scope
|
468
|
-
// or, if that also doesn't exist, from "en" locale scope
|
469
|
-
I18n.t("some.missing.scope");
|
470
|
-
```
|
471
|
-
|
472
|
-
Custom fallback rules can also be specified for a particular language. There are
|
473
|
-
three different ways of doing it so:
|
474
|
-
|
475
|
-
```javascript
|
476
|
-
I18n.locales.no = ["nb", "en"];
|
477
|
-
I18n.locales.no = "nb";
|
478
|
-
I18n.locales.no = function (locale) {
|
479
|
-
return ["nb"];
|
480
|
-
};
|
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
|
+
});
|
481
207
|
```
|
482
208
|
|
483
|
-
|
484
|
-
|
485
|
-
[missing "name of scope" translation]
|
209
|
+
#### Plugin API
|
486
210
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
```javascript
|
491
|
-
I18n.missingBehaviour = "guess";
|
492
|
-
```
|
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)
|
493
214
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
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
|
498
229
|
|
499
|
-
|
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
|
500
241
|
|
501
|
-
|
502
|
-
|
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
|
503
252
|
|
504
|
-
|
505
|
-
|
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
|
506
263
|
```
|
507
264
|
|
508
|
-
|
265
|
+
The class `I18nJS::Plugin` implements some helper methods that you can use:
|
509
266
|
|
510
|
-
|
511
|
-
|
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.
|
512
272
|
|
513
|
-
|
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.
|
514
279
|
|
515
|
-
|
280
|
+
### Listing missing translations
|
516
281
|
|
517
|
-
|
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:
|
518
286
|
|
519
|
-
```
|
520
|
-
|
521
|
-
|
522
|
-
|
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)
|
523
300
|
```
|
524
301
|
|
525
|
-
|
302
|
+
This command will exist with status 1 whenever there are missing translations.
|
303
|
+
This way you can use it as a CI linting.
|
526
304
|
|
527
|
-
|
528
|
-
I18n.t("inbox.counting", { count: 10 }); // You have 10 messages
|
529
|
-
```
|
305
|
+
You can ignore keys by adding a list to the config file:
|
530
306
|
|
531
|
-
|
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.
|
532
380
|
|
533
381
|
```yaml
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
zero: You have no messages
|
540
|
-
```
|
541
|
-
|
542
|
-
**NOTE:** Rails I18n recognizes the `zero` option.
|
543
|
-
|
544
|
-
If you need special rules just define them for your language, for example
|
545
|
-
Russian, just add a new pluralizer:
|
546
|
-
|
547
|
-
```javascript
|
548
|
-
I18n.pluralization["ru"] = function (count) {
|
549
|
-
var key =
|
550
|
-
count % 10 == 1 && count % 100 != 11
|
551
|
-
? "one"
|
552
|
-
: [2, 3, 4].indexOf(count % 10) >= 0 &&
|
553
|
-
[12, 13, 14].indexOf(count % 100) < 0
|
554
|
-
? "few"
|
555
|
-
: count % 10 == 0 ||
|
556
|
-
[5, 6, 7, 8, 9].indexOf(count % 10) >= 0 ||
|
557
|
-
[11, 12, 13, 14].indexOf(count % 100) >= 0
|
558
|
-
? "many"
|
559
|
-
: "other";
|
560
|
-
return [key];
|
561
|
-
};
|
562
|
-
```
|
563
|
-
|
564
|
-
You can find all rules on
|
565
|
-
<https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html>.
|
566
|
-
|
567
|
-
If you're using the same scope over and over again, you may use the `scope`
|
568
|
-
option.
|
569
|
-
|
570
|
-
```javascript
|
571
|
-
var options = { scope: "activerecord.attributes.user" };
|
572
|
-
|
573
|
-
I18n.t("name", options);
|
574
|
-
I18n.t("email", options);
|
575
|
-
I18n.t("username", options);
|
576
|
-
```
|
577
|
-
|
578
|
-
You can also provide an array as scope.
|
579
|
-
|
580
|
-
```javascript
|
581
|
-
// use the greetings.hello scope
|
582
|
-
I18n.t(["greetings", "hello"]);
|
583
|
-
```
|
584
|
-
|
585
|
-
#### Number formatting
|
586
|
-
|
587
|
-
Similar to Rails helpers, you have localized number and currency formatting.
|
588
|
-
|
589
|
-
```javascript
|
590
|
-
I18n.l("currency", 1990.99);
|
591
|
-
// $1,990.99
|
592
|
-
|
593
|
-
I18n.l("number", 1990.99);
|
594
|
-
// 1,990.99
|
595
|
-
|
596
|
-
I18n.l("percentage", 123.45);
|
597
|
-
// 123.450%
|
598
|
-
```
|
599
|
-
|
600
|
-
To have more control over number formatting, you can use the `I18n.toNumber`,
|
601
|
-
`I18n.toPercentage`, `I18n.toCurrency` and `I18n.toHumanSize` functions.
|
602
|
-
|
603
|
-
```javascript
|
604
|
-
I18n.toNumber(1000); // 1,000.000
|
605
|
-
I18n.toCurrency(1000); // $1,000.00
|
606
|
-
I18n.toPercentage(100); // 100.000%
|
607
|
-
```
|
608
|
-
|
609
|
-
The `toNumber` and `toPercentage` functions accept the following options:
|
610
|
-
|
611
|
-
- `precision`: defaults to `3`
|
612
|
-
- `separator`: defaults to `.`
|
613
|
-
- `delimiter`: defaults to `,`
|
614
|
-
- `strip_insignificant_zeros`: defaults to `false`
|
615
|
-
|
616
|
-
See some number formatting examples:
|
617
|
-
|
618
|
-
```javascript
|
619
|
-
I18n.toNumber(1000, { precision: 0 }); // 1,000
|
620
|
-
I18n.toNumber(1000, { delimiter: ".", separator: "," }); // 1.000,000
|
621
|
-
I18n.toNumber(1000, { delimiter: ".", precision: 0 }); // 1.000
|
622
|
-
```
|
623
|
-
|
624
|
-
The `toCurrency` function accepts the following options:
|
625
|
-
|
626
|
-
- `precision`: sets the level of precision
|
627
|
-
- `separator`: sets the separator between the units
|
628
|
-
- `delimiter`: sets the thousands delimiter
|
629
|
-
- `format`: sets the format of the output string
|
630
|
-
- `unit`: sets the denomination of the currency
|
631
|
-
- `strip_insignificant_zeros`: defaults to `false`
|
632
|
-
- `sign_first`: defaults to `true`
|
633
|
-
|
634
|
-
You can provide only the options you want to override:
|
382
|
+
---
|
383
|
+
translations:
|
384
|
+
- file: app/frontend/translations.json
|
385
|
+
patterns:
|
386
|
+
- "*"
|
635
387
|
|
636
|
-
|
637
|
-
|
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`
|
638
392
|
```
|
639
393
|
|
640
|
-
|
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`.
|
641
396
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
-
|
646
|
-
|
647
|
-
-
|
648
|
-
|
649
|
-
<!---->
|
650
|
-
|
651
|
-
```javascript
|
652
|
-
I18n.toHumanSize(1234); // 1KB
|
653
|
-
I18n.toHumanSize(1234 * 1024); // 1MB
|
654
|
-
```
|
397
|
+
```yaml
|
398
|
+
---
|
399
|
+
translations:
|
400
|
+
- file: app/frontend/translations.json
|
401
|
+
patterns:
|
402
|
+
- "*"
|
655
403
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
// accepted formats
|
660
|
-
I18n.l("date.formats.short", "2009-09-18"); // yyyy-mm-dd
|
661
|
-
I18n.l("time.formats.short", "2009-09-18 23:12:43"); // yyyy-mm-dd hh:mm:ss
|
662
|
-
I18n.l("time.formats.short", "2009-11-09T18:10:34"); // JSON format with local Timezone (part of ISO-8601)
|
663
|
-
I18n.l("time.formats.short", "2009-11-09T18:10:34Z"); // JSON format in UTC (part of ISO-8601)
|
664
|
-
I18n.l("date.formats.short", 1251862029000); // Epoch time
|
665
|
-
I18n.l("date.formats.short", "09/18/2009"); // mm/dd/yyyy
|
666
|
-
I18n.l("date.formats.short", new Date()); // Date object
|
404
|
+
lint:
|
405
|
+
patterns:
|
406
|
+
- "app/assets/**/*.ts"
|
667
407
|
```
|
668
408
|
|
669
|
-
|
670
|
-
|
671
|
-
```javascript
|
672
|
-
I18n.translations["en"] = {
|
673
|
-
date: {
|
674
|
-
formats: {
|
675
|
-
ordinal_day: "%B %{day}",
|
676
|
-
},
|
677
|
-
},
|
678
|
-
};
|
679
|
-
|
680
|
-
I18n.l("date.formats.ordinal_day", "2009-09-18", { day: "18th" }); // Sep 18th
|
681
|
-
```
|
409
|
+
## Automatically export translations
|
682
410
|
|
683
|
-
|
684
|
-
format dates.
|
411
|
+
### Using [watchman](https://facebook.github.io/watchman/)
|
685
412
|
|
686
|
-
|
687
|
-
var date = new Date();
|
688
|
-
I18n.toTime("date.formats.short", "2009-09-18");
|
689
|
-
I18n.toTime("date.formats.short", date);
|
690
|
-
I18n.strftime(date, "%d/%m/%Y");
|
691
|
-
```
|
413
|
+
Create a script at `bin/i18n-watch`.
|
692
414
|
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
%w - Day of the week (Sunday is 0, 0..6)
|
715
|
-
%y - Year without a century (00..99)
|
716
|
-
%-y - Year without a century (0..99)
|
717
|
-
%Y - Year with century
|
718
|
-
%z/%Z - Timezone offset (+0545)
|
719
|
-
|
720
|
-
Check out `spec/*.spec.js` files for more examples!
|
721
|
-
|
722
|
-
#### Using pluralization and number formatting together
|
723
|
-
|
724
|
-
Sometimes you might want to display translation with formatted number, like
|
725
|
-
adding thousand delimiters to displayed number You can do this:
|
726
|
-
|
727
|
-
```json
|
728
|
-
{
|
729
|
-
"en": {
|
730
|
-
"point": {
|
731
|
-
"one": "1 Point",
|
732
|
-
"other": "{{formatted_number}} Points",
|
733
|
-
"zero": "0 Points"
|
734
|
-
}
|
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"]
|
735
436
|
}
|
736
|
-
|
737
|
-
|
437
|
+
]
|
438
|
+
JSON
|
738
439
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
});
|
440
|
+
# If you're running this through Foreman,
|
441
|
+
# the uncomment the following lines:
|
442
|
+
# while true; do
|
443
|
+
# sleep 1
|
444
|
+
# done
|
745
445
|
```
|
746
446
|
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
This method is useful for very large apps where a single contained
|
752
|
-
translations.js file is not desirable. Examples would be a global translations
|
753
|
-
file and a more specific route translation file.
|
754
|
-
|
755
|
-
### Rails without asset pipeline
|
756
|
-
|
757
|
-
1. Setup your `config/i18n-js.yml` to have multiple files and try to minimize
|
758
|
-
any overlap.
|
759
|
-
|
760
|
-
```yaml
|
761
|
-
sort_translation_keys: true
|
762
|
-
fallbacks: false
|
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:
|
763
451
|
|
764
|
-
translations:
|
765
|
-
+ file: "app/assets/javascript/nls/welcome.js"
|
766
|
-
only:
|
767
|
-
+ '*.welcome.*'
|
768
|
-
|
769
|
-
+ file: "app/assets/javascript/nls/albums.js"
|
770
|
-
only:
|
771
|
-
+ '*.albums.*'
|
772
|
-
|
773
|
-
+ file: "app/assets/javascript/nls/global.js"
|
774
|
-
only:
|
775
|
-
+ '*'
|
776
|
-
# Exempt any routes specific translations from being
|
777
|
-
# included in the global translation file
|
778
|
-
except:
|
779
|
-
+ '*.welcome.*'
|
780
|
-
+ '*.albums.*'
|
781
452
|
```
|
782
|
-
|
783
|
-
When `rake i18n:js:export` is executed it will create 3 translations files that
|
784
|
-
can be loaded via the `javascript_include_tag`
|
785
|
-
|
786
|
-
2. Add the `javascript_include_tag` to your layout and to any route specific
|
787
|
-
files that will require it.
|
788
|
-
|
789
|
-
```ruby
|
790
|
-
# views/layouts/application.html.erb
|
791
|
-
<%= javascript_include_tag(
|
792
|
-
"i18n"
|
793
|
-
"nls/global"
|
794
|
-
) %>
|
453
|
+
i18n: ./bin/i18n-watch
|
795
454
|
```
|
796
455
|
|
797
|
-
|
456
|
+
### Using [guard](https://rubygems.org/gems/guard)
|
798
457
|
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
"nls/welcome"
|
803
|
-
) %>
|
804
|
-
```
|
805
|
-
|
806
|
-
3. Make sure that you add these files to your `config/application.rb`
|
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:
|
807
461
|
|
808
462
|
```ruby
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
To use this with require.js we are only going to change a few things from above.
|
818
|
-
|
819
|
-
1. In your `config/i18n-js.yml` we need to add a better location for the i18n to
|
820
|
-
be exported to. You want to use this location so that it can be properly
|
821
|
-
precompiled by r.js.
|
822
|
-
|
823
|
-
```yaml
|
824
|
-
export_i18n_js: "app/assets/javascript/nls"
|
825
|
-
```
|
826
|
-
|
827
|
-
2. In your `config/require.yml` we need to add a map, shim all the translations,
|
828
|
-
and include them into the appropriate modules
|
829
|
-
|
830
|
-
```yaml
|
831
|
-
# In your maps add (if you do not have this you will need to add it)
|
832
|
-
map:
|
833
|
-
'*':
|
834
|
-
i18n: 'nls/i18n'
|
835
|
-
|
836
|
-
# In your shims
|
837
|
-
shims:
|
838
|
-
nls/welcome:
|
839
|
-
deps:
|
840
|
-
+ i18n
|
841
|
-
|
842
|
-
nls/global:
|
843
|
-
deps:
|
844
|
-
+ i18n
|
845
|
-
|
846
|
-
# Finally in your modules
|
847
|
-
modules:
|
848
|
-
+ name: 'application'
|
849
|
-
include:
|
850
|
-
+ i18n
|
851
|
-
+ 'nls/global'
|
852
|
-
|
853
|
-
+ name: 'welcome'
|
854
|
-
exclude:
|
855
|
-
+ application
|
856
|
-
include:
|
857
|
-
+ 'nls/welcome'
|
858
|
-
```
|
859
|
-
|
860
|
-
3. When `rake assets:precompile` is executed it will optimize the translations
|
861
|
-
into the correct modules so they are loaded with their assigned module, and
|
862
|
-
loading them with requirejs is as simple as requiring any other shim.
|
863
|
-
|
864
|
-
```javascript
|
865
|
-
define(["welcome/other_asset", "nls/welcome"], function (otherAsset) {
|
866
|
-
// ...
|
867
|
-
});
|
868
|
-
```
|
869
|
-
|
870
|
-
4. (optional) As an additional configuration we can make a task to be run before
|
871
|
-
the requirejs optimizer. This will allow any automated scripts that run the
|
872
|
-
requirejs optimizer to export the strings before we run r.js.
|
873
|
-
|
874
|
-
```rake
|
875
|
-
# lib/tasks/i18n.rake
|
876
|
-
Rake::Task[:'i18n:js:export'].prerequisites.clear
|
877
|
-
task :'i18n:js:export' => :'i18n:js:before_export'
|
878
|
-
task :'requirejs:precompile:external' => :'i18n:js:export'
|
879
|
-
|
880
|
-
namespace :i18n do
|
881
|
-
namespace :js do
|
882
|
-
task :before_export => :'assets:environment' do
|
883
|
-
I18n.load_path += Dir[Rails.root.join('config', 'locales', '*.{yml,rb}')]
|
884
|
-
I18n.backend.load_translations
|
885
|
-
end
|
886
|
-
end
|
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")
|
887
470
|
end
|
888
471
|
```
|
889
472
|
|
890
|
-
|
473
|
+
If your files are located in a different path, remember to configure file paths
|
474
|
+
accordingly.
|
891
475
|
|
892
|
-
|
893
|
-
[your favorite language here]. The only requirement is that you need to set the
|
894
|
-
`translations` attribute like following:
|
476
|
+
Now you can run `guard start -i`.
|
895
477
|
|
896
|
-
|
897
|
-
I18n.translations = {};
|
478
|
+
### Using [listen](https://rubygems.org/gems/listen)
|
898
479
|
|
899
|
-
|
900
|
-
message: "Some special message for you",
|
901
|
-
};
|
480
|
+
Create a file under `config/initializers/i18n.rb` with the following content:
|
902
481
|
|
903
|
-
|
904
|
-
|
905
|
-
|
482
|
+
```ruby
|
483
|
+
Rails.application.config.after_initialize do
|
484
|
+
require "i18n-js/listen"
|
485
|
+
I18nJS.listen
|
486
|
+
end
|
906
487
|
```
|
907
488
|
|
908
|
-
|
909
|
-
|
910
|
-
### Missing translations in precompiled file(s) after adding any new locale file
|
911
|
-
|
912
|
-
Due to the design of `sprockets`:
|
913
|
-
|
914
|
-
- `depend_on` only takes file paths, not directory paths
|
915
|
-
- registered `preprocessors` are only run when the fingerprint of any asset
|
916
|
-
file, including `.erb` files, is changed
|
917
|
-
|
918
|
-
This means that new locale files will not be detected, and so they will not
|
919
|
-
trigger a i18n-js refresh. There are a few approaches to work around this:
|
920
|
-
|
921
|
-
1. You can force i18n-js to update its translations by completely clearing the
|
922
|
-
assets cache. Use one of the following:
|
489
|
+
The code above will watch for changes based on `config/i18n.yml` and
|
490
|
+
`config/locales`. You can customize these options:
|
923
491
|
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
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.
|
929
498
|
|
930
|
-
|
931
|
-
recompile them with
|
499
|
+
Example:
|
932
500
|
|
933
|
-
```
|
934
|
-
|
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
|
+
)
|
935
508
|
```
|
936
509
|
|
937
|
-
|
938
|
-
cached pages may be broken by this, so they will need to be refreshed.
|
939
|
-
|
940
|
-
2. You can change something in a different locale file.
|
941
|
-
|
942
|
-
3. Finally, you can change `config.assets.version`.
|
943
|
-
|
944
|
-
**Note:** See issue [#213](https://github.com/fnando/i18n-js/issues/213) for
|
945
|
-
more details and discussion of this issue.
|
946
|
-
|
947
|
-
### Translations in JS are not updated when Sprockets not loaded before this gem
|
948
|
-
|
949
|
-
The "rails engine" declaration will try to detect existence of "sprockets"
|
950
|
-
before adding the initailizer If sprockets is loaded after this gem, the
|
951
|
-
preprocessor for making JS translations file cache to depend on content of
|
952
|
-
locale files will not be hooked. So ensure sprockets is loaded before this gem
|
953
|
-
by moving the entry of sprockets in the Gemfile or adding "require" statements
|
954
|
-
for sprockets somewhere.
|
510
|
+
### Integrating with your frontend
|
955
511
|
|
956
|
-
|
957
|
-
|
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.
|
958
515
|
|
959
|
-
###
|
516
|
+
### FAQ
|
960
517
|
|
961
|
-
|
962
|
-
http://2ality.com/2012/07/large-integers.html
|
518
|
+
#### I'm running v3. Is there a migration plan?
|
963
519
|
|
964
|
-
|
965
|
-
|
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.
|
966
524
|
|
967
|
-
|
525
|
+
#### How can I export translations without having a database around?
|
968
526
|
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
```
|
975
|
-
|
976
|
-
For now, i18n-js is only compatible with the `Simple` backend. If you need a
|
977
|
-
more sophisticated backend for your rails application, like
|
978
|
-
`I18n::Backend::ActiveRecord`, you can setup i18n-js to get translations from a
|
979
|
-
separate `Simple` backend, by adding the following in an initializer:
|
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:
|
980
532
|
|
981
533
|
```ruby
|
982
|
-
|
983
|
-
I18n.backend = I18n::Backend::Chain.new(<your other backend(s)>, I18n.backend)
|
984
|
-
```
|
985
|
-
|
986
|
-
This will use your backend with the default `Simple` backend as fallback, while
|
987
|
-
i18n-js only sees and uses the simple backend. This means however, that only
|
988
|
-
translations from your static locale files will be present in JavaScript.
|
534
|
+
# frozen_string_literal: true
|
989
535
|
|
990
|
-
|
536
|
+
require "bundler/setup"
|
537
|
+
require "rails"
|
538
|
+
require "active_support/railtie"
|
539
|
+
require "action_view/railtie"
|
991
540
|
|
992
|
-
|
993
|
-
I18n::JS.backend = I18n::Backend::Simple.new
|
994
|
-
I18n.backend = <something different>
|
541
|
+
I18n.load_path += Dir["./config/locales/**/*.yml"]
|
995
542
|
```
|
996
543
|
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
```ruby
|
1004
|
-
module I18n
|
1005
|
-
def self.reload!
|
1006
|
-
I18n::JS.backend.reload!
|
1007
|
-
super
|
1008
|
-
end
|
1009
|
-
end
|
1010
|
-
```
|
1011
|
-
|
1012
|
-
See issue [#428](https://github.com/fnando/i18n-js/issues/428) for more details
|
1013
|
-
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.
|
1014
549
|
|
1015
550
|
## Maintainer
|
1016
551
|
|
1017
|
-
- Nando Vieira
|
1018
|
-
|
1019
|
-
## Contributing
|
1020
|
-
|
1021
|
-
Once you've made your great commits:
|
552
|
+
- [Nando Vieira](https://github.com/fnando)
|
1022
553
|
|
1023
|
-
|
1024
|
-
2. Create a branch with a clear name
|
1025
|
-
3. Make your changes (Please also add/change spec, README and CHANGELOG if
|
1026
|
-
applicable)
|
1027
|
-
4. Push changes to the created branch
|
1028
|
-
5. [Create an Pull Request](http://github.com/fnando/i18n-js/pulls)
|
1029
|
-
6. That's it!
|
554
|
+
## Contributors
|
1030
555
|
|
1031
|
-
|
1032
|
-
And don't touch the versioning thing.
|
556
|
+
- https://github.com/fnando/i18n-js/contributors
|
1033
557
|
|
1034
|
-
##
|
1035
|
-
|
1036
|
-
You can run I18n tests using Node.js or your browser.
|
1037
|
-
|
1038
|
-
To use Node.js, install the `jasmine-node` library:
|
1039
|
-
|
1040
|
-
$ npm install jasmine-node
|
1041
|
-
|
1042
|
-
Then execute the following command from the lib's root directory:
|
1043
|
-
|
1044
|
-
$ npm test
|
1045
|
-
|
1046
|
-
To run using your browser, just open the `spec/js/specs.html` file.
|
558
|
+
## Contributing
|
1047
559
|
|
1048
|
-
|
560
|
+
For more details about how to contribute, please read
|
561
|
+
https://github.com/fnando/i18n-js/blob/main/CONTRIBUTING.md.
|
1049
562
|
|
1050
563
|
## License
|
1051
564
|
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
this software and associated documentation files (the 'Software'), to deal in
|
1056
|
-
the Software without restriction, including without limitation the rights to
|
1057
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
1058
|
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
1059
|
-
subject to the following conditions:
|
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.
|
1060
568
|
|
1061
|
-
|
1062
|
-
copies or substantial portions of the Software.
|
569
|
+
## Code of Conduct
|
1063
570
|
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
1068
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
1069
|
-
CONNECTION WITH THE 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).
|