twine 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,73 +29,41 @@ module Twine
29
29
  return
30
30
  end
31
31
 
32
- def read_file(path, lang)
32
+ def read(io, lang)
33
33
  comment_regex = /#\. *"?(.*)"?$/
34
34
  key_regex = /msgid *"(.*)"$/
35
35
  value_regex = /msgstr *"(.*)"$/m
36
36
 
37
- encoding = Twine::Encoding.encoding_for_path(path)
38
- sep = nil
39
- if !encoding.respond_to?(:encode)
40
- # This code is not necessary in 1.9.3 and does not work as it did in 1.8.7.
41
- if encoding.end_with? 'LE'
42
- sep = "\x0a\x00"
43
- elsif encoding.end_with? 'BE'
44
- sep = "\x00\x0a"
45
- else
46
- sep = "\n"
37
+ last_comment = nil
38
+ while line = io.gets
39
+ comment_match = comment_regex.match(line)
40
+ if comment_match
41
+ comment = comment_match[1]
47
42
  end
48
- end
49
-
50
- if encoding.index('UTF-16')
51
- mode = "rb:#{encoding}"
52
- else
53
- mode = "r:#{encoding}"
54
- end
55
-
56
- File.open(path, mode) do |f|
57
- last_comment = nil
58
- while line = (sep) ? f.gets(sep) : f.gets
59
- if encoding.index('UTF-16')
60
- if line.respond_to? :encode!
61
- line.encode!('UTF-8')
62
- else
63
- require 'iconv'
64
- line = Iconv.iconv('UTF-8', encoding, line).join
65
- end
66
- end
67
-
68
- comment_match = comment_regex.match(line)
69
- if comment_match
70
- comment = comment_match[1]
71
- end
72
-
73
- key_match = key_regex.match(line)
74
- if key_match
75
- key = key_match[1].gsub('\\"', '"')
76
- end
77
- value_match = value_regex.match(line)
78
- if value_match
79
- value = value_match[1].gsub(/"\n"/, '').gsub('\\"', '"')
80
- end
81
43
 
44
+ key_match = key_regex.match(line)
45
+ if key_match
46
+ key = key_match[1].gsub('\\"', '"')
47
+ end
48
+ value_match = value_regex.match(line)
49
+ if value_match
50
+ value = value_match[1].gsub(/"\n"/, '').gsub('\\"', '"')
51
+ end
82
52
 
83
- if key and key.length > 0 and value and value.length > 0
84
- set_translation_for_key(key, lang, value)
85
- if comment and comment.length > 0 and !comment.start_with?("--------- ")
86
- set_comment_for_key(key, comment)
87
- end
88
- key = nil
89
- value = nil
90
- comment = nil
53
+ if key and key.length > 0 and value and value.length > 0
54
+ set_translation_for_key(key, lang, value)
55
+ if comment and comment.length > 0 and !comment.start_with?("--------- ")
56
+ set_comment_for_key(key, comment)
91
57
  end
92
-
58
+ key = nil
59
+ value = nil
60
+ comment = nil
93
61
  end
94
62
  end
95
63
  end
96
64
 
97
- def format_file(strings, lang)
98
- @default_lang = strings.language_codes[0]
65
+ def format_file(lang)
66
+ @default_lang = @strings.language_codes[0]
99
67
  result = super
100
68
  @default_lang = nil
101
69
  result
@@ -21,57 +21,31 @@ module Twine
21
21
  return
22
22
  end
23
23
 
24
- def read_file(path, lang)
25
- encoding = Twine::Encoding.encoding_for_path(path)
26
- sep = nil
27
- if !encoding.respond_to?(:encode)
28
- # This code is not necessary in 1.9.3 and does not work as it did in 1.8.7.
29
- if encoding.end_with? 'LE'
30
- sep = "\x0a\x00"
31
- elsif encoding.end_with? 'BE'
32
- sep = "\x00\x0a"
24
+ def read(io, lang)
25
+ last_comment = nil
26
+ while line = io.gets
27
+ match = /((?:[^"\\]|\\.)+)\s*=\s*((?:[^"\\]|\\.)*)/.match(line)
28
+ if match
29
+ key = match[1]
30
+ value = match[2].strip
31
+ value.gsub!(/\{[0-9]\}/, '%@')
32
+ set_translation_for_key(key, lang, value)
33
+ if last_comment
34
+ set_comment_for_key(key, last_comment)
35
+ end
36
+ end
37
+
38
+ match = /# *(.*)/.match(line)
39
+ if match
40
+ last_comment = match[1]
33
41
  else
34
- sep = "\n"
42
+ last_comment = nil
35
43
  end
36
44
  end
45
+ end
37
46
 
