i18n-migrations 1.2.4 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b8b7ac35780cf4cf39728ec9c140324a1df5b21868fbeff0d8082eb2b612e1e
4
- data.tar.gz: c8c7f5867dc9d8485d527d0b9286a3925eaa69eea40e82ae5ee0a6ca2240f89e
3
+ metadata.gz: 155eb3f6fe33497d8988ba301c6535df404f0d02c59cfaac034a77a169508d62
4
+ data.tar.gz: 04a5b6dcc152e3ad7b7b88bc898acae8fc2fed122947a3ae1609edcdb6023e64
5
5
  SHA512:
6
- metadata.gz: 3ebecbc35805df21ae0db20ac14931517fb6e55126a3a3b853d1486ab59264f38ea58e66077f665ed226b66afb828544866dae8810fd6c6c93c5ce52f9367d89
7
- data.tar.gz: 5a449c05dd1f4d2a2a5492cf0e0ff6fb016344fc1db735e51eaeff4bc9466f62cfb1979c381ad8d35fa503e58d8964fbe044438e9ec1ac51a09fb1483d4356bc
6
+ metadata.gz: 324c96c71807c3fb331a25742f84d82242a0c3ea0bd12f0df7cebe9a6bdd5e0261d7830cfd843e0debe50475a948347603a6a23d93efd2d610b054d251ecc782
7
+ data.tar.gz: cb47a3ffd0fc1eae8a5b1b2e6996857c1b092f705112252818c9cb3d939246bc5711fa1357ef20e32c4243091fd59793544a7cd52c0cd295d9aca5a859585d58
@@ -30,17 +30,17 @@ module I18n
30
30
 
31
31
  # this will replace everything about a locale, it will create a locale that does not yet exist
32
32
  def force_push(locale)
33
- data, notes = locale.read_data_and_notes(parse: false)
33
+ data, metadata = locale.read_data_and_metadata(parse: false)
34
34
  client.put_locale(locale.name,
35
35
  name: locale.name,
36
36
  yaml_file: data,
37
- yaml_notes_file: notes)
37
+ yaml_metadata_file: metadata)
38
38
 
39
39
  end
40
40
 
41
41
  def pull_from_crowd_translate(locale)
42
42
  data = client.get_locale_file(locale.name)
43
- locale.write_raw_data("#{locale.name}.yml", data)
43
+ locale.data_file.write(data)
44
44
  locale.write_remote_version(YAML::load(data)[locale.name])
45
45
  end
46
46
 
@@ -28,9 +28,9 @@ module I18n
28
28
  put("migrations/#{version}.json", migration: { ruby_file: ruby_file })
29
29
  end
30
30
 
31
- def put_locale(locale_code, name:, yaml_file:, yaml_notes_file:)
31
+ def put_locale(locale_code, name:, yaml_file:, yaml_metadata_file:)
32
32
  put("locales/#{locale_code}",
33
- locale: { name: name, yaml_file: yaml_file, yaml_notes_file: yaml_notes_file })
33
+ locale: { name: name, yaml_file: yaml_file, yaml_metadata_file: yaml_metadata_file })
34
34
  end
35
35
 
36
36
  private
@@ -1,4 +1,5 @@
1
1
  require_relative './google_spreadsheet'
2
+ require_relative '../metadata'
2
3
 
3
4
  module I18n
4
5
  module Migrations
@@ -36,7 +37,7 @@ module I18n
36
37
  def pull_from_sheet(sheet, locale)
37
38
  puts "Pulling #{locale.name}"
38
39
  data = {}
39
- notes = {}
40
+ metadata = Metadata.new
40
41
  count = 0
41
42
 
42
43
  (2..sheet.num_rows).each do |row|
@@ -44,14 +45,14 @@ module I18n
44
45
  if key.present?
45
46
  locale.assign_complex_key(data, key.split('.'), value.present? ? value : '')
46
47
  if note.present?
