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