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.
- checksums.yaml +5 -5
- data/.gitattributes +20 -20
- data/.gitignore +19 -15
- data/.rspec +3 -3
- data/.rubocop.yml +28 -0
- data/.translatomatic/config.yml +4 -0
- data/.travis.yml +4 -6
- data/.yardopts +9 -9
- data/Gemfile +8 -4
- data/Guardfile +4 -5
- data/README.de.md +55 -50
- data/README.en.md +177 -0
- data/README.es.md +53 -48
- data/README.fr.md +53 -48
- data/README.it.md +54 -49
- data/README.ja.md +63 -58
- data/README.ko.md +59 -54
- data/README.md +17 -13
- data/README.ms.md +50 -45
- data/README.pt.md +54 -49
- data/README.ru.md +57 -52
- data/README.sv.md +51 -46
- data/README.zh.md +60 -55
- data/Rakefile +3 -3
- data/TODO.txt +6 -0
- data/bin/console +3 -3
- data/bin/translatomatic +4 -2
- data/config/i18n-tasks.yml +130 -0
- data/config/locales/translatomatic/de.yml +141 -99
- data/config/locales/translatomatic/en.yml +129 -89
- data/config/locales/translatomatic/es.yml +136 -99
- data/config/locales/translatomatic/fr.yml +139 -100
- data/config/locales/translatomatic/it.yml +135 -97
- data/config/locales/translatomatic/ja.yml +137 -98
- data/config/locales/translatomatic/ko.yml +138 -98
- data/config/locales/translatomatic/ms.yml +138 -100
- data/config/locales/translatomatic/pt.yml +137 -101
- data/config/locales/translatomatic/ru.yml +136 -98
- data/config/locales/translatomatic/sv.yml +134 -96
- data/config/locales/translatomatic/zh.yml +136 -97
- data/db/migrate/201712170000_initial.rb +2 -3
- data/lib/translatomatic.rb +40 -25
- data/lib/translatomatic/cli.rb +5 -1
- data/lib/translatomatic/cli/base.rb +61 -58
- data/lib/translatomatic/cli/common_options.rb +14 -11
- data/lib/translatomatic/cli/config.rb +96 -91
- data/lib/translatomatic/cli/database.rb +85 -23
- data/lib/translatomatic/cli/main.rb +158 -104
- data/lib/translatomatic/cli/thor.rb +29 -0
- data/lib/translatomatic/cli/translate.rb +134 -157
- data/lib/translatomatic/config.rb +10 -301
- data/lib/translatomatic/config/display.rb +78 -0
- data/lib/translatomatic/config/files.rb +60 -0
- data/lib/translatomatic/config/location_settings.rb +133 -0
- data/lib/translatomatic/config/options.rb +68 -0
- data/lib/translatomatic/config/selector.rb +127 -0
- data/lib/translatomatic/config/settings.rb +148 -0
- data/lib/translatomatic/converter.rb +40 -28
- data/lib/translatomatic/database.rb +127 -110
- data/lib/translatomatic/define_options.rb +4 -5
- data/lib/translatomatic/escaped_unicode.rb +86 -76
- data/lib/translatomatic/extractor.rb +5 -2
- data/lib/translatomatic/extractor/base.rb +12 -12
- data/lib/translatomatic/extractor/ruby.rb +7 -6
- data/lib/translatomatic/file_translator.rb +101 -244
- data/lib/translatomatic/flattenation.rb +39 -0
- data/lib/translatomatic/http.rb +13 -0
- data/lib/translatomatic/http/client.rb +144 -0
- data/lib/translatomatic/http/exception.rb +43 -0
- data/lib/translatomatic/http/file_param.rb +27 -0
- data/lib/translatomatic/http/param.rb +37 -0
- data/lib/translatomatic/http/request.rb +91 -0
- data/lib/translatomatic/i18n.rb +43 -0
- data/lib/translatomatic/locale.rb +71 -59
- data/lib/translatomatic/logger.rb +43 -28
- data/lib/translatomatic/metadata.rb +58 -0
- data/lib/translatomatic/model.rb +4 -2
- data/lib/translatomatic/model/locale.rb +5 -5
- data/lib/translatomatic/model/text.rb +5 -5
- data/lib/translatomatic/option.rb +57 -34
- data/lib/translatomatic/path_utils.rb +126 -0
- data/lib/translatomatic/progress_updater.rb +13 -16
- data/lib/translatomatic/provider.rb +101 -0
- data/lib/translatomatic/provider/base.rb +136 -0
- data/lib/translatomatic/provider/frengly.rb +55 -0
- data/lib/translatomatic/provider/google.rb +78 -0
- data/lib/translatomatic/provider/google_web.rb +50 -0
- data/lib/translatomatic/provider/microsoft.rb +144 -0
- data/lib/translatomatic/provider/my_memory.rb +75 -0
- data/lib/translatomatic/provider/yandex.rb +61 -0
- data/lib/translatomatic/resource_file.rb +59 -53
- data/lib/translatomatic/resource_file/base.rb +171 -237
- data/lib/translatomatic/resource_file/csv.rb +176 -24
- data/lib/translatomatic/resource_file/html.rb +21 -42
- data/lib/translatomatic/resource_file/key_value_support.rb +117 -0
- data/lib/translatomatic/resource_file/markdown.rb +36 -38
- data/lib/translatomatic/resource_file/plist.rb +121 -126
- data/lib/translatomatic/resource_file/po.rb +104 -82
- data/lib/translatomatic/resource_file/properties.rb +48 -77
- data/lib/translatomatic/resource_file/properties.treetop +87 -0
- data/lib/translatomatic/resource_file/resw.rb +56 -41
- data/lib/translatomatic/resource_file/subtitle.rb +86 -54
- data/lib/translatomatic/resource_file/text.rb +18 -18
- data/lib/translatomatic/resource_file/xcode_strings.rb +32 -63
- data/lib/translatomatic/resource_file/xcode_strings.treetop +85 -0
- data/lib/translatomatic/resource_file/xml.rb +94 -81
- data/lib/translatomatic/resource_file/yaml.rb +54 -68
- data/lib/translatomatic/retry_executor.rb +37 -0
- data/lib/translatomatic/slurp.rb +32 -0
- data/lib/translatomatic/string_batcher.rb +50 -0
- data/lib/translatomatic/string_escaping.rb +61 -0
- data/lib/translatomatic/text.rb +263 -0
- data/lib/translatomatic/text_collection.rb +66 -0
- data/lib/translatomatic/tmx.rb +5 -3
- data/lib/translatomatic/tmx/document.rb +107 -82
- data/lib/translatomatic/tmx/translation_unit.rb +19 -18
- data/lib/translatomatic/translation.rb +8 -28
- data/lib/translatomatic/translation/collection.rb +199 -0
- data/lib/translatomatic/translation/fetcher.rb +123 -0
- data/lib/translatomatic/translation/munging.rb +112 -0
- data/lib/translatomatic/translation/result.rb +50 -0
- data/lib/translatomatic/translation/sharer.rb +32 -0
- data/lib/translatomatic/translation/stats.rb +44 -0
- data/lib/translatomatic/translator.rb +91 -88
- data/lib/translatomatic/type_cast.rb +63 -0
- data/lib/translatomatic/util.rb +37 -33
- data/lib/translatomatic/version.rb +2 -2
- data/translatomatic.gemspec +57 -46
- metadata +136 -59
- data/lib/translatomatic/http_request.rb +0 -162
- data/lib/translatomatic/string.rb +0 -188
- data/lib/translatomatic/translation_result.rb +0 -86
- data/lib/translatomatic/translation_stats.rb +0 -31
- data/lib/translatomatic/translator/base.rb +0 -128
- data/lib/translatomatic/translator/frengly.rb +0 -62
- data/lib/translatomatic/translator/google.rb +0 -37
- data/lib/translatomatic/translator/microsoft.rb +0 -41
- data/lib/translatomatic/translator/my_memory.rb +0 -68
- data/lib/translatomatic/translator/yandex.rb +0 -56
@@ -1,153 +1,148 @@
|
|
1
|
-
module Translatomatic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
1
|
+
module Translatomatic
|
2
|
+
module ResourceFile
|
3
|
+
# Property list resource file
|
4
|
+
# @see https://en.wikipedia.org/wiki/Property_list
|
5
|
+
class Plist < XML
|
6
|
+
# property list types:
|
7
|
+
# array, dict, string, data, date, integer, real, boolean
|
8
|
+
# boolean is <true /> or <false />
|
9
|
+
|
10
|
+
# (see Base.extensions)
|
11
|
+
def self.extensions
|
12
|
+
%w[plist]
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
# (see Base.key_value?)
|
16
|
+
def self.key_value?
|
17
|
+
true
|
18
|
+
end
|
20
19
|
|
21
|
-
|
20
|
+
private
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
def init_nodemap
|
23
|
+
result = Parser.new.parse(@doc)
|
24
|
+
# puts "parser result:"
|
25
|
+
# p result
|
26
|
+
@flattened_data = flatten(result)
|
27
|
+
@nodemap = @flattened_data.transform_values(&:node)
|
28
|
+
# puts "nodemap:"
|
29
|
+
# p @nodemap
|
30
|
+
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
def init_properties
|
33
|
+
@properties = @flattened_data.transform_values(&:content)
|
34
|
+
end
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
36
|
+
def create_node(key, value)
|
37
|
+
# add properties to first dict found
|
38
|
+
dict = @doc.xpath('//dict')
|
39
|
+
# TODO: not sure sure what to do if dict is missing
|
40
|
+
raise 'missing top level dictionary' unless dict.present?
|
41
|
+
dict = dict[0]
|
42
|
+
|
43
|
+
# add xml: <data name="key"><value>value</value></data>
|
44
|
+
key_node = Nokogiri::XML::Node.new('key', @doc)
|
45
|
+
key_node.content = key
|
46
|
+
value_node = Nokogiri::XML::Node.new('string', @doc)
|
47
|
+
value_node.content = value
|
48
|
+
dict.add_child(key_node)
|
49
|
+
dict.add_child(value_node)
|
50
|
+
|
51
|
+
@nodemap[key] = value_node
|
52
|
+
@properties[key] = value
|
53
|
+
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
def empty_doc
|
56
|
+
Nokogiri::XML(EMPTY_DOC)
|
57
|
+
end
|
59
58
|
|
60
|
-
|
61
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
62
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
63
|
-
<plist version="1.0">
|
59
|
+
EMPTY_DOC = <<EOM.strip_heredoc.freeze
|
60
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
61
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
62
|
+
<plist version="1.0">
|
64
63
|
<dict>
|
65
64
|
</dict>
|
66
|
-
</plist>
|
65
|
+
</plist>
|
67
66
|
EOM
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
# @private
|
69
|
+
PlistNode = Struct.new(:node, :content) do
|
70
|
+
def inspect
|
71
|
+
"PlistNode:#{content}"
|
72
|
+
end
|
72
73
|
end
|
73
|
-
end
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
75
|
+
# Adapted from nokogiri-plist parser
|
76
|
+
# @see https://github.com/caseyhoward/nokogiri-plist
|
77
|
+
# @private
|
78
|
+
class Parser
|
79
|
+
def parse(xml, options = {})
|
80
|
+
@converters = {
|
81
|
+
'integer' => proc { |node| node.content.to_i },
|
82
|
+
'real' => proc { |node| node.content.to_f },
|
83
|
+
'string' => proc { |node| node.content.to_s },
|
84
|
+
# DateTime.parse(node.content)
|
85
|
+
'date' => proc { |node| node.content.to_s },
|
86
|
+
'true' => proc { |_node| true },
|
87
|
+
'false' => proc { |_node| false },
|
88
|
+
'dict' => proc { |node| parse_dict(node) },
|
89
|
+
'array' => proc { |node| parse_array(node) },
|
90
|
+
'data' => proc { |node| node.content.to_s }
|
91
|
+
}.merge(options[:converters] || {})
|
92
|
+
|
93
|
+
@dict_class = options[:dict_class] || Hash
|
94
|
+
plist = xml.root
|
95
|
+
plist = plist.children.first if plist.name == 'plist'
|
96
|
+
result = parse_value_node(next_valid_sibling(plist))
|
97
|
+
plist_node_value(result)
|
98
|
+
end
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
def parse_value_node(value_node)
|
101
|
+
value = @converters[value_node.name].call(value_node)
|
102
|
+
PlistNode.new(value_node, value)
|
103
|
+
end
|
104
104
|
|
105
|
-
|
106
|
-
|
107
|
-
|
105
|
+
def valid_type?(type)
|
106
|
+
@converters.key? type
|
107
|
+
end
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
109
|
+
def valid_node?(node)
|
110
|
+
valid_type?(node.name) || node.name == 'key'
|
111
|
+
end
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
113
|
+
def parse_dict(node)
|
114
|
+
node.xpath('./key').each_with_object(@dict_class.new) do |k, v|
|
115
|
+
plist_node = parse_value_node(next_valid_sibling(k))
|
116
|
+
value = plist_node_value(plist_node)
|
117
|
+
v[k.content] = value
|
118
|
+
end
|
119
119
|
end
|
120
|
-
end
|
121
120
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
121
|
+
# if the PlistNode value is an array or hash, use that directly
|
122
|
+
# instead of the PlistNode.
|
123
|
+
def plist_node_value(plist_node)
|
124
|
+
content = plist_node.content
|
125
|
+
if content.is_a?(Array) || content.is_a?(Hash)
|
126
|
+
content
|
127
|
+
else
|
128
|
+
plist_node
|
129
|
+
end
|
130
130
|
end
|
131
|
-
end
|
132
131
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
132
|
+
def parse_array(node)
|
133
|
+
node.children.each_with_object([]) do |child, result|
|
134
|
+
if valid_node?(child)
|
135
|
+
plist_node = parse_value_node(child)
|
136
|
+
result << plist_node_value(plist_node)
|
137
|
+
end
|
138
138
|
end
|
139
|
-
result
|
140
139
|
end
|
141
|
-
end
|
142
140
|
|
143
|
-
|
144
|
-
|
145
|
-
node
|
141
|
+
def next_valid_sibling(node)
|
142
|
+
node = node.next_sibling until node.nil? || valid_type?(node.name)
|
143
|
+
node
|
146
144
|
end
|
147
|
-
node
|
148
145
|
end
|
149
|
-
|
150
146
|
end
|
151
|
-
|
152
|
-
|
153
|
-
end # module
|
147
|
+
end
|
148
|
+
end
|
@@ -1,107 +1,129 @@
|
|
1
1
|
require 'poparser'
|
2
2
|
|
3
|
-
module Translatomatic
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
module Translatomatic
|
4
|
+
module ResourceFile
|
5
|
+
# Property list resource file
|
6
|
+
# @see https://en.wikipedia.org/wiki/Property_list
|
7
|
+
class PO < Base
|
8
|
+
# (see Base.extensions)
|
9
|
+
def self.extensions
|
10
|
+
%w[po pot]
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# (see Base.key_value?)
|
14
|
+
def self.key_value?
|
15
|
+
true
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
entry = po_property.entry
|
33
|
-
if entry.plural?
|
34
|
-
msgstr = entry.msgstr || []
|
35
|
-
msgstr[po_property.index] = value
|
36
|
-
entry.msgstr = msgstr
|
18
|
+
# (see Base#set)
|
19
|
+
def set(key, value)
|
20
|
+
super(key, value)
|
21
|
+
|
22
|
+
if @pomap.include?(key)
|
23
|
+
po_property = @pomap[key]
|
24
|
+
entry = po_property.entry
|
25
|
+
if entry.plural?
|
26
|
+
msgstr = entry.msgstr || []
|
27
|
+
msgstr[po_property.msgstr_index] = value
|
28
|
+
entry.msgstr = msgstr
|
29
|
+
else
|
30
|
+
entry.msgstr = value
|
31
|
+
end
|
37
32
|
else
|
38
|
-
|
33
|
+
# new key, create po entry
|
34
|
+
@po << {
|
35
|
+
msgid: key,
|
36
|
+
msgstr: value
|
37
|
+
}
|
38
|
+
entry = @po.entries[-1]
|
39
|
+
add_entry(entry, :msgid, 0)
|
39
40
|
end
|
40
41
|
end
|
41
|
-
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
# (see Base#save)
|
44
|
+
def save(target = path, options = {})
|
45
|
+
return unless @po
|
46
46
|
add_created_by unless options[:no_created_by]
|
47
47
|
target.write(@po.to_s)
|
48
48
|
end
|
49
|
-
end
|
50
49
|
|
51
|
-
|
50
|
+
private
|
52
51
|
|
53
|
-
|
54
|
-
attr_reader :entry
|
55
|
-
attr_reader :value_index
|
52
|
+
PO_DATE_FORMAT = '%Y-%M-%d %HH:%MM%Z'.freeze
|
56
53
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
# used to index into PO msgstr[]
|
55
|
+
# @private
|
56
|
+
class PoProperty
|
57
|
+
attr_reader :entry
|
58
|
+
attr_reader :msgstr_index
|
61
59
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
def initialize(entry, msgstr_index)
|
61
|
+
@entry = entry
|
62
|
+
@msgstr_index = msgstr_index
|
63
|
+
end
|
64
|
+
|
65
|
+
def value
|
66
|
+
if entry.plural?
|
67
|
+
entry.msgstr[msgstr_index]
|
68
|
+
else
|
69
|
+
entry.msgstr
|
70
|
+
end
|
67
71
|
end
|
68
72
|
end
|
69
|
-
end
|
70
73
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
74
|
+
def init
|
75
|
+
@po = PoParser.parse('')
|
76
|
+
@pomap = {}
|
77
|
+
end
|
75
78
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
def load
|
80
|
+
@metadata.reset
|
81
|
+
content = read_contents(@path)
|
82
|
+
@po = PoParser.parse(content)
|
83
|
+
init_pomap(@po)
|
84
|
+
@properties = pomap_to_properties
|
85
|
+
end
|
82
86
|
|
83
|
-
|
84
|
-
|
85
|
-
|
87
|
+
def add_created_by
|
88
|
+
header = po_header
|
89
|
+
header['PO-RevisionDate'] = Time.now.strftime(PO_DATE_FORMAT)
|
90
|
+
header['Last-Translator'] = 'Translatomatic ' + VERSION
|
91
|
+
end
|
86
92
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
po.entries.each do |entry|
|
91
|
-
add_entry(entry, :msgid, 0)
|
92
|
-
add_entry(entry, :msgid_plural, 1) if entry.plural?
|
93
|
+
def po_header
|
94
|
+
# TODO: get or create header entry
|
95
|
+
{}
|
93
96
|
end
|
94
|
-
pomap
|
95
|
-
end
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
# create mapping from key to PoProperty
|
99
|
+
def init_pomap(po)
|
100
|
+
po.entries.each_with_index do |entry, i|
|
101
|
+
# skip PO file header if present
|
102
|
+
# TODO: update PO-Revision-Date, Last-Provider ?
|
103
|
+
next if entry.msgid == '' && i.zero?
|
104
|
+
|
105
|
+
if entry.extracted_comment
|
106
|
+
@metadata.parse_comment(entry.extracted_comment.value)
|
107
|
+
end
|
108
|
+
add_entry(entry, :msgid, 0)
|
109
|
+
add_entry(entry, :msgid_plural, 1) if entry.plural?
|
110
|
+
@metadata.clear_context
|
111
|
+
end
|
112
|
+
end
|
100
113
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
114
|
+
def pomap_to_properties
|
115
|
+
@pomap.transform_values { |i| i.value.to_s }
|
116
|
+
end
|
105
117
|
|
106
|
-
|
107
|
-
|
118
|
+
def add_entry(entry, key, msgstr_index)
|
119
|
+
map_key = entry.send(key).to_s
|
120
|
+
return unless map_key
|
121
|
+
|
122
|
+
msg_context = entry.msgctxt
|
123
|
+
map_key = map_key + '.' + msg_context.to_s if msg_context
|
124
|
+
@pomap[map_key] = PoProperty.new(entry, msgstr_index)
|
125
|
+
@metadata.assign_key(map_key, keep_context: true)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|