i18n-translators-tools 0.1.1 → 0.2

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