android-specific improvements

- cross-reference between iOS/Android language/region codes
- support for default languages other than English
- proper escaping of ' and "
- translation between %@ and %s, including numbered parameters (e.g. %1$@)
- proper XML-escaping of < and &
This commit is contained in:
Scott Weber 2012-02-20 14:43:33 -05:00
parent 125eb59036
commit ce96dcd425
2 changed files with 57 additions and 17 deletions

View file

@ -1,6 +1,5 @@
# encoding: utf-8
require 'cgi'
require 'rexml/document'
module Twine
@ -9,6 +8,20 @@ module Twine
FORMAT_NAME = 'android'
EXTENSION = '.xml'
DEFAULT_FILE_NAME = 'strings.xml'
LANG_CODES = Hash[
'zh' => 'zh-Hans',
'zh-rCN' => 'zh-Hans',
'zh-rHK' => 'zh-Hant',
'zh-rTW' => 'zh-TW',
'en-rGB' => 'en-UK',
'fr-rCA' => 'fr-CA',
'in' => 'id',
'nb' => 'no'
# TODO: spanish
]
DEFAULT_LANG_CODES = Hash[
'zh-TW' => 'zh-Hant' # if we don't have a zh-TW translation, try zh-Hant before en
]
def self.can_handle_directory?(path)
Dir.entries(path).any? { |item| /^values-.+$/.match(item) }
@ -24,6 +37,7 @@ module Twine
match = /^values-(.*)$/.match(segment)
if match
lang = match[1]
lang = LANG_CODES.fetch(lang, lang)
lang.sub!('-r', '-')
return lang
end
@ -42,14 +56,19 @@ module Twine
value.gsub!('\\\'', '\'')
value.gsub!('\\"', '"')
value.gsub!(/\n/, '')
value.gsub!('%s', '%@')
value.gsub!(/%([0-9\$]*)s/, '%\1@')
value.gsub!('&lt;', '<')
value.gsub!('&amp;', '&')
set_translation_for_key(key, lang, value)
end
end
end
def write_file(path, lang)
default_lang = @strings.language_codes[0]
default_lang = [@strings.language_codes[0]]
if DEFAULT_LANG_CODES.has_key?(lang)
default_lang.insert(0, DEFAULT_LANG_CODES[lang])
end
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>'
@ -67,22 +86,32 @@ module Twine
end
key = row.key
key = CGI.escapeHTML(key)
value = row.translated_string_for_lang(lang, default_lang)
value.gsub!('\'', '\\\\\'')
value.gsub!('%@', '%s')
value = CGI.escapeHTML(value)
comment = row.comment
if comment
comment = comment.gsub('--', '—')
value = String.new(value) # use a copy to prevent modifying the original
if value
# if values is nil, there was no default, so let Android handle the defaulting
# Android enforces the following rules on the values
# 1) apostrophes and quotes must be escaped with a backslash
value.gsub!('\'', '\\\\\'')
value.gsub!('"', '\\\\"')
# 2) ampersand and less-than must be in XML-escaped form
value.gsub!('&', '&amp;')
value.gsub!('<', '&lt;')
# 3) use "s" instead of "@" for substituting strings
value.gsub!(/%([0-9\$]*)@/, '%\1s')
comment = row.comment
if comment
comment = comment.gsub('--', '—')
end
if comment && comment.length > 0
f.puts "\t<!-- #{comment} -->\n"
end
f.puts "\t<string name=\"#{key}\">#{value}</string>"
end
if comment && comment.length > 0
f.puts "\t<!-- #{comment} -->\n"
end
f.puts "\t<string name=\"#{key}\">#{value}</string>"
end
end
end

View file

@ -41,7 +41,18 @@ module Twine
end
def translated_string_for_lang(lang, default_lang=nil)
@translations[lang] || @translations[default_lang]
if @translations[lang]
return @translations[lang]
elsif default_lang.respond_to?("each")
default_lang.each do |def_lang|
if @translations[def_lang]
return @translations[def_lang]
end
end
return nil
else
return @translations[default_lang]
end
end
end