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
@@ -1,15 +1,9 @@
1
- require "rails_translation_manager"
1
+ require "i18n/tasks/cli"
2
+ require_relative "../rails_translation_manager"
3
+ require_relative "../rails_translation_manager/i18n_tasks_option_parser"
2
4
 
3
5
  namespace :translation do
4
6
 
5
- desc "Regenerate all locales from the EN locale - run this after adding keys"
6
- task(:regenerate, [:directory] => [:environment]) do |t, args|
7
- directory = args[:directory] || "tmp/locale_csv"
8
-
9
- Rake::Task["translation:export:all"].invoke(directory)
10
- Rake::Task["translation:import:all"].invoke(directory)
11
- end
12
-
13
7
  desc "Export a specific locale to CSV."
14
8
  task :export, [:directory, :base_locale, :target_locale] => [:environment] do |t, args|
15
9
  FileUtils.mkdir_p(args[:directory]) unless File.exist?(args[:directory])
@@ -36,51 +30,58 @@ namespace :translation do
36
30
  end
37
31
 
38
32
  desc "Import a specific locale CSV to YAML within the app."
39
- task :import, [:locale, :path] => [:environment] do |t, args|
40
- importer = RailsTranslationManager::Importer.new(args[:locale], args[:path], Rails.root.join("config", "locales"))
33
+ task :import, [:csv_path, :multiple_files_per_language] => [:environment] do |t, args|
34
+ import_dir = Rails.root.join("config", "locales")
35
+ csv_path = args[:csv_path]
36
+
37
+ importer = RailsTranslationManager::Importer.new(
38
+ locale: File.basename(args[:csv_path], ".csv"),
39
+ csv_path: csv_path,
40
+ import_directory: Rails.root.join("config", "locales"),
41
+ multiple_files_per_language: args[:multiple_files_per_language] || false
42
+ )
41
43
  importer.import
44
+
45
+ puts "Imported CSV from: #{csv_path} to #{import_dir}"
42
46
  end
43
47
 
44
48
  namespace :import do
45
49
  desc "Import all locale CSV files to YAML within the app."
46
- task :all, [:directory] => [:environment] do |t, args|
47
- directory = args[:directory] || "tmp/locale_csv"
48
- Dir[File.join(directory, "*.csv")].each do |csv_path|
49
- locale = File.basename(csv_path, ".csv")
50
- importer = RailsTranslationManager::Importer.new(locale, csv_path, Rails.root.join("config", "locales"))
50
+ task :all, [:csv_directory, :multiple_files_per_language] => [:environment] do |t, args|
51
+ directory = args[:csv_directory] || "tmp/locale_csv"
52
+ import_dir = Rails.root.join("config", "locales")
53
+
54
+ Dir[Rails.root.join(directory, "*.csv")].each do |csv_path|
55
+ importer = RailsTranslationManager::Importer.new(
56
+ locale: File.basename(csv_path, ".csv"),
57
+ csv_path: csv_path,
58
+ import_directory: import_dir,
59
+ multiple_files_per_language: args[:multiple_files_per_language] || false
60
+ )
51
61
  importer.import
52
62
  end
53
- end
54
- end
55
63
 
56
- desc "Check translation files for errors"
57
- task :validate do
58
- require 'rails_translation_manager/validator'
59
- logger = Logger.new(STDOUT)
60
- validator = RailsTranslationManager::Validator.new(Rails.root.join('config', 'locales'), logger)
61
- errors = validator.check!
62
- if errors.any?
63
- puts "Found #{errors.size} errors:"
64
- puts errors.map(&:to_s).join("\n")
65
- else
66
- puts "Success! No unexpected interpolation keys found."
64
+ puts "Imported all CSVs from: #{directory} to #{import_dir}"
67
65
  end
68
66
  end
69
67
 
70
- desc "Import and convert a locale file from another app."
71
- task :steal, [:locale, :source_app_path, :mapping_file_path] do |t, args|
72
- stealer = RailsTranslationManager::Stealer.new(args[:locale], args[:source_app_path], args[:mapping_file_path], Rails.root.join('config', 'locales'))
73
- stealer.steal_locale
74
- end
68
+ desc "Add missing translations"
69
+ task(:add_missing, [:locale] => [:environment]) do |t, args|
70
+ option_parser = RailsTranslationManager::I18nTasksOptionParser.new(
71
+ ["add-missing", "--nil-value"], args[:locale]
72
+ ).with_optional_locale
75
73
 
