localer 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f209f3d523ec5d1b9359ab7020d498a116e3aa2c
4
- data.tar.gz: 7c407ebd3dd1a4c0b57648180385c2f624e6126c
2
+ SHA256:
3
+ metadata.gz: 165de59d1c94a764cc7dbdec0c2327c2f96c75ef608dd86b4ea627ba2ff071cc
4
+ data.tar.gz: ba9fc37a955d84b70754bc03e894eb01093f9ca231bd0fce2622bf1fc4b19ead
5
5
  SHA512:
6
- metadata.gz: eb602e9bcbc1972e42adc455ff2b8588d9c9db0d2a0c5925ee331b2dff54603e17526199113ad00ffadaa3fb43b943a9ffcd6cdc87f60446658e5c9607315f6a
7
- data.tar.gz: 42c257fa60196ef6e883d610eba1da8d1bfe63867fec1e73a162b73cccab558e01c3edd176300b4183533de9c48fc8baab0906a2f3e3b87487a6c19b461785ab
6
+ metadata.gz: 382f19b3867430029b46eeb76f39d9c6366160f5a325170778c825be4160f067ee276afc0047577655c20415db03c11cfcd99b3342668a7c33a64e8f6039aa1d
7
+ data.tar.gz: eea447818e4ed693cf08e27b656d4a084a6b266dca554af277817532656fc1b304b830ee4dfb4aca97e450fed82f6c27430c89dba49d46b6ac8c3c2eb0dd5762
data/.gitignore CHANGED
@@ -8,3 +8,8 @@
8
8
  /tmp/
9
9
 
10
10
  Gemfile.lock
11
+ erl_crash.dump
12
+ .ruby-version
13
+ .vscode
14
+ spec/dummy_app/log/
15
+ vendor/
@@ -6,9 +6,13 @@ AllCops:
6
6
  - 'spec/**/*.rb'
7
7
  Exclude:
8
8
  - 'bin/**/*'
9
+ - 'lib/localer/ext/*.rb'
10
+ - 'Appraisals'
9
11
  - 'Gemfile'
10
12
  - 'Rakefile'
11
13
  - '*.gemspec'
14
+ - 'gemfiles/vendor/**/*'
15
+ - 'vendor/**/*'
12
16
  DisplayCopNames: true
13
17
  StyleGuideCopsOnly: false
14
18
  TargetRubyVersion: 2.4
@@ -1,5 +1,21 @@
1
1
  sudo: false
2
+ dist: trusty
2
3
  language: ruby
4
+
5
+ before_install:
6
+ - gem install bundler
3
7
  rvm:
8
+ - 2.3.3
4
9
  - 2.4.3
