translatomatic 0.1.3 → 0.2.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.
Files changed (139) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +20 -20
  3. data/.gitignore +19 -15
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +28 -0
  6. data/.translatomatic/config.yml +4 -0
  7. data/.travis.yml +4 -6
  8. data/.yardopts +9 -9
  9. data/Gemfile +8 -4
  10. data/Guardfile +4 -5
  11. data/README.de.md +55 -50
  12. data/README.en.md +177 -0
  13. data/README.es.md +53 -48
  14. data/README.fr.md +53 -48
  15. data/README.it.md +54 -49
  16. data/README.ja.md +63 -58
  17. data/README.ko.md +59 -54
  18. data/README.md +17 -13
  19. data/README.ms.md +50 -45
  20. data/README.pt.md +54 -49
  21. data/README.ru.md +57 -52
  22. data/README.sv.md +51 -46
  23. data/README.zh.md +60 -55
  24. data/Rakefile +3 -3
  25. data/TODO.txt +6 -0
  26. data/bin/console +3 -3
  27. data/bin/translatomatic +4 -2
  28. data/config/i18n-tasks.yml +130 -0
  29. data/config/locales/translatomatic/de.yml +141 -99
  30. data/config/locales/translatomatic/en.yml +129 -89
  31. data/config/locales/translatomatic/es.yml +136 -99
  32. data/config/locales/translatomatic/fr.yml +139 -100
  33. data/config/locales/translatomatic/it.yml +135 -97
  34. data/config/locales/translatomatic/ja.yml +137 -98
  35. data/config/locales/translatomatic/ko.yml +138 -98
  36. data/config/locales/translatomatic/ms.yml +138 -100
  37. data/config/locales/translatomatic/pt.yml +137 -101
  38. data/config/locales/translatomatic/ru.yml +136 -98
  39. data/config/locales/translatomatic/sv.yml +134 -96
  40. data/config/locales/translatomatic/zh.yml +136 -97
  41. data/db/migrate/201712170000_initial.rb +2 -3
  42. data/lib/translatomatic.rb +40 -25
  43. data/lib/translatomatic/cli.rb +5 -1
  44. data/lib/translatomatic/cli/base.rb +61 -58
  45. data/lib/translatomatic/cli/common_options.rb +14 -11
  46. data/lib/translatomatic/cli/config.rb +96 -91
  47. data/lib/translatomatic/cli/database.rb +85 -23
  48. data/lib/translatomatic/cli/main.rb +158 -104
  49. data/lib/translatomatic/cli/thor.rb +29 -0
  50. data/lib/translatomatic/cli/translate.rb +134 -157
  51. data/lib/translatomatic/config.rb +10 -301
  52. data/lib/translatomatic/config/display.rb +78 -0
  53. data/lib/translatomatic/config/files.rb +60 -0
  54. data/lib/translatomatic/config/location_settings.rb +133 -0
  55. data/lib/translatomatic/config/options.rb +68 -0
  56. data/lib/translatomatic/config/selector.rb +127 -0
  57. data/lib/translatomatic/config/settings.rb +148 -0
  58. data/lib/translatomatic/converter.rb +40 -28
  59. data/lib/translatomatic/database.rb +127 -110
  60. data/lib/translatomatic/define_options.rb +4 -5
  61. data/lib/translatomatic/escaped_unicode.rb +86 -76
  62. data/lib/translatomatic/extractor.rb +5 -2
  63. data/lib/translatomatic/extractor/base.rb +12 -12
  64. data/lib/translatomatic/extractor/ruby.rb +7 -6
  65. data/lib/translatomatic/file_translator.rb +101 -244
  66. data/lib/translatomatic/flattenation.rb +39 -0
  67. data/lib/translatomatic/http.rb +13 -0
  68. data/lib/translatomatic/http/client.rb +144 -0
  69. data/lib/translatomatic/http/exception.rb +43 -0
  70. data/lib/translatomatic/http/file_param.rb +27 -0
  71. data/lib/translatomatic/http/param.rb +37 -0
  72. data/lib/translatomatic/http/request.rb +91 -0
  73. data/lib/translatomatic/i18n.rb +43 -0
  74. data/lib/translatomatic/locale.rb +71 -59
  75. data/lib/translatomatic/logger.rb +43 -28
  76. data/lib/translatomatic/metadata.rb +58 -0
  77. data/lib/translatomatic/model.rb +4 -2
  78. data/lib/translatomatic/model/locale.rb +5 -5
  79. data/lib/translatomatic/model/text.rb +5 -5
  80. data/lib/translatomatic/option.rb +57 -34
  81. data/lib/translatomatic/path_utils.rb +126 -0
  82. data/lib/translatomatic/progress_updater.rb +13 -16
  83. data/lib/translatomatic/provider.rb +101 -0
  84. data/lib/translatomatic/provider/base.rb +136 -0
  85. data/lib/translatomatic/provider/frengly.rb +55 -0
  86. data/lib/translatomatic/provider/google.rb +78 -0
  87. data/lib/translatomatic/provider/google_web.rb +50 -0
  88. data/lib/translatomatic/provider/microsoft.rb +144 -0
  89. data/lib/translatomatic/provider/my_memory.rb +75 -0
  90. data/lib/translatomatic/provider/yandex.rb +61 -0
  91. data/lib/translatomatic/resource_file.rb +59 -53
  92. data/lib/translatomatic/resource_file/base.rb +171 -237
  93. data/lib/translatomatic/resource_file/csv.rb +176 -24
  94. data/lib/translatomatic/resource_file/html.rb +21 -42
  95. data/lib/translatomatic/resource_file/key_value_support.rb +117 -0
  96. data/lib/translatomatic/resource_file/markdown.rb +36 -38
  97. data/lib/translatomatic/resource_file/plist.rb +121 -126
  98. data/lib/translatomatic/resource_file/po.rb +104 -82
  99. data/lib/translatomatic/resource_file/properties.rb +48 -77
  100. data/lib/translatomatic/resource_file/properties.treetop +87 -0
  101. data/lib/translatomatic/resource_file/resw.rb +56 -41
  102. data/lib/translatomatic/resource_file/subtitle.rb +86 -54
  103. data/lib/translatomatic/resource_file/text.rb +18 -18
  104. data/lib/translatomatic/resource_file/xcode_strings.rb +32 -63
  105. data/lib/translatomatic/resource_file/xcode_strings.treetop +85 -0
  106. data/lib/translatomatic/resource_file/xml.rb +94 -81
  107. data/lib/translatomatic/resource_file/yaml.rb +54 -68
  108. data/lib/translatomatic/retry_executor.rb +37 -0
  109. data/lib/translatomatic/slurp.rb +32 -0
  110. data/lib/translatomatic/string_batcher.rb +50 -0
  111. data/lib/translatomatic/string_escaping.rb +61 -0
  112. data/lib/translatomatic/text.rb +263 -0
  113. data/lib/translatomatic/text_collection.rb +66 -0
  114. data/lib/translatomatic/tmx.rb +5 -3
  115. data/lib/translatomatic/tmx/document.rb +107 -82
  116. data/lib/translatomatic/tmx/translation_unit.rb +19 -18
  117. data/lib/translatomatic/translation.rb +8 -28
  118. data/lib/translatomatic/translation/collection.rb +199 -0
  119. data/lib/translatomatic/translation/fetcher.rb +123 -0
  120. data/lib/translatomatic/translation/munging.rb +112 -0
  121. data/lib/translatomatic/translation/result.rb +50 -0
  122. data/lib/translatomatic/translation/sharer.rb +32 -0
  123. data/lib/translatomatic/translation/stats.rb +44 -0
  124. data/lib/translatomatic/translator.rb +91 -88
  125. data/lib/translatomatic/type_cast.rb +63 -0
  126. data/lib/translatomatic/util.rb +37 -33
  127. data/lib/translatomatic/version.rb +2 -2
  128. data/translatomatic.gemspec +57 -46
  129. metadata +136 -59
  130. data/lib/translatomatic/http_request.rb +0 -162
  131. data/lib/translatomatic/string.rb +0 -188
  132. data/lib/translatomatic/translation_result.rb +0 -86
  133. data/lib/translatomatic/translation_stats.rb +0 -31
  134. data/lib/translatomatic/translator/base.rb +0 -128
  135. data/lib/translatomatic/translator/frengly.rb +0 -62
  136. data/lib/translatomatic/translator/google.rb +0 -37
  137. data/lib/translatomatic/translator/microsoft.rb +0 -41
  138. data/lib/translatomatic/translator/my_memory.rb +0 -68
  139. data/lib/translatomatic/translator/yandex.rb +0 -56
