i18n-translators-tools 0.1.1 → 0.2

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.
@@ -13,11 +13,11 @@ module I18n::Translate::Processor
13
13
  protected
14
14
 
15
15
  def import(data)
16
- ::YAML.load(data)
16
+ migrate(::YAML.load(data))
17
17
  end
18
18
 
19
19
  def export(data)
20
- data.ya2yaml
20
+ data.ya2yaml(:minimum_block_length => 80)
21
21
  end
22
22
 
23
23
  end
@@ -6,27 +6,28 @@
6
6
  require 'fileutils'
7
7
  require 'find'
8
8
 
9
- require 'i18n/processor'
10
-
11
9
  # I18n::Translate introduces new format for translations. To make
12
10
  # I18n.t work properly you need to include Translator's backend:
13
11
  #
14
12
  # I18n::Backend::Simple.send(:include, I18n::Backend::Translate)
15
13
  # I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
16
14
  #
17
- # notice that Translator have to be included BEFORE Fallback otherwise
15
+ # notice that Translator have to be included BEFORE Fallbacks otherwise
18
16
  # the fallback will get Hash (even with empty translation) and won't work.
19
17
  #
20
- # It is hightly recommended to use Fallback backend together with Translate.
18
+ # It is hightly recommended to use Fallbacks backend together with Translate.
21
19
  # If you have experienced nil or empty translations this can fix the problem.
22
20
  #
23
21
  # Format of entry:
24
22
  #
25
- # old: old default string
23
+ # old_default: old default string
26
24
  # default: new default string
27
25
  # comment: translator's comments
28
- # t: translation
29
- # line: the lines, where is this key used # not implemented yet
26
+ # translation: translation itself (optionaly in the file can be just t as
27
+ # a shorthand, however, the tools will allways write translation)
28
+ # extracted_comment: po compatibility
29
+ # file: file where it is # po compatibility
30
+ # line: the lines, where is this key used # po compatibility
30
31
  # flag: ok || incomplete || changed || untranslated
31
32
  # fuzzy: true # exists only where flag != ok (nice to have when you want
32
33
  # edit files manualy)
@@ -35,20 +36,29 @@ require 'i18n/processor'
35
36
  #
36
37
  # key:
37
38
  # one:
38
- # old:
39
+ # old_default:
39
40
  # default:
40
- # t:
41
+ # translation:
41
42
  # ...
42
43
  # other:
43
- # old:
44
+ # old_default:
44
45
  # default:
45
- # t:
46
+ # translation:
46
47
  # ...
47
48
  #
48
49
  module I18n::Translate
49
50
 
50
51
  FLAGS = %w(ok incomplete changed untranslated)
51
- FORMATS = %w(yml rb po) # the first one is preferred if :format => auto
52
+ #FORMATS = %w(yml rb po pot ts properties) # the first one is preferred if :format => auto
53
+ # function I18n::Translate::Processor.init will register known
54
+ # formats
55
+ FORMATS = %w() # the first one is preferred if :format => auto
56
+
57
+ # read configuration file
58
+ # config format is ruby file which returns hash
59
+ def self.read_config(filename)
60
+ eval File.read(filename)
61
+ end
52
62
 
53
63
  # returns flat array of all keys e.g. ["system.message.ok", "system.message.error", ...]
54
64
  def self.hash_to_keys(hash, separator=".", prefix="")
@@ -59,7 +69,7 @@ module I18n::Translate
59
69
  str = hash_to_keys( hash[key], separator, str )
60
70
  end
61
71
  res << str
62
- end
72
+ end if hash
63
73
  res.flatten
64
74
  end
65
75
 
@@ -107,8 +117,13 @@ module I18n::Translate
107
117
  locale, format = Translate.valid_file?(entry, o[:format])
108
118
  if (not format) or (locale == o[:default])
109
119
  puts "#{entry}...skipping" if o[:verbose]
110
- next unless format
111
- next if locale == o[:default]
120
+ next
121
+ end
122
+
123
+ # skip if not desired locale
124
+ if o[:locale] and (o[:locale] != "auto") and (o[:locale] != locale)
125
+ puts "#{entry}...skipping" if o[:verbose]
126
+ next
112
127
  end
