i18n-js 4.0.0 → 4.1.0

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.
data/bin/pack ADDED
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+ require_relative "../lib/i18n-js/version"
6
+
7
+ def write_file(path, contents)
8
+ File.open(File.expand_path(path), "w") do |io|
9
+ io << contents
10
+ end
11
+ end
12
+
13
+ changelog_path = "./CHANGELOG.md"
14
+ version_path = "./lib/i18n-js/version.rb"
15
+
16
+ version = nil
17
+ segments = I18nJS::VERSION.split(".")
18
+ major, minor, patch = *segments.take(3).map(&:to_i)
19
+ pre = segments[4].to_s
20
+ pre_version = pre.gsub(/[^\d]/m, "").to_i
21
+ date = Time.now.strftime("%b %d, %Y")
22
+ release = false
23
+ alpha = false
24
+
25
+ OptionParser.new do |opts|
26
+ opts.on("--major") do
27
+ version = "#{major + 1}.0.0"
28
+ end
29
+
30
+ opts.on("--minor") do
31
+ version = "#{major}.#{minor + 1}.0"
32
+ end
33
+
34
+ opts.on("--patch") do
35
+ version = "#{major}.#{minor}.#{patch + 1}"
36
+ end
37
+
38
+ opts.on("--alpha") do
39
+ alpha = true
40
+ end
41
+
42
+ opts.on("--release") do
43
+ release = true
44
+ end
45
+ end.parse!
46
+
47
+ version = "#{version}.alpha#{pre_version + 1}" if alpha
48
+
49
+ unless version
50
+ puts "ERROR: You need to use either one of: --major, --minor, --patch"
51
+ exit 1
52
+ end
53
+
54
+ puts "=> Current version: #{I18nJS::VERSION}"
55
+ puts "=> Next version: #{version}"
56
+
57
+ system "yarn", "install"
58
+ system "yarn", "compile"
59
+
60
+ write_file changelog_path,
61
+ File.read(changelog_path)
62
+ .gsub("Unreleased", "v#{version} - #{date}")
63
+
64
+ puts "=> Updated #{changelog_path}"
65
+
66
+ write_file version_path,
67
+ File.read(version_path)
68
+ .gsub(/VERSION = ".*?"/, %[VERSION = "#{version}"])
69
+
70
+ puts "=> Updated #{version_path}"
71
+
72
+ if release
73
+ system "git", "add", changelog_path, version_path
74
+ system "git", "commit", "-m", "Bump up version (v#{version})"
75
+ system "git", "tag", "v#{version}"
76
+ end
77
+
78
+ system "rake", "build"
79
+ system "git", "checkout", changelog_path, version_path unless release
data/i18n-js.gemspec CHANGED
@@ -28,19 +28,22 @@ Gem::Specification.new do |spec|
28
28
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
29
29
  `git ls-files -z`
30
30
  .split("\x0")
31
- .reject {|f| f.match(%r{^(test|spec|features)/}) }
31
+ .reject {|f| f.match(%r{^(test|spec|features|images)/}) }
32
32
  end
33
33
 
34
+ spec.files << "lib/i18n-js/lint.js"
35
+
34
36
  spec.bindir = "exe"
35
37
  spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
36
38
  spec.require_paths = ["lib"]
37
39
 
38
- spec.add_dependency "glob"
40
+ spec.add_dependency "glob", ">= 0.4.0"
39
41
  spec.add_dependency "i18n"
40
42
 
41
43
  spec.add_development_dependency "activesupport"
42
44
  spec.add_development_dependency "minitest"
43
45
  spec.add_development_dependency "minitest-utils"
46
+ spec.add_development_dependency "mocha"
44
47
  spec.add_development_dependency "pry-meta"
45
48
  spec.add_development_dependency "rake"
46
49
  spec.add_development_dependency "rubocop"
data/lib/guard/i18n-js.rb CHANGED
@@ -53,13 +53,30 @@ module Guard
53
53
  info("Changes detected: #{changes.join(', ')}") if changes
54
54
 
55
55
  @current_thread = Thread.new do
56
- require @require_file
57
- ::I18nJS.call(config_file: @config_file)
56
+ capture do
57
+ system "i18n",
58
+ "export",
59
+ "--config",
60
+ config_file.to_s,
61
+ "--require",
62
+ require_file.to_s,
63
+ "--quiet"
64
+ end
58
65
  end
59
66
 
60
67
  current_thread.join
61
68
  end
62
69
 
70
+ def capture
71
+ original = $stdout
72
+ $stdout = StringIO.new
73
+ yield
74
+ rescue StandardError
75
+ # noop
76
+ ensure
77
+ $stdout = original
78
+ end
79
+
63
80
  def validate_file(key, file)
