rails_translation_manager 0.1.0 → 1.0.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +8 -0
  5. data/Rakefile +13 -7
  6. data/config/locales/plurals.rb +49 -0
  7. data/lib/rails_translation_manager/cleaner.rb +14 -0
  8. data/lib/rails_translation_manager/locale_checker/all_locales.rb +61 -0
  9. data/lib/rails_translation_manager/locale_checker/base_checker.rb +13 -0
  10. data/lib/rails_translation_manager/locale_checker/incompatible_plurals.rb +79 -0
  11. data/lib/rails_translation_manager/locale_checker/locale_checker_helper.rb +23 -0
  12. data/lib/rails_translation_manager/locale_checker/missing_english_locales.rb +31 -0
  13. data/lib/rails_translation_manager/locale_checker/missing_foreign_locales.rb +31 -0
  14. data/lib/rails_translation_manager/locale_checker/plural_forms.rb +16 -0
  15. data/lib/rails_translation_manager/locale_checker.rb +50 -0
  16. data/lib/rails_translation_manager/version.rb +1 -1
  17. data/lib/rails_translation_manager.rb +20 -0
  18. data/lib/tasks/translation.rake +13 -0
  19. data/lib/tasks/translation_helper.rb +11 -0
  20. data/rails_translation_manager.gemspec +4 -1
  21. data/spec/locales/cleaner/clean.yml +5 -0
  22. data/spec/locales/cleaner/with_whitespace.yml +5 -0
  23. data/spec/locales/in_sync/cy/browse.yml +12 -0
  24. data/spec/locales/in_sync/en/browse.yml +8 -0
  25. data/spec/locales/out_of_sync/cy.yml +3 -0
  26. data/spec/locales/out_of_sync/en.yml +6 -0
  27. data/spec/rails_translation_manager/cleaner_spec.rb +13 -0
  28. data/spec/rails_translation_manager/locale_checker/all_locales_spec.rb +24 -0
  29. data/spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb +94 -0
  30. data/spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb +61 -0
  31. data/spec/rails_translation_manager/locale_checker/missing_english_locales_spec.rb +72 -0
  32. data/spec/rails_translation_manager/locale_checker/missing_foreign_locales_spec.rb +80 -0
  33. data/spec/rails_translation_manager/locale_checker/plural_forms_spec.rb +27 -0
  34. data/spec/rails_translation_manager/locale_checker_spec.rb +68 -0
  35. data/spec/spec_helper.rb +19 -0
  36. data/spec/support/tasks.rb +7 -0
  37. data/spec/tasks/translation_spec.rb +38 -0
  38. data/tmp/.gitkeep +0 -0
  39. metadata +95 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9053fb88fb8e337bba059e6b16a13f16ffc058de0cc87b6be236c78d92a54dcd
4
- data.tar.gz: 626c3ea8e720db73bce5ca3618f58d4ce121b7b845dd66e282572c4f9830406a
3
+ metadata.gz: f2407e2bb0a7f4cf4628d9a9f35bcfb668b085c23bb92418f708c09f571c1b92
4
+ data.tar.gz: e0584a3f42216bdc0b9589ab6bb26e7246d6856e0b33f7599ac1e6eed0070af8
5
5
  SHA512:
6
- metadata.gz: 6d5334a90e4b52b7c4976846984dc83b9adb8ed2e3724eb181dde8f86118f72fc42375466285487f9eb3f1dafc78799662fe513cacdaad5902ededdbad54f2c1
7
- data.tar.gz: ae8c750799c3882ce9b11408968e8322ea37f0bffcb3ca76ef8d117de8ab8ce30e65661e4a5bb1e297db106ff29436c03bc169bf31b6187f9dec309da4bcab7d
6
+ metadata.gz: '0249a48778dade714f10f47d4185136da8a8a9d11313273d659817dba1968c01ed5756c42b87e0b1963d3f9a41c1316392317f7214137dca5af8e4962f311f4a'
7
+ data.tar.gz: 63553a493e8c36c8daa8fe0bdf388384c2bf80fca3e41fd038f4ed8f92078c4accfe18c9a9561c2dc9faa82a35e9cd4642c7b1894a21a1fbd2fb0302c18092a1
data/.gitignore CHANGED
@@ -6,7 +6,7 @@
6
6
  /doc/