113
128
 
114
129
  exclude = false
@@ -133,7 +148,13 @@ module I18n::Translate
133
148
  locales
134
149
  end
135
150
 
136
-
151
+ # create new locale and returns I18n::Translate::Translate object
152
+ def self.create_locale(lang, opts={})
153
+ tr = I18n::Translate::Translate.new(lang, opts)
154
+ tr.assign(tr.merge)
155
+ tr.export!
156
+ tr
157
+ end
137
158
 
138
159
  # it breaks proc and lambdas objects
139
160
  class Translate
@@ -151,19 +172,20 @@ module I18n::Translate
151
172
  # loads default and lang files
152
173
  def initialize(lang, opts={})
153
174
  @lang = lang.to_s
154
- raise "Empty locale" if @lang.empty?
175
+ raise "Empty locale" if @lang.empty? and not opts[:empty]
155
176
  @options = DEFAULT_OPTIONS.merge(opts)
156
177
  @options[:default_format] ||= @options[:format]
157
178
 
158
- @default, @default_file = load_locale( @options[:default], @options[:default_format] )
159
- @target, @lang_file = load_locale( @lang )
160
-
161
- merge!
179
+ if @lang and not opts[:empty]
180
+ @default, @default_file = load_locale( @options[:default], @options[:default_format] )
181
+ @target, @lang_file = load_locale( @lang )
182
+ merge!
183
+ end
162
184
  end
163
185
 
164
186
  # check if the file has supported format
165
187
  def self.valid_file?(fname, format=Translate::DEFAULT_OPTIONS[:format])
166
- pattern = ".*?"
188
+ pattern = "[^\.]+"
167
189
  pattern = format if format != "auto"
