i18n-translators-tools 0.2.3 → 0.2.4

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.
data/README.md CHANGED
@@ -2,7 +2,10 @@ I18n translation and locales management utility
2
2
  ===============================================
3
3
 
4
4
  This package brings you useful utility and library which can help you to handle
5
- locale files and translations in your Ruby projects.
5
+ locale files and translations in your Ruby projects. It is build upon i18n
6
+ library and extends it's simple format so you can simply track field changes
7
+ or keep translator's notes. Conversion back to simple format is possible and as
8
+ simple as call 'i18n-translate strip'.
6
9
 
7
10
 
8
11
  Interesting features
@@ -11,6 +14,8 @@ Interesting features
11
14
  * no database required
12
15
  * merging and changes propagation (adding, removing and changed default text)
13
16
  keeping default file untouched
17
+ * hard/soft merging (hard deletes extra keys from target, soft set them
18
+ obsolete; default is soft)
14
19
  * creating new locale file based on default file
15
20
  * converting from one format to another (yml <=> rb <=> po <=> ts <=> properties)
16
21
  * statistics
@@ -275,7 +280,7 @@ New format looks like:
275
280
  file: "file parsed from reference"
276
281
  line: "line parsed from po reference"
277
282
  translation: "translation itself"
278
- flag: "one of (ok || incomplete || changed || untranslated)"
283
+ flag: "one of (ok || incomplete || changed || untranslated || obsolete)"
279
284
  fuzzy: true # exists only where flag != ok (nice to have when you want
280
285
  edit files manually)
281
286
 
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/testtask'
8
8
  require 'rake/gempackagetask'
9
9
  require 'rake/clean'
10
10
 
11
- CLEAN << "coverage" << "pkg" << "README.html" << "CHANGELOG.html"
11
+ CLEAN << "coverage" << "pkg" << "README.html" << "CHANGELOG.html" << '*.rbc'
12
12
 
13
13
  task :default => [:test, :doc, :gem]
14
14
  Rake::TestTask.new(:test) do |t|
@@ -23,6 +23,12 @@ task :rcov do |t|
23
23
  system "rcov --exclude .rvm,lib/ruby --sort coverage --text-summary --text-coverage-diff -o coverage test/all.rb"
24
24
  end
25
25
 
26
+ desc "Benchmark processors"
27
+ task :bench do |t|
28
+ system "ruby benchmark/gettext_scan.rb"
29
+ system "ruby benchmark/gettext_reg.rb"
30
+ end
31
+
26
32
  begin
27
33
  require 'bluecloth'
28
34
 
@@ -47,5 +53,5 @@ begin
47
53
  build_document("CHANGELOG.md")
48
54
  end
49
55
 
50
- rescue
56
+ rescue LoadError
51
57
  end
data/bin/i18n-translate CHANGED
@@ -51,6 +51,8 @@ options:
51
51
  --quiet, -q -- show less information
52
52
  --verbose, -v -- shows more information during
53
53
  locales processing
54
+ --hard, -h -- deletes from target files all missing keys in default
55
+ normaly those keys are kept but flag is set to obsolete
54
56
 
55
57
 
56
58
  CONFIG FILE
@@ -184,7 +186,8 @@ opts = GetoptLong.new(
184
186
  ["--encoding", "-e", GetoptLong::REQUIRED_ARGUMENT],
185
187
  ["--verbose", "-v", GetoptLong::NO_ARGUMENT],
186
188
  ["--quiet", "-q", GetoptLong::NO_ARGUMENT],
187
- ["--locale", "-l", GetoptLong::REQUIRED_ARGUMENT]
189
+ ["--locale", "-l", GetoptLong::REQUIRED_ARGUMENT],
190
+ ["--hard", "-h", GetoptLong::NO_ARGUMENT]
188
191
  )
189
192
 
190
193
 
@@ -214,6 +217,8 @@ opts.each do |opt, val|
214
217
  tmp[optkey] = val
215
218
  when :deep
216
219
  tmp[optkey] = val
220
+ when :hard
221
+ tmp[:merge] = "hard"
217
222
  when :verbose
218
223
  tmp[:verbose] = true
219
224
  tmp[:quiet] = false
@@ -13,9 +13,9 @@ spec = Gem::Specification.new do |s|
13
13
  s.email = "pejuko@gmail.com"
14
14
  s.authors = ["Petr Kovar"]
15
15
  s.name = 'i18n-translators-tools'
16
- s.version = '0.2.3'
16
+ s.version = '0.2.4'
17
17
  s.date = Time.now.strftime("%Y-%m-%d")