@@ -1,91 +1,62 @@
1
- require 'date'
2
-
3
- module Translatomatic::ResourceFile
4
- # Properties resource file
5
- # @see https://docs.oracle.com/javase/tutorial/essential/environment/properties.html
6
- class Properties < Base
7
-
8
- # (see Translatomatic::ResourceFile::Base.extensions)
9
- def self.extensions
10
- %w{properties}
11
- end
12
-
13
- # (see Translatomatic::ResourceFile::Base.is_key_value?)
14
- def self.is_key_value?
15
- true
16
- end
17
-
18
- # (see Translatomatic::ResourceFile::Base.supports_variable_interpolation?)
19
- def self.supports_variable_interpolation?
20
- true
21
- end
22
-
23
- # (see Translatomatic::ResourceFile::Base#save)
24
- def save(target = path, options = {})
25
- out = ""
26
- out += add_created_by unless options[:no_created_by]
27
- properties.each do |key, value|
28
- # TODO: maintain original line ending format?
29
- value = value.gsub("\n", "\\n") if value # convert newlines to \n
30
- out += "#{key} = #{value}\n"
1
+ require 'treetop'
2
+
3
+ module Translatomatic
4
+ module ResourceFile
5
+ # Properties resource file
6
+ # @see https://docs.oracle.com/javase/tutorial/essential/environment/properties.html
7
+ class Properties < Base
8
+ include KeyValueSupport
9
+
10
+ # (see Base.extensions)
11
+ def self.extensions
12
+ %w[properties]
31
13
  end
32
- # escape unicode characters
33
- out = Translatomatic::EscapedUnicode.escape(out)
34
- target.write(out)
35
- end
36
-
37
- # (see Translatomatic::ResourceFile::Base#create_variable)
38
- def create_variable(name)
39
- return "{#{name}}"
40
- end
41
14
 
42
- # (see Translatomatic::ResourceFile::Base#variable_regex)
43
- def variable_regex
44
- /\{.*?\}/
45
- end
15
+ # (see Base.supports_variable_interpolation?)
16
+ def self.supports_variable_interpolation?
17
+ true
18
+ end
46
19
 
47
- private
20
+ # (see Base.preferred_locale_separator)
21
+ def self.preferred_locale_separator
22
+ '_'
23
+ end
48
24
 
49
- def load
50
- @properties = read_properties
51
- end
25
+ # (see Base#create_variable)
26
+ def create_variable(name)
27
+ "{#{name}}"
28
+ end
52
29
 
53
- def add_created_by
54
- comment(created_by)
55
- end
30
+ # (see Base#variable_regex)
31
+ def variable_regex
32
+ /\{.*?\}/
33
+ end
56
34
 
57
- def comment(text)
58
- "# #{text}\n"
59
- end
35
+ private
60
36
 
61
- def read_properties
62
- contents = read_contents(@path)
63
- # convert escaped unicode characters into unicode
64
- contents = Translatomatic::EscapedUnicode.unescape(contents)
65
- result = {}
66
- contents.gsub!(/\\\s*\n\s*/m, '') # put multi line strings on one line
67
- lines = contents.split("\n")
37
+ Treetop.load(File.join(__dir__, 'properties.treetop'))
38
+ # @private
39
+ class Parser < PropertiesParser; end
68
40
 
69
- lines.each do |line|
70
- line.strip!
71
- next if line.length == 0
72
- equal_idx = line.index("=")
73
- colon_idx = line.index(":")
41
+ def parse_doc(content)
42
+ Parser.new.parse(content)
43
+ end
74
44
 
75
- if line[0] == ?! || line[0] == ?#
76
- # comment
77
- # TODO: translate comments or keep originals?
78
- next
79
- elsif equal_idx.nil? && colon_idx.nil?
80
- # TODO: throw exception here?
81
- return {}
45
+ def render_element(element)
46
+ if element.is_a? Comment
47
+ return '' if element.text.nil?
48
+ comments = element.text.split(/[\r\n]+/)
49
+ comments.collect do |comment|
50
+ format("%<type>c %<comment>s\n",
51
+ type: element.type, comment: comment.strip)
52
+ end.join
53
+ elsif element.is_a? Definition
54
+ key = element.key
55
+ value = element.value
56
+ format(%(%<key>s = %<value>s\n),
57
+ key: escape(key), value: escape(value))
82
58
  end
83
- name, value = line.split(/\s*[=:]\s*/, 2)
84
- value = value.gsub("\\n", "\n") # convert \n to newlines
85
- result[name] = value
86
59
  end
87
- result
88
60
  end
89
-
90
61
  end
91
62
  end
@@ -0,0 +1,87 @@
1
+ # https://en.wikipedia.org/wiki/.properties
2
+ grammar Properties
3
+ rule document
4
+ (whitespace / comment / definition)* {
5
+ def content
6
+ elements.collect { |i| i.content }.select { |i| i[0] != :whitespace }
7
+ end
8
+ }
9
+ end
10
+
11
+ rule comment
12
+ start_comment comment_content end_comment {
13
+ def content
14
+ [:comment, elements[1].text_value, elements[0].text_value]
15
+ end
16
+ }
17
+ end
18
+
19
+ rule start_comment
20
+ '#' / '!'
21
+ end
22
+
23
+ rule eof
24
+ !character
25
+ end
26
+
27
+ rule end_comment
28
+ newline / eof
29
+ end
30
+
31
+ rule comment_content
32
+ (!end_comment character)+
33
+ end
34
+
35
+ rule definition
36
+ key assignment value? end_definition {
37
+ def content
38
+ [:definition, elements[0].text_value, elements[2] ? elements[2].text_value : nil]
39
+ end
40
+ }
41
+ end
42
+
43
+ rule end_definition
44
+ newline / eof
45
+ end
46
+
47
+ rule newline
48
+ "\n"
49
+ end
50
+
51
+ rule line_continuation
52
+ "\\" newline whitespace*
53
+ end
54
+
55
+ rule assignment
56
+ whitespace* ('=' / ':') whitespace*
57
+ end
58
+
59
+ rule character
60
+ .
61
+ end
62
+
63
+ rule key
64
+ ('\\ ' / !assignment character)+
65
+ #(!assignment word)+
66
+ end
67
+
68
+ rule value_character
69
+ line_continuation / character
70
+ end
71
+
72
+ rule word
73
+ ('\\ ' / !assignment character)+
74
+ end
75
+
76
+ rule value
77
+ (!newline value_character)+
78
+ end
79
+
80
+ rule whitespace
81
+ [ \t\r\n] {
82
+ def content
83
+ [:whitespace, text_value]
84
+ end
85
+ }
86
+ end
87
+ end
@@ -1,51 +1,66 @@
1
- module Translatomatic::ResourceFile
2
- # Windows resources file (XML)
3
- class RESW < XML
1
+ module Translatomatic
2
+ module ResourceFile
3
+ # Windows resources file (XML)
4
+ class RESW < XML
5
+ # (see Base.extensions)
6
+ def self.extensions
7
+ %w[resw resx]
8
+ end
4
9
 
5
- # (see Translatomatic::ResourceFile::Base.extensions)
6
- def self.extensions
7
- %w{resw resx}
8
- end
10
+ # (see Base.key_value?)
11
+ def self.key_value?
12
+ true
13
+ end
9
14
 
10
- # (see Translatomatic::ResourceFile::Base.is_key_value?)
11
- def self.is_key_value?
12
- true
13
- end
15
+ private
14
16
 
