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.
This commit is contained in:
parent
4399266aeb
commit
9d905e449f
6 changed files with 85 additions and 69 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Android Strings File -->\n<!-- Generated by Twine -->\n<!-- Language: #{lang} -->"
|
||||
f.write '<resources>'
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Reference in a new issue