47
- locale.assign_complex_key(notes, key.split('.'), note)
48
+ metadata[key] = parse_metadatum(note)
48
49
  end
49
50
  count += 1
50
- print '.'
51
+ #print '.'
51
52
  end
52
53
  end
53
54
 
54
- locale.write_data_and_notes(data, notes)
55
+ locale.write_data_and_metadata(data, metadata)
55
56
  locale.write_remote_version(data)
56
57
 
57
58
  puts "\n#{count} keys"
@@ -59,7 +60,7 @@ module I18n
59
60
 
60
61
  def push_to_sheet(sheet, locale)
61
62
  main_data = locale.main_locale.read_data
62
- data, notes = locale.read_data_and_notes
63
+ data, metadata = locale.read_data_and_metadata
63
64
  row = 2
64
65
 
65
66
  puts "Pushing #{locale.name}"
@@ -68,9 +69,9 @@ module I18n
68
69
  sheet[row, 1] = key
69
70
  sheet[row, 2] = value
70
71
  sheet[row, 3] = data[key]
71
- sheet[row, 4] = notes[key]
72
+ sheet[row, 4] = unparse_metadatum(metadata[key])
72
73
  row += 1
73
- print '.'
74
+ #print '.'
74
75
  end
75
76
 
76
77
  sheet.synchronize
@@ -78,6 +79,28 @@ module I18n
78
79
 
79
80
  puts "\n#{main_data.keys.length} keys"
80
81
  end
82
+
83
+ def parse_metadatum(text)
84
+ m = Metadata::Metadatum.new({})
85
+ m.notes = text.gsub("[autotranslated]") do
86
+ m.autotranslated = true
87
+ ''
88
+ end.gsub(/\[error: ([^\]]+)\]/) do
89
+ m.errors << $1
90
+ ''
91
+ end.strip
92
+ m
93
+ end
94
+
95
+ def unparse_metadatum(metadatum)
96
+ string = []
97
+ string << '[autotranslated]' if metadatum.autotranslated
98
+ metadatum.errors.each do |error|
99
+ string << "[error: #{error}]"
100
+ end
101
+ string << metadatum.notes unless metadatum.notes.blank?
102
+ string.join("\n")
103
+ end
81
104
  end
82
105
  end
83
106
  end
@@ -9,7 +9,7 @@ module I18n
9
9
  end
10
10
 
11
11
  # key is provided so we can figure out if this is text or html
12
- # returns [translated term, notes]
12
+ # returns [translated term, errors || []]
13
13
  def lookup(term, key: nil)
14
14
  return [term, ''] if @from_locale == @to_locale
15
15
 
@@ -19,11 +19,7 @@ module I18n
19
19
  format: format(key),
20
20
  q: term)
21
21
  translated_term = JSON.parse(response.body)['data']['translations'].first['translatedText']
22
- translated_term, errors = fix(term, translated_term, key: key)
23
- unless errors.empty?
24
- STDERR.puts "'#{term}' => '#{translated_term}'\n#{errors.join(', ').red}"
25
- end
26
- [translated_term, (errors.map { |e| "[error: #{e}]" } + ['[autotranslated]']).join("\n")]
22
+ fix(term, translated_term, key: key)
27
23
  end
28
24
 
29
25
  VARIABLE_STRING_REGEX = /%\{[^\}]+\}/
@@ -42,7 +38,7 @@ module I18n
42
38
  # returns updated after term, errors
43
39
  def fix(before, after, key: nil)
44
40
  is_html = format(key) == :html
45
- errors = ["#{@to_locale}: #{key}"]
41
+ errors = []
46
42
 
47
43
  # do not translate
48
44
  @do_not_translate.each do |term, bad_translations|
@@ -114,7 +110,7 @@ module I18n
114
110
  end
115
111
 
116
112
 
117
- [after, errors.length > 1 ? errors : []]
113
+ [after, errors]
118
114
  end
119
115
 
120
116
  private
