translatomatic 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.translatomatic/config.yml +18 -0
  3. data/.travis.yml +33 -33
  4. data/Gemfile +6 -4
  5. data/README.de.md +53 -18
  6. data/README.es.md +55 -20
  7. data/README.fr.md +54 -19
  8. data/README.it.md +58 -23
  9. data/README.ja.md +54 -19
  10. data/README.ko.md +58 -23
  11. data/README.md +167 -141
  12. data/README.ms.md +51 -16
  13. data/README.pt.md +58 -23
  14. data/README.ru.md +53 -18
  15. data/README.sv.md +53 -18
  16. data/README.zh.md +53 -18
  17. data/bin/translatomatic +6 -6
  18. data/bin/travis +24 -26
  19. data/config/locales/translatomatic/de.yml +22 -11
  20. data/config/locales/translatomatic/en.yml +21 -12
  21. data/config/locales/translatomatic/es.yml +22 -11
  22. data/config/locales/translatomatic/fr.yml +22 -12
  23. data/config/locales/translatomatic/it.yml +22 -11
  24. data/config/locales/translatomatic/ja.yml +22 -11
  25. data/config/locales/translatomatic/ko.yml +22 -11
  26. data/config/locales/translatomatic/ms.yml +22 -11
  27. data/config/locales/translatomatic/pt.yml +22 -11
  28. data/config/locales/translatomatic/ru.yml +22 -11
  29. data/config/locales/translatomatic/sv.yml +22 -11
  30. data/config/locales/translatomatic/zh.yml +22 -11
  31. data/db/migrate/201712170000_initial.rb +25 -25
  32. data/lib/translatomatic/cli/base.rb +81 -73
  33. data/lib/translatomatic/cli/config.rb +110 -81
  34. data/lib/translatomatic/cli/main.rb +85 -72
  35. data/lib/translatomatic/cli/translate.rb +141 -106
  36. data/lib/translatomatic/cli.rb +8 -8
  37. data/lib/translatomatic/config.rb +302 -155
  38. data/lib/translatomatic/converter.rb +28 -260
  39. data/lib/translatomatic/database.rb +134 -134
  40. data/lib/translatomatic/define_options.rb +22 -0
  41. data/lib/translatomatic/escaped_unicode.rb +0 -0
  42. data/lib/translatomatic/extractor/base.rb +16 -16
  43. data/lib/translatomatic/extractor/ruby.rb +6 -6
  44. data/lib/translatomatic/extractor.rb +5 -5
  45. data/lib/translatomatic/file_translator.rb +269 -0
  46. data/lib/translatomatic/http_request.rb +162 -162
  47. data/lib/translatomatic/locale.rb +76 -76
  48. data/lib/translatomatic/logger.rb +23 -23
  49. data/lib/translatomatic/model/locale.rb +25 -25
  50. data/lib/translatomatic/model/text.rb +19 -19
  51. data/lib/translatomatic/model.rb +1 -1
  52. data/lib/translatomatic/option.rb +37 -41
  53. data/lib/translatomatic/progress_updater.rb +13 -13
  54. data/lib/translatomatic/resource_file/base.rb +269 -192
  55. data/lib/translatomatic/resource_file/csv.rb +37 -0
  56. data/lib/translatomatic/resource_file/html.rb +54 -47
  57. data/lib/translatomatic/resource_file/markdown.rb +50 -55
  58. data/lib/translatomatic/resource_file/plist.rb +153 -19
  59. data/lib/translatomatic/resource_file/po.rb +107 -0
  60. data/lib/translatomatic/resource_file/properties.rb +91 -90
  61. data/lib/translatomatic/resource_file/resw.rb +50 -30
  62. data/lib/translatomatic/resource_file/subtitle.rb +75 -0
  63. data/lib/translatomatic/resource_file/text.rb +24 -30
  64. data/lib/translatomatic/resource_file/xcode_strings.rb +75 -80
  65. data/lib/translatomatic/resource_file/xml.rb +98 -91
  66. data/lib/translatomatic/resource_file/yaml.rb +94 -116
  67. data/lib/translatomatic/resource_file.rb +87 -78
  68. data/lib/translatomatic/string.rb +188 -188
  69. data/lib/translatomatic/tmx/document.rb +99 -99
  70. data/lib/translatomatic/translation_result.rb +63 -63
  71. data/lib/translatomatic/{converter_stats.rb → translation_stats.rb} +17 -17
  72. data/lib/translatomatic/translator/base.rb +1 -1
  73. data/lib/translatomatic/translator/google.rb +2 -0
  74. data/lib/translatomatic/translator.rb +10 -2
  75. data/lib/translatomatic/util.rb +45 -45
  76. data/lib/translatomatic/version.rb +7 -7
  77. data/lib/translatomatic.rb +52 -49
  78. data/translatomatic.gemspec +3 -2
  79. metadata +25 -5
