module Aigu class AndroidImporter < Importer ARRAY_REGEX = /__ARRAY_ITEM__#(?<index>\d+)$/ TYPE_REGEX = /__@TYPE_(?<type_key>.+)$/ def process! puts "Generating Android XML files in `#{@output_directory}` based on Accent-generated `#{@input_file}` file" puts '---' parse_json @blob = split_res_types(@object) write_xml_files puts '---' puts 'Done' end protected def parse_json json = File.read(@input_file) @object = JSON.parse(json) @object = expand_content_values(@object) @object = escape_special_characters(@object) end def write_xml_files @blob.each_pair do |type, hash| # TODO: add locale to filename qualifier = type.gsub(/_/, '-') if qualifier != '' qualifier.prepend('-') end if @locale language = '-' + @locale else language = '' end file_path = File.join(@output_directory, "values#{language}#{qualifier}", 'strings.xml') puts "Generating #{file_path}" FileUtils.mkdir_p(File.dirname(file_path)) File.open(file_path, 'w+:bom|utf-8') do |file| file << res_to_xml(hash) end end end # TODO: Use XML tooling def res_to_xml(content) xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" xml << "<resources>\n" content.each_pair do |key, value| if value.is_a?(Array) xml << ' <string-array name=' << key.encode(xml: :attr) << ">\n" value.each do |item| xml << ' <item>' << item.encode(xml: :text) << "</item>\n" if item end xml << " </string-array>\n" elsif value.is_a?(String) xml << ' <string name=' << key.encode(xml: :attr) << '>' << value.encode(xml: :text) << "</string>\n" end end xml << "</resources>\n" xml end def localize_content_keys(content) content.reduce({}) do |memo, (key, value)| localized_key = key.gsub(/^([^|]+)\|/, "\\1.#{@locale}|#{@locale}.") memo.merge localized_key => value end end def expand_content_values(content) content.each_with_object({}) do |(key, value), memo| match_data = key.match(ARRAY_REGEX) if match_data canonical_key = key.gsub(ARRAY_REGEX, '') value_index = match_data[:index].to_i memo[canonical_key] ||= [] memo[canonical_key][value_index] = sanitize_string_to_value(value) else memo[key] = sanitize_string_to_value(value) end end end def escape_special_characters(content) content.each_with_object({}) do |(key, value), memo| if value.is_a?(Array) memo[key] = value.map { |s| escape_string_special_characters(s) } else memo[key] = escape_string_special_characters(value) end end end def escape_string_special_characters(string) string.gsub("'", "\\\\'") end def split_res_types(content) res_types = {} content.each_pair do |key, value| match_data = key.match(TYPE_REGEX) if match_data canonical_key = key.gsub(TYPE_REGEX, '') res_type = match_data[:type_key] else canonical_key = key res_type = '' end res_types[res_type] ||= {} res_types[res_type][canonical_key] = value end res_types end def sanitize_string_to_value(string) case string when '___TRUE___' true when '___FALSE___' false when '___NULL___' nil else string end end end end