38
- if encoding.index('UTF-16')
39
- mode = "rb:#{encoding}"
40
- else
41
- mode = "r:#{encoding}"
42
- end
43
-
44
- File.open(path, mode) do |f|
45
- last_comment = nil
46
- while line = (sep) ? f.gets(sep) : f.gets
47
- if encoding.index('UTF-16')
48
- if line.respond_to? :encode!
49
- line.encode!('UTF-8')
50
- else
51
- require 'iconv'
52
- line = Iconv.iconv('UTF-8', encoding, line).join
53
- end
54
- end
55
- match = /((?:[^"\\]|\\.)+)\s*=\s*((?:[^"\\]|\\.)*)/.match(line)
56
- if match
57
- key = match[1]
58
- value = match[2].strip
59
- value.gsub!(/\{[0-9]\}/, '%@')
60
- set_translation_for_key(key, lang, value)
61
- if last_comment
62
- set_comment_for_key(key, last_comment)
63
- end
64
- end
65
-
66
- match = /# *(.*)/.match(line)
67
- if match
68
- last_comment = match[1]
69
- else
70
- last_comment = nil
71
- end
72
-
73
- end
74
- end
47
+ def format_sections(strings, lang)
48
+ super + "\n"
75
49
  end
76
50
 
77
51
  def format_header(lang)
@@ -31,40 +31,39 @@ module Twine
31
31
  return
32
32
  end
33
33
 
34
- def read_file(path, lang)
34
+ def read(io, lang)
35
35
  comment_regex = /#.? *"(.*)"$/
36
36
  key_regex = /msgctxt *"(.*)"$/
37
37
  value_regex = /msgstr *"(.*)"$/m
38
- File.open(path, 'r:UTF-8') do |f|
39
- while item = f.gets("\n\n")
40
- key = nil
41
- value = nil
42
- comment = nil
43
-
44
- comment_match = comment_regex.match(item)
45
- if comment_match
46
- comment = comment_match[1]
47
- end
48
- key_match = key_regex.match(item)
49
- if key_match
50
- key = key_match[1].gsub('\\"', '"')
51
- end
52
- value_match = value_regex.match(item)
53
- if value_match
54
- value = value_match[1].gsub(/"\n"/, '').gsub('\\"', '"')
55
- end
56
- if key and key.length > 0 and value and value.length > 0
57
- set_translation_for_key(key, lang, value)
58
- if comment and comment.length > 0 and !comment.start_with?("SECTION:")
59
- set_comment_for_key(key, comment)
60
- end
61
- comment = nil
38
+
39
+ while item = io.gets("\n\n")
40
+ key = nil
41
+ value = nil
42
+ comment = nil
43
+
44
+ comment_match = comment_regex.match(item)
45
+ if comment_match
46
+ comment = comment_match[1]
47
+ end
48
+ key_match = key_regex.match(item)
49
+ if key_match
50
+ key = key_match[1].gsub('\\"', '"')
51
+ end
52
+ value_match = value_regex.match(item)
53
+ if value_match
54
+ value = value_match[1].gsub(/"\n"/, '').gsub('\\"', '"')
55
+ end
56
+ if key and key.length > 0 and value and value.length > 0
57
+ set_translation_for_key(key, lang, value)
58
+ if comment and comment.length > 0 and !comment.start_with?("SECTION:")
59
+ set_comment_for_key(key, comment)
62
60
  end
61
+ comment = nil
63
62
  end
64
63
  end
65
64
  end
66
65
 
67
- def format_file(strings, lang)
66
+ def format_file(lang)
68
67
  @default_lang = strings.language_codes[0]
69
68
  result = super
70
69
  @default_lang = nil
@@ -29,23 +29,23 @@ module Twine
29
29
  return
30
30
  end
31
31
 
32
- def read_file(path, lang)
32
+ def read(io, lang)
33
33
  begin
34
34
  require "json"
35
35
  rescue LoadError
36
36
  raise Twine::Error.new "You must run 'gem install json' in order to read or write jquery-localize files."
37
37
  end
38
38
 
39
- open(path) do |io|
40
- json = JSON.load(io)
41
- json.each do |key, value|
42
- set_translation_for_key(key, lang, value)
43
- end
39
+ json = JSON.load(io)
40
+ json.each do |key, value|
41
+ set_translation_for_key(key, lang, value)
44
42
  end
45
43
  end
46
44
 
47
- def format_file(strings, lang)
48
- "{\n#{super}\n}"
45
+ def format_file(lang)
46
+ result = super
47
+ return result unless result
48
+ "{\n#{super}\n}\n"
49
49
  end
50
50
 
51
51
  def format_sections(strings, lang)
@@ -49,7 +49,7 @@ module Twine
49
49
  return
50
50
  end
51
51
 
52
- def read_file(path, lang)
52
+ def read(io, lang)
53
53
  resources_regex = /<resources(?:[^>]*)>(.*)<\/resources>/m