7
7
  /pkg/
8
8
  /spec/reports/
9
- /tmp/
9
+ /tmp/*
10
10
  *.bundle
11
11
  *.so
12
12
  *.o
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.5
1
+ 2.6.6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 1.0.0
2
+
3
+ Adds logic to verify locale files are in sync with each other and have the
4
+ correct plural forms. Also introduces RSpec into the Gem which will replace
5
+ Minitest in the coming iterations.
6
+
7
+ Integrates RTM with I18n-tasks. Adds wrappers around `add-missing` & `normalize` tasks, adds Cleaner class to remove the whitespace added by i18n-tasks, adds tests and byebug gem as a debugging tool.
8
+
1
9
  ## 0.1.0
2
10
 
3
11
  Don't change the $LOAD_PATH in translation.rake.
data/Rakefile CHANGED
@@ -1,11 +1,17 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ # frozen_string_literal: true
3
2
 
4
- Rake::TestTask.new("test") do |t|
5
- t.description = "Run tests"
6
- t.libs << "test"
7
- t.test_files = FileList["test/**/*_test.rb"]
3
+ require 'rspec/core/rake_task'
4
+ require 'bundler/gem_tasks'
5
+ require 'rake/testtask'
6
+ require 'rails_i18n'
7
+
8
+ Rake::TestTask.new('test') do |t|
9
+ t.description = 'Run tests'
10
+ t.libs << 'test'
11
+ t.test_files = FileList['test/**/*_test.rb']
8
12
  t.verbose = true
9
13
  end
10
14
 
