localer 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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