76
- namespace :steal do
77
- desc "Import and convert all locale files from another app."
78
- task :all, [:source_app_path, :mapping_file_path] => [:environment] do |t, args|
79
- I18n.available_locales.reject { |l| l == :en }.each do |locale|
80
- stealer = RailsTranslationManager::Stealer.new(locale.to_s, args[:source_app_path], args[:mapping_file_path], Rails.root.join('config', 'locales'))
81
- stealer.steal_locale
82
- end
83
- end
74
+ I18n::Tasks::CLI.start(option_parser)
75
+ RailsTranslationManager::Cleaner.new(Rails.root.join("config", "locales")).clean
84
76
  end
85
77
 
78
+ desc "Normalize translations"
79
+ task(:normalize, [:locale] => [:environment]) do |t, args|
80
+ option_parser = RailsTranslationManager::I18nTasksOptionParser.new(
81
+ ["normalize"], args[:locale]
82
+ ).with_optional_locale
83
+
84
+ I18n::Tasks::CLI.start(option_parser)
85
+ RailsTranslationManager::Cleaner.new(Rails.root.join("config", "locales")).clean
86
+ end
86
87
  end
@@ -18,10 +18,14 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "rails-i18n"
22
21
  spec.add_dependency "activesupport"
22
+ spec.add_dependency "csv", "~> 3.2"
23
+ spec.add_dependency "i18n-tasks"
24
+ spec.add_dependency "rails-i18n"
23
25
 
24
- spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "bundler"
25
27
  spec.add_development_dependency "rake", "~> 10.0"
26
28
  spec.add_development_dependency "minitest"
29
+ spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "byebug"
27
31
  end