15
- # (see Translatomatic::ResourceFile::Base#locale_path)
16
- def locale_path(locale)
17
- # e.g. strings/en-US/resources.resw
18
- dir = path.dirname
19
- dir.parent + locale.to_s + path.basename
20
- end
17
+ def init_nodemap
18
+ @nodemap = {}
19
+ nodes = @doc.search('//data/@name|//text()|//comment()')
20
+ nodes.each do |node|
21
+ process_node(node)
22
+ end
23
+ end
21
24
 
22
- private
25
+ def process_node(node)
26
+ if node.comment?
27
+ @metadata.parse_comment(node.content)
28
+ elsif node.type == Nokogiri::XML::Node::ATTRIBUTE_NODE # data name=""
29
+ @key = node.content
30
+ elsif node.text?
31
+ process_text_node(node)
32
+ end
33
+ end
23
34
 
24
- def init_nodemap
25
- result = {}
26
- key_values = @doc.search('//data/@name|//text()')
27
- key_values.each_slice(2) do |key, value|
28
- key = key.value
29
- value = value
30
- result[key] = value
35
+ def process_text_node(node)
36
+ parent = node.parent
37
+ if parent.name == 'value' # <value>content</value>
38
+ found_value(node)
39
+ elsif parent.name == 'comment'
40
+ @metadata.parse_comment(node.content)
41
+ end
42
+ end
43
+
44
+ def found_value(value)
45
+ @nodemap[@key] = value if @key
46
+ @metadata.assign_key(@key)
47
+ @key = nil
31
48
  end
32
- @nodemap = result
33
- end
34
49
 
35
- def create_node(key, value)
36
- # add xml: <data name="key"><value>value</value></data>
37
- data_node = Nokogiri::XML::Node.new("data", @doc)
38
- data_node["name"] = key
39
- value_node = Nokogiri::XML::Node.new("value", @doc)
40
- text_node = Nokogiri::XML::Text.new(value, @doc)
41
- value_node.add_child(text_node)
42
- data_node.add_child(value_node)
50
+ def create_node(key, value)
51
+ # add xml: <data name="key"><value>value</value></data>
52
+ data_node = Nokogiri::XML::Node.new('data', @doc)
53
+ data_node['name'] = key
54
+ value_node = Nokogiri::XML::Node.new('value', @doc)
55
+ text_node = Nokogiri::XML::Text.new(value.to_s, @doc)
56
+ value_node.add_child(text_node)
57
+ data_node.add_child(value_node)
43
58
 
44
- @doc.root.add_child(data_node)
59
+ @doc.root.add_child(data_node)
45
60
 
46
- @nodemap[key] = text_node
47
- @properties[key] = value
61
+ @nodemap[key] = text_node
62
+ @properties[key] = value
63
+ end
48
64
  end
49
-
50
- end # class
51
- end # module
65
+ end
66
+ end
@@ -1,75 +1,107 @@
1
- module Translatomatic::ResourceFile
2
- # Subtitle resource file.
3
- # requires 'titlekit' gem
4
- class Subtitle < Base
1
+ module Translatomatic
2
+ module ResourceFile
3
+ # Subtitle resource file.
4
+ # requires 'titlekit' gem
5
+ class Subtitle < Base
6
+ # (see Base.extensions)
7
+ def self.extensions
8
+ %w[srt ass ssa]
9
+ end
5
10
 
6
- # (see Translatomatic::ResourceFile::Base.extensions)
7
- def self.extensions
8
- %w{srt ass ssa}
9
- end
11
+ # (see Base.enabled?)
12
+ def self.enabled?
13
+ @enabled ||= begin
14
+ require 'titlekit'
15
+ true
16
+ rescue LoadError
17
+ false
18
+ end
19
+ end
10
20
 
11
- # (see Translatomatic::ResourceFile::Base.enabled?)
12
- def self.enabled?
13
- @enabled ||= begin
14
- require 'titlekit'
15
- true
16
- rescue LoadError
17
- false
21
+ # (see Base#set)
22
+ def set(key, value)
23
+ super(key, value)
24
+
25
+ if @subtitle_map.include?(key)
26
+ @subtitle_map[key][:lines] = value.to_s
27
+ else
28
+ add_subtitle(lines: value) unless value.blank?
29
+ end
18
30
  end
