rails_translation_manager 0.1.0 → 1.1.2

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +20 -0
  5. data/Jenkinsfile +3 -1
  6. data/Rakefile +13 -7
  7. data/config/locales/plurals.rb +49 -0
  8. data/lib/rails_translation_manager/cleaner.rb +14 -0
  9. data/lib/rails_translation_manager/i18n_tasks_option_parser.rb +10 -0
  10. data/lib/rails_translation_manager/importer.rb +32 -14
  11. data/lib/rails_translation_manager/locale_checker/all_locales.rb +61 -0
  12. data/lib/rails_translation_manager/locale_checker/base_checker.rb +13 -0
  13. data/lib/rails_translation_manager/locale_checker/incompatible_plurals.rb +79 -0
  14. data/lib/rails_translation_manager/locale_checker/locale_checker_helper.rb +23 -0
  15. data/lib/rails_translation_manager/locale_checker/missing_english_locales.rb +31 -0
  16. data/lib/rails_translation_manager/locale_checker/missing_foreign_locales.rb +31 -0
  17. data/lib/rails_translation_manager/locale_checker/plural_forms.rb +16 -0
  18. data/lib/rails_translation_manager/locale_checker.rb +50 -0
  19. data/lib/rails_translation_manager/version.rb +1 -1
  20. data/lib/rails_translation_manager.rb +21 -3
  21. data/lib/tasks/translation.rake +43 -42
  22. data/rails_translation_manager.gemspec +6 -2
  23. data/spec/locales/cleaner/clean.yml +5 -0
  24. data/spec/locales/cleaner/with_whitespace.yml +5 -0
  25. data/spec/locales/importer/fr.csv +8 -0
  26. data/spec/locales/importer/hy_with_byte_order_mark.csv +4 -0
  27. data/spec/locales/in_sync/cy/browse.yml +12 -0
  28. data/spec/locales/in_sync/en/browse.yml +8 -0
  29. data/spec/locales/out_of_sync/cy.yml +3 -0
  30. data/spec/locales/out_of_sync/en.yml +6 -0
  31. data/spec/rails_translation_manager/cleaner_spec.rb +13 -0
  32. data/spec/rails_translation_manager/importer_spec.rb +93 -0
  33. data/spec/rails_translation_manager/locale_checker/all_locales_spec.rb +24 -0
  34. data/spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb +94 -0
  35. data/spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb +61 -0
  36. data/spec/rails_translation_manager/locale_checker/missing_english_locales_spec.rb +72 -0
  37. data/spec/rails_translation_manager/locale_checker/missing_foreign_locales_spec.rb +80 -0
  38. data/spec/rails_translation_manager/locale_checker/plural_forms_spec.rb +27 -0
  39. data/spec/rails_translation_manager/locale_checker_spec.rb +68 -0
  40. data/spec/spec_helper.rb +19 -0
  41. data/spec/support/tasks.rb +7 -0
  42. data/spec/tasks/translation_spec.rb +127 -0
  43. data/test/rails_translation_manager/yaml_writer_test.rb +2 -0
  44. data/tmp/.gitkeep +0 -0
  45. metadata +117 -18
  46. data/lib/rails_translation_manager/stealer.rb +0 -85
  47. data/lib/rails_translation_manager/validator.rb +0 -92
  48. data/test/rails_translation_manager/importer_test.rb +0 -132
  49. data/test/rails_translation_manager/stealer_test.rb +0 -251
  50. data/test/rails_translation_manager/validator_test.rb +0 -51
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe RailsTranslationManager::LocaleChecker do
6
+ context "when the locales are valid" do
7
+ it "calls each checker class" do
8
+ described_class::CHECKER_CLASSES.each do |checker|
9
+ expect_any_instance_of(checker).to receive(:report)
10
+ end
11
+
12
+ described_class.new("spec/locales/in_sync/**/*.yml").validate_locales
13
+ end
14
+
15
+ it "outputs a confirmation" do
16
+ expect { described_class.new("spec/locales/in_sync/**/*.yml").validate_locales }
17
+ .to output("Locale files are in sync, nice job!\n").to_stdout
18
+ end
19
+
20
+ it "returns true" do
21
+ expect(described_class.new("spec/locales/in_sync/**/*.yml").validate_locales)
22
+ .to eq(true)
23
+ end
24
+ end
25
+
26
+ context "when the locales are not valid" do
27
+ it "calls each checker class" do
28
+ described_class::CHECKER_CLASSES.each do |checker|
29
+ expect_any_instance_of(checker).to receive(:report)
30
+ end
31
+
32
+ described_class.new("spec/locales/out_of_sync/*.yml").validate_locales
33
+ end
34
+
35
+ it "outputs the report" do
36
+ printed = capture_stdout do
37
+ described_class.new("spec/locales/out_of_sync/*.yml").validate_locales
38
+ end
39
+
40
+ expect(printed.scan(/\[ERROR\]/).count).to eq(3)
41
+ end
42
+
43
+ it "returns false" do
44
+ expect(described_class.new("spec/locales/out_of_sync/*.yml").validate_locales)
45
+ .to eq(false)
46
+ end
47
+ end
48
+
49
+ context "when the locale path doesn't relate to any YAML files" do
50
+ it "doesn't call any checker classes" do
51
+ described_class::CHECKER_CLASSES.each do |checker|
52
+ expect_any_instance_of(checker).to_not receive(:report)
53
+ end
54
+
55
+ described_class.new("some/random/path").validate_locales
56
+ end
57
+
58
+ it "outputs an error message" do
59
+ expect { described_class.new("some/random/path").validate_locales }
60
+ .to output("No locale files found for the supplied path\n").to_stdout
61
+ end
62
+
63
+ it "returns false" do
64
+ expect(described_class.new("some/random/path").validate_locales)
65
+ .to eq(false)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_translation_manager"
4
+
5
+ RSpec.configure do |config|
6
+ config.before do
7
+ I18n.available_locales = %i[en cy]
8
+ config.before { allow($stdout).to receive(:puts) }
9
+ end
10
+
11
+ def capture_stdout(&blk)
12
+ old = $stdout
13
+ $stdout = fake = StringIO.new
14
+ blk.call
15
+ fake.string
16
+ ensure
17
+ $stdout = old
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ require 'rake'
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:suite) do
5
+ Dir.glob('lib/tasks/*.rake').each { |r| Rake::DefaultLoader.new.load r }
6
+ end
7
+ end
@@ -0,0 +1,127 @@
1
+ require "spec_helper"
2
+ require_relative "../../spec/support/tasks"
3
+
4
+ describe "rake tasks" do
5
+ before do
6
+ fake_rails = double()
7
+ fake_rails.stub(:root) { Pathname.new("spec") }
8
+ stub_const("Rails", fake_rails)
9
+ end
10
+
11
+ describe "translation:import", type: :task do
12
+ let(:task) { Rake::Task["translation:import"] }
13
+ let(:csv_path) { "path/to/import/fr.csv" }
14
+ let!(:importer_instance) { stub_importer }
15
+
16
+ it "outputs to stdout" do
17
+ expect { task.execute(csv_path: csv_path) }
18
+ .to output("Imported CSV from: #{csv_path} to #{Rails.root.join("config", "locales")}\n")
19
+ .to_stdout
20
+ end
21
+
22
+ it "calls the Importer class with the csv and import paths" do
23
+ task.execute(csv_path: csv_path)
24
+
25
+ expect(RailsTranslationManager::Importer)
26
+ .to have_received(:new)
27
+ .with(locale: "fr",
28
+ csv_path: csv_path,
29
+ import_directory: Rails.root.join("config", "locales"),
30
+ multiple_files_per_language: false)
31
+ expect(importer_instance).to have_received(:import)
32
+ end
33
+ end
34
+
35
+ describe "translation:import:all", type: :task do
36
+ let(:task) { Rake::Task["translation:import:all"] }
37
+ let(:csv_directory) { "locales/importer" }
38
+ let!(:importer_instance) { stub_importer }
39
+
40
+ it "outputs to stdout" do
41
+ expect { task.execute(csv_directory: csv_directory) }
42
+ .to output("Imported all CSVs from: #{csv_directory} to #{Rails.root.join("config", "locales")}\n")
43
+ .to_stdout
44
+ end
45
+
46
+ it "calls the importer class for each target path" do
47
+ task.execute(csv_directory: csv_directory, multiple_files_per_language: true)
48
+ import_paths = Dir["spec/locales/importer/*.csv"]
49
+
50
+ import_paths.each do |csv_path|
51
+ expect(RailsTranslationManager::Importer)
52
+ .to have_received(:new)
53
+ .with(locale: File.basename(csv_path, ".csv"),
54
+ csv_path: csv_path,
55
+ import_directory: Rails.root.join("config", "locales"),
56
+ multiple_files_per_language: true)
57
+ end
58
+
59
+ expect(importer_instance).to have_received(:import).exactly(import_paths.count)
60
+ end
61
+ end
62
+
63
+ describe "translation:add_missing", type: :task do
64
+ let(:task) { Rake::Task["translation:add_missing"] }
65
+ let!(:cleaner_instance) { stub_cleaner }
66
+
67
+ before do
68
+ allow(I18n::Tasks::CLI).to receive(:start)
69
+ end
70
+
71
+ it "triggers Cleaner and allows to receive the right arguments" do
72
+ task.execute
73
+ expect(RailsTranslationManager::Cleaner)
74
+ .to have_received(:new)
75
+ .with(Rails.root.join("config", "locales"))
76
+ expect(cleaner_instance).to have_received(:clean)
77
+ end
78
+
79
+ it "triggers i18n task and allows to receive the right arguments" do
80
+ task.execute(locale: "fr")
81
+ expect(I18n::Tasks::CLI).to have_received(:start).with(
82
+ ["add-missing", "--nil-value", ["-l", "fr"]]
83
+ )
84
+ end
85
+ end
86
+
87
+ describe "translation:normalize", type: :task do
88
+ let(:task) { Rake::Task["translation:normalize"] }
89
+ let!(:cleaner_instance) { stub_cleaner }
90
+
91
+ before do
92
+ allow(I18n::Tasks::CLI).to receive(:start)
93
+ end
94
+
95
+ it "triggers Cleaner and allows to receive the right arguments" do
96
+ task.execute(locale_directory: "config/locales")
97
+ expect(RailsTranslationManager::Cleaner)
98
+ .to have_received(:new)
99
+ .with(Rails.root.join("config", "locales"))
100
+ expect(cleaner_instance).to have_received(:clean)
101
+ end
102
+
103
+ it "triggers i18n task and allows to receive the right arguments" do
104
+ task.execute
105
+ expect(I18n::Tasks::CLI).to have_received(:start).with(["normalize"])
106
+ end
107
+ end
108
+
109
+ def stub_importer
110
+ importer_instance = instance_double(RailsTranslationManager::Importer)
111
+ allow(RailsTranslationManager::Importer).to receive(:new)
112
+ .and_return(importer_instance)
113
+ allow(importer_instance).to receive(:import)
114
+
115
+ importer_instance
116
+ end
117
+
118
+ def stub_cleaner
119
+ cleaner_instance = instance_double(RailsTranslationManager::Cleaner)
120
+ allow(RailsTranslationManager::Cleaner)
121
+ .to receive(:new)
122
+ .and_return(cleaner_instance)
123
+ allow(cleaner_instance).to receive(:clean)
124
+
125
+ cleaner_instance
126
+ end
127
+ end
@@ -1,3 +1,5 @@
1
+ require "test_helper"
2
+ require "rails_translation_manager/yaml_writer"
1
3
  module RailsTranslationManager
