├── .github
└── workflows
│ ├── bundle-audit.yml
│ ├── rubocop.yml
│ └── test.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── Appraisals
├── CODE_OF_CONDUCT.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── _config.yml
├── bin
└── localer
├── features
├── global_exclude.feature
├── locale_option.feature
├── missing_app.feature
├── real_locales.feature
├── simple.feature
├── step_definitions
│ └── additional_cli_steps.rb
└── support
│ └── env.rb
├── gemfiles
├── rails50.gemfile
├── rails50.gemfile.lock
├── rails51.gemfile
├── rails51.gemfile.lock
├── rails52.gemfile
├── rails52.gemfile.lock
├── rails60.gemfile
├── rails60.gemfile.lock
├── rails61.gemfile
└── rails61.gemfile.lock
├── lib
├── localer.rb
└── localer
│ ├── config.rb
│ ├── config
│ └── locale.rb
│ ├── data.rb
│ ├── data
│ ├── checker.rb
│ ├── missing_translations.rb
│ ├── processor.rb
│ └── service.rb
│ ├── ext
│ ├── hash.rb
│ └── string.rb
│ ├── rails.rb
│ ├── rake_task.rb
│ └── version.rb
├── localer.gemspec
├── public
├── localer-full.png
├── localer-full2.png
└── localer-logo.png
└── spec
├── dummy_app
├── config.ru
└── config
│ ├── application.rb
│ ├── environment.rb
│ └── locales
│ ├── en.rails.rb
│ ├── en.rails.yml
│ ├── ru.rails.rb
│ ├── ru.rails.yml
│ ├── us.rails.rb
│ └── us.rails.yml
├── localer_spec.rb
└── spec_helper.rb
/.github/workflows/bundle-audit.yml:
--------------------------------------------------------------------------------
1 | name: Bundle Audit
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | schedule:
9 | - cron: "0 0 * * *"
10 |
11 | jobs:
12 | rubocop:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: ruby/setup-ruby@v1
17 | with:
18 | ruby-version: 2.7
19 | - name: Patch-level verification for Bundler
20 | run: |
21 | gem install bundle-audit
22 | bundle-audit check --update
23 |
--------------------------------------------------------------------------------
/.github/workflows/rubocop.yml:
--------------------------------------------------------------------------------
1 | name: Lint Ruby
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | rubocop:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: ruby/setup-ruby@v1
15 | with:
16 | ruby-version: 2.7
17 | - name: Lint Ruby code with RuboCop
18 | run: |
19 | gem install rubocop
20 | rubocop
21 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | env:
13 | BUNDLE_JOBS: 4
14 | BUNDLE_RETRY: 3
15 | VERIFY_RESERVED: 1
16 | CI: true
17 | CUCUMBER_PUBLISH_QUIET: true
18 | strategy:
19 | fail-fast: false
20 | matrix:
21 | ruby: [2.7, 2.6, 2.5, 2.4]
22 | gemfile: [
23 | 'gemfiles/rails50.gemfile',
24 | 'gemfiles/rails51.gemfile',
25 | 'gemfiles/rails52.gemfile',
26 | 'gemfiles/rails60.gemfile',
27 | 'gemfiles/rails61.gemfile'
28 | ]
29 | exclude:
30 | - ruby: 2.4
31 | gemfile: gemfiles/rails52.gemfile
32 | - ruby: 2.4
33 | gemfile: gemfiles/rails60.gemfile
34 | - ruby: 2.4
35 | gemfile: gemfiles/rails61.gemfile
36 |
37 |
38 | steps:
39 | - uses: actions/checkout@v2
40 | - uses: actions/cache@v1
41 | with:
42 | path: /home/runner/bundle
43 | key: bundle-${{ matrix.ruby }}-${{ matrix.gemfile }}-${{ hashFiles(matrix.gemfile) }}-${{ hashFiles('**/*.gemspec') }}
44 | restore-keys: |
45 | bundle-${{ matrix.ruby }}-${{ matrix.gemfile }}-
46 | - uses: ruby/setup-ruby@v1
47 | with:
48 | ruby-version: ${{ matrix.ruby }}
49 | - name: Bundle install
50 | run: |
51 | bundle config path /home/runner/bundle
52 | bundle config --global gemfile ${{ matrix.gemfile }}
53 | bundle install
54 | bundle update
55 | - name: Run tests
56 | run: bundle exec rake
57 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /coverage/
5 | /doc/
6 | /pkg/
7 | /spec/reports/
8 | /tmp/
9 |
10 | Gemfile.lock
11 | erl_crash.dump
12 | .ruby-version
13 | .vscode
14 | spec/dummy_app/log/
15 | vendor/
16 | .devcontainer/
17 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | # Include gemspec and Rakefile
3 | Include:
4 | - "lib/**/*.rb"
5 | - "lib/**/*.rake"
6 | - "spec/**/*.rb"
7 | Exclude:
8 | - "bin/**/*"
9 | - "lib/localer/ext/*.rb"
10 | - "Appraisals"
11 | - "Gemfile"
12 | - "Rakefile"
13 | - "*.gemspec"
14 | - "spec/dummy_app/"
15 | DisplayCopNames: true
16 | NewCops: enable
17 | StyleGuideCopsOnly: false
18 |
19 | Naming/AccessorMethodName:
20 | Enabled: false
21 |
22 | Style/PercentLiteralDelimiters:
23 | Enabled: false
24 |
25 | Style/TrivialAccessors:
26 | Enabled: false
27 |
28 | Style/Documentation:
29 | Exclude:
30 | - "spec/**/*.rb"
31 |
32 | Style/StringLiterals:
33 | Enabled: false
34 |
35 | Style/BlockDelimiters:
36 | Exclude:
37 | - "spec/**/*.rb"
38 |
39 | Style/DoubleNegation:
40 | Enabled: false
41 |
42 | Style/HashEachMethods:
43 | Enabled: true
44 |
45 | Style/HashTransformKeys:
46 | Enabled: true
47 |
48 | Style/HashTransformValues:
49 | Enabled: true
50 |
51 | Layout/SpaceInsideStringInterpolation:
52 | EnforcedStyle: no_space
53 |
54 | Lint/AmbiguousRegexpLiteral:
55 | Enabled: false
56 |
57 | Lint/AmbiguousBlockAssociation:
58 | Enabled: false
59 |
60 | Metrics/MethodLength:
61 | Exclude:
62 | - "spec/**/*.rb"
63 |
64 | Layout/LineLength:
65 | Max: 120
66 | Exclude:
67 | - "spec/**/*.rb"
68 |
69 | Metrics/BlockLength:
70 | Exclude:
71 | - "spec/**/*.rb"
72 |
73 | Security/YAMLLoad:
74 | Enabled: false
75 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise 'rails50' do
2 | gem 'rails', '~> 5.0'
3 | end
4 |
5 | appraise 'rails51' do
6 | gem 'rails', '~> 5.1'
7 | end
8 |
9 | appraise 'rails52' do
10 | gem 'rails', '~> 5.2'
11 | end
12 |
13 | appraise 'rails60' do
14 | gem 'rails', '~> 6.0'
15 | end
16 |
17 | appraise 'rails61' do
18 | gem 'rails', '~> 6.1'
19 | end
20 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at deriabin@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4 |
5 | # Specify your gem's dependencies in localer.gemspec
6 | gemspec
7 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | localer (0.2.0)
5 | dry-initializer (>= 2.0)
6 | thor (>= 0.19)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | activesupport (6.1.4)
12 | concurrent-ruby (~> 1.0, >= 1.0.2)
13 | i18n (>= 1.6, < 2)
14 | minitest (>= 5.1)
15 | tzinfo (~> 2.0)
16 | zeitwerk (~> 2.3)
17 | appraisal (2.4.0)
18 | bundler
19 | rake
20 | thor (>= 0.14.0)
21 | aruba (1.1.2)
22 | bundler (>= 1.17, < 3.0)
23 | childprocess (>= 2.0, < 5.0)
24 | contracts (>= 0.16.0, < 0.18.0)
25 | cucumber (>= 2.4, < 7.0)
26 | rspec-expectations (~> 3.4)
27 | thor (~> 1.0)
28 | ast (2.4.2)
29 | builder (3.2.4)
30 | childprocess (4.1.0)
31 | concurrent-ruby (1.1.9)
32 | contracts (0.16.1)
33 | cucumber (6.1.0)
34 | builder (~> 3.2, >= 3.2.4)
35 | cucumber-core (~> 9.0, >= 9.0.1)
36 | cucumber-create-meta (~> 4.0, >= 4.0.0)
37 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
38 | cucumber-gherkin (~> 18.1, >= 18.1.0)
39 | cucumber-html-formatter (~> 13.0, >= 13.0.0)
40 | cucumber-messages (~> 15.0, >= 15.0.0)
41 | cucumber-wire (~> 5.0, >= 5.0.1)
42 | diff-lcs (~> 1.4, >= 1.4.4)
43 | mime-types (~> 3.3, >= 3.3.1)
44 | multi_test (~> 0.1, >= 0.1.2)
45 | sys-uname (~> 1.2, >= 1.2.2)
46 | cucumber-core (9.0.1)
47 | cucumber-gherkin (~> 18.1, >= 18.1.0)
48 | cucumber-messages (~> 15.0, >= 15.0.0)
49 | cucumber-tag-expressions (~> 3.0, >= 3.0.1)
50 | cucumber-create-meta (4.0.0)
51 | cucumber-messages (~> 15.0, >= 15.0.0)
52 | sys-uname (~> 1.2, >= 1.2.2)
53 | cucumber-cucumber-expressions (12.1.1)
54 | cucumber-gherkin (18.1.1)
55 | cucumber-messages (~> 15.0, >= 15.0.0)
56 | cucumber-html-formatter (13.0.0)
57 | cucumber-messages (~> 15.0, >= 15.0.0)
58 | cucumber-messages (15.0.0)
59 | protobuf-cucumber (~> 3.10, >= 3.10.8)
60 | cucumber-tag-expressions (3.0.1)
61 | cucumber-wire (5.0.1)
62 | cucumber-core (~> 9.0, >= 9.0.1)
63 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
64 | cucumber-messages (~> 15.0, >= 15.0.0)
65 | diff-lcs (1.4.4)
66 | dry-initializer (3.0.4)
67 | ffi (1.15.3)
68 | i18n (1.8.10)
69 | concurrent-ruby (~> 1.0)
70 | middleware (0.1.0)
71 | mime-types (3.3.1)
72 | mime-types-data (~> 3.2015)
73 | mime-types-data (3.2021.0225)
74 | minitest (5.14.4)
75 | multi_test (0.1.2)
76 | parallel (1.20.1)
77 | parser (3.0.1.1)
78 | ast (~> 2.4.1)
79 | protobuf-cucumber (3.10.8)
80 | activesupport (>= 3.2)
81 | middleware
82 | thor
83 | thread_safe
84 | rainbow (3.0.0)
85 | rake (13.0.3)
86 | regexp_parser (2.1.1)
87 | rexml (3.2.5)
88 | rspec (3.10.0)
89 | rspec-core (~> 3.10.0)
90 | rspec-expectations (~> 3.10.0)
91 | rspec-mocks (~> 3.10.0)
92 | rspec-core (3.10.1)
93 | rspec-support (~> 3.10.0)
94 | rspec-expectations (3.10.1)
95 | diff-lcs (>= 1.2.0, < 2.0)
96 | rspec-support (~> 3.10.0)
97 | rspec-mocks (3.10.2)
98 | diff-lcs (>= 1.2.0, < 2.0)
99 | rspec-support (~> 3.10.0)
100 | rspec-support (3.10.2)
101 | rubocop (0.93.1)
102 | parallel (~> 1.10)
103 | parser (>= 2.7.1.5)
104 | rainbow (>= 2.2.2, < 4.0)
105 | regexp_parser (>= 1.8)
106 | rexml
107 | rubocop-ast (>= 0.6.0)
108 | ruby-progressbar (~> 1.7)
109 | unicode-display_width (>= 1.4.0, < 2.0)
110 | rubocop-ast (1.7.0)
111 | parser (>= 3.0.1.1)
112 | ruby-progressbar (1.11.0)
113 | sys-uname (1.2.2)
114 | ffi (~> 1.1)
115 | thor (1.1.0)
116 | thread_safe (0.3.6)
117 | tzinfo (2.0.4)
118 | concurrent-ruby (~> 1.0)
119 | unicode-display_width (1.7.0)
120 | zeitwerk (2.4.2)
121 |
122 | PLATFORMS
123 | ruby
124 |
125 | DEPENDENCIES
126 | appraisal
127 | aruba
128 | bundler (~> 1.17)
129 | cucumber
130 | localer!
131 | rake (>= 12.3.3)
132 | rspec (~> 3.0)
133 | rubocop (~> 0.50)
134 |
135 | BUNDLED WITH
136 | 1.17.3
137 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Andrey Deryabin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | [](https://rubygems.org/gems/localer) [](https://github.com/aderyabin/localer/actions)
7 |
8 | Localer is a tool that automatically detects missing I18n translations.
9 |
10 | The goal is to preserve the integrity of translations. Localer parses and merges all application locales’ keys. At the next step, it searches for missing translations among the calculated keys.
11 |
12 |
13 |
14 |
15 |
16 | ## Installation
17 |
18 | Add this line to your application's Gemfile:
19 |
20 | ```ruby
21 | gem 'localer'
22 | ```
23 |
24 | And then execute:
25 |
26 | $ bundle
27 |
28 | Or install it yourself as:
29 |
30 | $ gem install localer
31 |
32 | ## Usage
33 |
34 | At the root directory of a Rails app, run:
35 |
36 | $ localer check .
37 |
38 | or for specific Rails path:
39 |
40 | $ localer check /path/to/rails/application
41 |
42 | ## CI integration
43 |
44 | Localer is easy to integrate into your favorite CI workflow:
45 | ```yml
46 | # .travis.yml
47 |
48 | # other configuration options
49 | script:
50 | - bundle exec bundle-audit
51 | - bundle exec rubocop
52 | - bundle exec rspec
53 | - bundle exec localer
54 | ```
55 |
56 | or
57 |
58 | ```ruby
59 | # Rakefile
60 |
61 | # other requirements
62 | require 'localer/rake_task'
63 | Localer::RakeTask.new()
64 |
65 | task(:default).clear
66 | task default: [:rubocop, :spec, :localer]
67 | ```
68 |
69 | ## Support
70 |
71 | Localer supports
72 | * Ruby: 2.4, 2.5, 2.6, 2.7
73 | * Rails: 5.0, 5.1, 5.2, 6.0, 6.1
74 |
75 | ## Configuration
76 |
77 | 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.
78 |
79 | #### Disable specific locale
80 |
81 | By default, Localer enables all locales, but you can disable it:
82 |
83 | ```yml
84 | Locale:
85 | EN:
86 | Enabled: false
87 | ```
88 |
89 | #### Exclude keys globally
90 | By default, Localer enables all keys, but you can disable keys started with specified string or by regex:
91 |
92 | ```yml
93 | Exclude:
94 | - /population\z/
95 | - .countries.france
96 | ```
97 |
98 | #### Exclude keys for specific locale
99 | ```yml
100 | Locale:
101 | EN:
102 | Exclude:
103 | - /population\z/
104 | - .countries.france
105 | ```
106 |
107 | ## Using Rake
108 |
109 | Localer ships with a rake task. To use Localer's rake task you simply need to require the task file and define a task with it. Below is a rake task that will run `localer`:
110 |
111 | ```ruby
112 | require 'rubygems'
113 | require 'localer'
114 | require 'localer/rake_task'
115 |
116 | Localer::RakeTask.new(:localer)
117 | ```
118 |
119 | When you now run:
120 |
121 | $ rake -T
122 |
123 | you should see
124 |
125 | ```
126 | rake localer # Run Localer
127 | ```
128 |
129 | ## Development
130 |
131 | 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.
132 |
133 | ## Built With
134 |
135 | * [Thor](https://github.com/erikhuda/thor) - Used for building command-line interfaces.
136 | * [Appraisal](https://github.com/thoughtbot/appraisal) - Used for testing against different versions of dependencies
137 | * [Cucumber](https://github.com/cucumber/cucumber) + [Aruba](https://github.com/cucumber/aruba) - Used for testing command-line commands
138 |
139 | ## Acknowledge
140 | Special thanks to [Roman Shamin](https://www.facebook.com/romanshamin) for the logo.
141 |
142 | ## Contributing
143 |
144 | Bug reports and pull requests are welcome on GitHub at https://github.com/aderyabin/localer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
145 |
146 | ## License
147 |
148 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
149 |
150 | ## Code of Conduct
151 |
152 | Everyone interacting in the Localer project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/aderyabin/localer/blob/master/CODE_OF_CONDUCT.md).
153 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rubocop/rake_task"
3 | require 'cucumber/rake/task'
4 |
5 | RuboCop::RakeTask.new
6 |
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
18 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-leap-day
--------------------------------------------------------------------------------
/bin/localer:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "thor"
4 | require "irb"
5 | require_relative "../lib/localer"
6 |
7 | module Localer
8 | class CLI < Thor
9 | desc "version", "Print Localer version"
10 | def version
11 | say Localer::VERSION
12 | end
13 |
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?
21 | say "\xE2\x9C\x94 No missing translations found.", :green
22 | else
23 | missing_translations = Localer.data.missing_translations
24 | say "\xE2\x9C\x96 Missing translations found (#{missing_translations.count}):", :red
25 | missing_translations.each do |tr|
26 | say "* #{tr}"
27 | end
28 |
29 | exit 1
30 | end
31 | end
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
41 | end
42 | end
43 | end
44 |
45 |
46 | Localer::CLI.start(ARGV)
47 |
--------------------------------------------------------------------------------
/features/global_exclude.feature:
--------------------------------------------------------------------------------
1 | Feature: Localer
2 | Scenario: Exclude strings
3 | Given a real locales
4 | Given a config file with:
5 | """
6 | Exclude:
7 | - .countries.france
8 | - .population
9 | """
10 | When I run checker
11 | Then the checker should pass
12 |
13 | Scenario: Exclude regexp
14 | Given a real locales
15 | Given a config file with:
16 | """
17 | Exclude:
18 | - /population\z/
19 | """
20 | When I run checker
21 | Then the checker should pass
22 |
23 | Scenario: Exclude strict regexp
24 | Given a real locales
25 | Given a config file with:
26 | """
27 | Exclude:
28 | - /^.population/
29 | """
30 | When I run checker
31 | Then the checker should returns 1 missing translations:
32 | | en.countries.france.population |
33 |
--------------------------------------------------------------------------------
/features/locale_option.feature:
--------------------------------------------------------------------------------
1 | Feature: Localer
2 | Scenario: Disable en locale
3 | Given a real locales
4 | Given a config file with:
5 | """
6 | Locale:
7 | en:
8 | Enabled: false
9 | """
10 | When I run checker
11 | Then the checker should pass
12 |
13 | Scenario: Disable case-insensitive EN locale
14 | Given a real locales
15 | Given a config file with:
16 | """
17 | Locale:
18 | EN:
19 | Enabled: false
20 | """
21 | When I run checker
22 | Then the checker should pass
23 |
24 | Scenario: Disable en.population
25 | Given a real locales
26 | Given a config file with:
27 | """
28 | Locale:
29 | en:
30 | Exclude:
31 | - .population.italy
32 | """
33 | When I run checker
34 | Then the checker should returns 2 missing translations:
35 | | en.countries.france.population |
36 | | en.population.france |
37 |
38 | Scenario: With empty config file
39 | Given a real locales
40 | Given a config file with:
41 | """
42 | """
43 | When I run checker
44 | Then the checker should returns 4 missing translations:
45 | | ru.population.italy |
46 | | us.population.italy |
47 | | en.countries.france.population |
48 | | en.population.france |
49 |
--------------------------------------------------------------------------------
/features/missing_app.feature:
--------------------------------------------------------------------------------
1 | Feature: Localer
2 |
3 | Scenario: No rails application
4 | When I run `localer check`
5 | Then the checker should not found rails application
6 |
7 | Scenario: No rails application at existed paths
8 | When I run `localer check`
9 | Then the checker should not found rails application
10 |
11 | Scenario: No rails application at not non-existed
12 | When I run `localer check non-existed_path`
13 | Then the checker should not found rails application
14 |
--------------------------------------------------------------------------------
/features/real_locales.feature:
--------------------------------------------------------------------------------
1 | Feature: Localer
2 |
3 | Scenario: Real locales does not pass
4 | Given a real locales
5 | When I run checker
6 | Then the checker should returns 4 missing translations:
7 | | ru.population.italy |
8 | | us.population.italy |
9 | | en.population.france |
10 | | en.countries.france.population |
--------------------------------------------------------------------------------
/features/simple.feature:
--------------------------------------------------------------------------------
1 | Feature: Localer
2 |
3 | Scenario: No locales files
4 | When I run checker
5 | Then the checker should pass
6 |
7 | Scenario: Empty en locale
8 | Given a "en" locale file with:
9 | """
10 | en:
11 | """
12 | When I run checker
13 | Then the checker should pass
14 |
15 | Scenario: Empty ru locale
16 | Given a "ru" locale file with:
17 | """
18 | ru:
19 | """
20 | When I run checker
21 | Then the checker should pass
22 |
23 | Scenario: Complete locales
24 | Given a "en" locale file with:
25 | """
26 | en:
27 | one: one
28 | """
29 | Given a "ru" locale file with:
30 | """
31 | ru:
32 | one: один
33 | """
34 | Given a "us" locale file with:
35 | """
36 | us:
37 | one: one
38 | """
39 | When I run checker
40 | Then the checker should pass
41 |
42 | Scenario: Empty en locale
43 | Given a "ru" locale file with:
44 | """
45 | ru:
46 | one: один
47 | """
48 | When I run checker
49 | Then the checker should fail
50 |
51 | Scenario: Incorrect structure
52 | Given a "en" locale file with:
53 | """
54 | en:
55 | too_long: "Too Long"
56 | """
57 | Given a "ru" locale file with:
58 | """
59 | ru:
60 | too_long:
61 | one: слишком большой длины (не может быть больше чем %{count} символ)
62 | other: слишком большой длины (не может быть больше чем %{count} символа)
63 | """
64 | When I run checker
65 | Then the checker should fail
66 |
--------------------------------------------------------------------------------
/features/step_definitions/additional_cli_steps.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Given /^a "(.*)" locale file with:$/ do |locale, file_content|
4 | write_file("#{LOCALE_DIR}/#{locale}.yml", file_content)
5 | end
6 |
7 | Given /^a config file with:$/ do |file_content|
8 | write_file(CONFIG_PATH, file_content)
9 | end
10 |
11 | Given /^a real locales$/ do # rubocop:disable Metrics/BlockLength
12 | steps %{
13 | Given a "en" locale file with:
14 | """
15 | en:
16 | population:
17 | italy: 60.6
18 | countries:
19 | italy:
20 | city: Rome
21 | spain:
22 | city: Madrid
23 | france:
24 | city: Paris
25 | """
26 | Given a "ru" locale file with:
27 | """
28 | ru:
29 | population:
30 | france: 66.9
31 | countries:
32 | italy:
33 | city: Рим
34 | spain:
35 | city: Мадрид
36 | france:
37 | city: Париж
38 | population: 66.9
39 | """
40 | Given a "us" locale file with:
41 | """
42 | us:
43 | population:
44 | france: 66.9
45 | countries:
46 | italy:
47 | city: Rome
48 | spain:
49 | city: Madrid
50 | france:
51 | city: Paris
52 | population: 66.9
53 | """
54 | }
55 | end
56 |
57 | Then /^the checker should pass$/ do
58 | step 'the output should contain "✔ No missing translations found"'
59 | step 'the exit status should be 0'
60 | end
61 |
62 | Then /^the checker should fail$/ do
63 | step 'the output should contain "✖ Missing translations found"'
64 | step 'the exit status should be 1'
65 | end
66 |
67 | Then /^the checker should returns (.*) missing translations:$/ do |int, translations|
68 | step %{the output should contain "✖ Missing translations found (#{int})"}
69 | translations.raw.each do |tr|
70 | step %{the output should match /^#{Regexp.escape('* ' + tr[0])}$/}
71 | end
72 |
73 | step 'the exit status should be 1'
74 | end
75 |
76 | Then /^the checker should not found rails application$/ do
77 | step 'the output should contain "No Rails application found"'
78 | step 'the exit status should be 1'
79 | end
80 |
81 | When /^I run checker$/ do
82 | if defined?(run)
83 | run("localer check ../../spec/dummy_app")
84 | else
85 | run_command("localer check ../../spec/dummy_app")
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/features/support/env.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'aruba/cucumber'
4 |
5 | DUMMY_APP_DIR = "../../spec/dummy_app"
6 | LOCALE_DIR = "#{DUMMY_APP_DIR}/config/locales"
7 | CONFIG_PATH = "#{DUMMY_APP_DIR}/.localer.yml"
8 |
9 | After do |_|
10 | %w[ru en us].each do |locale|
11 | path = "#{LOCALE_DIR}/#{locale}.yml"
12 | remove(path) if exist?(path)
13 | end
14 |
15 | remove(CONFIG_PATH) if exist?(CONFIG_PATH)
16 | end
17 |
--------------------------------------------------------------------------------
/gemfiles/rails50.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rails", "~> 5.0"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/rails50.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | localer (0.2.0)
5 | dry-initializer (>= 2.0)
6 | thor (>= 0.19)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (5.0.0)
12 | actionpack (= 5.0.0)
13 | nio4r (~> 1.2)
14 | websocket-driver (~> 0.6.1)
15 | actionmailer (5.0.0)
16 | actionpack (= 5.0.0)
17 | actionview (= 5.0.0)
18 | activejob (= 5.0.0)
19 | mail (~> 2.5, >= 2.5.4)
20 | rails-dom-testing (~> 2.0)
21 | actionpack (5.0.0)
22 | actionview (= 5.0.0)
23 | activesupport (= 5.0.0)
24 | rack (~> 2.0)
25 | rack-test (~> 0.6.3)
26 | rails-dom-testing (~> 2.0)
27 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
28 | actionview (5.0.0)
29 | activesupport (= 5.0.0)
30 | builder (~> 3.1)
31 | erubis (~> 2.7.0)
32 | rails-dom-testing (~> 2.0)
33 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
34 | activejob (5.0.0)
35 | activesupport (= 5.0.0)
36 | globalid (>= 0.3.6)
37 | activemodel (5.0.0)
38 | activesupport (= 5.0.0)
39 | activerecord (5.0.0)
40 | activemodel (= 5.0.0)
41 | activesupport (= 5.0.0)
42 | arel (~> 7.0)
43 | activesupport (5.0.0)
44 | concurrent-ruby (~> 1.0, >= 1.0.2)
45 | i18n (~> 0.7)
46 | minitest (~> 5.1)
47 | tzinfo (~> 1.1)
48 | appraisal (2.2.0)
49 | bundler
50 | rake
51 | thor (>= 0.14.0)
52 | arel (7.1.4)
53 | aruba (0.14.3)
54 | childprocess (~> 0.8.0)
55 | contracts (~> 0.9)
56 | cucumber (>= 1.3.19)
57 | ffi (~> 1.9.10)
58 | rspec-expectations (>= 2.99)
59 | thor (~> 0.19)
60 | ast (2.4.0)
61 | backports (3.11.1)
62 | builder (3.2.3)
63 | childprocess (0.8.0)
64 | ffi (~> 1.0, >= 1.0.11)
65 | concurrent-ruby (1.0.5)
66 | contracts (0.16.0)
67 | crass (1.0.3)
68 | cucumber (3.1.0)
69 | builder (>= 2.1.2)
70 | cucumber-core (~> 3.1.0)
71 | cucumber-expressions (~> 5.0.4)
72 | cucumber-wire (~> 0.0.1)
73 | diff-lcs (~> 1.3)
74 | gherkin (~> 5.0)
75 | multi_json (>= 1.7.5, < 2.0)
76 | multi_test (>= 0.1.2)
77 | cucumber-core (3.1.0)
78 | backports (>= 3.8.0)
79 | cucumber-tag_expressions (~> 1.1.0)
80 | gherkin (>= 5.0.0)
81 | cucumber-expressions (5.0.13)
82 | cucumber-tag_expressions (1.1.1)
83 | cucumber-wire (0.0.1)
84 | diff-lcs (1.3)
85 | dry-initializer (3.0.3)
86 | erubis (2.7.0)
87 | ffi (1.9.21)
88 | gherkin (5.0.0)
89 | globalid (0.4.1)
90 | activesupport (>= 4.2.0)
91 | i18n (0.9.3)
92 | concurrent-ruby (~> 1.0)
93 | loofah (2.1.1)
94 | crass (~> 1.0.2)
95 | nokogiri (>= 1.5.9)
96 | mail (2.7.0)
97 | mini_mime (>= 0.1.1)
98 | method_source (0.9.0)
99 | mini_mime (1.0.0)
100 | mini_portile2 (2.3.0)
101 | minitest (5.11.3)
102 | multi_json (1.13.1)
103 | multi_test (0.1.2)
104 | nio4r (1.2.1)
105 | nokogiri (1.8.2)
106 | mini_portile2 (~> 2.3.0)
107 | parallel (1.12.1)
108 | parser (2.4.0.2)
109 | ast (~> 2.3)
110 | powerpack (0.1.1)
111 | rack (2.0.3)
112 | rack-test (0.6.3)
113 | rack (>= 1.0)
114 | rails (5.0.0)
115 | actioncable (= 5.0.0)
116 | actionmailer (= 5.0.0)
117 | actionpack (= 5.0.0)
118 | actionview (= 5.0.0)
119 | activejob (= 5.0.0)
120 | activemodel (= 5.0.0)
121 | activerecord (= 5.0.0)
122 | activesupport (= 5.0.0)
123 | bundler (>= 1.3.0, < 2.0)
124 | railties (= 5.0.0)
125 | sprockets-rails (>= 2.0.0)
126 | rails-dom-testing (2.0.3)
127 | activesupport (>= 4.2.0)
128 | nokogiri (>= 1.6)
129 | rails-html-sanitizer (1.0.3)
130 | loofah (~> 2.0)
131 | railties (5.0.0)
132 | actionpack (= 5.0.0)
133 | activesupport (= 5.0.0)
134 | method_source
135 | rake (>= 0.8.7)
136 | thor (>= 0.18.1, < 2.0)
137 | rainbow (3.0.0)
138 | rake (13.0.1)
139 | rspec (3.7.0)
140 | rspec-core (~> 3.7.0)
141 | rspec-expectations (~> 3.7.0)
142 | rspec-mocks (~> 3.7.0)
143 | rspec-core (3.7.1)
144 | rspec-support (~> 3.7.0)
145 | rspec-expectations (3.7.0)
146 | diff-lcs (>= 1.2.0, < 2.0)
147 | rspec-support (~> 3.7.0)
148 | rspec-mocks (3.7.0)
149 | diff-lcs (>= 1.2.0, < 2.0)
150 | rspec-support (~> 3.7.0)
151 | rspec-support (3.7.1)
152 | rubocop (0.52.1)
153 | parallel (~> 1.10)
154 | parser (>= 2.4.0.2, < 3.0)
155 | powerpack (~> 0.1)
156 | rainbow (>= 2.2.2, < 4.0)
157 | ruby-progressbar (~> 1.7)
158 | unicode-display_width (~> 1.0, >= 1.0.1)
159 | ruby-progressbar (1.9.0)
160 | sprockets (3.7.1)
161 | concurrent-ruby (~> 1.0)
162 | rack (> 1, < 3)
163 | sprockets-rails (3.2.1)
164 | actionpack (>= 4.0)
165 | activesupport (>= 4.0)
166 | sprockets (>= 3.0.0)
167 | thor (0.19.0)
168 | thread_safe (0.3.6)
169 | tzinfo (1.2.4)
170 | thread_safe (~> 0.1)
171 | unicode-display_width (1.3.0)
172 | websocket-driver (0.6.5)
173 | websocket-extensions (>= 0.1.0)
174 | websocket-extensions (0.1.3)
175 |
176 | PLATFORMS
177 | ruby
178 |
179 | DEPENDENCIES
180 | appraisal
181 | aruba
182 | bundler (~> 1.17)
183 | cucumber
184 | localer!
185 | rails (~> 5.0)
186 | rake (>= 12.3.3)
187 | rspec (~> 3.0)
188 | rubocop (~> 0.50)
189 |
190 | BUNDLED WITH
191 | 1.17.3
192 |
--------------------------------------------------------------------------------
/gemfiles/rails51.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rails", "~> 5.1"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/rails51.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | localer (0.2.0)
5 | dry-initializer (>= 2.0)
6 | thor (>= 0.19)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (5.1.0)
12 | actionpack (= 5.1.0)
13 | nio4r (~> 2.0)
14 | websocket-driver (~> 0.6.1)
15 | actionmailer (5.1.0)
16 | actionpack (= 5.1.0)
17 | actionview (= 5.1.0)
18 | activejob (= 5.1.0)
19 | mail (~> 2.5, >= 2.5.4)
20 | rails-dom-testing (~> 2.0)
21 | actionpack (5.1.0)
22 | actionview (= 5.1.0)
23 | activesupport (= 5.1.0)
24 | rack (~> 2.0)
25 | rack-test (~> 0.6.3)
26 | rails-dom-testing (~> 2.0)
27 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
28 | actionview (5.1.0)
29 | activesupport (= 5.1.0)
30 | builder (~> 3.1)
31 | erubi (~> 1.4)
32 | rails-dom-testing (~> 2.0)
33 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
34 | activejob (5.1.0)
35 | activesupport (= 5.1.0)
36 | globalid (>= 0.3.6)
37 | activemodel (5.1.0)
38 | activesupport (= 5.1.0)
39 | activerecord (5.1.0)
40 | activemodel (= 5.1.0)
41 | activesupport (= 5.1.0)
42 | arel (~> 8.0)
43 | activesupport (5.1.0)
44 | concurrent-ruby (~> 1.0, >= 1.0.2)
45 | i18n (~> 0.7)
46 | minitest (~> 5.1)
47 | tzinfo (~> 1.1)
48 | appraisal (2.2.0)
49 | bundler
50 | rake
51 | thor (>= 0.14.0)
52 | arel (8.0.0)
53 | aruba (0.14.3)
54 | childprocess (~> 0.8.0)
55 | contracts (~> 0.9)
56 | cucumber (>= 1.3.19)
57 | ffi (~> 1.9.10)
58 | rspec-expectations (>= 2.99)
59 | thor (~> 0.19)
60 | ast (2.4.0)
61 | backports (3.11.1)
62 | builder (3.2.3)
63 | childprocess (0.8.0)
64 | ffi (~> 1.0, >= 1.0.11)
65 | concurrent-ruby (1.0.5)
66 | contracts (0.16.0)
67 | crass (1.0.3)
68 | cucumber (3.1.0)
69 | builder (>= 2.1.2)
70 | cucumber-core (~> 3.1.0)
71 | cucumber-expressions (~> 5.0.4)
72 | cucumber-wire (~> 0.0.1)
73 | diff-lcs (~> 1.3)
74 | gherkin (~> 5.0)
75 | multi_json (>= 1.7.5, < 2.0)
76 | multi_test (>= 0.1.2)
77 | cucumber-core (3.1.0)
78 | backports (>= 3.8.0)
79 | cucumber-tag_expressions (~> 1.1.0)
80 | gherkin (>= 5.0.0)
81 | cucumber-expressions (5.0.13)
82 | cucumber-tag_expressions (1.1.1)
83 | cucumber-wire (0.0.1)
84 | diff-lcs (1.3)
85 | dry-initializer (3.0.3)
86 | erubi (1.7.0)
87 | ffi (1.9.21)
88 | gherkin (5.0.0)
89 | globalid (0.4.1)
90 | activesupport (>= 4.2.0)
91 | i18n (0.9.3)
92 | concurrent-ruby (~> 1.0)
93 | loofah (2.1.1)
94 | crass (~> 1.0.2)
95 | nokogiri (>= 1.5.9)
96 | mail (2.7.0)
97 | mini_mime (>= 0.1.1)
98 | method_source (0.9.0)
99 | mini_mime (1.0.0)
100 | mini_portile2 (2.3.0)
101 | minitest (5.11.3)
102 | multi_json (1.13.1)
103 | multi_test (0.1.2)
104 | nio4r (2.2.0)
105 | nokogiri (1.8.2)
106 | mini_portile2 (~> 2.3.0)
107 | parallel (1.12.1)
108 | parser (2.4.0.2)
109 | ast (~> 2.3)
110 | powerpack (0.1.1)
111 | rack (2.0.3)
112 | rack-test (0.6.3)
113 | rack (>= 1.0)
114 | rails (5.1.0)
115 | actioncable (= 5.1.0)
116 | actionmailer (= 5.1.0)
117 | actionpack (= 5.1.0)
118 | actionview (= 5.1.0)
119 | activejob (= 5.1.0)
120 | activemodel (= 5.1.0)
121 | activerecord (= 5.1.0)
122 | activesupport (= 5.1.0)
123 | bundler (>= 1.3.0, < 2.0)
124 | railties (= 5.1.0)
125 | sprockets-rails (>= 2.0.0)
126 | rails-dom-testing (2.0.3)
127 | activesupport (>= 4.2.0)
128 | nokogiri (>= 1.6)
129 | rails-html-sanitizer (1.0.3)
130 | loofah (~> 2.0)
131 | railties (5.1.0)
132 | actionpack (= 5.1.0)
133 | activesupport (= 5.1.0)
134 | method_source
135 | rake (>= 0.8.7)
136 | thor (>= 0.18.1, < 2.0)
137 | rainbow (3.0.0)
138 | rake (13.0.1)
139 | rspec (3.7.0)
140 | rspec-core (~> 3.7.0)
141 | rspec-expectations (~> 3.7.0)
142 | rspec-mocks (~> 3.7.0)
143 | rspec-core (3.7.1)
144 | rspec-support (~> 3.7.0)
145 | rspec-expectations (3.7.0)
146 | diff-lcs (>= 1.2.0, < 2.0)
147 | rspec-support (~> 3.7.0)
148 | rspec-mocks (3.7.0)
149 | diff-lcs (>= 1.2.0, < 2.0)
150 | rspec-support (~> 3.7.0)
151 | rspec-support (3.7.1)
152 | rubocop (0.52.1)
153 | parallel (~> 1.10)
154 | parser (>= 2.4.0.2, < 3.0)
155 | powerpack (~> 0.1)
156 | rainbow (>= 2.2.2, < 4.0)
157 | ruby-progressbar (~> 1.7)
158 | unicode-display_width (~> 1.0, >= 1.0.1)
159 | ruby-progressbar (1.9.0)
160 | sprockets (3.7.1)
161 | concurrent-ruby (~> 1.0)
162 | rack (> 1, < 3)
163 | sprockets-rails (3.2.1)
164 | actionpack (>= 4.0)
165 | activesupport (>= 4.0)
166 | sprockets (>= 3.0.0)
167 | thor (0.19.0)
168 | thread_safe (0.3.6)
169 | tzinfo (1.2.4)
170 | thread_safe (~> 0.1)
171 | unicode-display_width (1.3.0)
172 | websocket-driver (0.6.5)
173 | websocket-extensions (>= 0.1.0)
174 | websocket-extensions (0.1.3)
175 |
176 | PLATFORMS
177 | ruby
178 |
179 | DEPENDENCIES
180 | appraisal
181 | aruba
182 | bundler (~> 1.17)
183 | cucumber
184 | localer!
185 | rails (~> 5.1)
186 | rake (>= 12.3.3)
187 | rspec (~> 3.0)
188 | rubocop (~> 0.50)
189 |
190 | BUNDLED WITH
191 | 1.17.3
192 |
--------------------------------------------------------------------------------
/gemfiles/rails52.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rails", "~> 5.2"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/rails52.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | localer (0.2.0)
5 | dry-initializer (>= 2.0)
6 | thor (>= 0.19)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (5.2.6)
12 | actionpack (= 5.2.6)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | actionmailer (5.2.6)
16 | actionpack (= 5.2.6)
17 | actionview (= 5.2.6)
18 | activejob (= 5.2.6)
19 | mail (~> 2.5, >= 2.5.4)
20 | rails-dom-testing (~> 2.0)
21 | actionpack (5.2.6)
22 | actionview (= 5.2.6)
23 | activesupport (= 5.2.6)
24 | rack (~> 2.0, >= 2.0.8)
25 | rack-test (>= 0.6.3)
26 | rails-dom-testing (~> 2.0)
27 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
28 | actionview (5.2.6)
29 | activesupport (= 5.2.6)
30 | builder (~> 3.1)
31 | erubi (~> 1.4)
32 | rails-dom-testing (~> 2.0)
33 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
34 | activejob (5.2.6)
35 | activesupport (= 5.2.6)
36 | globalid (>= 0.3.6)
37 | activemodel (5.2.6)
38 | activesupport (= 5.2.6)
39 | activerecord (5.2.6)
40 | activemodel (= 5.2.6)
41 | activesupport (= 5.2.6)
42 | arel (>= 9.0)
43 | activestorage (5.2.6)
44 | actionpack (= 5.2.6)
45 | activerecord (= 5.2.6)
46 | marcel (~> 1.0.0)
47 | activesupport (5.2.6)
48 | concurrent-ruby (~> 1.0, >= 1.0.2)
49 | i18n (>= 0.7, < 2)
50 | minitest (~> 5.1)
51 | tzinfo (~> 1.1)
52 | appraisal (2.4.0)
53 | bundler
54 | rake
55 | thor (>= 0.14.0)
56 | arel (9.0.0)
57 | aruba (1.1.2)
58 | bundler (>= 1.17, < 3.0)
59 | childprocess (>= 2.0, < 5.0)
60 | contracts (>= 0.16.0, < 0.18.0)
61 | cucumber (>= 2.4, < 7.0)
62 | rspec-expectations (~> 3.4)
63 | thor (~> 1.0)
64 | ast (2.4.2)
65 | builder (3.2.4)
66 | childprocess (4.1.0)
67 | concurrent-ruby (1.1.9)
68 | contracts (0.16.1)
69 | crass (1.0.6)
70 | cucumber (6.1.0)
71 | builder (~> 3.2, >= 3.2.4)
72 | cucumber-core (~> 9.0, >= 9.0.1)
73 | cucumber-create-meta (~> 4.0, >= 4.0.0)
74 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
75 | cucumber-gherkin (~> 18.1, >= 18.1.0)
76 | cucumber-html-formatter (~> 13.0, >= 13.0.0)
77 | cucumber-messages (~> 15.0, >= 15.0.0)
78 | cucumber-wire (~> 5.0, >= 5.0.1)
79 | diff-lcs (~> 1.4, >= 1.4.4)
80 | mime-types (~> 3.3, >= 3.3.1)
81 | multi_test (~> 0.1, >= 0.1.2)
82 | sys-uname (~> 1.2, >= 1.2.2)
83 | cucumber-core (9.0.1)
84 | cucumber-gherkin (~> 18.1, >= 18.1.0)
85 | cucumber-messages (~> 15.0, >= 15.0.0)
86 | cucumber-tag-expressions (~> 3.0, >= 3.0.1)
87 | cucumber-create-meta (4.0.0)
88 | cucumber-messages (~> 15.0, >= 15.0.0)
89 | sys-uname (~> 1.2, >= 1.2.2)
90 | cucumber-cucumber-expressions (12.1.1)
91 | cucumber-gherkin (18.1.1)
92 | cucumber-messages (~> 15.0, >= 15.0.0)
93 | cucumber-html-formatter (13.0.0)
94 | cucumber-messages (~> 15.0, >= 15.0.0)
95 | cucumber-messages (15.0.0)
96 | protobuf-cucumber (~> 3.10, >= 3.10.8)
97 | cucumber-tag-expressions (3.0.1)
98 | cucumber-wire (5.0.1)
99 | cucumber-core (~> 9.0, >= 9.0.1)
100 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
101 | cucumber-messages (~> 15.0, >= 15.0.0)
102 | diff-lcs (1.4.4)
103 | dry-initializer (3.0.4)
104 | erubi (1.10.0)
105 | ffi (1.15.3)
106 | globalid (0.4.2)
107 | activesupport (>= 4.2.0)
108 | i18n (1.8.10)
109 | concurrent-ruby (~> 1.0)
110 | loofah (2.10.0)
111 | crass (~> 1.0.2)
112 | nokogiri (>= 1.5.9)
113 | mail (2.7.1)
114 | mini_mime (>= 0.1.1)
115 | marcel (1.0.1)
116 | method_source (1.0.0)
117 | middleware (0.1.0)
118 | mime-types (3.3.1)
119 | mime-types-data (~> 3.2015)
120 | mime-types-data (3.2021.0225)
121 | mini_mime (1.1.0)
122 | mini_portile2 (2.5.3)
123 | minitest (5.14.4)
124 | multi_test (0.1.2)
125 | nio4r (2.5.7)
126 | nokogiri (1.11.7)
127 | mini_portile2 (~> 2.5.0)
128 | racc (~> 1.4)
129 | parallel (1.20.1)
130 | parser (3.0.1.1)
131 | ast (~> 2.4.1)
132 | protobuf-cucumber (3.10.8)
133 | activesupport (>= 3.2)
134 | middleware
135 | thor
136 | thread_safe
137 | racc (1.5.2)
138 | rack (2.2.3)
139 | rack-test (1.1.0)
140 | rack (>= 1.0, < 3)
141 | rails (5.2.6)
142 | actioncable (= 5.2.6)
143 | actionmailer (= 5.2.6)
144 | actionpack (= 5.2.6)
145 | actionview (= 5.2.6)
146 | activejob (= 5.2.6)
147 | activemodel (= 5.2.6)
148 | activerecord (= 5.2.6)
149 | activestorage (= 5.2.6)
150 | activesupport (= 5.2.6)
151 | bundler (>= 1.3.0)
152 | railties (= 5.2.6)
153 | sprockets-rails (>= 2.0.0)
154 | rails-dom-testing (2.0.3)
155 | activesupport (>= 4.2.0)
156 | nokogiri (>= 1.6)
157 | rails-html-sanitizer (1.3.0)
158 | loofah (~> 2.3)
159 | railties (5.2.6)
160 | actionpack (= 5.2.6)
161 | activesupport (= 5.2.6)
162 | method_source
163 | rake (>= 0.8.7)
164 | thor (>= 0.19.0, < 2.0)
165 | rainbow (3.0.0)
166 | rake (13.0.3)
167 | regexp_parser (2.1.1)
168 | rexml (3.2.5)
169 | rspec (3.10.0)
170 | rspec-core (~> 3.10.0)
171 | rspec-expectations (~> 3.10.0)
172 | rspec-mocks (~> 3.10.0)
173 | rspec-core (3.10.1)
174 | rspec-support (~> 3.10.0)
175 | rspec-expectations (3.10.1)
176 | diff-lcs (>= 1.2.0, < 2.0)
177 | rspec-support (~> 3.10.0)
178 | rspec-mocks (3.10.2)
179 | diff-lcs (>= 1.2.0, < 2.0)
180 | rspec-support (~> 3.10.0)
181 | rspec-support (3.10.2)
182 | rubocop (0.93.1)
183 | parallel (~> 1.10)
184 | parser (>= 2.7.1.5)
185 | rainbow (>= 2.2.2, < 4.0)
186 | regexp_parser (>= 1.8)
187 | rexml
188 | rubocop-ast (>= 0.6.0)
189 | ruby-progressbar (~> 1.7)
190 | unicode-display_width (>= 1.4.0, < 2.0)
191 | rubocop-ast (1.7.0)
192 | parser (>= 3.0.1.1)
193 | ruby-progressbar (1.11.0)
194 | sprockets (4.0.2)
195 | concurrent-ruby (~> 1.0)
196 | rack (> 1, < 3)
197 | sprockets-rails (3.2.2)
198 | actionpack (>= 4.0)
199 | activesupport (>= 4.0)
200 | sprockets (>= 3.0.0)
201 | sys-uname (1.2.2)
202 | ffi (~> 1.1)
203 | thor (1.1.0)
204 | thread_safe (0.3.6)
205 | tzinfo (1.2.9)
206 | thread_safe (~> 0.1)
207 | unicode-display_width (1.7.0)
208 | websocket-driver (0.7.5)
209 | websocket-extensions (>= 0.1.0)
210 | websocket-extensions (0.1.5)
211 |
212 | PLATFORMS
213 | ruby
214 |
215 | DEPENDENCIES
216 | appraisal
217 | aruba
218 | bundler (~> 1.17)
219 | cucumber
220 | localer!
221 | rails (~> 5.2)
222 | rake (>= 12.3.3)
223 | rspec (~> 3.0)
224 | rubocop (~> 0.50)
225 |
226 | BUNDLED WITH
227 | 1.17.3
228 |
--------------------------------------------------------------------------------
/gemfiles/rails60.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rails", "~> 6.0"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/rails60.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | localer (0.2.0)
5 | dry-initializer (>= 2.0)
6 | thor (>= 0.19)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (6.1.4)
12 | actionpack (= 6.1.4)
13 | activesupport (= 6.1.4)
14 | nio4r (~> 2.0)
15 | websocket-driver (>= 0.6.1)
16 | actionmailbox (6.1.4)
17 | actionpack (= 6.1.4)
18 | activejob (= 6.1.4)
19 | activerecord (= 6.1.4)
20 | activestorage (= 6.1.4)
21 | activesupport (= 6.1.4)
22 | mail (>= 2.7.1)
23 | actionmailer (6.1.4)
24 | actionpack (= 6.1.4)
25 | actionview (= 6.1.4)
26 | activejob (= 6.1.4)
27 | activesupport (= 6.1.4)
28 | mail (~> 2.5, >= 2.5.4)
29 | rails-dom-testing (~> 2.0)
30 | actionpack (6.1.4)
31 | actionview (= 6.1.4)
32 | activesupport (= 6.1.4)
33 | rack (~> 2.0, >= 2.0.9)
34 | rack-test (>= 0.6.3)
35 | rails-dom-testing (~> 2.0)
36 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
37 | actiontext (6.1.4)
38 | actionpack (= 6.1.4)
39 | activerecord (= 6.1.4)
40 | activestorage (= 6.1.4)
41 | activesupport (= 6.1.4)
42 | nokogiri (>= 1.8.5)
43 | actionview (6.1.4)
44 | activesupport (= 6.1.4)
45 | builder (~> 3.1)
46 | erubi (~> 1.4)
47 | rails-dom-testing (~> 2.0)
48 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
49 | activejob (6.1.4)
50 | activesupport (= 6.1.4)
51 | globalid (>= 0.3.6)
52 | activemodel (6.1.4)
53 | activesupport (= 6.1.4)
54 | activerecord (6.1.4)
55 | activemodel (= 6.1.4)
56 | activesupport (= 6.1.4)
57 | activestorage (6.1.4)
58 | actionpack (= 6.1.4)
59 | activejob (= 6.1.4)
60 | activerecord (= 6.1.4)
61 | activesupport (= 6.1.4)
62 | marcel (~> 1.0.0)
63 | mini_mime (>= 1.1.0)
64 | activesupport (6.1.4)
65 | concurrent-ruby (~> 1.0, >= 1.0.2)
66 | i18n (>= 1.6, < 2)
67 | minitest (>= 5.1)
68 | tzinfo (~> 2.0)
69 | zeitwerk (~> 2.3)
70 | appraisal (2.4.0)
71 | bundler
72 | rake
73 | thor (>= 0.14.0)
74 | aruba (1.1.2)
75 | bundler (>= 1.17, < 3.0)
76 | childprocess (>= 2.0, < 5.0)
77 | contracts (>= 0.16.0, < 0.18.0)
78 | cucumber (>= 2.4, < 7.0)
79 | rspec-expectations (~> 3.4)
80 | thor (~> 1.0)
81 | ast (2.4.2)
82 | builder (3.2.4)
83 | childprocess (4.1.0)
84 | concurrent-ruby (1.1.9)
85 | contracts (0.16.1)
86 | crass (1.0.6)
87 | cucumber (6.1.0)
88 | builder (~> 3.2, >= 3.2.4)
89 | cucumber-core (~> 9.0, >= 9.0.1)
90 | cucumber-create-meta (~> 4.0, >= 4.0.0)
91 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
92 | cucumber-gherkin (~> 18.1, >= 18.1.0)
93 | cucumber-html-formatter (~> 13.0, >= 13.0.0)
94 | cucumber-messages (~> 15.0, >= 15.0.0)
95 | cucumber-wire (~> 5.0, >= 5.0.1)
96 | diff-lcs (~> 1.4, >= 1.4.4)
97 | mime-types (~> 3.3, >= 3.3.1)
98 | multi_test (~> 0.1, >= 0.1.2)
99 | sys-uname (~> 1.2, >= 1.2.2)
100 | cucumber-core (9.0.1)
101 | cucumber-gherkin (~> 18.1, >= 18.1.0)
102 | cucumber-messages (~> 15.0, >= 15.0.0)
103 | cucumber-tag-expressions (~> 3.0, >= 3.0.1)
104 | cucumber-create-meta (4.0.0)
105 | cucumber-messages (~> 15.0, >= 15.0.0)
106 | sys-uname (~> 1.2, >= 1.2.2)
107 | cucumber-cucumber-expressions (12.1.1)
108 | cucumber-gherkin (18.1.1)
109 | cucumber-messages (~> 15.0, >= 15.0.0)
110 | cucumber-html-formatter (13.0.0)
111 | cucumber-messages (~> 15.0, >= 15.0.0)
112 | cucumber-messages (15.0.0)
113 | protobuf-cucumber (~> 3.10, >= 3.10.8)
114 | cucumber-tag-expressions (3.0.1)
115 | cucumber-wire (5.0.1)
116 | cucumber-core (~> 9.0, >= 9.0.1)
117 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
118 | cucumber-messages (~> 15.0, >= 15.0.0)
119 | diff-lcs (1.4.4)
120 | dry-initializer (3.0.4)
121 | erubi (1.10.0)
122 | ffi (1.15.3)
123 | globalid (0.4.2)
124 | activesupport (>= 4.2.0)
125 | i18n (1.8.10)
126 | concurrent-ruby (~> 1.0)
127 | loofah (2.10.0)
128 | crass (~> 1.0.2)
129 | nokogiri (>= 1.5.9)
130 | mail (2.7.1)
131 | mini_mime (>= 0.1.1)
132 | marcel (1.0.1)
133 | method_source (1.0.0)
134 | middleware (0.1.0)
135 | mime-types (3.3.1)
136 | mime-types-data (~> 3.2015)
137 | mime-types-data (3.2021.0225)
138 | mini_mime (1.1.0)
139 | mini_portile2 (2.5.3)
140 | minitest (5.14.4)
141 | multi_test (0.1.2)
142 | nio4r (2.5.7)
143 | nokogiri (1.11.7)
144 | mini_portile2 (~> 2.5.0)
145 | racc (~> 1.4)
146 | parallel (1.20.1)
147 | parser (3.0.1.1)
148 | ast (~> 2.4.1)
149 | protobuf-cucumber (3.10.8)
150 | activesupport (>= 3.2)
151 | middleware
152 | thor
153 | thread_safe
154 | racc (1.5.2)
155 | rack (2.2.3)
156 | rack-test (1.1.0)
157 | rack (>= 1.0, < 3)
158 | rails (6.1.4)
159 | actioncable (= 6.1.4)
160 | actionmailbox (= 6.1.4)
161 | actionmailer (= 6.1.4)
162 | actionpack (= 6.1.4)
163 | actiontext (= 6.1.4)
164 | actionview (= 6.1.4)
165 | activejob (= 6.1.4)
166 | activemodel (= 6.1.4)
167 | activerecord (= 6.1.4)
168 | activestorage (= 6.1.4)
169 | activesupport (= 6.1.4)
170 | bundler (>= 1.15.0)
171 | railties (= 6.1.4)
172 | sprockets-rails (>= 2.0.0)
173 | rails-dom-testing (2.0.3)
174 | activesupport (>= 4.2.0)
175 | nokogiri (>= 1.6)
176 | rails-html-sanitizer (1.3.0)
177 | loofah (~> 2.3)
178 | railties (6.1.4)
179 | actionpack (= 6.1.4)
180 | activesupport (= 6.1.4)
181 | method_source
182 | rake (>= 0.13)
183 | thor (~> 1.0)
184 | rainbow (3.0.0)
185 | rake (13.0.3)
186 | regexp_parser (2.1.1)
187 | rexml (3.2.5)
188 | rspec (3.10.0)
189 | rspec-core (~> 3.10.0)
190 | rspec-expectations (~> 3.10.0)
191 | rspec-mocks (~> 3.10.0)
192 | rspec-core (3.10.1)
193 | rspec-support (~> 3.10.0)
194 | rspec-expectations (3.10.1)
195 | diff-lcs (>= 1.2.0, < 2.0)
196 | rspec-support (~> 3.10.0)
197 | rspec-mocks (3.10.2)
198 | diff-lcs (>= 1.2.0, < 2.0)
199 | rspec-support (~> 3.10.0)
200 | rspec-support (3.10.2)
201 | rubocop (0.93.1)
202 | parallel (~> 1.10)
203 | parser (>= 2.7.1.5)
204 | rainbow (>= 2.2.2, < 4.0)
205 | regexp_parser (>= 1.8)
206 | rexml
207 | rubocop-ast (>= 0.6.0)
208 | ruby-progressbar (~> 1.7)
209 | unicode-display_width (>= 1.4.0, < 2.0)
210 | rubocop-ast (1.7.0)
211 | parser (>= 3.0.1.1)
212 | ruby-progressbar (1.11.0)
213 | sprockets (4.0.2)
214 | concurrent-ruby (~> 1.0)
215 | rack (> 1, < 3)
216 | sprockets-rails (3.2.2)
217 | actionpack (>= 4.0)
218 | activesupport (>= 4.0)
219 | sprockets (>= 3.0.0)
220 | sys-uname (1.2.2)
221 | ffi (~> 1.1)
222 | thor (1.1.0)
223 | thread_safe (0.3.6)
224 | tzinfo (2.0.4)
225 | concurrent-ruby (~> 1.0)
226 | unicode-display_width (1.7.0)
227 | websocket-driver (0.7.5)
228 | websocket-extensions (>= 0.1.0)
229 | websocket-extensions (0.1.5)
230 | zeitwerk (2.4.2)
231 |
232 | PLATFORMS
233 | ruby
234 |
235 | DEPENDENCIES
236 | appraisal
237 | aruba
238 | bundler (~> 1.17)
239 | cucumber
240 | localer!
241 | rails (~> 6.0)
242 | rake (>= 12.3.3)
243 | rspec (~> 3.0)
244 | rubocop (~> 0.50)
245 |
246 | BUNDLED WITH
247 | 1.17.3
248 |
--------------------------------------------------------------------------------
/gemfiles/rails61.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rails", "~> 6.1"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/rails61.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | localer (0.2.0)
5 | dry-initializer (>= 2.0)
6 | thor (>= 0.19)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (6.1.4)
12 | actionpack (= 6.1.4)
13 | activesupport (= 6.1.4)
14 | nio4r (~> 2.0)
15 | websocket-driver (>= 0.6.1)
16 | actionmailbox (6.1.4)
17 | actionpack (= 6.1.4)
18 | activejob (= 6.1.4)
19 | activerecord (= 6.1.4)
20 | activestorage (= 6.1.4)
21 | activesupport (= 6.1.4)
22 | mail (>= 2.7.1)
23 | actionmailer (6.1.4)
24 | actionpack (= 6.1.4)
25 | actionview (= 6.1.4)
26 | activejob (= 6.1.4)
27 | activesupport (= 6.1.4)
28 | mail (~> 2.5, >= 2.5.4)
29 | rails-dom-testing (~> 2.0)
30 | actionpack (6.1.4)
31 | actionview (= 6.1.4)
32 | activesupport (= 6.1.4)
33 | rack (~> 2.0, >= 2.0.9)
34 | rack-test (>= 0.6.3)
35 | rails-dom-testing (~> 2.0)
36 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
37 | actiontext (6.1.4)
38 | actionpack (= 6.1.4)
39 | activerecord (= 6.1.4)
40 | activestorage (= 6.1.4)
41 | activesupport (= 6.1.4)
42 | nokogiri (>= 1.8.5)
43 | actionview (6.1.4)
44 | activesupport (= 6.1.4)
45 | builder (~> 3.1)
46 | erubi (~> 1.4)
47 | rails-dom-testing (~> 2.0)
48 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
49 | activejob (6.1.4)
50 | activesupport (= 6.1.4)
51 | globalid (>= 0.3.6)
52 | activemodel (6.1.4)
53 | activesupport (= 6.1.4)
54 | activerecord (6.1.4)
55 | activemodel (= 6.1.4)
56 | activesupport (= 6.1.4)
57 | activestorage (6.1.4)
58 | actionpack (= 6.1.4)
59 | activejob (= 6.1.4)
60 | activerecord (= 6.1.4)
61 | activesupport (= 6.1.4)
62 | marcel (~> 1.0.0)
63 | mini_mime (>= 1.1.0)
64 | activesupport (6.1.4)
65 | concurrent-ruby (~> 1.0, >= 1.0.2)
66 | i18n (>= 1.6, < 2)
67 | minitest (>= 5.1)
68 | tzinfo (~> 2.0)
69 | zeitwerk (~> 2.3)
70 | appraisal (2.4.0)
71 | bundler
72 | rake
73 | thor (>= 0.14.0)
74 | aruba (1.1.2)
75 | bundler (>= 1.17, < 3.0)
76 | childprocess (>= 2.0, < 5.0)
77 | contracts (>= 0.16.0, < 0.18.0)
78 | cucumber (>= 2.4, < 7.0)
79 | rspec-expectations (~> 3.4)
80 | thor (~> 1.0)
81 | ast (2.4.2)
82 | builder (3.2.4)
83 | childprocess (4.1.0)
84 | concurrent-ruby (1.1.9)
85 | contracts (0.16.1)
86 | crass (1.0.6)
87 | cucumber (6.1.0)
88 | builder (~> 3.2, >= 3.2.4)
89 | cucumber-core (~> 9.0, >= 9.0.1)
90 | cucumber-create-meta (~> 4.0, >= 4.0.0)
91 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
92 | cucumber-gherkin (~> 18.1, >= 18.1.0)
93 | cucumber-html-formatter (~> 13.0, >= 13.0.0)
94 | cucumber-messages (~> 15.0, >= 15.0.0)
95 | cucumber-wire (~> 5.0, >= 5.0.1)
96 | diff-lcs (~> 1.4, >= 1.4.4)
97 | mime-types (~> 3.3, >= 3.3.1)
98 | multi_test (~> 0.1, >= 0.1.2)
99 | sys-uname (~> 1.2, >= 1.2.2)
100 | cucumber-core (9.0.1)
101 | cucumber-gherkin (~> 18.1, >= 18.1.0)
102 | cucumber-messages (~> 15.0, >= 15.0.0)
103 | cucumber-tag-expressions (~> 3.0, >= 3.0.1)
104 | cucumber-create-meta (4.0.0)
105 | cucumber-messages (~> 15.0, >= 15.0.0)
106 | sys-uname (~> 1.2, >= 1.2.2)
107 | cucumber-cucumber-expressions (12.1.1)
108 | cucumber-gherkin (18.1.1)
109 | cucumber-messages (~> 15.0, >= 15.0.0)
110 | cucumber-html-formatter (13.0.0)
111 | cucumber-messages (~> 15.0, >= 15.0.0)
112 | cucumber-messages (15.0.0)
113 | protobuf-cucumber (~> 3.10, >= 3.10.8)
114 | cucumber-tag-expressions (3.0.1)
115 | cucumber-wire (5.0.1)
116 | cucumber-core (~> 9.0, >= 9.0.1)
117 | cucumber-cucumber-expressions (~> 12.1, >= 12.1.1)
118 | cucumber-messages (~> 15.0, >= 15.0.0)
119 | diff-lcs (1.4.4)
120 | dry-initializer (3.0.4)
121 | erubi (1.10.0)
122 | ffi (1.15.3)
123 | globalid (0.4.2)
124 | activesupport (>= 4.2.0)
125 | i18n (1.8.10)
126 | concurrent-ruby (~> 1.0)
127 | loofah (2.10.0)
128 | crass (~> 1.0.2)
129 | nokogiri (>= 1.5.9)
130 | mail (2.7.1)
131 | mini_mime (>= 0.1.1)
132 | marcel (1.0.1)
133 | method_source (1.0.0)
134 | middleware (0.1.0)
135 | mime-types (3.3.1)
136 | mime-types-data (~> 3.2015)
137 | mime-types-data (3.2021.0225)
138 | mini_mime (1.1.0)
139 | mini_portile2 (2.5.3)
140 | minitest (5.14.4)
141 | multi_test (0.1.2)
142 | nio4r (2.5.7)
143 | nokogiri (1.11.7)
144 | mini_portile2 (~> 2.5.0)
145 | racc (~> 1.4)
146 | parallel (1.20.1)
147 | parser (3.0.1.1)
148 | ast (~> 2.4.1)
149 | protobuf-cucumber (3.10.8)
150 | activesupport (>= 3.2)
151 | middleware
152 | thor
153 | thread_safe
154 | racc (1.5.2)
155 | rack (2.2.3)
156 | rack-test (1.1.0)
157 | rack (>= 1.0, < 3)
158 | rails (6.1.4)
159 | actioncable (= 6.1.4)
160 | actionmailbox (= 6.1.4)
161 | actionmailer (= 6.1.4)
162 | actionpack (= 6.1.4)
163 | actiontext (= 6.1.4)
164 | actionview (= 6.1.4)
165 | activejob (= 6.1.4)
166 | activemodel (= 6.1.4)
167 | activerecord (= 6.1.4)
168 | activestorage (= 6.1.4)
169 | activesupport (= 6.1.4)
170 | bundler (>= 1.15.0)
171 | railties (= 6.1.4)
172 | sprockets-rails (>= 2.0.0)
173 | rails-dom-testing (2.0.3)
174 | activesupport (>= 4.2.0)
175 | nokogiri (>= 1.6)
176 | rails-html-sanitizer (1.3.0)
177 | loofah (~> 2.3)
178 | railties (6.1.4)
179 | actionpack (= 6.1.4)
180 | activesupport (= 6.1.4)
181 | method_source
182 | rake (>= 0.13)
183 | thor (~> 1.0)
184 | rainbow (3.0.0)
185 | rake (13.0.3)
186 | regexp_parser (2.1.1)
187 | rexml (3.2.5)
188 | rspec (3.10.0)
189 | rspec-core (~> 3.10.0)
190 | rspec-expectations (~> 3.10.0)
191 | rspec-mocks (~> 3.10.0)
192 | rspec-core (3.10.1)
193 | rspec-support (~> 3.10.0)
194 | rspec-expectations (3.10.1)
195 | diff-lcs (>= 1.2.0, < 2.0)
196 | rspec-support (~> 3.10.0)
197 | rspec-mocks (3.10.2)
198 | diff-lcs (>= 1.2.0, < 2.0)
199 | rspec-support (~> 3.10.0)
200 | rspec-support (3.10.2)
201 | rubocop (0.93.1)
202 | parallel (~> 1.10)
203 | parser (>= 2.7.1.5)
204 | rainbow (>= 2.2.2, < 4.0)
205 | regexp_parser (>= 1.8)
206 | rexml
207 | rubocop-ast (>= 0.6.0)
208 | ruby-progressbar (~> 1.7)
209 | unicode-display_width (>= 1.4.0, < 2.0)
210 | rubocop-ast (1.7.0)
211 | parser (>= 3.0.1.1)
212 | ruby-progressbar (1.11.0)
213 | sprockets (4.0.2)
214 | concurrent-ruby (~> 1.0)
215 | rack (> 1, < 3)
216 | sprockets-rails (3.2.2)
217 | actionpack (>= 4.0)
218 | activesupport (>= 4.0)
219 | sprockets (>= 3.0.0)
220 | sys-uname (1.2.2)
221 | ffi (~> 1.1)
222 | thor (1.1.0)
223 | thread_safe (0.3.6)
224 | tzinfo (2.0.4)
225 | concurrent-ruby (~> 1.0)
226 | unicode-display_width (1.7.0)
227 | websocket-driver (0.7.5)
228 | websocket-extensions (>= 0.1.0)
229 | websocket-extensions (0.1.5)
230 | zeitwerk (2.4.2)
231 |
232 | PLATFORMS
233 | ruby
234 |
235 | DEPENDENCIES
236 | appraisal
237 | aruba
238 | bundler (~> 1.17)
239 | cucumber
240 | localer!
241 | rails (~> 6.1)
242 | rake (>= 12.3.3)
243 | rspec (~> 3.0)
244 | rubocop (~> 0.50)
245 |
246 | BUNDLED WITH
247 | 1.17.3
248 |
--------------------------------------------------------------------------------
/lib/localer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "dry-initializer"
4 | require_relative "localer/version"
5 | require_relative "localer/rails"
6 | require_relative "localer/config"
7 | require_relative "localer/data"
8 |
9 | module Localer # :nodoc:
10 | using Localer::Ext::Hash
11 | # using Localer::Ext::String
12 |
13 | class << self
14 | def data
15 | @data ||= load_data
16 | end
17 |
18 | def config
19 | @config ||= configure
20 | end
21 |
22 | def configure(options = {})
23 | @config = Config.load(options)
24 | end
25 |
26 | def load_data(source = Localer::Rails.translations)
27 | @data = Data.new(source)
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/localer/config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'yaml'
4 | require_relative '../localer/ext/hash'
5 | require_relative 'config/locale'
6 |
7 | module Localer # :nodoc:
8 | using Localer::Ext::Hash
9 |
10 | # Loads and parse Localer config file `.localer.yml`
11 | class Config
12 | extend Dry::Initializer
13 |
14 | APP_PATH = Dir.pwd
15 | CONFIG_FILENAME = ".localer.yml"
16 |
17 | option :exclude, default: -> { [] }
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
28 |
29 | def file_config(filename, path)
30 | filename = File.expand_path(filename, path)
31 | return {} unless File.exist?(filename)
32 | return {} if File.zero?(filename)
33 |
34 | YAML
35 | .load_file(filename)
36 | .deep_downcase_keys
37 | .deep_symbolize_keys
38 | end
39 |
40 | def parse_locales(hash)
41 | hash.each_with_object(Hash.new(Locale.new)) do |(l, v), h|
42 | h[l] = Locale.new(v)
43 | end
44 | end
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/lib/localer/config/locale.rb:
--------------------------------------------------------------------------------
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
13 |
--------------------------------------------------------------------------------
/lib/localer/data.rb:
--------------------------------------------------------------------------------
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
40 |
--------------------------------------------------------------------------------
/lib/localer/data/checker.rb:
--------------------------------------------------------------------------------
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
19 |
--------------------------------------------------------------------------------
/lib/localer/data/missing_translations.rb:
--------------------------------------------------------------------------------
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
19 |
--------------------------------------------------------------------------------
/lib/localer/data/processor.rb:
--------------------------------------------------------------------------------
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.downcase].enabled
22 |
23 | @locales.push locale
24 | prepare(locale, translation)
25 | end
26 | [@locales, @data]
27 | end
28 |
29 | private
30 |
31 | def prepare(locale, translation, prefix = "")
32 | if translation.is_a?(Hash)
33 | translation.each do |(key, value)|
34 | full_key = prefix + ".#{key}"
35 | next if exclude?(full_key, locale)
36 |
37 | prepare(locale, value, full_key)
38 | end
39 | else
40 | # @data[prefix] ||= {}
41 | @data[prefix][locale] = translation
42 | end
43 | end
44 |
45 | def exclude?(key, locale)
46 | (config.exclude + config.locale[locale.downcase].exclude).any? do |pattern|
47 | match?(key, pattern)
48 | end
49 | end
50 |
51 | def match?(key, pattern)
52 | if (regex = pattern.to_regexp)
53 | key =~ regex
54 | else
55 | key.start_with?(pattern)
56 | end
57 | end
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/lib/localer/data/service.rb:
--------------------------------------------------------------------------------
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
19 |
--------------------------------------------------------------------------------
/lib/localer/ext/hash.rb:
--------------------------------------------------------------------------------
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
90 |
--------------------------------------------------------------------------------
/lib/localer/ext/string.rb:
--------------------------------------------------------------------------------
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
67 |
--------------------------------------------------------------------------------
/lib/localer/rails.rb:
--------------------------------------------------------------------------------
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 |
16 | I18n.backend.send(:init_translations)
17 | I18n.backend.send(:translations)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/localer/rake_task.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rake'
4 | require 'rake/tasklib'
5 | require 'localer'
6 |
7 | module Localer
8 | # Defines a Rake task for running Localer.
9 | # The simplest use of it goes something like:
10 | #
11 | # Localer::Rakeask.new
12 | # This will define a task named localer described as 'Run Localer'.
13 | class RakeTask < Rake::TaskLib
14 | def initialize(name = :localer, *args) # rubocop:disable Lint/MissingSuper
15 | @name = name
16 | desc 'Run Localer'
17 | task(name, *args) do |_, _task_args|
18 | sh('localer check') do |ok, res|
19 | exit res.exitstatus unless ok
20 | end
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/localer/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Localer
4 | VERSION = "0.2.0"
5 | end
6 |
--------------------------------------------------------------------------------
/localer.gemspec:
--------------------------------------------------------------------------------
1 |
2 | lib = File.expand_path("../lib", __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require "localer/version"
5 |
6 | Gem::Specification.new do |spec|
7 | spec.name = "localer"
8 | spec.version = Localer::VERSION
9 | spec.authors = ["Andrey Deryabin"]
10 | spec.email = ["deriabin@gmail.com"]
11 |
12 | spec.summary = %q{Automatic detecting missing I18n translations tool.}
13 | spec.description = %q{Automatic detecting missing I18n translations tool.}
14 | spec.homepage = "https://github.com/aderyabin/localer"
15 | spec.license = "MIT"
16 | spec.files = `git ls-files -z`.split("\x0").reject do |f|
17 | f.match(%r{^(test|spec|features|gemfiles)/})
18 | end
19 | spec.bindir = "bin"
20 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21 | spec.require_paths = ["lib"]
22 |
23 | spec.add_development_dependency "appraisal"
24 | spec.add_development_dependency "bundler", "~> 1.17"
25 | spec.add_development_dependency "rake", ">= 12.3.3"
26 | spec.add_development_dependency "rspec", "~> 3.0"
27 | spec.add_development_dependency "rubocop", "~> 0.50"
28 | spec.add_development_dependency "cucumber"
29 | spec.add_development_dependency "aruba"
30 |
31 | spec.add_dependency "thor", ">= 0.19"
32 | spec.add_dependency "dry-initializer", ">= 2.0"
33 | end
34 |
--------------------------------------------------------------------------------
/public/localer-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aderyabin/localer/4622d727ab40e6cd0365cfc72d5ddb25abe06edc/public/localer-full.png
--------------------------------------------------------------------------------
/public/localer-full2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aderyabin/localer/4622d727ab40e6cd0365cfc72d5ddb25abe06edc/public/localer-full2.png
--------------------------------------------------------------------------------
/public/localer-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aderyabin/localer/4622d727ab40e6cd0365cfc72d5ddb25abe06edc/public/localer-logo.png
--------------------------------------------------------------------------------
/spec/dummy_app/config.ru:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require ::File.expand_path("../config/environment", __FILE__)
4 |
5 | Rails.application.eager_load!
6 |
7 | run Rails.application
8 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/application.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails"
4 |
5 | class DummyApp < Rails::Application
6 | config.eager_load = false
7 | end
8 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/environment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Load the Rails application.
4 | require File.expand_path('application', __dir__)
5 |
6 | # Initialize the Rails application.
7 | Rails.application.initialize!
8 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/locales/en.rails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | {
4 | en: {
5 | number: {
6 | nth: {
7 | ordinals: lambda do |_key, options|
8 | number = options[:number]
9 | case number
10 | when 1 then "st"
11 | when 2 then "nd"
12 | when 3 then "rd"
13 | when 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 then "th"
14 | else
15 | num_modulo = number.to_i.abs % 100
16 | num_modulo %= 10 if num_modulo > 13
17 | case num_modulo
18 | when 1 then "st"
19 | when 2 then "nd"
20 | when 3 then "rd"
21 | else "th"
22 | end
23 | end
24 | end,
25 |
26 | ordinalized: lambda do |_key, options|
27 | number = options[:number]
28 | "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
29 | end
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/locales/en.rails.yml:
--------------------------------------------------------------------------------
1 | en:
2 | # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
3 | datetime:
4 | distance_in_words:
5 | half_a_minute: "half a minute"
6 | less_than_x_seconds:
7 | one: "less than 1 second"
8 | other: "less than %{count} seconds"
9 | x_seconds:
10 | one: "1 second"
11 | other: "%{count} seconds"
12 | less_than_x_minutes:
13 | one: "less than a minute"
14 | other: "less than %{count} minutes"
15 | x_minutes:
16 | one: "1 minute"
17 | other: "%{count} minutes"
18 | about_x_hours:
19 | one: "about 1 hour"
20 | other: "about %{count} hours"
21 | x_days:
22 | one: "1 day"
23 | other: "%{count} days"
24 | about_x_months:
25 | one: "about 1 month"
26 | other: "about %{count} months"
27 | x_months:
28 | one: "1 month"
29 | other: "%{count} months"
30 | about_x_years:
31 | one: "about 1 year"
32 | other: "about %{count} years"
33 | over_x_years:
34 | one: "over 1 year"
35 | other: "over %{count} years"
36 | almost_x_years:
37 | one: "almost 1 year"
38 | other: "almost %{count} years"
39 | prompts:
40 | year: "Year"
41 | month: "Month"
42 | day: "Day"
43 | hour: "Hour"
44 | minute: "Minute"
45 | second: "Seconds"
46 |
47 | helpers:
48 | select:
49 | # Default value for :prompt => true in FormOptionsHelper
50 | prompt: "Please select"
51 |
52 | # Default translation keys for submit and button FormHelper
53 | submit:
54 | create: 'Create %{model}'
55 | update: 'Update %{model}'
56 | submit: 'Save %{model}'
57 | date:
58 | formats:
59 | # Use the strftime parameters for formats.
60 | # When no format has been given, it uses default.
61 | # You can provide other formats here if you like!
62 | default: "%Y-%m-%d"
63 | short: "%b %d"
64 | long: "%B %d, %Y"
65 |
66 | day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
67 | abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
68 |
69 | # Don't forget the nil at the beginning; there's no such thing as a 0th month
70 | month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
71 | abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
72 | # Used in date_select and datetime_select.
73 | order:
74 | - year
75 | - month
76 | - day
77 |
78 | time:
79 | formats:
80 | default: "%a, %d %b %Y %H:%M:%S %z"
81 | short: "%d %b %H:%M"
82 | long: "%B %d, %Y %H:%M"
83 | am: "am"
84 | pm: "pm"
85 |
86 | # Used in array.to_sentence.
87 | support:
88 | array:
89 | words_connector: ", "
90 | two_words_connector: " and "
91 | last_word_connector: ", and "
92 | number:
93 | # Used in NumberHelper.number_to_delimited()
94 | # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
95 | format:
96 | # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
97 | separator: "."
98 | # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
99 | delimiter: ","
100 | # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
101 | precision: 3
102 | # Determine how rounding is performed (see BigDecimal::mode)
103 | round_mode: default
104 | # If set to true, precision will mean the number of significant digits instead
105 | # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
106 | significant: false
107 | # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
108 | strip_insignificant_zeros: false
109 |
110 | # Used in NumberHelper.number_to_currency()
111 | currency:
112 | format:
113 | # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
114 | format: "%u%n"
115 | unit: "$"
116 | # These five are to override number.format and are optional
117 | separator: "."
118 | delimiter: ","
119 | precision: 2
120 | significant: false
121 | strip_insignificant_zeros: false
122 |
123 | # Used in NumberHelper.number_to_percentage()
124 | percentage:
125 | format:
126 | # These five are to override number.format and are optional
127 | # separator:
128 | delimiter: ""
129 | # precision:
130 | # significant: false
131 | # strip_insignificant_zeros: false
132 | format: "%n%"
133 |
134 | # Used in NumberHelper.number_to_rounded()
135 | precision:
136 | format:
137 | # These five are to override number.format and are optional
138 | # separator:
139 | delimiter: ""
140 | # precision:
141 | # significant: false
142 | # strip_insignificant_zeros: false
143 |
144 | # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
145 | human:
146 | format:
147 | # These five are to override number.format and are optional
148 | # separator:
149 | delimiter: ""
150 | precision: 3
151 | significant: true
152 | strip_insignificant_zeros: true
153 | # Used in number_to_human_size()
154 | storage_units:
155 | # Storage units output formatting.
156 | # %u is the storage unit, %n is the number (default: 2 MB)
157 | format: "%n %u"
158 | units:
159 | byte:
160 | one: "Byte"
161 | other: "Bytes"
162 | kb: "KB"
163 | mb: "MB"
164 | gb: "GB"
165 | tb: "TB"
166 | pb: "PB"
167 | eb: "EB"
168 | # Used in NumberHelper.number_to_human()
169 | decimal_units:
170 | format: "%n %u"
171 | # Decimal units output formatting
172 | # By default we will only quantify some of the exponents
173 | # but the commented ones might be defined or overridden
174 | # by the user.
175 | units:
176 | # femto: Quadrillionth
177 | # pico: Trillionth
178 | # nano: Billionth
179 | # micro: Millionth
180 | # mili: Thousandth
181 | # centi: Hundredth
182 | # deci: Tenth
183 | unit: ""
184 | # ten:
185 | # one: Ten
186 | # other: Tens
187 | # hundred: Hundred
188 | thousand: Thousand
189 | million: Million
190 | billion: Billion
191 | trillion: Trillion
192 | quadrillion: Quadrillion
193 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/locales/ru.rails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | {
4 | ru: {
5 | number: {
6 | nth: {
7 | ordinals: ->(_key, _options) {},
8 | ordinalized: ->(_key, options) {}
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/locales/ru.rails.yml:
--------------------------------------------------------------------------------
1 | ru:
2 | date:
3 | abbr_day_names:
4 | -
5 | abbr_month_names:
6 | -
7 | day_names:
8 | -
9 | month_names:
10 | -
11 | order:
12 | -
13 | formats:
14 | default: "%d.%m.%Y"
15 | long: "%-d %B %Y"
16 | short: "%-d %b"
17 | time:
18 | am: утра
19 | formats:
20 | default: "%a, %d %b %Y, %H:%M:%S %z"
21 | long: "%d %B %Y, %H:%M"
22 | short: "%d %b, %H:%M"
23 | pm: вечера
24 | datetime:
25 | prompts:
26 | day: День
27 | hour: Часов
28 | minute: Минут
29 | month: Месяц
30 | second: Секунд
31 | year: Год
32 | distance_in_words:
33 | about_x_hours:
34 | one: около %{count} часа
35 | other: около %{count} часа
36 | about_x_months:
37 | one: около %{count} месяца
38 | other: около %{count} месяца
39 | about_x_years:
40 | one: около %{count} года
41 | other: около %{count} лет
42 | almost_x_years:
43 | one: почти %{count} год
44 | other: почти %{count} лет
45 | half_a_minute: меньше минуты
46 | less_than_x_minutes:
47 | one: меньше %{count} минуты
48 | other: меньше %{count} минуты
49 | less_than_x_seconds:
50 | one: меньше %{count} секунды
51 | other: меньше %{count} секунды
52 | over_x_years:
53 | one: больше %{count} года
54 | other: больше %{count} лет
55 | x_days:
56 | one: "%{count} день"
57 | other: "%{count} дня"
58 | x_minutes:
59 | one: "%{count} минуту"
60 | other: "%{count} минуты"
61 | x_months:
62 | one: "%{count} месяц"
63 | other: "%{count} месяца"
64 | x_seconds:
65 | one: "%{count} секунду"
66 | other: "%{count} секунды"
67 | number:
68 | precision:
69 | format:
70 | delimiter: ''
71 | format:
72 | delimiter: " "
73 | precision: 3
74 | separator: ","
75 | round_mode: default
76 | significant: false
77 | strip_insignificant_zeros: false
78 | percentage:
79 | format:
80 | delimiter: ''
81 | format: "%n%"
82 | human:
83 | format:
84 | delimiter: ''
85 | precision: 1
86 | significant: false
87 | strip_insignificant_zeros: false
88 | decimal_units:
89 | format: "%n %u"
90 | units:
91 | unit: ''
92 | billion: миллиард
93 | million: миллион
94 | quadrillion: квадриллион
95 | thousand: тысяча
96 | trillion: триллион
97 | storage_units:
98 | format: "%n %u"
99 | units:
100 | byte:
101 | one: байт
102 | other: байта
103 | gb: ГБ
104 | kb: КБ
105 | mb: МБ
106 | tb: ТБ
107 | pb: ""
108 | eb: ""
109 |
110 | currency:
111 | format:
112 | delimiter: " "
113 | format: "%n %u"
114 | precision: 2
115 | separator: ","
116 | significant: false
117 | strip_insignificant_zeros: false
118 | unit: руб.
119 | helpers:
120 | select:
121 | prompt: 'Выберите: '
122 | submit:
123 | create: Создать %{model}
124 | submit: Сохранить %{model}
125 | update: Сохранить %{model}
126 | support:
127 | array:
128 | last_word_connector: " и "
129 | two_words_connector: " и "
130 | words_connector: ", "
131 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/locales/us.rails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | {
4 | us: {
5 | number: {
6 | nth: {
7 | ordinals: lambda do |_key, options|
8 | number = options[:number]
9 | case number
10 | when 1 then "st"
11 | when 2 then "nd"
12 | when 3 then "rd"
13 | when 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 then "th"
14 | else
15 | num_modulo = number.to_i.abs % 100
16 | num_modulo %= 10 if num_modulo > 13
17 | case num_modulo
18 | when 1 then "st"
19 | when 2 then "nd"
20 | when 3 then "rd"
21 | else "th"
22 | end
23 | end
24 | end,
25 |
26 | ordinalized: lambda do |_key, options|
27 | number = options[:number]
28 | "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
29 | end
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/spec/dummy_app/config/locales/us.rails.yml:
--------------------------------------------------------------------------------
1 | us:
2 | # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
3 | datetime:
4 | distance_in_words:
5 | half_a_minute: "half a minute"
6 | less_than_x_seconds:
7 | one: "less than 1 second"
8 | other: "less than %{count} seconds"
9 | x_seconds:
10 | one: "1 second"
11 | other: "%{count} seconds"
12 | less_than_x_minutes:
13 | one: "less than a minute"
14 | other: "less than %{count} minutes"
15 | x_minutes:
16 | one: "1 minute"
17 | other: "%{count} minutes"
18 | about_x_hours:
19 | one: "about 1 hour"
20 | other: "about %{count} hours"
21 | x_days:
22 | one: "1 day"
23 | other: "%{count} days"
24 | about_x_months:
25 | one: "about 1 month"
26 | other: "about %{count} months"
27 | x_months:
28 | one: "1 month"
29 | other: "%{count} months"
30 | about_x_years:
31 | one: "about 1 year"
32 | other: "about %{count} years"
33 | over_x_years:
34 | one: "over 1 year"
35 | other: "over %{count} years"
36 | almost_x_years:
37 | one: "almost 1 year"
38 | other: "almost %{count} years"
39 | prompts:
40 | year: "Year"
41 | month: "Month"
42 | day: "Day"
43 | hour: "Hour"
44 | minute: "Minute"
45 | second: "Seconds"
46 |
47 | helpers:
48 | select:
49 | # Default value for :prompt => true in FormOptionsHelper
50 | prompt: "Please select"
51 |
52 | # Default translation keys for submit and button FormHelper
53 | submit:
54 | create: 'Create %{model}'
55 | update: 'Update %{model}'
56 | submit: 'Save %{model}'
57 | date:
58 | formats:
59 | # Use the strftime parameters for formats.
60 | # When no format has been given, it uses default.
61 | # You can provide other formats here if you like!
62 | default: "%Y-%m-%d"
63 | short: "%b %d"
64 | long: "%B %d, %Y"
65 |
66 | day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
67 | abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
68 |
69 | # Don't forget the nil at the beginning; there's no such thing as a 0th month
70 | month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
71 | abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
72 | # Used in date_select and datetime_select.
73 | order:
74 | - year
75 | - month
76 | - day
77 |
78 | time:
79 | formats:
80 | default: "%a, %d %b %Y %H:%M:%S %z"
81 | short: "%d %b %H:%M"
82 | long: "%B %d, %Y %H:%M"
83 | am: "am"
84 | pm: "pm"
85 |
86 | # Used in array.to_sentence.
87 | support:
88 | array:
89 | words_connector: ", "
90 | two_words_connector: " and "
91 | last_word_connector: ", and "
92 | number:
93 | # Used in NumberHelper.number_to_delimited()
94 | # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
95 | format:
96 | # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
97 | separator: "."
98 | # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
99 | delimiter: ","
100 | # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
101 | precision: 3
102 | round_mode: default
103 | # If set to true, precision will mean the number of significant digits instead
104 | # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
105 | significant: false
106 | # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
107 | strip_insignificant_zeros: false
108 |
109 | # Used in NumberHelper.number_to_currency()
110 | currency:
111 | format:
112 | # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
113 | format: "%u%n"
114 | unit: "$"
115 | # These five are to override number.format and are optional
116 | separator: "."
117 | delimiter: ","
118 | precision: 2
119 | significant: false
120 | strip_insignificant_zeros: false
121 |
122 | # Used in NumberHelper.number_to_percentage()
123 | percentage:
124 | format:
125 | # These five are to override number.format and are optional
126 | # separator:
127 | delimiter: ""
128 | # precision:
129 | # significant: false
130 | # strip_insignificant_zeros: false
131 | format: "%n%"
132 |
133 | # Used in NumberHelper.number_to_rounded()
134 | precision:
135 | format:
136 | # These five are to override number.format and are optional
137 | # separator:
138 | delimiter: ""
139 | # precision:
140 | # significant: false
141 | # strip_insignificant_zeros: false
142 |
143 | # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
144 | human:
145 | format:
146 | # These five are to override number.format and are optional
147 | # separator:
148 | delimiter: ""
149 | precision: 3
150 | significant: true
151 | strip_insignificant_zeros: true
152 | # Used in number_to_human_size()
153 | storage_units:
154 | # Storage units output formatting.
155 | # %u is the storage unit, %n is the number (default: 2 MB)
156 | format: "%n %u"
157 | units:
158 | byte:
159 | one: "Byte"
160 | other: "Bytes"
161 | kb: "KB"
162 | mb: "MB"
163 | gb: "GB"
164 | tb: "TB"
165 | pb: "PB"
166 | eb: "EB"
167 | # Used in NumberHelper.number_to_human()
168 | decimal_units:
169 | format: "%n %u"
170 | # Decimal units output formatting
171 | # By default we will only quantify some of the exponents
172 | # but the commented ones might be defined or overridden
173 | # by the user.
174 | units:
175 | # femto: Quadrillionth
176 | # pico: Trillionth
177 | # nano: Billionth
178 | # micro: Millionth
179 | # mili: Thousandth
180 | # centi: Hundredth
181 | # deci: Tenth
182 | unit: ""
183 | # ten:
184 | # one: Ten
185 | # other: Tens
186 | # hundred: Hundred
187 | thousand: Thousand
188 | million: Million
189 | billion: Billion
190 | trillion: Trillion
191 | quadrillion: Quadrillion
192 |
--------------------------------------------------------------------------------
/spec/localer_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe Localer do
4 | it "has a version number" do
5 | expect(Localer::VERSION).not_to be nil
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "bundler/setup"
4 | require "localer"
5 |
6 | RSpec.configure do |config|
7 | # Disable RSpec exposing methods globally on `Module` and `main`
8 | config.disable_monkey_patching!
9 |
10 | config.expect_with :rspec do |c|
11 | c.syntax = :expect
12 | end
13 | end
14 |
--------------------------------------------------------------------------------