19
- end
20
31
 
21
- # (see Translatomatic::ResourceFile::Base#set)
22
- def set(key, value)
23
- super(key, value)
24
- if @subtitle_map.include?(key)
25
- @subtitle_map[key][:lines] = value
32
+ # (see Base#save)
33
+ def save(target = path, options = {})
34
+ add_created_by unless options[:no_created_by] || created_by?
35
+ export(target)
26
36
  end
27
- end
28
37
 
29
- # (see Translatomatic::ResourceFile::Base#save)
30
- def save(target = path, options = {})
31
- export(target)
32
- end
38
+ private
33
39
 
34
- private
40
+ def init
41
+ @subtitle_map = {}
42
+ @subtitles = []
43
+ @keynum = 1
44
+ end
35
45
 
36
- def init
37
- @subtitle_map = {}
38
- @subtitles = []
39
- end
46
+ def load
47
+ @metadata.reset
48
+ subtitles = import(@path)
49
+ subtitles.each { |i| add_subtitle(i) }
50
+ init_properties
51
+ end
40
52
 
41
- def load
42
- @subtitles = import(@path)
43
- init_subtitle_map
44
- init_properties
45
- end
53
+ def process_metadata(key, subtitle)
54
+ lines = subtitle[:lines] || ''
55
+ context = @metadata.parse_comment(lines)
56
+ @metadata.assign_key(key) unless context.present?
57
+ end
46
58
 
47
- def init_subtitle_map
48
- # map of key1 => subtitle, key2 => subtitle, ...
49
- @keynum = 1
50
- @subtitles.each_with_index do |subtitle, i|
59
+ def add_created_by
60
+ # TODO
61
+ end
62
+
63
+ def add_subtitle(subtitle = {})
51
64
  key = "key#{@keynum}"
65
+ subtitle[:id] ||= @keynum
66
+ subtitle[:start] ||= @keynum * 10
67
+ subtitle[:end] ||= @keynum * 10 + 5
68
+ process_metadata(key, subtitle)
52
69
  @keynum += 1
53
70
  @subtitle_map[key] = subtitle
71
+ @subtitles << subtitle
54
72
  end
55
- end
56
73
 
57
- def init_properties
58
- @properties = @subtitle_map.transform_values { |i| i[:lines] }
59
- end
74
+ # Find the first gap in subtitles with a minimum length in seconds.
75
+ # @return [Array] [start, end] Start and end times of the gap
76
+ def find_gap(min_length)
77
+ last = 0
78
+ @subtitles.each do |subtitle|
79
+ return [last, subtitle.start] if subtitle.start - last >= min_length
80
+ last = subtitle.end
81
+ end
82
+ [last, -1]
83
+ end
60
84
 
61
- def import(path)
62
- import_export_class(path).import(read_contents(path))
63
- end
85
+ def init_properties
86
+ @properties = @subtitle_map.transform_values { |i| i[:lines] }
87
+ end
64
88
 
65
- def export(target, options = {})
66
- content = import_export_class(target).export(@subtitles) || ''
67
- target.write(content.chomp)
68
- end
89
+ def import(path)
90
+ contents = read_contents(path)
91
+ return [] if contents.blank?
92
+ import_export_class(path).import(contents)
93
+ end
69
94
 
70
- def import_export_class(path)
71
- class_name = path.extname.sub(/^\./, '').upcase
72
- Titlekit.const_get(class_name)
95
+ def export(target, _options = {})
96
+ content = import_export_class(target).export(@subtitles) || ''
97
+ content = content.gsub(/[\r\n]+\Z/, '') + "\n"
98
+ target.write(content)
99
+ end
100
+
101
+ def import_export_class(path)
102
+ class_name = path.extname.sub(/^\./, '').upcase
103
+ Titlekit.const_get(class_name)
104
+ end
73
105
  end
74
106
  end
75
107
  end