i18n-js 4.0.0.alpha2 → 4.0.0.alpha5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45e8592f190777f676916988517d474dffb4f18f09120df26733c947d63ca140
4
- data.tar.gz: 04dfd46cfd05501a7cd1e97c5612a1ab5dbec52f98bad5f9119febcf28ce3ab8
3
+ metadata.gz: a5b5c71c903af1425bb6d7b12d0e74dc2da41894cf75a428da19bb11715dc371
4
+ data.tar.gz: 4914b448a0aca280c53412827d11b0148a7ec6393ff875fca78efea84bb68ce2
5
5
  SHA512:
6
- metadata.gz: 96caedcf824fb434a3b77dba666dd23cff72fbfd9f3d3175c928d768920d402f2d9250bb9ff0810e836ff5fbd90d3825b19621a4fd7c27ef10a7a40586990326
7
- data.tar.gz: bc68a8b9e54c991dc21e50ca52986b2f5c39418e6aed3b48664efbcb501e8f34d7a1333a608d6de4b6d5d8bcf7f2fc3c2de5584123b47cc6873e29047da2fdfb
6
+ metadata.gz: dd4f1880a36a9bee698766b843b73489517c1e09c86e143f1e5582811275423714c4c4a741cd01e86c7af558a33866738c9235399b309d09b97fd71375af77cb
7
+ data.tar.gz: 47e0d59fb0d12e2203508ff0c1bef534c9e42a77a895d78bea0d811f97ab6e2d2e0cc4461e46a36ba07016d0d77a4ac488b88fabdf71eb94325080b2fe2acd10
@@ -0,0 +1,5 @@
1
+ ---
2
+ contact_links:
3
+ - name: "🤨 Q&A"
4
+ url: https://github.com/fnando/i18n-js/discussions/new?category=q-a
5
+ about: Have a question? Ask it away here!
@@ -2,7 +2,7 @@
2
2
  name: ruby-tests
3
3
 
4
4
  on:
5
- pull_request:
5
+ pull_request_target:
6
6
  push:
7
7
  workflow_dispatch:
8
8
  inputs: {}
@@ -14,14 +14,17 @@ jobs:
14
14
  strategy:
15
15
  fail-fast: false
16
16
  matrix:
17
- ruby: ["2.7", "3.0"]
17
+ ruby: ["2.7", "3.0", "3.1"]
18
18
  gemfile:
19
19
  - Gemfile
20
+ if: |
21
+ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request_target' ||
22
+ github.actor != 'dependabot[bot]'
20
23
 
21
24
  steps:
22
- - uses: actions/checkout@v1
25
+ - uses: actions/checkout@v3
23
26
 
24
- - uses: actions/cache@v2
27
+ - uses: actions/cache@v3
25
28
  id: bundler-cache
26
29
  with:
27
30
  path: vendor/bundle
data/.rubocop.yml CHANGED
@@ -7,11 +7,10 @@ AllCops:
7
7
  NewCops: enable
8
8
  Exclude:
9
9
  - tmp/**/*
10
+ - vendor/**/*
11
+ - gemfiles/**/*
10
12
 
11
13
  Naming/FileName:
12
14
  Exclude:
13
15
  - lib/i18n-js.rb
14
16
  - lib/guard/i18n-js.rb
