Class: Fastlane::Actions::IosLintLocalizationsAction

Inherits:
Action
  • Object
show all
Defined in:
lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb

Constant Summary collapse

RETRY_MESSAGE =
<<~MSG
  Inconsistencies found during Localization linting.
  You need to fix them before continuing. From this point on, you should either:

  - Cancel this lane (reply 'No' below), then work with polyglots in #i18n
    to fix those directly in GlotPress – by rejecting the inconsistent
    translations, or by submitting a fixed copy. Rerun the lane when everything
    has been fixed.

    This is the recommended way to go, as it will fix the issues at their source.

  - Or manually edit the `Localizable.strings` files to fix the inconsistencies
    locally, commit them, then reply 'Yes' below to re-lint and validate that all
    inconsistencies have been fixed locally so you can continue with the build.

    This is only a workaround to allow you to submit a build if translators are
    not available to help you fix the issues in GlotPress in time. You will still
    need to let the translators know that they will need to fix those copies
    at some point before the next build to fix the root of the issue.

  Did you fix the `.strings` files locally and want to lint them again?
MSG
ABORT_MESSAGE =
<<~MSG
  Inconsistencies found during Localization linting. Aborting.
MSG

Documentation collapse

Class Method Summary collapse

Class Method Details

.authorsObject



215
216
217
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 215

def self.authors
  ['Automattic']
end

.available_optionsObject



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 128

def self.available_options
  [
    FastlaneCore::ConfigItem.new(
      key: :install_path,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_INSTALL_PATH',
      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',
      type: String,
      optional: true,
      default_value: "vendor/swiftgen/#{Fastlane::Helper::Ios::L10nLinterHelper::SWIFTGEN_VERSION}"
    ),
    FastlaneCore::ConfigItem.new(
      key: :version,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_SWIFTGEN_VERSION',
      description: 'The version of SwiftGen to install and use for linting',
      type: String,
      optional: true,
      default_value: Fastlane::Helper::Ios::L10nLinterHelper::SWIFTGEN_VERSION
    ),
    FastlaneCore::ConfigItem.new(
      key: :input_dir,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_INPUT_DIR',
      description: 'The path to the directory containing the .lproj folders to lint, relative to your git repo root',
      type: String,
      optional: false
    ),
    FastlaneCore::ConfigItem.new(
      key: :base_lang,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_BASE_LANG',
      description: 'The language that should be used as the base language that every other language will be compared against',
      type: String,
      optional: true,
      default_value: Fastlane::Helper::Ios::L10nLinterHelper::DEFAULT_BASE_LANG
    ),
    FastlaneCore::ConfigItem.new(
      key: :only_langs,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_ONLY_LANGS',
      description: 'The list of languages to limit the analysis to',
      type: Array,
      optional: true
    ),
    FastlaneCore::ConfigItem.new(
      key: :abort_on_violations,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_ABORT',
      description: 'Should we abort the rest of the lane with a global error if any violations are found?',
      optional: true,
      default_value: true,
      type: Boolean
    ),
    FastlaneCore::ConfigItem.new(
      key: :allow_retry,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_ALLOW_RETRY',
      description: 'If any violations are found, show an interactive prompt allowing the user to manually fix the issues locally and retry the linting',
      optional: true,
      default_value: false,
      type: Boolean
    ),
    FastlaneCore::ConfigItem.new(
      key: :check_duplicate_keys,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_CHECK_DUPLICATE_KEYS',
      description: 'Checks the input files for duplicate keys',
      optional: true,
      default_value: true,
      type: Boolean
    ),
    FastlaneCore::ConfigItem.new(
      key: :fail_on_strings_not_in_base_language,
      env_name: 'FL_IOS_LINT_TRANSLATIONS_FAIL_ON_STRINGS_NOT_IN_BASE_LANGUAGE',
      description: 'Should we report violations when finding strings in translations that are not present in the base language',
      optional: true,
      default_value: true,
      type: Boolean
    ),
  ]
end

.descriptionObject