168
190
  fname =~ /\/?([^\/]*?)\.(#{pattern})$/
169
191
  locale, format = $1, $2
@@ -175,19 +197,17 @@ module I18n::Translate
175
197
 
176
198
  # will merge only one key and returns hash
177
199
  # {
178
- # :key => 'key',
179
- # :default => '', # value set in default file
180
- # :old_default => '', # value set as old in target file
200
+ # 'key' => 'key',
201
+ # 'default' => '', # value set in default file
202
+ # 'old_default' => '', # value set as old in target file
181
203
  # (value from default file from last translation
182
- # if the field has changed)
183
- # :old_t => '', # if flag == 'changed' then old_t = t and t = ''
184
- # :t => '', # value set in target file
185
- # :line => 'some/file.rb: 44', # name of source file and number of line
186
- # (a copy in target file from default file)
187
- # !!! line is unused for now
188
- # :comment => '' # a comment added by a translator
189
- # :flag => ok || incomplete || changed || untranslated # set by merging tool except incomplete
204
+ # if the field has changed)
205
+ # 'old_translation' => '', # if flag == 'changed' then old_translation = t and t = ''
206
+ # 'translation' => '', # value set in target file
207
+ # 'comment' => '' # a comment added by a translator
208
+ # 'flag' => ok || incomplete || changed || untranslated # set by merging tool except incomplete
190
209
  # which is set by translator
210
+ # # other keys helded for compatibility with other formats
191
211
  # }
192
212
  def [](key)
193
213
  d = I18n::Translate.find(key, @default, @options[:separator])
@@ -199,10 +219,10 @@ module I18n::Translate
199
219
  trg = I18n::Translate.find(key, @target, @options[:separator])
200
220
  if (not trg) or
201
221
  (trg.kind_of?(String) and trg.strip.empty?) or
202
- (trg.kind_of?(Hash) and trg["t"].to_s.strip.empty?)
222
+ (trg.kind_of?(Hash) and trg["translation"].to_s.strip.empty?)
203
223
  entry["old_default"] = ""
204
- entry["old_t"] = ""
205
- entry["t"] = ""
224
+ entry["old_translation"] = ""
225
+ entry["translation"] = ""
206
226
  entry["comment"] = trg.kind_of?(Hash) ? trg["comment"].to_s.strip : ""
207
227
  entry["flag"] = "untranslated"
208
228
  return entry
@@ -210,24 +230,24 @@ module I18n::Translate
210
230
 
211
231
  # default has changed => new translation is probably required
212
232
  if trg.kind_of?(Hash)
213
- entry["old_t"] = trg["t"].to_s.strip
214
- entry["t"] = ""
233
+ entry["old_translation"] = trg["translation"].to_s.strip
234
+ entry["translation"] = ""
215
235
  entry["comment"] = trg["comment"].to_s.strip
216
236
  entry["flag"] = "changed"
217
237
 
218
238
  if d != trg["default"]
219
239
  entry["old_default"] = trg["default"].to_s.strip
220
240
  return entry
221
- elsif not trg["old"].to_s.strip.empty?
222
- entry["old_default"] = trg["old"].to_s.strip
241
+ elsif not trg["old_default"].to_s.strip.empty?
242
+ entry["old_default"] = trg["old_default"].to_s.strip
223
243
  return entry
224
244
  end
225
245
  end
226
246
 
227
247
  # nothing has changed
228
- entry["old_default"] = trg.kind_of?(Hash) ? trg["old"].to_s.strip : ""
229
- entry["old_t"] = ""
230
- entry["t"] = trg.kind_of?(Hash) ? trg["t"].to_s.strip : trg.to_s.strip
248
+ entry["old_default"] = trg.kind_of?(Hash) ? trg["old_default"].to_s.strip : ""
249
+ entry["old_translation"] = ""
250
+ entry["translation"] = trg.kind_of?(Hash) ? trg["translation"].to_s.strip : trg.to_s.strip
231
251
  entry["comment"] = trg.kind_of?(Hash) ? trg["comment"].to_s.strip : ""
232
252
  entry["flag"] = (trg.kind_of?(Hash) and trg["flag"]) ? trg["flag"].to_s.strip : "ok"
233
253
 
@@ -235,7 +255,7 @@ module I18n::Translate
235
255
  end
236
256
 
237
257
  # wrapper for I18n::Translate.find with presets options
238
- def find(key, hash=@translate, separator=@options[:separator])
258
+ def find(key, hash=@target, separator=@options[:separator])
239
259
  I18n::Translate.find(key, hash, separator)
240
260
  end
241
261
 
@@ -261,8 +281,8 @@ module I18n::Translate
261
281
  key, values = transl
262
282
  end
263
283
 
264
- old_t = values["old_t"].to_s.strip
265
- new_t = values["t"].to_s.strip
284
+ old_t = values["old_translation"].to_s.strip
285
+ new_t = values["translation"].to_s.strip
266
286
  default = values["default"].to_s.strip
267
287
  old_default = values["old_default"].to_s.strip
268
288
  flag = values["flag"].to_s.strip
@@ -278,28 +298,32 @@ module I18n::Translate
278
298
  comment.force_encoding(enc)
279
299
  end
280
300
 
281
- trg = {
282
- "comment" => comment,
283
- "flag" => flag
284
- }
301
+ # merging with unknown fields
302
+ trg = find(key)
303
+ trg = {} if trg.nil? or not trg.kind_of?(Hash)
304
+ trg["comment"] = comment
305
+ trg["flag"] = flag
285
306
 
286
307
  if flag == "ok"
287
- trg["t"] = new_t.empty? ? old_t : new_t
308
+ trg["translation"] = new_t.empty? ? old_t : new_t
288
309
  trg["default"] = default
289
- trg["old"] = ""
310
+ trg["old_default"] = ""
290
311
  else
291
- trg["t"] = new_t.empty? ? old_t : new_t
312
+ trg["translation"] = new_t.empty? ? old_t : new_t
292
313
  trg["default"] = default
293
- trg["old"] = old_default
314
+ trg["old_default"] = old_default
294
315
  end
295
316
 
296
317
  # make fallback work
297
- trg["t"] = nil if trg["t"].empty?
318
+ trg["translation"] = nil if trg["translation"].empty?
298
319
 
299
320
  # say that this entry is not completed yet
300
321
  # useful if you edit files in text editor and serching for next one
301
322
  trg["fuzzy"] = true if flag != "ok"
302
323
 
324
+ # clean empty values
325
+ trg.delete_if{ |k,v| v.to_s.empty? }
326
+
303
327
  self[key] = trg
304
328
  end
305
329
  end
@@ -326,9 +350,10 @@ module I18n::Translate
326
350
  keys = I18n::Translate.hash_to_keys(@default, @options[:separator])
327
351
  keys.each do |key|
328
352
  entry = I18n::Translate.find(key, @target, @options[:separator])
329
- raise "Translate#[key]: wrong key '#{key}'" unless entry
353
+ next unless entry # skip entries that are not merged in target yet
354
+ #raise "Translate#[key]: wrong key '#{key}'" unless entry
330
355
  next unless entry.kind_of?(Hash)
331
- self[key] = entry["t"]
356
+ self[key] = entry["translation"]
332
357
  end
333
358
 
334
359
  self
@@ -349,17 +374,6 @@ module I18n::Translate
349
374
  stat
350
375
  end
351
376
 
352
- def to_yaml
353
- trg = {@lang => @target}
354
- #YAML.dump(trg)
355
- trg.ya2yaml
356
- end
357
-
358
- def to_rb
359
- trg = {@lang => @target}
360
- trg.to_rb
361
- end
362
-
363
377
  protected
364
378
 
365
379
  # returns first file for @lang.type
@@ -378,7 +392,8 @@ module I18n::Translate
378
392
  fname = file_name(lang, type)
379
393
 
380
394
  if File.exists?(fname)
381
- return [Processor.read(fname, self)[lang], fname]
395
+ data = Processor.read(fname, self)
396
+ return [data[lang], fname]
382
397
  else
383
398
  STDERR << "Warning: I18n::Translate#load_locale: file `#{fname}' does NOT exists. Creating empty locale.\n"
384
399
  end
@@ -0,0 +1,76 @@
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
+ module I18n::Translate
7
+
8
+ class Translator
9
+ def initialize(lang, opts={})
10
+ @translate = I18n::Translate::Translate.new(lang, opts)
11
+ end
12
+
13
+ def run
14
+ stat = @translate.stat
15
+ @translate.merge.select{|x| x["flag"] != "ok"}.each_with_index do |entry, i|
16
+ next_entry = false
17
+ while not next_entry
18
+ puts ""
19
+ puts ""
20
+ puts "[#{@translate.default_file} + #{@translate.lang_file}]"
21
+ puts "(#{i+1}/#{stat[:fuzzy]}) #{entry["key"]} (#{entry["flag"]})"
22
+ puts "comment: #{entry["comment"]}" unless entry["comment"].empty?
23
+ puts "old default: #{entry["old_default"]}" unless entry["old_default"].empty?
24
+ puts "old translation: #{entry["old_translation"]}" unless entry["old_translation"].empty?
25
+ puts "default: #{entry["default"]}"
26
+ puts "translation: #{entry["translation"]}"
27
+ puts ""
28
+ puts "Actions:"
29
+ puts "n (next) t (translate) f (change flag) c (comment) s (save) q (save & quit) x(exit no saving)"
30
+ action = STDIN.readline.strip
31
+ puts ""
32
+ case action
33
+ when 'n'
34
+ next_entry = true
35
+ when 't'
36
+ puts "Enter translation:"
37
+ entry["translation"] = STDIN.readline.strip
38
+ entry["flag"] = "ok" unless entry["translation"].empty?
39
+ @translate.assign( [entry] )
40
+ puts "Flag sets to #{entry["flag"]}"
41
+ when 'f'
42
+ puts "Change flag to:"
43
+ puts "o (ok), i (incomplete), c (changed), u (untranslated)"
44
+ f = STDIN.readline.strip
45
+ I18n::Translate::FLAGS.each do |fname|
46
+ if fname[0,1] == f
47
+ entry["flag"] = fname
48
+ break
49
+ end
50
+ end
51
+ @translate.assign( [entry] )
52
+ puts "Flag sets to #{entry["flag"]}"
53
+ when 'c'
54
+ puts "Enter comment:"
55
+ entry["comment"] = STDIN.readline.strip
56
+ @translate.assign( [entry] )
57
+ puts "Comment has changed."
58
+ when 's'
59
+ @translate.export!
60
+ puts "Translation saved"
61
+ when 'q'
62
+ @translate.export!
63
+ puts "Translation saved"
64
+ exit
65
+ when 'x'
66
+ exit
67
+ end # case
68
+ end # while
69
+ end # each_with_index
70
+
71
+ @translate.export!
72
+ @translate.reload!
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,89 @@
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
+ $KCODE='UTF8'
7
+
8
+ require 'test/unit'
9
+ require 'rubygems'
10
+ require 'yaml'
11
+
12
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), ".."))
13
+ require 'lib/i18n-translate'
14
+ require 'test/backend'
15
+ require 'test/processor'
16
+
17
+ $current_dir = File.expand_path(File.dirname(__FILE__))
18
+ $src_dir = File.join($current_dir, 'locale/src')
19
+ $trg_dir = File.join($current_dir, 'locale/trg')
20
+
21
+
22
+ # some data for comparation
23
+ $si = {
24
+ "comment" => "Comment",
25
+ "extracted_comment" => "Extracted Comment",
26
+ "reference" => "src/test.rb:31",
27
+ "file" => "src/test.rb",
28
+ "line" => "31",
29
+ "fuzzy" => true,
30
+ "flag" => "incomplete",
31
+ "old_default" => "Previous untranslated",
32
+ "default" => "Interpolated text '%{var}'",
33
+ "translation" => "Interpolovaný text '%{var}'"
34
+ }
35
+
36
+
37
+ def load_yml(default, cze)
38
+ tr = I18n::Translate::Translate.new('default', {:empty => true})
39
+ [ I18n::Translate::Processor.read(default, tr)["default"],
40
+ I18n::Translate::Processor.read(cze, tr)["cze"] ]
41
+ end
42
+
43
+ def load_src
44
+ load_yml("#{$src_dir}/default.yml", "#{$src_dir}/cze.yml")
45
+ end
46
+
47
+ def load_trg
48
+ load_yml("#{$trg_dir}/default.yml", "#{$trg_dir}/cze.yml")
49
+ end
50
+
51
+ def load_src_trg
52
+ res = {}
53
+ res[:src] = load_src
54
+ res[:trg] = load_trg
55
+ res
56
+ end
57
+
58
+
59
+ # helper for comparing Hasheds and Arrays
60
+ # prints out differences
61
+ def diff(src, trg)
62
+ return if src == trg
63
+ if src.kind_of?(Hash) and trg.kind_of?(Hash)
64
+ src.keys.each { |key| puts "src key: #{key}" unless trg.keys.include?(key) }
65
+ trg.keys.each { |key| puts "trg key: #{key}" unless src.keys.include?(key) }
66
+ src.keys.each do |key, value|
67
+ puts "#{key} #{src[key].inspect} != #{trg[key].inspect}" if src[key] != trg[key]
68
+ end
69
+ elsif src.kind_of?(Array) and trg.kind_of?(Array)
70
+ src.each {|k| puts "not in trg '#{k}'" unless trg.include?(k)}
71
+ trg.each {|k| puts "not in src '#{k}'" unless src.include?(k)}
72
+ else
73
+ puts "not equal types"
74
+ end
75
+ end
76
+
77
+
78
+ I18n::Backend::Simple.send(:include, I18n::Backend::Translate)
79
+ I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
80
+ I18n::Backend::Simple.send(:include, I18n::Backend::PO)
81
+ I18n::Backend::Simple.send(:include, I18n::Backend::TS)
82
+ I18n::Backend::Simple.send(:include, I18n::Backend::Properties)
83
+ I18n.default_locale = 'default'
84
+ I18n.locale = 'cze'
85
+
86
+
87
+ Dir[File.join($current_dir, "tc_*.rb")].each do |tc|
88
+ load tc
89
+ end