@@ -3,6 +3,7 @@ require 'yaml'
3
3
  require 'active_support'
4
4
  require 'colorize'
5
5
  require 'active_support/core_ext/object'
6
+ require_relative './metadata'
6
7
 
7
8
  # this class does all the work, but doesn't hold config or do more than one locale
8
9
  module I18n
@@ -16,7 +17,7 @@ module I18n
16
17
  name, locales_dir, main_locale_name, migrations, dictionary
17
18
  end
18
19
 
19
- def validate(data, notes)
20
+ def validate(data, metadata)
20
21
  fix_count, error_count = 0, 0
21
22
  main_data = main_locale.read_data
22
23
  main_data.each do |key, main_term|
@@ -31,7 +32,7 @@ module I18n
31
32
  puts
32
33
  fix_count += 1
33
34
  end
34
- replace_errors_in_notes(notes, key, errors)
35
+ metadata[key].errors = errors
35
36
  if errors.length > 0
36
37
  puts "Error #{errors.join(', ').red} #{key.yellow}"
37
38
  puts "#{@main_locale_name.bold}: #{main_term}"
@@ -46,18 +47,18 @@ module I18n
46
47
  end
47
48
 
48
49
  def update_info
49
- data, notes = read_data_and_notes
50
- yield data, notes
51
- write_data_and_notes(data, notes)
50
+ data, metadata = read_data_and_metadata
51
+ yield data, metadata
52
+ write_data_and_metadata(data, metadata)
52
53
  end
53
54
 
54
55
  def migrate!
55
- update_info do |data, notes|
56
- migrate(data, notes)
56
+ update_info do |data, metadata|
57
+ migrate(data, metadata)
57
58
  end
58
59
  end
59
60
 
60
- def migrate(data, notes)
61
+ def migrate(data, metadata)
61
62
  missing_versions = (@migrations.all_versions - read_versions(data)).sort
62
63
  if missing_versions.empty?
63
64
  puts "#{@name}: up-to-date"
@@ -65,11 +66,11 @@ module I18n
65
66
  end
66
67
  puts "#{@name}: Migrating #{missing_versions.join(', ')}"
67
68
  missing_versions.each do |version|
68
- migrate_to_version(data, notes, version, :up)
69
+ migrate_to_version(data, metadata, version, :up)
69
70
  end
70
71
  end
71
72
 
72
- def rollback(data, notes)
73
+ def rollback(data, metadata)
73
74
  last_version = read_versions(data).last
74
75
  if last_version == nil
75
76
  puts "#{@name}: no more migrations to roll back"
@@ -78,24 +79,25 @@ module I18n
78
79
  puts "#{@name}: Rolling back #{last_version}"
79
80
  raise "Can't find #{last_version}.rb to rollback" unless @migrations.all_versions.include?(last_version)
80
81
 
81
- migrate_to_version(data, notes, last_version, :down)
82
+ migrate_to_version(data, metadata, last_version, :down)
82
83
  end
83
84
 
84
85
  def create(limit = nil)
85
- new_data, new_notes = {}, {}
86
+ new_data, new_metadata = {}, Metadata.new
86
87
  count = 0
87
88
  main_data = main_locale.read_data
88
89
  main_data.each do |key, term|
89
90
  if key == 'VERSION'
90
91
  new_data['VERSION'] = main_data['VERSION']
91
92
  else
92
- new_data[key], new_notes[key] = @dictionary.lookup(term, key: key)
93
+ new_data[key], errors = @dictionary.lookup(term, key: key)
94
+ new_metadata[key].errors = errors
93
95
  end
94
96
  print '.'.green
95
97
  break if limit && limit < (count += 1)
96
98
  end
97
99
  puts
98
- write_data_and_notes(new_data, new_notes)
100
+ write_data_and_metadata(new_data, new_metadata)
99
101
  end
100
102
 
101
103
  def main_locale?
