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 +5 -5
- data/.gitignore +5 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +17 -1
- data/Appraisals +15 -0
- data/README.md +66 -6
- data/Rakefile +12 -3
- data/_config.yml +1 -0
- data/bin/localer +16 -8
- data/lib/localer.rb +13 -34
- data/lib/localer/config.rb +34 -11
- data/lib/localer/config/locale.rb +12 -0
- data/lib/localer/data.rb +39 -0
- data/lib/localer/data/checker.rb +18 -0
- data/lib/localer/data/missing_translations.rb +18 -0
- data/lib/localer/data/processor.rb +58 -0
- data/lib/localer/data/service.rb +18 -0
- data/lib/localer/ext/hash.rb +89 -0
- data/lib/localer/ext/string.rb +66 -0
- data/lib/localer/rails.rb +20 -0
- data/lib/localer/version.rb +1 -1
- data/localer.gemspec +4 -1
- metadata +56 -7
- data/lib/localer/checker.rb +0 -14
- data/lib/localer/missing_translations.rb +0 -14
- data/lib/localer/parser.rb +0 -39
- data/lib/localer/service.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 165de59d1c94a764cc7dbdec0c2327c2f96c75ef608dd86b4ea627ba2ff071cc
|
4
|
+
data.tar.gz: ba9fc37a955d84b70754bc03e894eb01093f9ca231bd0fce2622bf1fc4b19ead
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 382f19b3867430029b46eeb76f39d9c6366160f5a325170778c825be4160f067ee276afc0047577655c20415db03c11cfcd99b3342668a7c33a64e8f6039aa1d
|
7
|
+
data.tar.gz: eea447818e4ed693cf08e27b656d4a084a6b266dca554af277817532656fc1b304b830ee4dfb4aca97e450fed82f6c27430c89dba49d46b6ac8c3c2eb0dd5762
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -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
|
data/.travis.yml
CHANGED
@@ -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
|
-
|
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
|
data/Appraisals
ADDED
data/README.md
CHANGED
@@ -1,11 +1,21 @@
|
|
1
|
-
|
1
|
+
[](https://travis-ci.org/aderyabin/localer) [](https://rubygems.org/gems/localer)
|
2
2
|
|
3
|
-
|
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
|
-
<
|
7
|
-
<img src="https://
|
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 `
|
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
|
-
|
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
|
-
|
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
|
data/_config.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
theme: jekyll-theme-leap-day
|
data/bin/localer
CHANGED
@@ -11,12 +11,16 @@ module Localer
|
|
11
11
|
say Localer::VERSION
|
12
12
|
end
|
13
13
|
|
14
|
-
desc "check", "Check
|
15
|
-
def check
|
16
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
data/lib/localer.rb
CHANGED
@@ -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/
|
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/
|
7
|
+
require_relative "localer/data"
|
9
8
|
|
10
9
|
module Localer # :nodoc:
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
+
@data ||= load_data
|
27
16
|
end
|
28
17
|
|
29
|
-
|
30
|
-
|
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
|
40
|
-
@config
|
22
|
+
def configure(options = {})
|
23
|
+
@config = Config.load(options)
|
41
24
|
end
|
42
25
|
|
43
|
-
def
|
44
|
-
data
|
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
|
data/lib/localer/config.rb
CHANGED
@@ -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
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
19
|
-
|
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
|
data/lib/localer/data.rb
ADDED
@@ -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
|
data/lib/localer/version.rb
CHANGED
data/localer.gemspec
CHANGED
@@ -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
|
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-
|
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/
|
119
|
-
- lib/localer/
|
120
|
-
- lib/localer/
|
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.
|
192
|
+
rubygems_version: 2.7.4
|
144
193
|
signing_key:
|
145
194
|
specification_version: 4
|
146
195
|
summary: Automatic detecting missing I18n translations tool.
|
data/lib/localer/checker.rb
DELETED
@@ -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
|
data/lib/localer/parser.rb
DELETED
@@ -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
|
data/lib/localer/service.rb
DELETED
@@ -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
|