i18n-tasks 0.9.7 → 0.9.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cec4fddc37a4bbc56edf707c49e247e8418a8199
4
- data.tar.gz: 38edd490aaa36f481f2c0ae7868649c87ebad8ef
3
+ metadata.gz: d6e30b4e85fe299dbf8f89391c84f5dea2d242b2
4
+ data.tar.gz: 9f6d31de1c84187fbad5e929c36416857f66f6e5
5
5
  SHA512:
6
- metadata.gz: 2a727430cb2f8ca4ad602db64766da00f6eb9711be9a1425096c18dff6906c60b8af52defdecb67a54521ec7edd18bb7a7487d093aebabbebf5f3e8cc6a58dfc
7
- data.tar.gz: 2ec48ffcff555fbb597deed2270a0b0c5d2a859d29069751ff681429292c6652be5c57106a9a5ab22326bce32183cfb1ea36aaeaf70f20ea4303e74330a7fbcf
6
+ metadata.gz: a3602fac64546ce66b2b500c3f2e010f01458e1cbd0ff708cbd5e329cca144574a47003ccb8533441e033de579147fbd00c6cb84f90c509720228d2a5e407f53
7
+ data.tar.gz: b32c4925376a24e28cf1882d1e10168374e1c8770e3e7b43028c9820a11a2b2e59515b2b260fe4fdd03462789b0710528c779c7316155df214e11bfe75d1c8ee
data/README.md CHANGED
@@ -24,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
24
24
  Add i18n-tasks to the Gemfile:
25
25
 
26
26
  ```ruby
27
- gem 'i18n-tasks', '~> 0.9.7'
27
+ gem 'i18n-tasks', '~> 0.9.8'
28
28
  ```
29
29
 
