From 9d905e449fac047a0046d683a700ac18772274c6 Mon Sep 17 00:00:00 2001 From: Sebastian Celis Date: Fri, 10 Feb 2012 16:59:40 -0600 Subject: [PATCH] Improve ability to import new strings. We need an easier way to import new strings from a .strings or .xml file. However, in the normal usecase, we do not want to import unidentified strings since the strings data file is considered the master data file and we assume that if it doesn't contain a string that is found in an input file then that string was recently deleted from the database. So, we add a -a option to import all strings we find. We also print clearer warnings if an unidentified string is found. Also, clean up some of the code that makes more sense as methods of StringsFile or StringsRow instead of the abstract formatter class. --- lib/twine/cli.rb | 3 ++ lib/twine/formatters/abstract.rb | 37 +++++++++++++++++++++++-- lib/twine/formatters/android.rb | 47 +++++++++----------------------- lib/twine/formatters/apple.rb | 26 ++++++++---------- lib/twine/runner.rb | 17 ++++++------ lib/twine/stringsfile.rb | 24 ++++++++++------ 6 files changed, 85 insertions(+), 69 deletions(-) diff --git a/lib/twine/cli.rb b/lib/twine/cli.rb index ee7ea72..0902023 100644 --- a/lib/twine/cli.rb +++ b/lib/twine/cli.rb @@ -53,6 +53,9 @@ module Twine end @options[:format] = lformat end + opts.on('-a', '--all', 'Normally, when consuming a string file, Twine will ignore any string keys that do not exist in your master file. This flag will force those missing strings to be added to your master file.') do |a| + @options[:consume_all] = true + end opts.on('-h', '--help', 'Show this message.') do |h| puts opts.help exit diff --git a/lib/twine/formatters/abstract.rb b/lib/twine/formatters/abstract.rb index 72e250c..1ca25c4 100644 --- a/lib/twine/formatters/abstract.rb +++ b/lib/twine/formatters/abstract.rb @@ -1,10 +1,41 @@ module Twine module Formatters class Abstract + attr_accessor :strings + attr_accessor :options + def self.can_handle_directory?(path) return false end + def initialize(strings, options) + @strings = strings + @options = options + end + + def set_translation_for_key(key, lang, value) + if @strings.strings_map.include?(key) + @strings.strings_map[key].translations[lang] = value + elsif @options[:consume_all] + puts "Adding new string '#{key}' to strings data file." + arr = @strings.sections.select { |s| s.name == 'Uncategorized' } + current_section = arr ? arr[0] : nil + if !current_section + current_section = StringsSection.new('Uncategorized') + @strings.sections.insert(0, current_section) + end + current_row = StringsRow.new(key) + current_section.rows << current_row + @strings.strings_map[key] = current_row + @strings.strings_map[key].translations[lang] = value + else + puts "Warning: '#{key}' not found in strings data file." + end + if !@strings.language_codes.include?(lang) + @strings.add_language_code(lang) + end + end + def default_file_name raise NotImplementedError.new("You must implement default_file_name in your formatter class.") end @@ -13,15 +44,15 @@ module Twine raise NotImplementedError.new("You must implement determine_language_given_path in your formatter class.") end - def read_file(path, lang, strings) + def read_file(path, lang) raise NotImplementedError.new("You must implement read_file in your formatter class.") end - def write_file(path, lang, tags, strings) + def write_file(path, lang) raise NotImplementedError.new("You must implement write_file in your formatter class.") end - def write_all_files(path, tags, strings) + def write_all_files(path) if !File.directory?(path) raise Twine::Error.new("Directory does not exist: #{path}") end diff --git a/lib/twine/formatters/android.rb b/lib/twine/formatters/android.rb index 7011d2b..20c7d6d 100644 --- a/lib/twine/formatters/android.rb +++ b/lib/twine/formatters/android.rb @@ -21,7 +21,7 @@ module Twine def determine_language_given_path(path) path_arr = path.split(File::SEPARATOR) path_arr.each do |segment| - match = /^values-(.*)$/.match(path_arr) + match = /^values-(.*)$/.match(segment) if match lang = match[1] lang.sub!('-r', '-') @@ -32,52 +32,31 @@ module Twine return end - def read_file(path, lang, strings) + def read_file(path, lang) File.open(path, 'r:UTF-8') do |f| current_section = nil doc = REXML::Document.new(f) doc.elements.each('resources/string') do |ele| key = ele.attributes["name"] - if not strings.strings_map.include? key - puts "#{key} not found in strings data file - adding" - if !current_section - strings.sections.each do |section| - if section.name == 'Uncategorized' - current_section = section - end - end - if !current_section - current_section = StringsSection.new('Uncategorized') - strings.sections << current_section - end - end - current_row = StringsRow.new(key) - current_section.rows << current_row - strings.strings_map[key] = current_row - end - value = ele.text - if value - value.gsub!('\\\'', '\'') - value.gsub!(/\n/, '') - value.gsub!('%s', '%@') - strings.strings_map[key].translations[lang] = value - else - strings.strings_map[key].translations[lang] = "" - end + value = ele.text || '' + value.gsub!('\\\'', '\'') + value.gsub!(/\n/, '') + value.gsub!('%s', '%@') + set_translation_for_key(key, lang, value) end end end - def write_file(path, lang, tags, strings) - default_lang = strings.language_codes[0] + def write_file(path, lang) + default_lang = @strings.language_codes[0] File.open(path, 'w:UTF-8') do |f| f.puts "\n\n\n" f.write '' - strings.sections.each do |section| + @strings.sections.each do |section| printed_section = false section.rows.each do |row| - if row.matches_tags?(tags) - unless printed_section + if row.matches_tags?(options[:tags]) + if !printed_section f.puts '' if section.name && section.name.length > 0 section_name = section.name.gsub('--', '—') @@ -89,7 +68,7 @@ module Twine key = row.key key = CGI.escapeHTML(key) - value = row.translated_string_for_and_lang(lang, default_lang) + value = row.translated_string_for_lang(lang, default_lang) value.gsub!('\'', '\\\\\'') value.gsub!('%@', '%s') value = CGI.escapeHTML(value) diff --git a/lib/twine/formatters/apple.rb b/lib/twine/formatters/apple.rb index fb2d73b..133149f 100644 --- a/lib/twine/formatters/apple.rb +++ b/lib/twine/formatters/apple.rb @@ -25,34 +25,30 @@ module Twine return end - def read_file(path, lang, strings) + def read_file(path, lang) File.open(path, 'r:UTF-8') do |f| while line = f.gets - match = /"((?:[^"\\]|\\.)+)"\s*=\s*"((?:[^"\\]|\\.)*)/.match(line) + match = /"((?:[^"\\]|\\.)+)"\s*=\s*"((?:[^"\\]|\\.)*)"/.match(line) if match key = match[1] key.gsub!('\\"', '"') - if strings.strings_map.include? key - value = match[2] - value.gsub!('\\"', '"') - strings.strings_map[key].translations[lang] = value - else - puts "#{key} not found in strings data file." - end + value = match[2] + value.gsub!('\\"', '"') + set_translation_for_key(key, lang, value) end end end end - def write_file(path, lang, tags, strings) - default_lang = strings.language_codes[0] + def write_file(path, lang) + default_lang = @strings.language_codes[0] File.open(path, 'w:UTF-8') do |f| f.puts "/**\n * iOS Strings File\n * Generated by Twine\n * Language: #{lang}\n */" - strings.sections.each do |section| + @strings.sections.each do |section| printed_section = false section.rows.each do |row| - if row.matches_tag?(tags) - unless printed_section + if row.matches_tags?(options[:tags]) + if !printed_section f.puts '' if section.name && section.name.length > 0 f.puts "/* #{section.name} */" @@ -63,7 +59,7 @@ module Twine key = row.key key = key.gsub('"', '\\\\"') - value = row.translated_string_for_and_lang(lang, default_lang) + value = row.translated_string_for_lang(lang, default_lang) value = value.gsub('"', '\\\\"') comment = row.comment diff --git a/lib/twine/runner.rb b/lib/twine/runner.rb index f270c78..35aad2d 100644 --- a/lib/twine/runner.rb +++ b/lib/twine/runner.rb @@ -53,7 +53,7 @@ module Twine lang = @options[:languages][0] end - read_write_string_file(@options[:output_path], false, lang, @options[:format], @options[:tags]) + read_write_string_file(@options[:output_path], false, lang) end def generate_all_string_files @@ -71,7 +71,7 @@ module Twine formatter = formatter_for_format(format) - formatter.write_all_files(@options[:output_path], @options[:tags], @strings) + formatter.write_all_files(@options[:output_path]) end def consume_string_file @@ -80,15 +80,16 @@ module Twine lang = @options[:languages][0] end - read_write_string_file(@options[:input_path], true, lang, @options[:format], nil) + read_write_string_file(@options[:input_path], true, lang) @strings.write(@options[:strings_file]) end - def read_write_string_file(path, is_read, lang, format, tags) + def read_write_string_file(path, is_read, lang) if is_read && !File.file?(path) raise Twine::Error.new("File does not exist: #{path}") end + format = @options[:format] if !format format = determine_format_given_path(path) end @@ -113,9 +114,9 @@ module Twine end if is_read - formatter.read_file(path, lang, @strings) + formatter.read_file(path, lang) else - formatter.write_file(path, lang, tags, @strings) + formatter.write_file(path, lang) end end @@ -166,7 +167,7 @@ module Twine real_path = File.join(dir, entry.name) FileUtils.mkdir_p(File.dirname(real_path)) zipfile.extract(entry.name, real_path) - read_write_string_file(real_path, true, nil, nil, @options[:tags]) + read_write_string_file(real_path, true, nil) end end end @@ -259,7 +260,7 @@ module Twine def formatter_for_format(format) Formatters::FORMATTERS.each do |formatter| if formatter::FORMAT_NAME == format - return formatter.new + return formatter.new(@strings, @options) end end diff --git a/lib/twine/stringsfile.rb b/lib/twine/stringsfile.rb index f3e67e3..07aa299 100644 --- a/lib/twine/stringsfile.rb +++ b/lib/twine/stringsfile.rb @@ -40,7 +40,7 @@ module Twine end def translated_string_for_lang(lang, default_lang=nil) - row.translations[lang] || row.translations[default_lang] + @translations[lang] || @translations[default_lang] end end @@ -108,7 +108,7 @@ module Twine current_row.tags = value.split(',') else if !@language_codes.include? key - @language_codes << key + add_language_code(key) end current_row.translations[key] = value end @@ -120,12 +120,6 @@ module Twine raise Twine::Error.new("Unable to parse line #{line_num} of #{path}: #{line}") end end - - # Developer Language - dev_lang = @language_codes[0] - @language_codes.delete(dev_lang) - @language_codes.sort! - @language_codes.insert(0, dev_lang) end end @@ -144,7 +138,7 @@ module Twine f.puts "\t[#{row.key}]" value = row.translations[dev_lang] if !value - puts "Warning! #{row.key} does not exist in #{dev_lang}" + puts "Warning: #{row.key} does not exist in developer language '#{dev_lang}'" else if value[0,1] == ' ' || value[-1,1] == ' ' || (value[0,1] == '`' && value[-1,1] == '`') value = '`' + value + '`' @@ -172,5 +166,17 @@ module Twine end end end + + def add_language_code(code) + if @language_codes.length == 0 + @language_codes << code + elsif !@language_codes.include?(code) + dev_lang = @language_codes[0] + @language_codes << code + @language_codes.delete(dev_lang) + @language_codes.sort! + @language_codes.insert(0, dev_lang) + end + end end end