64
81
  return true if file && File.file?(file)
65
82
 
@@ -68,11 +85,11 @@ module Guard
68
85
  end
69
86
 
70
87
  def error(message)
71
- ::Guard::UI.error "[guard-i18n-js] #{message}"
88
+ ::Guard::UI.error "[i18n-js] #{message}"
72
89
  end
73
90
 
74
91
  def info(message)
75
- ::Guard::UI.info "[guard-i18n-js] #{message}"
92
+ ::Guard::UI.info "[i18n-js] #{message}"
76
93
  end
77
94
  end
78
95
  end
@@ -1,156 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "benchmark"
4
-
5
3
  module I18nJS
6
4
  class CLI
7
- class CheckCommand < Command
5
+ class CheckCommand < LintTranslationsCommand
8
6
  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"
7
+ description "Check for missing translations based on the default " \
8
+ "locale (DEPRECATED: Use `i18n lint:translations` instead)"
151
9
 
152
- options[:config_file] ||= config_file if File.file?(config_file)
153
- options[:require_file] ||= require_file if File.file?(require_file)
10
+ def command
11
+ ui.stderr_print "=> WARNING: `i18n check` has been deprecated in " \
12
+ "favor of `i18n lint:translations`"
13
+ super
154
14
  end
155
15
  end
156
16
  end
@@ -39,6 +39,16 @@ module I18nJS
39
39
  @options ||= {}
40
40
  end
41
41
 
42
+ private def load_config_file(config_file)
43
+ config = Glob::SymbolizeKeys.call(YAML.load_file(config_file))
44
+
45
+ if config.key?(:check)
46
+ config[:lint_translations] ||= config.delete(:check)
47
+ end
48
+
49
+ config
50
+ end
51
+
42
52
  private def load_require_file!(require_file)
43
53
  require_without_warnings(require_file)
44
54
  rescue Exception => error # rubocop:disable Lint/RescueException
@@ -27,6 +27,14 @@ module I18nJS
27
27
  options[:require_file] = require_file
28
28
  end
29
29
 
30
+ opts.on(
31
+ "-q",
32
+ "--quiet",
33
+ "A Ruby file that must be loaded"
34
+ ) do |quiet|
35
+ options[:quiet] = quiet
36
+ end
37
+
30
38
  opts.on("-h", "--help", "Prints this help") do
31
39
  ui.exit_with opts.to_s
32
40
  end
@@ -39,11 +47,11 @@ module I18nJS
39
47
  ui.fail_with("=> ERROR: you need to specify the config file")
40
48
  end
41
49
 
42
- ui.stdout_print("=> Config file:", options[:config_file].inspect)
50
+ log("=> Config file:", options[:config_file].inspect)
43
51
  config_file = File.expand_path(options[:config_file])
44
52
 
45
53
  if options[:require_file]
46
- ui.stdout_print("=> Require file:", options[:require_file].inspect)
54
+ log("=> Require file:", options[:require_file].inspect)
47
55
  require_file = File.expand_path(options[:require_file])
48
56
  end
49
57
 
@@ -66,7 +74,13 @@ module I18nJS
66
74
  I18nJS.call(config_file: config_file)
67
75
  end
68
76
 
69
- ui.stdout_print("=> Done in #{time.round(2)}s")
77
+ log("=> Done in #{time.round(2)}s")
78
+ end
79
+
80
+ private def log(*args)
81
+ return if options[:quiet]
82
+
83
+ ui.stdout_print(*args)
70
84
  end
71
85
 