2
4
  class DummyWriter
3
5
  include YAMLWriter
data/tmp/.gitkeep ADDED
File without changes
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_translation_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edd Sowden
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-24 00:00:00.000000000 Z
11
+ date: 2021-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails-i18n
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,7 +25,35 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: activesupport
28
+ name: csv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: i18n-tasks
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails-i18n
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - ">="
@@ -42,16 +70,16 @@ dependencies:
42
70
  name: bundler
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
- - - "~>"
73
+ - - ">="
46
74
  - !ruby/object:Gem::Version
47
- version: '1.7'
75
+ version: '0'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
- - - "~>"
80
+ - - ">="
53
81
  - !ruby/object:Gem::Version
54
- version: '1.7'
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rake
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +108,34 @@ dependencies:
80
108
  - - ">="
81
109
  - !ruby/object:Gem::Version
82
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: byebug
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
83
139
  description: ''
84
140
  email:
85
141
  - edd.sowden@digital.cabinet-office.gov.uk
@@ -97,22 +153,49 @@ files:
97
153
  - LICENSE.txt
98
154
  - README.md
99
155
  - Rakefile
156
+ - config/locales/plurals.rb
100
157
  - lib/rails_translation_manager.rb
158
+ - lib/rails_translation_manager/cleaner.rb
101
159
  - lib/rails_translation_manager/exporter.rb