30
30
  Copy the default [configuration file](#configuration):
@@ -122,6 +122,50 @@ Sort the keys, and move them to the respective files as defined by [`config.writ
122
122
  $ i18n-tasks normalize -p
123
123
  ```
124
124
 
125
+ ### Move / rename / merge keys
126
+
127
+ `i18n-tasks mv <pattern> <target>` is a versatile task to move or delete keys matching the given pattern.
128
+
129
+ All nodes (leafs or subtrees) matching [`<pattern>`](#key-pattern-syntax) are merged together and moved to `<target>`.
130
+
131
+ Rename a node (leaf or subtree):
132
+
133
+ ``` console
134
+ $ i18n-tasks mv user account
135
+ ```
136
+
137
+ Move a node:
138
+
139
+ ``` console
140
+ $ i18n-tasks mv user_alerts user.alerts
141
+ ```
142
+
143
+ Move the children one level up:
144
+
145
+ ``` console
146
+ $ i18n-tasks mv 'alerts.{:}' '\1'
147
+ ```
148
+
149
+ Merge-move multiple nodes:
150
+
151
+ ``` console
152
+ $ i18n-tasks mv '{user,profile}' account
153
+ ```
154
+
155
+ Merge (non-leaf) nodes into parent:
156
+
157
+ ``` console
158
+ $ i18n-tasks mv '{pages}.{a,b}' '\1'
159
+ ```
160
+
161
+ ### Delete keys
162
+
163
+ Delete the keys by using the `rm` task:
164
+
165
+ ```console
166
+ $ i18n-tasks rm 'user.{old_profile,old_title}' another_key
167
+ ```
168
+
125
169
  ### Compose tasks
126
170
 
127
171
  `i18n-tasks` also provides composable tasks for reading, writing and manipulating locale data. Examples below.
@@ -40,12 +40,15 @@ en:
40
40
  health: is everything OK?
41
41
  irb: start REPL session within i18n-tasks context
42
42
  missing: show missing translations
43
+ mv: rename/merge the keys in locale data that match the given pattern
43
44
  normalize: 'normalize translation data: sort and move to the right files'
44
45
  remove_unused: remove unused keys
46
+ rm: remove the keys in locale data that match the given pattern
45
47
  translate_missing: translate missing keys with Google Translate
46
48
  tree_convert: convert tree between formats
47
49
  tree_filter: filter tree by key pattern
48
50
  tree_merge: merge trees
51
+ tree_mv_key: rename/merge/remove the keys matching the given pattern
49
52
  tree_rename_key: rename tree node
50
53
  tree_set_value: set values of keys, optionally match a pattern
51
54
  tree_subtract: tree A minus the keys in tree B
@@ -38,12 +38,15 @@ ru:
38
38
  health: Всё ОК?
39
39
  irb: начать REPL сессию в контексте i18n-tasks
40
40
  missing: показать недостающие переводы
41
+ mv: переименовать / объединить ключи, которые соответствуют заданному шаблону
41
42
  normalize: нормализовать файлы переводов (сортировка и распределение)
42
43
  remove_unused: удалить неиспользуемые ключи
44
+ rm: удалить ключи, которые соответствуют заданному шаблону
43
45
  translate_missing: перевести недостающие переводы с Google Translate
44
46
  tree_convert: преобразовать дерево между форматами
45
47
  tree_filter: фильтровать дерево по ключу
46
48
  tree_merge: объединенить деревья
49
+ tree_mv_key: переименованить / объединить / удалить ключи соответствующие заданному шаблону
47
50
  tree_rename_key: переименовать узел дерева
48
51
  tree_set_value: заменить значения ключей
49
52
  tree_subtract: дерево A минус ключи в дереве B
@@ -19,6 +19,32 @@ module I18n::Tasks
19
19
  i18n.normalize_store! opt[:locales], opt[:pattern_router]
20
20
  end
21
21
 
22
+ cmd :mv,
23
+ pos: 'FROM_KEY_PATTERN TO_KEY_PATTERN',
24
+ desc: t('i18n_tasks.cmd.desc.mv')
25
+ def mv(opt = {})
26
+ fail CommandError, 'requires FROM_KEY_PATTERN and TO_KEY_PATTERN' if opt[:arguments].size < 2
27
+ from_pattern = opt[:arguments].shift
28
+ to_pattern = opt[:arguments].shift
29
+ forest = i18n.data_forest
30
+ results = forest.mv_key!(compile_key_pattern(from_pattern), to_pattern, root: false)
31
+ i18n.data.write forest
32
+ terminal_report.mv_results results
33
+ end
34
+
35
+ cmd :rm,
36
+ pos: 'KEY_PATTERN [KEY_PATTERN...]',
37
+ desc: t('i18n_tasks.cmd.desc.rm')
38
+ def rm(opt = {})
39
+ fail CommandError, 'requires KEY_PATTERN' if opt[:arguments].empty?
40
+ forest = i18n.data_forest
41
+ results = opt[:arguments].each_with_object({}) do |key_pattern, h|
42
+ h.merge! forest.mv_key!(compile_key_pattern(key_pattern), '', root: false)
43
+ end
44
+ i18n.data.write forest
45
+ terminal_report.mv_results results
46
+ end
47
+
22
48
  cmd :data,
23
49
  pos: '[locale ...]',
24
50
  desc: t('i18n_tasks.cmd.desc.data'),
@@ -4,6 +4,7 @@ module I18n::Tasks
4
4
  module Commands
5
5
  module Tree
6
6
  include Command::Collection
7
+ include I18n::Tasks::KeyPatternMatching
7
8
 
8
9
  cmd :tree_translate,
9
10
  pos: '[tree (or stdin)]',
@@ -47,6 +48,7 @@ module I18n::Tasks
47
48
  :data_format]
48
49
 
49
50
  def tree_rename_key(opt = {})
51
+ warn_deprecated 'Use tree-mv instead.'
50
52
  key = arg_or_pos! :key, opt
51
53
  name = arg_or_pos! :name, opt
52
54
  forest = forest_pos_or_stdin! opt
@@ -56,6 +58,19 @@ module I18n::Tasks
56
58
  print_forest forest, opt
57
59
  end
58
60
 
61
+ cmd :tree_mv,
62
+ pos: 'FROM_KEY_PATTERN TO_KEY_PATTERN [tree (or stdin)]',
63
+ desc: t('i18n_tasks.cmd.desc.tree_mv_key'),
64
+ args: [:data_format]
65
+ def tree_mv(opt = {})
66
+ fail CommandError, 'requires FROM_KEY_PATTERN and TO_KEY_PATTERN' if opt[:arguments].size < 2
67
+ from_pattern = opt[:arguments].shift
68
+ to_pattern = opt[:arguments].shift
69
+ forest = forest_pos_or_stdin!(opt)
70
+ forest.mv_key!(compile_key_pattern(from_pattern), to_pattern, root: false)
71
+ print_forest forest, opt
72
+ end
73
+
59
74
  cmd :tree_subtract,
60
75
  pos: '[[tree] [tree] ... (or stdin)]',
61
76
  desc: t('i18n_tasks.cmd.desc.tree_subtract'),
@@ -31,6 +31,7 @@ module I18n::Tasks
31
31
  # i18n-tasks-use t('i18n_tasks.cmd.args.desc.out_format')
32
32
  format_arg.call(:out_format, OUT_FORMATS)
33
33
 
34
+ # @return [I18n::Tasks::Data::Tree::Siblings]
34
35
  def forest_pos_or_stdin!(opt, format = opt[:format])
35
36
  parse_forest(pos_or_stdin!(opt), format)
36
37
  end
@@ -59,6 +60,7 @@ module I18n::Tasks
59
60
  end
60
61
  end
61
62
 
63
+ # @return [I18n::Tasks::Data::Tree::Siblings]
62
64
  def parse_forest(src, format)
63
65
  unless src
64
66
  fail CommandError, I18n.t('i18n_tasks.cmd.errors.pass_forest')
@@ -98,8 +98,7 @@ module I18n::Tasks::Data::Tree
98
98
  derive.append!(nodes)
99
99
  end
100
100
 
101
- def full_key(opts = {})
102
- root = opts.key?(:root) ? opts[:root] : true
101
+ def full_key(root: true)
103
102
  @full_key ||= {}
104
103
  @full_key[root] ||= "#{"#{parent.full_key(root: root)}." if parent? && (root || parent.parent?)}#{key}"
105
104
  end
@@ -44,6 +44,43 @@ module I18n::Tasks::Data::Tree
44
44
  self
45
45
  end
46
46
 
47
+ # @param from_pattern [Regexp]
48
+ # @param to_pattern [Regexp]
49
+ # @param root [Boolean]
50
+ # @return {old key => new key}
51
+ def mv_key!(from_pattern, to_pattern, root: false) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
52
+ moved_forest = Siblings.new
53
+ moved_nodes = []
54
+ old_key_to_new_key = {}
55
+ nodes do |node|
56
+ full_key = node.full_key(root: root)
57
+ if from_pattern =~ full_key
58
+ moved_nodes << node
59
+ if to_pattern.empty?
60
+ old_key_to_new_key[full_key] = nil
61
+ next
62
+ end
63
+ match = $~
64
+ new_key = to_pattern.gsub(/\\\d+/) { |m| match[m[1..-1].to_i] }
65
+ old_key_to_new_key[full_key] = new_key
66
+ moved_forest.merge!(Siblings.new.tap do |forest|
67
+ forest[[(node.root.try(:key) unless root), new_key].compact.join('.')] =
68
+ node.derive(key: split_key(new_key).last)
69
+ end)
70
+ end
71
+ end
72
+ # Adjust references
73
+ # TODO: support nested references better
74
+ nodes do |node|
75
+ next unless node.reference?
76
+ new_target = old_key_to_new_key[node.value.to_s]
77
+ node.value = new_target.to_sym if new_target
78
+ end
79
+ remove_nodes_and_emptied_ancestors! moved_nodes
80
+ merge! moved_forest
81
+ old_key_to_new_key
82
+ end
83
+
47
84
  def replace_node!(node, new_node)
48
85
  @list[@list.index(node)] = new_node
49
86
  key_to_node[new_node.key] = new_node
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ require 'set'
3
+
2
4
  module I18n::Tasks
3
5
  module Data::Tree
4
6
  # Any Enumerable that yields nodes can mix in this module
@@ -51,10 +53,9 @@ module I18n::Tasks
51
53
  end
52
54
 
53
55
  # @option root include root in full key
54
- def keys(key_opts = {}, &visitor)
55
- key_opts[:root] = false unless key_opts.key?(:root)
56
- return to_enum(:keys, key_opts) unless visitor
57
- leaves { |node| visitor.yield(node.full_key(key_opts), node) }
56
+ def keys(root: false, &visitor)
57
+ return to_enum(:keys, root: root) unless visitor
58
+ leaves { |node| visitor.yield(node.full_key(root: root), node) }
58
59
  self
59
60
  end
60
61
 
@@ -112,24 +113,36 @@ module I18n::Tasks
112
113
  self
113
114
  end
114
115
 
115
- # @return Siblings
116
- def select_keys(opts = {}, &block)
117
- root = opts.key?(:root) ? opts[:root] : false
118
- ok = {}
116
+ # @return [Siblings]
117
+ def select_keys(root: false, &block)
118
+ matches = get_nodes_by_key_filter(root: root, &block)
119
+ select_nodes do |node|
120
+ matches.include?(node)
121
+ end
122
+ end
123
+
124
+ # @return [Siblings]
125
+ def select_keys!(root: false, &block)
126
+ matches = get_nodes_by_key_filter(root: root, &block)
127
+ select_nodes! do |node|
128
+ matches.include?(node)
129
+ end
130
+ end
131
+
132
+ # @return [Set<I18n::Tasks::Data::Tree::Node>]
133
+ def get_nodes_by_key_filter(root: false, &block)
134
+ matches = Set.new
119
135
  keys(root: root) do |full_key, node|
120
136
  if block.yield(full_key, node)
121
137
  node.walk_to_root do |p|
122
- break if ok[p]
123
- ok[p] = true
138
+ break unless matches.add?(p)
124
139
  end
125
140
  end
126
141
  end
127
- select_nodes do |node|
128
- ok[node]
129
- end
142
+ matches
130
143
  end
131
144
 
132
- # @return Siblings
145
+ # @return [Siblings]
133
146
  def intersect_keys(other_tree, key_opts = {}, &block)
134
147
  if block
135
148
  select_keys(key_opts) do |key, node|
@@ -80,6 +80,16 @@ module I18n
80
80
  print_info "#{cyan title} #{cyan text}"
81
81
  end
82
82
 
83
+ def mv_results(results)
84
+ results.each do |(from, to)|
85
+ if to
86
+ print_info "#{cyan from} #{bold(yellow('⮕'))} #{cyan to}"
87
+ else
88
+ print_info "#{red from}#{bold(red(' 🗑'))}"
89
+ end
90
+ end
91
+ end
92
+
83
93
  private
84
94
 
85
95
  def missing_key_info(leaf)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module I18n
3
3
  module Tasks
4
- VERSION = '0.9.7'
4
+ VERSION = '0.9.8'
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.7
4
+ version: 0.9.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm