fastlane-plugin-wpmreleasetoolkit 11.0.2 → 11.0.3

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
2
  SHA256:
3
- metadata.gz: 1a01dbbf909c6d6ae5a683e4d4261e44ef7dda1f9b896e0f49529de45ffb8c58
4
- data.tar.gz: 5b6343174100d8167d85b7a31fe2484988877eb249d4ee527469fc025d201a26
3
+ metadata.gz: 7eae107cbe0b856fbe2c3292db7b84bc7ba57ec98da3717e7872243923e63b1d
4
+ data.tar.gz: 9576cd4c24122685ab6447fcdd64a2751e44fc36b368b9b22b623e5fe34353c4
5
5
  SHA512:
6
- metadata.gz: 10035929825ca4d666f3a75d184feba49720233b0a57a0e00ac7f9cf92097f332f2554a3e9f811ebdb5dda83a9d6b750dc22568bf3cddd992509f12ca551ee2b
7
- data.tar.gz: 0a67262b02fbf58244a5c779a6e517a27aa869c2e872c10f298a4c2621837e46206a78e4ec4c292df5f2086eb02054ea86470062435c7683f44a1a97bf278aec
6
+ metadata.gz: c2f85f419598ad21a97f91767bd264303cd4ce47514f61648bceb482214fbab0dc379704aa15dc9d189743ff31220c6e9c61b283826c453ab088594f3886fa3f
7
+ data.tar.gz: 4486133931d4c775ff8ec0a084bce9eba9309cebf85f06f9e9aa3182a5974b34d07cb598b48fc30834fb09f8e23a40c0d98aa0c08cc12d982ddea468f4a93c1a
@@ -152,6 +152,13 @@ module Fastlane
152
152
  (added_count + updated_count) != 0
153
153
  end
154
154
 
155
+ ########
156
+ # @!group Verify diff of library vs main strings matches
157
+ #
158
+ # @note This set of methods is used by `an_validate_lib_strings_action`
159
+ # (which doesn't seem to be used by any of our Android projects nowadays?)
160
+ ########
161
+
155
162
  def self.verify_diff(diff_string, main_strings, lib_strings, library)
156
163
  return unless diff_string.start_with?('name=')
157
164
 
@@ -198,6 +205,8 @@ module Fastlane
198
205
  end
199
206
  end
200
207
 
208
+ # @!endgroup
209
+
201
210
  ########
202
211
  # @!group Downloading translations from GlotPress
203
212
  ########
@@ -223,7 +232,7 @@ module Fastlane
223
232
  #
224
233
  # @param [String] res_dir The relative path to the `…/src/main/res` directory.
225
234
  # @param [String] glotpress_project_url The base URL to the glotpress project to download the strings from.
226
- # @param [Hash{String=>String}, Array] glotpress_filters
235
+ # @param [Hash{Symbol=>String}, Array] glotpress_filters
227
236
  # The filters to apply when exporting strings from GlotPress.
228
237
  # Typical examples include `{ status: 'current' }` or `{ status: 'review' }`.
229
238
  # If an array of Hashes is provided instead of a single Hash, this method will perform as many
@@ -235,10 +244,8 @@ module Fastlane
235
244
  def self.download_from_glotpress(res_dir:, glotpress_project_url:, locales_map:, glotpress_filters: { status: 'current' })
236
245
  glotpress_filters = [glotpress_filters] unless glotpress_filters.is_a?(Array)
237
246
 
238
- attributes_to_copy = %w[formatted] # Attributes that we want to replicate into translated `string.xml` files
239
247
  orig_file = File.join(res_dir, 'values', 'strings.xml')
240
248
  orig_xml = File.open(orig_file) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) }
241
- orig_attributes = orig_xml.xpath('//string').to_h { |tag| [tag['name'], tag.attributes.select { |k, _| attributes_to_copy.include?(k) }] }
242
249
 
243
250
  locales_map.each do |lang_codes|
244
251
  all_xml_documents = glotpress_filters.map do |filters|
@@ -250,13 +257,7 @@ module Fastlane
250
257
  # Merge all XMLs together
251
258
  merged_xml = merge_xml_documents(all_xml_documents)
252
259
 
