fastlane-plugin-wpmreleasetoolkit 3.1.0 → 4.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 +4 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_download_strings_files_from_glotpress.rb +1 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_extract_keys_from_strings_files.rb +35 -17
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_strings_files.rb +19 -15
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb +21 -6
- data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e6b39530c08effa688004cbc0c5d8fa8f4fcf3ae5795cb028ca7b60d84f25e0
|
4
|
+
data.tar.gz: bccb8c68d90bbb681f2fc41e896f1af775bf141ccd28d83eb3aac1f7ba40e28a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d5324c615ca08aba37682e1cd6d613e885245f75f1cea5b459c644f083e8cd43af6ba6e6f93932ee7ce8e2a6bb21ed75a12f655610784494955226e2119fb54
|
7
|
+
data.tar.gz: d2fbbaeb3d7b753b400d77f33f8bf863a8eafc0dccdab8c4e36886f32af1b7791ca02a9dee75ad9ff09b29659d7af2887c4840d029411f6bc71be098378b8410
|
data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_download_strings_files_from_glotpress.rb
CHANGED
@@ -10,6 +10,7 @@ module Fastlane
|
|
10
10
|
|
11
11
|
locales.each do |glotpress_locale, lproj_name|
|
12
12
|
# Download the export in the proper `.lproj` directory
|
13
|
+
UI.message "Downloading translations for '#{lproj_name}' from GlotPress (#{glotpress_locale}) [#{params[:filters]}]..."
|
13
14
|
lproj_dir = File.join(download_dir, "#{lproj_name}.lproj")
|
14
15
|
destination = File.join(lproj_dir, "#{params[:table_basename]}.strings")
|
15
16
|
FileUtils.mkdir(lproj_dir) unless Dir.exist?(lproj_dir)
|
data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_extract_keys_from_strings_files.rb
CHANGED
@@ -3,8 +3,14 @@ module Fastlane
|
|
3
3
|
class IosExtractKeysFromStringsFilesAction < Action
|
4
4
|
def self.run(params)
|
5
5
|
source_parent_dir = params[:source_parent_dir]
|
6
|
-
target_original_files = params[:target_original_files]
|
7
|
-
keys_to_extract_per_target_file = keys_list_per_target_file(target_original_files)
|
6
|
+
target_original_files = params[:target_original_files].keys # Array [original-file-paths]
|
7
|
+
keys_to_extract_per_target_file = keys_list_per_target_file(target_original_files) # Hash { original-file-path => [keys] }
|
8
|
+
prefix_to_remove_per_target_file = params[:target_original_files] # Hash { original-file-path => prefix }
|
9
|
+
|
10
|
+
UI.message("Extracting keys from `#{source_parent_dir}/*.lproj/#{params[:source_tablename]}.strings` into:")
|
11
|
+
target_original_files.each { |f| UI.message(' - ' + replace_lproj_in_path(f, with_lproj: '*.lproj')) }
|
12
|
+
|
13
|
+
updated_files_list = []
|
8
14
|
|
9
15
|
# For each locale, extract the right translations from `<source_tablename>.strings` into each target `.strings` file
|
10
16
|
Dir.glob('*.lproj', base: source_parent_dir).each do |lproj_dir_name|
|
@@ -12,21 +18,25 @@ module Fastlane
|
|
12
18
|
translations = Fastlane::Helper::Ios::L10nHelper.read_strings_file_as_hash(path: source_strings_file)
|
13
19
|
|
14
20
|
target_original_files.each do |target_original_file|
|
15
|
-
target_strings_file =
|
21
|
+
target_strings_file = replace_lproj_in_path(target_original_file, with_lproj: lproj_dir_name)
|
16
22
|
next if target_strings_file == target_original_file # do not generate/overwrite the original locale itself
|
17
23
|
|
18
|
-
|
19
|
-
|
24
|
+
keys_prefix = prefix_to_remove_per_target_file[target_original_file] || ''
|
25
|
+
keys_to_extract = keys_to_extract_per_target_file[target_original_file].map { |k| "#{keys_prefix}#{k}" }
|
26
|
+
extracted_translations = translations.slice(*keys_to_extract).transform_keys { |k| k.delete_prefix(keys_prefix) }
|
27
|
+
UI.verbose("Extracting #{extracted_translations.count} keys (out of #{keys_to_extract.count} expected) into #{target_strings_file}...")
|
20
28
|
|
21
|
-
extracted_translations = translations.slice(*keys_to_extract)
|
22
29
|
FileUtils.mkdir_p(File.dirname(target_strings_file)) # Ensure path up to parent dir exists, create it if not.
|
23
30
|
Fastlane::Helper::Ios::L10nHelper.generate_strings_file_from_hash(translations: extracted_translations, output_path: target_strings_file)
|
31
|
+
updated_files_list.append(target_strings_file)
|
24
32
|
rescue StandardError => e
|
25
33
|
UI.user_error!("Error while writing extracted translations to `#{target_strings_file}`: #{e.message}")
|
26
34
|
end
|
27
35
|
rescue StandardError => e
|
28
36
|
UI.user_error!("Error while reading the translations from source file `#{source_strings_file}`: #{e.message}")
|
29
37
|
end
|
38
|
+
|
39
|
+
updated_files_list
|
30
40
|
end
|
31
41
|
|
32
42
|
# Pre-load the list of keys to extract for each target file.
|
@@ -43,6 +53,15 @@ module Fastlane
|
|
43
53
|
UI.user_error!("Failed to read the keys to extract from originals file: #{e.message}")
|
44
54
|
end
|
45
55
|
|
56
|
+
# Replaces the `*.lproj` component of the path to a `.strings` file with a different `.lproj` folder
|
57
|
+
#
|
58
|
+
# @param [String] path The path the the `.strings` file, assumed to be in a `.lproj` parent folder
|
59
|
+
# @param [String] with_lproj The new name of the `.lproj` parent folder to point to
|
60
|
+
#
|
61
|
+
def self.replace_lproj_in_path(path, with_lproj:)
|
62
|
+
File.join(File.dirname(File.dirname(path)), with_lproj, File.basename(path))
|
63
|
+
end
|
64
|
+
|
46
65
|
#####################################################
|
47
66
|
# @!group Documentation
|
48
67
|
#####################################################
|
@@ -82,28 +101,27 @@ module Fastlane
|
|
82
101
|
default_value: 'Localizable'),
|
83
102
|
FastlaneCore::ConfigItem.new(key: :target_original_files,
|
84
103
|
env_name: 'FL_IOS_EXTRACT_KEYS_FROM_STRINGS_FILES_TARGET_ORIGINAL_FILES',
|
85
|
-
description: 'The path(s) to the `<base-locale>.lproj/<target-tablename>.strings` file(s) for which we want to extract the keys to. ' \
|
86
|
-
+ 'Each
|
87
|
-
+ 'For each
|
88
|
-
|
104
|
+
description: 'The path(s) to the `<base-locale>.lproj/<target-tablename>.strings` file(s) for which we want to extract the keys to, and the prefix to remove from their keys. ' \
|
105
|
+
+ 'Each key in the Hash should point to a file containing the original strings (typically `en` or `Base` locale), and will be used to determine which keys to extract from the `source_tablename`. ' \
|
106
|
+
+ 'For each key, the associated value is an optional prefix to remove from the keys (which can be useful if you used a prefix during `ios_merge_strings_files` to avoid duplicates). Can be nil or empty if no prefix was used during merge for that file.' \
|
107
|
+
+ 'Note: For each entry, the path(s) in which the translations will be extracted to will be the files with the same basename as the key in each of the other `*.lproj` sibling folders. ',
|
108
|
+
type: Hash,
|
89
109
|
verify_block: proc do |values|
|
90
110
|
UI.user_error!('`target_original_files` must contain at least one path to an original `.strings` file.') if values.empty?
|
91
|
-
values.each do |
|
92
|
-
UI.user_error!("Path `#{
|
93
|
-
UI.user_error! "Expected `#{
|
111
|
+
values.each do |path, _|
|
112
|
+
UI.user_error!("Path `#{path}` (found in `target_original_files`) does not exist.") unless File.exist?(path)
|
113
|
+
UI.user_error! "Expected `#{path}` (found in `target_original_files`) to be a path ending in a `*.lproj/*.strings`." unless File.extname(path) == '.strings' && File.extname(File.dirname(path)) == '.lproj'
|
94
114
|
end
|
95
115
|
end),
|
96
116
|
]
|
97
117
|
end
|
98
118
|
|
99
119
|
def self.return_type
|
100
|
-
|
101
|
-
# see RETURN_TYPES in https://github.com/fastlane/fastlane/blob/master/fastlane/lib/fastlane/action.rb
|
102
|
-
nil
|
120
|
+
:array_of_strings
|
103
121
|
end
|
104
122
|
|
105
123
|
def self.return_value
|
106
|
-
|
124
|
+
'The list of files which have been generated and written to disk by the action'
|
107
125
|
end
|
108
126
|
|
109
127
|
def self.authors
|
@@ -2,12 +2,18 @@ module Fastlane
|
|
2
2
|
module Actions
|
3
3
|
class IosMergeStringsFilesAction < Action
|
4
4
|
def self.run(params)
|
5
|
-
|
5
|
+
destination = params[:destination]
|
6
|
+
# Include the destination as the first of the files (without key prefixes) to be included in the merged result
|
7
|
+
all_paths_list = { destination => nil }.merge(params[:paths_to_merge])
|
6
8
|
|
7
|
-
|
9
|
+
UI.message "Merging strings files #{all_paths_list.inspect}"
|
10
|
+
|
11
|
+
File.write(destination, '') unless File.exist?(destination) # Create empty destination file if it does not exist yet
|
12
|
+
duplicates = Fastlane::Helper::Ios::L10nHelper.merge_strings(paths: all_paths_list, output_path: params[:destination])
|
8
13
|
duplicates.each do |dup_key|
|
9
14
|
UI.important "Duplicate key found while merging the `.strings` files: `#{dup_key}`"
|
10
15
|
end
|
16
|
+
UI.important 'Tip: To avoid those key conflicts, you might want to consider providing different prefixes in the `Hash` you used for the `paths:` parameter.' unless duplicates.empty?
|
11
17
|
duplicates
|
12
18
|
end
|
13
19
|
|
@@ -21,12 +27,11 @@ module Fastlane
|
|
21
27
|
|
22
28
|
def self.details
|
23
29
|
<<~DETAILS
|
24
|
-
Merge multiple `.strings` files into one.
|
30
|
+
Merge multiple `.strings` files into another one.
|
25
31
|
|
26
|
-
Especially useful to prepare a single `.strings` file merging
|
27
|
-
|
28
|
-
|
29
|
-
manually maintained by developers.
|
32
|
+
Especially useful to prepare a single `.strings` file merging string files like `InfoPlist.strings` — whose
|
33
|
+
content are typically manually maintained by developers — within the main `Localizable.strings` file — which
|
34
|
+
would have typically been previously generated from the codebase via `ios_generate_strings_file_from_code`.
|
30
35
|
|
31
36
|
The action only supports merging files which are in the OpenStep (`"key" = "value";`) text format (which is
|
32
37
|
the most common format for `.strings` files, and the one generated by `genstrings`), but can handle the case
|
@@ -38,19 +43,18 @@ module Fastlane
|
|
38
43
|
def self.available_options
|
39
44
|
[
|
40
45
|
FastlaneCore::ConfigItem.new(
|
41
|
-
key: :
|
42
|
-
env_name: '
|
43
|
-
description: '
|
44
|
-
type:
|
46
|
+
key: :paths_to_merge,
|
47
|
+
env_name: 'FL_IOS_MERGE_STRINGS_FILES_PATHS_TO_MERGE',
|
48
|
+
description: 'A hash of the paths of all the `.strings` files to merge into the `destination`, with the prefix to be used for their keys as associated value',
|
49
|
+
type: Hash,
|
45
50
|
optional: false
|
46
51
|
),
|
47
52
|
FastlaneCore::ConfigItem.new(
|
48
53
|
key: :destination,
|
49
54
|
env_name: 'FL_IOS_MERGE_STRINGS_FILES_DESTINATION',
|
50
|
-
description: 'The path of the
|
55
|
+
description: 'The path of the `.strings` file to merge the other ones into',
|
51
56
|
type: String,
|
52
|
-
optional:
|
53
|
-
default_value: nil
|
57
|
+
optional: false
|
54
58
|
),
|
55
59
|
]
|
56
60
|
end
|
@@ -60,7 +64,7 @@ module Fastlane
|
|
60
64
|
end
|
61
65
|
|
62
66
|
def self.return_value
|
63
|
-
'The list of duplicate keys found while merging the various `.strings` files'
|
67
|
+
'The list of duplicate keys (after prefix has been added to each) found while merging the various `.strings` files'
|
64
68
|
end
|
65
69
|
|
66
70
|
def self.authors
|
@@ -19,6 +19,8 @@ module Fastlane
|
|
19
19
|
# - `nil` if the file does not exist or is neither of those format (e.g. not a `.strings` file at all)
|
20
20
|
#
|
21
21
|
def self.strings_file_type(path:)
|
22
|
+
return :text if File.empty?(path) # If completely empty file, consider it as a valid `.strings` files in textual format
|
23
|
+
|
22
24
|
# Start by checking it seems like a valid property-list file (and not e.g. an image or plain text file)
|
23
25
|
_, status = Open3.capture2('/usr/bin/plutil', '-lint', path)
|
24
26
|
return nil unless status.success?
|
@@ -36,8 +38,8 @@ module Fastlane
|
|
36
38
|
|
37
39
|
# Merge the content of multiple `.strings` files into a new `.strings` text file.
|
38
40
|
#
|
39
|
-
# @param [
|
40
|
-
# @param [String]
|
41
|
+
# @param [Hash<String, String>] paths The paths of the `.strings` files to merge together, associated with the prefix to prepend to each of their respective keys
|
42
|
+
# @param [String] output_path The path to the merged `.strings` file to generate as a result.
|
41
43
|
# @return [Array<String>] List of duplicate keys found while validating the merge.
|
42
44
|
#
|
43
45
|
# @note For now, this method only supports merging `.strings` file in `:text` format
|
@@ -48,24 +50,35 @@ module Fastlane
|
|
48
50
|
#
|
49
51
|
# @raise [RuntimeError] If one of the paths provided is not in text format (but XML or binary instead), or if any of the files are missing.
|
50
52
|
#
|
51
|
-
def self.merge_strings(paths:, output_path:
|
53
|
+
def self.merge_strings(paths:, output_path:)
|
52
54
|
duplicates = []
|
53
55
|
Tempfile.create('wpmrt-l10n-merge-', encoding: 'utf-8') do |tmp_file|
|
54
56
|
all_keys_found = []
|
55
57
|
|
56
58
|
tmp_file.write("/* Generated File. Do not edit. */\n\n")
|
57
|
-
paths.each do |input_file|
|
59
|
+
paths.each do |input_file, prefix|
|
60
|
+
next if File.empty?(input_file) # Skip existing but totally empty files, to avoid adding useless `MARK:` comment for them
|
61
|
+
|
58
62
|
fmt = strings_file_type(path: input_file)
|
59
63
|
raise "The file `#{input_file}` does not exist or is of unknown format." if fmt.nil?
|
60
64
|
raise "The file `#{input_file}` is in #{fmt} format but we currently only support merging `.strings` files in text format." unless fmt == :text
|
61
65
|
|
62
|
-
string_keys = read_strings_file_as_hash(path: input_file).keys
|
66
|
+
string_keys = read_strings_file_as_hash(path: input_file).keys.map { |k| "#{prefix}#{k}" }
|
63
67
|
duplicates += (string_keys & all_keys_found) # Find duplicates using Array intersection, and add those to duplicates list
|
64
68
|
all_keys_found += string_keys
|
65
69
|
|
66
70
|
tmp_file.write("/* MARK: - #{File.basename(input_file)} */\n\n")
|
67
71
|
# Read line-by-line to reduce memory footprint during content copy; Be sure to guess file encoding using the Byte-Order-Mark.
|
68
|
-
File.readlines(input_file, mode: 'rb:BOM|UTF-8').each
|
72
|
+
File.readlines(input_file, mode: 'rb:BOM|UTF-8').each do |line|
|
73
|
+
unless prefix.nil? || prefix.empty?
|
74
|
+
# We need to ensure the line and RegExp are using the same encoding, so we transcode everything to UTF-8.
|
75
|
+
line.encode!(Encoding::UTF_8)
|
76
|
+
# The `/u` modifier on the RegExps is to make them UTF-8
|
77
|
+
line.gsub!(/^(\s*")/u, "\\1#{prefix}") # Lines starting with a quote are considered to be start of a key; add prefix right after the quote
|
78
|
+
line.gsub!(/^(\s*)([A-Z0-9_]+)(\s*=\s*")/ui, "\\1\"#{prefix}\\2\"\\3") # Lines starting with an identifier followed by a '=' are considered to be an unquoted key (typical in InfoPlist.strings files for example)
|
79
|
+
end
|
80
|
+
tmp_file.write(line)
|
81
|
+
end
|
69
82
|
tmp_file.write("\n")
|
70
83
|
end
|
71
84
|
tmp_file.close # ensure we flush the content to disk
|
@@ -81,6 +94,8 @@ module Fastlane
|
|
81
94
|
# @raise [RuntimeError] If the file is not a valid strings file or there was an error in parsing its content.
|
82
95
|
#
|
83
96
|
def self.read_strings_file_as_hash(path:)
|
97
|
+
return {} if File.empty?(path) # Return empty hash if completely empty file
|
98
|
+
|
84
99
|
output, status = Open3.capture2e('/usr/bin/plutil', '-convert', 'json', '-o', '-', path)
|
85
100
|
raise output unless status.success?
|
86
101
|
|
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:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorenzo Mattei
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diffy
|