rails_translation_manager 1.5.2 → 1.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0586632094e19c18a2fef0977554080b300afd1b590b25396f7212bc699bcf49'
4
- data.tar.gz: b40d1c4a33d1ab35e2dd997a28dbf1cf43303e96ef12b87b6f309038bee63754
3
+ metadata.gz: 96e105c50ae765f973ec2993ec1321ed96bd41594bca178dea8d7f411bf20414
4
+ data.tar.gz: cc4d21ec122aa9f69e33e83793eb8c4f04f70ed07dbba2ebeeb4c17a8c2e436e
5
5
  SHA512:
6
- metadata.gz: 6f99d23e8366b0d646d0e2e273cc2919cbf156b5b82a28de499ea4195f8c34ec956e00e118c8eefe09a378a07417bba8f7d6197c91559442e43f649081b63c94
7
- data.tar.gz: f1e76272d20dad72b019f078302329137308072e44054174591de882db82826f9fd9aca7c71df6a11d238b750e69039092316788e11d960172e7a673f9a01538
6
+ metadata.gz: f19d4c1ad7adbf432fb17143cd3ea7a2c22758ef15b45b6e875f2f53b3bd8010ccf19fc91cb789115c6c3a5b779a14c8d23b21888a3a073295eedc0de6a6f83c
7
+ data.tar.gz: 339310d4341e0f77cc6aeacbd06e51da634e251ee164bfd4055a3707d6f38c0fd007623cf9dd548d1d69690c724d3b94bd721d10758ea3e832608299596db5c4
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: /
5
+ schedule:
6
+ interval: daily
@@ -0,0 +1,36 @@
1
+ on: [push, pull_request]
2
+
3
+ jobs:
4
+ # This matrix job runs the test suite against multiple Ruby versions
5
+ test_matrix:
6
+ strategy:
7
+ fail-fast: false
8
+ matrix:
9
+ # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
10
+ ruby: [ 2.7, '3.0', 3.1 ]
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - uses: ruby/setup-ruby@v1
15
+ with:
16
+ ruby-version: ${{ matrix.ruby }}
17
+ bundler-cache: true
18
+ - run: bundle exec rake
19
+
20
+ # Branch protection rules cannot directly depend on status checks from matrix jobs.
21
+ # So instead we define `test` as a dummy job which only runs after the preceding `test_matrix` checks have passed.
22
+ # Solution inspired by: https://github.community/t/status-check-for-a-matrix-jobs/127354/3
23
+ test:
24
+ needs: test_matrix
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - run: echo "All matrix tests have passed 🚀"
28
+
29
+ publish:
30
+ needs: test
31
+ if: ${{ github.ref == 'refs/heads/main' }}
32
+ permissions:
33
+ contents: write
34
+ uses: alphagov/govuk-infrastructure/.github/workflows/publish-rubygem.yaml@main
35
+ secrets:
36
+ GEM_HOST_API_KEY: ${{ secrets.ALPHAGOV_RUBYGEMS_API_KEY }}
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 1.6.0
2
+
3
+ Update plural file loading https://github.com/alphagov/rails_translation_manager/pull/52
4
+ Update missing English keys checker https://github.com/alphagov/rails_translation_manager/pull/53
5
+ Add missing plurals for Welsh, Maltese and Hong Kong Chinese.
6
+ * https://github.com/alphagov/rails_translation_manager/pull/49
7
+ * https://github.com/alphagov/rails_translation_manager/pull/50
8
+
1
9
  # 1.5.2
2
10
 
3
11
  Add missing plurals for Gujarati and Yiddish https://github.com/alphagov/rails_translation_manager/pull/44