253
- # Process XML (text substitutions, replicate attributes, quick-lint string)
254
- merged_xml.xpath('//string').each do |string_tag|
255
- apply_substitutions(string_tag)
256
- orig_attributes[string_tag['name']]&.each { |k, v| string_tag[k] = v }
257
- quick_lint(string_tag, lang_codes[:android])
258
- end
259
- merged_xml.xpath('//string-array/item').each { |item_tag| apply_substitutions(item_tag) }
260
+ post_process_xml!(merged_xml, locale_code: lang_codes[:android], original_xml: orig_xml)
260
261
 
261
262
  # Save
262
263
  lang_dir = File.join(res_dir, "values-#{lang_codes[:android]}")
@@ -266,8 +267,10 @@ module Fastlane
266
267
  end
267
268
  end
268
269
 
270
+ # @!endgroup
271
+
269
272
  #####################
270
- # Private Helpers
273
+ # @!group Private Helpers
271
274
  #####################
272
275
 
273
276
  # Downloads the export from GlotPress for a given locale and given filters
@@ -276,7 +279,7 @@ module Fastlane
276
279
  # @param [String] locale The GlotPress locale code to download strings for.
277
280
  # @param [Hash{Symbol=>String}] filters The hash of filters to apply when exporting from GlotPress.
278
281
  # Typical examples include `{ status: 'current' }` or `{ status: 'review' }`.
279
- # @return [Nokogiri::XML] the download XML document, parsed as a Nokogiri::XML object
282
+ # @return [Nokogiri::XML::Document] the download XML document, parsed as a Nokogiri::XML object
280
283
  #
281
284
  def self.download_glotpress_export_file(project_url:, locale:, filters:)
282
285
  query_params = filters.transform_keys { |k| "filters[#{k}]" }.merge(format: 'android')
@@ -297,6 +300,9 @@ module Fastlane
297
300
 
298
301
  # Merge multiple Nokogiri::XML `strings.xml` documents together
299
302
  #
303
+ # Used especially when we provided multiple GlotPress filters to `download_from_glotpress`,
304
+ # as in this case we'd trigger one export per filter, then merge the result in a single XML
305
+ #
300
306
  # @param [Array<Nokogiri::XML::Document>] all_xmls Array of the Nokogiri XML documents to merge together
301
307
  # @return [Nokogiri::XML::Document] The merged document.
302
308
  #
@@ -323,11 +329,53 @@ module Fastlane
323
329
  end
324
330
  private_class_method :merge_xml_documents
325
331
 
326
- # Apply some common text substitutions to tag contents
332
+ # Process a downloaded XML (in-place), to apply the following
333
+ # - replicate attributes from the nodes of the original XML (`translatable`, `tools:ignore`, …) to the translated XML
334
+ # - text substitutions for common special characters
335
+ # - quick-lint string by searching for common issue patterns (using `%%` in a `formatted=false` string, etc)
336
+ #
337
+ # @param [Nokogiri::XML::Document] translated_xml The downloaded XML to post-process
338
+ # @param [String] locale_code The android locale code associated with the translated_xml
339
+ # @param [Nokogiri::XML::Document] original_xml The original `values/strings.xml` to use as reference
340
+ #
341
+ def self.post_process_xml!(translated_xml, locale_code:, original_xml:)
342
+ copy_orig_attributes = lambda do |node, xpath|
343
+ orig_attributes = original_xml.xpath(xpath)&.first&.attribute_nodes&.to_h do |attr|
344
+ [[attr.namespace&.prefix, attr.name].compact.join(':'), attr.value]
345
+ end
346
+ orig_attributes&.each { |k, v| node[k] = v unless k == 'name' }
347
+ end
348
+
349
+ # 1. Replicate namespaces on the document (especially `xmlns:tools` if present)
350
+ original_xml.namespaces.each { |k, v| translated_xml.root&.add_namespace(k.delete_prefix('xmlns:'), v) }
351
+ # 2. Replicate attributes on any node with `@name` attribute (`string`, `string-array`, `plurals`)
352
+ translated_xml.xpath('//*[@name]').each do |node|
353
+ copy_orig_attributes.call(node, "//#{node.name}[@name = '#{node['name']}']")
354
+ end
355
+ # 3. Process copies for `string` nodes
356
+ translated_xml.xpath('//string[@name]').each do |string_node|
357
+ apply_substitutions!(string_node)
358
+ quick_lint(string_node, locale_code)
359
+ end
360
+ # 4. Process copies for `string-array/item` nodes
361
+ translated_xml.xpath('//string-array[@name]/item').each do |item_node|
362
+ apply_substitutions!(item_node)
363
+ quick_lint(item_node, locale_code)
364
+ end
365
+ # 5. Replicate attributes + Process copies for `plurals/item` nodes
366
+ translated_xml.xpath('//plurals[@name]/item[@quantity]').each do |item_node|
367
+ copy_orig_attributes.call(item_node, "//plurals[@name = '#{item_node.parent['name']}']/item[@quantity = '#{item_node['quantity']}']")
368
+ apply_substitutions!(item_node)
369
+ quick_lint(item_node, locale_code)
370
+ end
371
+ end
372
+ private_class_method :post_process_xml!
373
+
374
+ # Apply some common text substitutions to tag contents, like `... => …` or en-dash instead of regular dash for ranges of numbers
327
375
  #
