rubocop-i18n 1.3.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1519b7393133fbbd4edcc8aa1fdeaa46bd93155b
4
- data.tar.gz: 2e16dc911733af1dc139d31b69c383b226c3db51
2
+ SHA256:
3
+ metadata.gz: 900bad082ace7d17bf13e8a01691f33c021fb683f5bf4963b1a4f217d98720c8
4
+ data.tar.gz: df4f70a869ae98441bb1d98b8b9365a8a06368ffeb6b63c16138291ff241fb32
5
5
  SHA512:
6
- metadata.gz: 9f2110409579d9d8eae8d3d5501a841d6b75dae69b66048563808bb4ed40391256360d48c8700851979d32bf171757469429f5d3a98c7f0a26c4b879ac71ff7d
7
- data.tar.gz: 8b1fde24e8b0368d2e7139369e3a94e799266f3be6bbb7b79c4bd3a887118adc5c6023ade4413183791856b0dc473e305258e772a1445acb71d3438e32abfd6d
6
+ metadata.gz: 666a5de6a09a035d64937dcdf1441faaef249ebc6df44572f37d858ffdf00f2509abc56581900fb839d857e2e4da2c7a9e71f0d20476ab67c8d83022670052b7
7
+ data.tar.gz: 9c5cc9f02c4408c8f3a2667125b65bb1a6385bf7d885b38787521961646240d4684d81988592298009ec54660f3533f8722b8c1118809528f2d6641f0ac808b2
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ **Expected behavior**
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ **Screenshots**
24
+ If applicable, add screenshots to help explain your problem.
25
+
26
+ **Desktop (please complete the following information):**
27
+ - OS: [e.g. iOS]
28
+ - Browser [e.g. chrome, safari]
29
+ - Version [e.g. 22]
30
+
31
+ **Smartphone (please complete the following information):**
32
+ - Device: [e.g. iPhone6]
33
+ - OS: [e.g. iOS8.1]
34
+ - Browser [e.g. stock browser, safari]
35
+ - Version [e.g. 22]
36
+
37
+ **Additional context**
38
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -1,3 +1,11 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
  require:
3
3
  - rubocop/cop/internal_affairs
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 2.5
7
+
8
+ Metrics/BlockLength:
9
+ Exclude:
10
+ # Exclude the spec directory because the rspec DSL results in long blocks
11
+ - 'spec/**/*'
@@ -1,43 +1,50 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2018-06-12 15:16:26 -0700 using RuboCop version 0.57.2.
3
+ # on 2020-10-25 09:49:30 UTC using RuboCop version 1.0.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 6
9
+ # Offense count: 1
10
+ # Configuration parameters: Include.
11
+ # Include: **/*.gemspec
12
+ Gemspec/RequiredRubyVersion:
13
+ Exclude:
14
+ - 'rubocop-i18n.gemspec'
15
+
16
+ # Offense count: 1
17
+ # Cop supports --auto-correct.
10
18
  Lint/InterpolationCheck:
11
19
  Exclude:
12
- - 'spec/rubocop/cop/i18n/gettext/decorate_string_formatting_using_interpolation_spec.rb'
13
20
  - 'spec/rubocop/cop/i18n/gettext/decorate_string_spec.rb'
14
21
 
15
- # Offense count: 4
22
+ # Offense count: 2
23
+ # Configuration parameters: IgnoredMethods.
16
24
  Metrics/AbcSize:
17
25
  Max: 29
18
26
 
19
- # Offense count: 6
20
- # Configuration parameters: CountComments, ExcludedMethods.
21
- Metrics/BlockLength:
22
- Max: 61
23
-
24
27
  # Offense count: 1
25
- # Configuration parameters: CountComments.
28
+ # Configuration parameters: CountComments, CountAsOne.
26
29
  Metrics/ClassLength:
27
- Max: 106
30
+ Max: 108
28
31
 
29
- # Offense count: 3
32
+ # Offense count: 5
33
+ # Configuration parameters: IgnoredMethods.
30
34
  Metrics/CyclomaticComplexity:
31
35
  Max: 8
32
36
 
33
37
  # Offense count: 7
34
- # Configuration parameters: CountComments.
38
+ # Configuration parameters: CountComments, CountAsOne, ExcludedMethods.
35
39
  Metrics/MethodLength:
36
40
  Max: 25
37
41
 
38
- # Offense count: 2
39
- Metrics/PerceivedComplexity:
40
- Max: 8
42
+ # Offense count: 1
43
+ # Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, Regex, IgnoreExecutableScripts, AllowedAcronyms.
44
+ # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
45
+ Naming/FileName:
46
+ Exclude:
47
+ - 'lib/rubocop-i18n.rb'
41
48
 
42
49
  # Offense count: 2
43
50
  Style/Documentation:
@@ -47,35 +54,38 @@ Style/Documentation:
47
54
  - 'lib/rubocop/cop/i18n/gettext.rb'
48
55
  - 'lib/rubocop/cop/i18n/gettext/decorate_function_message.rb'
49
56
 
50
- # Offense count: 11
51
- # Configuration parameters: EnforcedStyle.
57
+ # Offense count: 12
58
+ # Configuration parameters: MaxUnannotatedPlaceholdersAllowed.
52
59
  # SupportedStyles: annotated, template, unannotated
53
60
  Style/FormatStringToken:
54
- Exclude:
55
- - 'spec/rubocop/cop/i18n/gettext/decorate_function_message_spec.rb'
56
- - 'spec/rubocop/cop/i18n/gettext/decorate_string_formatting_using_interpolation_spec.rb'
57
- - 'spec/rubocop/cop/i18n/gettext/decorate_string_formatting_using_percent_spec.rb'
58
- - 'spec/rubocop/cop/i18n/gettext/decorate_string_spec.rb'
61
+ EnforcedStyle: template
59
62
 