54
54
  key_regex = /<string name="(\w+)">/
55
55
  comment_regex = /<!-- (.*) -->/
@@ -58,35 +58,33 @@ module Twine
58
58
  value = nil
59
59
  comment = nil
60
60
 
61
- File.open(path, 'r:UTF-8') do |f|
62
- content_match = resources_regex.match(f.read)
63
- if content_match
64
- for line in content_match[1].split(/\r?\n/)
65
- key_match = key_regex.match(line)
66
- if key_match
67
- key = key_match[1]
68
- value_match = value_regex.match(line)
69
- if value_match
70
- value = value_match[1]
71
- value = CGI.unescapeHTML(value)
72
- value.gsub!('\\\'', '\'')
73
- value.gsub!('\\"', '"')
74
- value = convert_placeholders_from_android_to_twine(value)
75
- value.gsub!(/(\\u0020)*|(\\u0020)*\z/) { |spaces| ' ' * (spaces.length / 6) }
76
- else
77
- value = ""
78
- end
79
- set_translation_for_key(key, lang, value)
80
- if comment and comment.length > 0 and !comment.start_with?("SECTION:")
81
- set_comment_for_key(key, comment)
82
- end
83
- comment = nil
61
+ content_match = resources_regex.match(io.read)
62
+ if content_match
63
+ for line in content_match[1].split(/\r?\n/)
64
+ key_match = key_regex.match(line)
65
+ if key_match
66
+ key = key_match[1]
67
+ value_match = value_regex.match(line)
68
+ if value_match
69
+ value = value_match[1]
70
+ value = CGI.unescapeHTML(value)
71
+ value.gsub!('\\\'', '\'')
72
+ value.gsub!('\\"', '"')
73
+ value = convert_placeholders_from_android_to_twine(value)
74
+ value.gsub!(/(\\u0020)*|(\\u0020)*\z/) { |spaces| ' ' * (spaces.length / 6) }
75
+ else
76
+ value = ""
84
77
  end
85
-
86
- comment_match = comment_regex.match(line)
87
- if comment_match
88
- comment = comment_match[1]
78
+ set_translation_for_key(key, lang, value)
79
+ if comment and comment.length > 0 and !comment.start_with?("SECTION:")
80
+ set_comment_for_key(key, comment)
89
81
  end
82
+ comment = nil
83
+ end
84
+
85
+ comment_match = comment_regex.match(line)
86
+ if comment_match
87
+ comment = comment_match[1]
90
88
  end
91
89
  end
92
90
  end
@@ -101,7 +99,7 @@ module Twine
101
99
 
102
100
  result += super + "\n"
103
101
 
104
- result += '</string_table>'
102
+ result += "</string_table>\n"
105
103
  end
106
104
 
107
105
  def format_section_header(section)
@@ -31,9 +31,9 @@ module Twine
31
31
 
32
32
  value = row.translated_string_for_lang(language)
33
33
 
34
- next if value && @options[:include] == 'untranslated'
34
+ next if value && @options[:include] == :untranslated
35
35
 
36
- if value.nil? && @options[:include] != 'translated'
36
+ if value.nil? && @options[:include] != :translated
37
37
  value = row.translated_string_for_lang(fallback_languages(language))
38
38
  end
39
39
 
data/lib/twine/runner.rb CHANGED
@@ -48,7 +48,12 @@ module Twine
48
48
  lang = nil
49
49
  lang = @options[:languages][0] if @options[:languages]
50
50
 
51
- write_string_file(@options[:output_path], lang)
51
+ formatter, lang = prepare_read_write(@options[:output_path], lang)
52
+ output = formatter.format_file(lang)
53
+
54
+ raise Twine::Error.new "Nothing to generate! The resulting file would not contain any strings." unless output
55
+
56
+ IO.write(@options[:output_path], output, encoding: encoding)
52
57
  end
53
58
 
54
59
  def generate_all_string_files
@@ -69,7 +74,51 @@ module Twine
69
74
  raise Twine::Error.new "Could not determine format given the contents of #{@options[:output_path]}"
70
75
  end
71
76
 