@@ -1,192 +1,269 @@
1
- # Base class for resource file implementations
2
- # @abstract Subclasses implement different types of resource files
3
- class Translatomatic::ResourceFile::Base
4
-
5
- attr_accessor :locale
6
- attr_accessor :path
7
-
8
- # @return [Hash<String,String>] key -> value properties
9
- attr_reader :properties
10
-
11
- # @return [Array<String>] File extensions supported by this resource file
12
- def self.extensions
13
- raise "extensions must be implemented by subclass"
14
- end
15
-
16
- # Create a new resource file.
17
- # If locale is unspecified, attempts to determine the locale of the file
18
- # automatically, and if that fails, uses the default locale.
19
- # @param path [String] Path to the file
20
- # @param locale [String] Locale of the file contents
21
- # @return [Translatomatic::ResourceFile::Base] the resource file.
22
- def initialize(path, locale = nil)
23
- @path = path.kind_of?(Pathname) ? path : Pathname.new(path)
24
- @locale = Translatomatic::Locale.parse(locale || detect_locale || Translatomatic::Locale.default)
25
- raise t("resource.unknown_locale") unless @locale && @locale.language
26
- @valid = false
27
- @properties = {}
28
- end
29
-
30
- # @return [String] The format of this resource file, e.g. "Properties"
31
- def format
32
- self.class.name.demodulize.downcase.to_sym
33
- end
34
-
35
- # Create a path for the current resource file with a given locale
36
- # @param locale [String] The target locale
37
- # @return [Pathname] The path of this resource file modified for the given locale
38
- def locale_path(locale)
39
- basename = path.sub_ext('').basename.to_s
40
-
41
- extlist = extension_list
42
- if extlist.length >= 2 && loc_idx = find_locale(extlist)
43
- # extension(s) contains locale, replace it
44
- extlist[loc_idx] = locale.to_s
45
- elsif valid_locale?(basename)
46
- # basename is a locale name, replace it
47
- path.dirname + (locale.to_s + path.extname)
48
- else
49
- # remove any underscore and trailing text from basename
50
- deunderscored = basename.sub(/_.*?$/, '')
51
- # add _locale.ext
52
- filename = deunderscored + "_" + locale.to_s + path.extname
53
- path.dirname + filename
54
- end
55
- end
56
-
57
- # Set all properties
58
- # @param properties [Hash<String,String>] New properties
59
- def properties=(properties)
60
- # use set rather that set @properties directly as subclasses override set()
61
- properties.each do |key, value|
62
- set(key, value)
63
- end
64
- end
65
-
66
- # Get the value of a property
67
- # @param key [String] The name of the property
68
- # @return [String] The value of the property
69
- def get(key)
70
- @properties[key]
71
- end
72
-
73
- # Set a property
74
- # @param key [String] The name of the property
75
- # @param value [String] The new value of the property
76
- # @return [String] The new value of the property
77
- def set(key, value)
78
- @properties[key] = value
79
- end
80
-
81
- # Test if the current resource file is valid
82
- # @return true if the current file is valid
83
- def valid?
84
- @valid
85
- end
86
-
87
- # Save the resource file.
88
- # @param target [Pathname] The destination path
89
- # @param options [Hash<Symbol, Object>] Output format options
90
- # @return [void]
91
- def save(target = path, options = {})
92
- raise "save(path) must be implemented by subclass"
93
- end
94
-
95
- # @return [String] String representation of this file
96
- def to_s
97
- path.basename.to_s
98
- end
99
-
100
- # @return [Array<String>] All property values split into sentences
101
- def sentences
102
- sentences = []
103
- properties.values.each do |value|
104
- string = Translatomatic::String.new(value, locale)
105
- sentences += string.sentences
106
- end
107
- sentences
108
- end
109
-
110
- # @return [boolean] true if this resource file supports variable interpolation
111
- def supports_variable_interpolation?
112
- false
113
- end
114
-
115
- # Create an interpolated variable string.
116
- # @return [String] A string representing the interpolated variable, or
117
- # nil if this resource file doesn't support variable interpolation.
118
- def create_variable(name)
119
- return nil unless supports_variable_interpolation?
120
- raise "create_variable(name) must be implemented by subclass"
121
- end
122
-
123
- # @return [Regexp] A regexp used to match interpolated variables
124
- def variable_regex
125
- return nil unless supports_variable_interpolation?
126
- raise "variable_regex must be implemented by subclass"
127
- end
128
-
129
- private
130
-
131
- include Translatomatic::Util
132
-
133
- def created_by
134
- t("resource.created_by", app: "Translatomatic",
135
- version: Translatomatic::VERSION, date: I18n.l(DateTime.now),
136
- locale: locale.language
137
- )
138
- end
139
-
140
- # detect locale from filename
141
- def detect_locale
142
- tag = nil
143
- basename = path.sub_ext('').basename.to_s
144
- directory = path.dirname.basename.to_s
145
- extlist = extension_list
146
-
147
- if basename.match(/_([-\w]{2,})$/i)
148
- # locale after underscore in filename
149
- tag = $1
150
- elsif directory.match(/^([-\w]+)\.lproj$/)
151
- # xcode localized strings
152
- tag = $1
153
- elsif extlist.length >= 2 && loc_idx = find_locale(extlist)
154
- # multiple parts to extension, e.g. index.html.en
155
- tag = extlist[loc_idx]
156
- elsif valid_locale?(basename)
157
- # try to match on entire basename
158
- # (support for rails en.yml)
159
- tag = basename
160
- elsif valid_locale?(path.parent.basename)
161
- # try to match on parent directory, e.g. strings/en-US/text.resw
162
- tag = path.parent.basename
163
- end
164
-
165
- tag ? Translatomatic::Locale.parse(tag, true) : nil
166
- end
167
-
168
- def valid_locale?(tag)
169
- Translatomatic::Locale.new(tag).valid?
170
- end
171
-
172
- # test if the list of strings contains a valid locale
173
- # return the index to the locale, or nil if no locales found
174
- def find_locale(list)
175
- list.find_index { |i| valid_locale?(i) }
176
- end
177
-
178
- # ext_sub() only removes the last extension
179
- def strip_extensions
180
- filename = path.basename.to_s
181
- filename.sub!(/\..*$/, '')
182
- path.parent + filename
183
- end
184
-
185
- # for index.html.de, returns ['html', 'de']
186
- def extension_list
187
- filename = path.basename.to_s
188
- idx = filename.index('.')
189
- idx && idx < filename.length - 1 ? filename[idx + 1..-1].split('.') : []
190
- end
191
-
192
- end
1
+ # Base class for resource file implementations
2
+ # @abstract Subclasses implement different types of resource files
3
+ class Translatomatic::ResourceFile::Base
4
+
5
+ attr_accessor :locale
6
+ attr_accessor :path
7
+
8
+ # @return [Hash<String,String>] key -> value properties
9
+ attr_reader :properties
10
+
11
+ # @return [boolean] True if this resource format is enabled
12
+ def self.enabled?
13
+ true
14
+ end
15
+
16
+ # @return [Array<String>] File extensions supported by this resource file
17
+ def self.extensions
18
+ raise "extensions must be implemented by subclass"
19
+ end
20
+
21
+ # @return [boolean] True if the file format consists of keys and values
22
+ def self.is_key_value?
23
+ false
24
+ end
25
+
26
+ # @return [boolean] true if this resource file supports variable interpolation
27
+ def self.supports_variable_interpolation?
28
+ false
29
+ end
30
+
31
+ # Create a new resource file or load an existing file.
32
+ # If options[:locale] is unspecified, attempts to determine
33
+ # the locale of the file automatically, and if that fails,
34
+ # uses the default locale.
35
+ # Raises an exception if errors were encountered loading the file.
36
+ # @param path [String] Path to the file
37
+ # @param options [Hash<Symbol,String>] Options
38
+ # @return [Translatomatic::ResourceFile::Base] the resource file.
39
+ def initialize(path = nil, options = {})
40
+ raise "expected options hash" if options && !options.kind_of?(Hash)
41
+ raise t("file.unsupported", file: path) unless self.class.enabled?
42
+ @options = options || {}
43
+ @properties = {}
44
+ @path = path.nil? || path.kind_of?(Pathname) ? path : Pathname.new(path)
45
+ update_locale
46
+ init
47
+ load if @path && @path.exist?
48
+ end
49
+
50
+ # Save the resource file.
51
+ # @param target [Pathname] The destination path
52
+ # @param options [Hash<Symbol, Object>] Output format options
53
+ # @return [void]
54
+ def save(target = path, options = {})
55
+ raise "save(path) must be implemented by subclass"
56
+ end
57
+
58
+ # @return [String] The format of this resource file, e.g. "Properties"
59
+ def format
60
+ self.class.name.demodulize.downcase.to_sym
61
+ end
62
+
63
+ # Create a path for the current resource file with a given locale
64
+ # @param locale [String] The target locale
65
+ # @return [Pathname] The path of this resource file modified for the given locale
66
+ def locale_path(locale)
67
+ basename = path.sub_ext('').basename.to_s
68
+
69
+ extlist = extension_list
70
+ if extlist.length >= 2 && loc_idx = find_locale(extlist)
71
+ # extension(s) contains locale, replace it
72
+ extlist[loc_idx] = locale.to_s
73
+ elsif valid_locale?(basename)
74
+ # basename is a locale name, replace it
75
+ path.dirname + (locale.to_s + path.extname)
76
+ else
77
+ # remove any underscore and trailing text from basename
78
+ deunderscored = basename.sub(/_.*?$/, '')
79
+ # add _locale.ext
80
+ filename = deunderscored + "_" + locale.to_s + path.extname
81
+ path.dirname + filename
82
+ end
83
+ end
84
+
85
+ # Set all properties
86
+ # @param properties [Hash<String,String>] New properties
87
+ def properties=(properties)
88
+ # use set rather that set @properties directly as subclasses override set()
89
+ properties.each do |key, value|
90
+ set(key, value)
91
+ end
92
+ end
93
+
94
+ # Get the value of a property
95
+ # @param key [String] The name of the property
96
+ # @return [String] The value of the property
97
+ def get(key)
98
+ @properties[key]
99
+ end
100
+
101
+ # Set a property
102
+ # @param key [String] The name of the property
103
+ # @param value [String] The new value of the property
104
+ # @return [String] The new value of the property
105
+ def set(key, value)
106
+ @properties[key] = value
107
+ end
108
+
109
+ # @return [String] String representation of this file
110
+ def to_s
111
+ path.basename.to_s
112
+ end
113
+
114
+ def type
115
+ self.class.name.demodulize
116
+ end
117
+
118
+ # @return [Array<String>] All property values split into sentences
119
+ def sentences
120
+ sentences = []
121
+ properties.values.each do |value|
122
+ string = Translatomatic::String.new(value, locale)
123
+ sentences += string.sentences
124
+ end
125
+ sentences
126
+ end
127
+
128
+ # Create an interpolated variable string.
129
+ # @return [String] A string representing the interpolated variable, or
130
+ # nil if this resource file doesn't support variable interpolation.
131
+ def create_variable(name)
132
+ return nil unless supports_variable_interpolation?
133
+ raise "create_variable(name) must be implemented by subclass"
134
+ end
135
+
136
+ # @return [Regexp] A regexp used to match interpolated variables
137
+ def variable_regex
138
+ return nil unless supports_variable_interpolation?
139
+ raise "variable_regex must be implemented by subclass"
140
+ end
141
+
142
+ private
143
+
144
+ include Translatomatic::Util
145
+
146
+ # called by constructor before load
147
+ def init
148
+ end
149
+
150
+ # load contents from @path
151
+ def load
152
+ raise "load must be implemented by subclass"
153
+ end
154
+
155
+ def update_locale
156
+ locale = @options[:locale] || detect_locale || Translatomatic::Locale.default
157
+ @locale = Translatomatic::Locale.parse(locale)
158
+ raise t("file.unknown_locale") unless @locale && @locale.language
159
+ end
160
+
161
+ def created_by
162
+ options = {
163
+ app: "Translatomatic",
164
+ version: Translatomatic::VERSION,
165
+ date: I18n.l(DateTime.now),
166
+ }
167
+ # use created by string in current file's locale, fall back to
168
+ # english locale if translation is missing.
169
+ t("file.created_by", options.merge({
170
+ locale: locale.language,
171
+ default: t("file.created_by", options.merge(locale: "en"))
172
+ })
173
+ )
174
+ end
175
+
176
+ def read_contents(path)
177
+ File.read(path.to_s, mode: "r:bom|utf-8")
178
+ end
179
+
180
+ def parsing_error(error)
181
+ raise Exception.new(error)
182
+ end
183
+
184
+ # detect locale from filename
185
+ def detect_locale
186
+ return nil unless path
187
+ tag = nil
188
+ basename = path.sub_ext('').basename.to_s
189
+ directory = path.dirname.basename.to_s
190
+ extlist = extension_list
191
+
192
+ if basename.match(/_([-\w]{2,})$/i)
193
+ # locale after underscore in filename
194
+ tag = $1
195
+ elsif directory.match(/^([-\w]+)\.lproj$/)
196
+ # xcode localized strings
197
+ tag = $1
198
+ elsif extlist.length >= 2 && loc_idx = find_locale(extlist)
199
+ # multiple parts to extension, e.g. index.html.en
200
+ tag = extlist[loc_idx]
201
+ elsif valid_locale?(basename)
202
+ # try to match on entire basename
203
+ # (support for rails en.yml)
204
+ tag = basename
205
+ elsif valid_locale?(path.parent.basename)
206
+ # try to match on parent directory, e.g. strings/en-US/text.resw
207
+ tag = path.parent.basename
208
+ end
209
+
210
+ tag ? Translatomatic::Locale.parse(tag, true) : nil
211
+ end
212
+
213
+ def valid_locale?(tag)
214
+ Translatomatic::Locale.new(tag).valid?
215
+ end
216
+
217
+ # test if the list of strings contains a valid locale
218
+ # return the index to the locale, or nil if no locales found
219
+ def find_locale(list)
220
+ list.find_index { |i| valid_locale?(i) }
221
+ end
222
+
223
+ # ext_sub() only removes the last extension
224
+ def strip_extensions
225
+ filename = path.basename.to_s
226
+ filename.sub!(/\..*$/, '')
227
+ path.parent + filename
228
+ end
229
+
230
+ # for index.html.de, returns ['html', 'de']
231
+ def extension_list
232
+ filename = path.basename.to_s
233
+ idx = filename.index('.')
234
+ idx && idx < filename.length - 1 ? filename[idx + 1..-1].split('.') : []
235
+ end
236
+
237
+ # flatten hash or array of hashes to a hash of key => value pairs
238
+ def flatten(data)
239
+ result = {}
240
+
241
+ if data.kind_of?(Hash)
242
+ data.each do |key, value|
243
+ flatten_add(result, key, value)
244
+ end
245
+ elsif data.kind_of?(Array)
246
+ data.each_with_index do |value, i|
247
+ key = "key" + i.to_s
248
+ flatten_add(result, key, value)
249
+ end
250
+ end
251
+
252
+ result
253
+ end
254
+
255
+ def flatten_add(result, key, value)
256
+ if needs_flatten?(value)
257
+ children = flatten(value)
258
+ children.each do |ck, cv|
259
+ result[key + "." + ck] = cv
260
+ end
261
+ else
262
+ result[key] = value
263
+ end
264
+ end
265
+
266
+ def needs_flatten?(value)
267
+ value.kind_of?(Array) || value.kind_of?(Hash)
268
+ end
269
+ end
@@ -0,0 +1,37 @@
1
+ require 'csv'
2
+
3
+ module Translatomatic::ResourceFile
4
+ # CSV resource file
5
+ class CSV < Base
6
+
7
+ # (see Translatomatic::ResourceFile::Base.extensions)
8
+ def self.extensions
9
+ %w{csv}
10
+ end
11
+
12
+ # (see Translatomatic::ResourceFile::Base#save)
13
+ def save(target = path, options = {})
14
+ ::CSV.open(target, "wb") do |csv|
15
+ @properties.each do |key, value|
16
+ csv << [key, value]
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def init
24
+ @rows = []
25
+ end
26
+
27
+ def load
28
+ contents = read_contents(@path)
29
+ @rows = ::CSV.parse(contents)
30
+ @properties = {}
31
+ @rows.each do |key, value|
32
+ @properties[key] = value
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -1,48 +1,55 @@
1
- module Translatomatic::ResourceFile
1
+ module Translatomatic::ResourceFile
2
2
  # HTML resource file
3
- class HTML < XML
4
-
5
- # (see Translatomatic::ResourceFile::Base.extensions)
6
- def self.extensions
7
- %w{html htm shtml}
8
- end
9
-
10
- # (see Translatomatic::ResourceFile::Base#locale_path)
11
- def locale_path(locale)
12
- extlist = extension_list
13
- if extlist.length >= 2 && loc_idx = find_locale(extlist)
14
- # part of the extension is the locale
15
- # replace that part with the new locale
16
- extlist[loc_idx] = locale.to_s
17
- new_extension = extlist.join(".")
18
- return strip_extensions.sub_ext("." + new_extension)
19
- else
20
- # add locale extension
21
- ext = path.extname
22
- # TODO: need configurable order for locale & ext here?
23
- #path.sub_ext("#{ext}." + locale.to_s)
24
- path.sub_ext("." + locale.to_s + ext)
25
- end
26
- end
27
-
28
- # (see Translatomatic::ResourceFile::Base#save)
29
- def save(target = path, options = {})
30
- if @doc
31
- add_created_by unless options[:no_created_by]
32
- target.write(@doc.to_html)
33
- end
34
- end
35
-
36
- private
37
-
38
- def text_nodes_xpath
39
- '//*[not(self::code)]/text()'
40
- end
41
-
42
- def read_doc(path)
43
- Nokogiri::HTML(path.open) do |config|
44
- config.noblanks
45
- end
46
- end
47
- end
48
- end
3
+ class HTML < XML
4
+
5
+ # (see Translatomatic::ResourceFile::Base.extensions)
6
+ def self.extensions
7
+ %w{html htm shtml}
8
+ end
9
+
10
+ # (see Translatomatic::ResourceFile::Base#locale_path)
11
+ def locale_path(locale)
12
+ extlist = extension_list
13
+ if extlist.length >= 2 && loc_idx = find_locale(extlist)
14
+ # part of the extension is the locale
15
+ # replace that part with the new locale
16
+ extlist[loc_idx] = locale.to_s
17
+ new_extension = extlist.join(".")
18
+ return strip_extensions.sub_ext("." + new_extension)
19
+ else
20
+ # add locale extension
21
+ ext = path.extname
22
+ # TODO: need configurable order for locale & ext here?
23
+ #path.sub_ext("#{ext}." + locale.to_s)
24
+ path.sub_ext("." + locale.to_s + ext)
25
+ end
26
+ end
27
+
28
+ # (see Translatomatic::ResourceFile::Base#save)
29
+ def save(target = path, options = {})
30
+ if @doc
31
+ add_created_by unless options[:no_created_by]
32
+ target.write(@doc.to_html)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def read_doc
39
+ doc = Nokogiri::HTML(@path.open) do |config|
40
+ config.noblanks
41
+ end
42
+ parse_error(doc.errors[0]) if doc.errors.present?
43
+ doc
44
+ end
45
+
46
+ def empty_doc
47
+ Nokogiri::HTML("<html><body></body></html>")
48
+ end
49
+
50
+ def text_nodes_xpath
51
+ '//*[not(self::code)]/text()'
52
+ end
53
+
54
+ end
55
+ end