160
+ - lib/rails_translation_manager/i18n_tasks_option_parser.rb
102
161
  - lib/rails_translation_manager/importer.rb
162
+ - lib/rails_translation_manager/locale_checker.rb
163
+ - lib/rails_translation_manager/locale_checker/all_locales.rb
164
+ - lib/rails_translation_manager/locale_checker/base_checker.rb
165
+ - lib/rails_translation_manager/locale_checker/incompatible_plurals.rb
166
+ - lib/rails_translation_manager/locale_checker/locale_checker_helper.rb
167
+ - lib/rails_translation_manager/locale_checker/missing_english_locales.rb
168
+ - lib/rails_translation_manager/locale_checker/missing_foreign_locales.rb
169
+ - lib/rails_translation_manager/locale_checker/plural_forms.rb
103
170
  - lib/rails_translation_manager/railtie.rb
104
- - lib/rails_translation_manager/stealer.rb
105
- - lib/rails_translation_manager/validator.rb
106
171
  - lib/rails_translation_manager/version.rb
107
172
  - lib/rails_translation_manager/yaml_writer.rb
108
173
  - lib/tasks/translation.rake
109
174
  - rails_translation_manager.gemspec
175
+ - spec/locales/cleaner/clean.yml
176
+ - spec/locales/cleaner/with_whitespace.yml
177
+ - spec/locales/importer/fr.csv
178
+ - spec/locales/importer/hy_with_byte_order_mark.csv
179
+ - spec/locales/in_sync/cy/browse.yml
180
+ - spec/locales/in_sync/en/browse.yml
181
+ - spec/locales/out_of_sync/cy.yml
182
+ - spec/locales/out_of_sync/en.yml
183
+ - spec/rails_translation_manager/cleaner_spec.rb
184
+ - spec/rails_translation_manager/importer_spec.rb
185
+ - spec/rails_translation_manager/locale_checker/all_locales_spec.rb
186
+ - spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb
187
+ - spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb
188
+ - spec/rails_translation_manager/locale_checker/missing_english_locales_spec.rb
189
+ - spec/rails_translation_manager/locale_checker/missing_foreign_locales_spec.rb
190
+ - spec/rails_translation_manager/locale_checker/plural_forms_spec.rb
191
+ - spec/rails_translation_manager/locale_checker_spec.rb
192
+ - spec/spec_helper.rb
193
+ - spec/support/tasks.rb
194
+ - spec/tasks/translation_spec.rb
110
195
  - test/rails_translation_manager/exporter_test.rb
