traduco 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.
@@ -0,0 +1,110 @@
1
+ module Traduco
2
+ module Formatters
3
+ class Flash < Abstract
4
+ FORMAT_NAME = 'flash'
5
+ EXTENSION = '.properties'
6
+ DEFAULT_FILE_NAME = 'resources.properties'
7
+
8
+ def self.can_handle_directory?(path)
9
+ return false
10
+ end
11
+
12
+ def default_file_name
13
+ return DEFAULT_FILE_NAME
14
+ end
15
+
16
+ def determine_language_given_path(path)
17
+ return
18
+ end
19
+
20
+ def read_file(path, lang)
21
+ encoding = Traduco::Encoding.encoding_for_path(path)
22
+ sep = nil
23
+ if !encoding.respond_to?(:encode)
24
+ # This code is not necessary in 1.9.3 and does not work as it did in 1.8.7.
25
+ if encoding.end_with? 'LE'
26
+ sep = "\x0a\x00"
27
+ elsif encoding.end_with? 'BE'
28
+ sep = "\x00\x0a"
29
+ else
30
+ sep = "\n"
31
+ end
32
+ end
33
+
34
+ if encoding.index('UTF-16')
35
+ mode = "rb:#{encoding}"
36
+ else
37
+ mode = "r:#{encoding}"
38
+ end
39
+
40
+ File.open(path, mode) do |f|
41
+ last_comment = nil
42
+ while line = (sep) ? f.gets(sep) : f.gets
43
+ if encoding.index('UTF-16')
44
+ if line.respond_to? :encode!
45
+ line.encode!('UTF-8')
46
+ else
47
+ require 'iconv'
48
+ line = Iconv.iconv('UTF-8', encoding, line).join
49
+ end
50
+ end
51
+ match = /((?:[^"\\]|\\.)+)\s*=\s*((?:[^"\\]|\\.)*)/.match(line)
52
+ if match
53
+ key = match[1]
54
+ value = match[2]
55
+ value.gsub!(/\{[0-9]\}/, '%@')
56
+ set_translation_for_key(key, lang, value)
57
+ if last_comment
58
+ set_comment_for_key(key, last_comment)
59
+ end
60
+ end
61
+ if @options[:consume_comments]
62
+ match = /#(.*)/.match(line)
63
+ if match
64
+ last_comment = match[1]
65
+ else
66
+ last_comment = nil
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def write_file(path, lang)
74
+ default_lang = @strings.language_codes[0]
75
+ encoding = @options[:output_encoding] || 'UTF-8'
76
+ File.open(path, "w:#{encoding}") do |f|
77
+ f.puts "## Flash Strings File\n## Generated by Traduco #{Traduco::VERSION}\n## Language: #{lang}\n"
78
+ @strings.sections.each do |section|
79
+ printed_section = false
80
+ section.rows.each do |row|
81
+ if row.matches_tags?(@options[:tags], @options[:untagged])
82
+ f.puts ''
83
+ if !printed_section
84
+ if section.name && section.name.length > 0
85
+ f.print "## #{section.name} ##\n\n"
86
+ end
87
+ printed_section = true
88
+ end
89
+
90
+ key = row.key
91
+ value = row.translated_string_for_lang(lang, default_lang)
92
+ if value
93
+ placeHolderNumber = -1
94
+ value = value.gsub(/%[d@]/) { placeHolderNumber += 1; '{%d}' % placeHolderNumber }
95
+
96
+ comment = row.comment
97
+ if comment && comment.length > 0
98
+ f.print "# #{comment}\n"
99
+ end
100
+
101
+ f.print "#{key}=#{value}"
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,98 @@
1
+ module Traduco
2
+ module Formatters
3
+ class Gettext < Abstract
4
+ FORMAT_NAME = 'gettext'
5
+ EXTENSION = '.po'
6
+ DEFAULT_FILE_NAME = 'strings.po'
7
+
8
+ def self.can_handle_directory?(path)
9
+ Dir.entries(path).any? { |item| /^.+\.po$/.match(item) }
10
+ end
11
+
12
+ def default_file_name
13
+ return DEFAULT_FILE_NAME
14
+ end
15
+
16
+ def determine_language_given_path(path)
17
+ path_arr = path.split(File::SEPARATOR)
18
+ path_arr.each do |segment|
19
+ match = /(..)\.po$/.match(segment)
20
+ if match
21
+ return match[1]
22
+ end
23
+ end
24
+
25
+ return
26
+ end
27
+
28
+ def read_file(path, lang)
29
+ comment_regex = /#.? *"(.*)"$/
30
+ key_regex = /msgctxt *"(.*)"$/
31
+ value_regex = /msgstr *"(.*)"$/m
32
+ File.open(path, 'r:UTF-8') do |f|
33
+ while item = f.gets("\n\n")
34
+ key = nil
35
+ value = nil
36
+ comment = nil
37
+
38
+ comment_match = comment_regex.match(item)
39
+ if comment_match
40
+ comment = comment_match[1]
41
+ end
42
+ key_match = key_regex.match(item)
43
+ if key_match
44
+ key = key_match[1].gsub('\\"', '"')
45
+ end
46
+ value_match = value_regex.match(item)
47
+ if value_match
48
+ value = value_match[1].gsub(/"\n"/, '').gsub('\\"', '"')
49
+ end
50
+ if key and key.length > 0 and value and value.length > 0
51
+ set_translation_for_key(key, lang, value)
52
+ if comment and comment.length > 0
53
+ set_comment_for_key(key, comment)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def write_file(path, lang)
61
+ default_lang = @strings.language_codes[0]
62
+ encoding = @options[:output_encoding] || 'UTF-8'
63
+ File.open(path, "w:#{encoding}") do |f|
64
+ f.puts "msgid \"\"\nmsgstr \"\"\n\"Language: #{lang}\\n\"\n\"X-Generator: Traduco #{Traduco::VERSION}\\n\"\n\n"
65
+ @strings.sections.each do |section|
66
+ printed_section = false
67
+ section.rows.each do |row|
68
+ if row.matches_tags?(@options[:tags], @options[:untagged])
69
+ basetrans = row.translated_string_for_lang(default_lang)
70
+
71
+ if basetrans
72
+ key = row.key
73
+ key = key.gsub('"', '\\\\"')
74
+
75
+ comment = row.comment
76
+ if comment
77
+ comment = comment.gsub('"', '\\\\"')
78
+ end
79
+
80
+ if comment && comment.length > 0
81
+ f.print "#. \"#{comment}\"\n"
82
+ end
83
+
84
+ f.print "msgctxt \"#{key}\"\nmsgid \"#{basetrans}\"\n"
85
+ value = row.translated_string_for_lang(lang)
86
+ if value
87
+ value = value.gsub('"', '\\\\"')
88
+ end
89
+ f.print "msgstr \"#{value}\"\n\n"
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,96 @@
1
+ module Traduco
2
+ module Formatters
3
+ class JQuery < Abstract
4
+ FORMAT_NAME = 'jquery'
5
+ EXTENSION = '.json'
6
+ DEFAULT_FILE_NAME = 'localize.json'
7
+
8
+ def self.can_handle_directory?(path)
9
+ Dir.entries(path).any? { |item| /^.+\.json$/.match(item) }
10
+ end
11
+
12
+ def default_file_name
13
+ return DEFAULT_FILE_NAME
14
+ end
15
+
16
+ def determine_language_given_path(path)
17
+ path_arr = path.split(File::SEPARATOR)
18
+ path_arr.each do |segment|
19
+ match = /^((.+)-)?([^-]+)\.json$/.match(segment)
20
+ if match
21
+ return match[3]
22
+ end
23
+ end
24
+
25
+ return
26
+ end
27
+
28
+ def read_file(path, lang)
29
+ begin
30
+ require "json"
31
+ rescue LoadError
32
+ raise Traduco::Error.new "You must run 'gem install json' in order to read or write jquery-localize files."
33
+ end
34
+
35
+ open(path) do |io|
36
+ json = JSON.load(io)
37
+ json.each do |key, value|
38
+ set_translation_for_key(key, lang, value)
39
+ end
40
+ end
41
+ end
42
+
43
+ def write_file(path, lang)
44
+ begin
45
+ require "json"
46
+ rescue LoadError
47
+ raise Traduco::Error.new "You must run 'gem install json' in order to read or write jquery-localize files."
48
+ end
49
+
50
+ default_lang = @strings.language_codes[0]
51
+ encoding = @options[:output_encoding] || 'UTF-8'
52
+ File.open(path, "w:#{encoding}") do |f|
53
+ f.puts "/**\n * JQuery Language File\n * Generated by Traduco\n * Language: #{lang}\n */"
54
+ f.puts "{"
55
+
56
+ @strings.sections.each_with_index do |section, si|
57
+ printed_section = false
58
+ section.rows.each_with_index do |row, ri|
59
+ if row.matches_tags?(@options[:tags], @options[:untagged])
60
+ if !printed_section
61
+ f.puts ''
62
+ if section.name && section.name.length > 0
63
+ f.puts "/* #{section.name} */"
64
+ end
65
+ printed_section = true
66
+ end
67
+
68
+ key = row.key
69
+ key = key.gsub('"', '\\\\"')
70
+
71
+ value = row.translated_string_for_lang(lang, default_lang)
72
+ value = value.gsub('"', '\\\\"')
73
+
74
+ comment = row.comment
75
+ if comment
76
+ comment = comment.gsub('*/', '* /')
77
+ end
78
+
79
+ f.print "\"#{key}\":\"#{value}\","
80
+
81
+ if comment && comment.length > 0
82
+ f.print " /* #{comment} */\n"
83
+ else
84
+ f.print "\n"
85
+ end
86
+ end
87
+ end
88
+ end
89
+ f.seek(-2, IO::SEEK_CUR)
90
+ f.puts "\n}"
91
+
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,209 @@
1
+ module Traduco
2
+ class StringsSection
3
+ attr_reader :name
4
+ attr_reader :rows
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ @rows = []
9
+ end
10
+ end
11
+
12
+ class StringsRow
13
+ attr_reader :key
14
+ attr_accessor :comment
15
+ attr_accessor :tags
16
+ attr_reader :translations
17
+
18
+ def initialize(key)
19
+ @key = key
20
+ @comment = nil
21
+ @tags = nil
22
+ @translations = {}
23
+ end
24
+
25
+ def matches_tags?(tags, include_untagged)
26
+ if tags == nil || tags.length == 0
27
+ # The user did not specify any tags. Everything passes.
28
+ return true
29
+ elsif @tags == nil || @tags.length == 0
30
+ # This row has no tags.
31
+ return (include_untagged) ? true : false
32
+ else
33
+ tags.each do |tag|
34
+ if @tags.include? tag
35
+ return true
36
+ end
37
+ end
38
+ end
39
+
40
+ return false
41
+ end
42
+
43
+ def translated_string_for_lang(lang, default_lang=nil)
44
+ if @translations[lang]
45
+ return @translations[lang]
46
+ elsif default_lang.respond_to?("each")
47
+ default_lang.each do |def_lang|
48
+ if @translations[def_lang]
49
+ return @translations[def_lang]
50
+ end
51
+ end
52
+ return nil
53
+ else
54
+ return @translations[default_lang]
55
+ end
56
+ end
57
+ end
58
+
59
+ class StringsFile
60
+ attr_reader :sections
61
+ attr_reader :strings_map
62
+ attr_reader :language_codes
63
+
64
+ def initialize
65
+ @sections = []
66
+ @strings_map = {}
67
+ @language_codes = []
68
+ end
69
+
70
+ def read(path)
71
+ if !File.file?(path)
72
+ raise Traduco::Error.new("File does not exist: #{path}")
73
+ end
74
+
75
+ File.open(path, 'r:UTF-8') do |f|
76
+ line_num = 0
77
+ current_section = nil
78
+ current_row = nil
79
+ while line = f.gets
80
+ parsed = false
81
+ line.strip!
82
+ line_num += 1
83
+
84
+ if line.length == 0
85
+ next
86
+ end
87
+
88
+ # comment
89
+ if line[0, 1] == '#'
90
+ next
91
+ end
92
+
93
+ # section
94
+ if line.length > 4 && line[0, 2] == '[['
95
+ match = /^\[\[(.+)\]\]$/.match(line)
96
+ if match
97
+ current_section = StringsSection.new(match[1])
98
+ @sections << current_section
99
+ parsed = true
100
+ end
101
+ # string
102
+ elsif line.length > 2 && line[0, 1] == '['
103
+ match = /^\[(.+)\]$/.match(line)
104
+ if match
105
+ current_row = StringsRow.new(match[1])
106
+ @strings_map[current_row.key] = current_row
107
+ if !current_section
108
+ current_section = StringsSection.new('')
109
+ @sections << current_section
110
+ end
111
+ current_section.rows << current_row
112
+ parsed = true
113
+ end
114
+ # translation, comment or other
115
+ else
116
+ match = /^([^=]+)=(.*)$/.match(line)
117
+ if match
118
+ key = match[1].strip
119
+ value = match[2].strip
120
+ if value[0,1] == '`' && value[-1,1] == '`'
121
+ value = value[1..-2]
122
+ end
123
+
124
+ case key
125
+ when "comment"
126
+ current_row.comment = value
127
+ when 'tags'
128
+ current_row.tags = value.split(',')
129
+ else
130
+ if !@language_codes.include? key
131
+ add_language_code(key)
132
+ end
133
+ current_row.translations[key] = value
134
+ end
135
+ parsed = true
136
+ end
137
+ end
138
+
139
+ if !parsed
140
+ raise Traduco::Error.new("Unable to parse line #{line_num} of #{path}: #{line}")
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ def write(path)
147
+ dev_lang = @language_codes[0]
148
+
149
+ File.open(path, 'w:UTF-8') do |f|
150
+ @sections.each do |section|
151
+ if f.pos > 0
152
+ f.puts ''
153
+ end
154
+
155
+ f.puts "[[#{section.name}]]"
156
+
157
+ section.rows.each do |row|
158
+ f.puts "\t[#{row.key}]"
159
+ value = row.translations[dev_lang]
160
+ if !value
161
+ puts "Warning: #{row.key} does not exist in developer language '#{dev_lang}'"
162
+ else
163
+ if value[0,1] == ' ' || value[-1,1] == ' ' || (value[0,1] == '`' && value[-1,1] == '`')
164
+ value = '`' + value + '`'
165
+ end
166
+ f.puts "\t\t#{dev_lang} = #{value}"
167
+ end
168
+
169
+ if row.tags && row.tags.length > 0
170
+ tag_str = row.tags.join(',')
171
+ f.puts "\t\ttags = #{tag_str}"
172
+ end
173
+ if row.comment && row.comment.length > 0
174
+ f.puts "\t\tcomment = #{row.comment}"
175
+ end
176
+ @language_codes[1..-1].each do |lang|
177
+ value = row.translations[lang]
178
+ if value
179
+ if value[0,1] == ' ' || value[-1,1] == ' ' || (value[0,1] == '`' && value[-1,1] == '`')
180
+ value = '`' + value + '`'
181
+ end
182
+ f.puts "\t\t#{lang} = #{value}"
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ def add_language_code(code)
191
+ if @language_codes.length == 0
192
+ @language_codes << code
193
+ elsif !@language_codes.include?(code)
194
+ dev_lang = @language_codes[0]
195
+ @language_codes << code
196
+ @language_codes.delete(dev_lang)
197
+ @language_codes.sort!
198
+ @language_codes.insert(0, dev_lang)
199
+ end
200
+ end
201
+
202
+ def set_developer_language_code(code)
203
+ if @language_codes.include?(code)
204
+ @language_codes.delete(code)
205
+ end
206
+ @language_codes.insert(0, code)
207
+ end
208
+ end
209
+ end