Class: Fastlane::Helper::Ios::L10nHelper
- Inherits:
-
Object
- Object
- Fastlane::Helper::Ios::L10nHelper
- Defined in:
- lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb
Class Method Summary collapse
-
.download_glotpress_export_file(project_url:, locale:, filters:, destination:) ⇒ Object
Downloads the export from GlotPress for a given locale and given filters.
-
.generate_strings_file_from_hash(translations:, output_path:) ⇒ Object
Generate a ‘.strings` file from a dictionary of string translations.
-
.merge_strings(paths:, output_path:) ⇒ Array<String>
Merge the content of multiple ‘.strings` files into a new `.strings` text file.
-
.read_strings_file_as_hash(path:) ⇒ Hash<String,String>
Return the list of translations in a ‘.strings` file.
-
.read_utf8_lines(file) { ... } ⇒ Object
Read a file line by line and iterate over it (just like ‘File.readlines` does), except that it also detects the encoding used by the file (using the BOM if present) when reading it, and then convert each line to UTF-8 before yielding it.
-
.strings_file_type(path:) ⇒ Symbol
Returns the type of a ‘.strings` file (XML, binary or ASCII).
Class Method Details
.download_glotpress_export_file(project_url:, locale:, filters:, destination:) ⇒ Object
Downloads the export from GlotPress for a given locale and given filters.
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb', line 164 def self.download_glotpress_export_file(project_url:, locale:, filters:, destination:) query_params = (filters || {}).transform_keys { |k| "filters[#{k}]" }.merge(format: 'strings') uri = URI.parse("#{project_url.chomp('/')}/#{locale}/default/export-translations/?#{URI.encode_www_form(query_params)}") # Set an unambiguous User Agent so GlotPress won't rate-limit us = { 'User-Agent' => Wpmreleasetoolkit::USER_AGENT } begin IO.copy_stream(uri.open(), destination) rescue StandardError => e UI.error "Error downloading locale `#{locale}` — #{e.} (#{uri})" retry if e.is_a?(OpenURI::HTTPError) && UI.confirm("Retry downloading `#{locale}`?") nil end end |
.generate_strings_file_from_hash(translations:, output_path:) ⇒ Object
The generated file will be in XML-plist format since ASCII plist is deprecated as an output format by every Apple tool so there’s no safe way to generate ASCII format.
Generate a ‘.strings` file from a dictionary of string translations.
Especially useful to generate ‘.strings` files not from code, but from keys extracted from another source (like a JSON file export from GlotPress, or subset of keys extracted from the main `Localizable.strings` to generate an `InfoPlist.strings`)
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb', line 136 def self.generate_strings_file_from_hash(translations:, output_path:) builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml| xml.doc.create_internal_subset( 'plist', '-//Apple//DTD PLIST 1.0//EN', 'http://www.apple.com/DTDs/PropertyList-1.0.dtd' ) xml.comment('Warning: Auto-generated file, do not edit.') xml.plist(version: '1.0') do xml.dict do translations.sort.each do |k, v| # NOTE: use `sort` just in order to be deterministic over various runs xml.key(k.to_s) xml.string(v.to_s) end end end end File.write(output_path, builder.to_xml) end |
.merge_strings(paths:, output_path:) ⇒ Array<String>
For now, this method only supports merging ‘.strings` file in `:text` format and basically concatenates the files (+ checking for duplicates in the process)
The method is able to handle input files which are using different encodings, guessing the encoding of each input file using the BOM (and defaulting to UTF8). The generated file will always be in utf-8, by convention.
Merge the content of multiple ‘.strings` files into a new `.strings` text file.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb', line 75 def self.merge_strings(paths:, output_path:) duplicates = [] Tempfile.create('wpmrt-l10n-merge-', encoding: 'utf-8') do |tmp_file| all_keys_found = [] tmp_file.write("/* Generated File. Do not edit. */\n\n") paths.each do |input_file, prefix| next if File.empty?(input_file) # Skip existing but totally empty files, to avoid adding useless `MARK:` comment for them fmt = strings_file_type(path: input_file) raise "The file `#{input_file}` does not exist or is of unknown format." if fmt.nil? raise "The file `#{input_file}` is in #{fmt} format but we currently only support merging `.strings` files in text format." unless fmt == :text string_keys = read_strings_file_as_hash(path: input_file).keys.map { |k| "#{prefix}#{k}" } duplicates += (string_keys & all_keys_found) # Find duplicates using Array intersection, and add those to duplicates list all_keys_found += string_keys tmp_file.write("/* MARK: - #{File.basename(input_file)} */\n\n") # Read line-by-line to reduce memory footprint during content copy read_utf8_lines(input_file).each do |line| unless prefix.nil? || prefix.empty? # The `/u` modifier on the RegExps is to make them UTF-8 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 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) end tmp_file.write(line) end tmp_file.write("\n") end tmp_file.close # ensure we flush the content to disk FileUtils.cp(tmp_file.path, output_path) end duplicates end |
.read_strings_file_as_hash(path:) ⇒ Hash<String,String>
Return the list of translations in a ‘.strings` file.
116 117 118 119 120 121 122 123 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb', line 116 def self.read_strings_file_as_hash(path:) return {} if File.empty?(path) # Return empty hash if completely empty file output, status = Open3.capture2e('/usr/bin/plutil', '-convert', 'json', '-o', '-', path) raise output unless status.success? JSON.parse(output) end |
.read_utf8_lines(file) { ... } ⇒ Object
Read a file line by line and iterate over it (just like ‘File.readlines` does), except that it also detects the encoding used by the file (using the BOM if present) when reading it, and then convert each line to UTF-8 before yielding it
This is particularly useful if you need to then use a ‘RegExp` to match part of the lines you’re iterating over, as the ‘RegExp` (which will typically be UTF-8) and the string you’re matching with it have to use the same encoding (otherwise we would get a ‘Encoding::CompatibilityError`)
remember to use the ‘u` flag on it (`/…/u`) to make it UTF-8-aware too.
53 54 55 56 57 58 59 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb', line 53 def self.read_utf8_lines(file) # Be sure to guess file encoding using the Byte-Order-Mark, and fallback to UTF-8 if there's no BOM. File.readlines(file, mode: 'rb:BOM|UTF-8').map do |line| # Ensure the line is re-encoded to UTF-8 regardless of the encoding that was used in the input file line.encode(Encoding::UTF_8) end end |
.strings_file_type(path:) ⇒ Symbol
Returns the type of a ‘.strings` file (XML, binary or ASCII)
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb', line 21 def self.strings_file_type(path:) return :text if File.empty?(path) # If completely empty file, consider it as a valid `.strings` files in textual format # Start by checking it seems like a valid property-list file (and not e.g. an image or plain text file) _, status = Open3.capture2('/usr/bin/plutil', '-lint', path) return nil unless status.success? # If it is a valid property-list file, determine the actual format used format_desc, status = Open3.capture2('/usr/bin/file', path) return nil unless status.success? case format_desc when /Apple binary property list/ then :binary when /XML/ then :xml when /text/ then :text end end |