5
- before_install: gem install bundler -v 1.16.0
10
+ - 2.5.0
11
+ gemfile:
12
+ - gemfiles/rails50.gemfile
13
+ - gemfiles/rails51.gemfile
14
+ matrix:
15
+ include:
16
+ - rvm: 2.5.0
17
+ env: RUBOCOP=true
18
+ - rvm: 2.3.3
19
+ gemfile: gemfiles/rails40.gemfile
20
+ - rvm: 2.3.3
21
+ gemfile: gemfiles/rails41.gemfile
@@ -0,0 +1,15 @@
1
+ appraise 'rails41' do
2
+ gem 'rails', '~> 4.1.0'
3
+ end
4
+
5
+ appraise 'rails42' do
6
+ gem 'rails', '~> 4.2.0'
7
+ end
8
+
9
+ appraise 'rails50' do
10
+ gem 'rails', '5.0.0'
11
+ end
12
+
13
+ appraise 'rails51' do
14
+ gem 'rails', '5.1.0'
15
+ end
data/README.md CHANGED
@@ -1,11 +1,21 @@
1
- # Localer
1
+ [![Build Status](https://travis-ci.org/aderyabin/localer.svg?branch=master)](https://travis-ci.org/aderyabin/localer) [![Gem Version](https://badge.fury.io/rb/localer.svg)](https://rubygems.org/gems/localer)
2
2
 
3
- Automatic detecting missing I18n translations tool.
3
+ # Localer
4
+ Localer is an automatic detecting missing I18n translations tool.
4
5
 
6
+ The aim is to preserve the integrity of translations.
7
+ Localer parses and merges keys for all locales in an application. At the next step, it searches missed translations for calculated keys.
5
8
 
6
- <a href="https://evilmartians.com/">
7
- <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
9
+ <p align="center">
10
+ <img align="center" height="500" src="https://gist.githubusercontent.com/aderyabin/cb0512cbcd6cb4c79a4d84a4831109a5/raw/localer2.png">
11
+ </p>
8
12
 
13
+ <p align="center">
14
+ <a href="https://evilmartians.com/?utm_source=localer">
15
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg"
16
+ alt="Sponsored by Evil Martians" width="236" height="54">
17
+ </a>
18
+ </p>
9
19
 
10
20
  ## Installation
11
21
 
@@ -23,11 +33,61 @@ Or install it yourself as:
23
33
 
24
34
  $ gem install localer
25
35
 
36
+ ## Usage
37
+
38
+ At the root directory of a Rails app, run:
39
+ ```
40
+ localer check .
41
+ ```
42
+ or for specific Rails path:
43
+ ```
44
+ localer check /path/to/rails/application
45
+ ```
46
+ ## Support
47
+
48
+ Localer supports Ruby 2.3+, Rails. 4.1+
49
+
50
+ ## Configuration
51
+
52
+ The behavior of Localer can be controlled via the `.localer.yml` configuration file. It makes it possible to disable locales and keys. The file can be placed in your project directory.
53
+
54
+ #### Disable specific locale
55
+
56
+ By default, Localer enables all locales, but you can disable it:
57
+
58
+ ```yml
59
+ Locale:
60
+ EN:
61
+ Enabled: false
62
+ ```
63
+
64
+ #### Exclude keys globally
65
+ By default, Localer enables all keys, but you can disable keys started with specified string or by regex:
66
+
67
+ ```yml
68
+ Exclude:
69
+ - /population\z/
70
+ - .countries.france
71
+ ```
72
+
73
+ #### Exclude keys for specific locale
74
+ ```yml
75
+ Locale:
76
+ EN:
77
+ Exclude:
78
+ - /population\z/
79
+ - .countries.france
80
+ ```
81
+
26
82
  ## Development
27
83
 
28
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
84
+ After checking out the repo, run `bundle exec appraisal install` to install dependencies for each appraisal. Then, run `bundle exec appraisal rake` to run the tests.
85
+
86
+ ## Built With
29
87
 
30
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
88
+ * [Thor](https://github.com/erikhuda/thor) - Used for building command-line interfaces.
89
+ * [Appraisal](https://github.com/thoughtbot/appraisal) - Used for testing against different versions of dependencies
90
+ * [Cucumber](https://github.com/cucumber/cucumber) + [Aruba](https://github.com/cucumber/aruba) - Used for testing command-line commands
31
91
 
32
92
  ## Contributing
33
93
 
data/Rakefile CHANGED
@@ -1,8 +1,17 @@
1
1
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
2
  require "rubocop/rake_task"
3
+ require 'cucumber/rake/task'
4
4
 
5
- RSpec::Core::RakeTask.new(:spec)
6
5
  RuboCop::RakeTask.new
7
6
 
8
- task default: [:rubocop, :spec]
7
+ Cucumber::Rake::Task.new(:features) do |t|
8
+ t.cucumber_opts = "features --format pretty"
9
+ end
10
+
11
+ task :default do
12
+ if ENV["RUBOCOP"]
13
+ Rake::Task["rubocop"].invoke
14
+ else
15
+ Rake::Task["features"].invoke
16
+ end
17
+ end
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-leap-day
@@ -11,12 +11,16 @@ module Localer
11
11
  say Localer::VERSION
12
12
  end
13
13
 
14
- desc "check", "Check I18n complete"
15
- def check
16
- if Localer.complete?
14
+ desc "check [/path/to/rails/application]", "Check missing translations"
15
+ def check(app_path = Localer::Config::APP_PATH)
16
+ Localer.configure(options.dup.merge(app_path: app_path))
17
+
18
+ connect_to_rails
19
+
20
+ if Localer.data.complete?
17
21
  say "\xE2\x9C\x94 No missing translations found.", :green
18
22
  else
19
- missing_translations = Localer.missing_translations
23
+ missing_translations = Localer.data.missing_translations
20
24
  say "\xE2\x9C\x96 Missing translations found (#{missing_translations.count}):", :red
21
25
  missing_translations.each do |tr|
22
26
  say "* #{tr}"
@@ -26,10 +30,14 @@ module Localer
26
30
  end
27
31
  end
28
32
 
29
- desc "console", "Run console"
30
- def console
31
- ARGV.clear
32
- IRB.start
33
+ default_task :check
34
+
35
+ private
36
+
37
+ def connect_to_rails
38
+ return if Localer::Rails.connect!
39
+ say "No Rails application found"
40
+ exit 1
33
41
  end
34
42
  end
35
43
  end
@@ -1,51 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry-initializer"
3
4
  require_relative "localer/version"
4
- require_relative "localer/service"
5
- require_relative "localer/parser"
6
- require_relative "localer/checker"
5
+ require_relative "localer/rails"
7
6
  require_relative "localer/config"
8
- require_relative "localer/missing_translations"
7
+ require_relative "localer/data"
9
8
 
10
9
  module Localer # :nodoc:
11
- class << self
12
- def parcer
13
- @parcer ||= begin
14
- require File.expand_path("config/environment", Dir.pwd)
15
- I18n.backend.send(:init_translations)
16
- Parser.call(I18n.backend.send(:translations))
17
- end
18
- end
19
-
20
- # returns array of available locales
21
- def locales
22
- parcer.locales
23
- end
10
+ using Localer::Ext::Hash
11
+ # using Localer::Ext::String
24
12
 
13
+ class << self
25
14
  def data
26
- parcer.data
15
+ @data ||= load_data
27
16
  end
28
17
 
29
- # checks missing translations
30
- def complete?
31
- Checker.call(locales, data)
32
- end
33
-
34
- # returns array of missing translations
35
- def missing_translations
36
- MissingTranslations.call(locales, data)
18
+ def config
19
+ @config ||= configure
37
20
  end
38
21
 
39
- def config
40
- @config ||= Config.call
22
+ def configure(options = {})
23
+ @config = Config.load(options)
41
24
  end
42
25
 
43
- def each_data
44
- data.each do |key, value|
45
- locales.each do |locale|
46
- yield locale, key, value[locale]
47
- end
48
- end
26
+ def load_data(source = Localer::Rails.translations)
27
+ @data = Data.new(source)
49
28
  end
50
29
  end
51
30
  end
@@ -1,22 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
+ require_relative '../localer/ext/hash'
5
+ require_relative 'config/locale'
6
+
7
+ module Localer # :nodoc:
8
+ using Localer::Ext::Hash
4
9
 
5
- module Localer
6
10
  # Loads and parse Localer config file `.localer.yml`
7
- class Config < Service
11
+ class Config
12
+ extend Dry::Initializer
13
+
14
+ APP_PATH = Dir.pwd
15
+ CONFIG_FILENAME = ".localer.yml"
16
+
8
17
  option :exclude, default: -> { [] }
9
- def call
10
- return unless File.exist?(filename)
11
- yaml = YAML.load_file(filename)
12
- @exclude = yaml["Exclude"] if yaml&.key?("Exclude")
13
- self
14
- end
18
+ option :locale, proc { |hash| parse_locales(hash) }, default: -> { Hash.new(Locale.new) }
19
+ option :app_path, default: -> { APP_PATH }
20
+
21
+ class << self
22
+ def load(options = {})
23
+ opts = options.deep_symbolize_keys
24
+ app_path = opts.fetch(:app_path, APP_PATH)
25
+ file_options = file_config(CONFIG_FILENAME, app_path)
26
+ new(file_options.deep_merge(opts).deep_symbolize_keys)
27
+ end
15
28
 
16
- private
29
+ def file_config(filename, path)
30
+ filename = File.expand_path(filename, path)
31
+ return {} unless File.exist?(filename)
32
+ YAML
33
+ .load_file(filename)
34
+ .deep_downcase_keys
35
+ .deep_symbolize_keys
36
+ end
17
37
 
18
- def filename
19
- File.expand_path(".localer.yml", Dir.pwd)
38
+ def parse_locales(hash)
39
+ hash.each_with_object(Hash.new(Locale.new)) do |(l, v), h|
40
+ h[l] = Locale.new(v)
41
+ end
42
+ end
20
43
  end
21
44
  end
22
45
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Localer
4
+ class Config
5
+ # Provide config for locale
6
+ class Locale
7
+ extend Dry::Initializer
8
+ option :exclude, default: -> { [] }
9
+ option :enabled, default: -> { true }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "data/service"
4
+ require_relative "data/checker"
5
+ require_relative "data/processor"
6
+ require_relative "data/missing_translations"
7
+
8
+ module Localer
9
+ # Stores translations and provides
10
+ # check methods
11
+ class Data
12
+ extend Dry::Initializer
13
+ param :source, default: -> { {} }
14
+ param :config, default: -> { Localer.config }
15
+
16
+ attr_reader :translations, :locales
17
+
18
+ def initialize(*args)
19
+ super
20
+ @locales, @translations = Processor.call(source, config)
21
+ end
22
+
23
+ def complete?
24
+ Checker.call(self)
25
+ end
26
+
27
+ def missing_translations
28
+ MissingTranslations.call(self)
29
+ end
30
+
31
+ def each
32
+ @translations.each do |key, value|
33
+ @locales.each do |locale|
34
+ yield locale, key, value[locale]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Localer
4
+ class Data
5
+ # Check missing translations
6
+ # Returns true if no missing translations found, otherwise false
7
+ class Checker < Service
8
+ param :data
9
+
10
+ def call
11
+ data.each do |_locale, _key, value|
12
+ return false if value.nil?
13
+ end
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Localer
4
+ class Data
5
+ # A service that returns array of missing translations
6
+ class MissingTranslations < Service
7
+ param :data
8
+
9
+ def call
10
+ missing = []
11
+ data.each do |locale, key, value|
12
+ missing.push("#{locale}#{key}") if value.nil?
13
+ end
14
+ missing
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../ext/string'
4
+ module Localer # :nodoc:
5
+ using Localer::Ext::String
6
+
7
+ class Data
8
+ # Parse translations into hash:
9
+ # key: translation key
10
+ # value: hash of locale values
11
+ class Processor < Service
12
+ param :translations
13
+ param :config, default: -> { Localer.config }
14
+
15
+ attr_reader :data, :locales
16
+
17
+ def call
18
+ @data = Hash.new { |hsh, key| hsh[key] = {} }
19
+ @locales = []
20
+ translations.each do |(locale, translation)|
21
+ next unless config.locale[locale].enabled
22
+ @locales.push locale
23
+ prepare(locale, translation)
24
+ end
25
+ [@locales, @data]
26
+ end
27
+
28
+ private
29
+
30
+ def prepare(locale, translation, prefix = "")
31
+ if translation.is_a?(Hash)
32
+ translation.each do |(key, value)|
33
+ full_key = prefix + ".#{key}"
34
+ next if exclude?(full_key, locale)
35
+ prepare(locale, value, full_key)
36
+ end
37
+ else
38
+ # @data[prefix] ||= {}
39
+ @data[prefix][locale] = translation
40
+ end
41
+ end
42
+
43
+ def exclude?(key, locale)
44
+ (config.exclude + config.locale[locale].exclude).any? do |pattern|
45
+ match?(key, pattern)
46
+ end
47
+ end
48
+
49
+ def match?(key, pattern)
50
+ if (regex = pattern.to_regexp)
51
+ key =~ regex
52
+ else
53
+ key.start_with?(pattern)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ module Localer
5
+ class Data
6
+ # Core service object
7
+ class Service
8
+ extend Dry::Initializer # use `param` and `option` for dependencies
9
+
10
+ class << self
11
+ # Instantiates and calls the service at once
12
+ def call(*args, &block)
13
+ new(*args).call(&block)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Localer
4
+ module Ext
5
+ # Extend Hash through refinements
6
+ module Hash
7
+ refine ::Hash do
8
+ # From ActiveSupport http://api.rubyonrails.org/classes/Hash.html#metho
9
+ def deep_merge!(other_hash)
10
+ other_hash.each_pair do |current_key, other_value|
11
+ this_value = self[current_key]
12
+
13
+ if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
14
+ this_value.deep_merge!(other_value)
15
+ this_value
16
+ else
17
+ self[current_key] = other_value
18
+ end
19
+ end
20
+
21
+ self
22
+ end
23
+
24
+ def deep_merge(other_hash, &block)
25
+ dup.deep_merge!(other_hash, &block)
26
+ end
27
+
28
+ def deep_symbolize_keys
29
+ deep_transform_keys do |key|
30
+ begin
31
+ key.to_sym
32
+ rescue StandardError
33
+ key
34
+ end
35
+ end
36
+ end
37
+
38
+ def deep_downcase_keys
39
+ deep_transform_keys do |key|
40
+ begin
41
+ key.downcase
42
+ rescue StandardError
43
+ key
44
+ end
45
+ end
46
+ end
47
+
48
+ def deep_transform_keys(&block)
49
+ _deep_transform_keys_in_object(self, &block)
50
+ end
51
+
52
+ def deep_transform_keys!(&block)
53
+ _deep_transform_keys_in_object!(self, &block)
54
+ end
55
+
56
+ private
57
+
58
+ def _deep_transform_keys_in_object!(object, &block)
59
+ case object
60
+ when ::Hash
61
+ object.keys.each do |key|
62
+ value = object.delete(key)
63
+ object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
64
+ end
65
+ object
66
+ when Array
67
+ object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
68
+ else
69
+ object
70
+ end
71
+ end
72
+
73
+ # support methods for deep transforming nested hashes and arrays
74
+ def _deep_transform_keys_in_object(object, &block)
75
+ case object
76
+ when ::Hash
77
+ object.each_with_object({}) do |(key, value), result|
78
+ result[yield(key)] = _deep_transform_keys_in_object(value, &block)
79
+ end
80
+ when Array
81
+ object.map { |e| _deep_transform_keys_in_object(e, &block) }
82
+ else
83
+ object
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Localer
4
+ module Ext
5
+ # Extend Hash through refinements
6
+ # taken from https://github.com/seamusabshere/to_regexp
7
+ module String
8
+ INLINE_OPTIONS = /[imxnesu]*/
9
+ REGEXP_DELIMITERS = {
10
+ '%r{' => '}',
11
+ '/' => '/'
12
+ }.freeze
13
+
14
+ refine ::String do
15
+ def literal?
16
+ REGEXP_DELIMITERS.none? { |s, e| start_with?(s) && self =~ /#{e}#{INLINE_OPTIONS}\z/ }
17
+ end
18
+
19
+ def to_regexp(options = {})
20
+ if args = as_regexp(options)
21
+ ::Regexp.new(*args)
22
+ end
23
+ end
24
+
25
+ def as_regexp(options = {})
26
+ raise ::ArgumentError, "[to_regexp] Options must be a Hash" unless options.is_a?(::Hash)
27
+ str = self
28
+
29
+ return if options[:detect] && (str == '')
30
+
31
+ if options[:literal] || (options[:detect] && str.literal?)
32
+ content = ::Regexp.escape str
33
+ elsif delim_set = REGEXP_DELIMITERS.detect { |k, _| str.start_with?(k) }
34
+ delim_start, delim_end = delim_set
35
+ /\A#{delim_start}(.*)#{delim_end}(#{INLINE_OPTIONS})\z/u =~ str
36
+ content = Regexp.last_match(1)
37
+ inline_options = Regexp.last_match(2)
38
+ return unless content.is_a?(::String)
39
+ content.gsub! '\\/', '/'
40
+ if inline_options
41
+ options[:ignore_case] = true if inline_options.include?('i')
42
+ options[:multiline] = true if inline_options.include?('m')
43
+ options[:extended] = true if inline_options.include?('x')
44
+ # 'n', 'N' = none, 'e', 'E' = EUC, 's', 'S' = SJIS, 'u', 'U' = UTF-8
45
+ options[:lang] = inline_options.scan(/[nesu]/i).join.downcase
46
+ end
47
+ else
48
+ return
49
+ end
50
+
51
+ ignore_case = options[:ignore_case] ? ::Regexp::IGNORECASE : 0
52
+ multiline = options[:multiline] ? ::Regexp::MULTILINE : 0
53
+ extended = options[:extended] ? ::Regexp::EXTENDED : 0
54
+ lang = options[:lang] || ''
55
+ lang = lang.delete 'u' if (::RUBY_VERSION > '1.9') && lang.include?('u')
56
+
57
+ if lang.empty?
58
+ [content, (ignore_case | multiline | extended)]
59
+ else
60
+ [content, (ignore_case | multiline | extended), lang]
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Localer
4
+ module Rails # :nodoc:
5
+ class << self
6
+ def connect!
7
+ require File.expand_path("config/environment", Localer.config.app_path)
8
+ true
9
+ rescue LoadError
10
+ false
11
+ end
12
+
13
+ def translations
14
+ return {} unless connect!
15
+ I18n.backend.send(:init_translations)
16
+ I18n.backend.send(:translations)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Localer
4
- VERSION = "0.0.2"
4
+ VERSION = "0.1.0"
5
5
  end
@@ -14,16 +14,19 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/aderyabin/localer"
15
15
  spec.license = "MIT"
16
16
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
- f.match(%r{^(test|spec|features)/})
17
+ f.match(%r{^(test|spec|features|gemfiles)/})
18
18
  end
19
19
  spec.bindir = "bin"
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
+ spec.add_development_dependency "appraisal"
23
24
  spec.add_development_dependency "bundler", "~> 1.16"
24
25
  spec.add_development_dependency "rake", "~> 10.0"
25
26
  spec.add_development_dependency "rspec", "~> 3.0"
26
27
  spec.add_development_dependency "rubocop", "~> 0.50"
28
+ spec.add_development_dependency "cucumber"
29
+ spec.add_development_dependency "aruba"
27
30
 
28
31
  spec.add_dependency "thor", "~> 0.20.0"
29
32
  spec.add_dependency "dry-initializer", "~> 2.3.0"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: localer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Deryabin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-24 00:00:00.000000000 Z
11
+ date: 2018-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: appraisal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,34 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0.50'
83
+ - !ruby/object:Gem::Dependency
84
+ name: cucumber
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: aruba
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'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: thor
71
113
  requirement: !ruby/object:Gem::Requirement
@@ -106,18 +148,25 @@ files:
106
148
  - ".rspec"
107
149
  - ".rubocop.yml"
108
150
  - ".travis.yml"
151
+ - Appraisals
109
152
  - CODE_OF_CONDUCT.md
110
153
  - Gemfile
111
154
  - LICENSE.txt
112
155
  - README.md
113
156
  - Rakefile
157
+ - _config.yml
114
158
  - bin/localer
115
159
  - lib/localer.rb
116
- - lib/localer/checker.rb
117
160
  - lib/localer/config.rb
118
- - lib/localer/missing_translations.rb
119
- - lib/localer/parser.rb
120
- - lib/localer/service.rb
161
+ - lib/localer/config/locale.rb
162
+ - lib/localer/data.rb
163
+ - lib/localer/data/checker.rb
164
+ - lib/localer/data/missing_translations.rb
165
+ - lib/localer/data/processor.rb
166
+ - lib/localer/data/service.rb
167
+ - lib/localer/ext/hash.rb
168
+ - lib/localer/ext/string.rb
169
+ - lib/localer/rails.rb
121
170
  - lib/localer/version.rb
122
171
  - localer.gemspec
123
172
  homepage: https://github.com/aderyabin/localer
@@ -140,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
189
  version: '0'
141
190
  requirements: []
142
191
  rubyforge_project:
143
- rubygems_version: 2.6.14
192
+ rubygems_version: 2.7.4
144
193
  signing_key:
145
194
  specification_version: 4
146
195
  summary: Automatic detecting missing I18n translations tool.
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Localer
4
- # Check missing translations
5
- # Returns true if no missing translations found, otherwise false
6
- class Checker < Service
7
- def call
8
- Localer.each_data do |_locale, _key, value|
9
- return false if value.nil?
10
- end
11
- true
12
- end
13
- end
14
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Localer
4
- # A service that returns array of missing translations
5
- class MissingTranslations < Service
6
- def call
7
- missing = []
8
- Localer.each_data do |locale, key, value|
9
- missing.push("#{locale}#{key}") if value.nil?
10
- end
11
- missing
12
- end
13
- end
14
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Localer
4
- # Parse translations into hash:
5
- # key: translation key
6
- # value: hash of locale values
7
- class Parser < Service
8
- param :translations
9
- param :config, default: -> { Localer.config }
10
-
11
- attr_reader :data, :locales
12
-
13
- def call
14
- @data = {}
15
- @locales = []
16
- @translations.each do |(locale, translation)|
17
- @locales.push locale
18
- compare(locale, translation)
19
- end
20
-
21
- self
22
- end
23
-
24
- private
25
-
26
- def compare(locale, translation, prefix = "")
27
- if translation.is_a?(Hash)
28
- translation.each do |(key, value)|
29
- full_key = prefix + ".#{key}"
30
- next if full_key.start_with?(*config.exclude)
31
- compare(locale, value, full_key)
32
- end
33
- else
34
- @data[prefix] ||= {}
35
- @data[prefix][locale] = translation
36
- end
37
- end
38
- end
39
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'dry-initializer'
4
- module Localer
5
- # Core service object
6
- class Service
7
- extend Dry::Initializer # use `param` and `option` for dependencies
8
-
9
- class << self
10
- # Instantiates and calls the service at once
11
- def call(*args, &block)
12
- new(*args).call(&block)
13
- end
14
-
15
- # Accepts both symbolized and stringified attributes
16
- def new(*args)
17
- args << args.pop.symbolize_keys if args.last.is_a?(Hash)
18
- super(*args)
19
- end
20
- end
21
- end
22
- end