60
- # Offense count: 3
63
+ # Offense count: 2
61
64
  # Configuration parameters: MinBodyLength.
62
65
  Style/GuardClause:
63
66
  Exclude:
64
67
  - 'lib/rubocop/cop/i18n/gettext/decorate_function_message.rb'
65
- - 'lib/rubocop/cop/i18n/gettext/decorate_string_formatting_using_interpolation.rb'
66
68
  - 'lib/rubocop/cop/i18n/gettext/decorate_string_formatting_using_percent.rb'
67
69
 
68
- # Offense count: 10
70
+ # Offense count: 6
69
71
  # Cop supports --auto-correct.
70
72
  Style/IfUnlessModifier:
71
73
  Exclude:
72
74
  - 'lib/rubocop/cop/i18n/gettext/decorate_function_message.rb'
73
- - 'lib/rubocop/cop/i18n/gettext/decorate_string_formatting_using_interpolation.rb'
74
75
  - 'lib/rubocop/cop/i18n/gettext/decorate_string_formatting_using_percent.rb'
75
- - 'lib/rubocop/rspec/cop_helper.rb'
76
76
 
77
- # Offense count: 93
78
- # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
77
+ # Offense count: 5
78
+ # Configuration parameters: AllowModifier.
79
+ Style/SoleNestedConditional:
80
+ Exclude:
81
+ - 'lib/rubocop/cop/i18n/gettext/decorate_function_message.rb'
82
+ - 'lib/rubocop/cop/i18n/gettext/decorate_string.rb'
83
+ - 'lib/rubocop/cop/i18n/gettext/decorate_string_formatting_using_percent.rb'
84
+ - 'lib/rubocop/cop/i18n/rails_i18n/decorate_string.rb'
85
+
86
+ # Offense count: 50
87
+ # Cop supports --auto-correct.
88
+ # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
79
89
  # URISchemes: http, https
80
- Metrics/LineLength:
90
+ Layout/LineLength:
81
91
  Max: 174
@@ -0,0 +1 @@
1
+ 2.7.2
@@ -1,7 +1,12 @@
1
+ ---
2
+ os: linux
3
+ dist: xenial
1
4
  language: ruby
2
- matrix:
5
+ rvm:
6
+ - 2.5.8
7
+ - 2.6.6
8
+ - 2.7.2
9
+ jobs:
3
10
  fast_finish: true
4
- include:
5
- - rvm: 2.4.1
6
11
  notifications:
7
- email: false
12
+ email: true
@@ -1,6 +1,31 @@
1
1
  # Change Log
2
2
 