72
86
  private def set_defaults!
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18nJS
4
+ class CLI
5
+ class LintScriptsCommand < Command
6
+ command_name "lint:scripts"
7
+ description "Lint files using TypeScript"
8
+
9
+ parse do |opts|
10
+ opts.banner = "Usage: i18n #{name} [options]"
11
+
12
+ opts.on(
13
+ "-cCONFIG_FILE",
14
+ "--config=CONFIG_FILE",
15
+ "The configuration file that will be used"
16
+ ) do |config_file|
17
+ options[:config_file] = config_file
18
+ end
19
+
20
+ opts.on(
21
+ "-rREQUIRE_FILE",
22
+ "--require=REQUIRE_FILE",
23
+ "A Ruby file that must be loaded"
24
+ ) do |require_file|
25
+ options[:require_file] = require_file
26
+ end
27
+
28
+ opts.on(
29
+ "-nNODE_PATH",
30
+ "--node-path=NODE_PATH",
31
+ "Set node.js path"
32
+ ) do |node_path|
33
+ options[:node_path] = node_path
34
+ end
35
+
36
+ opts.on("-h", "--help", "Prints this help") do
37
+ ui.exit_with opts.to_s
38
+ end
39
+ end
40
+
41
+ command do
42
+ set_defaults!
43
+ ui.colored = options[:colored]
44
+
45
+ unless options[:config_file]
46
+ ui.fail_with("=> ERROR: you need to specify the config file")
47
+ end
48
+
49
+ ui.stdout_print("=> Config file:", options[:config_file].inspect)
50
+ config_file = File.expand_path(options[:config_file])
51
+
52
+ if options[:require_file]
53
+ ui.stdout_print("=> Require file:", options[:require_file].inspect)
54
+ require_file = File.expand_path(options[:require_file])
55
+ end
56
+
57
+ node_path = options[:node_path] || find_node
58
+ ui.stdout_print("=> Node:", node_path.inspect)
59
+
60
+ unless File.file?(config_file)
61
+ ui.fail_with(
62
+ "=> ERROR: config file doesn't exist at",
63
+ config_file.inspect
64
+ )
65
+ end
66
+
67
+ if require_file && !File.file?(require_file)
68
+ ui.fail_with(
69
+ "=> ERROR: require file doesn't exist at",
70
+ require_file.inspect
71
+ )
72
+ end
73
+
74
+ found_node = node_path && File.executable?(File.expand_path(node_path))
75
+
76
+ unless found_node
77
+ ui.fail_with(
78
+ "=> ERROR: node.js couldn't be found (path: #{node_path})"
79
+ )
80
+ end
81
+
82
+ config = load_config_file(config_file)
83
+ Schema.validate!(config)
84
+
85
+ load_require_file!(require_file) if require_file
86
+
87
+ available_locales = I18n.available_locales
88
+ ignored_keys = config.dig(:lint_scripts, :ignore) || []
89
+
90
+ ui.stdout_print "=> Available locales: #{available_locales.inspect}"
91
+
92
+ exported_files = I18nJS.call(config_file: config_file)
93
+ data = exported_files.each_with_object({}) do |file, buffer|
94
+ buffer.merge!(JSON.load_file(file, symbolize_names: true))
95
+ end
96
+
97
+ lint_file = File.expand_path(File.join(__dir__, "../lint.js"))
98
+ patterns = config.dig(:lint_scripts, :patterns) || %w[
99
+ !(node_modules)/**/*.js
100
+ !(node_modules)/**/*.ts
101
+ !(node_modules)/**/*.jsx
102
+ !(node_modules)/**/*.tsx
103
+ ]
104
+
105
+ ui.stdout_print "=> Patterns: #{patterns.inspect}"
106
+
107
+ out = IO.popen([node_path, lint_file, patterns.join(":")]).read
108
+ scopes = JSON.parse(out, symbolize_names: true)
109
+ map = Glob::Map.call(data)
110
+ missing_count = 0
111
+ ignored_count = 0
112
+
113
+ messages = []
114
+
115
+ available_locales.each do |locale|
116
+ scopes.each do |scope|
117
+ scope_with_locale = "#{locale}.#{scope[:full]}"
118
+
119
+ ignored = ignored_keys.include?(scope[:full]) ||
120
+ ignored_keys.include?(scope_with_locale)
121
+
122
+ if ignored
123
+ ignored_count += 1
124
+ next
125
+ end
126
+
127
+ next if map.include?(scope_with_locale)
128
+
129
+ missing_count += 1
130
+ messages << " - #{scope[:location]}: #{scope_with_locale}"
131
+ end
132
+ end
133
+
134
+ ui.stdout_print "=> #{map.size} translations, #{missing_count} " \
135
+ "missing, #{ignored_count} ignored"
136
+ ui.stdout_print messages.sort.join("\n")
137
+
138
+ exit(missing_count.size)
139
+ end
140
+
141
+ private def set_defaults!
142
+ config_file = "./config/i18n.yml"
143
+ require_file = "./config/environment.rb"
144
+
145
+ options[:config_file] ||= config_file if File.file?(config_file)
146
+ options[:require_file] ||= require_file if File.file?(require_file)
147
+ end
148
+
149
+ private def find_node
150
+ ENV["PATH"]
151
+ .split(File::PATH_SEPARATOR)
152
+ .map {|dir| File.join(dir, "node") }
153
+ .find {|bin| File.executable?(bin) }
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18nJS
4
+ class CLI
5
+ class LintTranslationsCommand < Command
6
+ command_name "lint:translations"
7
+ description "Check for missing translations based on the default locale"
8
+
9
+ parse do |opts|
10
+ opts.banner = "Usage: i18n #{name} [options]"
11
+
12
+ opts.on(
13
+ "-cCONFIG_FILE",
14
+ "--config=CONFIG_FILE",
15
+ "The configuration file that will be used"
16
+ ) do |config_file|
17
+ options[:config_file] = config_file
18
+ end
19
+
20
+ opts.on(
21
+ "-rREQUIRE_FILE",
22
+ "--require=REQUIRE_FILE",
23
+ "A Ruby file that must be loaded"
24
+ ) do |require_file|
25
+ options[:require_file] = require_file
26
+ end
27
+
28
+ opts.on(
29
+ "--[no-]color",
30
+ "Force colored output"
31
+ ) do |colored|
32
+ options[:colored] = colored
33
+ end
34
+
35
+ opts.on("-h", "--help", "Prints this help") do
36
+ ui.exit_with opts.to_s
37
+ end
38
+ end
39
+
40
+ command do
41
+ set_defaults!
42
+ ui.colored = options[:colored]
43
+
44
+ unless options[:config_file]
45
+ ui.fail_with("=> ERROR: you need to specify the config file")
46
+ end
47
+
48
+ ui.stdout_print("=> Config file:", options[:config_file].inspect)
49
+ config_file = File.expand_path(options[:config_file])
50
+
51
+ if options[:require_file]
52
+ ui.stdout_print("=> Require file:", options[:require_file].inspect)
53
+ require_file = File.expand_path(options[:require_file])
54
+ end
55
+
56
+ unless File.file?(config_file)
57
+ ui.fail_with(
58
+ "=> ERROR: config file doesn't exist at",
59
+ config_file.inspect
60
+ )
61
+ end
62
+
63
+ if require_file && !File.file?(require_file)
64
+ ui.fail_with(
65
+ "=> ERROR: require file doesn't exist at",
66
+ require_file.inspect
67
+ )
68
+ end
69
+
70
+ config = load_config_file(config_file)
71
+ Schema.validate!(config)
72
+
73
+ load_require_file!(require_file) if require_file
74
+
75
+ default_locale = I18n.default_locale
76
+ available_locales = I18n.available_locales
77
+ ignored_keys = config.dig(:lint_translations, :ignore) || []
78
+
79
+ mapping = available_locales.each_with_object({}) do |locale, buffer|
80
+ buffer[locale] =
81
+ Glob::Map.call(Glob.filter(I18nJS.translations, ["#{locale}.*"]))
82
+ .map {|key| key.gsub(/^.*?\./, "") }
83
+ end
84
+
85
+ default_locale_keys = mapping.delete(default_locale)
86
+
87
+ if ignored_keys.any?
88
+ ui.stdout_print "=> Check #{options[:config_file].inspect} for " \
89
+ "ignored keys."
90
+ end
91
+
92
+ ui.stdout_print "=> #{default_locale}: #{default_locale_keys.size} " \
93
+ "translations"
94
+
95
+ total_missing_count = 0
96
+
97
+ mapping.each do |locale, partial_keys|
98
+ ignored_count = 0
99
+
100
+ # Compute list of filtered keys (i.e. keys not ignored)
101
+ filtered_keys = partial_keys.reject do |key|
102
+ key = "#{locale}.#{key}"
103
+
104
+ ignored = ignored_keys.include?(key)
105
+ ignored_count += 1 if ignored
106
+ ignored
107
+ end
108
+
109
+ extraneous = (partial_keys - default_locale_keys).reject do |key|
110
+ key = "#{locale}.#{key}"
111
+ ignored = ignored_keys.include?(key)
112
+ ignored_count += 1 if ignored
113
+ ignored
114
+ end
115
+
116
+ missing = (default_locale_keys - (filtered_keys - extraneous))
117
+ .reject {|key| ignored_keys.include?("#{locale}.#{key}") }
118
+
119
+ ignored_count += extraneous.size
120
+ total_missing_count += missing.size
121
+
122
+ ui.stdout_print "=> #{locale}: #{missing.size} missing, " \
123
+ "#{extraneous.size} extraneous, " \
124
+ "#{ignored_count} ignored"
125
+
126
+ all_keys = (default_locale_keys + extraneous + missing).uniq.sort
127
+
128
+ all_keys.each do |key|
129
+ next if ignored_keys.include?("#{locale}.#{key}")
130
+
131
+ label = if extraneous.include?(key)
132
+ ui.yellow("extraneous")
133
+ elsif missing.include?(key)
134
+ ui.red("missing")
135
+ else
136
+ next
137
+ end
138
+
139
+ ui.stdout_print(" - #{locale}.#{key} (#{label})")
140
+ end
141
+ end
142
+
143
+ exit(1) if total_missing_count.nonzero?
144
+ end
145
+
146
+ private def set_defaults!
147
+ config_file = "./config/i18n.yml"
148
+ require_file = "./config/environment.rb"
149
+
150
+ options[:config_file] ||= config_file if File.file?(config_file)
151
+ options[:require_file] ||= require_file if File.file?(require_file)
152
+ end
153
+ end
154
+ end
155
+ end