i18n_flow 0.1.0 → 0.2.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/.gitignore +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -1
- data/lib/i18n_flow/cli.rb +9 -7
- data/lib/i18n_flow/cli/copy_command.rb +3 -24
- data/lib/i18n_flow/cli/format_command.rb +57 -0
- data/lib/i18n_flow/cli/help_command.rb +1 -0
- data/lib/i18n_flow/cli/lint_command/ascii.erb +6 -0
- data/lib/i18n_flow/cli/lint_command/markdown.erb +8 -2
- data/lib/i18n_flow/corrector.rb +40 -0
- data/lib/i18n_flow/formatter.rb +20 -0
- data/lib/i18n_flow/validator/errors.rb +27 -1
- data/lib/i18n_flow/validator/symmetry.rb +20 -13
- data/lib/i18n_flow/version.rb +1 -1
- data/lib/i18n_flow/yaml_ast_proxy.rb +36 -0
- data/lib/i18n_flow/yaml_ast_proxy/mapping.rb +14 -0
- data/lib/i18n_flow/yaml_ast_proxy/node.rb +8 -1
- data/lib/i18n_flow/yaml_ast_proxy/sequence.rb +1 -1
- data/spec/lib/i18n_flow/corrector_spec.rb +197 -0
- data/spec/lib/i18n_flow/formatter_spec.rb +137 -0
- data/spec/lib/i18n_flow/validator/symmetry_spec.rb +23 -2
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08d2cf8d25372c57651808089a31dedc34a52f81'
|
4
|
+
data.tar.gz: 2a96774f923502e0dd534c8b632acf4f7e5d232f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '03463815dc69ad05dfb2e4b048201441e212a165947a546f4b8e1828b72f32cda5e1f16ab0d335fe9881bc949cfad7a5fa0eb6acf6ebc50a312cfa5d2080b17b'
|
7
|
+
data.tar.gz: 4bd655b11d659a008101bd28f113c65d8d03ea59ab7fe969c1c702e2dabacca9b52c20da33f4e2b4c3981f9b74af73cc87dbc11ae96fa269f2bcfd00fdc7f591
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -6,7 +6,7 @@ i18n_flow (beta)
|
|
6
6
|
[](https://travis-ci.org/creasty/i18n_flow)
|
7
7
|
[](./LICENSE)
|
8
8
|
|
9
|
-
**Manage translation status in YAML file
|
9
|
+
**Manage translation status in YAML file.**<br>
|
10
10
|
With an official [tag](http://www.yaml.org/spec/1.2/spec.html#id2784064) feature, `i18n_flow` enables you to annotate status information directly in YAML file.
|
11
11
|
|
12
12
|

|
@@ -23,6 +23,9 @@ Setup
|
|
23
23
|
Add this line to your Gemfile:
|
24
24
|
|
25
25
|
```ruby
|
26
|
+
gem 'i18n_flow'
|
27
|
+
|
28
|
+
# To use the latest version:
|
26
29
|
gem 'i18n_flow', github: 'creasty/i18n_flow'
|
27
30
|
```
|
28
31
|
|
@@ -61,6 +64,7 @@ Options:
|
|
61
64
|
|
62
65
|
Commands:
|
63
66
|
lint Validate files
|
67
|
+
format Format and correct errors
|
64
68
|
search Search contents and keys
|
65
69
|
copy Copy translations and mark as todo
|
66
70
|
split Split a file into proper-sized files
|
data/lib/i18n_flow/cli.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
require_relative 'util'
|
2
2
|
|
3
3
|
class I18nFlow::CLI
|
4
|
-
require_relative 'cli/lint_command'
|
5
|
-
require_relative 'cli/split_command'
|
6
4
|
require_relative 'cli/copy_command'
|
7
|
-
require_relative 'cli/
|
8
|
-
require_relative 'cli/version_command'
|
5
|
+
require_relative 'cli/format_command'
|
9
6
|
require_relative 'cli/help_command'
|
7
|
+
require_relative 'cli/lint_command'
|
10
8
|
require_relative 'cli/read_config_command'
|
9
|
+
require_relative 'cli/search_command'
|
10
|
+
require_relative 'cli/split_command'
|
11
|
+
require_relative 'cli/version_command'
|
11
12
|
|
12
13
|
COMMANDS = {
|
14
|
+
'copy' => CopyCommand,
|
15
|
+
'format' => FormatCommand,
|
16
|
+
'help' => HelpCommand,
|
13
17
|
'lint' => LintCommand,
|
18
|
+
'read_config' => ReadConfigCommand,
|
14
19
|
'search' => SearchCommand,
|
15
20
|
'split' => SplitCommand,
|
16
|
-
'copy' => CopyCommand,
|
17
21
|
'version' => VersionCommand,
|
18
|
-
'help' => HelpCommand,
|
19
|
-
'read_config' => ReadConfigCommand,
|
20
22
|
}
|
21
23
|
|
22
24
|
attr_reader :args
|
@@ -2,6 +2,7 @@ require 'psych'
|
|
2
2
|
require_relative 'command_base'
|
3
3
|
require_relative '../util'
|
4
4
|
require_relative '../parser'
|
5
|
+
require_relative '../yaml_ast_proxy'
|
5
6
|
|
6
7
|
class I18nFlow::CLI
|
7
8
|
class CopyCommand < CommandBase
|
@@ -12,7 +13,7 @@ class I18nFlow::CLI
|
|
12
13
|
|
13
14
|
parser.parse!
|
14
15
|
|
15
|
-
mark_as_todo(parser.root_proxy)
|
16
|
+
I18nFlow::YamlAstProxy.mark_as_todo(parser.root_proxy)
|
16
17
|
|
17
18
|
if locale && first_key_node
|
18
19
|
first_key_node.value = locale
|
@@ -35,11 +36,7 @@ class I18nFlow::CLI
|
|
35
36
|
|
36
37
|
def first_key_node
|
37
38
|
return @first_key_node if defined?(@first_key_node)
|
38
|
-
@first_key_node = parser.root_proxy
|
39
|
-
.send(:indexed_object)
|
40
|
-
.node
|
41
|
-
.tap { |n| break unless n.is_a?(Psych::Nodes::Mapping) }
|
42
|
-
&.tap { |n| break n.children.first }
|
39
|
+
@first_key_node = I18nFlow::YamlAstProxy.first_key_node_of(parser.root_proxy)
|
43
40
|
end
|
44
41
|
|
45
42
|
private
|
@@ -47,23 +44,5 @@ class I18nFlow::CLI
|
|
47
44
|
def parser
|
48
45
|
@parser ||= I18nFlow::Parser.new(File.read(src_file), file_path: src_file)
|
49
46
|
end
|
50
|
-
|
51
|
-
def mark_as_todo(ast)
|
52
|
-
if ast.alias?
|
53
|
-
return
|
54
|
-
end
|
55
|
-
if ast.scalar?
|
56
|
-
ast.node.tag = '!todo'
|
57
|
-
|
58
|
-
# https://github.com/ruby/psych/blob/f30b65befa4f0a5a8548d482424a84a2383b0284/ext/psych/yaml/emitter.c#L1187
|
59
|
-
ast.node.plain = ast.node.quoted = false
|
60
|
-
|
61
|
-
return
|
62
|
-
end
|
63
|
-
|
64
|
-
ast.each do |k, v|
|
65
|
-
mark_as_todo(v)
|
66
|
-
end
|
67
|
-
end
|
68
47
|
end
|
69
48
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative 'command_base'
|
2
|
+
require_relative 'color'
|
3
|
+
require_relative '../repository'
|
4
|
+
require_relative '../formatter'
|
5
|
+
require_relative '../corrector'
|
6
|
+
|
7
|
+
class I18nFlow::CLI
|
8
|
+
class FormatCommand < CommandBase
|
9
|
+
include I18nFlow::CLI::Color
|
10
|
+
|
11
|
+
def invoke!
|
12
|
+
puts color('==> Correcting', :yellow)
|
13
|
+
repository.asts_by_scope.each do |scope, locale_trees|
|
14
|
+
locale_pairs.each do |(master, slave)|
|
15
|
+
master_tree = locale_trees[master]
|
16
|
+
slave_tree = locale_trees[slave]
|
17
|
+
next unless master_tree && slave_tree
|
18
|
+
|
19
|
+
puts slave_tree.file_path
|
20
|
+
correct(slave_tree, master_tree)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
puts
|
25
|
+
puts color('==> Formatting', :yellow)
|
26
|
+
repository.asts_by_path.each do |path, tree|
|
27
|
+
puts path
|
28
|
+
|
29
|
+
format(tree)
|
30
|
+
|
31
|
+
output_path = I18nFlow.config.base_path.join(tree.file_path)
|
32
|
+
File.write(output_path, tree.to_yaml)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def locale_pairs
|
39
|
+
I18nFlow.config.locale_pairs
|
40
|
+
end
|
41
|
+
|
42
|
+
def repository
|
43
|
+
@repository ||= I18nFlow::Repository.new(
|
44
|
+
base_path: I18nFlow.config.base_path,
|
45
|
+
glob_patterns: I18nFlow.config.glob_patterns,
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def correct(slave_tree, master_tree)
|
50
|
+
I18nFlow::Corrector.new(slave_tree, master_tree).correct!
|
51
|
+
end
|
52
|
+
|
53
|
+
def format(tree)
|
54
|
+
I18nFlow::Formatter.new(tree).format!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -27,9 +27,15 @@
|
|
27
27
|
<%- when I18nFlow::Validator::InvalidTodoError -%>
|
28
28
|
Todo cannot be annotated on a mapping/sequence
|
29
29
|
<%- when I18nFlow::Validator::TodoContentError -%>
|
30
|
+
<%- if err.inverse -%>
|
31
|
+
It has "!todo" but the content diverges from the foreign file
|
32
|
+
foreign: <%= err.expect %>
|
33
|
+
master: <%= err.actual %>
|
34
|
+
<%- else -%>
|
30
35
|
It has "!todo" but the content diverges from the master file
|
31
36
|
master: <%= err.expect %>
|
32
37
|
foreign: <%= err.actual %>
|
38
|
+
<%- end -%>
|
33
39
|
<%- when I18nFlow::Validator::InvalidLocaleError -%>
|
34
40
|
It has "!only" but the locale is invalid
|
35
41
|
valid: [<%= err.expect.join(', ') %>]
|
@@ -32,9 +32,15 @@ An extra key found
|
|
32
32
|
<%- when I18nFlow::Validator::InvalidTodoError -%>
|
33
33
|
Todo cannot be annotated on a mapping/sequence
|
34
34
|
<%- when I18nFlow::Validator::TodoContentError -%>
|
35
|
+
<%- if err.inverse -%>
|
36
|
+
It has "!todo" but the content diverges from the foreign file
|
37
|
+
foreign: <%= err.expect %>
|
38
|
+
master: <%= err.actual %>
|
39
|
+
<%- else -%>
|
35
40
|
It has "!todo" but the content diverges from the master file
|
36
|
-
master:
|
37
|
-
foreign:
|
41
|
+
master: <%= err.expect %>
|
42
|
+
foreign: <%= err.actual %>
|
43
|
+
<%- end -%>
|
38
44
|
<%- when I18nFlow::Validator::InvalidLocaleError -%>
|
39
45
|
It has "!only" but the locale is invalid
|
40
46
|
valid: `[<%= err.expect.join(', ') %>]`
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'validator/symmetry'
|
2
|
+
require_relative 'validator/errors'
|
3
|
+
require_relative 'yaml_ast_proxy'
|
4
|
+
|
5
|
+
class I18nFlow::Corrector
|
6
|
+
attr_reader :ast_1
|
7
|
+
attr_reader :ast_2
|
8
|
+
|
9
|
+
def initialize(ast_1, ast_2)
|
10
|
+
@ast_1 = ast_1
|
11
|
+
@ast_2 = ast_2
|
12
|
+
end
|
13
|
+
|
14
|
+
def correct!
|
15
|
+
n1, n2 = [ast_1, ast_2]
|
16
|
+
.map { |n| I18nFlow::YamlAstProxy.first_value_node_of(n) }
|
17
|
+
.map { |n| I18nFlow::YamlAstProxy.create(n) }
|
18
|
+
|
19
|
+
errors = I18nFlow::Validator::Symmetry.new(n2, n1)
|
20
|
+
.tap(&:validate!)
|
21
|
+
.errors
|
22
|
+
|
23
|
+
errors.each do |error|
|
24
|
+
case error
|
25
|
+
when I18nFlow::Validator::MissingKeyError
|
26
|
+
src_node = error.src_node.clone
|
27
|
+
I18nFlow::YamlAstProxy.mark_as_todo(src_node)
|
28
|
+
error.dest_node[error.dest_key] = src_node.node
|
29
|
+
when I18nFlow::Validator::ExtraKeyError
|
30
|
+
if error.dest_node.mapping?
|
31
|
+
error.dest_node.delete(error.dest_key)
|
32
|
+
elsif error.dest_node.sequence?
|
33
|
+
error.dest_node.delete_at(error.dest_key)
|
34
|
+
end
|
35
|
+
when I18nFlow::Validator::TodoContentError
|
36
|
+
error.dest_node.node.value = error.src_node.node.value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class I18nFlow::Formatter
|
2
|
+
attr_reader :ast
|
3
|
+
|
4
|
+
def initialize(ast)
|
5
|
+
@ast = ast
|
6
|
+
end
|
7
|
+
|
8
|
+
def format!
|
9
|
+
sort_keys(ast)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def sort_keys(node)
|
15
|
+
node.sort_keys! if node.mapping?
|
16
|
+
node.each do |_, val|
|
17
|
+
sort_keys(val) if val.mapping?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -36,17 +36,34 @@ module I18nFlow::Validator
|
|
36
36
|
end
|
37
37
|
|
38
38
|
class MissingKeyError < Error
|
39
|
+
attr_reader :dest_node
|
40
|
+
attr_reader :dest_key
|
41
|
+
attr_reader :src_node
|
42
|
+
|
39
43
|
def initialize(key, single: false)
|
40
44
|
super(key)
|
41
45
|
@single = single
|
42
46
|
end
|
47
|
+
|
48
|
+
def set_correction_context(dest_node:, dest_key:, src_node:)
|
49
|
+
@dest_node, @dest_key, @src_node = dest_node, dest_key, src_node
|
50
|
+
self
|
51
|
+
end
|
43
52
|
end
|
44
53
|
|
45
54
|
class ExtraKeyError < Error
|
55
|
+
attr_reader :dest_node
|
56
|
+
attr_reader :dest_key
|
57
|
+
|
46
58
|
def initialize(key, single: false)
|
47
59
|
super(key)
|
48
60
|
@single = single
|
49
61
|
end
|
62
|
+
|
63
|
+
def set_correction_context(dest_node:, dest_key:)
|
64
|
+
@dest_node, @dest_key = dest_node, dest_key
|
65
|
+
self
|
66
|
+
end
|
50
67
|
end
|
51
68
|
|
52
69
|
class InvalidTodoError < Error
|
@@ -55,16 +72,25 @@ module I18nFlow::Validator
|
|
55
72
|
class TodoContentError < Error
|
56
73
|
attr_reader :expect
|
57
74
|
attr_reader :actual
|
75
|
+
attr_reader :dest_node
|
76
|
+
attr_reader :src_node
|
77
|
+
attr_reader :inverse
|
58
78
|
|
59
|
-
def initialize(key, expect:, actual:)
|
79
|
+
def initialize(key, expect:, actual:, inverse:)
|
60
80
|
super(key)
|
61
81
|
@expect = expect
|
62
82
|
@actual = actual
|
83
|
+
@inverse = inverse
|
63
84
|
end
|
64
85
|
|
65
86
|
def data
|
66
87
|
super + [expect, actual]
|
67
88
|
end
|
89
|
+
|
90
|
+
def set_correction_context(dest_node:, src_node:)
|
91
|
+
@dest_node, @src_node = dest_node, src_node
|
92
|
+
self
|
93
|
+
end
|
68
94
|
end
|
69
95
|
|
70
96
|
class InvalidLocaleError < Error
|
@@ -39,7 +39,7 @@ module I18nFlow::Validator
|
|
39
39
|
return
|
40
40
|
end
|
41
41
|
|
42
|
-
check_asymmetric_key(n1, n2, t2)&.tap do |err|
|
42
|
+
check_asymmetric_key(n1, n2, t2, key)&.tap do |err|
|
43
43
|
errors << err if err
|
44
44
|
return
|
45
45
|
end
|
@@ -112,28 +112,35 @@ module I18nFlow::Validator
|
|
112
112
|
InvalidTypeError.new(n2.full_key).set_location(n2)
|
113
113
|
end
|
114
114
|
|
115
|
-
def check_asymmetric_key(n1, n2, t2)
|
115
|
+
def check_asymmetric_key(n1, n2, t2, key)
|
116
116
|
return false if n1&.ignored_violation == :key || n2&.ignored_violation == :key
|
117
117
|
return if n1 && n2
|
118
118
|
|
119
119
|
if n1
|
120
120
|
full_key = [t2.locale, *n1.scopes.drop(1)].join('.')
|
121
|
-
MissingKeyError.new(full_key)
|
121
|
+
MissingKeyError.new(full_key)
|
122
|
+
.set_location(t2)
|
123
|
+
.set_correction_context(dest_node: t2, dest_key: key, src_node: n1)
|
122
124
|
else
|
123
|
-
ExtraKeyError.new(n2.full_key)
|
125
|
+
ExtraKeyError.new(n2.full_key)
|
126
|
+
.set_location(n2)
|
127
|
+
.set_correction_context(dest_node: t2, dest_key: key)
|
124
128
|
end
|
125
129
|
end
|
126
130
|
|
127
131
|
def check_todo_tag(n1, n2)
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
elsif n2.
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
132
|
+
if n1.scalar? && n1.marked_as_todo? && !n2.marked_as_todo? && n2.value != n1.value
|
133
|
+
TodoContentError.new(n1.full_key, expect: n2.value, actual: n1.value, inverse: true)
|
134
|
+
.set_location(n1)
|
135
|
+
.set_correction_context(dest_node: n1, src_node: n2)
|
136
|
+
elsif n2.marked_as_todo?
|
137
|
+
if !n2.scalar?
|
138
|
+
InvalidTodoError.new(n2.full_key).set_location(n2)
|
139
|
+
elsif n2.value != n1.value
|
140
|
+
TodoContentError.new(n2.full_key, expect: n1.value, actual: n2.value, inverse: false)
|
141
|
+
.set_location(n2)
|
142
|
+
.set_correction_context(dest_node: n2, src_node: n1)
|
143
|
+
end
|
137
144
|
end
|
138
145
|
end
|
139
146
|
|
data/lib/i18n_flow/version.rb
CHANGED
@@ -54,4 +54,40 @@ module I18nFlow::YamlAstProxy
|
|
54
54
|
stream.children << doc
|
55
55
|
create(stream)
|
56
56
|
end
|
57
|
+
|
58
|
+
def self.mark_as_todo(ast)
|
59
|
+
if ast.alias?
|
60
|
+
return
|
61
|
+
end
|
62
|
+
if ast.scalar?
|
63
|
+
ast.node.tag = '!todo'
|
64
|
+
|
65
|
+
# https://github.com/ruby/psych/blob/f30b65befa4f0a5a8548d482424a84a2383b0284/ext/psych/yaml/emitter.c#L1187
|
66
|
+
ast.node.plain = ast.node.quoted = false
|
67
|
+
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
ast.each do |k, v|
|
72
|
+
mark_as_todo(v)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.first_key_node_of(node)
|
77
|
+
first_node_of(node, 0)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.first_value_node_of(node)
|
81
|
+
first_node_of(node, 1)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def self.first_node_of(node, layout_offset)
|
87
|
+
node
|
88
|
+
.send(:indexed_object)
|
89
|
+
.node
|
90
|
+
.tap { |n| break unless n.is_a?(Psych::Nodes::Mapping) }
|
91
|
+
&.tap { |n| break n.children[layout_offset] }
|
92
|
+
end
|
57
93
|
end
|
@@ -25,6 +25,12 @@ module I18nFlow::YamlAstProxy
|
|
25
25
|
end
|
26
26
|
alias []= set
|
27
27
|
|
28
|
+
def delete(key)
|
29
|
+
indexed_object.delete(key)
|
30
|
+
cache.delete(key)
|
31
|
+
synchronize!
|
32
|
+
end
|
33
|
+
|
28
34
|
def batch
|
29
35
|
@locked = true
|
30
36
|
yield
|
@@ -49,6 +55,14 @@ module I18nFlow::YamlAstProxy
|
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
58
|
+
def sort_keys!
|
59
|
+
@indexed_object = indexed_object
|
60
|
+
.sort_by { |k, v| [v.is_a?(Psych::Nodes::Mapping) ? 1 : 0, k] }
|
61
|
+
.to_h
|
62
|
+
@cache = nil
|
63
|
+
synchronize!
|
64
|
+
end
|
65
|
+
|
52
66
|
private
|
53
67
|
|
54
68
|
def cache
|
@@ -10,7 +10,8 @@ module I18nFlow::YamlAstProxy
|
|
10
10
|
TAG_TODO = /^!todo(?::([,a-zA-Z_-]+))?$/
|
11
11
|
TAG_ONLY = /^!only(?::([,a-zA-Z_-]+))?$/
|
12
12
|
|
13
|
-
|
13
|
+
attr_accessor :node
|
14
|
+
protected :node=
|
14
15
|
attr_reader :parent
|
15
16
|
attr_reader :scopes
|
16
17
|
attr_reader :file_path
|
@@ -87,6 +88,12 @@ module I18nFlow::YamlAstProxy
|
|
87
88
|
indexed_object.keys
|
88
89
|
end
|
89
90
|
|
91
|
+
def clone
|
92
|
+
super.tap do |n|
|
93
|
+
n.node = node.clone
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
90
97
|
private
|
91
98
|
|
92
99
|
def identity_data
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'i18n_flow/corrector'
|
2
|
+
|
3
|
+
describe I18nFlow::Corrector do
|
4
|
+
def correct_ast(ast_1, ast_2 = nil)
|
5
|
+
I18nFlow::Corrector.new(ast_1, ast_2)
|
6
|
+
.tap(&:correct!)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#correct!' do
|
10
|
+
it 'should complement missing keys and mark as !todo' do
|
11
|
+
ast_1 = parse_yaml(<<-YAML)
|
12
|
+
es:
|
13
|
+
alfa: 'a'
|
14
|
+
delta: 'd'
|
15
|
+
echo: 'e'
|
16
|
+
golf: 'g'
|
17
|
+
hotel: 'h'
|
18
|
+
YAML
|
19
|
+
ast_2 = parse_yaml(<<-YAML)
|
20
|
+
en:
|
21
|
+
alfa: 'A'
|
22
|
+
bravo: 'B'
|
23
|
+
charlie: 'C'
|
24
|
+
delta: 'D'
|
25
|
+
echo: 'E'
|
26
|
+
foxtrot: !only 'F'
|
27
|
+
golf: 'G'
|
28
|
+
hotel: 'H'
|
29
|
+
YAML
|
30
|
+
result = parse_yaml(<<-YAML)
|
31
|
+
es:
|
32
|
+
alfa: 'a'
|
33
|
+
delta: 'd'
|
34
|
+
echo: 'e'
|
35
|
+
golf: 'g'
|
36
|
+
hotel: 'h'
|
37
|
+
bravo: !todo 'B'
|
38
|
+
charlie: !todo 'C'
|
39
|
+
YAML
|
40
|
+
|
41
|
+
corrected = correct_ast(ast_1, ast_2)
|
42
|
+
expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should complement missing elements in a sequence' do
|
46
|
+
ast_1 = parse_yaml(<<-YAML)
|
47
|
+
es:
|
48
|
+
foo:
|
49
|
+
- 'one'
|
50
|
+
- 'two'
|
51
|
+
YAML
|
52
|
+
ast_2 = parse_yaml(<<-YAML)
|
53
|
+
en:
|
54
|
+
foo:
|
55
|
+
- 'ONE'
|
56
|
+
- 'TWO'
|
57
|
+
- 'THREE'
|
58
|
+
- !only 'FOUR'
|
59
|
+
YAML
|
60
|
+
result = parse_yaml(<<-YAML)
|
61
|
+
es:
|
62
|
+
foo:
|
63
|
+
- 'one'
|
64
|
+
- 'two'
|
65
|
+
- !todo 'THREE'
|
66
|
+
YAML
|
67
|
+
|
68
|
+
corrected = correct_ast(ast_1, ast_2)
|
69
|
+
expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should delete extra keys which are not marked as !only' do
|
73
|
+
ast_1 = parse_yaml(<<-YAML)
|
74
|
+
es:
|
75
|
+
alfa: 'a'
|
76
|
+
bravo: 'b'
|
77
|
+
charlie: 'c'
|
78
|
+
delta: 'd'
|
79
|
+
echo: 'e'
|
80
|
+
foxtrot: !only 'f'
|
81
|
+
golf: 'g'
|
82
|
+
hotel: 'h'
|
83
|
+
YAML
|
84
|
+
ast_2 = parse_yaml(<<-YAML)
|
85
|
+
en:
|
86
|
+
alfa: 'A'
|
87
|
+
bravo: 'B'
|
88
|
+
charlie: 'C'
|
89
|
+
golf: 'G'
|
90
|
+
hotel: 'H'
|
91
|
+
YAML
|
92
|
+
result = parse_yaml(<<-YAML)
|
93
|
+
es:
|
94
|
+
alfa: 'a'
|
95
|
+
bravo: 'b'
|
96
|
+
charlie: 'c'
|
97
|
+
foxtrot: !only 'f'
|
98
|
+
golf: 'g'
|
99
|
+
hotel: 'h'
|
100
|
+
YAML
|
101
|
+
|
102
|
+
corrected = correct_ast(ast_1, ast_2)
|
103
|
+
expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should delete extra elements in a sequence' do
|
107
|
+
ast_1 = parse_yaml(<<-YAML)
|
108
|
+
es:
|
109
|
+
foo:
|
110
|
+
- 'one'
|
111
|
+
- 'two'
|
112
|
+
- 'three'
|
113
|
+
- !only 'four'
|
114
|
+
YAML
|
115
|
+
ast_2 = parse_yaml(<<-YAML)
|
116
|
+
en:
|
117
|
+
foo:
|
118
|
+
- 'ONE'
|
119
|
+
- 'TWO'
|
120
|
+
YAML
|
121
|
+
result = parse_yaml(<<-YAML)
|
122
|
+
es:
|
123
|
+
foo:
|
124
|
+
- 'one'
|
125
|
+
- 'two'
|
126
|
+
- !only 'four'
|
127
|
+
YAML
|
128
|
+
|
129
|
+
corrected = correct_ast(ast_1, ast_2)
|
130
|
+
expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should update translations with !todo according to the source' do
|
134
|
+
ast_1 = parse_yaml(<<-YAML)
|
135
|
+
es:
|
136
|
+
foo: !todo 'outdated'
|
137
|
+
YAML
|
138
|
+
ast_2 = parse_yaml(<<-YAML)
|
139
|
+
en:
|
140
|
+
foo: 'latest'
|
141
|
+
YAML
|
142
|
+
result = parse_yaml(<<-YAML)
|
143
|
+
es:
|
144
|
+
foo: !todo 'latest'
|
145
|
+
YAML
|
146
|
+
|
147
|
+
corrected = correct_ast(ast_1, ast_2)
|
148
|
+
expect(corrected.ast_1.to_yaml).to eq(result.to_yaml)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should update translations with !todo (works bidirectionally)' do
|
152
|
+
ast_1 = parse_yaml(<<-YAML)
|
153
|
+
es:
|
154
|
+
foo: 'latest'
|
155
|
+
YAML
|
156
|
+
ast_2 = parse_yaml(<<-YAML)
|
157
|
+
en:
|
158
|
+
foo: !todo 'outdated'
|
159
|
+
YAML
|
160
|
+
result_1 = parse_yaml(<<-YAML)
|
161
|
+
es:
|
162
|
+
foo: 'latest'
|
163
|
+
YAML
|
164
|
+
result_2 = parse_yaml(<<-YAML)
|
165
|
+
en:
|
166
|
+
foo: !todo 'latest'
|
167
|
+
YAML
|
168
|
+
|
169
|
+
corrected = correct_ast(ast_1, ast_2)
|
170
|
+
expect(corrected.ast_1.to_yaml).to eq(result_1.to_yaml)
|
171
|
+
expect(corrected.ast_2.to_yaml).to eq(result_2.to_yaml)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should update translations with !todo (synchronize)' do
|
175
|
+
ast_1 = parse_yaml(<<-YAML)
|
176
|
+
es:
|
177
|
+
foo: !todo 'outdated'
|
178
|
+
YAML
|
179
|
+
ast_2 = parse_yaml(<<-YAML)
|
180
|
+
en:
|
181
|
+
foo: !todo 'latest'
|
182
|
+
YAML
|
183
|
+
result_1 = parse_yaml(<<-YAML)
|
184
|
+
es:
|
185
|
+
foo: !todo 'latest'
|
186
|
+
YAML
|
187
|
+
result_2 = parse_yaml(<<-YAML)
|
188
|
+
en:
|
189
|
+
foo: !todo 'latest'
|
190
|
+
YAML
|
191
|
+
|
192
|
+
corrected = correct_ast(ast_1, ast_2)
|
193
|
+
expect(corrected.ast_1.to_yaml).to eq(result_1.to_yaml)
|
194
|
+
expect(corrected.ast_2.to_yaml).to eq(result_2.to_yaml)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'i18n_flow/formatter'
|
2
|
+
|
3
|
+
describe I18nFlow::Formatter do
|
4
|
+
def format_ast(ast)
|
5
|
+
I18nFlow::Formatter.new(ast)
|
6
|
+
.tap(&:format!)
|
7
|
+
.ast
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#format!' do
|
11
|
+
it 'should remove extra whitespaces' do
|
12
|
+
ast = parse_yaml(<<-YAML)
|
13
|
+
en:
|
14
|
+
alfa: 'A'
|
15
|
+
|
16
|
+
bravo: 'B'
|
17
|
+
charlie: 'C'
|
18
|
+
delta: 'D'
|
19
|
+
echo: 'E'
|
20
|
+
foxtrot: 'F'
|
21
|
+
|
22
|
+
golf: 'G'
|
23
|
+
hotel: 'H'
|
24
|
+
YAML
|
25
|
+
result = parse_yaml(<<-YAML)
|
26
|
+
en:
|
27
|
+
alfa: 'A'
|
28
|
+
bravo: 'B'
|
29
|
+
charlie: 'C'
|
30
|
+
delta: 'D'
|
31
|
+
echo: 'E'
|
32
|
+
foxtrot: 'F'
|
33
|
+
golf: 'G'
|
34
|
+
hotel: 'H'
|
35
|
+
YAML
|
36
|
+
|
37
|
+
formatted = format_ast(ast)
|
38
|
+
expect(formatted.to_yaml).to eq(result.to_yaml)
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'sort keys' do
|
42
|
+
it 'should sort keys' do
|
43
|
+
ast = parse_yaml(<<-YAML)
|
44
|
+
en:
|
45
|
+
echo: 'E'
|
46
|
+
delta: 'D'
|
47
|
+
foxtrot: 'F'
|
48
|
+
alfa: 'A'
|
49
|
+
hotel: 'H'
|
50
|
+
bravo: 'B'
|
51
|
+
charlie: 'C'
|
52
|
+
golf: 'G'
|
53
|
+
YAML
|
54
|
+
result = parse_yaml(<<-YAML)
|
55
|
+
en:
|
56
|
+
alfa: 'A'
|
57
|
+
bravo: 'B'
|
58
|
+
charlie: 'C'
|
59
|
+
delta: 'D'
|
60
|
+
echo: 'E'
|
61
|
+
foxtrot: 'F'
|
62
|
+
golf: 'G'
|
63
|
+
hotel: 'H'
|
64
|
+
YAML
|
65
|
+
|
66
|
+
formatted = format_ast(ast)
|
67
|
+
expect(formatted.to_yaml).to eq(result.to_yaml)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should sort keys recursively' do
|
71
|
+
ast = parse_yaml(<<-YAML)
|
72
|
+
en:
|
73
|
+
alfa: 'A'
|
74
|
+
bravo:
|
75
|
+
delta: 'D'
|
76
|
+
charlie: 'C'
|
77
|
+
echo:
|
78
|
+
foxtrot: 'F'
|
79
|
+
hotel: 'H'
|
80
|
+
golf: 'G'
|
81
|
+
YAML
|
82
|
+
result = parse_yaml(<<-YAML)
|
83
|
+
en:
|
84
|
+
alfa: 'A'
|
85
|
+
bravo:
|
86
|
+
charlie: 'C'
|
87
|
+
delta: 'D'
|
88
|
+
echo:
|
89
|
+
foxtrot: 'F'
|
90
|
+
golf: 'G'
|
91
|
+
hotel: 'H'
|
92
|
+
YAML
|
93
|
+
|
94
|
+
formatted = format_ast(ast)
|
95
|
+
expect(formatted.to_yaml).to eq(result.to_yaml)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should move mappings at the bottom' do
|
99
|
+
ast = parse_yaml(<<-YAML)
|
100
|
+
en:
|
101
|
+
alfa: 'A'
|
102
|
+
bravo: 'B'
|
103
|
+
charlie:
|
104
|
+
- 'one'
|
105
|
+
- 'two'
|
106
|
+
delta: 'D'
|
107
|
+
echo: 'E'
|
108
|
+
foxtrot:
|
109
|
+
bar: 'bar'
|
110
|
+
foo: 'foo'
|
111
|
+
golf: 'G'
|
112
|
+
hotel:
|
113
|
+
baz: 'baz'
|
114
|
+
YAML
|
115
|
+
result = parse_yaml(<<-YAML)
|
116
|
+
en:
|
117
|
+
alfa: 'A'
|
118
|
+
bravo: 'B'
|
119
|
+
charlie:
|
120
|
+
- 'one'
|
121
|
+
- 'two'
|
122
|
+
delta: 'D'
|
123
|
+
echo: 'E'
|
124
|
+
golf: 'G'
|
125
|
+
foxtrot:
|
126
|
+
bar: 'bar'
|
127
|
+
foo: 'foo'
|
128
|
+
hotel:
|
129
|
+
baz: 'baz'
|
130
|
+
YAML
|
131
|
+
|
132
|
+
formatted = format_ast(ast)
|
133
|
+
expect(formatted.to_yaml).to eq(result.to_yaml)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -377,7 +377,7 @@ describe I18nFlow::Validator::Symmetry do
|
|
377
377
|
])
|
378
378
|
end
|
379
379
|
|
380
|
-
it 'should fail if texts are different' do
|
380
|
+
it 'should fail if texts are different from the master' do
|
381
381
|
ast_1 = parse_yaml(<<-YAML)['en']
|
382
382
|
en:
|
383
383
|
key_1: text_1
|
@@ -394,7 +394,28 @@ describe I18nFlow::Validator::Symmetry do
|
|
394
394
|
validator.validate!
|
395
395
|
|
396
396
|
expect(validator.errors).to eq([
|
397
|
-
I18nFlow::Validator::TodoContentError.new('ja.key_2', expect: 'text_2', actual: 'text_9'),
|
397
|
+
I18nFlow::Validator::TodoContentError.new('ja.key_2', expect: 'text_2', actual: 'text_9', inverse: false),
|
398
|
+
])
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'should fail if texts are different from the foreign' do
|
402
|
+
ast_1 = parse_yaml(<<-YAML)['en']
|
403
|
+
en:
|
404
|
+
key_1: text_1
|
405
|
+
key_2: !todo text_9
|
406
|
+
YAML
|
407
|
+
ast_2 = parse_yaml(<<-YAML)['ja']
|
408
|
+
ja:
|
409
|
+
key_1: text_1
|
410
|
+
key_2: text_2
|
411
|
+
YAML
|
412
|
+
|
413
|
+
allow(validator).to receive(:ast_1).and_return(ast_1)
|
414
|
+
allow(validator).to receive(:ast_2).and_return(ast_2)
|
415
|
+
validator.validate!
|
416
|
+
|
417
|
+
expect(validator.errors).to eq([
|
418
|
+
I18nFlow::Validator::TodoContentError.new('en.key_2', expect: 'text_2', actual: 'text_9', inverse: true),
|
398
419
|
])
|
399
420
|
end
|
400
421
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i18n_flow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuki Iwanaga
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-07-
|
11
|
+
date: 2019-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: psych
|
@@ -125,6 +125,7 @@ files:
|
|
125
125
|
- lib/i18n_flow/cli/color.rb
|
126
126
|
- lib/i18n_flow/cli/command_base.rb
|
127
127
|
- lib/i18n_flow/cli/copy_command.rb
|
128
|
+
- lib/i18n_flow/cli/format_command.rb
|
128
129
|
- lib/i18n_flow/cli/help_command.rb
|
129
130
|
- lib/i18n_flow/cli/lint_command.rb
|
130
131
|
- lib/i18n_flow/cli/lint_command/ascii.erb
|
@@ -140,6 +141,8 @@ files:
|
|
140
141
|
- lib/i18n_flow/cli/split_command.rb
|
141
142
|
- lib/i18n_flow/cli/version_command.rb
|
142
143
|
- lib/i18n_flow/configuration.rb
|
144
|
+
- lib/i18n_flow/corrector.rb
|
145
|
+
- lib/i18n_flow/formatter.rb
|
143
146
|
- lib/i18n_flow/parser.rb
|
144
147
|
- lib/i18n_flow/repository.rb
|
145
148
|
- lib/i18n_flow/search.rb
|
@@ -162,6 +165,8 @@ files:
|
|
162
165
|
- spec/lib/i18n_flow/cli/help_command_spec.rb
|
163
166
|
- spec/lib/i18n_flow/cli/version_command_spec.rb
|
164
167
|
- spec/lib/i18n_flow/configuration_spec.rb
|
168
|
+
- spec/lib/i18n_flow/corrector_spec.rb
|
169
|
+
- spec/lib/i18n_flow/formatter_spec.rb
|
165
170
|
- spec/lib/i18n_flow/repository_spec.rb
|
166
171
|
- spec/lib/i18n_flow/splitter/merger_spec.rb
|
167
172
|
- spec/lib/i18n_flow/util_spec.rb
|
@@ -201,6 +206,8 @@ test_files:
|
|
201
206
|
- spec/lib/i18n_flow/cli/help_command_spec.rb
|
202
207
|
- spec/lib/i18n_flow/cli/version_command_spec.rb
|
203
208
|
- spec/lib/i18n_flow/configuration_spec.rb
|
209
|
+
- spec/lib/i18n_flow/corrector_spec.rb
|
210
|
+
- spec/lib/i18n_flow/formatter_spec.rb
|
204
211
|
- spec/lib/i18n_flow/repository_spec.rb
|
205
212
|
- spec/lib/i18n_flow/splitter/merger_spec.rb
|
206
213
|
- spec/lib/i18n_flow/util_spec.rb
|