328
376
  # @param [Nokogiri::XML::Node] tag The XML tag/node to apply substitutions to
329
377
  #
330
- def self.apply_substitutions(tag)
378
+ def self.apply_substitutions!(tag)
331
379
  tag.content = tag.content.gsub('...', '…')
332
380
 
333
381
  # Typography en-dash
@@ -339,17 +387,28 @@ module Fastlane
339
387
  is_negative_number ? str : "#{match[1]}\u{2013}#{match[2]}"
340
388
  end
341
389
  end
342
- private_class_method :apply_substitutions
390
+ private_class_method :apply_substitutions!
343
391
 
344
- # Perform some quick basic checks about an individual `<string>` tag and print warnings accordingly
392
+ # Perform some quick basic checks about an individual `<string>` tag and print warnings accordingly:
393
+ # - detect the use of `%%` in the string even if `formatted=false` is set
394
+ # - detect the presence of `\@string/` in translated XML, which suggests the original key that referenced `@string/…` did not set `translatable=false`
395
+ # and thus that `@string/…` copy was sent to GlotPress for translation, then escaped during exporting it back.
345
396
  #
346
- # @param [Nokogiri::XML::Node] string_tag The XML tag/node to check
397
+ # @param [Nokogiri::XML::Node] node The XML tag/node to check the content of
347
398
  # @param [String] lang The language we are currently processing. Used for providing context during logging / warning message
348
399
  #
349
- def self.quick_lint(string_tag, lang)
350
- return unless string_tag['formatted'] == 'false' && string_tag.content.include?('%%')
351
-
352
- UI.important "Warning: [#{lang}] translation for '#{string_tag['name']}' has attribute formatted=false, but still contains escaped '%%' in translation."
400
+ def self.quick_lint(node, lang)
401
+ named_node = node.has_attribute?('name') ? node : node.parent
402
+ if named_node['formatted'] == 'false' && node.content.include?('%%')
403
+ UI.important "Warning: [#{lang}] translation for '#{named_node['name']}' has attribute formatted=false, but still contains escaped '%%' in translation."
404
+ end
405
+ # rubocop:disable Style/GuardClause
406
+ if node.content.include?('\\@string/')
407
+ UI.important "Warning: [#{lang}] exported translation for '#{named_node['name']}' contains `\\@string/`. This is a sign that this entry was not marked as `translatable=false` " \
408
+ + 'in the original `values/strings.xml`, and was thus sent to GlotPress, which added the backslash when exporting it back.'
409
+ node.content = node.content.gsub('\\@string/', '@string/')
410
+ end
411
+ # rubocop:enable Style/GuardClause
353
412
  end
354
413
  private_class_method :quick_lint
355
414
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fastlane
4
4
  module Wpmreleasetoolkit
5
- VERSION = '11.0.2'
5
+ VERSION = '11.0.3'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-wpmreleasetoolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.0.2
4
+ version: 11.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Automattic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-04 00:00:00.000000000 Z
11
+ date: 2024-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport