module Poesie::AppleFormatter

Public Class Methods

write_strings_file(terms, file, substitutions: nil, print_date: false, exclude: Poesie::Filters::EXCLUDE_ANDROID) click to toggle source

Write the Localizable.strings output file

@param [Array<Hash<String, Any>>] terms

JSON returned by the POEditor API

@param [String] file

The path of the file to write

@param [Hash<String,String>] substitutions

The list of substitutions to apply to the translations

@param [Bool] print_date

Should we print the date in the header of the generated file

@param [Regexp] exclude

A regular expression to filter out terms.
Terms matching this Regexp will be ignored and won't be part of the generated file
# File lib/apple_formatter.rb, line 20
def self.write_strings_file(terms, file, substitutions: nil, print_date: false, exclude: Poesie::Filters::EXCLUDE_ANDROID)
  out_lines = ['/'+'*'*79, ' * Exported from POEditor - https://poeditor.com']
  out_lines << " * #{Time.now}" if print_date
  out_lines += [' '+'*'*79+'/', '']
  last_prefix = ''
  stats = { :excluded => 0, :nil => [], :count => 0 }

  terms.each do |term|
    (term, definition, comment, context) = ['term', 'definition', 'comment', 'context'].map { |k| term[k] }

    # Filter terms and update stats
    next if (term.nil? || term.empty? || definition.nil? || definition.empty?) && stats[:nil] << term
    next if (term =~ exclude) && stats[:excluded] += 1
    stats[:count] += 1

    # Generate MARK from prefixes
    prefix = %r(([^_]*)_.*).match(term)
    if prefix && prefix[1] != last_prefix
      last_prefix = prefix[1]
      mark = last_prefix[0].upcase + last_prefix[1..-1].downcase
      out_lines += ['', '/'*80, "// MARK: #{mark}"]
    end

    # If definition is a Hash, use the text for "one" if available (singular in languages using plurals)
    # otherwise (e.g. asian language where only key in hash will be "other", not "one"), then use the first entry
    if definition.is_a? Hash
      definition = definition["one"] || definition.values.first
    end

    definition = Poesie::process(definition, substitutions)
                .gsub("\u2028", '') # Sometimes inserted by the POEditor exporter
                .gsub("\n", '\n') # Replace actual CRLF with '\n'
                .gsub('"', '\\"') # Escape quotes
                .gsub(/%(\d+\$)?s/, '%\1@') # replace %s with %@ for iOS
    out_lines << %Q(// CONTEXT: #{context.gsub("\n", '\n')}) unless context.empty?
    out_lines << %Q("#{term}" = "#{definition}";)
  end

  content = out_lines.join("\n") + "\n"

  Log::info(" - Save to file: #{file}")
  File.open(file, "w") do |fh|
    fh.write(content)
  end
  Log::info("   [Stats] #{stats[:count]} strings processed")
  unless exclude.nil?
    Log::info("   Filtered out #{stats[:excluded]} strings matching #{exclude.inspect})")
  end
  unless stats[:nil].empty?
    Log::error("   Found #{stats[:nil].count} empty value(s) for the following term(s):")
    stats[:nil].each { |key| Log::error("    - #{key.inspect}") }
  end
end
write_stringsdict_file(terms, file, substitutions: nil, print_date: false, exclude: Poesie::Filters::EXCLUDE_ANDROID) click to toggle source

Write the Localizable.stringsdict output file

@param [Array<Hash<String, Any>>] terms

JSON returned by the POEditor API

@param [String] file

The path of the file to write

@param [Hash<String,String>] substitutions

The list of substitutions to apply to the translations

@param [Bool] print_date

Should we print the date in the header of the generated file

@param [Regexp] exclude

A regular expression to filter out terms.
Terms matching this Regexp will be ignored and won't be part of the generated file
# File lib/apple_formatter.rb, line 88
def self.write_stringsdict_file(terms, file, substitutions: nil, print_date: false, exclude: Poesie::Filters::EXCLUDE_ANDROID)
  stats = { :excluded => 0, :nil => [], :count => 0 }

  Log::info(" - Save to file: #{file}")
  File.open(file, "w") do |fh|
    xml_builder = Builder::XmlMarkup.new(:target => fh, :indent => 4)
    xml_builder.instruct!
    xml_builder.comment!("Exported from POEditor   ")
    xml_builder.comment!(Time.now) if print_date
    xml_builder.comment!("see https://poeditor.com ")
    xml_builder.plist(:version => '1.0') do |plist_node|
      plist_node.dict do |root_node|
        terms.each do |term|
          (term, term_plural, definition) = ['term', 'term_plural', 'definition'].map { |k| term[k] }

          # Filter terms and update stats
          next if (term.nil? || term.empty? || definition.nil?) && stats[:nil] << term
          next if (term =~ exclude) && stats[:excluded] += 1
          next unless definition.is_a? Hash
          stats[:count] += 1

          key = term_plural || term

          root_node.key(key)
          root_node.dict do |dict_node|
            dict_node.key('NSStringLocalizedFormatKey')
            dict_node.string('%#@format@')
            dict_node.key('format')
            dict_node.dict do |format_node|
              format_node.key('NSStringFormatSpecTypeKey')
              format_node.string('NSStringPluralRuleType')
              format_node.key('NSStringFormatValueTypeKey')
              format_node.string('d')

              definition.each do |(quantity, text)|
                text = Poesie::process(text, substitutions)
                text = Poesie::process(text, substitutions)
                          .gsub("\u2028", '') # Sometimes inserted by the POEditor exporter
                          .gsub('\n', "\n") # Replace '\n' with actual CRLF
                          .gsub(/%(\d+\$)?s/, '%\1@') # replace %s with %@ for iOS
                format_node.key(quantity)
                format_node.string(text)
              end
            end
          end
        end
      end
    end
  end

  Log::info("   [Stats] #{stats[:count]} strings processed")
  unless exclude.nil?
    Log::info("   Filtered out #{stats[:excluded]} strings matching #{exclude.inspect})")
  end
  unless stats[:nil].empty?
    Log::error("   Found #{stats[:nil].count} empty value(s) for the following term(s):")
    stats[:nil].each { |key| Log::error("    - #{key.inspect}") }
  end
end