15
-
16
- Metrics:
17
- Enabled: false
data/README.md CHANGED
@@ -1,15 +1,30 @@
1
- # i18n-js
2
-
3
- [![Tests](https://github.com/fnando/i18n-js/workflows/ruby-tests/badge.svg)](https://github.com/fnando/i18n-js)
4
- [![Code Climate](https://codeclimate.com/github/fnando/i18n-js/badges/gpa.svg)](https://codeclimate.com/github/fnando/i18n-js)
5
- [![Gem](https://img.shields.io/gem/v/i18n-js.svg)](https://rubygems.org/gems/i18n-js)
6
- [![Gem](https://img.shields.io/gem/dt/i18n-js.svg)](https://rubygems.org/gems/i18n-js)
7
-
8
- Export [i18n](https://rubygems.org/gems/i18n) translations to JSON. A perfect
9
- fit if you want to export translations to JavaScript.
10
-
11
- Oh, you don't use Ruby? No problem! You can still use i18n-js and the
12
- [companion JavaScript package](https://npmjs.com/package/i18n-js).
1
+ <p align="center">
2
+ <img width="250" height="58" src="https://github.com/fnando/i18n-js/raw/main/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/next">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
+ > **Warning**: This branch contains the code for v4, our next major release.
13
28
 
14
29
  ## Installation
15
30
 
@@ -45,11 +60,16 @@ translations:
45
60
  - "!*.errors"
46
61
  - "!*.number.nth"
47
62
 
48
- - file: app/frontend/locales/:locale.json
63
+ - file: app/frontend/locales/:locale.:digest.json
49
64
  patterns:
50
65
  - "*"
51
66
  ```
52
67
 
68
+ The output path can use the following placeholders:
69
+
70
+ - `:locale`: the language that's being exported.
71
+ - `:digest`: the MD5 hex digest of the exported file.
72
+
53
73
  The Ruby API:
54
74
 
55
75
  ```ruby
@@ -62,16 +82,108 @@ I18nJS.call(config: config)
62
82
  The CLI API:
63
83
 
64
84
  ```console
65
- $ i18n init --config config/i18n.yml
66
- $ i18n export --config config/i18n.yml --require config/environment.rb
85
+ $ i18n --help
86
+ Usage: i18n COMMAND FLAGS
87
+
88
+ Commands:
89
+
90
+ - init: Initialize a project
91
+ - export: Export translations as JSON files
92
+ - version: Show package version
93
+ - check: Check for missing translations
94
+
95
+ Run `i18n COMMAND --help` for more information on specific commands.
67
96
  ```
68
97
 
69
98
  By default, `i18n` will use `config/i18n.yml` and `config/environment.rb` as the
70
99
  configuration files. If you don't have these files, then you'll need to specify
71
100
  both `--config` and `--require`.
72
101
 
102
+ ### Listing missing translations
103
+
104
+ To list missing and extraneous translations, you can use `i18n check`. This
105
+ command will load your translations similarly to how `i18n export` does, but
106
+ will output the list of keys that don't have a matching translation against the
107
+ default locale. Here's an example:
108
+
109
+ ![`i18n check` command in action](https://github.com/fnando/i18n-js/raw/main/images/i18njs-check.gif)
110
+
111
+ This command will exist with status 1 whenever there are missing translations.
112
+ This way you can use it as a CI linting.
113
+
114
+ You can ignore keys by adding a list to the config file:
115
+
116
+ ```yml
117
+ ---
118
+ translations:
119
+ - file: app/frontend/locales/en.json
120
+ patterns:
121
+ - "*"
122
+ - "!*.activerecord"
123
+ - "!*.errors"
124
+ - "!*.number.nth"
125
+
126
+ - file: app/frontend/locales/:locale.:digest.json
127
+ patterns:
128
+ - "*"
129
+
130
+ check:
131
+ ignore:
132
+ - en.mailer.login.subject
133
+ - en.mailer.login.body
134
+ ```
135
+
136
+ > **Note**: In order to avoid mistakenly ignoring keys, this configuration
137
+ > option only accepts the full translation scope, rather than accepting a
138
+ > pattern like `pt.ignored.scope.*`.
139
+
73
140
  ## Automatically export translations
74
141
 
142
+ ### Using watchman
143
+
144
+ Create a script at `bin/i18n-watch`.
145
+
146
+ ```bash
147
+ #!/usr/bin/env bash
148
+
149
+ root=`pwd`
150
+
151
+ watchman watch-del "$root"
152
+ watchman watch-project "$root"
153
+ watchman trigger-del "$root" i18n
154
+
155
+ watchman -j <<-JSON
156
+ [
157
+ "trigger",
158
+ "$root",
159
+ {
160
+ "name": "i18n",
161
+ "expression": [
162
+ "anyof",
163
+ ["match", "config/locales/**/*.yml", "wholename"],
164
+ ["match", "config/i18n.yml", "wholename"]
165
+ ],
166
+ "command": ["i18n", "export"]
167
+ }
168
+ ]
169
+ JSON
170
+
171
+ # If you're running this through Foreman,
172
+ # the uncomment the following lines:
173
+ # while true; do
174
+ # sleep 1
175
+ # done
176
+ ```
177
+
178
+ Make it executable with `chmod +x bin/i18n-watch`. To watch for changes, run
179
+ `./bin/i18n-watch`. If you're using Foreman, make sure you uncommented the lines
180
+ that keep the process running (`while..`), and add something like the following
181
+ line to your Procfile:
182
+
183
+ ```
184
+ i18n: ./bin/i18n-watch
185
+ ```
186
+
75
187
  ### Using guard
76
188
 
77
189
  Install [guard](https://rubygems.org/packages/guard) and
@@ -89,7 +201,7 @@ guard(:"i18n-js",
89
201
  end
90
202
  ```
91
203
 
92
- If you files in a different location, the remember to configure file paths
204
+ If your files are located in a different path, remember to configure file paths
93
205
  accordingly.
94
206
 
95
207
  Now you can run `guard start -i`.
@@ -115,6 +227,31 @@ You're done exporting files, now what? Well, go to
115
227
  [i18n](https://github.com/fnando/i18n) to discover how to use the NPM package
116
228
  that loads all the exported translation.
117
229
 
230
+ ### FAQ
231
+
232
+ #### How can I export translations without having a database around?
233
+
234
+ Some people may have a build process using something like Docker that don't
235
+ necessarily have a database available. In this case, you may define your own
236
+ loading file by using something like
237
+ `i18n export --require ./config/i18n_export.rb`, where `i18n_export.rb` may look
238
+ like this:
239
+
240
+ ```ruby
241
+ # frozen_string_literal: true
242
+
243
+ require "bundler/setup"
244
+ require "rails"
245
+ require "active_support/railtie"
246
+ require "action_view/railtie"
247
+
248
+ I18n.load_path += Dir["./config/locales/**/*.yml"]
249
+ ```
250
+
251
+ Notice that you may not need to load ActiveSupport and ActionView lines, or even
252
+ may need to add additional requires for other libs. With this approach you have
253
+ full control on what's going to be loaded.
254
+
118
255
  ## Maintainer
119
256
 
120
257
  - [Nando Vieira](https://github.com/fnando)
data/i18njs.png ADDED
Binary file
Binary file
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+
5
+ module I18nJS
6
+ class CLI
7
+ class CheckCommand < Command
8
+ command_name "check"
9
+ description "Check for missing translations"
10
+
11
+ parse do |opts|
12
+ opts.banner = "Usage: i18n #{name} [options]"
13
+
14
+ opts.on(
15
+ "-cCONFIG_FILE",
16
+ "--config=CONFIG_FILE",
17
+ "The configuration file that will be used"
18
+ ) do |config_file|
19
+ options[:config_file] = config_file
20
+ end
21
+
22
+ opts.on(
23
+ "-rREQUIRE_FILE",
24
+ "--require=REQUIRE_FILE",
25
+ "A Ruby file that must be loaded"
26
+ ) do |require_file|
27
+ options[:require_file] = require_file
28
+ end
29
+
30
+ opts.on(
31
+ "--[no-]color",
32
+ "Force colored output"
33
+ ) do |colored|
34
+ options[:colored] = colored
35
+ end
36
+
37
+ opts.on("-h", "--help", "Prints this help") do
38
+ ui.exit_with opts.to_s
39
+ end
40
+ end
41
+
42
+ command do
43
+ set_defaults!
44
+ ui.colored = options[:colored]
45
+
46
+ unless options[:config_file]
47
+ ui.fail_with("=> ERROR: you need to specify the config file")
48
+ end
49
+
50
+ ui.stdout_print("=> Config file:", options[:config_file].inspect)
51
+ config_file = File.expand_path(options[:config_file])
52
+
53
+ if options[:require_file]
54
+ ui.stdout_print("=> Require file:", options[:require_file].inspect)
55
+ require_file = File.expand_path(options[:require_file])
56
+ end
57
+
58
+ unless File.file?(config_file)
59
+ ui.fail_with(
60
+ "=> ERROR: config file doesn't exist at",
61
+ config_file.inspect
62
+ )
63
+ end
64
+
65
+ if require_file && !File.file?(require_file)
66
+ ui.fail_with(
67
+ "=> ERROR: require file doesn't exist at",
68
+ require_file.inspect
69
+ )
70
+ end
71
+
72
+ config = Glob::SymbolizeKeys.call(YAML.load_file(config_file))
73
+ Schema.validate!(config)
74
+
75
+ load_require_file!(require_file) if require_file
76
+
77
+ default_locale = I18n.default_locale
78
+ available_locales = I18n.available_locales
79
+ ignored_keys = config.dig(:check, :ignore) || []
80
+
81
+ mapping = available_locales.each_with_object({}) do |locale, buffer|
82
+ buffer[locale] =
83
+ Glob::Map.call(Glob.filter(I18nJS.translations, ["#{locale}.*"]))
84
+ .map {|key| key.gsub(/^.*?\./, "") }
85
+ end
86
+
87
+ default_locale_keys = mapping.delete(default_locale)
88
+
89
+ if ignored_keys.any?
90
+ ui.stdout_print "=> Check #{options[:config_file].inspect} for " \
91
+ "ignored keys."
92
+ end
93
+
94
+ ui.stdout_print "=> #{default_locale}: #{default_locale_keys.size} " \
95
+ "translations"
96
+
97
+ total_missing_count = 0
98
+
99
+ mapping.each do |locale, partial_keys|
100
+ ignored_count = 0
101
+
102
+ # Compute list of filtered keys (i.e. keys not ignored)
103
+ filtered_keys = partial_keys.reject do |key|
104
+ key = "#{locale}.#{key}"
105
+
106
+ ignored = ignored_keys.include?(key)
107
+ ignored_count += 1 if ignored
108
+ ignored
109
+ end
110
+
111
+ extraneous = (partial_keys - default_locale_keys).reject do |key|
112
+ key = "#{locale}.#{key}"
113
+ ignored = ignored_keys.include?(key)
114
+ ignored_count += 1 if ignored
115
+ ignored
116
+ end
117
+
118
+ missing = (default_locale_keys - (filtered_keys - extraneous))
119
+ .reject {|key| ignored_keys.include?("#{locale}.#{key}") }
120
+
121
+ ignored_count += extraneous.size
122
+ total_missing_count += missing.size
123
+
124
+ ui.stdout_print "=> #{locale}: #{missing.size} missing, " \
125
+ "#{extraneous.size} extraneous, " \
126
+ "#{ignored_count} ignored"
127
+
128
+ all_keys = (default_locale_keys + extraneous + missing).uniq.sort
129
+
130
+ all_keys.each do |key|
131
+ next if ignored_keys.include?("#{locale}.#{key}")
132
+
133
+ label = if extraneous.include?(key)
134
+ ui.yellow("extraneous")
135
+ elsif missing.include?(key)
136
+ ui.red("missing")
137
+ else
138
+ next
139
+ end
140
+
141
+ ui.stdout_print(" - #{locale}.#{key} (#{label})")
142
+ end
143
+ end
144
+
145
+ exit(1) if total_missing_count.nonzero?
146
+ end
147
+
148
+ private def set_defaults!
149
+ config_file = "./config/i18n.yml"
150
+ require_file = "./config/environment.rb"
151
+
152
+ options[:config_file] ||= config_file if File.file?(config_file)
153
+ options[:require_file] ||= require_file if File.file?(require_file)
154
+ end
155
+ end
156
+ end
157
+ end
@@ -38,6 +38,32 @@ module I18nJS
38
38
  def options
39
39
  @options ||= {}
40
40
  end
41
+
42
+ private def load_require_file!(require_file)
43
+ require_without_warnings(require_file)
44
+ rescue Exception => error # rubocop:disable Lint/RescueException
45
+ ui.stderr_print("=> ERROR: couldn't load",
46
+ options[:require_file].inspect)
47
+ ui.fail_with(
48
+ "\n#{error_description(error)}\n#{error.backtrace.join("\n")}"
49
+ )
50
+ end
51
+
52
+ private def error_description(error)
53
+ [
54
+ error.class.name,
55
+ error.message
56
+ ].reject(&:empty?).join(" => ")
57
+ end
58
+
59
+ private def require_without_warnings(path)
60
+ old_verbose = $VERBOSE
61
+ $VERBOSE = nil
62
+
63
+ load path
64
+ ensure
65
+ $VERBOSE = old_verbose
66
+ end
41
67
  end
42
68
  end
43
69
  end
@@ -14,7 +14,7 @@ module I18nJS
14
14
  opts.on(
15
15
  "-cCONFIG_FILE",
16
16
  "--config=CONFIG_FILE",
17
- "The configuration file that will be generated"
17
+ "The configuration file that will be used"
18
18
  ) do |config_file|
19
19
  options[:config_file] = config_file
20
20
  end
@@ -35,16 +35,15 @@ module I18nJS
35
35
  command do
36
36
  set_defaults!
37
37
 
38
- ui.stdout_print("=> config file:", options[:config_file].inspect)
39
- ui.stdout_print("=> require file:", options[:require_file].inspect)
40
-
41
38
  unless options[:config_file]
42
39
  ui.fail_with("=> ERROR: you need to specify the config file")
43
40
  end
44
41
 
42
+ ui.stdout_print("=> Config file:", options[:config_file].inspect)
45
43
  config_file = File.expand_path(options[:config_file])
46
44
 
47
45
  if options[:require_file]
46
+ ui.stdout_print("=> Require file:", options[:require_file].inspect)
48
47
  require_file = File.expand_path(options[:require_file])
49
48
  end
50
49
 
@@ -67,7 +66,7 @@ module I18nJS
67
66
  I18nJS.call(config_file: config_file)
68
67
  end
69
68
 
70
- ui.stdout_print("=> done in #{time.round(2)}s")
69
+ ui.stdout_print("=> Done in #{time.round(2)}s")
71
70
  end
72
71
 
73
72
  private def set_defaults!
@@ -77,32 +76,6 @@ module I18nJS
77
76
  options[:config_file] ||= config_file if File.file?(config_file)
78
77
  options[:require_file] ||= require_file if File.file?(require_file)
79
78
  end
80
-
81
- private def load_require_file!(require_file)
82
- require_without_warnings(require_file)
83
- rescue Exception => error # rubocop:disable Lint/RescueException
84
- ui.stderr_print("=> ERROR: couldn't load",
85
- options[:require_file].inspect)
86
- ui.fail_with(
87
- "\n#{error_description(error)}\n#{error.backtrace.join("\n")}"
88
- )
89
- end
90
-
91
- private def error_description(error)
92
- [
93
- error.class.name,
94
- error.message
95
- ].reject(&:empty?).join(" => ")
96
- end
97
-
98
- private def require_without_warnings(path)
99
- old_verbose = $VERBOSE
100
- $VERBOSE = nil
101
-
102
- require path
103
- ensure
104
- $VERBOSE = old_verbose
105
- end
106
79
  end
107
80
  end
108
81
  end
@@ -23,7 +23,9 @@ module I18nJS
23
23
  end
24
24
 
25
25
  command do
26
- file_path = File.expand_path(options[:config_file])
26
+ file_path = File.expand_path(
27
+ options.fetch(:config_file, "config/i18n.yml")
28
+ )
27
29
 
28
30
  if File.file?(file_path)
29
31
  ui.fail_with("ERROR: #{file_path} already exists!")
@@ -3,17 +3,21 @@
3
3
  module I18nJS
4
4
  class CLI
5
5
  class UI
6
- def initialize(stdout:, stderr:)
6
+ attr_reader :stdout, :stderr
7
+ attr_accessor :colored
8
+
9
+ def initialize(stdout:, stderr:, colored: nil)
7
10
  @stdout = stdout
8
11
  @stderr = stderr
12
+ @colored = colored
9
13
  end
10
14
 
11
15
  def stdout_print(*message)
12
- @stdout << "#{message.join(' ')}\n"
16
+ stdout << "#{message.join(' ')}\n"
13
17
  end
14
18
 
15
19
  def stderr_print(*message)
16
- @stderr << "#{message.join(' ')}\n"
20
+ stderr << "#{message.join(' ')}\n"
17
21
  end
18
22
 
19
23
  def fail_with(*message)
@@ -25,6 +29,36 @@ module I18nJS
25
29
  stdout_print(message)
26
30
  exit(0)
27
31
  end
32
+
33
+ def yellow(text)
34
+ ansi(text, 33)
35
+ end
36
+
37
+ def red(text)
38
+ ansi(text, 31)
39
+ end
40
+
41
+ def colored?
42
+ colored_output = if colored.nil?
43
+ stdout.tty?
44
+ else
45
+ colored
46
+ end
47
+
48
+ colored_output && !no_color?
49
+ end
50
+
51
+ def ansi(text, code)
52
+ if colored?
53
+ "\e[#{code}m#{text}\e[0m"
54
+ else
55
+ text
56
+ end
57
+ end
58
+
59
+ def no_color?
60
+ !ENV["NO_COLOR"].nil? && ENV["NO_COLOR"] == "1"
61
+ end
28
62
  end
29
63
  end
30
64
  end
data/lib/i18n-js/cli.rb CHANGED
@@ -6,14 +6,15 @@ require_relative "cli/ui"
6
6
  require_relative "cli/init_command"
7
7
  require_relative "cli/version_command"
8
8
  require_relative "cli/export_command"
9
+ require_relative "cli/check_command"
9
10
 
10
11
  module I18nJS
11
12
  class CLI
12
13
  attr_reader :ui
13
14
 
14
- def initialize(argv:, stdout:, stderr:)
15
+ def initialize(argv:, stdout:, stderr:, colored: stdout.tty?)
15
16
  @argv = argv.dup
16
- @ui = UI.new(stdout: stdout, stderr: stderr)
17
+ @ui = UI.new(stdout: stdout, stderr: stderr, colored: colored)
17
18
  end
18
19
 
19
20
  def call
@@ -26,7 +27,7 @@ module I18nJS
26
27
  end
27
28
 
28
29
  private def command_classes
29
- [InitCommand, ExportCommand, VersionCommand]
30
+ [InitCommand, ExportCommand, VersionCommand, CheckCommand]
30
31
  end
31
32
 
32
33
  private def commands
@@ -4,8 +4,9 @@ module I18nJS
4
4
  class Schema
5
5
  InvalidError = Class.new(StandardError)
6
6
 
7
- ROOT_KEYS = %i[translations].freeze
7
+ ROOT_KEYS = %i[translations check].freeze
8
8
  REQUIRED_ROOT_KEYS = %i[translations].freeze
9
+ REQUIRED_CHECK_KEYS = %i[ignore].freeze
9
10
  REQUIRED_TRANSLATION_KEYS = %i[file patterns].freeze
10
11
  TRANSLATION_KEYS = %i[file patterns].freeze
11
12
 
@@ -25,6 +26,17 @@ module I18nJS
25
26
  expect_required_keys(REQUIRED_ROOT_KEYS, target)
26
27
  reject_extraneous_keys(ROOT_KEYS, target)
27
28
  validate_translations
29
+ validate_check
30
+ end
31
+
32
+ def validate_check
33
+ return unless target.key?(:check)
34
+
35
+ check = target[:check]
36
+
37
+ expect_type(:check, check, Hash, target)
38
+ expect_required_keys(REQUIRED_CHECK_KEYS, check)
39
+ expect_type(:ignore, check[:ignore], Array, check)
28
40
  end
29
41
 
30
42
  def validate_translations
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nJS
4
- VERSION = "4.0.0.alpha2"
4
+ VERSION = "4.0.0.alpha5"
5
5
  end
data/lib/i18n-js.rb CHANGED
@@ -21,32 +21,45 @@ module I18nJS
21
21
 
22
22
  config = Glob::SymbolizeKeys.call(config || YAML.load_file(config_file))
23
23
  Schema.validate!(config)
24
+ exported_files = []
24
25
 
25
26
  config[:translations].each do |group|
26
- export_group(group)
27
+ exported_files += export_group(group)
27
28
  end
29
+
30
+ exported_files
28
31
  end
29
32
 
30
33
  def self.export_group(group)
31
34
  filtered_translations = Glob.filter(translations, group[:patterns])
32
35
  output_file_path = File.expand_path(group[:file])
36
+ exported_files = []
33
37
 
34
38
  if output_file_path.include?(":locale")
35
39
  filtered_translations.each_key do |locale|
36
40
  locale_file_path = output_file_path.gsub(/:locale/, locale.to_s)
37
- write_file(locale_file_path, locale => filtered_translations[locale])
41
+ exported_files << write_file(locale_file_path,
42
+ locale => filtered_translations[locale])
38
43
  end
39
44
  else
40
- write_file(output_file_path, filtered_translations)
45
+ exported_files << write_file(output_file_path, filtered_translations)
41
46
  end
47
+
48
+ exported_files
42
49
  end
43
50
 
44
51
  def self.write_file(file_path, translations)
45
52
  FileUtils.mkdir_p(File.dirname(file_path))
46
53
 
54
+ contents = ::JSON.pretty_generate(translations)
55
+ digest = Digest::MD5.hexdigest(contents)
56
+ file_path = file_path.gsub(/:digest/, digest)
57
+
47
58
  File.open(file_path, "w") do |file|
48
- file << ::JSON.pretty_generate(translations)
59
+ file << contents
49
60
  end
61
+
62
+ file_path
50
63
  end
51
64
 
52
65
  def self.translations
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n-js
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.alpha2
4
+ version: 4.0.0.alpha5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Vieira
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-28 00:00:00.000000000 Z
11
+ date: 2022-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glob
@@ -161,6 +161,7 @@ files:
161
161
  - ".github/CODEOWNERS"
162
162
  - ".github/FUNDING.yml"
163
163
  - ".github/ISSUE_TEMPLATE/bug_report.md"
164
+ - ".github/ISSUE_TEMPLATE/config.yml"
164
165
  - ".github/ISSUE_TEMPLATE/feature_request.md"
165
166
  - ".github/PULL_REQUEST_TEMPLATE.md"
166
167
  - ".github/dependabot.yml"
@@ -176,11 +177,14 @@ files:
176
177
  - Rakefile
177
178
  - exe/i18n
178
179
  - i18n-js.gemspec
180
+ - i18njs.png
181
+ - images/i18njs-check.gif
179
182
  - lib/guard/i18n-js.rb
180
183
  - lib/guard/i18n-js/templates/Guardfile
181
184
  - lib/guard/i18n-js/version.rb
182
185
  - lib/i18n-js.rb
183
186
  - lib/i18n-js/cli.rb
187
+ - lib/i18n-js/cli/check_command.rb
184
188
  - lib/i18n-js/cli/command.rb
185
189
  - lib/i18n-js/cli/export_command.rb
186
190
  - lib/i18n-js/cli/init_command.rb
@@ -196,10 +200,10 @@ metadata:
196
200
  rubygems_mfa_required: 'true'
197
201
  homepage_uri: https://github.com/fnando/i18n-js
198
202
  bug_tracker_uri: https://github.com/fnando/i18n-js/issues
199
- source_code_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha2
200
- changelog_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha2/CHANGELOG.md
201
- documentation_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha2/README.md
202
- license_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha2/LICENSE.md
203
+ source_code_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha5
204
+ changelog_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha5/CHANGELOG.md
205
+ documentation_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha5/README.md
206
+ license_uri: https://github.com/fnando/i18n-js/tree/v4.0.0.alpha5/LICENSE.md
203
207
  post_install_message:
204
208
  rdoc_options: []
205
209
  require_paths:
@@ -215,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
219
  - !ruby/object:Gem::Version
216
220
  version: 1.3.1
217
221
  requirements: []
218
- rubygems_version: 3.2.32
222
+ rubygems_version: 3.3.7
219
223
  signing_key:
220
224
  specification_version: 4
221
225
  summary: Export i18n translations and use them on JavaScript.