18
- s.add_dependency('i18n', '>= 0.4.1')
18
+ s.add_dependency('i18n', '>= 0.5.0')
19
19
  s.add_dependency('ya2yaml')
20
20
  s.require_path = 'lib'
21
21
  s.files = ["bin/i18n-translate", "README.md", "i18n-translators-tools.gemspec", "Rakefile"]
@@ -37,7 +37,7 @@ Supported formats:
37
37
 
38
38
  Backends:
39
39
  * Extended format. i18n-translators-tools brings extended format
40
- I18n::Backend::Simple.send(:include, I18n::Backend::Translator)
40
+ I18n::Backend::Simple.send(:include, I18n::Backend::Translate)
41
41
  * Gettext po
42
42
  I18n::Backend::Simple.send(:include, I18n::Backend::PO)
43
43
  * QT Linguist TS
@@ -64,9 +64,19 @@ Changelog:
64
64
  v0.2.3
65
65
  * fix: hash_to_keys can work with enhanced format => default can be in
66
66
  enchanced format
67
+ * default file can be now in enhanced format. if translation field is missing
67
68
  * i18n-translate <source file> <target file>
68
69
  automaticlay perform convert action from one file to another
69
70
 
71
+ v0.2.4
72
+ * enhanced support for java properties
73
+ * hard/soft merges. hard deletes deleted keys in target and soft set them to
74
+ obsolete
75
+ * processors now generate keys list from provided data and not from @tr.default
76
+ * flag obsolete added
77
+ * delete function
78
+ * i18n-0.5.0 compatibility (for older i18n user v0.2.3)
79
+
70
80
  For more information read README.md and CHANGELOG.md
71
81
 
72
82
  -----------------------------------------------------------------------------
@@ -77,10 +87,13 @@ http://github.com/pejuko/i18n-translators-tools
77
87
  EOF
78
88
  s.description = <<EOF
79
89
  This package brings you useful utility and library which can help you to handle
80
- locale files and translations in your Ruby projects. Offers also built-in simple
81
- console editor. Supported formats are YAML, Ruby, Gettext po, QT Linguist TS and
82
- Java Properties. Read README.md file and run i18n-translate without parameters
83
- for more information.
90
+ locale files and translations in your Ruby projects.
91
+ It is build upon i18n library and extends it's simple format so you can simply
92
+ track field changes or keep translator's notes. Conversion back to simple format
93
+ is possible and as simple as call 'i18n-translate strip'. Offers also built-in
94
+ simple console editor. Supported formats are YAML, Ruby, Gettext po,
95
+ QT Linguist TS and Java Properties. Read README.md file and run i18n-translate
96
+ without parameters for more information.
84
97
  EOF
85
98
  end
86
99
 
@@ -8,7 +8,7 @@ module I18n
8
8
  module Backend
9
9
  # It is highly recommended to use Translator wit Fallback plugin
10
10
  #
11
- # I18n::Backend::Simple.send(:include, I18n::Backend::Translator)
11
+ # I18n::Backend::Simple.send(:include, I18n::Backend::Translate)
12
12
  # I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
13
13
  #
14
14
  # notice that Translator have to be included BEFORE Fallback otherwise
@@ -28,7 +28,7 @@ module I18n
28
28
 
29
29
  return nil if tr.to_s.empty?
30
30
 
31
- values = options.except(*I18n::Backend::Base::RESERVED_KEYS)
31
+ values = options.except(*RESERVED_KEYS)
32
32
 
33
33
  tr = resolve(locale, key, tr, options)
34
34
  tr = interpolate(locale, tr, values) if values
@@ -121,7 +121,7 @@ module I18n::Translate
121
121
  # converts inspected string back into normal string
122
122
  def uninspect(str)
123
123
  return nil unless str