@@ -107,29 +109,27 @@ module I18n
107
109
  end
108
110
 
109
111
  def read_data(parse: true)
110
- read_from_file("#{@name}.yml", parse: parse)
112
+ contents = data_file.read
113
+ return contents unless parse
114
+
115
+ hash = {}
116
+ add_to_hash(hash, parse_yaml(contents)[@name.to_s])
117
+ hash
111
118
  end
112
119
 
113
- def read_data_and_notes(parse: true)
120
+ def read_data_and_metadata(parse: true)
114
121
  data = read_data(parse: parse)
115
- notes = main_locale? ? (parse ? {} : '--- {}') : read_from_file("../#{@name}_notes.yml", parse: parse)
116
- [data, notes]
122
+ metadata = read_metadata(parse: parse)
123
+ [data, metadata]
117
124
  end
118
125
 
119
- def write_data_and_notes(data, notes)
126
+ def write_data_and_metadata(data, metadata)
120
127
  write_data(data)
121
- write_to_file("../#{@name}_notes.yml", notes) unless main_locale?
122
- end
123
-
124
- def write_raw_data(filename, data)
125
- File.open(File.join(@locales_dir, filename), 'w') do |file|
126
- file << data
127
- end
128
+ write_metadata(metadata)
128
129
  end
129
130
 
130
131
  def write_remote_version(data)
131
- write_to_file("../#{@name}_remote_version.yml",
132
- { 'VERSION' => read_versions(data) })
132
+ remote_version_file.write({ 'VERSION' => read_versions(data) }.to_yaml)
133
133
  end
134
134
 
135
135
  def main_locale
@@ -151,26 +151,48 @@ module I18n
151
151
  end
152
152
  end
153
153
 
154
- private
154
+ def data_file
155
+ file("#{name}.yml")
156
+ end
155
157
 
156
- def replace_errors_in_notes(all_notes, key, errors)
157
- return if all_notes[key].blank? && errors.empty?
158
+ def metadata_file
159
+ file("../#{name}_metadata.yml")
160
+ end
158
161
 
159
- notes = all_notes[key]
160
- notes = notes.present? ? notes.split("\n") : []
161
- notes = notes.reject { |n| n.start_with?("[error:") }
162
- all_notes[key] = (errors.map { |e| "[error: #{e}]" } + notes).join("\n")
162
+ def remote_version_file
163
+ file("../#{@name}_remote_version.yml")
163
164
  end
164
165
 
166
+ private
167
+
165
168
  def write_data(data)
166
- write_to_file("#{@name}.yml", data)
169
+ # we have to go from flat keys -> values to a hash that contains other hashes
170
+ complex_hash = {}
171
+ data.keys.sort.each do |key|
172
+ value = data[key]
173
+ assign_complex_key(complex_hash, key.split('.'), value.present? ? value : '')
174
+ end
175
+ data_file.write({ @name => complex_hash }.to_yaml)
176
+ end
177
+
178
+ def read_metadata(parse: true)
179
+ contents = if metadata_file.exist?
180
+ metadata_file.read
181
+ else
182
+ {}.to_yaml
183
+ end
184
+ parse ? Metadata.new(parse_yaml(contents)) : contents
167
185
  end
168
186
 
169
- def migrate_to_version(data, notes, version, direction)
187
+ def write_metadata(metadata)
188
+ metadata_file.write(metadata.to_yaml)
189
+ end
190
+
191
+ def migrate_to_version(data, metadata, version, direction)
170
192
  migrations.play_migration(version: version,
171
193
  locale: @name,
172
194
  data: data,
173
- notes: notes,
195
+ metadata: metadata,
174
196
  dictionary: @dictionary,
175
197
  direction: direction)
176
198
 
@@ -185,29 +207,12 @@ module I18n
185
207
  (data['VERSION'] && data['VERSION'].split("\n")) || []
186
208
  end
187
209
 