72
- formatter.write_all_files(@options[:output_path])
77
+ file_name = @options[:file_name] || formatter.default_file_name
78
+ if @options[:create_folders]
79
+ @strings.language_codes.each do |lang|
80
+ output_path = File.join(@options[:output_path], formatter.output_path_for_language(lang))
81
+
82
+ FileUtils.mkdir_p(output_path)
83
+
84
+ file_path = File.join(output_path, file_name)
85
+
86
+ output = formatter.format_file(lang)
87
+ unless output
88
+ Twine::stderr.puts "Skipping file at path #{file_path} since it would not contain any strings."
89
+ next
90
+ end
91
+
92
+ IO.write(file_path, output, encoding: encoding)
93
+ end
94
+ else
95
+ language_found = false
96
+ Dir.foreach(@options[:output_path]) do |item|
97
+ next if item == "." or item == ".."
98
+
99
+ output_path = File.join(@options[:output_path], item)
100
+ next unless File.directory?(output_path)
101
+
102
+ lang = formatter.determine_language_given_path(output_path)
103
+ next unless lang
104
+
105
+ language_found = true
106
+
107
+ file_path = File.join(output_path, file_name)
108
+ output = formatter.format_file(lang)
109
+ unless output
110
+ Twine::stderr.puts "Skipping file at path #{file_path} since it would not contain any strings."
111
+ next
112
+ end
113
+
114
+ IO.write(file_path, output, encoding: encoding)
115
+ end
116
+
117
+ unless language_found
118
+ raise Twine::Error.new("Failed to generate any files: No languages found at #{@options[:output_path]}")
119
+ end
120
+ end
121
+
73
122
  end
74
123
 
75
124
  def consume_string_file
@@ -111,7 +160,7 @@ module Twine
111
160
  File.delete(@options[:output_path])
112
161
  end
113
162
 
114
- Dir.mktmpdir do |dir|
163
+ Dir.mktmpdir do |temp_dir|
115
164
  Zip::File.open(@options[:output_path], Zip::File::CREATE) do |zipfile|
116
165
  zipfile.mkdir('Locales')
117
166
 
@@ -119,10 +168,17 @@ module Twine
119
168
  @strings.language_codes.each do |lang|
120
169
  if @options[:languages] == nil || @options[:languages].length == 0 || @options[:languages].include?(lang)
121
170
  file_name = lang + formatter.extension
122
- real_path = File.join(dir, file_name)
171
+ temp_path = File.join(temp_dir, file_name)
123
172
  zip_path = File.join('Locales', file_name)
124
- formatter.write_file(real_path, lang)
125
- zipfile.add(zip_path, real_path)
173
+
174
+ output = formatter.format_file(lang)
175
+ unless output
176
+ Twine::stderr.puts "Skipping file #{file_name} since it would not contain any strings."
177
+ next
178
+ end
179
+
180
+ IO.write(temp_path, output, encoding: encoding)
181
+ zipfile.add(zip_path, temp_path)
126
182
  end
127
183
  end
128
184
  end
@@ -136,18 +192,18 @@ module Twine
136
192
  raise Twine::Error.new("File does not exist: #{@options[:input_path]}")
137
193
  end
138
194
 
139
- Dir.mktmpdir do |dir|
195
+ Dir.mktmpdir do |temp_dir|
140
196
  Zip::File.open(@options[:input_path]) do |zipfile|
141
197
  zipfile.each do |entry|
142
- if !entry.name.end_with?'/' and !File.basename(entry.name).start_with?'.'
143
- real_path = File.join(dir, entry.name)
144
- FileUtils.mkdir_p(File.dirname(real_path))
145
- zipfile.extract(entry.name, real_path)
146
- begin
147
- read_string_file(real_path)
148
- rescue Twine::Error => e
149
- Twine::stderr.puts "#{e.message}"
150
- end
198
+ next if entry.name.end_with? '/' or File.basename(entry.name).start_with? '.'
199
+
200
+ real_path = File.join(temp_dir, entry.name)
201
+ FileUtils.mkdir_p(File.dirname(real_path))
202
+ zipfile.extract(entry.name, real_path)
203
+ begin
204
+ read_string_file(real_path)
205
+ rescue Twine::Error => e
206
+ Twine::stderr.puts "#{e.message}"
151
207
  end
152
208
  end
153
209
  end
@@ -204,6 +260,10 @@ module Twine
204
260
 
205
261
  private
206
262
 
263
+ def encoding
264
+ @options[:output_encoding] || 'UTF-8'
265
+ end
266
+
207
267
  def require_rubyzip
208
268
  begin
209
269
  require 'zip'
@@ -236,12 +296,12 @@ module Twine
236
296
 
237
297
  formatter, lang = prepare_read_write(path, lang)
238
298
 
239
- formatter.read_file(path, lang)
240
- end
299
+ encoding = @options[:encoding] || Twine::Encoding.encoding_for_path(path)
241
300
 
242
- def write_string_file(path, lang)
243
- formatter, lang = prepare_read_write(path, lang)
244
- formatter.write_file(path, lang)
301
+ IO.open(IO.sysopen(path, 'rb'), 'rb', external_encoding: encoding, internal_encoding: 'UTF-8') do |io|
302
+ io.read(2) if Twine::Encoding.has_bom?(path)
303
+ formatter.read(io, lang)
304
+ end
245
305
  end
246
306
 
247
307
  def prepare_read_write(path, lang)