111
- - test/rails_translation_manager/importer_test.rb
112
- - test/rails_translation_manager/stealer_test.rb
113
- - test/rails_translation_manager/validator_test.rb
114
196
  - test/rails_translation_manager/yaml_writer_test.rb
115
197
  - test/test_helper.rb
198
+ - tmp/.gitkeep
116
199
  homepage: ''
117
200
  licenses:
118
201
  - MIT
@@ -132,15 +215,31 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
215
  - !ruby/object:Gem::Version
133
216
  version: '0'
134
217
  requirements: []
135
- rubyforge_project:
136
- rubygems_version: 2.7.6
218
+ rubygems_version: 3.0.3
137
219
  signing_key:
138
220
  specification_version: 4
139
221
  summary: Tasks to manage translation files
140
222
  test_files:
223
+ - spec/locales/cleaner/clean.yml
224
+ - spec/locales/cleaner/with_whitespace.yml
225
+ - spec/locales/importer/fr.csv
226
+ - spec/locales/importer/hy_with_byte_order_mark.csv
227
+ - spec/locales/in_sync/cy/browse.yml
228
+ - spec/locales/in_sync/en/browse.yml
229
+ - spec/locales/out_of_sync/cy.yml
230
+ - spec/locales/out_of_sync/en.yml
231
+ - spec/rails_translation_manager/cleaner_spec.rb
232
+ - spec/rails_translation_manager/importer_spec.rb
233
+ - spec/rails_translation_manager/locale_checker/all_locales_spec.rb
234
+ - spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb
235
+ - spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb
236
+ - spec/rails_translation_manager/locale_checker/missing_english_locales_spec.rb
237
+ - spec/rails_translation_manager/locale_checker/missing_foreign_locales_spec.rb
238
+ - spec/rails_translation_manager/locale_checker/plural_forms_spec.rb
239
+ - spec/rails_translation_manager/locale_checker_spec.rb
240
+ - spec/spec_helper.rb
241
+ - spec/support/tasks.rb
242
+ - spec/tasks/translation_spec.rb
141
243
  - test/rails_translation_manager/exporter_test.rb