3
- ## master (unreleased)
3
+ ### [master (Unreleased)](https://github.com/puppetlabs/rubocop-i18n/compare/v3.0.0...master)
4
+ ### [3.0.0](https://github.com/puppetlabs/rubocop-i18n/compare/v2.0.2...v3.0.0)
5
+
6
+ * Update Rubocop version to 1.0! Thank you @mvz!
7
+ * Cop namespace fixes, documentation updates, loading issues. Thank you again @mvz !
8
+ * Gem maintainership has been passed to @highb. Thank you @binford2k and @lucywyman!
9
+ * Update Ruby versions in gemspec, Travis, .ruby-version. Thank you @sfeuga!
10
+
11
+ ### [2.0.2](https://github.com/puppetlabs/rubocop-i18n/compare/v2.0.1...v2.0.2)
12
+
13
+ * Add auto-correct for `DecorateString` (#40) Thanks @mvz!
14
+ * Update rake and bundler requirements to be more permissive of newer versions (#43)
15
+
16
+ ### 2.0.1
17
+
18
+ * `chmod` all the files to be world readable and ensured that `gem
19
+ build` doesn't emit any warnings.
20
+ * fixes license name
21
+ * specifies version for pry and rb-readline
22
+ * bump Z version
23
+
24
+ ### 2.0.0
25
+
26
+ * Add rails-i18n support and documentation in README. Thanks @kbacha!
27
+
28
+ ### 1.3.1, 1.3.0, 1.2.0
4
29
 
5
30
  * Updated DecorateString to look for sentences using a regular expression that should be decorated. This limits the number of strings that it finds to things that look like a sentence.
6
31
  * Code restructure (no API changes)
@@ -0,0 +1,5 @@
1
+ # These owners will be the default owners for everything in
2
+ # the repo. Unless a later match takes precedence,
3
+ # @global-owner1 and @global-owner2 will be requested for
4
+ # review when someone opens a pull request.
5
+ * @highb @puppetlabs/open-source-stewards
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in rubocop-i18n.gemspec
data/README.md CHANGED
@@ -1,7 +1,14 @@
1
1
  # Rubocop::I18n
2
2
 
3
+ [![Build Status](https://travis-ci.org/puppetlabs/rubocop-i18n.svg?branch=master)](https://travis-ci.org/puppetlabs/rubocop-i18n)
4
+
3
5
  A set of cops for detecting strings that need i18n decoration in your project.
4
6
 
7
+ Supports the following framework styles:
8
+
9
+ - gettext
10
+ - rails-i18n
11
+
5
12
  ## Installation
6
13
 
7
14
  Add this line to your application's Gemfile:
@@ -25,19 +32,35 @@ In your `rubocop.yml`:
25
32
  require:
26
33
  - rubocop-i18n
27
34
  ...
28
- GetText/DecorateString:
35
+ # You *must* choose GetText or Rails-i18n style checking
36
+ # If you want GetText-style checking
37
+ I18n/GetText:
29
38
  Enabled: true
30
- GetText/DecorateFunctionMessage:
31
- Enabled: true
32
- GetText/DecorateStringFormattingUsingInterpolation
33
- Enabled: true
34
- GetText/DecorateStringFormattingUsingPercent
39
+ I18n/RailsI18n:
40
+ Enabled: false
41
+ # If you want rails-i18n-style checking
42
+ I18n/RailsI18n:
35
43
  Enabled: true
44
+ I18n/GetText:
45
+ Enabled: false
46
+ # If you want custom control of all the cops
47
+ I18n/GetText/DecorateString:
48
+ Enabled: false
49
+ # Disable the autocorrect
50
+ AutoCorrect: false
51
+ I18n/GetText/DecorateFunctionMessage:
52
+ Enabled: false
53
+ I18n/GetText/DecorateStringFormattingUsingInterpolation:
54
+ Enabled: false
55
+ I18n/GetText/DecorateStringFormattingUsingPercent:
56
+ Enabled: false
57
+ I18n/RailsI18n/DecorateString:
58
+ Enabled: false
36
59
  ```
37
60
 
38
61
  ## Cops
39
62
 
40
- ### GetText/DecorateString
63
+ ### I18n/GetText/DecorateString
41
64
 
42
65
  This cop is looks for strings that appear to be sentences but are not decorated.
43
66
  Sentences are determined by the STRING_REGEXP.
@@ -67,7 +90,7 @@ _("Result is good.")
67
90
  "a string that doesn't start with a capital letter."
68
91
  ```
69
92
 
70
- ### GetText/DecorateFunctionMessage
93
+ ### I18n/GetText/DecorateFunctionMessage
71
94
 
72
95
  This cop looks for any raise or fail functions and checks that the user visible message is using gettext decoration with the _() function.
73
96
  This cop makes sure the message is decorated, as well as checking that the formatting of the message is compliant according to the follow rules.
@@ -183,7 +206,7 @@ raise(someOtherFuntioncall(foo, "bar"))
183
206
  In this raise or fail function, the message does not contain any decoration at all and the message is not a simple string. It may make sense to convert the message to a simple string. eg [Simple decoration of a message](#Simple-decoration-of-a-message).
184
207
  Or ignore this raise or fail function following this [How to ignore rules in code](#How-to-ignore-rules-in-code) section.
185
208
 
186
- ### GetText/DecorateStringFormattingUsingInterpolation
209
+ ### I18n/GetText/DecorateStringFormattingUsingInterpolation
187
210
 
188
211
  This cop looks for decorated gettext methods _() and checks that all strings contained
189
212
  within do not use string interpolation '#{}'
@@ -210,7 +233,7 @@ puts _("a message with a #{'interpolation'}")
210
233
  puts _("a message that is %{type}") % { type: 'translatable' }
211
234
  ```
212
235
 
213
- ### GetText/DecorateStringFormattingUsingPercent
236
+ ### I18n/GetText/DecorateStringFormattingUsingPercent
214
237
 
215
238
  This cop looks for decorated gettext methods _() and checks that all strings contained
216
239
  within do not use sprintf formatting '%s' etc
@@ -233,14 +256,66 @@ raise(_("Warning is %s") % ['bad'])
233
256
  raise(_("Warning is %{value}") % { value: 'bad' })
234
257
  ```
235
258
 
259
+ ### I18n/RailsI18n/DecorateString
260
+
261
+ This cop looks for decorated rails-i18n methods.
262
+
263
+ ##### Error message thrown
264
+
265
+ ```
266
+ decorator is missing around sentence
267
+ ```
268
+
269
+ ##### Bad
270
+
271
+ ``` ruby
272
+ raise("Warning is %s" % ['bad'])
273
+ ```
274
+
275
+ ##### Good
276
+
277
+ ``` ruby
278
+ raise(t("Warning is %{value}") % { value: 'good' })
279
+ raise(translate("Warning is %{value}") % { value: 'good' })
280
+ raise(I18n.t("Warning is %{value}") % { value: 'good' })
281
+ ```
282
+
283
+ ### I18n/RailsI18n/DecorateStringFormattingUsingInterpolation
284
+
285
+ This cop looks for decorated rails-i18n methods like `t()` and `translate()` and checks that all strings contained
286
+ within do not use string interpolation '#{}'
287
+
288
+ #### Simple decoration of a message
289
+
290
+ Simple message strings should be decorated with the t() function
291
+
292
+ ##### Error message thrown
293
+
294
+ ```
295
+ 't' function, message key string should not contain #{} formatting
296
+ ```
297
+
298
+ ##### Bad
299
+
300
+ ``` ruby
301
+ puts t("path.to.key.with.#{'interpolation'}")
302
+ ```
303
+
304
+ ##### Good
305
+
306
+ ``` ruby
307
+ puts t("path.to.key.with.interpolation")
308
+ ```
309
+
236
310
  ## How to ignore rules in code
237
311
 
238
312
  It may be necessary to ignore a cop for a particular piece of code. We follow standard rubocop idioms.
239
313
  ``` ruby
240
- raise("We don't want this translated.") # rubocop:disable GetText/DecorateString
241
- raise("We don't want this translated") # rubocop:disable GetText/DecorateFunctionMessage
242
- raise(_("We don't want this translated #{crazy}") # rubocop:disable GetText/DecorateStringFormattingUsingInterpolation)
243
- raise(_("We don't want this translated %s") % ['crazy'] # rubocop:disable GetText/DecorateStringFormattingUsingPercent)
314
+ raise("We don't want this translated.") # rubocop:disable I18n/GetText/DecorateString
315
+ raise("We don't want this translated.") # rubocop:disable I18n/RailsI18n/DecorateString
316
+ raise("We don't want this translated") # rubocop:disable I18n/GetText/DecorateFunctionMessage
317
+ raise(_("We don't want this translated #{crazy}") # rubocop:disable I18n/GetText/DecorateStringFormattingUsingInterpolation)
318
+ raise(_("We don't want this translated %s") % ['crazy'] # rubocop:disable I18n/GetText/DecorateStringFormattingUsingPercent)
244
319
  ```
245
320
 
246
321
  ## Known Issues
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
5
  require 'rubocop/i18n'
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubocop/cop/i18n'
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubocop'
2
4
  require 'rubocop/cop/i18n/gettext'
3
5
  require 'rubocop/cop/i18n/gettext/decorate_string'
4
6
  require 'rubocop/cop/i18n/gettext/decorate_function_message'
5
7
  require 'rubocop/cop/i18n/gettext/decorate_string_formatting_using_interpolation'
6
8
  require 'rubocop/cop/i18n/gettext/decorate_string_formatting_using_percent'
9
+
10
+ require 'rubocop/cop/i18n/rails_i18n'
11
+ require 'rubocop/cop/i18n/rails_i18n/decorate_string'
12
+ require 'rubocop/cop/i18n/rails_i18n/decorate_string_formatting_using_interpolation'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RuboCop
2
4
  module Cop
3
5
  module I18n
@@ -6,7 +8,9 @@ module RuboCop
6
8
  def on_send(node)
7
9
  method_name = node.loc.selector.source
8
10
  return unless GetText.supported_method?(method_name)
9
- _, method_name, *arg_nodes = *node
11
+
12
+ method_name = node.method_name
13
+ arg_nodes = node.arguments
10
14
  if !arg_nodes.empty? && !already_decorated?(node) && (contains_string?(arg_nodes) || string_constant?(arg_nodes))
11
15
  message_section = if string_constant?(arg_nodes)
12
16
  arg_nodes[1]
@@ -51,7 +55,8 @@ module RuboCop
51
55
  def detect_and_report(_node, message_section, method_name)
52
56
  errors = how_bad_is_it(message_section)
53
57
  return if errors.empty?
54
- error_message = "'#{method_name}' function, "
58
+
59
+ error_message = ["'#{method_name}' function, "]
55
60
  errors.each do |error|
56
61
  error_message << 'message string should be decorated. ' if error == :simple
57
62
  error_message << 'message should not be a concatenated string. ' if error == :concatenation
@@ -59,6 +64,7 @@ module RuboCop
59
64
  error_message << 'message should use correctly formatted interpolation. ' if error == :interpolation
60
65
  error_message << 'message should be decorated. ' if error == :no_decoration
61
66
  end
67
+ error_message = error_message.join('\n')
62
68
  add_offense(message_section, message: error_message)
63
69
  end
64
70
 
@@ -115,6 +121,7 @@ module RuboCop
115
121
  # dstrs are split into "str" segments and other segments.
116
122
  # The "other" segments are the interpolated values.
117
123
  next unless child.begin_type?
124
+
118
125
  value = child.children[0]
119
126
  hash_key = 'value'
120
127
  if value.lvar_type?
@@ -20,7 +20,7 @@ module RuboCop
20
20
  #
21
21
  # _("Result is good.")
22
22
  class DecorateString < Cop
23
- STRING_REGEXP = /^\s*[[:upper:]][[:alpha:]]*[[:blank:]]+.*[.!?]$/
23
+ STRING_REGEXP = /^\s*[[:upper:]][[:alpha:]]*[[:blank:]]+.*[.!?]$/.freeze
24
24
 
25
25
  def on_dstr(node)
26
26
  check_for_parent_decorator(node) if dstr_contains_sentence?(node)
@@ -37,6 +37,10 @@ module RuboCop
37
37
  check_for_parent_decorator(node)
38
38
  end
39
39
 
40
+ def autocorrect(node)
41
+ single_string_correct(node) if node.str_type?
42
+ end
43
+
40
44
  private
41
45
 
42
46
  def sentence?(node)
@@ -63,11 +67,18 @@ module RuboCop
63
67
  if parent.respond_to?(:type) && parent.send_type?
64
68
  method_name = parent.loc.selector.source
65
69
  return if GetText.supported_decorator?(method_name)
66
- elsif parent.respond_to?(:method_name) && parent.method_name == :[]
70
+ elsif parent.respond_to?(:method_name) && parent.method?(:[])
67
71
  return
68
72
  end
69
73
  add_offense(node, message: 'decorator is missing around sentence')
70
74
  end
75
+
76
+ def single_string_correct(node)
77
+ lambda { |corrector|
78
+ corrector.insert_before(node.source_range, '_(')
79
+ corrector.insert_after(node.source_range, ')')
80
+ }
81
+ end
71
82
  end
72
83
  end
73
84
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RuboCop
2
4
  module Cop
3
5
  module I18n
@@ -24,11 +26,13 @@ module RuboCop
24
26
  def on_send(node)
25
27
  decorator_name = node.loc.selector.source
26
28
  return unless GetText.supported_decorator?(decorator_name)
27
- _, method_name, *arg_nodes = *node
28
- if !arg_nodes.empty? && contains_string_formatting_with_interpolation?(arg_nodes)
29
- message_section = arg_nodes[0]
30
- add_offense(message_section, message: "'#{method_name}' function, message string should not contain \#{} formatting")
31
- end
29
+
30
+ method_name = node.method_name
31
+ arg_nodes = node.arguments
32
+ return unless !arg_nodes.empty? && contains_string_formatting_with_interpolation?(arg_nodes)
33
+
34
+ message_section = arg_nodes[0]
35
+ add_offense(message_section, message: "'#{method_name}' function, message string should not contain \#{} formatting")
32
36
  end
33
37
 
34
38
  private
@@ -38,19 +42,12 @@ module RuboCop
38
42
  end
39
43
 
40
44
  def contains_string_formatting_with_interpolation?(node)
41
- if node.is_a?(Array)
42
- return node.any? { |n| contains_string_formatting_with_interpolation?(n) }
43
- end
44
-
45
- if node.respond_to?(:type)
46
- if node.str_type? || node.dstr_type?
47
- return string_contains_interpolation_format?(node.source)
48
- end
49
- end
50
-
51
- if node.respond_to?(:children)
52
- return node.children.any? { |child| contains_string_formatting_with_interpolation?(child) }
53
- end
45
+ return node.any? { |n| contains_string_formatting_with_interpolation?(n) } if node.is_a?(Array)
46
+
47
+ return string_contains_interpolation_format?(node.source) if node.respond_to?(:type) && (node.str_type? || node.dstr_type?)
48
+
49
+ return node.children.any? { |child| contains_string_formatting_with_interpolation?(child) } if node.respond_to?(:children)
50
+
54
51
  false
55
52
  end
56
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RuboCop
2
4
  module Cop
3
5
  module I18n
@@ -27,7 +29,9 @@ module RuboCop
27
29
  def on_send(node)
28
30
  decorator_name = node.loc.selector.source
29
31
  return unless GetText.supported_decorator?(decorator_name)
30
- _, method_name, *arg_nodes = *node
32
+
33
+ method_name = node.method_name
34
+ arg_nodes = node.arguments
31
35
  if !arg_nodes.empty? && contains_string_with_percent_format?(arg_nodes)
32
36
  message_section = arg_nodes[0]
33
37
  add_offense(message_section, message: "'#{method_name}' function, message string should not contain sprintf style formatting (ie %s)")
@@ -54,6 +58,7 @@ module RuboCop
54
58
  if node.respond_to?(:children)
55
59
  return node.children.any? { |child| contains_string_with_percent_format?(child) }
56
60
  end
61
+
57
62
  false
58
63
  end
59
64
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module I18n
6
+ # The Rails I18n module contains cops used to lint and enforce the use of strings
7
+ # in rails applications that want to use the I18n gem.
8
+ module RailsI18n
9
+ def self.supported_decorators
10
+ %w[
11
+ t
12
+ t!
13
+ translate
14
+ translate!
15
+ ].freeze
16
+ end
17
+
18
+ def self.supported_decorator?(decorator_name)
19
+ supported_decorators.include?(decorator_name)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module I18n
6
+ module RailsI18n
7
+ # This cop is looks for strings that appear to be sentences but are not decorated.
8
+ # Sentences are determined by the SENTENCE_REGEXP. (Upper case character, at least one space,
9
+ # and sentence punctuation at the end)
10
+ #
11
+ # @example
12
+ #
13
+ # # bad
14
+ #
15
+ # "Result is bad."
16
+ #
17
+ # @example
18
+ #
19
+ # # good
20
+ #
21
+ # t("result_is_good")
22
+ # I18n.t("result_is_good")
23
+ #
24
+ # There are several options for configuration.
25
+ #
26
+ # @example IgnoreExceptions: true
27
+ # # OK
28
+ #
29
+ # raise "Some string sentence"
30
+ #
31
+ # @example EnforcedSentenceType: sentence
32
+ # # bad
33
+ #
34
+ # "Result is bad."
35
+ #
36
+ # # good
37
+ #
38
+ # t("result_is_good")
39
+ # I18n.t("result_is_good")
40
+ #
41
+ # @example EnforcedSentenceType: fragmented_sentence
42
+ # # bad
43
+ #
44
+ # "Result is bad" # Contains a capital to start
45
+ # "result is bad." # Ends in punctuation
46
+ #
47
+ # # good
48
+ #
49
+ # t("result_is_good")
50
+ # I18n.t("result_is_good")
51
+ #
52
+ # @example EnforcedSentenceType: fragment
53
+ # # bad
54
+ #
55
+ # "result is bad" # A series of words
56
+ #
57
+ # # good
58
+ #
59
+ # t("result_is_good")
60
+ # I18n.t("result_is_good")
61
+ #
62
+ # @example Regexp: ^only-this-text$
63
+ #
64
+ # # bad
65
+ #
66
+ # "only-this-text"
67
+ #
68
+ # # good
69
+ #
70
+ # "Any other string is fine now"
71
+ # t("only_this_text")
72
+ #
73
+ class DecorateString < Cop
74
+ SENTENCE_REGEXP = /^\s*[[:upper:]][[:alpha:]]*[[:blank:]]+.*[.!?]$/.freeze
75
+ FRAGMENTED_SENTENCE_REGEXP = /^\s*([[:upper:]][[:alpha:]]*[[:blank:]]+.*)|([[:alpha:]]*[[:blank:]]+.*[.!?])$/.freeze
76
+ FRAGMENT_REGEXP = /^\s*[[:alpha:]]*[[:blank:]]+.*$/.freeze
77
+ SUPPORTED_DECORATORS = %w[
78
+ t
79
+ t!
80
+ translate
81
+ translate!
82
+ ].freeze
83
+
84
+ def on_dstr(node)
85
+ check_for_parent_decorator(node) if dstr_contains_sentence?(node)
86
+ end
87
+
88
+ def on_str(node)
89
+ return unless sentence?(node)
90
+
91
+ parent = node.parent
92
+ if parent.respond_to?(:type)
93
+ return if parent.regexp_type? || parent.dstr_type?
94
+ end
95
+
96
+ check_for_parent_decorator(node)
97
+ end
98
+
99
+ private
100
+
101
+ def sentence?(node)
102
+ child = node.children[0]
103
+ if child.is_a?(String)
104
+ if child.valid_encoding?
105
+ child.encode(Encoding::UTF_8).chomp =~ regexp
106
+ else
107
+ false
108
+ end
109
+ elsif child.respond_to?(:type) && child.str_type?
110
+ sentence?(child)
111
+ else
112
+ false
113
+ end
114
+ end
115
+
116
+ def regexp
117
+ @regexp ||= regexp_from_config || regexp_from_string_type
118
+ end
119
+
120
+ def regexp_from_string_type
121
+ case cop_config['EnforcedSentenceType'].to_s.downcase
122
+ when 'sentence' then SENTENCE_REGEXP
123
+ when 'fragmented_sentence' then FRAGMENTED_SENTENCE_REGEXP
124
+ when 'fragment' then FRAGMENT_REGEXP
125
+ else
126
+ SENTENCE_REGEXP
127
+ end
128
+ end
129
+
130
+ def regexp_from_config
131
+ Regexp.new(cop_config['Regexp']) if cop_config['Regexp']
132
+ end
133
+
134
+ def dstr_contains_sentence?(node)
135
+ node.children.any? { |child| sentence?(child) }
136
+ end
137
+
138
+ def check_for_parent_decorator(node)
139
+ return if parent_is_translator?(node.parent)
140
+ return if parent_is_indexer?(node.parent)
141
+ return if ignoring_raised_parent?(node.parent)
142
+
143
+ add_offense(node, message: 'decorator is missing around sentence')
144
+ end
145
+
146
+ def ignoring_raised_parent?(parent)
147
+ return false unless cop_config['IgnoreExceptions']
148
+
149
+ return true if parent.respond_to?(:method_name) && %i[raise fail].include?(parent.method_name)
150
+
151
+ # Commonly exceptions are initialized manually.
152
+ return ignoring_raised_parent?(parent.parent) if parent.respond_to?(:method_name) && parent.method?(:new)
153
+
154
+ false
155
+ end
156
+
157
+ def parent_is_indexer?(parent)
158
+ parent.respond_to?(:method_name) && parent.method?(:[])
159
+ end
160
+
161
+ def parent_is_translator?(parent)
162
+ if parent.respond_to?(:type) && parent.send_type?
163
+ method_name = parent.loc.selector.source
164
+ if RailsI18n.supported_decorator?(method_name)
165
+ # Implicit receiver is assumed correct.
166
+ return true if parent.receiver.nil?
167
+ return true if parent.receiver.children == [nil, :I18n]
168
+ end
169
+ end
170
+ false
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module I18n
6
+ module RailsI18n
7
+ # When using an decorated string to support I18N, any strings inside the decoration should not contain
8
+ # the '#{}' interpolation string as this makes it hard to translate the strings.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ #
14
+ # t("status.#{status_string}")
15
+ # t("status." + "accepted")
16
+ #
17
+ # @example
18
+ #
19
+ # # good
20
+ #
21
+ # t("status.accepted")
22
+ #
23
+ class DecorateStringFormattingUsingInterpolation < Cop
24
+ def on_send(node)
25
+ return unless node&.loc&.selector
26
+
27
+ decorator_name = node.loc.selector.source
28
+ return unless RailsI18n.supported_decorator?(decorator_name)
29
+
30
+ method_name = node.method_name
31
+ arg_nodes = node.arguments
32
+ return unless !arg_nodes.empty? && contains_string_formatting_with_interpolation?(arg_nodes)
33
+
34
+ message_section = arg_nodes[0]
35
+ add_offense(message_section, message: error_message(method_name))
36
+ end
37
+
38
+ private
39
+
40
+ def error_message(method_name)
41
+ "'#{method_name}' function, message key string should not contain \#{} formatting"
42
+ end
43
+
44
+ def string_contains_interpolation_format?(str)
45
+ str.match(/\#{[^}]+}/)
46
+ end
47
+
48
+ def contains_string_formatting_with_interpolation?(node)
49
+ return node.any? { |n| contains_string_formatting_with_interpolation?(n) } if node.is_a?(Array)
50
+
51
+ return string_contains_interpolation_format?(node.source) if node.respond_to?(:type) && (node.str_type? || node.dstr_type?)
52
+
53
+ return node.children.any? { |child| contains_string_formatting_with_interpolation?(child) } if node.respond_to?(:children)
54
+
55
+ false
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,17 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- rubocop_version = '~> 0.51'
4
5
 
5
6
  Gem::Specification.new do |spec|
6
7
  spec.name = 'rubocop-i18n'
7
- spec.version = '1.3.0'
8
+ spec.version = '3.0.0'
8
9
  spec.authors = ['Puppet', 'Brandon High', 'TP Honey', 'Helen Campbell']
9
10
  spec.email = ['team-modules@puppet.com', 'brandon.high@puppet.com', 'tp@puppet.com', 'helen@puppet.com']
10
11
 
11
12
  spec.summary = 'RuboCop rules for i18n'
12
- spec.description = 'RuboCop rules for detecting and autocorrecting undecorated strings for i18n'
13
+ spec.description = 'RuboCop rules for detecting and autocorrecting undecorated strings for i18n (gettext and rails-i18n)'
13
14
  spec.homepage = 'https://github.com/puppetlabs/rubocop-i18n'
14
- spec.license = 'Apache-2'
15
+ spec.license = 'Apache-2.0'
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
18
  f.match(%r{^(test|spec|features)/})
@@ -20,11 +21,12 @@ Gem::Specification.new do |spec|
20
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
22
  spec.require_paths = ['lib']
22
23
 
23
- spec.add_development_dependency 'bundler', '~> 1.14'
24
- spec.add_development_dependency 'pry'
25
- spec.add_development_dependency 'rake', '~> 10.0'
26
- spec.add_development_dependency 'rb-readline'
24
+ spec.required_ruby_version = '>= 2.5.8'
25
+
26
+ spec.add_development_dependency 'bundler', '>= 1.17.3'
27
+ spec.add_development_dependency 'pry', '~> 0.13.1'
28
+ spec.add_development_dependency 'rake', '>= 12.3.3'
29
+ spec.add_development_dependency 'rb-readline', '~> 0.5.5'
27
30
  spec.add_development_dependency 'rspec', '~> 3.0'
28
- spec.add_development_dependency 'rubocop', rubocop_version
29
- spec.add_runtime_dependency 'rubocop', rubocop_version
31
+ spec.add_runtime_dependency 'rubocop', '~> 1.0'
30
32
  end
metadata CHANGED
@@ -1,118 +1,104 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-i18n
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  - Brandon High
9
9
  - TP Honey
10
10
  - Helen Campbell
11
- autorequire:
11
+ autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2018-09-21 00:00:00.000000000 Z
14
+ date: 2020-12-14 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
18
- requirement: !ruby/object:Gem::Requirement
19
- requirements:
20
- - - "~>"
21
- - !ruby/object:Gem::Version
22
- version: '1.14'
23
- type: :development
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '1.14'
30
- - !ruby/object:Gem::Dependency
31
- name: pry
32
18
  requirement: !ruby/object:Gem::Requirement
33
19
  requirements:
34
20
  - - ">="
35
21
  - !ruby/object:Gem::Version
36
- version: '0'
22
+ version: 1.17.3
37
23
  type: :development
38
24
  prerelease: false
39
25
  version_requirements: !ruby/object:Gem::Requirement
40
26
  requirements:
41
27
  - - ">="
42
28
  - !ruby/object:Gem::Version
43
- version: '0'
29
+ version: 1.17.3
44
30
  - !ruby/object:Gem::Dependency
45
- name: rake
31
+ name: pry
46
32
  requirement: !ruby/object:Gem::Requirement
47
33
  requirements:
48
34
  - - "~>"
49
35
  - !ruby/object:Gem::Version
50
- version: '10.0'
36
+ version: 0.13.1
51
37
  type: :development
52
38
  prerelease: false
53
39
  version_requirements: !ruby/object:Gem::Requirement
54
40
  requirements:
55
41
  - - "~>"
56
42
  - !ruby/object:Gem::Version
57
- version: '10.0'
43
+ version: 0.13.1
58
44
  - !ruby/object:Gem::Dependency
59
- name: rb-readline
45
+ name: rake
60
46
  requirement: !ruby/object:Gem::Requirement
61
47
  requirements:
62
48
  - - ">="
63
49
  - !ruby/object:Gem::Version
64
- version: '0'
50
+ version: 12.3.3
65
51
  type: :development
66
52
  prerelease: false
67
53
  version_requirements: !ruby/object:Gem::Requirement
68
54
  requirements:
69
55
  - - ">="
70
56
  - !ruby/object:Gem::Version
71
- version: '0'
57
+ version: 12.3.3
72
58
  - !ruby/object:Gem::Dependency
73
- name: rspec
59
+ name: rb-readline
74
60
  requirement: !ruby/object:Gem::Requirement
75
61
  requirements:
76
62
  - - "~>"
77
63
  - !ruby/object:Gem::Version
78
- version: '3.0'
64
+ version: 0.5.5
79
65
  type: :development
80
66
  prerelease: false
81
67
  version_requirements: !ruby/object:Gem::Requirement
82
68
  requirements:
83
69
  - - "~>"
84
70
  - !ruby/object:Gem::Version
85
- version: '3.0'
71
+ version: 0.5.5
86
72
  - !ruby/object:Gem::Dependency
87
- name: rubocop
73
+ name: rspec
88
74
  requirement: !ruby/object:Gem::Requirement
89
75
  requirements:
90
76
  - - "~>"
91
77
  - !ruby/object:Gem::Version
92
- version: '0.51'
78
+ version: '3.0'
93
79
  type: :development
94
80
  prerelease: false
95
81
  version_requirements: !ruby/object:Gem::Requirement
96
82
  requirements:
97
83
  - - "~>"
98
84
  - !ruby/object:Gem::Version
99
- version: '0.51'
85
+ version: '3.0'
100
86
  - !ruby/object:Gem::Dependency
101
87
  name: rubocop
102
88
  requirement: !ruby/object:Gem::Requirement
103
89
  requirements:
104
90
  - - "~>"
105
91
  - !ruby/object:Gem::Version
106
- version: '0.51'
92
+ version: '1.0'
107
93
  type: :runtime
108
94
  prerelease: false
109
95
  version_requirements: !ruby/object:Gem::Requirement
110
96
  requirements:
111
97
  - - "~>"
112
98
  - !ruby/object:Gem::Version
113
- version: '0.51'
99
+ version: '1.0'
114
100
  description: RuboCop rules for detecting and autocorrecting undecorated strings for
115
- i18n
101
+ i18n (gettext and rails-i18n)
116
102
  email:
117
103
  - team-modules@puppet.com
118
104
  - brandon.high@puppet.com
@@ -122,11 +108,15 @@ executables: []
122
108
  extensions: []
123
109
  extra_rdoc_files: []
124
110
  files:
111
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
112
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
125
113
  - ".gitignore"
126
114
  - ".rubocop.yml"
127
115
  - ".rubocop_todo.yml"
116
+ - ".ruby-version"
128
117
  - ".travis.yml"
129
118
  - CHANGELOG.md
119
+ - CODEOWNERS
130
120
  - CODE_OF_CONDUCT.md
131
121
  - Gemfile
132
122
  - LICENSE
@@ -141,13 +131,15 @@ files:
141
131
  - lib/rubocop/cop/i18n/gettext/decorate_string.rb
142
132
  - lib/rubocop/cop/i18n/gettext/decorate_string_formatting_using_interpolation.rb
143
133
  - lib/rubocop/cop/i18n/gettext/decorate_string_formatting_using_percent.rb
144
- - lib/rubocop/rspec/cop_helper.rb
134
+ - lib/rubocop/cop/i18n/rails_i18n.rb
135
+ - lib/rubocop/cop/i18n/rails_i18n/decorate_string.rb
136
+ - lib/rubocop/cop/i18n/rails_i18n/decorate_string_formatting_using_interpolation.rb
145
137
  - rubocop-i18n.gemspec
146
138
  homepage: https://github.com/puppetlabs/rubocop-i18n
147
139
  licenses:
148
- - Apache-2
140
+ - Apache-2.0
149
141
  metadata: {}
150
- post_install_message:
142
+ post_install_message:
151
143
  rdoc_options: []
152
144
  require_paths:
153
145
  - lib
@@ -155,16 +147,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
147
  requirements:
156
148
  - - ">="
157
149
  - !ruby/object:Gem::Version
158
- version: '0'
150
+ version: 2.5.8
159
151
  required_rubygems_version: !ruby/object:Gem::Requirement
160
152
  requirements:
161
153
  - - ">="
162
154
  - !ruby/object:Gem::Version
163
155
  version: '0'
164
156
  requirements: []
165
- rubyforge_project:
166
- rubygems_version: 2.6.14.1
167
- signing_key:
157
+ rubygems_version: 3.1.4
158
+ signing_key:
168
159
  specification_version: 4
169
160
  summary: RuboCop rules for i18n
170
161
  test_files: []
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tempfile'
4
-
5
- # This module provides methods that make it easier to test Cops.
6
- module CopHelper
7
- extend RSpec::SharedContext
8
-
9
- let(:ruby_version) { 2.2 }
10
- let(:enabled_rails) { false }
11
- let(:rails_version) { false }
12
-
13
- def inspect_source_file(source)
14
- Tempfile.open('tmp') { |f| inspect_source(source, f) }
15
- end
16
-
17
- def inspect_gemfile(source)
18
- inspect_source(source, 'Gemfile')
19
- end
20
-
21
- def inspect_source(source, file = nil)
22
- if source.is_a?(Array) && source.size == 1
23
- raise "Don't use an array for a single line of code: #{source}"
24
- end
25
- RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {}
26
- RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
27
- processed_source = parse_source(source, file)
28
- raise 'Error parsing example code' unless processed_source.valid_syntax?
29
- _investigate(cop, processed_source)
30
- end
31
-
32
- def parse_source(source, file = nil)
33
- source = source.join($RS) if source.is_a?(Array)
34
-
35
- if file && file.respond_to?(:write)
36
- file.write(source)
37
- file.rewind
38
- file = file.path
39
- end
40
-
41
- RuboCop::ProcessedSource.new(source, ruby_version, file)
42
- end
43
-
44
- def autocorrect_source_file(source)
45
- Tempfile.open('tmp') { |f| autocorrect_source(source, f) }
46
- end
47
-
48
- def autocorrect_source(source, file = nil)
49
- cop.instance_variable_get(:@options)[:auto_correct] = true
50
- processed_source = parse_source(source, file)
51
- _investigate(cop, processed_source)
52
-
53
- corrector =
54
- RuboCop::Cop::Corrector.new(processed_source.buffer, cop.corrections)
55
- corrector.rewrite
56
- end
57
-
58
- def autocorrect_source_with_loop(source, file = nil)
59
- loop do
60
- cop.instance_variable_set(:@corrections, [])
61
- new_source = autocorrect_source(source, file)
62
- return new_source if new_source == source
63
- source = new_source
64
- end
65
- end
66
-
67
- def _investigate(cop, processed_source)
68
- forces = RuboCop::Cop::Force.all.each_with_object([]) do |klass, instances|
69
- next unless cop.join_force?(klass)
70
- instances << klass.new([cop])
71
- end
72
-
73
- commissioner =
74
- RuboCop::Cop::Commissioner.new([cop], forces, raise_error: true)
75
- commissioner.investigate(processed_source)
76
- commissioner
77
- end
78
- end
79
-
80
- module RuboCop
81
- module Cop
82
- # Monkey-patch Cop for tests to provide easy access to messages and
83
- # highlights.
84
- class Cop
85
- def messages
86
- offenses.sort.map(&:message)
87
- end
88
-
89
- def highlights
90
- offenses.sort.map { |o| o.location.source }
91
- end
92
- end
93
- end
94
- end
95
-
96
- RSpec.configure do |config|
97
- config.include CopHelper
98
- end