11
- task :default => :test
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ task default: %i[spec test]
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ {
4
+ # Dari - this isn't an iso code. Probably should be 'prs' as per ISO 639-3.
5
+ dr: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
6
+ # Latin America and Caribbean Spanish
7
+ "es-419": { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
8
+ # Scottish Gaelic
9
+ gd: { i18n: { plural: { keys: %i[one two few other],
10
+ rule: lambda do |n|
11
+ if [1, 11].include?(n)
12
+ :one
13
+ elsif [2, 12].include?(n)
14
+ :two
15
+ elsif [3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15, 16, 17, 18, 19].include?(n)
16
+ :few
17
+ else
18
+ :other
19
+ end
20
+ end } } },
21
+ # Gujarati
22
+ gu: { i18n: { plural: { keys: %i[one other], rule: ->(n) { [0, 1].include?(n) ? :one : :other } } } },
23
+ # Armenian
24
+ hy: { i18n: { plural: { keys: %i[one other], rule: ->(n) { [0, 1].include?(n) ? :one : :other } } } },
25
+ # Kazakh
26
+ kk: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
27
+ # Pashto
28
+ ps: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
29
+ # Punjabi Shahmukhi
30
+ "pa-pk": { i18n: { plural: { keys: %i[one other], rule: ->(n) { [0, 1].include?(n) ? :one : :other } } } },
31
+ # Sinhalese
32
+ si: { i18n: { plural: { keys: %i[one other], rule: ->(n) { [0, 1].include?(n) ? :one : :other } } } },
33
+ # Somali
34
+ so: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
35
+ # Albanian
36
+ sq: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
37
+ # Norwegian
38
+ no: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
39
+ # Tamil
40
+ ta: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
41
+ # Turkmen
42
+ tk: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
43
+ # Uzbek
44
+ uz: { i18n: { plural: { keys: %i[one other], rule: ->(n) { n == 1 ? :one : :other } } } },
45
+ # Chinese Hong Kong
46
+ 'zh-hk' => { i18n: { plural: { keys: %i[other], rule: -> { :other } } } },
47
+ # Chinese Taiwan
48
+ 'zh-tw' => { i18n: { plural: { keys: %i[other], rule: -> { :other } } } }
49
+ }
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsTranslationManager
4
+ class Cleaner
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ end
9
+
10
+ def clean
11
+ `find #{@path} -type f -exec perl -p -i -e \"s/[ \t]$//g\" {} \\;`
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AllLocales
4
+ attr_reader :locale_path
5
+
6
+ def initialize(locale_path)
7
+ @locale_path = locale_path
8
+ end
9
+
10
+ def generate
11
+ paths = locale_file_paths.compact
12
+
13
+ raise NoLocaleFilesFound, "No locale files found for the supplied path" if paths.blank?
14
+
15
+ paths.flat_map do |locale_group|
16
+ {
17
+ locale: locale_group[:locale],
18
+ keys: all_keys_for_locale(locale_group)
19
+ }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def locale_file_paths
26
+ I18n.available_locales.map do |locale|
27
+ grouped_paths = Dir[locale_path].select do |path|
28
+ locale = locale.downcase
29
+
30
+ path =~ %r{/#{locale}/} || path =~ %r{/#{locale}\.yml$}
31
+ end
32
+
33
+ { locale: locale.downcase, paths: grouped_paths } if grouped_paths.present?
34
+ end
35
+ end
36
+
37
+ def all_keys_for_locale(locale_group)
38
+ locale_group[:paths].flat_map do |path|
39
+ keys_from_file(file_path: path)
40
+ end
41
+ end
42
+
43
+ def keys_from_file(locale_hash: nil, key_chain: nil, locale_keys: [], file_path: nil)
44
+ locale_hash ||= YAML.load_file(file_path)
45
+ keys = locale_hash.keys
46
+ keys.each do |key|
47
+ if locale_hash.fetch(key).is_a?(Hash)
48
+ keys_from_file(locale_hash: locale_hash.fetch(key), key_chain: "#{key_chain}.#{key}", locale_keys: locale_keys)
49
+ else
50
+ keys.each do |final_key|
51
+ locale_keys << "#{key_chain}.#{final_key}"
52
+ end
53
+ end
54
+ end
55
+
56
+ # remove locale prefix from keys, e.g: ".en.browse.page" -> "browse.page"
57
+ locale_keys.uniq.map { |key| key.split(".")[2..].join(".") }
58
+ end
59
+
60
+ class NoLocaleFilesFound < StandardError; end
61
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BaseChecker
4
+ attr_reader :all_locales
5
+
6
+ def initialize(all_locales)
7
+ @all_locales = all_locales
8
+ end
9
+
10
+ def report
11
+ raise "You must define a `report` method in the child class!"
12
+ end
13
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IncompatiblePlurals < BaseChecker
4
+ include LocaleCheckerHelper
5
+
6
+ def report
7
+ format_plural_errors if plural_errors.present?
8
+ end
9
+
10
+ private
11
+
12
+ def format_plural_errors
13
+ <<~OUTPUT.chomp
14
+ \e[31m[ERROR]\e[0m Incompatible plural forms, for:
15
+
16
+ #{plural_errors.join("\n\n")}
17
+
18
+ \e[1mIf the keys reported above are not plurals, rename them avoiding plural keywords: #{PLURAL_KEYS}\e[22m
19
+ OUTPUT
20
+ end
21
+
22
+ def plural_errors
23
+ @plural_errors ||= grouped_plural_keys_for_locale.flat_map do |plurals|
24
+ plural_form = all_plural_forms[plurals[:locale]]
25
+
26
+ if plural_form.blank?
27
+ "- \e[31m[ERROR]\e[0m Please add plural form for '#{plurals[:locale]}' <link to future documentation>"
28
+ else
29
+ incompatible_plurals(plurals, plural_form)
30
+ end
31
+ end
32
+ end
33
+
34
+ def incompatible_plurals(plurals, plural_form)
35
+ error_messages = plurals[:groups].map do |plural_group|
36
+ actual_plural_form = plural_group[:keys].sort
37
+
38
+ next if actual_plural_form == plural_form.sort
39
+
40
+ "- '#{plurals[:locale]}', with parent '#{plural_group[:parent]}'. Expected: #{plural_form}, actual: #{actual_plural_form}"
41
+ end
42
+
43
+ error_messages.compact
44
+ end
45
+
46
+ def grouped_plural_keys_for_locale
47
+ all_locales.map do |locale|
48
+ {
49
+ locale: locale[:locale],
50
+ groups: grouped_plural_keys(only_plurals(locale[:keys]))
51
+ }
52
+ end
53
+ end
54
+
55
+ def grouped_plural_keys(keys)
56
+ # %w[parent.key parent.other_key] -> [{ parent: "parent", keys: %w[key other_key] }]
57
+ group_by_parent_keys(keys).map do |plural_key_group|
58
+ {
59
+ parent: plural_key_group.first,
60
+ keys: plural_key_group.last.map { |key_chain| key_chain.split(".").last.to_sym }
61
+ }
62
+ end
63
+ end
64
+
65
+ def group_by_parent_keys(keys)
66
+ # %w[parent.key parent.other_key] -> { "parent" => %w[parent.key parent.other_key] }
67
+ keys.group_by do |key|
68
+ key.split('.')[0..-2].join('.')
69
+ end
70
+ end
71
+
72
+ def only_plurals(keys)
73
+ keys.select { |key| key_is_plural?(key) }
74
+ end
75
+
76
+ def all_plural_forms
77
+ @all_plural_forms ||= PluralForms.all
78
+ end
79
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LocaleCheckerHelper
4
+ PLURAL_KEYS = %w[zero one two few many other].freeze
5
+
6
+ def exclude_plurals(keys)
7
+ keys.reject { |key| key_is_plural?(key) }
8
+ end
9
+
10
+ def group_keys(locales)
11
+ locales.keys.group_by do |key|
12
+ locales[key]
13
+ end
14
+ end
15
+
16
+ def english_keys_excluding_plurals(all_locales)
17
+ exclude_plurals(all_locales.find { |locale| locale[:locale] == :en }[:keys])
18
+ end
19
+
20
+ def key_is_plural?(key)
21
+ PLURAL_KEYS.include?(key.split(".").last)
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MissingEnglishLocales < BaseChecker
4
+ include LocaleCheckerHelper
5
+
6
+ def report
7
+ grouped_missing_keys = group_keys(missing_english_locales)
8
+
9
+ format_missing_english_locales(grouped_missing_keys) if grouped_missing_keys.present?
10
+ end
11
+
12
+ private
13
+
14
+ def format_missing_english_locales(keys)
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]}"
17
+ end
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")}"
20
+ end
21
+
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)
25
+
26
+ next if missing_keys.blank?
27
+
28
+ hsh[locale[:locale]] = missing_keys
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MissingForeignLocales < BaseChecker
4
+ include LocaleCheckerHelper
5
+
6
+ def report
7
+ grouped_missing_keys = group_keys(missing_foreign_locales)
8
+
9
+ format_missing_foreign_locales(grouped_missing_keys) if grouped_missing_keys.present?
10
+ end
11
+
12
+ private
13
+
14
+ def format_missing_foreign_locales(keys)
15
+ formatted_keys = keys.to_a.map do |group|
16
+ "\e[1mMissing foreign keys:\e[22m #{group[0]}\n\e[1mAbsent from:\e[22m #{group[1]}"
17
+ end
18
+
19
+ "\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\n\n#{formatted_keys.join("\n\n")}"
20
+ end
21
+
22
+ def missing_foreign_locales
23
+ all_locales.each_with_object({}) do |locale, hsh|
24
+ missing_keys = english_keys_excluding_plurals(all_locales) - exclude_plurals(locale[:keys])
25
+
26
+ next if missing_keys.blank?
27
+
28
+ hsh[locale[:locale]] = missing_keys
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PluralForms
4
+ def self.all
5
+ I18n.available_locales.each_with_object({}) do |locale, hsh|
6
+ begin
7
+ # fetches plural form (rule) from rails-i18n for locale
8
+ plural_form = I18n.with_locale(locale) { I18n.t!("i18n.plural.keys") }.sort
9
+ rescue I18n::MissingTranslationData
10
+ plural_form = nil
11
+ end
12
+
13
+ hsh[locale.downcase] = plural_form
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsTranslationManager
4
+ class LocaleChecker
5
+ attr_reader :locale_path
6
+
7
+ CHECKER_CLASSES = [MissingEnglishLocales,
8
+ MissingForeignLocales,
9
+ IncompatiblePlurals].freeze
10
+
11
+ def initialize(locale_path)
12
+ @locale_path = locale_path
13
+ end
14
+
15
+ def validate_locales
16
+ output_result
17
+ rescue AllLocales::NoLocaleFilesFound => e
18
+ puts e
19
+ false
20
+ end
21
+
22
+ private
23
+
24
+ def output_result
25
+ errors = checker_errors.compact
26
+
27
+ if errors.blank?
28
+ puts "Locale files are in sync, nice job!"
29
+ true
30
+ else
31
+ errors.each do |error_message|
32
+ puts "\n"
33
+ puts error_message
34
+ puts "\n"
35
+ end
36
+ false
37
+ end
38
+ end
39
+
40
+ def checker_errors
41
+ CHECKER_CLASSES.flat_map do |checker|
42
+ checker.new(all_locales).report
43
+ end
44
+ end
45
+
46
+ def all_locales
47
+ @all_locales ||= AllLocales.new(locale_path).generate
48
+ end
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsTranslationManager
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,9 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails_translation_manager/version"
2
4
  require "rails_translation_manager/railtie" if defined?(Rails)
3
5
  require "rails-i18n"
4
6
 
7
+ require "rails_translation_manager/locale_checker/base_checker"
8
+ require "rails_translation_manager/locale_checker/locale_checker_helper"
9
+ require "rails_translation_manager/locale_checker/missing_foreign_locales"
10
+ require "rails_translation_manager/locale_checker/missing_english_locales"
11
+ require "rails_translation_manager/locale_checker/plural_forms"
12
+ require "rails_translation_manager/locale_checker/incompatible_plurals"
13
+ require "rails_translation_manager/locale_checker/all_locales"
14
+ require "rails_translation_manager/locale_checker"
15
+ require "rails_translation_manager/cleaner"
16
+
5
17
  module RailsTranslationManager
6
18
  autoload :Exporter, "rails_translation_manager/exporter"
7
19
  autoload :Importer, "rails_translation_manager/importer"
8
20
  autoload :Stealer, "rails_translation_manager/stealer"
21
+
22
+ rails_i18n_path = Gem::Specification.find_by_name("rails-i18n").gem_dir
23
+ rails_translation_manager = Gem::Specification.find_by_name("rails_translation_manager").gem_dir
24
+
25
+ I18n.load_path.concat(
26
+ Dir["#{rails_i18n_path}/rails/pluralization/*.rb"],
27
+ ["#{rails_translation_manager}/config/locales/plurals.rb"]
28
+ )
9
29
  end
@@ -1,4 +1,6 @@
1
1
  require "rails_translation_manager"
2
+ require "i18n/tasks/cli"
3
+ require_relative "../tasks/translation_helper"
2
4
 
3
5
  namespace :translation do
4
6
 
@@ -83,4 +85,15 @@ namespace :translation do
83
85
  end
84
86
  end
85
87
 
88
+ desc "Add missing translations"
89
+ task(:add_missing, [:locale] => [:environment]) do |t, args|
90
+ I18n::Tasks::CLI.start(TranslationHelper.new(["add-missing", "--nil-value"], args[:locale]).with_optional_locale)
91
+ RailsTranslationManager::Cleaner.new(Rails.root.join("config", "locales")).clean
92
+ end
93
+
94
+ desc "Normalize translations"
95
+ task(:normalize, [:locale] => [:environment]) do |t, args|
96
+ I18n::Tasks::CLI.start(TranslationHelper.new(["normalize"], args[:locale]).with_optional_locale)
97
+ RailsTranslationManager::Cleaner.new(Rails.root.join("config", "locales")).clean
98
+ end
86
99
  end
@@ -0,0 +1,11 @@
1
+ class TranslationHelper
2
+
3
+ def initialize(task_options, locale)
4
+ @task_options = task_options
5
+ @locale = locale
6
+ end
7
+
8
+ def with_optional_locale
9
+ @locale ? @task_options << ["-l", @locale] : @task_options
10
+ end
11
+ end
@@ -20,8 +20,11 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency "rails-i18n"
22
22
  spec.add_dependency "activesupport"
23
+ spec.add_dependency "i18n-tasks"
23
24
 
24
- spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "bundler"
25
26
  spec.add_development_dependency "rake", "~> 10.0"
26
27
  spec.add_development_dependency "minitest"
28
+ spec.add_development_dependency "rspec"
29
+ spec.add_development_dependency "byebug"
27
30
  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,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,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
@@ -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,38 @@
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:add_missing', type: :task do
12
+ let(:task) { Rake::Task["translation:add_missing"] }
13
+
14
+ it 'is executed' do
15
+ expect { task.execute }.to output.to_stdout
16
+ end
17
+
18
+ it 'triggers i18n task and allows to receive the right arguments' do
19
+ allow(I18n::Tasks::CLI).to receive(:start)
20
+ task.execute
21
+ expect(I18n::Tasks::CLI).to have_received(:start).with(["add-missing", "--nil-value"])
22
+ end
23
+ end
24
+
25
+ describe 'translation:normalize', type: :task do
26
+ let(:task) { Rake::Task["translation:normalize"] }
27
+
28
+ it 'is executed' do
29
+ expect { task.execute }.to_not output.to_stdout
30
+ end
31
+
32
+ it 'triggers i18n task and allows to receive the right arguments' do
33
+ allow(I18n::Tasks::CLI).to receive(:start)
34
+ task.execute
35
+ expect(I18n::Tasks::CLI).to have_received(:start).with(["normalize"])
36
+ end
37
+ end
38
+ end
data/tmp/.gitkeep ADDED
File without changes
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: 0.1.0
4
+ version: 1.0.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: 2018-08-24 00:00:00.000000000 Z
11
+ date: 2021-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails-i18n
@@ -38,20 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
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'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - "~>"
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: '1.7'
61
+ version: '0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - "~>"
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: '1.7'
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,34 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
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'
83
125
  description: ''
84
126
  email:
85
127
  - edd.sowden@digital.cabinet-office.gov.uk
@@ -97,22 +139,51 @@ files:
97
139
  - LICENSE.txt
98
140
  - README.md
99
141
  - Rakefile
142
+ - config/locales/plurals.rb
100
143
  - lib/rails_translation_manager.rb
144
+ - lib/rails_translation_manager/cleaner.rb
101
145
  - lib/rails_translation_manager/exporter.rb
102
146
  - lib/rails_translation_manager/importer.rb
147
+ - lib/rails_translation_manager/locale_checker.rb
148
+ - lib/rails_translation_manager/locale_checker/all_locales.rb
149
+ - lib/rails_translation_manager/locale_checker/base_checker.rb
150
+ - lib/rails_translation_manager/locale_checker/incompatible_plurals.rb
151
+ - lib/rails_translation_manager/locale_checker/locale_checker_helper.rb
152
+ - lib/rails_translation_manager/locale_checker/missing_english_locales.rb
153
+ - lib/rails_translation_manager/locale_checker/missing_foreign_locales.rb
154
+ - lib/rails_translation_manager/locale_checker/plural_forms.rb
103
155
  - lib/rails_translation_manager/railtie.rb
104
156
  - lib/rails_translation_manager/stealer.rb
105
157
  - lib/rails_translation_manager/validator.rb
106
158
  - lib/rails_translation_manager/version.rb
107
159
  - lib/rails_translation_manager/yaml_writer.rb
108
160
  - lib/tasks/translation.rake
161
+ - lib/tasks/translation_helper.rb
109
162
  - rails_translation_manager.gemspec
163
+ - spec/locales/cleaner/clean.yml
164
+ - spec/locales/cleaner/with_whitespace.yml
165
+ - spec/locales/in_sync/cy/browse.yml
166
+ - spec/locales/in_sync/en/browse.yml
167
+ - spec/locales/out_of_sync/cy.yml
168
+ - spec/locales/out_of_sync/en.yml
169
+ - spec/rails_translation_manager/cleaner_spec.rb
170
+ - spec/rails_translation_manager/locale_checker/all_locales_spec.rb
171
+ - spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb
172
+ - spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb
173
+ - spec/rails_translation_manager/locale_checker/missing_english_locales_spec.rb
174
+ - spec/rails_translation_manager/locale_checker/missing_foreign_locales_spec.rb
175
+ - spec/rails_translation_manager/locale_checker/plural_forms_spec.rb
176
+ - spec/rails_translation_manager/locale_checker_spec.rb
177
+ - spec/spec_helper.rb
178
+ - spec/support/tasks.rb
179
+ - spec/tasks/translation_spec.rb
110
180
  - test/rails_translation_manager/exporter_test.rb
111
181
  - test/rails_translation_manager/importer_test.rb
112
182
  - test/rails_translation_manager/stealer_test.rb
113
183
  - test/rails_translation_manager/validator_test.rb
114
184
  - test/rails_translation_manager/yaml_writer_test.rb
115
185
  - test/test_helper.rb
186
+ - tmp/.gitkeep
116
187
  homepage: ''
117
188
  licenses:
118
189
  - MIT
@@ -132,12 +203,28 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
203
  - !ruby/object:Gem::Version
133
204
  version: '0'
134
205
  requirements: []
135
- rubyforge_project:
136
- rubygems_version: 2.7.6
206
+ rubygems_version: 3.0.3
137
207
  signing_key:
138
208
  specification_version: 4
139
209
  summary: Tasks to manage translation files
140
210
  test_files:
211
+ - spec/locales/cleaner/clean.yml
212
+ - spec/locales/cleaner/with_whitespace.yml
213
+ - spec/locales/in_sync/cy/browse.yml
214
+ - spec/locales/in_sync/en/browse.yml
215
+ - spec/locales/out_of_sync/cy.yml
216
+ - spec/locales/out_of_sync/en.yml
217
+ - spec/rails_translation_manager/cleaner_spec.rb
218
+ - spec/rails_translation_manager/locale_checker/all_locales_spec.rb
219
+ - spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb
220
+ - spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb
221
+ - spec/rails_translation_manager/locale_checker/missing_english_locales_spec.rb
222
+ - spec/rails_translation_manager/locale_checker/missing_foreign_locales_spec.rb
223
+ - spec/rails_translation_manager/locale_checker/plural_forms_spec.rb
224
+ - spec/rails_translation_manager/locale_checker_spec.rb
225
+ - spec/spec_helper.rb
226
+ - spec/support/tasks.rb
227
+ - spec/tasks/translation_spec.rb
141
228
  - test/rails_translation_manager/exporter_test.rb
142
229
  - test/rails_translation_manager/importer_test.rb
143
230
  - test/rails_translation_manager/stealer_test.rb