traduco 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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