188
- def read_from_file(filename, parse: true)
189
- filename = File.join(@locales_dir, filename)
190
- begin
191
- contents = File.read(filename)
192
- return contents unless parse
193
-
194
- hash = {}
195
- add_to_hash(hash, YAML.load(contents)[@name.to_s])
196
- hash
197
- rescue
198
- puts "Error loading #{filename}"
199
- raise
200
- end
210
+ def file(filename)
211
+ Pathname.new(File.join(@locales_dir, filename))
201
212
  end
202
213
 
203
- def write_to_file(filename, hash)
204
- # we have to go from flat keys -> values to a hash that contains other hashes
205
- complex_hash = {}
206
- hash.keys.sort.each do |key|
207
- value = hash[key]
208
- assign_complex_key(complex_hash, key.split('.'), value.present? ? value : '')
209
- end
210
- write_raw_data(filename, { @name => complex_hash }.to_yaml)
214
+ def parse_yaml(string)
215
+ YAML.load(string)
211
216
  end
212
217
 
213
218
  # flattens new_hash and adds it to hash
@@ -0,0 +1,57 @@
1
+ # this class stores metadata about terms in a locale
2
+ # specifically things like errors, notes, autotranslated
3
+ # it acts kind of like a hash where you give it a key and it returns a metadatum object
4
+ class Metadata
5
+ def initialize(hash = {})
6
+ @hash = hash
7
+ end
8
+
9
+ def [](key)
10
+ if @hash[key].is_a?(Metadatum)
11
+ @hash[key]
12
+ else
13
+ @hash[key] = Metadatum.new(@hash[key])
14
+ end
15
+ end
16
+
17
+ def []=(key, value)
18
+ raise("you may only assign a metadatum") unless value.is_a?(Metadatum)
19
+ @hash[key] = value.dup
20
+ end
21
+
22
+ def delete(key)
23
+ @hash.delete(key)
24
+ end
25
+
26
+ def to_h
27
+ compacted_hash = {}
28
+ @hash.keys.sort.each do |key|
29
+ value = @hash[key].to_h
30
+ compacted_hash[key] = value if value.present?
31
+ end
32
+ compacted_hash
33
+ end
34
+
35
+ def to_yaml
36
+ to_h.to_yaml
37
+ end
38
+
39
+ class Metadatum
40
+ attr_accessor :errors, :notes, :autotranslated
41
+
42
+ def initialize(hash)
43
+ safe_hash = hash || {}
44
+ @errors = safe_hash['errors'] || []
45
+ @notes = safe_hash['notes']
46
+ @autotranslated = !!safe_hash['autotranslated']
47
+ end
48
+
49
+ def to_h
50
+ hash = {}
51
+ hash['errors'] = @errors unless @errors.empty?
52
+ hash['notes'] = @notes unless @notes.blank?
53
+ hash['autotranslated'] = @autotranslated if autotranslated
54
+ hash
55
+ end
56
+ end
57
+ end
@@ -101,10 +101,10 @@ module I18n
101
101
 
102
102
  def assign_translation(key, term, overrides)
103
103
  if overrides[@locale_code.to_sym]
104
- @translations.set_term(key, value: overrides[@locale_code.to_sym])
104
+ @translations.set_term(key, value: overrides[@locale_code.to_sym], errors: [], autotranslated: false)
105
105
  else
106
- value, notes = @dictionary.lookup(term, key: key)
107
- @translations.set_term(key, value: value, notes: notes)
106
+ value, errors = @dictionary.lookup(term, key: key)
107
+ @translations.set_term(key, value: value, errors: errors, autotranslated: true)
108
108
  end
109
109
  end
110
110
  end
@@ -20,14 +20,14 @@ module I18n
20
20
  File.join(@migration_dir, "#{version}.rb")
21
21
  end
22
22
 
23
- def play_migration(version:, locale:, data:, notes:, dictionary:, direction:)
23
+ def play_migration(version:, locale:, data:, metadata:, dictionary:, direction:)
24
24
  filename = File.join(@migration_dir, "#{version}.rb")
