class Terrestrial::Cli::DotStringsParser

Public Class Methods

new(path) click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 13
def initialize(path)
  @path = path
end
parse_file(path) click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 8
def parse_file(path)
  new(path).parse
end

Public Instance Methods

parse() click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 17
def parse
  entries = do_parse_file read_file_with_correct_encoding(path)
  entries.each do |entry|
    entry["type"] = "localizable.strings"
    entry["file"] = path
  end
end

Private Instance Methods

do_parse_file(contents) click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 31
def do_parse_file(contents)
  results = []
  
  multiline_comment = false
  expecting_string  = false
  multiline_string  = false
  current = {}

  contents.split("\n").each_with_index do |line, line_number|
    @current_line = line
    @current_line_number = line_number
    line = line.rstrip
    line = remove_comments(line) unless multiline_string

    if !multiline_string && !multiline_comment && line == ""
      # Just empty line between entries
      next 
    elsif line.start_with?("\"") && !line.end_with?(";")
      # Start multiline string"
      current_id = line.split("=").map(&:strip)[0][1..-1][0..-2]
      current_string = line.split("=").map(&:strip)[1][1..-1]

      current["identifier"] = current_id
      current["string"] = current_string unless current_string.empty?
      multiline_string = true
    elsif multiline_string && !line.end_with?(";")
      # Continuing multiline string
      if current["string"].nil?
        current["string"] = line.lstrip
      else
        current["string"] << "\n" + line
      end
    elsif multiline_string && line.end_with?(";")
      # Ending multiline string
      current["string"] << "\n#{line[0..-3]}"
      multiline_string = false
      results << current
      current = {}
    elsif !expecting_string && line.lstrip.start_with?("/*") && !line.end_with?("*/")
      # Start multline comment
      tmp_content = line[2..-1].strip
      current["context"] = "\n" + tmp_content unless tmp_content.empty?
      multiline_comment = true
    elsif multiline_comment && !line.end_with?("*/")
      # Continuing multline comment
      if current["context"].nil?
        current["context"] = line.lstrip
      else
        current["context"] << "\n" + line
      end
    elsif multiline_comment && line.end_with?("*/")
      # Ending multline comment
      tmp_content = line[0..-3].strip
      current["context"] << (tmp_content.empty? ? "" : "\n#{tmp_content}")
      multiline_comment = false
      expecting_string  = true
    elsif !expecting_string && line.start_with?("/*") && line.end_with?("*/")
      # Single line comment
      current["context"] = line[2..-1][0..-3].strip
      expecting_string = true
    elsif expecting_string && line.end_with?(";")
      # Single line id/string pair after a comment
      current_id, current_string = get_string_and_id(line)
      current["identifier"] = current_id
      current["string"] = current_string

      expecting_string = false
      results << current
      current = {}
    elsif !expecting_string && line.end_with?(";")
      # id/string without comment first
      current_id, current_string = get_string_and_id(line)
      current["identifier"] = current_id
      current["string"] = current_string

      results << current
      current = {}
    else
      raise LocaliableStringsParserError
    end
  end
  results
rescue
  puts "There was an error parsing #{path}:"
  puts ""
  puts "  line #{@current_line_number}: "
  puts "  #{@current_line}"
  Kernel.abort
end
get_string_and_id(line) click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 121
def get_string_and_id(line)
  key_and_string_escape_double_quotes = /"((?:[^"\\]|\\.)*)"\s*=\s*\"((?:[^"\\]|\\.)*)";$/
  cap = line.match(key_and_string_escape_double_quotes).captures
  if cap[0].nil? || cap[1].nil?
    raise LocaliableStringsParserError
  else
    cap
  end
end
path() click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 27
def path
  @path
end
read_file_with_correct_encoding(path) click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 131
def read_file_with_correct_encoding(path)
  # Genstrings creates files with BOM UTF-16LE encoding.
  # If we realise that we cannot operate on the content
  # of the file assumin UTF-8, we try UTF-16!

  content = File.read path
  begin 
    # Try performing an operation on the content
    content.split("\n") 
  rescue ArgumentError
    # Failure! We think this is a UTF-16 file

    # Remove the byte order marker from the beginning
    # of the file. We tried doing this with a simple
    # sub! of \xFF\xFE, but we kept running into
    # more issues. We instead do it manually.
    content = content.bytes[2..-1].pack('c*')

    # Force UTF-16LE encoding as a setting (not actually
    # changing any representations yet!), then encode from
    # that to UTF-8 again
    content = content
                .force_encoding(Encoding::UTF_16LE)
                .encode!(Encoding::UTF_8)
  end
  content
end
remove_comments(line) click to toggle source
# File lib/terrestrial/cli/dot_strings_parser.rb, line 159
def remove_comments(line)
  # Regex delightfully borrowed from
  #   http://stackoverflow.com/questions/6462578/alternative-to-regex-match-all-instances-not-inside-quotes
  double_slashes_not_inside_quotes = /\/\/(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)/
  line = line.split(double_slashes_not_inside_quotes)[0] || ""
  line.rstrip
end