@@ -0,0 +1,5 @@
1
+ ---
2
+ en:
3
+ test: key
4
+ key_1: foo
5
+ key_2: bar
@@ -0,0 +1,5 @@
1
+ ---
2
+ en:
3
+ test: key
4
+ key_1: foo
5
+ key_2: bar
@@ -0,0 +1,8 @@
1
+ key,source,translation
2
+ world_location.type.country,Country,Pays
3
+ world_location.fruit,"[Apples, Bananas, Pears]","[Pommes, Bananes, Poires]"
4
+ world_location.things,nil,nil
5
+ world_location.sentiment,:whatever,:bof
6
+ shared.price,123,123
7
+ shared.key1,is true,true
8
+ shared.key2,is false,false
@@ -0,0 +1,4 @@
1
+ key,source,translation
2
+ unpublishing.title,The page you're looking for is no longer available,Ձեր փնտրած էջն այլևս հասանելի չէ
3
+ working_group.contact_details,Contact details,Կոնտակտային տվյալներ
4
+ working_group.policies,Policies,Քաղաքականություններ
@@ -0,0 +1,12 @@
1
+ ---
2
+ cy:
3
+ in_sync:
4
+ key: bwyd
5
+ second_key: cath
6
+ some_plurals:
7
+ few:
8
+ many:
9
+ one:
10
+ other:
11
+ two:
12
+ zero:
@@ -0,0 +1,8 @@
1
+ ---
2
+ cy:
3
+ in_sync:
4
+ key: food
5
+ second_key: cat
6
+ some_plurals:
7
+ one:
8
+ other:
@@ -0,0 +1,3 @@
1
+ ---
2
+ cy:
3
+ other_test: key
@@ -0,0 +1,6 @@
1
+ ---
2
+ en:
3
+ test: key
4
+ wrong_plural:
5
+ one: correct
6
+ zero: failure
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RailsTranslationManager::Cleaner do
4
+
5
+ before(:each) do
6
+ FileUtils.cp('spec/locales/cleaner/with_whitespace.yml', 'tmp/')
7
+ end
8
+
9
+ it 'removes whitespace at the end of the line' do
10
+ described_class.new('tmp').clean
11
+ expect(FileUtils.compare_file('tmp/with_whitespace.yml', 'spec/locales/cleaner/clean.yml')).to be_truthy
12
+ end
13
+ end
@@ -0,0 +1,93 @@
1
+ require "spec_helper"
2
+ require "tmpdir"
3
+
4
+ RSpec.describe RailsTranslationManager::Importer do
5
+ let(:import_directory) { Dir.mktmpdir }
6
+
7
+ it "imports CSV containing a byte order mark" do
8
+ importer = described_class.new(
9
+ locale: "hy",
10
+ csv_path: "spec/locales/importer/hy_with_byte_order_mark.csv",
11
+ import_directory: import_directory,
12
+ multiple_files_per_language: false
13
+ )
14
+ importer.import
15
+
16
+ expect(File).to exist(import_directory + "/hy.yml")
17
+ end
18
+
19
+ context "when there is one locale file per language" do
20
+ let(:yaml_translation_data) { YAML.load_file(import_directory + "/fr.yml")["fr"] }
21
+
22
+ before do
23
+ importer = described_class.new(
24
+ locale: "fr",
25
+ csv_path: "spec/locales/importer/fr.csv",
26
+ import_directory: import_directory,
27
+ multiple_files_per_language: false
28
+ )
29
+ importer.import
30
+ end
31
+
32
+ it "creates one YAML file per language" do
33
+ expect(File).to exist(import_directory + "/fr.yml")
34
+ end
35
+
36
+ it "imports nested locales" do
37
+ expected = { "type" => { "country" => "Pays" } }
38
+ expect(yaml_translation_data).to include("world_location" => hash_including(expected))
39
+ end
40
+
41
+ it "imports arrays from CSV as arrays" do
42
+ expected = { "fruit" => ["Pommes", "Bananes", "Poires"] }
43
+ expect(yaml_translation_data).to include("world_location" => hash_including(expected))
44
+ end
45
+
46
+ it "imports string 'nil' as nil" do
47
+ expected = { "things" => nil }
48
+ expect(yaml_translation_data).to include("world_location" => hash_including(expected))
49
+ end
50
+
51
+ it "imports string ':thing' as symbol" do
52
+ expected = { "sentiment" => :bof }
53
+ expect(yaml_translation_data).to include("world_location" => hash_including(expected))
54
+ end
55
+
56
+ it "imports integer strings as integers" do
57
+ expected = { "price" => 123 }
58
+ expect(yaml_translation_data).to include("shared" => hash_including(expected))
59
+ end
60
+
61
+ it "imports boolean values as booleans, not strings" do
62
+ expected = { "key1" => true, "key2" => false }
63
+ expect(yaml_translation_data).to include("shared" => hash_including(expected))
64
+ end
65
+ end
66
+
67
+ context "when there are multiple files per locale" do
68
+ before do
69
+ importer = described_class.new(
70
+ locale: "fr",
71
+ csv_path: "spec/locales/importer/fr.csv",
72
+ import_directory: import_directory,
73
+ multiple_files_per_language: true
74
+ )
75
+ importer.import
76
+ end
77
+
78
+ it "creates multiple YAML files per language in the language's directory" do
79
+ expect(File).to exist(import_directory + "/fr/world_location.yml")
80
+ .and exist(import_directory + "/fr/shared.yml")
81
+ end
82
+
83
+ it "imports only 'world_location' locales to the relevant file" do
84
+ yaml_translation_data = YAML.load_file(import_directory + "/fr/world_location.yml")["fr"]
85
+ expect(yaml_translation_data).to match("world_location" => anything)
86
+ end
87
+
88
+ it "imports only 'shared' locales to the relevant file" do
89
+ yaml_translation_data = YAML.load_file(import_directory + "/fr/shared.yml")["fr"]
90
+ expect(yaml_translation_data).to match("shared" => anything)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe AllLocales do
6
+ context "when there are locale files" do
7
+ it "generates an array of hashes for all locales" do
8
+ expect(described_class.new("spec/locales/out_of_sync/*.yml").generate)
9
+ .to eq(
10
+ [
11
+ { keys: %w[test wrong_plural wrong_plural.one wrong_plural.zero], locale: :en },
12
+ { keys: %w[other_test], locale: :cy }
13
+ ]
14
+ )
15
+ end
16
+ end
17
+
18
+ context "when there aren't any locale files present" do
19
+ it "raises an error" do
20
+ expect { described_class.new("path/to/none/existant/locale/files").generate }
21
+ .to raise_error(AllLocales::NoLocaleFilesFound, 'No locale files found for the supplied path')
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe IncompatiblePlurals do
6
+ context "when there are missing plurals" do
7
+ let(:all_locales) do
8
+ [
9
+ {
10
+ locale: :en,
11
+ keys: %w[browse.some_key.other browse.some_other_key.one]
12
+ }
13
+ ]
14
+ end
15
+
16
+ before do
17
+ allow(PluralForms).to receive(:all).and_return({ en: %i[one other] })
18
+ end
19
+
20
+ it "returns the missing plural forms" do
21
+ expect(described_class.new(all_locales).report)
22
+ .to eq(
23
+ <<~OUTPUT.chomp
24
+ \e[31m[ERROR]\e[0m Incompatible plural forms, for:
25
+
26
+ - 'en', with parent 'browse.some_key'. Expected: [:one, :other], actual: [:other]
27
+
28
+ - 'en', with parent 'browse.some_other_key'. Expected: [:one, :other], actual: [:one]
29
+
30
+ \e[1mIf the keys reported above are not plurals, rename them avoiding plural keywords: #{LocaleCheckerHelper::PLURAL_KEYS}\e[22m
31
+ OUTPUT
32
+ )
33
+ end
34
+ end
35
+
36
+ context "when there aren't any missing plurals" do
37
+ let(:all_locales) do
38
+ [
39
+ {
40
+ locale: :en,
41
+ keys: %w[browse.some_key.other browse.some_key.one]
42
+ }
43
+ ]
44
+ end
45
+
46
+ before do
47
+ allow(PluralForms).to receive(:all).and_return({ en: %i[one other] })
48
+ end
49
+
50
+ it "returns nil" do
51
+ expect(described_class.new(all_locales).report)
52
+ .to be_nil
53
+ end
54
+ end
55
+
56
+ context "when plural forms aren't available for the locales" do
57
+ let(:all_locales) do
58
+ [
59
+ {
60
+ locale: :en,
61
+ keys: %w[browse.some_key.one browse.some_key.other]
62
+ },
63
+ {
64
+ locale: :cy,
65
+ keys: %w[browse.some_key.few
66
+ browse.some_key.many
67
+ browse.some_key.one
68
+ browse.some_key.other
69
+ browse.some_key.two
70
+ browse.some_key.zero]
71
+ }
72
+ ]
73
+ end
74
+
75
+ before do
76
+ allow(PluralForms).to receive(:all).and_return({ cy: nil, en: nil })
77
+ end
78
+
79
+ it "outputs the missing plural forms" do
80
+ expect(described_class.new(all_locales).report)
81
+ .to eq(
82
+ <<~OUTPUT.chomp
83
+ \e[31m[ERROR]\e[0m Incompatible plural forms, for:
84
+
85
+ - \e[31m[ERROR]\e[0m Please add plural form for 'en' <link to future documentation>
86
+
87
+ - \e[31m[ERROR]\e[0m Please add plural form for 'cy' <link to future documentation>
88
+
89
+ \e[1mIf the keys reported above are not plurals, rename them avoiding plural keywords: #{LocaleCheckerHelper::PLURAL_KEYS}\e[22m
90
+ OUTPUT
91
+ )
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe LocaleCheckerHelper do
6
+ include LocaleCheckerHelper
7
+
8
+ describe "#exclude_plurals" do
9
+ it "excludes plural groups" do
10
+ expect(exclude_plurals(%w[key.one key.other]))
11
+ .to eq([])
12
+ end
13
+
14
+ it "doesn't exclude non-plurals" do
15
+ expect(exclude_plurals(%w[thing.bing red.blue]))
16
+ .to eq(%w[thing.bing red.blue])
17
+ end
18
+ end
19
+
20
+ describe "#group_keys" do
21
+ it "groups locales by keys" do
22
+ keys = {
23
+ en: %w[key.first key.second],
24
+ fr: %w[key.first key.second],
25
+ es: %w[key.second]
26
+ }
27
+
28
+ expect(group_keys(keys))
29
+ .to eq(
30
+ {
31
+ %w[key.first key.second] => %i[en fr],
32
+ %w[key.second] => %i[es]
33
+ }
34
+ )
35
+ end
36
+ end
37
+
38
+ describe "#english_keys_excluding_plurals" do
39
+ it "returns english keys excluding plurals" do
40
+ keys = [
41
+ { locale: :en, keys: %w[key.english plural.other] },
42
+ { locale: :fr, keys: %w[key.french] }
43
+ ]
44
+
45
+ expect(english_keys_excluding_plurals(keys))
46
+ .to eq(%w[key.english])
47
+ end
48
+ end
49
+
50
+ describe "#key_is_plural?" do
51
+ it "returns true if key is a plural keyword" do
52
+ expect(key_is_plural?("other"))
53
+ .to eq(true)
54
+ end
55
+
56
+ it "returns false if key isn't a plural keyword" do
57
+ expect(key_is_plural?("random"))
58
+ .to eq(false)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe MissingEnglishLocales do
6
+ context "where there are missing English locales" do
7
+ let(:all_locales) do
8
+ [
9
+ {
10
+ locale: :en,
11
+ keys: %w[browse.same_key]
12
+ },
13
+ {
14
+ locale: :cy,
15
+ keys: %w[browse.same_key browse.extra_key]
16
+ }
17
+ ]
18
+ end
19
+
20
+ it "outputs the missing locales" do
21
+ expect(described_class.new(all_locales).report)
22
+ .to eq(
23
+ <<~OUTPUT.chomp
24
+ \e[31m[ERROR]\e[0m Missing English locales, either remove these keys from the foreign locales or add them to the English locales
25
+
26
+ \e[1mMissing English keys:\e[22m ["browse.extra_key"]
27
+ \e[1mFound in:\e[22m [:cy]
28
+ OUTPUT
29
+ )
30
+ end
31
+ end
32
+
33
+ context "where there aren't missing English locales" do
34
+ let(:all_locales) do
35
+ [
36
+ {
37
+ locale: :en,
38
+ keys: %w[browse.same_key]
39
+ },
40
+ {
41
+ locale: :cy,
42
+ keys: %w[browse.same_key]
43
+ }
44
+ ]
45
+ end
46
+
47
+ it 'outputs nil' do
48
+ expect(described_class.new(all_locales).report)
49
+ .to be_nil
50
+ end
51
+ end
52
+
53
+ context "when there are plurals" do
54
+ let(:all_locales) do
55
+ [
56
+ {
57
+ locale: :en,
58
+ keys: []
59
+ },
60
+ {
61
+ locale: :cy,
62
+ keys: %w[browse.plurals.one browse.fake_plurals.other]
63
+ }
64
+ ]
65
+ end
66
+
67
+ it "doesn't include them in the report" do
68
+ expect(described_class.new(all_locales).report)
69
+ .to be_nil
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe MissingForeignLocales do
6
+ context "when there are missing foreign locales" do
7
+ let(:all_locales) do
8
+ [
9
+ {
10
+ locale: :en,
11
+ keys: %w[browse.same_key browse.extra_nested_key.nest]
12
+ },
13
+ {
14
+ locale: :cy,
15
+ keys: %w[browse.same_key]
16
+ },
17
+ {
18
+ locale: :fr,
19
+ keys: %w[browse.same_key]
20
+ }
21
+ ]
22
+ end
23
+
24
+ before do
25
+ I18n.available_locales << %i[fr]
26
+ end
27
+
28
+ it "outputs the missing locales and groups them by common keys" do
29
+ expect(described_class.new(all_locales).report)
30
+ .to eq(
31
+ <<~OUTPUT.chomp
32
+ \e[31m[ERROR]\e[0m Missing foreign locales, either add these keys to the foreign locales or remove them from the English locales\e[0m
33
+
34
+ \e[1mMissing foreign keys:\e[22m ["browse.extra_nested_key.nest"]
35
+ \e[1mAbsent from:\e[22m [:cy, :fr]
36
+ OUTPUT
37
+ )
38
+ end
39
+ end
40
+
41
+ context "when there aren't missing foreign locales" do
42
+ let(:all_locales) do
43
+ [
44
+ {
45
+ locale: :en,
46
+ keys: %w[browse.same_key]
47
+ },
48
+ {
49
+ locale: :cy,
50
+ keys: %w[browse.same_key]
51
+ }
52
+ ]
53
+ end
54
+
55
+ it "outputs nil" do
56
+ expect(described_class.new(all_locales).report)
57
+ .to be_nil
58
+ end
59
+ end
60
+
61
+ context "when there are plurals" do
62
+ let(:all_locales) do
63
+ [
64
+ {
65
+ locale: :en,
66
+ keys: %w[browse.plurals.one browse.fake_plurals.other]
67
+ },
68
+ {
69
+ locale: :cy,
70
+ keys: []
71
+ }
72
+ ]
73
+ end
74
+
75
+ it "doesn't include them in the report" do
76
+ expect(described_class.new(all_locales).report)
77
+ .to be_nil
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe PluralForms do
6
+ it "returns plural forms" do
7
+ expect(described_class.all).to include(
8
+ { cy: %i[few many one other two zero], en: %i[one other] }
9
+ )
10
+ end
11
+
12
+ context "when there are missing plural forms" do
13
+ around do |example|
14
+ load_path = I18n.load_path
15
+ # strips I18n of all pluralization files (rules)
16
+ I18n.load_path = I18n.load_path.flatten.reject { |path| path =~ /plural/ }
17
+
18
+ example.run
19
+
20
+ I18n.load_path = load_path
21
+ end
22
+
23
+ it "returns nil for associated locales" do
24
+ expect(described_class.all).to include({ cy: nil, en: nil })
25
+ end
26
+ end
27
+ end