fastlane-plugin-wpmreleasetoolkit 2.2.0 → 3.1.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/android/an_localize_libs_action.rb +8 -3
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_betabuild_prechecks.rb +8 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_release.rb +11 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_codefreeze_prechecks.rb +10 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/buildkite_trigger_build_action.rb +90 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_downloadmetadata_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/upload_to_s3.rb +112 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_betabuild_prechecks.rb +8 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_release.rb +10 -5
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_codefreeze_prechecks.rb +10 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_download_strings_files_from_glotpress.rb +113 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_extract_keys_from_strings_files.rb +118 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_generate_strings_file_from_code.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb +5 -5
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_localize_project.rb +2 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_strings_files.rb +75 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_git_helper.rb +0 -20
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_localize_helper.rb +8 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb +3 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_git_helper.rb +3 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb +108 -173
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_linter_helper.rb +207 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/promo_screenshots_helper.rb +3 -3
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/file_reference.rb +1 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +1 -1
- metadata +25 -39
- data/bin/drawText +0 -20
- data/ext/drawText/drawText/Assets/style.css +0 -1
- data/ext/drawText/drawText/CoreTextStack.swift +0 -113
- data/ext/drawText/drawText/Helpers/CommandLineHelpers.swift +0 -36
- data/ext/drawText/drawText/Helpers/Extensions.swift +0 -27
- data/ext/drawText/drawText/Helpers/FileSystemHelper.swift +0 -24
- data/ext/drawText/drawText/Stylesheet.swift +0 -48
- data/ext/drawText/drawText/TextImage.swift +0 -100
- data/ext/drawText/drawText/main.swift +0 -61
- data/ext/drawText/drawText Tests/DigitParsingTests.swift +0 -21
- data/ext/drawText/drawText Tests/ExtensionsTests.swift +0 -5
- data/ext/drawText/drawText Tests/Info.plist +0 -22
- data/ext/drawText/drawText Tests/StylesheetTests.swift +0 -31
- data/ext/drawText/drawText Tests/Test Cases/default-stylesheet.txt +0 -10
- data/ext/drawText/drawText Tests/Test Cases/external-styles-sample.css +0 -3
- data/ext/drawText/drawText Tests/Test Cases/external-styles-test.txt +0 -13
- data/ext/drawText/drawText Tests/Test Cases/large-text-block.txt +0 -1
- data/ext/drawText/drawText Tests/Test Cases/regular-text-block.txt +0 -2
- data/ext/drawText/drawText Tests/Test Cases/rtl-text-block.txt +0 -2
- data/ext/drawText/drawText Tests/Test Cases/text-size-adjustment-test.txt +0 -10
- data/ext/drawText/drawText Tests/TextImageTests.swift +0 -99
- data/ext/drawText/drawText Tests/drawText_Tests.swift +0 -14
- data/ext/drawText/drawText.xcodeproj/project.pbxproj +0 -508
- data/ext/drawText/drawText.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- data/ext/drawText/drawText.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- data/ext/drawText/drawText.xcodeproj/xcshareddata/xcschemes/drawText Tests.xcscheme +0 -57
- data/ext/drawText/drawText.xcodeproj/xcshareddata/xcschemes/drawText.xcscheme +0 -109
- data/ext/drawText/extconf.rb +0 -36
- data/ext/drawText/makefile.example +0 -8
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_merge_translators_strings.rb +0 -106
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_metadata.rb +0 -52
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_translators_strings.rb +0 -93
@@ -0,0 +1,118 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class IosExtractKeysFromStringsFilesAction < Action
|
4
|
+
def self.run(params)
|
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)
|
8
|
+
|
9
|
+
# For each locale, extract the right translations from `<source_tablename>.strings` into each target `.strings` file
|
10
|
+
Dir.glob('*.lproj', base: source_parent_dir).each do |lproj_dir_name|
|
11
|
+
source_strings_file = File.join(source_parent_dir, lproj_dir_name, "#{params[:source_tablename]}.strings")
|
12
|
+
translations = Fastlane::Helper::Ios::L10nHelper.read_strings_file_as_hash(path: source_strings_file)
|
13
|
+
|
14
|
+
target_original_files.each do |target_original_file|
|
15
|
+
target_strings_file = File.join(File.dirname(File.dirname(target_original_file)), lproj_dir_name, File.basename(target_original_file))
|
16
|
+
next if target_strings_file == target_original_file # do not generate/overwrite the original locale itself
|
17
|
+
|
18
|
+
keys_to_extract = keys_to_extract_per_target_file[target_original_file]
|
19
|
+
UI.message("Extracting #{keys_to_extract.count} keys into #{target_strings_file}...")
|
20
|
+
|
21
|
+
extracted_translations = translations.slice(*keys_to_extract)
|
22
|
+
FileUtils.mkdir_p(File.dirname(target_strings_file)) # Ensure path up to parent dir exists, create it if not.
|
23
|
+
Fastlane::Helper::Ios::L10nHelper.generate_strings_file_from_hash(translations: extracted_translations, output_path: target_strings_file)
|
24
|
+
rescue StandardError => e
|
25
|
+
UI.user_error!("Error while writing extracted translations to `#{target_strings_file}`: #{e.message}")
|
26
|
+
end
|
27
|
+
rescue StandardError => e
|
28
|
+
UI.user_error!("Error while reading the translations from source file `#{source_strings_file}`: #{e.message}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Pre-load the list of keys to extract for each target file.
|
33
|
+
#
|
34
|
+
# @param [Array<String>] original_files array of paths to the originals of target files
|
35
|
+
# @return [Hash<String, Array<String>>] The hash listing the keys to extract for each target file
|
36
|
+
#
|
37
|
+
def self.keys_list_per_target_file(original_files)
|
38
|
+
original_files.map do |original_file|
|
39
|
+
keys = Fastlane::Helper::Ios::L10nHelper.read_strings_file_as_hash(path: original_file).keys
|
40
|
+
[original_file, keys]
|
41
|
+
end.to_h
|
42
|
+
rescue StandardError => e
|
43
|
+
UI.user_error!("Failed to read the keys to extract from originals file: #{e.message}")
|
44
|
+
end
|
45
|
+
|
46
|
+
#####################################################
|
47
|
+
# @!group Documentation
|
48
|
+
#####################################################
|
49
|
+
|
50
|
+
def self.description
|
51
|
+
'Extracts a subset of keys from a `.strings` file into separate `.strings` file(s)'
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.details
|
55
|
+
<<~DETAILS
|
56
|
+
Extracts a subset of keys from a `.strings` file into separate `.strings` file(s), for each `*.lproj` subdirectory.
|
57
|
+
|
58
|
+
This is especially useful to extract, for each locale, the translations for files like `InfoPlist.strings` or
|
59
|
+
`<SomeIntentDefinitionFile>.strings` from the `Localizable.strings` file that we exported/downloaded back from GlotPress.
|
60
|
+
|
61
|
+
Since we typically merge all `*.strings` original files (e.g. `en.lproj/Localizable.strings` + `en.lproj/InfoPlist.strings` + …)
|
62
|
+
via `ios_merge_strings_file` before sending the originals to translations, we then need to extract the relevant keys and
|
63
|
+
translations back into the `*.lproj/InfoPlist.strings` after we pull those translations back from GlotPress
|
64
|
+
(`ios_download_strings_files_from_glotpress`). This is what this `ios_extract_keys_from_strings_files` action is for.
|
65
|
+
DETAILS
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.available_options
|
69
|
+
[
|
70
|
+
FastlaneCore::ConfigItem.new(key: :source_parent_dir,
|
71
|
+
env_name: 'FL_IOS_EXTRACT_KEYS_FROM_STRINGS_FILES_SOURCE_PARENT_DIR',
|
72
|
+
description: 'The parent directory containing all the `*.lproj` subdirectories in which the source `.strings` files reside',
|
73
|
+
type: String,
|
74
|
+
verify_block: proc do |value|
|
75
|
+
UI.user_error!("`source_parent_dir` should be a path to an existing directory, but found `#{value}`.") unless File.directory?(value)
|
76
|
+
UI.user_error!("`source_parent_dir` should contain at least one `.lproj` subdirectory, but `#{value}` does not contain any.") if Dir.glob('*.lproj', base: value).empty?
|
77
|
+
end),
|
78
|
+
FastlaneCore::ConfigItem.new(key: :source_tablename,
|
79
|
+
env_name: 'FL_IOS_EXTRACT_KEYS_FROM_STRINGS_FILES_SOURCE_TABLENAME',
|
80
|
+
description: 'The basename of the `.strings` file (without the extension) to extract the keys and translations from for each locale',
|
81
|
+
type: String,
|
82
|
+
default_value: 'Localizable'),
|
83
|
+
FastlaneCore::ConfigItem.new(key: :target_original_files,
|
84
|
+
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 of those files should containing the original strings (typically `en` or `Base` locale) and will be used to determine which keys to extract from the `source_tablename`. ' \
|
87
|
+
+ 'For each of those, the path(s) in which the translations will be extracted will be the files with the same basename in each of the other `*.lproj` sibling folders',
|
88
|
+
type: Array,
|
89
|
+
verify_block: proc do |values|
|
90
|
+
UI.user_error!('`target_original_files` must contain at least one path to an original `.strings` file.') if values.empty?
|
91
|
+
values.each do |v|
|
92
|
+
UI.user_error!("Path `#{v}` (found in `target_original_files`) does not exist.") unless File.exist?(v)
|
93
|
+
UI.user_error! "Expected `#{v}` (found in `target_original_files`) to be a path ending in a `*.lproj/*.strings`." unless File.extname(v) == '.strings' && File.extname(File.dirname(v)) == '.lproj'
|
94
|
+
end
|
95
|
+
end),
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.return_type
|
100
|
+
# Describes what type of data is expected to be returned
|
101
|
+
# see RETURN_TYPES in https://github.com/fastlane/fastlane/blob/master/fastlane/lib/fastlane/action.rb
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.return_value
|
106
|
+
# Freeform textual description of the return value
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.authors
|
110
|
+
['Automattic']
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.is_supported?(platform)
|
114
|
+
[:ios, :mac].include?(platform)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_generate_strings_file_from_code.rb
CHANGED
@@ -36,7 +36,7 @@ module Fastlane
|
|
36
36
|
#####################################################
|
37
37
|
|
38
38
|
def self.description
|
39
|
-
'Generate the
|
39
|
+
'Generate the `.strings` files from your Objective-C and Swift code'
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.details
|
@@ -17,8 +17,8 @@ module Fastlane
|
|
17
17
|
def self.run_linter(params)
|
18
18
|
UI.message 'Linting localizations for parameter placeholders consistency...'
|
19
19
|
|
20
|
-
require_relative '../../helper/ios/
|
21
|
-
helper = Fastlane::Helper::Ios::
|
20
|
+
require_relative '../../helper/ios/ios_l10n_linter_helper'
|
21
|
+
helper = Fastlane::Helper::Ios::L10nLinterHelper.new(
|
22
22
|
install_path: resolve_path(params[:install_path]),
|
23
23
|
version: params[:version]
|
24
24
|
)
|
@@ -92,7 +92,7 @@ module Fastlane
|
|
92
92
|
description: 'The path where to install the SwiftGen tooling needed to run the linting process. If a relative path, should be relative to your repo_root',
|
93
93
|
type: String,
|
94
94
|
optional: true,
|
95
|
-
default_value: "vendor/swiftgen/#{Fastlane::Helper::Ios::
|
95
|
+
default_value: "vendor/swiftgen/#{Fastlane::Helper::Ios::L10nLinterHelper::SWIFTGEN_VERSION}"
|
96
96
|
),
|
97
97
|
FastlaneCore::ConfigItem.new(
|
98
98
|
key: :version,
|
@@ -100,7 +100,7 @@ module Fastlane
|
|
100
100
|
description: 'The version of SwiftGen to install and use for linting',
|
101
101
|
type: String,
|
102
102
|
optional: true,
|
103
|
-
default_value: Fastlane::Helper::Ios::
|
103
|
+
default_value: Fastlane::Helper::Ios::L10nLinterHelper::SWIFTGEN_VERSION
|
104
104
|
),
|
105
105
|
FastlaneCore::ConfigItem.new(
|
106
106
|
key: :input_dir,
|
@@ -115,7 +115,7 @@ module Fastlane
|
|
115
115
|
description: 'The language that should be used as the base language that every other language will be compared against',
|
116
116
|
type: String,
|
117
117
|
optional: true,
|
118
|
-
default_value: Fastlane::Helper::Ios::
|
118
|
+
default_value: Fastlane::Helper::Ios::L10nLinterHelper::DEFAULT_BASE_LANG
|
119
119
|
),
|
120
120
|
FastlaneCore::ConfigItem.new(
|
121
121
|
key: :only_langs,
|
@@ -16,11 +16,11 @@ module Fastlane
|
|
16
16
|
#####################################################
|
17
17
|
|
18
18
|
def self.description
|
19
|
-
'Gathers the
|
19
|
+
'Gathers the strings to localise. Deprecated'
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.details
|
23
|
-
'Gathers the
|
23
|
+
'Gathers the strings to localise. Deprecated in favor of the new `ios_generate_strings_file_from_code`'
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.category
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class IosMergeStringsFilesAction < Action
|
4
|
+
def self.run(params)
|
5
|
+
UI.message "Merging strings files: #{params[:paths].inspect}"
|
6
|
+
|
7
|
+
duplicates = Fastlane::Helper::Ios::L10nHelper.merge_strings(paths: params[:paths], output_path: params[:destination])
|
8
|
+
duplicates.each do |dup_key|
|
9
|
+
UI.important "Duplicate key found while merging the `.strings` files: `#{dup_key}`"
|
10
|
+
end
|
11
|
+
duplicates
|
12
|
+
end
|
13
|
+
|
14
|
+
#####################################################
|
15
|
+
# @!group Documentation
|
16
|
+
#####################################################
|
17
|
+
|
18
|
+
def self.description
|
19
|
+
'Merge multiple `.strings` files into one'
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.details
|
23
|
+
<<~DETAILS
|
24
|
+
Merge multiple `.strings` files into one.
|
25
|
+
|
26
|
+
Especially useful to prepare a single `.strings` file merging strings from both `Localizable.strings` from
|
27
|
+
the app code — typically previously extracted from `ios_generate_strings_file_from_code` —
|
28
|
+
and string files like `InfoPlist.strings` — which values may not be generated from the codebase but
|
29
|
+
manually maintained by developers.
|
30
|
+
|
31
|
+
The action only supports merging files which are in the OpenStep (`"key" = "value";`) text format (which is
|
32
|
+
the most common format for `.strings` files, and the one generated by `genstrings`), but can handle the case
|
33
|
+
of different files using different encodings (UTF8 vs UTF16) and is able to detect and report duplicates.
|
34
|
+
It does not handle `.strings` files in XML or binary-plist formats (which are valid but more rare)
|
35
|
+
DETAILS
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.available_options
|
39
|
+
[
|
40
|
+
FastlaneCore::ConfigItem.new(
|
41
|
+
key: :paths,
|
42
|
+
env_name: 'FL_IOS_MERGE_STRINGS_FILES_PATHS',
|
43
|
+
description: 'The paths of all the `.strings` files to merge together',
|
44
|
+
type: Array,
|
45
|
+
optional: false
|
46
|
+
),
|
47
|
+
FastlaneCore::ConfigItem.new(
|
48
|
+
key: :destination,
|
49
|
+
env_name: 'FL_IOS_MERGE_STRINGS_FILES_DESTINATION',
|
50
|
+
description: 'The path of the merged `.strings` file to generate. If nil, the merge will happen in-place in the first file in the `paths:` list',
|
51
|
+
type: String,
|
52
|
+
optional: true,
|
53
|
+
default_value: nil
|
54
|
+
),
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.return_type
|
59
|
+
:array_of_strings
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.return_value
|
63
|
+
'The list of duplicate keys found while merging the various `.strings` files'
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.authors
|
67
|
+
['automattic']
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.is_supported?(platform)
|
71
|
+
[:ios, :mac].include? platform
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -27,26 +27,6 @@ module Fastlane
|
|
27
27
|
)
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
31
|
-
# Calls the `tools/update-translations.sh` script from the project repo, then lint them using the provided gradle task
|
32
|
-
#
|
33
|
-
# Deprecated. Use the `android_download_translations` action instead.
|
34
|
-
#
|
35
|
-
# @env PROJECT_ROOT_FOLDER The path to the git root of the project
|
36
|
-
# @env PROJECT_NAME The name of the directory containing the project code (especially containing the `build.gradle` file)
|
37
|
-
#
|
38
|
-
# @param [String] validate_translation_command The name of the gradle task to run to validate the translations
|
39
|
-
#
|
40
|
-
# @todo Remove this once every client has migrated to `android_download_translations` and we got rid of that legacy action.
|
41
|
-
#
|
42
|
-
def self.update_metadata(validate_translation_command)
|
43
|
-
Action.sh('./tools/update-translations.sh')
|
44
|
-
Action.sh('git', 'submodule', 'update', '--init', '--recursive')
|
45
|
-
Action.sh('./gradlew', validate_translation_command)
|
46
|
-
|
47
|
-
res_dir = File.join(ENV['PROJECT_ROOT_FOLDER'], ENV['PROJECT_NAME'], 'src', 'main', 'res')
|
48
|
-
Fastlane::Helper::GitHelper.commit(message: 'Update translations', files: res_dir, push: true)
|
49
|
-
end
|
50
30
|
end
|
51
31
|
end
|
52
32
|
end
|
@@ -92,6 +92,14 @@ module Fastlane
|
|
92
92
|
UI.user_error!("String #{string_name} [#{string_content}] was found in library #{library[:library]} but not in the main file.")
|
93
93
|
end
|
94
94
|
|
95
|
+
# Merge strings from a library into the strings.xml of the main app
|
96
|
+
#
|
97
|
+
# @param [String] main Path to the main strings.xml file (something like `…/res/values/strings.xml`)
|
98
|
+
# @param [Hash] library Hash describing the library to merge. The Hash should contain the following keys:
|
99
|
+
# - `:library`: The human readable name of the library, used to display in console messages
|
100
|
+
# - `:strings_path`: The path to the strings.xml file of the library to merge into the main one
|
101
|
+
# - `:exclusions`: An array of strings keys to exclude during merge. Any of those keys from the library's `strings.xml` will be skipped and won't be merged into the main one.
|
102
|
+
# @return [Boolean] True if at least one string from the library has been added to (or has updated) the main strings file.
|
95
103
|
def self.merge_lib(main, library)
|
96
104
|
UI.message("Merging #{library[:library]} strings into #{main}")
|
97
105
|
main_strings = File.open(main) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) }
|
@@ -5,6 +5,9 @@ module Fastlane
|
|
5
5
|
# Helper methods to execute git-related operations
|
6
6
|
#
|
7
7
|
module GitHelper
|
8
|
+
# Fallback default branch of the client repository.
|
9
|
+
DEFAULT_GIT_BRANCH = 'trunk'.freeze
|
10
|
+
|
8
11
|
# Checks if the given path, or current directory if no path is given, is
|
9
12
|
# inside a Git repository
|
10
13
|
#
|
@@ -35,8 +35,9 @@ module Fastlane
|
|
35
35
|
# @env PROJECT_ROOT_FOLDER The path to the git root of the project
|
36
36
|
# @env PROJECT_NAME The name of the directory containing the project code (especially containing the `build.gradle` file)
|
37
37
|
#
|
38
|
-
# @
|
39
|
-
#
|
38
|
+
# @deprecated This method is only used by the `ios_localize_project` action, which is itself deprecated
|
39
|
+
# in favor of the new `ios_generate_strings_file_from_code` action
|
40
|
+
# @todo [Next Major] Remove this method once we fully remove `ios_localize_project`
|
40
41
|
#
|
41
42
|
def self.localize_project
|
42
43
|
Action.sh("cd #{get_from_env!(key: 'PROJECT_ROOT_FOLDER')} && ./Scripts/localize.py")
|