142
- - test/rails_translation_manager/importer_test.rb
143
- - test/rails_translation_manager/stealer_test.rb
144
- - test/rails_translation_manager/validator_test.rb
145
244
  - test/rails_translation_manager/yaml_writer_test.rb
146
245
  - test/test_helper.rb
@@ -1,85 +0,0 @@
1
- require "yaml"
2
- require "i18n"
3
- require_relative "yaml_writer"
4
-
5
- class RailsTranslationManager::Stealer
6
- include YAMLWriter
7
-
8
- # locale is the locale name as a string.
9
- # source_app_path is the path to the root of the app to steal from.
10
- # mapping_file_path is the path to a YAML file mapping translation keys in
11
- # the source app to those in the target app. For example:
12
- # document.type: content_item.format
13
- # document.published: content_item.metadata.published
14
- # which will import everything under "document.type" and "document.published"
15
- # in the source app, and write it to "content_item.format" and
16
- # "content_item.metadata.published" in the target app.
17
- # locales_path is the path to the locale files to output, which is usually
18
- # Rails.root.join('config/locales').
19
- # The process will preserve data already in the output file if it is not
20
- # referenced in the mapping, but will always override data belonging to keys
21
- # that are in the mapping.
22
- def initialize(locale, source_app_path, mapping_file_path, locales_path)
23
- @locale = locale
24
- @source_app_path = source_app_path
25
- @mapping_file_path = mapping_file_path
26
- @locales_path = locales_path
27
- end
28
-
29
- def steal_locale
30
- target_data = convert_locale(get_target_data)
31
- write_yaml(target_locale_path, target_data)
32
- end
33
-
34
- def convert_locale(target_data)
35
- mapping_data.each do |source, target|
36
- data = source_data[@locale]
37
- source.split('.').each { |key| data = data.fetch(key, {}) }
38
- set_recursive(target_data[@locale], target.split("."), data)
39
- end
40
- target_data
41
- end
42
-
43
- private
44
-
45
- def set_recursive(hash, keys, data)
46
- if keys.empty?
47
- data
48
- else
49
- key = keys.shift
50
- hash.tap do |h|
51
- h.merge!({ key => set_recursive(hash.fetch(key, {}), keys, data)})
52
- end
53
- end
54
- end
55
-
56
- def source_locale_path
57
- File.join(@source_app_path, 'config', 'locales', "#{@locale}.yml")
58
- end
59
-
60
- def source_data
61
- @source_data ||= YAML.load_file(source_locale_path)
62
- end
63
-
64
- def target_locale_path
65
- File.join(@locales_path, "#{@locale}.yml")
66
- end
67
-
68
- def default_target_data
69
- { @locale => {} }
70
- end
71
-
72
- def get_target_data
73
- if File.exist?(target_locale_path)
74
- YAML.load_file(target_locale_path) || default_target_data
75
- else
76
- default_target_data
77
- end
78
- end
79
-
80
- def mapping_data
81
- @mapping_data ||= YAML.load_file(@mapping_file_path)
82
- end
83
-
84
- end
85
-
@@ -1,92 +0,0 @@
1
- require 'yaml'
2
- require 'logger'
3
-
4
- class RailsTranslationManager::Validator
5
- def initialize(translation_file_path, logger = Logger.new(nil))
6
- @translation_file_path = translation_file_path
7
- @logger = logger
8
- end
9
-
10
- def check!
11
- @logger.info "Checking translation files in '#{@translation_file_path}' for unexpected interpolation keys"
12
- @logger.info "Loading reference file (#{reference_file_name})"
13
- @logger.info "Checking..."
14
- reference = load_translation_file("#{@translation_file_path}/#{reference_file_name}")
15
- Dir["#{@translation_file_path}/*.yml"].reject do |entry|
16
- File.basename(entry) == reference_file_name
17
- end.inject([]) do |errors, entry|
18
- translation_file = load_translation_file(entry)
19
- errors + unexpected_substitution_keys(reference, translation_file)
20
- end
21
- end
22
-
23
- def unexpected_substitution_keys(reference, translation_file)
24
- reference_substitutions = substitutions_in(reference)
25
- target_substitutions = substitutions_in(translation_file)
26
-
27
- targets_by_path = target_substitutions.each_with_object({}) do |target, hash|
28
- hash[exclude_locale_from_path(target.path)] = target
29
- end
30
-
31
- reference_substitutions.each_with_object([]) do |reference, unexpected_substitutions|
32
- target = targets_by_path[exclude_locale_from_path(reference.path)]
33
- next if target.nil? || reference.has_all_substitutions?(target)
34
- unexpected_substitutions << UnexpectedSubstition.new(target, reference)
35
- end
36
- end
37
-
38
- def substitutions_in(translation_file)
39
- flatten(translation_file).reject do |translation|
40
- translation.substitutions.empty?
41
- end
42
- end
43
-
44
- class TranslationEntry < Struct.new(:path, :value)
45
- def substitutions
46
- @substitutions ||= self.value.scan(/%{([^}]*)}/)
47
- end
48
-
49
- def has_all_substitutions?(other)
50
- (other.substitutions - self.substitutions).empty?
51
- end
52
- end
53
-
54
- class UnexpectedSubstition < Struct.new(:target, :reference)
55
- def to_s
56
- missing = (self.reference.substitutions - self.target.substitutions)
57
- extras = (self.target.substitutions - self.reference.substitutions)
58
- message = %Q{Key "#{target.path.join('.')}":}
59
- if extras.any?
60
- message << %Q{ Extra substitutions: ["#{extras.join('", "')}"].}
61
- end
62
- if missing.any?
63
- message << %Q{ Missing substitutions: ["#{missing.join('", "')}"].}
64
- end
65
- message
66
- end
67
- end
68
-
69
- def flatten(translation_file, path = [])
70
- translation_file.map do |key, value|
71
- case value
72
- when Hash
73
- flatten(value, path + [key])
74
- else
75
- TranslationEntry.new(path + [key], value || "")
76
- end
77
- end.flatten
78
- end
79
-
80
- def load_translation_file(filename)
81
- YAML.load_file(filename)
82
- end
83
-
84
- def reference_file_name
85
- "en.yml"
86
- end
87
-
88
- private
89
- def exclude_locale_from_path(path)
90
- path[1..-1]
91
- end
92
- end