fastlane-plugin-wpmreleasetoolkit 3.1.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|