@@ -1,6 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  {
4
+ # Welsh
5
+ cy: { i18n: { plural: { keys: %i[zero one two few many other],
6
+ rule:
7
+ lambda do |n|
8
+ case n
9
+ when 0 then :zero
10
+ when 1 then :one
11
+ when 2 then :two
12
+ when 3 then :few
13
+ when 6 then :many
14
+ else :other
15
+ end
16
+ end } } },
4
17
  # Dari - this isn't an iso code. Probably should be 'prs' as per ISO 639-3.
5
18
  dr: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
6
19
  # Latin America and Caribbean Spanish
@@ -25,6 +38,23 @@
25
38
  # Kazakh
26
39
  kk: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
27
40
  # Pashto
41
+ # Maltese
42
+ mt: { i18n: { plural: { keys: %i[one few many other],
43
+ rule:
44
+ lambda do |n|
45
+ n ||= 0
46
+ mod100 = n % 100
47
+
48
+ if n == 1
49
+ :one
50
+ elsif n.zero? || (2..10).to_a.include?(mod100)
51
+ :few
52
+ elsif (11..19).to_a.include?(mod100)
53
+ :many
54
+ else
55
+ :other
56
+ end
57
+ end } } },
28
58
  ps: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
29
59
  # Punjabi Shahmukhi
30
60
  "pa-pk": { i18n: { plural: { keys: %i[one other], rule: ->(n) { [0, 1].include?(n) ? :one : :other } } } },
@@ -44,6 +74,8 @@
44
74
  uz: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
45
75
  # Yiddish
46
76
  yi: { i18n: { plural: { keys: %i[one other], rule: ->(n) { [0, 1].include?(n) ? :one : :other } } } },
77
+ # Chinese
78
+ zh: { i18n: { plural: { keys: %i[other], rule: ->(_) { :other } } } },
47
79
  # Chinese Hong Kong
48
80
  'zh-hk' => { i18n: { plural: { keys: %i[other], rule: -> { :other } } } },
49
81
  # Chinese Taiwan
@@ -13,19 +13,23 @@ class MissingEnglishLocales < BaseChecker
13
13
 
14
14
  def format_missing_english_locales(keys)
15
15
  formatted_keys = keys.to_a.map do |group|
16
- "\e[1mMissing English keys:\e[22m #{group[0]}\n\e[1mFound in:\e[22m #{group[1]}"
16
+ "\e[1mMissing English locales:\e[22m #{group[0]}\n\e[1mFound in:\e[22m #{group[1]}"
17
17
  end
18
18
 
19
- "\e[31m[ERROR]\e[0m Missing English locales, either remove these keys from the foreign locales or add them to the English locales\n\n#{formatted_keys.join("\n\n")}"
19
+ "\e[31m[ERROR]\e[0m Missing English locales, either remove them from the foreign locale files or add them to the English locale files\n\n#{formatted_keys.join("\n\n")}"
20
20
  end
21
21
 
22
22
  def missing_english_locales
23
- all_locales.each_with_object({}) do |locale, hsh|
24
- missing_keys = exclude_plurals(locale[:keys]) - english_keys_excluding_plurals(all_locales)
23
+ all_locales.each_with_object({}) do |group, hsh|
24
+ next if group[:locale] == :en
25
+
26
+ keys = exclude_plurals(group[:keys])
27
+
28
+ missing_keys = keys.reject { |key| I18n.exists?(key) }
25
29
 
26
30
  next if missing_keys.blank?
27
31
 
28
- hsh[locale[:locale]] = missing_keys
32
+ hsh[group[:locale]] = missing_keys
29
33
  end
30
34
  end
31
35
  end
@@ -4,7 +4,7 @@ class PluralForms
4
4
  def self.all
5
5
  I18n.available_locales.each_with_object({}) do |locale, hsh|
6
6
  begin
7
- # fetches plural form (rule) from rails-i18n for locale
7
+ # fetches plural form (rule) from rails-i18n or config/locales/plurals.rb for locale
8
8
  plural_form = I18n.with_locale(locale) { I18n.t!("i18n.plural.keys") }.sort
9
9
  rescue I18n::MissingTranslationData
10
10
  plural_form = nil
@@ -1,3 +1,3 @@
1
1
  module RailsTranslationManager
2
- VERSION = "1.5.2"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -20,13 +20,9 @@ require "rails_translation_manager/exporter"
20
20
  require "rails_translation_manager/importer"
21
21
 
22
22
  module RailsTranslationManager
23
- rails_i18n_path = Gem::Specification.find_by_name("rails-i18n").gem_dir
24
23
  rails_translation_manager = Gem::Specification.find_by_name("rails_translation_manager").gem_dir
25
24
 
26
- I18n.load_path.concat(
27
- Dir["#{rails_i18n_path}/rails/pluralization/*.rb"],
28
- ["#{rails_translation_manager}/config/locales/plurals.rb"]
29
- )
25
+ I18n.load_path += ["#{rails_translation_manager}/config/locales/plurals.rb"]
30
26
 
31
27
  def self.locale_root
32
28
  if ENV["RAILS_TRANSLATION_MANAGER_LOCALE_ROOT"]
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.add_development_dependency "bundler"
27
27
  spec.add_development_dependency "climate_control"
28
- spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rake", "~> 13.0"
29
29
  spec.add_development_dependency "minitest"
30
30
  spec.add_development_dependency "rspec"
31
31
  spec.add_development_dependency "byebug"
@@ -1,5 +1,5 @@
1
1
  ---
2
- cy:
2
+ en:
3
3
  in_sync:
4
4
  key: food
5
5
  second_key: cat
@@ -32,73 +32,81 @@ RSpec.describe RailsTranslationManager::Importer do
32
32
  context "when there is one locale file per language" do
33
33
  let(:yaml_translation_data) { YAML.load_file(import_directory + "/fr.yml")["fr"] }
34
34
 
35
- before do
36
- importer = described_class.new(
35
+ let(:importer) do
36
+ described_class.new(
37
37
  locale: "fr",
38
38
  csv_path: "spec/locales/importer/fr.csv",
39
39
  import_directory: import_directory,
40
40
  multiple_files_per_language: false
41
41
  )
42
- importer.import
43
42
  end
44
43
 
45
44
  it "creates one YAML file per language" do
45
+ expect { importer.import }.to output.to_stdout
46
46
  expect(File).to exist(import_directory + "/fr.yml")
47
47
  end
48
48
 
49
49
  it "imports nested locales" do
50
+ expect { importer.import }.to output.to_stdout
50
51
  expected = { "type" => { "country" => "Pays" } }
51
52
  expect(yaml_translation_data).to include("world_location" => hash_including(expected))
52
53
  end
53
54
 
54
55
  it "imports arrays from CSV as arrays" do
56
+ expect { importer.import }.to output.to_stdout
55
57
  expected = { "fruit" => ["Pommes", "Bananes", "Poires"] }
56
58
  expect(yaml_translation_data).to include("world_location" => hash_including(expected))
57
59
  end
58
60
 
59
61
  it "imports string 'nil' as nil" do
62
+ expect { importer.import }.to output.to_stdout
60
63
  expected = { "things" => nil }
61
64
  expect(yaml_translation_data).to include("world_location" => hash_including(expected))
62
65
  end
63
66
 
64
67
  it "imports string ':thing' as symbol" do
68
+ expect { importer.import }.to output.to_stdout
65
69
  expected = { "sentiment" => :bof }
66
70
  expect(yaml_translation_data).to include("world_location" => hash_including(expected))
67
71
  end
68
72
 
69
73
  it "imports integer strings as integers" do
74
+ expect { importer.import }.to output.to_stdout
70
75
  expected = { "price" => 123 }
71
76
  expect(yaml_translation_data).to include("shared" => hash_including(expected))
72
77
  end
73
78
 
74
79
  it "imports boolean values as booleans, not strings" do
80
+ expect { importer.import }.to output.to_stdout
75
81
  expected = { "key1" => true, "key2" => false }
76
82
  expect(yaml_translation_data).to include("shared" => hash_including(expected))
77
83
  end
78
84
  end
79
85
 
80
86
  context "when there are multiple files per locale" do
81
- before do
82
- importer = described_class.new(
87
+ let(:importer) do
88
+ described_class.new(
83
89
  locale: "fr",
84
90
  csv_path: "spec/locales/importer/fr.csv",
85
91
  import_directory: import_directory,
86
92
  multiple_files_per_language: true
87
93
  )
88
- importer.import
89
94
  end
90
95
 
91
96
  it "creates multiple YAML files per language in the language's directory" do
97
+ expect { importer.import }.to output.to_stdout
92
98
  expect(File).to exist(import_directory + "/fr/world_location.yml")
93
99
  .and exist(import_directory + "/fr/shared.yml")
94
100
  end
95
101
 
96
102
  it "imports only 'world_location' locales to the relevant file" do
103
+ expect { importer.import }.to output.to_stdout
97
104
  yaml_translation_data = YAML.load_file(import_directory + "/fr/world_location.yml")["fr"]
98
105
  expect(yaml_translation_data).to match("world_location" => anything)
99
106
  end
100
107
 
101
108
  it "imports only 'shared' locales to the relevant file" do
109
+ expect { importer.import }.to output.to_stdout
102
110
  yaml_translation_data = YAML.load_file(import_directory + "/fr/shared.yml")["fr"]
103
111
  expect(yaml_translation_data).to match("shared" => anything)
104
112
  end
@@ -4,6 +4,12 @@ require "spec_helper"
4
4
 
5
5
  RSpec.describe MissingEnglishLocales do
6
6
  context "where there are missing English locales" do
7
+ before do
8
+ I18n.backend.store_translations :en, { browse: { same_key: "value" } }
9
+ I18n.backend.store_translations :cy, { browse: { same_key: "value" } }
10
+ I18n.backend.store_translations :cy, { browse: { extra_key: "extra_key" } }
11
+ end
12
+
7
13
  let(:all_locales) do
8
14
  [
9
15
  {
@@ -21,9 +27,9 @@ RSpec.describe MissingEnglishLocales do
21
27
  expect(described_class.new(all_locales).report)
22
28
  .to eq(
23
29
  <<~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
30
+ \e[31m[ERROR]\e[0m Missing English locales, either remove them from the foreign locale files or add them to the English locale files
25
31
 
26
- \e[1mMissing English keys:\e[22m ["browse.extra_key"]
32
+ \e[1mMissing English locales:\e[22m ["browse.extra_key"]
27
33
  \e[1mFound in:\e[22m [:cy]
28
34
  OUTPUT
29
35
  )
@@ -31,6 +37,11 @@ RSpec.describe MissingEnglishLocales do
31
37
  end
32
38
 
33
39
  context "where there aren't missing English locales" do
40
+ before do
41
+ I18n.backend.store_translations :en, { browse: { same_key: "value" } }
42
+ I18n.backend.store_translations :cy, { browse: { same_key: "value" } }
43
+ end
44
+
34
45
  let(:all_locales) do
35
46
  [
36
47
  {
@@ -21,6 +21,9 @@ RSpec.describe PluralForms do
21
21
  end
22
22
 
23
23
  it "returns nil for associated locales" do
24
+ # clears any in-memory translations
25
+ I18n.backend.reload!
26
+
24
27
  expect(described_class.all).to include({ cy: nil, en: nil })
25
28
  end
26
29
  end
@@ -4,12 +4,19 @@ require "spec_helper"
4
4
 
5
5
  RSpec.describe RailsTranslationManager::LocaleChecker do
6
6
  context "when the locales are valid" do
7
+ before do
8
+ I18n.backend.load_translations(
9
+ ["spec/locales/in_sync/cy/browse.yml", "spec/locales/in_sync/en/browse.yml"]
10
+ )
11
+ end
12
+
7
13
  it "calls each checker class" do
8
14
  described_class::CHECKER_CLASSES.each do |checker|
9
15
  expect_any_instance_of(checker).to receive(:report)
10
16
  end
11
17
 
12
- described_class.new("spec/locales/in_sync/**/*.yml").validate_locales
18
+ expect { described_class.new("spec/locales/in_sync/**/*.yml").validate_locales }
19
+ .to output.to_stdout
13
20
  end
14
21
 
15
22
  it "outputs a confirmation" do
@@ -18,51 +25,51 @@ RSpec.describe RailsTranslationManager::LocaleChecker do
18
25
  end
19
26
 
20
27
  it "returns true" do
21
- expect(described_class.new("spec/locales/in_sync/**/*.yml").validate_locales)
22
- .to eq(true)
28
+ outcome = nil
29
+ expect { outcome = described_class.new("spec/locales/in_sync/**/*.yml").validate_locales }.to output.to_stdout
30
+ expect(outcome).to be(true)
23
31
  end
24
32
  end
25
33
 
26
34
  context "when the locales are not valid" do
35
+ before do
36
+ I18n.backend.load_translations(
37
+ ["spec/locales/out_of_sync/cy.yml", "spec/locales/out_of_sync/en.yml"]
38
+ )
39
+ end
40
+
27
41
  it "calls each checker class" do
28
42
  described_class::CHECKER_CLASSES.each do |checker|
29
43
  expect_any_instance_of(checker).to receive(:report)
30
44
  end
31
45
 
32
- described_class.new("spec/locales/out_of_sync/*.yml").validate_locales
46
+ expect { described_class.new("spec/locales/out_of_sync/*.yml").validate_locales }
47
+ .to output.to_stdout
33
48
  end
34
49
 
35
50
  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)
51
+ # expect output to contain 3 errors ([ERROR])
52
+ expect { described_class.new("spec/locales/out_of_sync/*.yml").validate_locales }
53
+ .to output(/(?:\[ERROR\](?:.|\n)*){3}/).to_stdout
41
54
  end
42
55
 
43
56
  it "returns false" do
44
- expect(described_class.new("spec/locales/out_of_sync/*.yml").validate_locales)
45
- .to eq(false)
57
+ outcome = nil
58
+ expect { outcome = described_class.new("spec/locales/out_of_sync/*.yml").validate_locales }.to output.to_stdout
59
+ expect(outcome).to be(false)
46
60
  end
47
61
  end
48
62
 
49
63
  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
64
  it "outputs an error message" do
59
65
  expect { described_class.new("some/random/path").validate_locales }
60
66
  .to output("No locale files found for the supplied path\n").to_stdout
61
67
  end
62
68
 
63
69
  it "returns false" do
64
- expect(described_class.new("some/random/path").validate_locales)
65
- .to eq(false)
70
+ outcome = nil
71
+ expect { outcome = described_class.new("some/random/path").validate_locales }.to output.to_stdout
72
+ expect(outcome).to be(false)
66
73
  end
67
74
  end
68
75
  end
data/spec/spec_helper.rb CHANGED
@@ -6,15 +6,23 @@ require "climate_control"
6
6
  RSpec.configure do |config|
7
7
  config.before do
8
8
  I18n.available_locales = %i[en cy]
9
- config.before { allow($stdout).to receive(:puts) }
10
- end
9
+ I18n.backend.store_translations :en, i18n: { plural: { keys: %i[one other],
10
+ rule:
11
+ lambda do |n|
12
+ n == 1 ? :one : :other
13
+ end } }
14
+ I18n.backend.store_translations :cy, i18n: { plural: { keys: %i[zero one two few many other],
15
+ rule:
16
+ lambda do |n|
17
+ case n
18
+ when 0 then :zero
19
+ when 1 then :one
20
+ when 2 then :two
21
+ when 3 then :few
22
+ when 6 then :many
23
+ else :other
24
+ end
25
+ end } }
11
26
 
12
- def capture_stdout(&blk)
13
- old = $stdout
14
- $stdout = fake = StringIO.new
15
- blk.call
16
- fake.string
17
- ensure
18
- $stdout = old
19
27
  end
20
28
  end
@@ -20,7 +20,7 @@ describe "rake tasks" do
20
20
  end
21
21
 
22
22
  it "calls the Importer class with the csv and import paths" do
23
- task.execute(csv_path: csv_path)
23
+ expect { task.execute(csv_path: csv_path) }.to output.to_stdout
24
24
 
25
25
  expect(RailsTranslationManager::Importer)
26
26
  .to have_received(:new)
@@ -44,7 +44,10 @@ describe "rake tasks" do
44
44
  end
45
45
 
46
46
  it "calls the importer class for each target path" do
47
- task.execute(csv_directory: csv_directory, multiple_files_per_language: true)
47
+ expect { task.execute(csv_directory: csv_directory, multiple_files_per_language: true) }
48
+ .to output
49
+ .to_stdout
50
+
48
51
  import_paths = Dir["spec/locales/importer/*.csv"]
49
52
 
50
53
  import_paths.each do |csv_path|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_translation_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edd Sowden
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-07 00:00:00.000000000 Z
11
+ date: 2022-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '10.0'
103
+ version: '13.0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '10.0'
110
+ version: '13.0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: minitest
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -157,11 +157,12 @@ executables: []
157
157
  extensions: []
158
158
  extra_rdoc_files: []
159
159
  files:
160
+ - ".github/dependabot.yml"
161
+ - ".github/workflows/ci.yml"
160
162
  - ".gitignore"
161
163
  - ".ruby-version"
162
164
  - CHANGELOG.md
163
165
  - Gemfile
164
- - Jenkinsfile
165
166
  - LICENSE.txt
166
167
  - README.md
167
168
  - Rakefile
@@ -235,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
235
236
  - !ruby/object:Gem::Version
236
237
  version: '0'
237
238
  requirements: []
238
- rubygems_version: 3.1.6
239
+ rubygems_version: 3.3.26
239
240
  signing_key:
240
241
  specification_version: 4
241
242
  summary: Tasks to manage translation files
data/Jenkinsfile DELETED
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env groovy
2
-
3
- library("govuk")
4
-
5
- node {
6
- govuk.buildProject(
7
- cleanWorkspace: true,
8
- )
9
- }