120
121
122
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 120

def self.description
  'Lint the different *.lproj/.strings files for each locale and ensure the parameter placeholders are consistent.'
end

.detailsObject



124
125
126
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 124

def self.details
  'Compares the translations against a base language to find potential mismatches for the %s/%d/… parameter placeholders between locales.'
end

.find_duplicated_keys(params) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 51

def self.find_duplicated_keys(params)
  duplicate_keys = {}

  files_to_lint = Dir.glob('*.lproj/Localizable.strings', base: params[:input_dir])
  files_to_lint.each do |file|
    language = File.basename(File.dirname(file), '.lproj')
    path = File.join(params[:input_dir], file)

    status, payload = Fastlane::Helper::Ios::StringsFileValidationHelper.scan_for_duplicate_keys(file: path)
    case status
    when :scanned
      duplicate_keys[language] = payload.map { |key, value| "`#{key}` was found at multiple lines: #{value.join(', ')}" } unless payload.empty?
    when :unsupported_format
      UI.important <<~WRONG_FORMAT
        File `#{path}` is in #{payload} format, while finding duplicate keys can only occur on files that are in ASCII-plist format.
        Since your files are in #{payload} format, you should probably disable the `check_duplicate_keys` option from this `#{action_name}` call.
      WRONG_FORMAT
    when :unscannable
      UI.important <<~UNSCANNABLE
        Could not check `#{path}` for duplicate keys: #{payload.strip}
        The file parses as a property list but isn't a flat `.strings` file the duplicate-key scanner understands, so duplicate detection was skipped for it.
      UNSCANNABLE
    end
  end

  duplicate_keys
end

.is_supported?(platform) ⇒ Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 219

def self.is_supported?(platform)
  %i[ios mac].include?(platform)
end

.outputObject



203
204
205
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 203

def self.output
  nil
end

.repo_rootObject



106
107
108
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 106

def self.repo_root
  @repo_root || `git rev-parse --show-toplevel`.chomp
end

.report(violations:, base_lang:) ⇒ Object



45
46
47
48
49
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 45

def self.report(violations:, base_lang:)
  violations.each do |lang, lang_violations|
    UI.error "Inconsistencies found between '#{base_lang}' and '#{lang}':\n\n#{lang_violations.join("\n")}\n"
  end
end

.resolve_path(path) ⇒ Object

If the path is relative, makes the path absolute by resolving it relative to the repository's root. If the path is already absolute, it will not affect it and return it as-is.



112
113
114
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 112

def self.resolve_path(path)
  File.absolute_path(path, repo_root)
end

.return_typeObject



207
208
209
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 207

def self.return_type
  :hash
end

.return_valueObject



211
212
213
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 211

def self.return_value
  'A hash, keyed by language code, whose values are arrays of violations found for that language'
end

.run(params) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 6

def self.run(params)
  violations = nil

  loop do
    violations = run_linter(params)
    violations.default = [] # Set the default value for when querying a missing key

    if params[:check_duplicate_keys]
      find_duplicated_keys(params).each do |language, duplicates|
        violations[language] += duplicates
      end
    end

    report(violations: violations, base_lang: params[:base_lang])
    break unless !violations.empty? && params[:allow_retry] && UI.confirm(RETRY_MESSAGE)
  end

  UI.abort_with_message!(ABORT_MESSAGE) if !violations.empty? && params[:abort_on_violations]

  violations
end

.run_linter(params) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb', line 28

def self.run_linter(params)
  UI.message 'Linting localizations for parameter placeholders consistency...'

  require_relative '../../helper/ios/ios_l10n_linter_helper'
  helper = Fastlane::Helper::Ios::L10nLinterHelper.new(
    install_path: resolve_path(params[:install_path]),
    version: params[:version]
  )

  helper.run(
    input_dir: resolve_path(params[:input_dir]),
    base_lang: params[:base_lang],
    only_langs: params[:only_langs],
    fail_on_strings_not_in_base_language: params[:fail_on_strings_not_in_base_language]
  )
end