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,55 +1,50 @@
1
- require 'kramdown'
2
- require 'reverse_markdown'
3
-
4
- module Translatomatic::ResourceFile
5
- # Markdown resource file
6
- class Markdown < HTML
7
-
8
- # (see Translatomatic::ResourceFile::Base.extensions)
9
- def self.extensions
10
- %w{md}
11
- end
12
-
13
- # (see Translatomatic::ResourceFile::Base#save)
14
- def save(target = path, options = {})
15
- if @doc
16
- begin
17
- add_created_by unless options[:no_created_by]
18
- html = @doc.to_html
19
- # convert html back to markdown
20
- markdown = ReverseMarkdown.convert(html, unknown_tags: :bypass)
21
- target.write(markdown.chomp)
22
- rescue Exception => e
23
- log.error t("resource.error", message: e.message)
24
- end
25
- end
26
- end
27
-
28
- private
29
-
30
- def add_created_by
31
- @created_by ||= begin
32
- body = @doc.at('body')
33
- body.add_child("<p><i>#{created_by}</i></p>")
34
- end
35
- end
36
-
37
- def read(path)
38
- begin
39
- # read markdown and convert to html
40
- markdown = path.read
41
- html = Kramdown::Document.new(markdown).to_html
42
- # parse html with nokogiri
43
- @doc = Nokogiri::HTML(html) do |config|
44
- config.noblanks
45
- end
46
- init_nodemap(@doc)
47
- rescue Exception => e
48
- log.error t("resource.error", message: e.message)
49
- @valid = false
50
- {}
51
- end
52
- end
53
-
54
- end # class
55
- end # module
1
+ require 'kramdown'
2
+ require 'reverse_markdown'
3
+
4
+ module Translatomatic::ResourceFile
5
+ # Markdown resource file
6
+ class Markdown < HTML
7
+
8
+ # (see Translatomatic::ResourceFile::Base.extensions)
9
+ def self.extensions
10
+ %w{md}
11
+ end
12
+
13
+ # (see Translatomatic::ResourceFile::Base#save)
14
+ def save(target = path, options = {})
15
+ if @doc
16
+ add_created_by unless options[:no_created_by]
17
+ html = @doc.to_html
18
+ # convert html back to markdown
19
+ markdown = ReverseMarkdown.convert(html, unknown_tags: :bypass)
20
+ target.write(markdown.chomp)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def add_created_by
27
+ @created_by ||= begin
28
+ body = @doc.at('body')
29
+ body.add_child("<p><i>#{created_by}</i></p>")
30
+ end
31
+ end
32
+
33
+ def read_doc
34
+ # read markdown and convert to html
35
+ markdown = read_contents(@path)
36
+ if markdown.blank?
37
+ empty_doc
38
+ else
39
+ html = Kramdown::Document.new(markdown).to_html
40
+ # parse html with nokogiri
41
+ doc = Nokogiri::HTML(html) do |config|
42
+ config.noblanks
43
+ end
44
+ parse_error(doc.errors[0]) if doc.errors.present?
45
+ doc
46
+ end
47
+ end
48
+
49
+ end # class
50
+ end # module
@@ -1,19 +1,153 @@
1
- module Translatomatic::ResourceFile
2
- # Property list resource file
3
- # @see https://en.wikipedia.org/wiki/Property_list
4
- class Plist < XML
5
- include Translatomatic::ResourceFile::XCodeStringsLocalePath
6
-
7
- # (see Translatomatic::ResourceFile::Base.extensions)
8
- def self.extensions
9
- %w{plist}
10
- end
11
-
12
- private
13
-
14
- def text_nodes_xpath
15
- '//*[not(self::key)]/text()'
16
- end
17
-
18
- end # class
19
- end # module
1
+ module Translatomatic::ResourceFile
2
+ # Property list resource file
3
+ # @see https://en.wikipedia.org/wiki/Property_list
4
+ class Plist < XML
5
+ # property list types:
6
+ # array, dict, string, data, date, integer, real, boolean
7
+ # boolean is <true /> or <false />
8
+
9
+ include Translatomatic::ResourceFile::XCodeStringsLocalePath
10
+
11
+ # (see Translatomatic::ResourceFile::Base.extensions)
12
+ def self.extensions
13
+ %w{plist}
14
+ end
15
+
16
+ # (see Translatomatic::ResourceFile::Base.is_key_value?)
17
+ def self.is_key_value?
18
+ true
19
+ end
20
+
21
+ private
22
+
23
+ def init_nodemap
24
+ result = Parser.new.parse(@doc)
25
+ #puts "parser result:"
26
+ #p result
27
+ @flattened_data = flatten(result)
28
+ @nodemap = @flattened_data.transform_values { |i| i.node }
29
+ #puts "nodemap:"
30
+ #p @nodemap
31
+ end
32
+
33
+ def init_properties
34
+ @properties = @flattened_data.transform_values { |i| i.content }
35
+ end
36
+
37
+ def create_node(key, value)
38
+ # add properties to first dict found
39
+ dict = @doc.xpath("//dict")
40
+ # TODO: not sure sure what to do if dict is missing
41
+ raise "missing top level dictionary" unless dict.present?
42
+ dict = dict[0]
43
+
44
+ # add xml: <data name="key"><value>value</value></data>
45
+ key_node = Nokogiri::XML::Node.new("key", @doc)
46
+ key_node.content = key
47
+ value_node = Nokogiri::XML::Node.new("string", @doc)
48
+ value_node.content = value
49
+ dict.add_child(key_node)
50
+ dict.add_child(value_node)
51
+
52
+ @nodemap[key] = value_node
53
+ @properties[key] = value
54
+ end
55
+
56
+ def empty_doc
57
+ Nokogiri::XML(EMPTY_DOC)
58
+ end
59
+
60
+ EMPTY_DOC=<<EOM
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">
64
+ <dict>
65
+ </dict>
66
+ </plist>
67
+ EOM
68
+
69
+ PlistNode = Struct.new(:node, :content) do
70
+ def inspect
71
+ "PlistNode:#{content}"
72
+ end
73
+ end
74
+
75
+ # Adapted from nokogiri-plist parser
76
+ # @see https://github.com/caseyhoward/nokogiri-plist
77
+ class Parser
78
+
79
+ def parse(xml, options = {})
80
+ @converters = {
81
+ 'integer' => Proc.new { |node| node.content.to_i },
82
+ 'real' => Proc.new { |node| node.content.to_f },
83
+ 'string' => Proc.new { |node| node.content.to_s },
84
+ # DateTime.parse(node.content)
85
+ 'date' => Proc.new { |node| node.content.to_s },
86
+ 'true' => Proc.new { |node| true },
87
+ 'false' => Proc.new { |node| false },
88
+ 'dict' => Proc.new { |node| parse_dict(node) },
89
+ 'array' => Proc.new { |node| parse_array(node) },
90
+ 'data' => Proc.new { |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
+
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
+
105
+ def valid_type?(type)
106
+ @converters.has_key? type
107
+ end
108
+
109
+ def valid_node?(node)
110
+ valid_type?(node.name) or node.name == "key"
111
+ end
112
+
113
+ def parse_dict(node)
114
+ node.xpath('./key').inject(@dict_class.new) do |result, key_node|
115
+ plist_node = parse_value_node(next_valid_sibling key_node)
116
+ value = plist_node_value(plist_node)
117
+ result[key_node.content] = value
118
+ result
119
+ end
120
+ end
121
+
122
+ # if the PlistNode value is an array or hash, use that directly
123
+ # instead of the PlistNode.
124
+ def plist_node_value(plist_node)
125
+ content = plist_node.content
126
+ if content.kind_of?(Array) || content.kind_of?(Hash)
127
+ content
128
+ else
129
+ plist_node
130
+ end
131
+ end
132
+
133
+ def parse_array(node)
134
+ node.children.inject([]) do |result, child|
135
+ if valid_node?(child)
136
+ plist_node = parse_value_node(child)
137
+ result << plist_node_value(plist_node)
138
+ end
139
+ result
140
+ end
141
+ end
142
+
143
+ def next_valid_sibling(node)
144
+ until node.nil? or valid_type? node.name
145
+ node = node.next_sibling
146
+ end
147
+ node
148
+ end
149
+
150
+ end
151
+
152
+ end # class
153
+ end # module
@@ -0,0 +1,107 @@
1
+ require 'poparser'
2
+
3
+ module Translatomatic::ResourceFile
4
+ # Property list resource file
5
+ # @see https://en.wikipedia.org/wiki/Property_list
6
+ class PO < Base
7
+
8
+ # (see Translatomatic::ResourceFile::Base.extensions)
9
+ def self.extensions
10
+ %w{po pot}
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#set)
19
+ def set(key, value)
20
+ super(key, value)
21
+
22
+ unless @pomap.include?(key)
23
+ # new key, create po entry
24
+ po << {
25
+ msgid: key,
26
+ msgstr: value
27
+ }
28
+ entry = po.entries[-1]
29
+ add_entry(entry, :msgid, 0)
30
+ else
31
+ po_property = @pomap[key]
32
+ entry = po_property.entry
33
+ if entry.plural?
34
+ msgstr = entry.msgstr || []
35
+ msgstr[po_property.index] = value
36
+ entry.msgstr = msgstr
37
+ else
38
+ entry.msgstr = value
39
+ end
40
+ end
41
+ end
42
+
43
+ # (see Translatomatic::ResourceFile::Base#save)
44
+ def save(target = path, options = {})
45
+ if @po
46
+ add_created_by unless options[:no_created_by]
47
+ target.write(@po.to_s)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ class PoProperty
54
+ attr_reader :entry
55
+ attr_reader :value_index
56
+
57
+ def initialize(entry, value_index)
58
+ @entry = entry
59
+ @value_index = value_index
60
+ end
61
+
62
+ def value
63
+ if entry.plural?
64
+ entry.msgstr[value_index]
65
+ else
66
+ entry.msgstr
67
+ end
68
+ end
69
+ end
70
+
71
+ def init
72
+ @po = PoParser.parse('')
73
+ @pomap = {}
74
+ end
75
+
76
+ def load
77
+ content = read_contents(@path)
78
+ @po = PoParser.parse(content)
79
+ @pomap = init_pomap(@po)
80
+ @properties = pomap_to_properties
81
+ end
82
+
83
+ def add_created_by
84
+ # TODO
85
+ end
86
+
87
+ # create mapping from key to PoProperty
88
+ def init_pomap(po)
89
+ pomap = {}
90
+ po.entries.each do |entry|
91
+ add_entry(entry, :msgid, 0)
92
+ add_entry(entry, :msgid_plural, 1) if entry.plural?
93
+ end
94
+ pomap
95
+ end
96
+
97
+ def pomap_to_properties
98
+ @pomap.transform_values { |i| i.value }
99
+ end
100
+
101
+ def add_entry(entry, key, index)
102
+ untranslated = entry.send(key)
103
+ @pomap[untranslated] = PoProperty.new(entry, index) if untranslated
104
+ end
105
+
106
+ end # class
107
+ end # module
@@ -1,90 +1,91 @@
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#initialize)
14
- def initialize(path, locale = nil)
15
- super(path, locale)
16
- @valid = true
17
- @properties = @path.exist? ? read(@path) : {}
18
- end
19
-
20
- # (see Translatomatic::ResourceFile::Base#save)
21
- def save(target = path, options = {})
22
- out = ""
23
- out += add_created_by unless options[:no_created_by]
24
- properties.each do |key, value|
25
- # TODO: maintain original line ending format?
26
- value = value.gsub("\n", "\\n") if value # convert newlines to \n
27
- out += "#{key} = #{value}\n"
28
- end
29
- # escape unicode characters
30
- out = Translatomatic::EscapedUnicode.escape(out)
31
- target.write(out)
32
- end
33
-
34
- # (see Translatomatic::ResourceFile::Base#supports_variable_interpolation?)
35
- def supports_variable_interpolation?
36
- true
37
- end
38
-
39
- # (see Translatomatic::ResourceFile::Base#create_variable)
40
- def create_variable(name)
41
- return "{#{name}}"
42
- end
43
-
44
- # (see Translatomatic::ResourceFile::Base#variable_regex)
45
- def variable_regex
46
- /\{.*?\}/
47
- end
48
-
49
- private
50
-
51
- def add_created_by
52
- comment(created_by)
53
- end
54
-
55
- def comment(text)
56
- "# #{text}\n"
57
- end
58
-
59
- # parse key = value property file
60
- def read(path)
61
- contents = path.read
62
- # convert escaped unicode characters into unicode
63
- contents = Translatomatic::EscapedUnicode.unescape(contents)
64
- result = {}
65
- contents.gsub!(/\\\s*\n\s*/m, '') # put multi line strings on one line
66
- lines = contents.split("\n")
67
-
68
- lines.each do |line|
69
- line.strip!
70
- next if line.length == 0
71
- equal_idx = line.index("=")
72
- colon_idx = line.index(":")
73
-
74
- if line[0] == ?! || line[0] == ?#
75
- # comment
76
- # TODO: translate comments or keep originals?
77
- next
78
- elsif equal_idx.nil? && colon_idx.nil?
79
- @valid = false
80
- return {}
81
- end
82
- name, value = line.split(/\s*[=:]\s*/, 2)
83
- value = value.gsub("\\n", "\n") # convert \n to newlines
84
- result[name] = value
85
- end
86
- result
87
- end
88
-
89
- end
90
- end
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"
31
+ 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
+
42
+ # (see Translatomatic::ResourceFile::Base#variable_regex)
43
+ def variable_regex
44
+ /\{.*?\}/
45
+ end
46
+
47
+ private
48
+
49
+ def load
50
+ @properties = read_properties
51
+ end
52
+
53
+ def add_created_by
54
+ comment(created_by)
55
+ end
56
+
57
+ def comment(text)
58
+ "# #{text}\n"
59
+ end
60
+
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")
68
+
69
+ lines.each do |line|
70
+ line.strip!
71
+ next if line.length == 0
72
+ equal_idx = line.index("=")
73
+ colon_idx = line.index(":")
74
+
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 {}
82
+ end
83
+ name, value = line.split(/\s*[=:]\s*/, 2)
84
+ value = value.gsub("\\n", "\n") # convert \n to newlines
85
+ result[name] = value
86
+ end
87
+ result
88
+ end
89
+
90
+ end
91
+ end
@@ -1,31 +1,51 @@
1
- module Translatomatic::ResourceFile
1
+ module Translatomatic::ResourceFile
2
2
  # Windows resources file (XML)
3
- class RESW < XML
4
-
5
- # (see Translatomatic::ResourceFile::Base.extensions)
6
- def self.extensions
7
- %w{resw}
8
- end
9
-
10
- # (see Translatomatic::ResourceFile::Base#locale_path)
11
- def locale_path(locale)
12
- # e.g. strings/en-US/resources.resw
13
- dir = path.dirname
14
- dir.parent + locale.to_s + path.basename
15
- end
16
-
17
- private
18
-
19
- def create_nodemap(doc)
20
- result = {}
21
- key_values = doc.search('//data/@name|//text()')
22
- key_values.each_slice(2) do |key, value|
23
- key = key.value
24
- value = value
25
- result[key] = value
26
- end
27
- result
28
- end
29
-
30
- end # class
31
- end # module
3
+ class RESW < XML
4
+
5
+ # (see Translatomatic::ResourceFile::Base.extensions)
6
+ def self.extensions
7
+ %w{resw resx}
8
+ end
9
+
10
+ # (see Translatomatic::ResourceFile::Base.is_key_value?)
11
+ def self.is_key_value?
12
+ true
13
+ end
14
+
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
21
+
22
+ private
23
+
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
31
+ end
32
+ @nodemap = result
33
+ end
34
+
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)
43
+
44
+ @doc.root.add_child(data_node)
45
+
46
+ @nodemap[key] = text_node
47
+ @properties[key] = value
48
+ end
49
+
50
+ end # class
51
+ end # module