25
25
  require filename
26
26
 
27
27
  raise("Can't parse version: #{version}") unless version =~ /^(\d{12})_(.*)/
28
28
  migration_class_name = "#{$2.camelcase}#{$1}"
29
29
 
30
- translations = Translations.new(data: data, notes: notes)
30
+ translations = Translations.new(data: data, metadata: metadata)
31
31
  migration = begin
32
32
  migration_class_name.constantize.new(locale_code: locale,
33
33
  translations: translations,
@@ -42,33 +42,37 @@ module I18n
42
42
 
43
43
  # This is a facade over our translations
44
44
  # data = all keys -> all translations in this locale
45
- # notes = some keys -> notes about the translation in this locale
45
+ # metadata = some keys -> metadata about the translation in this locale
46
46
  class Translations
47
- def initialize(data:, notes:)
48
- @data, @notes = data, notes
47
+ def initialize(data:, metadata:)
48
+ @data, @metadata = data, metadata
49
49
  end
50
50
 
51
51
  def get_term(key)
52
52
  @data[key]
53
53
  end
54
54
 
55
- def set_term(key, value:, notes: nil)
55
+ def set_term(key, value:, errors:, autotranslated:)
56
+ # translated_term, errors = lookup_with_errors(term, key: key)
57
+ # unless errors.empty?
58
+ # STDERR.puts "'#{term}' => '#{translated_term}'\n#{errors.join(', ').red}"
59
+ # end
60
+ # [translated_term, (errors.map { |e| "[error: #{e}]" } + ['[autotranslated]']).join("\n")]
61
+
56
62
  @data[key] = value
57
- if notes.present?
58
- @notes[key] = notes
59
- else
60
- @notes.delete(key)
61
- end
63
+ @metadata[key].errors = errors
64
+ @metadata[key].notes = nil
65
+ @metadata[key].autotranslated = autotranslated
62
66
  end
63
67
 
64
68
  def delete_term(key)
65
69
  @data.delete(key)
66
- @notes.delete(key)
70
+ @metadata.delete(key)
67
71
  end
68
72
 
69
73
  def move_term(from, to)
70
74
  @data[to] = @data.delete(from)
71
- @notes[to] = @notes.delete(from)
75
+ @metadata[to] = @metadata.delete(from)
72
76
  end
73
77
  end
74
78
  end
@@ -64,8 +64,8 @@ end
64
64
 
65
65
  def rollback(locale_or_all)
66
66
  each_locale(locale_or_all) do |locale|
67
- locale.update_info do |data, notes|
68
- locale.rollback(data, notes)
67
+ locale.update_info do |data, metadata|
68
+ locale.rollback(data, metadata)
69
69
  end
70
70
  end
71
71
  end
@@ -97,8 +97,8 @@ end
97
97
  def validate(locale_or_all)
98
98
  each_locale(locale_or_all, async: false) do |locale|
99
99
  next if locale.main_locale?
100
- locale.update_info do |data, notes|
101
- locale.validate(data, notes)
100
+ locale.update_info do |data, metadata|
101
+ locale.validate(data, metadata)
102
102
  end
103
103
  end
104
104
  end
@@ -1,5 +1,5 @@
1
1
  module I18n
2
2
  module Migrations
3
- VERSION = "1.2.4"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n-migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Lightsmith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-04 00:00:00.000000000 Z
11
+ date: 2020-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -130,6 +130,7 @@ files:
130
130
  - lib/i18n/migrations/config.rb
131
131
  - lib/i18n/migrations/google_translate_dictionary.rb
132
132
  - lib/i18n/migrations/locale.rb
133
+ - lib/i18n/migrations/metadata.rb
133
134
  - lib/i18n/migrations/migration.rb
134
135
  - lib/i18n/migrations/migration_factory.rb
135
136
  - lib/i18n/migrations/migrator.rb