Class: Fastlane::Actions::AndroidPruneOrphanedTranslationsAction
- Inherits:
-
Action
- Object
- Action
- Fastlane::Actions::AndroidPruneOrphanedTranslationsAction
- Defined in:
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb
Constant Summary collapse
- LOCALE_VALUES_DIR_REGEX =
Matches an Android
values-<qualifier>directory whose qualifier is a locale: a 2- or 3-letter language code (fr,kmr), an optional region (pt-rBR,es-r419), or a BCP-47b+form (b+sr+Latn), so non-locale qualifier dirs (e.g.values-night,values-v21,values-land) are left untouched. /\Avalues-(?:b\+[a-zA-Z]+(?:\+[a-zA-Z0-9]+)*|[a-z]{2,3}(?:-r(?:[A-Z]{2}|\d{3}))?)\z/- UI_MODE_QUALIFIERS =
Android UI-mode qualifiers, which are never locales: https://developer.android.com/guide/topics/resources/providing-resources#UiModeQualifier
caris indistinguishable by shape from a 3-letter ISO 639 language code, soLOCALE_VALUES_DIR_REGEXwould otherwise treatvalues-caras a locale and prune its (valid) car-mode resources. There's no purely syntactic way to tell the two apart, so we exclude the documented UI-mode qualifiers explicitly. (caris the only one short enough to currently match the regex; the rest are listed to capture the full set.) %w[car desk television appliance watch vrheadset].freeze
- DEFAULT_SOURCE_STRINGS_FILE_NAME =
'strings.xml'- DEFAULT_SOURCE_STRINGS_RELATIVE_PATH =
File.join('values', DEFAULT_SOURCE_STRINGS_FILE_NAME).freeze
Documentation collapse
- .authors ⇒ Object
- .available_options ⇒ Object
- .description ⇒ Object
- .details ⇒ Object
- .is_supported?(platform) ⇒ Boolean
- .return_value ⇒ Object
Class Method Summary collapse
-
.collect_keys(paths) ⇒ Set<String>
Collects the set of resource names (string, string-array, plurals, …) declared in the given strings files.
-
.prune_file(file:, valid_keys:) ⇒ Array<String>
Removes from
fileany resource entry whosenameis not invalid_keys, preserving the rest of the file's formatting (so the change shows up as a minimal diff). - .run(params) ⇒ Object
Class Method Details
.authors ⇒ Object
135 136 137 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 135 def self. ['Automattic'] end |
.available_options ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 103 def self. [ FastlaneCore::ConfigItem.new( key: :res_dir, description: "Path to the Android project's `res` directory containing the `values-*` locale subdirectories to prune", type: String, optional: false, verify_block: proc do |value| source_path = File.join(value, DEFAULT_SOURCE_STRINGS_RELATIVE_PATH) UI.user_error!("Source strings file not found: `#{source_path}`") unless File.file?(source_path) end ), FastlaneCore::ConfigItem.new( key: :additional_source_strings_paths, description: 'Paths to additional default `strings.xml` files whose keys should also be treated as valid ' \ '(e.g. a base module that the pruned `res_dir` overlays at build time)', type: Array, optional: true, default_value: [], verify_block: proc do |value| value.each do |path| UI.user_error!("Source strings file not found: `#{path}`") unless File.file?(path) end end ), ] end |
.collect_keys(paths) ⇒ Set<String>
Collects the set of resource names (string, string-array, plurals, …) declared in the given strings files.
52 53 54 55 56 57 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 52 def self.collect_keys(paths) paths.each_with_object(Set.new) do |path, keys| doc = File.open(path) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) } doc.xpath('/resources/*[@name]').each { |node| keys << node['name'] } end end |
.description ⇒ Object
86 87 88 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 86 def self.description 'Removes translations whose keys are not present in the source strings, to avoid Lint `ExtraTranslation` errors' end |
.details ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 90 def self.details <<~DETAILS When downloading translations from GlotPress, the export may include keys that are no longer present in the app's source strings (e.g. removed or renamed since the GlotPress source was last synced). Android Lint flags these orphaned translations as `ExtraTranslation` errors. This action removes — from every `values-*/strings.xml` under `res_dir` — any `<string>`, `<string-array>` or `<plurals>` entry whose `name` is not declared in the default `values/strings.xml` of `res_dir`, optionally unioned with `additional_source_strings_paths` (useful when a product flavor overlays a base module's resources at build time, so the base module's keys are valid too). DETAILS end |
.is_supported?(platform) ⇒ Boolean
139 140 141 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 139 def self.is_supported?(platform) platform == :android end |
.prune_file(file:, valid_keys:) ⇒ Array<String>
Removes from file any resource entry whose name is not in valid_keys, preserving the rest of the
file's formatting (so the change shows up as a minimal diff).
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 65 def self.prune_file(file:, valid_keys:) doc = File.open(file) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) } orphans = doc.xpath('/resources/*[@name]').reject { |node| valid_keys.include?(node['name']) } return [] if orphans.empty? names = orphans.map { |node| node['name'] } orphans.each do |node| # Drop the indentation/newline text node right before the element too, to avoid leaving a blank line. previous = node.previous_sibling previous.remove if previous&.text? && previous.text.strip.empty? node.remove end File.open(file, 'w') { |f| doc.write_to(f, encoding: Encoding::UTF_8.to_s, indent: 4) } names end |
.return_value ⇒ Object
131 132 133 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 131 def self.return_value 'The total number of orphaned translation entries that were pruned' end |
.run(params) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_prune_orphaned_translations_action.rb', line 25 def self.run(params) res_dir = params[:res_dir] source_paths = [File.join(res_dir, DEFAULT_SOURCE_STRINGS_RELATIVE_PATH)] + params[:additional_source_strings_paths] valid_keys = collect_keys(source_paths) locale_files = Dir.glob(File.join(res_dir, 'values-*', DEFAULT_SOURCE_STRINGS_FILE_NAME)).select do |file| dir_name = File.basename(File.dirname(file)) dir_name.match?(LOCALE_VALUES_DIR_REGEX) && !UI_MODE_QUALIFIERS.include?(dir_name.delete_prefix('values-')) end total_pruned = 0 locale_files.each do |file| pruned = prune_file(file: file, valid_keys: valid_keys) next if pruned.empty? total_pruned += pruned.count UI.("Pruned #{pruned.count} orphaned entries from `#{file}`: #{pruned.join(', ')}") end UI.success("Pruned #{total_pruned} orphaned translation entries across #{locale_files.count} locale file(s).") total_pruned end |