i18nliner 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +8 -6
- data/lib/i18nliner/base.rb +26 -27
- data/lib/i18nliner/call_helpers.rb +42 -34
- data/lib/i18nliner/commands/basic_formatter.rb +2 -0
- data/lib/i18nliner/commands/check.rb +14 -14
- data/lib/i18nliner/commands/color_formatter.rb +2 -0
- data/lib/i18nliner/commands/dump.rb +6 -6
- data/lib/i18nliner/commands/generic_command.rb +4 -2
- data/lib/i18nliner/controller_scope.rb +2 -0
- data/lib/i18nliner/errors.rb +5 -4
- data/lib/i18nliner/erubi.rb +5 -4
- data/lib/i18nliner/erubis.rb +2 -0
- data/lib/i18nliner/extensions/controller.rb +8 -8
- data/lib/i18nliner/extensions/core.rb +16 -18
- data/lib/i18nliner/extensions/inferpolation.rb +11 -7
- data/lib/i18nliner/extensions/model.rb +11 -10
- data/lib/i18nliner/extensions/view.rb +13 -9
- data/lib/i18nliner/extractors/ruby_extractor.rb +18 -16
- data/lib/i18nliner/extractors/sexp_helper.rb +8 -5
- data/lib/i18nliner/extractors/translate_call.rb +31 -26
- data/lib/i18nliner/extractors/translation_hash.rb +10 -10
- data/lib/i18nliner/pre_processors/erb_pre_processor.rb +63 -49
- data/lib/i18nliner/processors/abstract_processor.rb +9 -4
- data/lib/i18nliner/processors/erb_processor.rb +10 -8
- data/lib/i18nliner/processors/ruby_processor.rb +13 -10
- data/lib/i18nliner/processors.rb +2 -0
- data/lib/i18nliner/railtie.rb +10 -10
- data/lib/i18nliner/reserved_keys.rb +2 -0
- data/lib/i18nliner/scope.rb +7 -9
- data/lib/i18nliner.rb +8 -6
- data/lib/tasks/i18nliner.rake +8 -7
- data/spec/fixtures/app/models/invalid.rb +2 -0
- data/spec/fixtures/app/models/valid.rb +2 -0
- data/spec/{commands → i18nliner/commands}/check_spec.rb +5 -6
- data/spec/{commands → i18nliner/commands}/dump_spec.rb +9 -10
- data/spec/{extensions → i18nliner/extensions}/controller_spec.rb +9 -8
- data/spec/{extensions → i18nliner/extensions}/core_spec.rb +45 -34
- data/spec/i18nliner/extensions/inferpolation_spec.rb +51 -0
- data/spec/i18nliner/extensions/model_spec.rb +31 -0
- data/spec/{extensions → i18nliner/extensions}/view_spec.rb +15 -13
- data/spec/{extractors → i18nliner/extractors}/ruby_extractor_spec.rb +24 -15
- data/spec/{extractors → i18nliner/extractors}/translate_call_spec.rb +73 -65
- data/spec/{extractors → i18nliner/extractors}/translation_hash_spec.rb +13 -12
- data/spec/{reserved_keys_spec.rb → i18nliner/i18n_spec.rb} +3 -1
- data/spec/{pre_processors → i18nliner/pre_processors}/erb_pre_processor_spec.rb +80 -77
- data/spec/i18nliner/processors/erb_processor_spec.rb +45 -0
- data/spec/i18nliner/processors/ruby_processor_spec.rb +27 -0
- metadata +58 -88
- data/spec/extensions/inferpolation_spec.rb +0 -49
- data/spec/extensions/model_spec.rb +0 -30
- data/spec/processors/erb_processor_spec.rb +0 -45
- data/spec/processors/ruby_processor_spec.rb +0 -28
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sexp_processor"
|
|
4
|
+
require "i18nliner/errors"
|
|
5
|
+
require "i18nliner/extractors/translate_call"
|
|
6
|
+
require "i18nliner/extractors/sexp_helper"
|
|
5
7
|
|
|
6
8
|
module I18nliner
|
|
7
9
|
module Extractors
|
|
8
10
|
class RubyExtractor < ::SexpProcessor
|
|
9
11
|
include SexpHelper
|
|
10
12
|
|
|
11
|
-
TRANSLATE_CALLS = [
|
|
12
|
-
attr_reader :current_line
|
|
13
|
+
TRANSLATE_CALLS = %i[t translate].freeze
|
|
14
|
+
attr_reader :current_line, :current_defn
|
|
13
15
|
|
|
14
16
|
def self.pattern
|
|
15
17
|
/(^|\W)(t|translate)(\W|$)/
|
|
@@ -26,13 +28,12 @@ module I18nliner
|
|
|
26
28
|
process(@sexps)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
attr_reader :current_defn
|
|
30
31
|
def process_defn(exp)
|
|
31
32
|
exp.shift
|
|
32
33
|
@current_defn = exp.shift
|
|
33
34
|
process exp.shift until exp.empty?
|
|
34
35
|
@current_defn = nil
|
|
35
|
-
s
|
|
36
|
+
s
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def process_call(exp)
|
|
@@ -40,7 +41,7 @@ module I18nliner
|
|
|
40
41
|
receiver_exp = exp.shift
|
|
41
42
|
receiver = nil
|
|
42
43
|
if receiver_exp
|
|
43
|
-
receiver = receiver_exp[0] == :const ? receiver_exp.last : UnsupportedExpression
|
|
44
|
+
receiver = (receiver_exp[0] == :const) ? receiver_exp.last : UnsupportedExpression
|
|
44
45
|
process(receiver_exp)
|
|
45
46
|
end
|
|
46
47
|
method = exp.shift
|
|
@@ -48,7 +49,7 @@ module I18nliner
|
|
|
48
49
|
if extractable_call?(receiver, method)
|
|
49
50
|
@current_line = exp.line
|
|
50
51
|
|
|
51
|
-
# convert s-exps into literals where possible
|
|
52
|
+
# convert s-exps into literals where possible
|
|
52
53
|
args = process_arguments(exp)
|
|
53
54
|
|
|
54
55
|
process_translate_call(receiver, method, args)
|
|
@@ -61,7 +62,7 @@ module I18nliner
|
|
|
61
62
|
s
|
|
62
63
|
end
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
protected
|
|
65
66
|
|
|
66
67
|
def extractable_call?(receiver, method)
|
|
67
68
|
TRANSLATE_CALLS.include?(method) && (receiver.nil? || receiver == :I18n)
|
|
@@ -69,15 +70,15 @@ module I18nliner
|
|
|
69
70
|
|
|
70
71
|
def process_translate_call(receiver, method, args)
|
|
71
72
|
scope = receiver ? Scope.root : @scope
|
|
72
|
-
call = TranslateCall.new(scope, @current_line, method, args, :
|
|
73
|
-
call.translations.each
|
|
73
|
+
call = TranslateCall.new(scope, @current_line, method, args, explicit_receiver: !receiver.nil?)
|
|
74
|
+
call.translations.each(&@block)
|
|
74
75
|
end
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
private
|
|
77
78
|
|
|
78
79
|
def process_arguments(args)
|
|
79
80
|
values = []
|
|
80
|
-
while arg = args.shift
|
|
81
|
+
while (arg = args.shift)
|
|
81
82
|
values << evaluate_expression(arg)
|
|
82
83
|
end
|
|
83
84
|
values
|
|
@@ -90,13 +91,14 @@ module I18nliner
|
|
|
90
91
|
return raw(exp) if exp.sexp_type == :lit
|
|
91
92
|
return string_from(exp) if stringish?(exp)
|
|
92
93
|
return hash_from(exp) if exp.sexp_type == :hash
|
|
94
|
+
|
|
93
95
|
process(exp)
|
|
94
96
|
UnsupportedExpression
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
def hash_from(exp)
|
|
98
100
|
exp.shift
|
|
99
|
-
values = exp.map{ |e| evaluate_expression(e) }
|
|
101
|
+
values = exp.map { |e| evaluate_expression(e) }
|
|
100
102
|
Hash[*values]
|
|
101
103
|
end
|
|
102
104
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module I18nliner
|
|
2
4
|
module Extractors
|
|
3
5
|
module SexpHelper
|
|
@@ -7,18 +9,19 @@ module I18nliner
|
|
|
7
9
|
|
|
8
10
|
def string_concatenation?(exp)
|
|
9
11
|
exp.sexp_type == :call &&
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
exp[2] == :+ &&
|
|
13
|
+
exp.last &&
|
|
14
|
+
exp.last.sexp_type == :str
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def raw(exp)
|
|
16
18
|
exp.shift
|
|
17
|
-
|
|
19
|
+
exp.shift
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
def string_from(exp)
|
|
21
23
|
return raw(exp) if exp.sexp_type == :str
|
|
24
|
+
|
|
22
25
|
exp.shift
|
|
23
26
|
lhs = exp.shift
|
|
24
27
|
exp.shift
|
|
@@ -33,7 +36,7 @@ module I18nliner
|
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
UnsupportedExpression = Object.new.freeze
|
|
37
40
|
end
|
|
38
41
|
end
|
|
39
42
|
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/string/inflections"
|
|
4
|
+
require "i18nliner/base"
|
|
5
|
+
require "i18nliner/call_helpers"
|
|
6
|
+
require "i18nliner/errors"
|
|
5
7
|
|
|
6
8
|
module I18nliner
|
|
7
9
|
module Extractors
|
|
@@ -30,48 +32,48 @@ module I18nliner
|
|
|
30
32
|
|
|
31
33
|
def normalize
|
|
32
34
|
@key = normalize_key(@key, @scope, @options[:i18nliner_inferred_key], @options[:scope])
|
|
33
|
-
@default = normalize_default(@default, @options || {}, {:
|
|
35
|
+
@default = normalize_default(@default, @options || {}, { remove_whitespace: @scope.remove_whitespace? })
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def translations
|
|
37
39
|
return [] unless default
|
|
40
|
+
|
|
38
41
|
keys = Array(key)
|
|
39
42
|
keys.inject([]) do |result, key|
|
|
40
43
|
if default.is_a?(String)
|
|
41
44
|
result << [key, default]
|
|
42
45
|
else
|
|
43
|
-
result.concat
|
|
46
|
+
result.concat(default.map { |dk, dv| ["#{key}.#{dk}", dv] })
|
|
44
47
|
end
|
|
45
48
|
end
|
|
46
49
|
end
|
|
47
50
|
|
|
48
|
-
def validate_key
|
|
49
|
-
end
|
|
51
|
+
def validate_key; end
|
|
50
52
|
|
|
51
53
|
def validate_default
|
|
52
54
|
return unless @default.is_a?(Hash)
|
|
53
|
-
if (keys = @default.keys - ALLOWED_PLURALIZATION_KEYS).size
|
|
55
|
+
if (keys = @default.keys - ALLOWED_PLURALIZATION_KEYS).size.positive?
|
|
54
56
|
raise InvalidPluralizationKeyError.new(@line, keys)
|
|
55
57
|
elsif REQUIRED_PLURALIZATION_KEYS & (keys = @default.keys) != REQUIRED_PLURALIZATION_KEYS
|
|
56
58
|
raise MissingPluralizationKeyError.new(@line, keys)
|
|
57
59
|
else
|
|
58
|
-
@default.
|
|
60
|
+
@default.each_value do |value|
|
|
59
61
|
raise InvalidPluralizationDefaultError.new(@line, value) unless value.is_a?(String)
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
return if I18nliner.infer_interpolation_values
|
|
66
|
+
|
|
67
|
+
if @default.is_a?(String)
|
|
68
|
+
validate_interpolation_values(@default)
|
|
69
|
+
else
|
|
70
|
+
@default.each_value do |default|
|
|
71
|
+
validate_interpolation_values(default)
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
private
|
|
75
77
|
|
|
76
78
|
# Possible translate signatures:
|
|
77
79
|
#
|
|
@@ -85,26 +87,29 @@ module I18nliner
|
|
|
85
87
|
@default = @options.delete(:default)
|
|
86
88
|
@default = nil if @default.is_a?(Symbol)
|
|
87
89
|
@default = @default.detect { |d| d.is_a?(String) } if @default.is_a?(Array)
|
|
88
|
-
|
|
90
|
+
unless @default.nil? || @default.is_a?(String) || @default.is_a?(Hash)
|
|
91
|
+
raise InvalidSignatureError.new(@line,
|
|
92
|
+
args)
|
|
93
|
+
end
|
|
89
94
|
rescue ArgumentError
|
|
90
95
|
raise InvalidSignatureError.new(@line, args)
|
|
91
96
|
end
|
|
92
97
|
|
|
93
|
-
def validate_interpolation_values(
|
|
94
|
-
default.scan(/%\{([
|
|
98
|
+
def validate_interpolation_values(default)
|
|
99
|
+
default.scan(/%\{([^}]+)\}/) do |match|
|
|
95
100
|
placeholder = match[0].to_sym
|
|
96
101
|
next if @options.include?(placeholder)
|
|
102
|
+
|
|
97
103
|
raise MissingInterpolationValueError.new(@line, placeholder)
|
|
98
104
|
end
|
|
99
105
|
end
|
|
100
106
|
|
|
101
107
|
def validate_options
|
|
102
|
-
if @default.is_a?(Hash)
|
|
103
|
-
raise MissingCountValueError.new(@line) unless @options && @options.key?(:count)
|
|
104
|
-
end
|
|
108
|
+
raise MissingCountValueError, @line if @default.is_a?(Hash) && !@options&.key?(:count)
|
|
105
109
|
return if @options.nil?
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
|
|
111
|
+
@options.each_key do |key|
|
|
112
|
+
raise InvalidOptionKeyError, @line unless key.is_a?(String) || key.is_a?(Symbol)
|
|
108
113
|
end
|
|
109
114
|
end
|
|
110
115
|
end
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module I18nliner
|
|
2
4
|
module Extractors
|
|
3
5
|
class TranslationHash < Hash
|
|
4
6
|
attr_accessor :line
|
|
5
7
|
|
|
6
8
|
def self.new(hash = {})
|
|
7
|
-
hash.
|
|
9
|
+
hash.each_with_object(super()) do |(key, value), result|
|
|
8
10
|
result.store(key.to_s, value.is_a?(Hash) ? new(value) : value)
|
|
9
|
-
result
|
|
10
11
|
end
|
|
11
12
|
end
|
|
12
13
|
|
|
@@ -16,13 +17,13 @@ module I18nliner
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def []=(key, value)
|
|
19
|
-
parts = key.split(
|
|
20
|
+
parts = key.split(".")
|
|
20
21
|
leaf = parts.pop
|
|
21
22
|
hash = self
|
|
22
|
-
while part = parts.shift
|
|
23
|
+
while (part = parts.shift)
|
|
23
24
|
if hash[part]
|
|
24
25
|
unless hash[part].is_a?(Hash)
|
|
25
|
-
intermediate_key = key.sub((parts + [leaf]).join(
|
|
26
|
+
intermediate_key = key.sub((parts + [leaf]).join("."), "")
|
|
26
27
|
raise KeyAsScopeError, intermediate_key
|
|
27
28
|
end
|
|
28
29
|
else
|
|
@@ -32,11 +33,10 @@ module I18nliner
|
|
|
32
33
|
end
|
|
33
34
|
if hash[leaf]
|
|
34
35
|
if hash[leaf] != value
|
|
35
|
-
if hash[leaf].is_a?(Hash)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end
|
|
36
|
+
raise KeyAsScopeError.new(@line, key) if hash[leaf].is_a?(Hash)
|
|
37
|
+
|
|
38
|
+
raise KeyInUseError.new(@line, key)
|
|
39
|
+
|
|
40
40
|
end
|
|
41
41
|
else
|
|
42
42
|
@total_size += 1
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "i18nliner/errors"
|
|
4
|
+
require "i18nliner/call_helpers"
|
|
5
|
+
require "i18nliner/extractors/sexp_helper"
|
|
6
|
+
require "nokogiri"
|
|
7
7
|
|
|
8
8
|
module I18nliner
|
|
9
9
|
module PreProcessors
|
|
10
10
|
class ErbPreProcessor
|
|
11
|
-
|
|
12
11
|
class Context
|
|
13
12
|
attr_reader :buffer, :parent
|
|
14
13
|
|
|
15
14
|
def initialize(parent = nil)
|
|
16
15
|
@parent = parent
|
|
17
|
-
@buffer =
|
|
16
|
+
@buffer = +""
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
def <<(string)
|
|
@@ -35,14 +34,26 @@ module I18nliner
|
|
|
35
34
|
include Extractors::SexpHelper
|
|
36
35
|
|
|
37
36
|
DEFINITIONS = [
|
|
38
|
-
{:
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
{ method: :link_to, pattern: /link_to/, arg: 0 }
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
def self.parser
|
|
41
|
+
@parser ||= begin
|
|
42
|
+
require "prism"
|
|
43
|
+
Prism::Translation::RubyParser.new
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.ruby2ruby
|
|
48
|
+
@ruby2ruby ||= begin
|
|
49
|
+
require "ruby2ruby"
|
|
50
|
+
Ruby2Ruby.new
|
|
51
|
+
end
|
|
52
|
+
end
|
|
42
53
|
|
|
43
54
|
def self.match_for(string)
|
|
44
55
|
DEFINITIONS.each do |info|
|
|
45
|
-
return Helper.new(info, string) if string
|
|
56
|
+
return Helper.new(info, string) if string&.match?(info[:pattern])
|
|
46
57
|
end
|
|
47
58
|
nil
|
|
48
59
|
end
|
|
@@ -58,9 +69,10 @@ module I18nliner
|
|
|
58
69
|
|
|
59
70
|
SEXP_ARG_OFFSET = 3
|
|
60
71
|
def wrappable?
|
|
61
|
-
return @wrappable
|
|
72
|
+
return @wrappable unless @wrappable.nil?
|
|
73
|
+
|
|
62
74
|
begin
|
|
63
|
-
sexps =
|
|
75
|
+
sexps = self.class.parser.parse(@source)
|
|
64
76
|
@wrappable = sexps.sexp_type == :call &&
|
|
65
77
|
sexps[1].nil? &&
|
|
66
78
|
sexps[2] == @method &&
|
|
@@ -75,10 +87,10 @@ module I18nliner
|
|
|
75
87
|
if stringish?(sexp)
|
|
76
88
|
@content = string_from(sexp)
|
|
77
89
|
else
|
|
78
|
-
@placeholder =
|
|
90
|
+
@placeholder = self.class.ruby2ruby.process(sexp)
|
|
79
91
|
end
|
|
80
92
|
sexps[@arg + SEXP_ARG_OFFSET] = Sexp.new(:str, "\\1")
|
|
81
|
-
@wrapper =
|
|
93
|
+
@wrapper = self.class.ruby2ruby.process(sexps)
|
|
82
94
|
end
|
|
83
95
|
end
|
|
84
96
|
|
|
@@ -93,17 +105,17 @@ module I18nliner
|
|
|
93
105
|
def <<(string)
|
|
94
106
|
case string
|
|
95
107
|
when ERB_BLOCK_EXPRESSION
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
else
|
|
99
|
-
raise TBlockNestingError.new("can't nest block expressions inside a t block")
|
|
108
|
+
unless string =~ ERB_T_BLOCK_EXPRESSION
|
|
109
|
+
raise TBlockNestingError, "can't nest block expressions inside a t block"
|
|
100
110
|
end
|
|
111
|
+
|
|
112
|
+
TBlock.new(self, $&)
|
|
113
|
+
|
|
101
114
|
when ERB_STATEMENT
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
115
|
+
raise TBlockNestingError, "can't nest statements inside a t block" unless ERB_END_STATEMENT.match?(string)
|
|
116
|
+
|
|
117
|
+
@parent << result
|
|
118
|
+
|
|
107
119
|
else
|
|
108
120
|
# expressions and the like are handled a bit later
|
|
109
121
|
# TODO: perhaps a tad more efficient to capture/transform them
|
|
@@ -119,7 +131,7 @@ module I18nliner
|
|
|
119
131
|
result = "<%= t :#{key}, #{default}"
|
|
120
132
|
result << ", " << options if options
|
|
121
133
|
result << ", " << wrappers if wrappers
|
|
122
|
-
result << (@lines
|
|
134
|
+
result << (@lines.positive? ? "\n" * @lines : " ")
|
|
123
135
|
result << "%>"
|
|
124
136
|
end
|
|
125
137
|
|
|
@@ -127,10 +139,10 @@ module I18nliner
|
|
|
127
139
|
# expression
|
|
128
140
|
def infer_interpolation_key(string, others)
|
|
129
141
|
key = string.downcase
|
|
130
|
-
key.
|
|
131
|
-
key.gsub!(/[^a-z0-9]/,
|
|
142
|
+
key.delete_suffix!(".html_safe")
|
|
143
|
+
key.gsub!(/[^a-z0-9]/, " ")
|
|
132
144
|
key.strip!
|
|
133
|
-
key.gsub!(/ +/,
|
|
145
|
+
key.gsub!(/ +/, "_")
|
|
134
146
|
key.slice!(20)
|
|
135
147
|
i = 0
|
|
136
148
|
base_key = key
|
|
@@ -143,12 +155,11 @@ module I18nliner
|
|
|
143
155
|
|
|
144
156
|
def extract_wrappers!(source, wrappers, placeholder_map)
|
|
145
157
|
source = extract_html_wrappers!(source, wrappers, placeholder_map)
|
|
146
|
-
|
|
147
|
-
source
|
|
158
|
+
extract_helper_wrappers!(source, wrappers, placeholder_map)
|
|
148
159
|
end
|
|
149
160
|
|
|
150
161
|
def find_or_add_wrapper(wrapper, wrappers)
|
|
151
|
-
unless pos = wrappers.index(wrapper)
|
|
162
|
+
unless (pos = wrappers.index(wrapper))
|
|
152
163
|
pos = wrappers.size
|
|
153
164
|
wrappers << wrapper
|
|
154
165
|
end
|
|
@@ -157,12 +168,12 @@ module I18nliner
|
|
|
157
168
|
|
|
158
169
|
# incidentally this converts entities to their corresponding values
|
|
159
170
|
def extract_html_wrappers!(source, wrappers, placeholder_map)
|
|
160
|
-
default =
|
|
171
|
+
default = +""
|
|
161
172
|
nodes = Nokogiri::HTML.fragment(source).children
|
|
162
173
|
nodes.each do |node|
|
|
163
174
|
if node.is_a?(Nokogiri::XML::Text)
|
|
164
175
|
default << node.content
|
|
165
|
-
elsif text = extract_text(node)
|
|
176
|
+
elsif (text = extract_text(node))
|
|
166
177
|
wrapper = node.to_s.sub(text, "\\\\1")
|
|
167
178
|
wrapper = prepare_wrapper(wrapper, placeholder_map)
|
|
168
179
|
pos = find_or_add_wrapper(wrapper, wrappers)
|
|
@@ -201,16 +212,16 @@ module I18nliner
|
|
|
201
212
|
end
|
|
202
213
|
|
|
203
214
|
def extract_temp_placeholders!
|
|
204
|
-
extract_placeholders!(@buffer, ERB_EXPRESSION, false) do |str, map|
|
|
215
|
+
extract_placeholders!(@buffer, ERB_EXPRESSION, wrap_placeholder: false) do |str, map|
|
|
205
216
|
["__I18NLINER_#{map.size}__", str]
|
|
206
217
|
end
|
|
207
218
|
end
|
|
208
219
|
|
|
209
|
-
def extract_placeholders!(buffer = @buffer, pattern = ERB_EXPRESSION, wrap_placeholder
|
|
220
|
+
def extract_placeholders!(buffer = @buffer, pattern = ERB_EXPRESSION, wrap_placeholder: true)
|
|
210
221
|
map = {}
|
|
211
|
-
buffer.gsub!(pattern) do
|
|
212
|
-
key,
|
|
213
|
-
map[key] =
|
|
222
|
+
buffer.gsub!(pattern) do
|
|
223
|
+
key, str2 = yield($~[:content], map)
|
|
224
|
+
map[key] = str2
|
|
214
225
|
wrap_placeholder ? "%{#{key}}" : key
|
|
215
226
|
end
|
|
216
227
|
map
|
|
@@ -227,7 +238,7 @@ module I18nliner
|
|
|
227
238
|
end
|
|
228
239
|
|
|
229
240
|
default.strip!
|
|
230
|
-
default.gsub!(/\s+/,
|
|
241
|
+
default.gsub!(/\s+/, " ")
|
|
231
242
|
|
|
232
243
|
key = infer_key(default, options)
|
|
233
244
|
default = default.inspect
|
|
@@ -239,21 +250,23 @@ module I18nliner
|
|
|
239
250
|
def options_to_ruby(options)
|
|
240
251
|
options["i18nliner_inferred_key"] = "true"
|
|
241
252
|
options.map do |key, value|
|
|
242
|
-
"
|
|
253
|
+
":#{key} => (#{value})"
|
|
243
254
|
end.join(", ")
|
|
244
255
|
end
|
|
245
256
|
|
|
246
257
|
def wrappers_to_ruby(wrappers)
|
|
247
|
-
return if wrappers.
|
|
248
|
-
|
|
258
|
+
return if wrappers.empty?
|
|
259
|
+
|
|
260
|
+
":wrappers => [#{wrappers.join(", ")}]"
|
|
249
261
|
end
|
|
250
262
|
|
|
251
263
|
def extract_text(root_node)
|
|
252
264
|
text = nil
|
|
253
265
|
nodes = root_node.children.to_a
|
|
254
|
-
while node = nodes.shift
|
|
266
|
+
while (node = nodes.shift)
|
|
255
267
|
if node.is_a?(Nokogiri::XML::Text) && !node.content.strip.empty?
|
|
256
|
-
raise UnwrappableContentError
|
|
268
|
+
raise UnwrappableContentError, "multiple text nodes in html markup" if text
|
|
269
|
+
|
|
257
270
|
text = node.content
|
|
258
271
|
else
|
|
259
272
|
nodes.concat node.children
|
|
@@ -264,7 +277,7 @@ module I18nliner
|
|
|
264
277
|
|
|
265
278
|
def wrap(text, index)
|
|
266
279
|
delimiter = "*" * index
|
|
267
|
-
"
|
|
280
|
+
"#{delimiter}#{text}#{delimiter}"
|
|
268
281
|
end
|
|
269
282
|
|
|
270
283
|
def infer_wrappers(source)
|
|
@@ -282,7 +295,7 @@ module I18nliner
|
|
|
282
295
|
<%=
|
|
283
296
|
.*?
|
|
284
297
|
(\sdo|\{)
|
|
285
|
-
(\s*\|[
|
|
298
|
+
(\s*\|[^|]+\|)?
|
|
286
299
|
\s*
|
|
287
300
|
%>
|
|
288
301
|
\z
|
|
@@ -326,7 +339,8 @@ module I18nliner
|
|
|
326
339
|
# since it's a no-go
|
|
327
340
|
# TODO get line numbers for errors
|
|
328
341
|
ctx = @source.split(ERB_TOKENIZER).inject(Context.new, :<<)
|
|
329
|
-
raise MalformedErbError
|
|
342
|
+
raise MalformedErbError, "possibly unterminated block expression" if ctx.parent
|
|
343
|
+
|
|
330
344
|
ctx.result
|
|
331
345
|
end
|
|
332
346
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "globby"
|
|
4
|
+
require "i18nliner/base"
|
|
5
|
+
require "i18nliner/processors"
|
|
4
6
|
|
|
5
7
|
module I18nliner
|
|
6
8
|
module Processors
|
|
@@ -31,7 +33,9 @@ module I18nliner
|
|
|
31
33
|
def check_files
|
|
32
34
|
Dir.chdir(I18nliner.base_path) do
|
|
33
35
|
files.each do |file|
|
|
34
|
-
@checker.call file
|
|
36
|
+
@checker.call(file) do |file|
|
|
37
|
+
check_file(file)
|
|
38
|
+
end
|
|
35
39
|
end
|
|
36
40
|
end
|
|
37
41
|
end
|
|
@@ -42,6 +46,7 @@ module I18nliner
|
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
def self.inherited(klass)
|
|
49
|
+
super
|
|
45
50
|
Processors.register klass
|
|
46
51
|
end
|
|
47
52
|
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
if defined?(Rails)
|
|
4
|
+
require "i18nliner/erubi"
|
|
3
5
|
else
|
|
4
|
-
require
|
|
6
|
+
require "erubi"
|
|
5
7
|
end
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
+
require "i18nliner/processors/ruby_processor"
|
|
9
|
+
require "i18nliner/pre_processors/erb_pre_processor"
|
|
8
10
|
|
|
9
11
|
module I18nliner
|
|
10
12
|
module Processors
|
|
11
13
|
class ErbProcessor < RubyProcessor
|
|
12
|
-
default_pattern
|
|
14
|
+
default_pattern "*.erb"
|
|
13
15
|
|
|
14
16
|
if defined?(::Rails) # block expressions and all that jazz
|
|
15
17
|
def pre_process(source)
|
|
@@ -26,8 +28,8 @@ module I18nliner
|
|
|
26
28
|
def scope_for(path)
|
|
27
29
|
scope = path.dup
|
|
28
30
|
if scope.sub!(VIEW_PATH, '\2')
|
|
29
|
-
scope = scope.gsub(
|
|
30
|
-
Scope.new(scope, :
|
|
31
|
+
scope = scope.gsub(%r{/_?}, ".")
|
|
32
|
+
Scope.new(scope, allow_relative: true, remove_whitespace: true, context: self)
|
|
31
33
|
else
|
|
32
34
|
Scope.root
|
|
33
35
|
end
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "i18nliner/processors/abstract_processor"
|
|
4
|
+
require "i18nliner/extractors/ruby_extractor"
|
|
5
|
+
require "i18nliner/scope"
|
|
6
|
+
require "i18nliner/controller_scope"
|
|
5
7
|
|
|
6
8
|
module I18nliner
|
|
7
9
|
module Processors
|
|
8
10
|
class RubyProcessor < AbstractProcessor
|
|
9
|
-
default_pattern
|
|
11
|
+
default_pattern "*.rb"
|
|
10
12
|
|
|
11
13
|
def check_contents(source, scope = Scope.new)
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
+
return unless source&.match?(Extractors::RubyExtractor.pattern)
|
|
15
|
+
|
|
16
|
+
sexps = Prism::Translation::RubyParser.new.parse(pre_process(source))
|
|
14
17
|
extractor = Extractors::RubyExtractor.new(sexps, scope)
|
|
15
18
|
extractor.each_translation do |key, value|
|
|
16
19
|
@translation_count += 1
|
|
@@ -28,14 +31,14 @@ module I18nliner
|
|
|
28
31
|
def scope_for(path)
|
|
29
32
|
scope = path.dup
|
|
30
33
|
if scope.sub!(CONTROLLER_PATH, '\2')
|
|
31
|
-
scope = scope.gsub(
|
|
32
|
-
ControllerScope.new(scope, :
|
|
34
|
+
scope = scope.gsub(%r{/_?}, ".")
|
|
35
|
+
ControllerScope.new(scope, allow_relative: true, context: self)
|
|
33
36
|
else
|
|
34
37
|
Scope.root
|
|
35
38
|
end
|
|
36
39
|
end
|
|
37
40
|
else
|
|
38
|
-
def scope_for(
|
|
41
|
+
def scope_for(_path)
|
|
39
42
|
Scope.root
|
|
40
43
|
end
|
|
41
44
|
end
|