any2tmx 1.0.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 +7 -0
- data/Gemfile +8 -0
- data/History.txt +8 -0
- data/Rakefile +7 -0
- data/any2tmx.gemspec +26 -0
- data/bin/android2tmx +32 -0
- data/bin/json2tmx +32 -0
- data/bin/yaml2tmx +32 -0
- data/lib/any2tmx/android_xml_parser.rb +146 -0
- data/lib/any2tmx/options.rb +120 -0
- data/lib/any2tmx/phrase_set.rb +14 -0
- data/lib/any2tmx/tmx_writer.rb +34 -0
- data/lib/any2tmx/transforms/android_transform.rb +13 -0
- data/lib/any2tmx/transforms/json_transform.rb +15 -0
- data/lib/any2tmx/transforms/result.rb +24 -0
- data/lib/any2tmx/transforms/transform.rb +26 -0
- data/lib/any2tmx/transforms/yaml_transform.rb +20 -0
- data/lib/any2tmx/transforms.rb +9 -0
- data/lib/any2tmx/traversable.rb +62 -0
- data/lib/any2tmx/version.rb +3 -0
- data/lib/any2tmx.rb +8 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e34e73e877005effaf94729c0d3929359ee04e96
|
4
|
+
data.tar.gz: 5778d82075a5db914398b25af2c19e69885fd043
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7e7539b3f90f17c4b94e22838ad0d9325ed2a0201a03aba27dfff39f15422ca8c4d24d9b70130e0a702a1748b4a35a500daa3b7f1b35a60fa17fe11c18538926
|
7
|
+
data.tar.gz: 6658db3c6acd69e5fe0cfbb1013becffee73e159cee8b98f7483adca03448543dfdfea8f18a609af1754ac07e28db7fe4c1da12bd52c771abd178bacc05f87e4
|
data/Gemfile
ADDED
data/History.txt
ADDED
data/Rakefile
ADDED
data/any2tmx.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
2
|
+
require 'any2tmx/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "any2tmx"
|
6
|
+
s.version = ::Any2Tmx::VERSION
|
7
|
+
s.authors = ["Cameron Dutro"]
|
8
|
+
s.email = ["camertron@gmail.com"]
|
9
|
+
s.homepage = "http://github.com/camertron/any2tmx"
|
10
|
+
|
11
|
+
s.description = s.summary = "A command-line tool to convert certain file types to the standard TMX format for translation memories."
|
12
|
+
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.has_rdoc = true
|
15
|
+
|
16
|
+
s.add_dependency 'xml-write-stream', '~> 1.0'
|
17
|
+
s.add_dependency 'nokogiri', '~> 1.6'
|
18
|
+
s.add_dependency 'htmlentities', '~> 4.3'
|
19
|
+
|
20
|
+
s.executables << 'yaml2tmx'
|
21
|
+
s.executables << 'json2tmx'
|
22
|
+
s.executables << 'android2tmx'
|
23
|
+
|
24
|
+
s.require_path = 'lib'
|
25
|
+
s.files = Dir["{lib,spec}/**/*", "Gemfile", "History.txt", "README.md", "Rakefile", "any2tmx.gemspec"]
|
26
|
+
end
|
data/bin/android2tmx
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'any2tmx'
|
4
|
+
|
5
|
+
options = Any2Tmx::Options.new('android2tmx')
|
6
|
+
|
7
|
+
unless options.valid?
|
8
|
+
puts options.errors.first
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
if options.help?
|
13
|
+
options.print_help
|
14
|
+
exit 0
|
15
|
+
end
|
16
|
+
|
17
|
+
transform = Any2Tmx::Transforms::AndroidTransform.new(options)
|
18
|
+
result = transform.result
|
19
|
+
|
20
|
+
STDERR.write("Matched #{result.processed_count} of #{result.source_phrase_count} source phrases\n")
|
21
|
+
|
22
|
+
stream = if options.output
|
23
|
+
File.open(options.output, 'w+')
|
24
|
+
else
|
25
|
+
STDOUT
|
26
|
+
end
|
27
|
+
|
28
|
+
result.write(stream)
|
29
|
+
|
30
|
+
if options.output
|
31
|
+
STDERR.write("Wrote #{options.output}\n")
|
32
|
+
end
|
data/bin/json2tmx
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'any2tmx'
|
4
|
+
|
5
|
+
options = Any2Tmx::Options.new('json2tmx')
|
6
|
+
|
7
|
+
unless options.valid?
|
8
|
+
puts options.errors.first
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
if options.help?
|
13
|
+
options.print_help
|
14
|
+
exit 0
|
15
|
+
end
|
16
|
+
|
17
|
+
transform = Any2Tmx::Transforms::JsonTransform.new(options)
|
18
|
+
result = transform.result
|
19
|
+
|
20
|
+
STDERR.write("Matched #{result.processed_count} of #{result.source_phrase_count} source phrases\n")
|
21
|
+
|
22
|
+
stream = if options.output
|
23
|
+
File.open(options.output, 'w+')
|
24
|
+
else
|
25
|
+
STDOUT
|
26
|
+
end
|
27
|
+
|
28
|
+
result.write(stream)
|
29
|
+
|
30
|
+
if options.output
|
31
|
+
STDERR.write("Wrote #{options.output}\n")
|
32
|
+
end
|
data/bin/yaml2tmx
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'any2tmx'
|
4
|
+
|
5
|
+
options = Any2Tmx::Options.new('yaml2tmx')
|
6
|
+
|
7
|
+
unless options.valid?
|
8
|
+
puts options.errors.first
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
if options.help?
|
13
|
+
options.print_help
|
14
|
+
exit 0
|
15
|
+
end
|
16
|
+
|
17
|
+
transform = Any2Tmx::Transforms::YamlTransform.new(options)
|
18
|
+
result = transform.result
|
19
|
+
|
20
|
+
STDERR.write("Matched #{result.processed_count} of #{result.source_phrase_count} source phrases\n")
|
21
|
+
|
22
|
+
stream = if options.output
|
23
|
+
File.open(options.output, 'w+')
|
24
|
+
else
|
25
|
+
STDOUT
|
26
|
+
end
|
27
|
+
|
28
|
+
result.write(stream)
|
29
|
+
|
30
|
+
if options.output
|
31
|
+
STDERR.write("Wrote #{options.output}\n")
|
32
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'htmlentities'
|
3
|
+
|
4
|
+
# These classes have been adapted from:
|
5
|
+
# https://github.com/rosette-proj/rosette-extractor-xml
|
6
|
+
|
7
|
+
class HTMLEntities
|
8
|
+
MAPPINGS['android_xml'] = MAPPINGS['xhtml1'].dup.tap do |mappings|
|
9
|
+
mappings.delete('apos')
|
10
|
+
end
|
11
|
+
|
12
|
+
FLAVORS << 'android_xml'
|
13
|
+
|
14
|
+
class AndroidXmlDecoder < Decoder
|
15
|
+
def initialize
|
16
|
+
super('android_xml')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Any2Tmx
|
22
|
+
class AndroidXmlParser
|
23
|
+
class << self
|
24
|
+
def parse(xml_content)
|
25
|
+
doc = parse_xml(xml_content)
|
26
|
+
result = {}.tap do |result|
|
27
|
+
collector = lambda { |text, key| result[key] = text }
|
28
|
+
each_string_entry(doc, &collector)
|
29
|
+
each_array_entry(doc, &collector)
|
30
|
+
each_plural_entry(doc, &collector)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def parse_xml(xml_content)
|
37
|
+
Nokogiri::XML(xml_content) do |config|
|
38
|
+
config.options = Nokogiri::XML::ParseOptions::NONET
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def each_string_entry(doc)
|
43
|
+
doc.xpath('//string').each do |node|
|
44
|
+
yield(
|
45
|
+
text_from(node),
|
46
|
+
name_from(node)
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def each_array_entry(doc)
|
52
|
+
doc.xpath('//string-array').each do |array|
|
53
|
+
prefix = name_from(array)
|
54
|
+
|
55
|
+
array.xpath('item').each_with_index do |item, idx|
|
56
|
+
yield(
|
57
|
+
text_from(item),
|
58
|
+
"#{prefix}.#{idx}"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_plural_entry(doc)
|
65
|
+
doc.xpath('//plurals').each do |plurals|
|
66
|
+
prefix = name_from(plurals)
|
67
|
+
|
68
|
+
plurals.xpath('item').each do |item|
|
69
|
+
quantity = item.attributes['quantity'].value
|
70
|
+
|
71
|
+
yield(
|
72
|
+
text_from(item),
|
73
|
+
"#{prefix}.#{quantity}"
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def text_from(node)
|
80
|
+
builder = Nokogiri::XML::Builder.new do |builder|
|
81
|
+
builder.root do
|
82
|
+
node.children.each do |child|
|
83
|
+
serialize(child, builder)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# safe to call `strip` after `to_xml` because any string that
|
89
|
+
# needs leading or trailing whitespace preserved should be wrapped
|
90
|
+
# in double quotes
|
91
|
+
unescape(
|
92
|
+
strip_enclosing_quotes(
|
93
|
+
builder.doc.xpath('/root/node()').to_xml.strip
|
94
|
+
)
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def serialize(node, builder)
|
99
|
+
if node.text?
|
100
|
+
builder.text(unescape(node.text))
|
101
|
+
else
|
102
|
+
builder.send("#{node.name}_", node.attributes) do
|
103
|
+
node.children.each do |child|
|
104
|
+
serialize(child, builder)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def name_from(node)
|
111
|
+
if attribute = node.attributes['name']
|
112
|
+
attribute.value
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def unescape(text)
|
117
|
+
text = text
|
118
|
+
.gsub("\\'", "'")
|
119
|
+
.gsub('\\"', '"')
|
120
|
+
.gsub("\\n", "\n")
|
121
|
+
.gsub("\\r", "\r")
|
122
|
+
.gsub("\\t", "\t")
|
123
|
+
|
124
|
+
coder.decode(text)
|
125
|
+
end
|
126
|
+
|
127
|
+
def coder
|
128
|
+
@coder ||= HTMLEntities::AndroidXmlDecoder.new
|
129
|
+
end
|
130
|
+
|
131
|
+
def strip_enclosing_quotes(text)
|
132
|
+
quote = case text[0]
|
133
|
+
when "'", '"'
|
134
|
+
text[0]
|
135
|
+
end
|
136
|
+
|
137
|
+
if quote
|
138
|
+
text.gsub(/\A#{quote}(.*)#{quote}\z/) { $1 }
|
139
|
+
else
|
140
|
+
text
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Any2Tmx
|
4
|
+
class Options
|
5
|
+
attr_reader :errors
|
6
|
+
|
7
|
+
def initialize(executable_name)
|
8
|
+
@options = {}
|
9
|
+
@errors = []
|
10
|
+
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: #{executable_name} [options]"
|
13
|
+
|
14
|
+
opts.on('-s', '--source [file]:[locale]', 'File containing phrases in the source locale (locale appended with colon).') do |source|
|
15
|
+
file, locale = source.split(':')
|
16
|
+
@options[:source] = file
|
17
|
+
@options[:source_locale] = locale
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on('-t', '--target [file]:[locale]', 'File containing translations in the target locale (locale appended with colon).') do |target|
|
21
|
+
file, locale = target.split(':')
|
22
|
+
@options[:target] = file
|
23
|
+
@options[:target_locale] = locale
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('-o', '--output [file]', 'The TMX output file to write. If not specified, output is printed to stdout.') do |output|
|
27
|
+
@options[:output] = output
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-h', '--help', 'Prints this help message.') do
|
31
|
+
@options[:help] = true
|
32
|
+
end
|
33
|
+
|
34
|
+
@opts = opts
|
35
|
+
|
36
|
+
# give derived classes the opportunity to add additional options
|
37
|
+
yield opts if block_given?
|
38
|
+
end.parse!
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid?
|
42
|
+
errors.clear
|
43
|
+
validate
|
44
|
+
errors.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
def source
|
48
|
+
@options[:source]
|
49
|
+
end
|
50
|
+
|
51
|
+
def target
|
52
|
+
@options[:target]
|
53
|
+
end
|
54
|
+
|
55
|
+
def source_locale
|
56
|
+
@options[:source_locale]
|
57
|
+
end
|
58
|
+
|
59
|
+
def target_locale
|
60
|
+
@options[:target_locale]
|
61
|
+
end
|
62
|
+
|
63
|
+
def output
|
64
|
+
@options[:output]
|
65
|
+
end
|
66
|
+
|
67
|
+
def help?
|
68
|
+
@options[:help]
|
69
|
+
end
|
70
|
+
|
71
|
+
def print_help
|
72
|
+
puts @opts
|
73
|
+
end
|
74
|
+
|
75
|
+
# allow derived classes to add additional validation routines
|
76
|
+
def before_validate
|
77
|
+
end
|
78
|
+
|
79
|
+
def after_validate
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def validate
|
85
|
+
unless help?
|
86
|
+
before_validate
|
87
|
+
validate_source
|
88
|
+
validate_target
|
89
|
+
validate_output
|
90
|
+
after_validate
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def validate_source
|
95
|
+
unless File.exist?(source)
|
96
|
+
errors << 'Source file does not exist.'
|
97
|
+
end
|
98
|
+
|
99
|
+
unless source_locale
|
100
|
+
errors << 'Source locale not provided. Try running with the -h option for usage details.'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate_target
|
105
|
+
unless File.exist?(target)
|
106
|
+
errors << 'Target file does not exist.'
|
107
|
+
end
|
108
|
+
|
109
|
+
unless target_locale
|
110
|
+
errors << 'Target locale not provided. Try running with the -h option for usage details.'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate_output
|
115
|
+
unless File.exist?(File.dirname(target))
|
116
|
+
errors << 'Output path does not exist.'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Any2Tmx
|
2
|
+
class PhraseSet
|
3
|
+
attr_reader :traversable, :locale
|
4
|
+
|
5
|
+
def initialize(traversable, locale)
|
6
|
+
@traversable = traversable
|
7
|
+
@locale = locale
|
8
|
+
end
|
9
|
+
|
10
|
+
def zip(other_set, &block)
|
11
|
+
traversable.zip(other_set.traversable, &block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'xml-write-stream'
|
2
|
+
|
3
|
+
module Any2Tmx
|
4
|
+
class TmxWriter
|
5
|
+
class << self
|
6
|
+
def write(trans_map, source_locale, target_locale, io)
|
7
|
+
writer = XmlWriteStream.from_stream(io)
|
8
|
+
writer.open_tag('tmx', version: '1.4')
|
9
|
+
writer.open_single_line_tag('header', srclang: source_locale, datatype: 'plaintext', segtype: 'paragraph')
|
10
|
+
writer.close_tag
|
11
|
+
|
12
|
+
writer.open_tag('body')
|
13
|
+
|
14
|
+
trans_map.each_pair do |source_phrase, target_phrase|
|
15
|
+
writer.open_tag('tu')
|
16
|
+
writer.open_tag('tuv', 'xml:lang' => source_locale)
|
17
|
+
writer.open_single_line_tag('seg')
|
18
|
+
writer.write_text(source_phrase)
|
19
|
+
writer.close_tag # seg
|
20
|
+
writer.close_tag # tuv
|
21
|
+
|
22
|
+
writer.open_tag('tuv', 'xml:lang' => target_locale)
|
23
|
+
writer.open_single_line_tag('seg')
|
24
|
+
writer.write_text(target_phrase)
|
25
|
+
writer.close_tag # seg
|
26
|
+
writer.close_tag # tuv
|
27
|
+
writer.close_tag # tu
|
28
|
+
end
|
29
|
+
|
30
|
+
writer.close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Any2Tmx
|
2
|
+
module Transforms
|
3
|
+
class AndroidTransform < Transform
|
4
|
+
private
|
5
|
+
|
6
|
+
def load(file, locale)
|
7
|
+
phrases = Any2Tmx::AndroidXmlParser.parse(File.read(file))
|
8
|
+
traversable = Any2Tmx::Traversable.new(phrases)
|
9
|
+
Any2Tmx::PhraseSet.new(traversable, locale)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Any2Tmx
|
4
|
+
module Transforms
|
5
|
+
class JsonTransform < Transform
|
6
|
+
private
|
7
|
+
|
8
|
+
def load(file, locale)
|
9
|
+
phrases = JSON.parse(File.read(file))
|
10
|
+
traversable = Any2Tmx::Traversable.new(phrases)
|
11
|
+
Any2Tmx::PhraseSet.new(traversable, locale)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Any2Tmx
|
2
|
+
module Transforms
|
3
|
+
class Result
|
4
|
+
attr_reader :source, :target, :collection, :processed_count
|
5
|
+
|
6
|
+
def initialize(source, target, collection, processed_count)
|
7
|
+
@source = source
|
8
|
+
@target = target
|
9
|
+
@collection = collection
|
10
|
+
@processed_count = processed_count
|
11
|
+
end
|
12
|
+
|
13
|
+
def source_phrase_count
|
14
|
+
source.traversable.size
|
15
|
+
end
|
16
|
+
|
17
|
+
def write(io)
|
18
|
+
Any2Tmx::TmxWriter.write(
|
19
|
+
collection, source.locale, target.locale, io
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Any2Tmx
|
2
|
+
module Transforms
|
3
|
+
class Transform
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def result
|
11
|
+
source = load(options.source, options.source_locale)
|
12
|
+
target = load(options.target, options.target_locale)
|
13
|
+
count = 0
|
14
|
+
zipped = source.zip(target) { count += 1 }
|
15
|
+
Result.new(source, target, zipped, count)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def load(file, locale)
|
21
|
+
raise NotImplementedError,
|
22
|
+
"#{__method__} must be defined in derived classes"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Any2Tmx
|
4
|
+
module Transforms
|
5
|
+
class YamlTransform < Transform
|
6
|
+
private
|
7
|
+
|
8
|
+
def load(file, locale)
|
9
|
+
phrases = YAML.load_file(file)
|
10
|
+
|
11
|
+
if phrases.include?(locale)
|
12
|
+
phrases = phrases[locale]
|
13
|
+
end
|
14
|
+
|
15
|
+
traversable = Any2Tmx::Traversable.new(phrases)
|
16
|
+
Any2Tmx::PhraseSet.new(traversable, locale)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Any2Tmx
|
2
|
+
module Transforms
|
3
|
+
autoload :AndroidTransform, 'any2tmx/transforms/android_transform'
|
4
|
+
autoload :JsonTransform, 'any2tmx/transforms/json_transform'
|
5
|
+
autoload :Transform, 'any2tmx/transforms/transform'
|
6
|
+
autoload :Result, 'any2tmx/transforms/result'
|
7
|
+
autoload :YamlTransform, 'any2tmx/transforms/yaml_transform'
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Any2Tmx
|
2
|
+
class Traversable
|
3
|
+
attr_reader :collection
|
4
|
+
|
5
|
+
def initialize(collection)
|
6
|
+
@collection = collection
|
7
|
+
end
|
8
|
+
|
9
|
+
def each_entry(&block)
|
10
|
+
if block_given?
|
11
|
+
each_entry_helper(collection, [], &block)
|
12
|
+
else
|
13
|
+
to_enum(__method__)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def size
|
18
|
+
each_entry.inject(0) { |ret, _| ret + 1 }
|
19
|
+
end
|
20
|
+
|
21
|
+
def dig(path)
|
22
|
+
path.inject(:start) do |ret, seg|
|
23
|
+
if ret == :start
|
24
|
+
collection[seg]
|
25
|
+
elsif ret
|
26
|
+
if seg.is_a?(Numeric) && ret.is_a?(Array)
|
27
|
+
ret[seg] # array index case
|
28
|
+
elsif seg.is_a?(String) && ret.is_a?(Hash)
|
29
|
+
ret[seg] # hash key case
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def zip(other_traversable)
|
36
|
+
each_entry.each_with_object({}) do |(entry, path), ret|
|
37
|
+
other_entry = other_traversable.dig(path)
|
38
|
+
ret[entry] = other_entry if other_entry
|
39
|
+
yield if block_given? && other_entry
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def each_entry_helper(coll, path, &block)
|
46
|
+
case coll
|
47
|
+
when Hash
|
48
|
+
coll.each_pair do |key, value|
|
49
|
+
each_entry_helper(value, path + [key], &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
when Array
|
53
|
+
coll.each_with_index do |element, idx|
|
54
|
+
each_entry_helper(element, path + [idx], &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
when String
|
58
|
+
yield coll, path
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/any2tmx.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
module Any2Tmx
|
2
|
+
autoload :AndroidXmlParser, 'any2tmx/android_xml_parser'
|
3
|
+
autoload :Options, 'any2tmx/options'
|
4
|
+
autoload :PhraseSet, 'any2tmx/phrase_set'
|
5
|
+
autoload :TmxWriter, 'any2tmx/tmx_writer'
|
6
|
+
autoload :Transforms, 'any2tmx/transforms'
|
7
|
+
autoload :Traversable, 'any2tmx/traversable'
|
8
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: any2tmx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cameron Dutro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: xml-write-stream
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: htmlentities
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.3'
|
55
|
+
description: A command-line tool to convert certain file types to the standard TMX
|
56
|
+
format for translation memories.
|
57
|
+
email:
|
58
|
+
- camertron@gmail.com
|
59
|
+
executables:
|
60
|
+
- yaml2tmx
|
61
|
+
- json2tmx
|
62
|
+
- android2tmx
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- Gemfile
|
67
|
+
- History.txt
|
68
|
+
- Rakefile
|
69
|
+
- any2tmx.gemspec
|
70
|
+
- bin/android2tmx
|
71
|
+
- bin/json2tmx
|
72
|
+
- bin/yaml2tmx
|
73
|
+
- lib/any2tmx.rb
|
74
|
+
- lib/any2tmx/android_xml_parser.rb
|
75
|
+
- lib/any2tmx/options.rb
|
76
|
+
- lib/any2tmx/phrase_set.rb
|
77
|
+
- lib/any2tmx/tmx_writer.rb
|
78
|
+
- lib/any2tmx/transforms.rb
|
79
|
+
- lib/any2tmx/transforms/android_transform.rb
|
80
|
+
- lib/any2tmx/transforms/json_transform.rb
|
81
|
+
- lib/any2tmx/transforms/result.rb
|
82
|
+
- lib/any2tmx/transforms/transform.rb
|
83
|
+
- lib/any2tmx/transforms/yaml_transform.rb
|
84
|
+
- lib/any2tmx/traversable.rb
|
85
|
+
- lib/any2tmx/version.rb
|
86
|
+
homepage: http://github.com/camertron/any2tmx
|
87
|
+
licenses: []
|
88
|
+
metadata: {}
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 2.2.3
|
106
|
+
signing_key:
|
107
|
+
specification_version: 4
|
108
|
+
summary: A command-line tool to convert certain file types to the standard TMX format
|
109
|
+
for translation memories.
|
110
|
+
test_files: []
|
111
|
+
has_rdoc: true
|