124
- str.gsub(%r!\\([\\#"abefnrstvx]|u\d{4}|u\{[^\}]+\}|\d{1,3}|x\d{1,2}|cx|C-[a-zA-Z]|M-[a-zA-Z])!) do |m|
124
+ str.gsub(%r!\\([\\#"abefnrstvx]|u\d{4}|u\{[^\}]+\}|\d{1,3}|x\d{1,2}|cx|C-[a-zA-Z]|M-[a-zA-Z]| |=|:)!) do |m|
125
125
  repl = ""
126
126
  if ['\\', '#', '"'].include?($1)
127
127
  repl = $1
@@ -117,7 +117,7 @@ module I18n::Translate::Processor
117
117
  def export(data)
118
118
  target = data[@translate.lang]
119
119
  str = ""
120
- keys = I18n::Translate.hash_to_keys(@translate.default).sort
120
+ keys = I18n::Translate.hash_to_keys(target).sort
121
121
 
122
122
  str << %~msgid ""\n~
123
123
  str << %~msgstr ""\n~
@@ -0,0 +1,190 @@
1
+ # -*- coding: utf-8 -*-
2
+ # vi: fenc=utf-8:expandtab:ts=2:sw=2:sts=2
3
+ #
4
+ # @author: Petr Kovar <pejuko@gmail.com>
5
+
6
+ require 'strscan'
7
+
8
+ module I18n::Translate::Processor
9
+
10
+ class GettextScanner < Template
11
+ FORMAT = ['po', 'pot']
12
+
13
+ protected
14
+
15
+ def import(data)
16
+ hash = {}
17
+
18
+ entry = {}
19
+ key = nil
20
+ last = nil
21
+
22
+
23
+ s = StringScanner.new(data)
24
+
25
+ until s.eos?
26
+
27
+ # empty line starts new entry
28
+ if s.scan /\n\s*\n/
29
+ if not entry.empty? and key
30
+ key = entry["default"].dup unless key
31
+ I18n::Translate.set(key, entry, hash, @translate.options[:separator])
32
+ end
33
+ key = last = nil
34
+ entry = {}
35
+ next
36
+ end
37
+
38
+ # skip end of line
39
+ if s.scan /\n/
40
+ next
41
+ end
42
+
43
+ # comments
44
+ if s.scan /#/
45
+ # translator's comment
46
+ if s.scan %r{\s+}
47
+ entry["comment"] = s.scan(/.*?$/).to_s.strip
48
+
49
+ # extracted comment
50
+ elsif s.scan %r{\.\s+}
51
+ entry["extracted_comment"] = s.scan(/.*?$/).to_s.strip
52
+
53
+ # reference
54
+ elsif s.scan %r{:\s+}
55
+ entry["reference"] = s.scan(/.*?$/).to_s.strip
56
+ if entry["reference"] =~ %r{(.*):(\d+)}
57
+ entry["file"] = $1.to_s.strip
58
+ entry["line"] = $2.to_s.strip
59
+ end
60
+
61
+ # flag
62
+ elsif s.scan %r{,\s+}
63
+ flags = s.scan(/.*?$/).split(",").compact.map{|x| x.strip}
64
+ fuzzy = flags.delete("fuzzy")
65
+ unless fuzzy
66
+ entry["flag"] = "ok"
67
+ else
68
+ flags.delete_if{|x| not I18n::Translate::FLAGS.include?(x)}
69
+ entry["flag"] = flags.first unless flags.empty?
70
+ entry["fuzzy"] = true
71
+ end
72
+
73
+ # old default
74
+ elsif s.scan %r{\| msgid\s+"}
75
+ match = s.scan(%r{.*"$}).to_s[0..-2]
76
+ entry["old_default"] = match
77
+ # expect that this entry has no key
78
+ # if does, will be overwriten later
79
+ key = entry["old_default"].dup
80
+ end
81
+ end # end of scan for comments
82
+
83
+ # key (context)
84
+ if s.scan %r{msgctxt\s+"}
85
+ key = get_string(s)
86
+ last = "key"
87
+
88
+ # default
89
+ elsif s.scan %r{msgid\s+"}
90
+ match = get_string(s)
91
+ if match.empty?
92
+ last = "po-header"
93
+ else
94
+ last = "default"
95
+ entry[last] = uninspect(match)
96
+ key = entry[last].dup unless key
97
+ end
98
+
99
+ # translation
100
+ elsif s.scan %r{msgstr\s+"}
101
+ match = get_string(s)
102
+ last = "translation" unless last == "po-header"
103
+ entry[last] = uninspect(match)
104
+
105
+ # string continuation
106
+ elsif s.scan %r{"}
107
+ match = get_string(s)
108
+ if last == "key"
109
+ key = "#{key}#{match}"
110
+ elsif last == "po-header"
111
+ case match
112
+ when %r{Content-Type: text/plain; charset=(.*)}
113
+ enc = uninspect($1.to_s).strip
114
+ @translate.options[:encoding] = enc unless enc.empty?
115
+ when %r{X-Language: (.*)}
116
+ # skip language is set from filename
117
+ end
118
+ elsif last
119
+ entry[last] = "#{entry[last]}#{uninspect(match)}"
120
+ end
121
+ end
122
+ end
123
+
124
+ # last line at end of file
125
+ if not entry.empty? and key
126
+ I18n::Translate.set(key, entry, hash, @translate.options[:separator])
127
+ end
128
+
129
+ {@translate.lang => hash}
130
+ end
131
+
132
+
133
+ def export(data)
134
+ target = data[@translate.lang]
135
+ str = ""
136
+ keys = I18n::Translate.hash_to_keys(@translate.default).sort
137
+
138
+ str << %~msgid ""\n~
139
+ str << %~msgstr ""\n~
140
+ str << %~"Content-Type: text/plain; charset=#{@translate.options[:encoding]}\\n"\n~
141
+ str << %~"X-Language: #{@translate.lang}\\n"\n~
142
+ keys.each do |key|
143
+ entry = [""]
144
+ value = @translate.find(key, target)
145
+ next unless value
146
+
147
+ if value.kind_of?(String)
148
+ # leave out msgctxt if using po strings as a key
149
+ default = @translate.find(key, @translate.default)
150
+ entry << %~msgctxt #{key.inspect}~ if key != default
151
+ entry << %~msgid #{default.to_s.inspect}~
152
+ entry << %~msgstr #{value.to_s.inspect}~
153
+ else
154
+ entry << %~# #{value["comment"].to_s.strip}~ unless value["comment"].to_s.strip.empty?
155
+ entry << %~#. #{value["extracted_comment"].to_s.strip}~ unless value["extracted_comment"].to_s.strip.empty?
156
+ if not value["reference"].to_s.strip.empty?
157
+ entry << %~#: #{value["reference"].to_s.strip}~
158
+ elsif value["file"] or value["line"]
159
+ entry << %~#: #{value["file"].to_s.strip}:#{value["line"].to_s.strip}~
160
+ end
161
+ key_default = nil
162
+ key_default = value["default"] if value["default"] == key
163
+ key_default = value["old_default"] if value["old_default"] == key
164
+ flags = []
165
+ flags << "fuzzy" if (not value["flag"].nil?) and (value["flag"] != "ok")
166
+ flags << value["flag"] unless value["flag"].to_s.strip.empty?
167
+ entry << %~#, #{flags.join(", ")}~ unless flags.empty?
168
+ entry << %~#| msgid #{value["old_default"].to_s.inspect}~ unless value["old_default"].to_s.empty?
169
+ entry << %~msgctxt #{key.inspect}~ if key != key_default
170
+ entry << %~msgid #{value["default"].to_s.inspect}~
171
+ entry << %~msgstr #{value["translation"].to_s.inspect}~
172
+ end
173
+
174
+ entry << ""
175
+
176
+ str << entry.join("\n")
177
+ end
178
+
179
+ str
180
+ end
181
+
182
+ private
183
+
184
+ def get_string scanner
185
+ scanner.scan(%r{.*?"$}).to_s[0..-2]
186
+ end
187
+
188
+ end
189
+
190
+ end
@@ -8,6 +8,13 @@ module I18n::Translate::Processor
8
8
  class Properties < Template
9
9
  FORMAT = ['properties']
10
10
 
11
+ WHITE_SPACE = / |\n|\t|\f|\r|\\u0020|\\u0009|\\u000C/
12
+ ASSIGN = /(?:#{WHITE_SPACE})*?(?:=|:)(?:#{WHITE_SPACE})*/
13
+ KEY = /^((?:[^\\:=]|\\:|\\=|\\ )+?)/m
14
+ VALUE_MULTILINE = /(.*?(?:\\\\)*)\\$/
15
+ VALUE = /(.*?(?:\\\\)*$)/
16
+ VALUE_END = /([^=]+?(?:\\\\)*$)/
17
+
11
18
  protected
12
19
 
13
20
  def import(data)
@@ -15,44 +22,53 @@ module I18n::Translate::Processor
15
22
 
16
23
  key = nil
17
24
  value = nil
25
+ status = :first
18
26
  line_number = 0
19
27
  data.each_line do |line|
20
28
  line_number += 1
21
- # skip empty line and comments
22
- next if (line =~ %r{^\s*$}) or (line =~ %r{^#.*})
29
+ # skip empty line and comments (first non white character is # or !)
30
+ next if (line =~ %r{^(#{WHITE_SPACE})*$}) or (line =~ %r{^(#{WHITE_SPACE})*(#|!).*})
23
31
 
24
- case line
25
32
  # multiline string
26
- when %r{^([^=]+)\s*=\s*(.*)\\$}
27
- key = $1.to_s.strip
28
- value = $2.to_s.strip
33
+ if line[%r{#{KEY}#{ASSIGN}#{VALUE_MULTILINE}}] and (status == :first)
34
+ status = :inside
35
+ key = $1.to_s
36
+ value = $2.to_s
29
37
 
30
38
  # continuous multiline string
31
- when %r{^\s*([^=]+)\\$}
32
- value << $1.to_s.strip
39
+ elsif line[%r{^(?:#{WHITE_SPACE})*#{VALUE_MULTILINE}}] and (status == :inside)
40
+ value << $1.to_s
33
41
 
34
42
  # end of continuous string
35
- when %r{^\s*([^=]+)$}
43
+ elsif line[%r{^(?:#{WHITE_SPACE})*#{VALUE_END}$}] and (status == :inside)
36
44
  value << $1.to_s.strip
37
- I18n::Translate.set(key, uninspect(value), hash, @translate.options[:separator])
45
+ I18n::Translate.set(uninspect(key), uninspect(value), hash, @translate.options[:separator])
38
46
  value = nil
47
+ status = :first
39
48
 
40
49
  # simple key = value
41
- when %r{^([^=]+)\s*=\s*(.*)$}
50
+ elsif line[%r{#{KEY}#{ASSIGN}#{VALUE}}]
42
51
  key, value = $1.to_s.strip, $2.to_s.strip
43
- I18n::Translate.set(key, uninspect(value), hash, @translate.options[:separator])
52
+ I18n::Translate.set(uninspect(key), uninspect(value), hash, @translate.options[:separator])
53
+
54
+ # empty key
55
+ elsif line[/#{KEY}$/]
56
+ key = $1.to_s.strip
57
+ value = ""
58
+ I18n::Translate.set(uninspect(key), uninspect(value), hash, @translate.options[:separator])
59
+ else
60
+ puts "*** not match: '#{line}'"
44
61
  end
45
62
  end
46
63
 
47
64
  {@lang => hash}
48
65
  end
49
66
 
50
-
51
67
  # this export ignores data
52
68
  def export(data)
53
69
  target = data[@translate.lang]
54
70
  str = ""
55
- keys = I18n::Translate.hash_to_keys(@translate.default).sort
71
+ keys = I18n::Translate.hash_to_keys(target).sort
56
72
 
57
73
  keys.each do |key|
58
74
  value = @translate.find(key, target)
@@ -61,13 +77,13 @@ module I18n::Translate::Processor
61
77
 
62
78
 
63
79
  if value.kind_of?(String)
64
- entry = value.strip
80
+ entry = value
65
81
  else
66
- entry = value["translation"].to_s.strip
82
+ entry = value["translation"].to_s
67
83
  end
68
84
 
69
85
  # create record in format: key = value
70
- str << key << " = " << entry.gsub("\n", "\\n") << "\n"
86
+ str << key.gsub(/( |:|=)/){|m| "\\#{m}"} << " = " << entry.gsub("\n", "\\n") << "\n"
71
87
  end
72
88
 
73
89
  str
@@ -53,6 +53,7 @@ module I18n::Translate::Processor
53
53
  entry["comment"] = get(message, "translatorcomment").to_s.strip
54
54
  entry["translation"] = get(message, "translation")
55
55
  fuzzy = get(message, "translation", "type").to_s.strip
56
+ entry["flag"] = fuzzy if fuzzy == "obsolete"
56
57
  entry["fuzzy"] = true unless fuzzy.empty?
57
58
  flag = get(message, "extra-po-flags").to_s.strip
58
59
  entry["flag"] = flag unless flag.empty?
@@ -77,7 +78,7 @@ module I18n::Translate::Processor
77
78
  <TS version="2.0" language="#{@translate.lang}">
78
79
  EOF
79
80
 
80
- keys = I18n::Translate.hash_to_keys(@translate.default).sort
81
+ keys = I18n::Translate.hash_to_keys(target).sort
81
82
  keys.each do |key|
82
83
  value = @translate.find(key, target)
83
84
  next unless value
@@ -94,7 +95,7 @@ EOF
94
95
  </context>
95
96
  EOF
96
97
  else
97
- fuzzy = ((value["flag"] == "ok") or value["flag"].to_s.strip.empty?) ? "" : %~ type="unfinished"~
98
+ fuzzy = ((value["flag"] == "ok") or value["flag"].to_s.strip.empty?) ? "" : (value["flag"] == "obsolete") ? %~ type="obsolete"~ : %~ type="unfinished"~
98
99
  xml += <<EOF
99
100
  <context>
100
101
  <name>#{::CGI.escapeHTML(key)}</name>
@@ -28,7 +28,7 @@ require 'find'
28
28
  # extracted_comment: po compatibility
29
29
  # file: file where it is # po compatibility
30
30
  # line: the lines, where is this key used # po compatibility
31
- # flag: ok || incomplete || changed || untranslated
31
+ # flag: ok || incomplete || changed || untranslated || obsolete
32
32
  # fuzzy: true # exists only where flag != ok (nice to have when you want
33
33
  # edit files manualy)
34
34
  #
@@ -48,7 +48,7 @@ require 'find'
48
48
  #
49
49
  module I18n::Translate
50
50
 
51
- FLAGS = %w(ok incomplete changed untranslated)
51
+ FLAGS = %w(ok incomplete changed untranslated obsolete)
52
52
  #FORMATS = %w(yml rb po pot ts properties) # the first one is preferred if :format => auto
53
53
  # function I18n::Translate::Processor.init will register known
54
54
  # formats
@@ -103,12 +103,26 @@ module I18n::Translate
103
103
  h = h[chunk]
104
104
  end
105
105
  unless value
106
- h[path[-1]] = nil
106
+ h.delete(path[-1])
107
107
  else
108
108
  h[path[-1]] = value
109
109
  end
110
110
  end
111
111
 
112
+ def self.delete(key, hash, separator=".")
113
+ path = key.split(separator)
114
+ set(key, nil, hash, separator)
115
+ i = path.size - 1
116
+ while i >= 0
117
+ k = path[0..i].join(separator)
118
+ trg = find(k, hash, separator)
119
+ if trg and trg.kind_of?(Hash) and trg.empty?
120
+ set(k, nil, hash, separator)
121
+ end
122
+ i -= 1
123
+ end
124
+ end
125
+
112
126
  # scans :locale_dir for files with valid formats and returns
113
127
  # list of files with locales. If block is given then
114
128
  # it creates Translate object for each entry and pass it to the block
@@ -175,7 +189,8 @@ module I18n::Translate
175
189
  :default => "default", # default name for file containing default app's key => string
176
190
  :force_encoding => true, # in ruby 1.9 forces string encoding
177
191
  :encoding => "utf-8", # encoding name to be forced to
178
- :format => "auto" # auto, rb, yml
192
+ :format => "auto", # auto, rb, yml
193
+ :merge => "soft", # hard or soft: hard strips old keys from target and soft set it to obsolete
179
194
  }
180
195
 
181
196
  attr_reader :default, :target, :merge, :options, :lang, :default_file, :lang_file
@@ -225,8 +240,9 @@ module I18n::Translate
225
240
  # 'old_translation' => '', # if flag == 'changed' then old_translation = t and t = ''
226
241
  # 'translation' => '', # value set in target file
227
242
  # 'comment' => '' # a comment added by a translator
228
- # 'flag' => ok || incomplete || changed || untranslated # set by merging tool except incomplete
229
- # which is set by translator
243
+ # 'flag' => ok || incomplete || changed || untranslated || obsolete
244
+ # # set by merging tool except incomplete
245
+ # which is set by translator
230
246
  # # other keys helded for compatibility with other formats
231
247
  # }
232
248
  def [](key)
@@ -279,6 +295,11 @@ module I18n::Translate
279
295
  I18n::Translate.find(key, hash, separator)
280
296
  end
281
297
 
298
+ def delete(key)
299
+ I18n::Translate.delete(key, @default, @options[:separator])
300
+ I18n::Translate.delete(key, @target, @options[:separator])
301
+ end
302
+
282
303
  # will create path in @target for 'key' and set the 'value'
283
304
  def []=(key, value)
284
305
  I18n::Translate.set(key, value, @target, @options[:separator])
@@ -346,6 +367,32 @@ module I18n::Translate
346
367
 
347
368
  self[key] = trg
348
369
  end
370
+ obsolete!
371
+ end
372
+
373
+ def obsolete!(merge = @options[:merge])
374
+ def_keys = I18n::Translate.hash_to_keys(@default, @options[:separator]).sort
375
+ trg_keys = I18n::Translate.hash_to_keys(@target, @options[:separator]).sort
376
+
377
+ obsolete_keys = trg_keys - def_keys
378
+ obsolete_keys.each do |key|
379
+ if merge == "hard"
380
+ I18n::Translate.delete(key, @target, @options[:separator])
381
+ next
382
+ end
383
+
384
+ trg = find(key)
385
+ next unless trg
386
+
387
+ if trg.kind_of?(String)
388
+ trg = {"translation" => trg, "flag" => "obsolete"}
389
+ else
390
+ trg["flag"] = "obsolete"
391
+ trg["fuzzy"] = true
392
+ end
393
+
394
+ I18n::Translate.set(key, trg, @target, @options[:separator])
395
+ end
349
396
  end
350
397
 
351
398
  # re-read @target data from the disk and create @merge
@@ -380,7 +427,7 @@ module I18n::Translate
380
427
  end
381
428
 
382
429
  # returns statistics hash
383
- # {:total => N, :ok => N, :changed => N, :incomplete => N, :untranslated => N, :fuzzy => N, :progress => N}
430
+ # {:total => N, :ok => N, :changed => N, :obsolete => N, :incomplete => N, :untranslated => N, :fuzzy => N, :progress => N}
384
431
  def stat
385
432
  stat = {
386
433
  :total => @merge.size,
@@ -388,6 +435,7 @@ module I18n::Translate
388
435
  :changed => @merge.select{|e| e["flag"] == "changed"}.size,
389
436
  :incomplete => @merge.select{|e| e["flag"] == "incomplete"}.size,
390
437
  :untranslated => @merge.select{|e| e["flag"] == "untranslated"}.size,
438
+ :obsolete => @merge.select{|e| e["flag"] == "obsolete"}.size,
391
439
  :fuzzy => @merge.select{|e| e["flag"] != "ok"}.size
392
440
  }
393
441
  stat[:progress] = (stat[:ok].to_f / stat[:total].to_f) * 100
data/test/all.rb CHANGED
@@ -8,6 +8,7 @@ $KCODE='UTF8'
8
8
  require 'test/unit'
9
9
  require 'rubygems'
10
10
  require 'yaml'
11
+ require 'pp'
11
12
 
12
13
  $:.unshift File.expand_path(File.join(File.dirname(__FILE__), ".."))
13
14
  require 'lib/i18n-translate'
@@ -64,7 +65,13 @@ def diff(src, trg)
64
65
  src.keys.each { |key| puts "src key: #{key}" unless trg.keys.include?(key) }
65
66
  trg.keys.each { |key| puts "trg key: #{key}" unless src.keys.include?(key) }
66
67
  src.keys.each do |key, value|
67
- puts "#{key} #{src[key].inspect} != #{trg[key].inspect}" if src[key] != trg[key]
68
+ if src[key] != trg[key]
69
+ print "#{key}: "
70
+ pp src[key]
71
+ puts "!="
72
+ pp trg[key]
73
+ end
74
+ #puts "#{key} #{src[key].inspect} != #{trg[key].inspect}" if src[key] != trg[key]
68
75
  end
69
76
  elsif src.kind_of?(Array) and trg.kind_of?(Array)
70
77
  src.each {|k| puts "not in trg '#{k}'" unless trg.include?(k)}
data/test/processor.rb CHANGED
@@ -10,7 +10,7 @@ module I18n::Test
10
10
  def __prepare(processor, file)
11
11
  # prepare object with correct data from yaml
12
12
  @tr = I18n::Translate::Translate.new('cze', {:locale_dir => $src_dir, :default_format => 'yml', :format => 'yml'})
13
- @tr.assign(@tr.target)
13
+ @tr.assign(@tr.merge)
14
14
 
15
15
  # prepare reader and writer
16
16
  @src_file = File.join($src_dir, file)
@@ -13,4 +13,20 @@ class TestBackendProperties < Test::Unit::TestCase
13
13
 
14
14
  include I18n::Test::Backend
15
15
 
16
+ def test_1000_colon_as_assign_operator
17
+ assert_equal( "apple, banana, pear, cantaloupe, watermelon, kiwi, mango", I18n.t("fruits") )
18
+ end
19
+
20
+ def test_1010_space_at_key_begining
21
+ assert_equal( "Beauty", I18n.t("Truth") )
22
+ end
23
+
24
+ def test_1020_escaped_operators_in_key
25
+ assert_equal( "operators", I18n.t("esc=aped:operators") )
26
+ end
27
+
28
+ def test_1030_key_without_assign_and_value
29
+ assert_equal( "translation missing: cze.empty_key", I18n.t("empty_key") )
30
+ end
31
+
16
32
  end
@@ -12,7 +12,7 @@ class TestProcessorTS < Test::Unit::TestCase
12
12
 
13
13
  include I18n::Test::Processor
14
14
 
15
- def test_0030_read_po_to_ts
15
+ def test_1010_read_po_to_ts
16
16
  t = I18n::Translate::Translate.new('cze', {:locale_dir => $src_dir, :default_format => 'yml', :format => 'yml'})
17
17
  file = File.join($src_dir, 'po_to_ts.ts')
18
18
  reader = I18n::Translate::Processor::TS.new(file, t)
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n-translators-tools
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 2
9
- - 3
10
- version: 0.2.3
8
+ - 4
9
+ version: 0.2.4
11
10
  platform: ruby
12
11
  authors:
13
12
  - Petr Kovar
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-07-30 00:00:00 +02:00
17
+ date: 2010-11-29 00:00:00 +01:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,12 +25,11 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 13
30
28
  segments:
31
29
  - 0
32
- - 4
33
- - 1
34
- version: 0.4.1
30
+ - 5
31
+ - 0
32
+ version: 0.5.0
35
33
  type: :runtime
36
34
  version_requirements: *id001
37
35
  - !ruby/object:Gem::Dependency
@@ -42,7 +40,6 @@ dependencies:
42
40
  requirements:
43
41
  - - ">="
44
42
  - !ruby/object:Gem::Version
45
- hash: 3
46
43
  segments:
47
44
  - 0
48
45
  version: "0"
@@ -50,10 +47,13 @@ dependencies:
50
47
  version_requirements: *id002
51
48
  description: |
52
49
  This package brings you useful utility and library which can help you to handle
53
- locale files and translations in your Ruby projects. Offers also built-in simple
54
- console editor. Supported formats are YAML, Ruby, Gettext po, QT Linguist TS and
55
- Java Properties. Read README.md file and run i18n-translate without parameters
56
- for more information.
50
+ locale files and translations in your Ruby projects.
51
+ It is build upon i18n library and extends it's simple format so you can simply
52
+ track field changes or keep translator's notes. Conversion back to simple format
53
+ is possible and as simple as call 'i18n-translate strip'. Offers also built-in
54
+ simple console editor. Supported formats are YAML, Ruby, Gettext po,
55
+ QT Linguist TS and Java Properties. Read README.md file and run i18n-translate
56
+ without parameters for more information.
57
57
 
58
58
  email: pejuko@gmail.com
59
59
  executables:
@@ -74,6 +74,7 @@ files:
74
74
  - lib/i18n/backend/translate.rb
75
75
  - lib/i18n/processor/properties.rb
76
76
  - lib/i18n/processor/ts.rb
77
+ - lib/i18n/processor/gettext_strscan.rb
77
78
  - lib/i18n/processor/ruby.rb
78
79
  - lib/i18n/processor/yaml.rb
79
80
  - lib/i18n/processor/gettext.rb
@@ -124,7 +125,7 @@ post_install_message: |
124
125
 
125
126
  Backends:
126
127
  * Extended format. i18n-translators-tools brings extended format
127
- I18n::Backend::Simple.send(:include, I18n::Backend::Translator)
128
+ I18n::Backend::Simple.send(:include, I18n::Backend::Translate)
128
129
  * Gettext po
129
130
  I18n::Backend::Simple.send(:include, I18n::Backend::PO)
130
131
  * QT Linguist TS
@@ -151,9 +152,19 @@ post_install_message: |
151
152
  v0.2.3
152
153
  * fix: hash_to_keys can work with enhanced format => default can be in
153
154
  enchanced format
155
+ * default file can be now in enhanced format. if translation field is missing
154
156
  * i18n-translate <source file> <target file>
155
157
  automaticlay perform convert action from one file to another
156
158
 
159
+ v0.2.4
160
+ * enhanced support for java properties
161
+ * hard/soft merges. hard deletes deleted keys in target and soft set them to
162
+ obsolete
163
+ * processors now generate keys list from provided data and not from @tr.default
164
+ * flag obsolete added
165
+ * delete function
166
+ * i18n-0.5.0 compatibility (for older i18n user v0.2.3)
167
+
157
168
  For more information read README.md and CHANGELOG.md
158
169
 
159
170
  -----------------------------------------------------------------------------
@@ -171,7 +182,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
171
182
  requirements:
172
183
  - - ">="
173
184
  - !ruby/object:Gem::Version
174
- hash: 3
175
185
  segments:
176
186
  - 0
177
187
  version: "0"
@@ -180,7 +190,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
190
  requirements:
181
191
  - - ">="
182
192
  - !ruby/object:Gem::Version
183
- hash: 3
184
193
  segments:
185
194
  - 0
186
195
  version: "0"