packnga 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/packnga.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2011 yoshihara haruka <yoshihara@clear-code.com>
3
+ # Copyright (C) 2011 Haruka Yoshihara <yoshihara@clear-code.com>
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -19,5 +19,4 @@ require "rake"
19
19
 
20
20
  require "packnga/version"
21
21
  require "packnga/document-task"
22
- require "packnga/reference-task"
23
22
  require "packnga/release-task"
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2011 yoshihara haruka <yoshihara@clear-code.com>
3
+ # Copyright (C) 2011 Haruka Yoshihara <yoshihara@clear-code.com>
4
4
  # Copyright (C) 2011 Kouhei Sutou <kou@clear-code.com>
5
5
  #
6
6
  # This library is free software; you can redistribute it and/or
@@ -21,34 +21,26 @@ require "packnga/yard-task"
21
21
  require "packnga/reference-task"
22
22
 
23
23
  module Packnga
24
- # This class creates docment tasks.
25
- # They generate YARD doucment or references.
24
+ # This class creates tasks for document.
25
+ # They generate YARD document or references.
26
26
  #
27
27
  # @since 0.9.0
28
28
  class DocumentTask
29
29
  include Rake::DSL
30
30
  # Defines tasks to generate YARD documentation
31
31
  # and to translate references.
32
- # @param [Jeweler::Task] spec created by Jeweler::Task.new.
32
+ # @param [Gem::Specification] spec specification for your package
33
33
  def initialize(spec)
34
34
  @spec = spec
35
35
  @yard_task = YARDTask.new(@spec)
36
36
  @reference_task = ReferenceTask.new(@spec)
37
37
  self.base_dir = "doc"
38
- if block_given?
39
- yield(self)
40
- define
41
- end
42
- end
43
-
44
- # @private
45
- def define
46
- set_default_values
47
- define_tasks
38
+ yield(self) if block_given?
39
+ define
48
40
  end
49
41
 
50
42
  # Sets base directory for documents. Default value is "doc".
51
- # @param [String] base direcory path
43
+ # @param [String] dir base direcory path
52
44
  def base_dir=(dir)
53
45
  dir = Pathname.new(dir)
54
46
  @yard_task.base_dir = dir
@@ -70,7 +62,49 @@ module Packnga
70
62
  end
71
63
 
72
64
  private
65
+ def define
66
+ set_default_values
67
+ define_tasks
68
+ end
69
+
73
70
  def set_default_values
71
+ set_default_readme
72
+ set_default_source_files
73
+ set_default_text_files
74
+ end
75
+
76
+ def set_default_readme
77
+ readme = @spec.files.find do |file|
78
+ file.include?("README")
79
+ end
80
+ @yard_task.readme = readme
81
+ @reference_task.readme = readme
82
+ end
83
+
84
+ def set_default_source_files
85
+ source_files = @spec.files.find_all do |file|
86
+ ruby_source_file?(file) or
87
+ c_source_file?(file)
88
+ end
89
+ @yard_task.source_files = source_files
90
+ @reference_task.source_files = source_files
91
+ end
92
+
93
+ def ruby_source_file?(file)
94
+ file.start_with?("lib/") and file.end_with?(".rb")
95
+ end
96
+
97
+ def c_source_file?(file)
98
+ file.start_with?("ext/") and file.end_with?(".c")
99
+ end
100
+
101
+ def set_default_text_files
102
+ text_dir = @yard_task.base_dir + "text"
103
+ text_files = @spec.files.find_all do |file|
104
+ file.start_with?(text_dir.to_s)
105
+ end
106
+ @yard_task.text_files = text_files
107
+ @reference_task.text_files = text_files
74
108
  end
75
109
 
76
110
  def define_tasks
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2011 yoshihara haruka <yoshihara@clear-code.com>
3
+ # Copyright (C) 2011 Haruka Yoshihara <yoshihara@clear-code.com>
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -16,6 +16,10 @@
16
16
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
 
18
18
  require "erb"
19
+ require "gettext/tools"
20
+ require "tempfile"
21
+ require "tmpdir"
22
+ require "rake/clean"
19
23
 
20
24
  module Packnga
21
25
  # This class creates reference tasks.
@@ -26,27 +30,36 @@ module Packnga
26
30
  include Rake::DSL
27
31
  include ERB::Util
28
32
 
33
+ # This attribute is used to set path of base directory of document.
29
34
  # @return [String] path of base directory of document
30
- attr_writer :base_dir
35
+ attr_accessor :base_dir
31
36
 
32
- # @return [String] mode used in xml2po. The default is "docbook".
33
- attr_writer :mode
37
+ # This attribute is used to set README file.
38
+ # @return [String] path of readme file
39
+ attr_accessor :readme
40
+
41
+ # This attribute is used to set source files for document.
42
+ # @return [Array<String>] target source files
43
+ attr_accessor :source_files
44
+
45
+ # This attribute is used to set text files for document.
46
+ # @return [Array<String>] target text files
47
+ attr_accessor :text_files
34
48
 
35
49
  # @private
36
50
  def initialize(spec)
37
51
  @spec = spec
38
52
  @base_dir = nil
39
- @mode = nil
40
53
  @translate_languages = nil
41
54
  @supported_languages = nil
55
+ @source_files = nil
56
+ @text_files = nil
57
+ @readme = nil
58
+ @extra_files = nil
59
+ @files = nil
42
60
  @html_files = nil
43
- @htaccess = nil
44
61
  @po_dir = nil
45
62
  @pot_file = nil
46
- if block_given?
47
- yield(self)
48
- define
49
- end
50
63
  end
51
64
 
52
65
  # @private
@@ -63,12 +76,14 @@ module Packnga
63
76
  private
64
77
  def set_default_values
65
78
  @base_dir ||= Pathname.new("doc")
66
- @mode ||= "docbook"
67
79
  @translate_languages ||= [:ja]
68
80
  @supported_languages = [:en, *@translate_languages]
69
81
  @html_files = FileList[(doc_en_dir + "**/*.html").to_s].to_a
70
- @po_dir = "doc/po"
82
+ @po_dir = "#{@base_dir}/po"
71
83
  @pot_file = "#{@po_dir}/#{@spec.name}.pot"
84
+ @extra_files = @text_files
85
+ @extra_files += [@readme] if @readme
86
+ @files = @source_files + @extra_files
72
87
  end
73
88
 
74
89
  def reference_base_dir
@@ -100,14 +115,9 @@ module Packnga
100
115
  def define_pot_tasks
101
116
  namespace :pot do
102
117
  directory @po_dir
103
- file @pot_file => [@po_dir, *@html_files] do |t|
104
- sh("xml2po",
105
- "--keep-entities",
106
- "--mode", @mode,
107
- "--output", t.name,
108
- *@html_files)
118
+ file @pot_file => [@po_dir, *@files] do |t|
119
+ create_pot_file(@pot_file)
109
120
  end
110
-
111
121
  desc "Generates pot file."
112
122
  task :generate => @pot_file do |t|
113
123
  end
@@ -117,23 +127,22 @@ module Packnga
117
127
  def define_po_tasks
118
128
  namespace :po do
119
129
  namespace :update do
120
- @translate_languages.each do |language|
130
+ @translate_languages.each do |language|
121
131
  po_file = "#{@po_dir}/#{language}.po"
122
132
 
123
133
  if File.exist?(po_file)
124
- file po_file => @html_files do |t|
125
- sh("xml2po",
126
- "--keep-entities",
127
- "--mode", @mode,
128
- "--update", t.name,
129
- *@html_files)
134
+ file po_file => @files do |t|
135
+ current_pot_file = "tmp.pot"
136
+ create_pot_file(current_pot_file)
137
+ GetText.msgmerge(po_file, current_pot_file,
138
+ "#{@spec.name} #{Packnga::VERSION}")
139
+ FileUtils.rm_f(current_pot_file)
130
140
  end
131
141
  else
132
142
  file po_file => @pot_file do |t|
133
- sh("msginit",
134
- "--input=#{@pot_file}",
135
- "--output=#{t.name}",
136
- "--locale=#{language}")
143
+ GetText::Tools::MsgInit.run("--input", @pot_file,
144
+ "--output", t.name,
145
+ "--locale", language.to_s)
137
146
  end
138
147
  end
139
148
 
@@ -144,41 +153,35 @@ module Packnga
144
153
 
145
154
  desc "Updates po files."
146
155
  task :update do
147
- ruby($0, "clobber")
148
- ruby($0, "yard")
156
+ Rake::Task["clobber"].invoke
149
157
  @translate_languages.each do |language|
150
- ruby($0, "reference:po:update:#{language}")
158
+ Rake::Task["reference:po:update:#{language}"].invoke
151
159
  end
152
160
  end
153
161
  end
154
162
  end
155
163
 
164
+ def create_pot_file(pot_file_path)
165
+ options = ["-o", pot_file_path]
166
+ options += @source_files
167
+ options += ["-"]
168
+ options += @extra_files
169
+ YARD::CLI::I18n.run(*options)
170
+ end
171
+
156
172
  def define_translate_task
173
+ directory reference_base_dir.to_s
157
174
  namespace :translate do
158
175
  @translate_languages.each do |language|
159
176
  po_file = "#{@po_dir}/#{language}.po"
160
- translate_doc_dir = "#{reference_base_dir}/#{language}"
161
177
  desc "Translates documents to #{language}."
162
- task language => [po_file, reference_base_dir, *@html_files] do
163
- doc_en_dir.find do |path|
164
- base_path = path.relative_path_from(doc_en_dir)
165
- translated_path = "#{translate_doc_dir}/#{base_path}"
166
- if path.directory?
167
- mkdir_p(translated_path)
168
- next
169
- end
170
- case path.extname
171
- when ".html"
172
- sh("xml2po",
173
- "--keep-entities",
174
- "--mode", @mode,
175
- "--po-file", po_file.to_s,
176
- "--language", language.to_s,
177
- "--output", translated_path.to_s,
178
- path.to_s)
179
- else
180
- cp(path.to_s, translated_path, :preserve => true)
181
- end
178
+ task language => [po_file, reference_base_dir, *@files] do
179
+ locale = YARD::I18n::Locale.new(language)
180
+ locale.load(@po_dir)
181
+ Dir.mktmpdir do |temp_dir|
182
+ create_translated_sources(temp_dir, locale)
183
+ copy_extra_files(temp_dir)
184
+ create_translated_documents(temp_dir, locale)
182
185
  end
183
186
  end
184
187
  end
@@ -283,5 +286,487 @@ module Packnga
283
286
  erb.filename = file
284
287
  erb
285
288
  end
289
+
290
+ def create_translated_documents(output_dir, locale)
291
+ language = locale.name.to_s
292
+ translate_doc_dir = "#{reference_base_dir}/#{language}"
293
+ po_dir = File.expand_path(@po_dir)
294
+ mkdir_p(translate_doc_dir)
295
+
296
+ Dir.chdir(output_dir) do
297
+ YARD::Registry.clear
298
+ YARD.parse(@source_files)
299
+
300
+ options = [
301
+ "--title", @spec.name,
302
+ "-o", translate_doc_dir,
303
+ "--po-dir", po_dir,
304
+ "--locale", language,
305
+ "--charset", "utf-8",
306
+ "--no-private"
307
+ ]
308
+ options += ["--readme", @readme] if @readme
309
+ options += @source_files
310
+ options += ["-"]
311
+ options += @text_files
312
+
313
+ YARD::CLI::Yardoc.run(*options)
314
+ end
315
+ translated_files = File.join(output_dir, translate_doc_dir, "**")
316
+ FileUtils.cp_r(Dir.glob(translated_files), translate_doc_dir)
317
+ end
318
+
319
+ def create_translated_sources(output_dir, locale)
320
+ YARD.parse(@source_files)
321
+ create_translated_files(@source_files, output_dir) do |content|
322
+ code_objects = YARD::Registry.all
323
+ code_objects.each do |code_object|
324
+ original_docstring = code_object.docstring
325
+ content = translate_content_part(content,
326
+ original_docstring,
327
+ locale)
328
+
329
+ original_docstring.tags.each do |tag|
330
+ original_tag_text = tag.text
331
+ next if original_tag_text.nil?
332
+ content = translate_content_part(content,
333
+ original_tag_text,
334
+ locale)
335
+ end
336
+ end
337
+ content
338
+ end
339
+ end
340
+
341
+ def copy_extra_files(output_dir)
342
+ @extra_files.each do |file|
343
+ target_extra_file = File.join(output_dir, file)
344
+ FileUtils.mkdir_p(File.dirname(target_extra_file))
345
+ FileUtils.cp_r(file, target_extra_file)
346
+ end
347
+ end
348
+
349
+ def create_translated_files(original_files, output_dir)
350
+ original_files.each do |file|
351
+ translated_file = File.join(output_dir, file)
352
+ FileUtils.mkdir_p(File.dirname(translated_file))
353
+ content = File.read(file)
354
+
355
+ translated_text = yield(content)
356
+
357
+ File.open(translated_file, "w") do |file|
358
+ file.puts(translated_text)
359
+ end
360
+ end
361
+ end
362
+
363
+ def translate_content_part(content, original_text, locale)
364
+ translated_content = ""
365
+ text = YARD::I18n::Text.new(original_text)
366
+ translate_text = text.translate(locale)
367
+ original_text = original_text.each_line.collect do |line|
368
+ "(.+)#{Regexp.escape(line)}"
369
+ end
370
+ translate_text = translate_text.each_line.collect do |line|
371
+ "\\1#{line}"
372
+ end
373
+ content.sub(/#{original_text.join}/, translate_text.join)
374
+ end
375
+ end
376
+ end
377
+
378
+ # XXX: This module is the patch to translate documents.
379
+ # This module should be deleted after
380
+ # https://github.com/lsegal/yard/pull/594 is merged and released.
381
+ # @private
382
+ module YARD
383
+ # @private
384
+ module I18n
385
+ # @private
386
+ class Locale
387
+ def translate(message)
388
+ return message if @messages[message].nil?
389
+ return message if @messages[message].empty?
390
+ @messages[message]
391
+ end
392
+ end
393
+ end
394
+ end
395
+
396
+ # XXX: This module is the re-definition of YARD module.
397
+ # this module should be deleted in the next release of YARD.
398
+ # @private
399
+ module YARD
400
+ module CLI
401
+ # @private
402
+ class Yardoc
403
+ def parse_arguments(*args)
404
+ parse_yardopts_options(*args)
405
+
406
+ # Parse files and then command line arguments
407
+ optparse(*support_rdoc_document_file!) if use_document_file
408
+ optparse(*yardopts) if use_yardopts_file
409
+ optparse(*args)
410
+
411
+ # Last minute modifications
412
+ self.files = ['{lib,app}/**/*.rb', 'ext/**/*.c'] if self.files.empty?
413
+ self.files.delete_if {|x| x =~ /\A\s*\Z/ } # remove empty ones
414
+ readme = Dir.glob('README*').first
415
+ readme ||= Dir.glob(files.first).first if options.onefile
416
+ options.readme ||= CodeObjects::ExtraFileObject.new(readme) if readme
417
+ options.files.unshift(options.readme).uniq! if options.readme
418
+
419
+ Tags::Library.visible_tags -= hidden_tags
420
+ add_visibility_verifier
421
+ add_api_verifier
422
+
423
+ apply_locale
424
+
425
+ # US-ASCII is invalid encoding for onefile
426
+ if defined?(::Encoding) && options.onefile
427
+ if ::Encoding.default_internal == ::Encoding::US_ASCII
428
+ log.warn "--one-file is not compatible with US-ASCII encoding, using ASCII-8BIT"
429
+ ::Encoding.default_external, ::Encoding.default_internal = ['ascii-8bit'] * 2
430
+ end
431
+ end
432
+
433
+ if generate && !verify_markup_options
434
+ false
435
+ else
436
+ true
437
+ end
438
+ end
439
+
440
+ def apply_locale
441
+ options.files.each do |file|
442
+ file.locale = options.locale
443
+ end
444
+ end
445
+
446
+ def output_options(opts)
447
+ opts.separator ""
448
+ opts.separator "Output options:"
449
+
450
+ opts.on('--one-file', 'Generates output as a single file') do
451
+ options.onefile = true
452
+ end
453
+
454
+ opts.on('--list', 'List objects to standard out (implies -n)') do |format|
455
+ self.generate = false
456
+ self.list = true
457
+ end
458
+
459
+ opts.on('--no-public', "Don't show public methods. (default shows public)") do
460
+ visibilities.delete(:public)
461
+ end
462
+
463
+ opts.on('--protected', "Show protected methods. (default hides protected)") do
464
+ visibilities.push(:protected)
465
+ end
466
+
467
+ opts.on('--private', "Show private methods. (default hides private)") do
468
+ visibilities.push(:private)
469
+ end
470
+
471
+ opts.on('--no-private', "Hide objects with @private tag") do
472
+ options.verifier.add_expressions '!object.tag(:private) &&
473
+ (object.namespace.is_a?(CodeObjects::Proxy) || !object.namespace.tag(:private))'
474
+ end
475
+
476
+ opts.on('--[no-]api API', 'Generates documentation for a given API',
477
+ '(objects which define the correct @api tag).',
478
+ 'If --no-api is given, displays objects with',
479
+ 'no @api tag.') do |api|
480
+ api = '' if api == false
481
+ apis.push(api)
482
+ end
483
+
484
+ opts.on('--embed-mixins', "Embeds mixin methods into class documentation") do
485
+ options.embed_mixins << '*'
486
+ end
487
+
488
+ opts.on('--embed-mixin [MODULE]', "Embeds mixin methods from a particular",
489
+ " module into class documentation") do |mod|
490
+ options.embed_mixins << mod
491
+ end
492
+
493
+ opts.on('--no-highlight', "Don't highlight code blocks in output.") do
494
+ options.highlight = false
495
+ end
496
+
497
+ opts.on('--default-return TYPE', "Shown if method has no return type. ",
498
+ " (defaults to 'Object')") do |type|
499
+ options.default_return = type
500
+ end
501
+
502
+ opts.on('--hide-void-return', "Hides return types specified as 'void'. ",
503
+ " (default is shown)") do
504
+ options.hide_void_return = true
505
+ end
506
+
507
+ opts.on('--query QUERY', "Only show objects that match a specific query") do |query|
508
+ next if YARD::Config.options[:safe_mode]
509
+ options.verifier.add_expressions(query.taint)
510
+ end
511
+
512
+ opts.on('--title TITLE', 'Add a specific title to HTML documents') do |title|
513
+ options.title = title
514
+ end
515
+
516
+ opts.on('-r', '--readme FILE', '--main FILE', 'The readme file used as the title page',
517
+ ' of documentation.') do |readme|
518
+ if File.file?(readme)
519
+ options.readme = CodeObjects::ExtraFileObject.new(readme)
520
+ else
521
+ log.warn "Could not find readme file: #{readme}"
522
+ end
523
+ end
524
+
525
+ opts.on('--files FILE1,FILE2,...', 'Any extra comma separated static files to be ',
526
+ ' included (eg. FAQ)') do |files|
527
+ add_extra_files(*files.split(","))
528
+ end
529
+
530
+ opts.on('--asset FROM[:TO]', 'A file or directory to copy over to output ',
531
+ ' directory after generating') do |asset|
532
+ re = /^(?:\.\.\/|\/)/
533
+ from, to = *asset.split(':').map {|f| File.cleanpath(f) }
534
+ to ||= from
535
+ if from =~ re || to =~ re
536
+ log.warn "Invalid file '#{asset}'"
537
+ else
538
+ assets[from] = to
539
+ end
540
+ end
541
+
542
+ opts.on('-o', '--output-dir PATH',
543
+ 'The output directory. (defaults to ./doc)') do |dir|
544
+ options.serializer.basepath = dir
545
+ end
546
+
547
+ opts.on('-m', '--markup MARKUP',
548
+ 'Markup style used in documentation, like textile, ',
549
+ ' markdown or rdoc. (defaults to rdoc)') do |markup|
550
+ self.has_markup = true
551
+ options.markup = markup.to_sym
552
+ end
553
+
554
+ opts.on('-M', '--markup-provider MARKUP_PROVIDER',
555
+ 'Overrides the library used to process markup ',
556
+ ' formatting (specify the gem name)') do |markup_provider|
557
+ options.markup_provider = markup_provider.to_sym
558
+ end
559
+
560
+ opts.on('--charset ENC', 'Character set to use when parsing files ',
561
+ ' (default is system locale)') do |encoding|
562
+ begin
563
+ if defined?(Encoding) && Encoding.respond_to?(:default_external=)
564
+ Encoding.default_external, Encoding.default_internal = encoding, encoding
565
+ end
566
+ rescue ArgumentError => e
567
+ raise OptionParser::InvalidOption, e
568
+ end
569
+ end
570
+
571
+ opts.on('-t', '--template TEMPLATE',
572
+ 'The template to use. (defaults to "default")') do |template|
573
+ options.template = template.to_sym
574
+ end
575
+
576
+ opts.on('-p', '--template-path PATH',
577
+ 'The template path to look for templates in.',
578
+ ' (used with -t).') do |path|
579
+ next if YARD::Config.options[:safe_mode]
580
+ YARD::Templates::Engine.register_template_path(File.expand_path(path))
581
+ end
582
+
583
+ opts.on('-f', '--format FORMAT',
584
+ 'The output format for the template.',
585
+ ' (defaults to html)') do |format|
586
+ options.format = format.to_sym
587
+ end
588
+
589
+ opts.on('--no-stats', 'Don\'t print statistics') do
590
+ self.statistics = false
591
+ end
592
+
593
+ opts.on('--locale LOCALE',
594
+ 'The locale for generated documentation.',
595
+ ' (defaults to en)') do |locale|
596
+ options.locale = locale
597
+ end
598
+
599
+ opts.on('--po-dir DIR',
600
+ 'The directory that has .po files.',
601
+ ' (defaults to #{YARD::Registry.po_dir})') do |dir|
602
+ YARD::Registry.po_dir = dir
603
+ end
604
+ end
605
+ end
606
+ end
607
+
608
+ module CodeObjects
609
+ # @private
610
+ class ExtraFileObject
611
+ attr_writer :attributes
612
+ attr_reader :locale
613
+
614
+ def initialize(filename, contents = nil)
615
+ self.filename = filename
616
+ self.name = File.basename(filename).gsub(/\.[^.]+$/, '')
617
+ self.attributes = SymbolHash.new(false)
618
+ @original_contents = contents
619
+ @parsed = false
620
+ @locale = nil
621
+ ensure_parsed
622
+ end
623
+
624
+ def attributes
625
+ ensure_parsed
626
+ @attributes
627
+ end
628
+
629
+ def contents
630
+ ensure_parsed
631
+ @contents
632
+ end
633
+
634
+ def contents=(contents)
635
+ @original_contents = contents
636
+ @parsed = false
637
+ end
638
+
639
+ def locale=(locale)
640
+ @locale = locale
641
+ @parsed = false
642
+ end
643
+
644
+ private
645
+ def ensure_parsed
646
+ return if @parsed
647
+ @parsed = true
648
+ @contents = parse_contents(@original_contents || File.read(@filename))
649
+ end
650
+
651
+ def parse_contents(data)
652
+ retried = false
653
+ cut_index = 0
654
+ data = translate(data)
655
+ data = data.split("\n")
656
+ data.each_with_index do |line, index|
657
+ case line
658
+ when /^#!(\S+)\s*$/
659
+ if index == 0
660
+ attributes[:markup] = $1
661
+ else
662
+ cut_index = index
663
+ break
664
+ end
665
+ when /^\s*#\s*@(\S+)\s*(.+?)\s*$/
666
+ attributes[$1] = $2
667
+ else
668
+ cut_index = index
669
+ break
670
+ end
671
+ end
672
+ data = data[cut_index..-1] if cut_index > 0
673
+ contents = data.join("\n")
674
+
675
+ if contents.respond_to?(:force_encoding) && attributes[:encoding]
676
+ begin
677
+ contents.force_encoding(attributes[:encoding])
678
+ rescue ArgumentError
679
+ log.warn "Invalid encoding `#{attributes[:encoding]}' in #{filename}"
680
+ end
681
+ end
682
+ contents
683
+ rescue ArgumentError => e
684
+ if retried && e.message =~ /invalid byte sequence/
685
+ # This should never happen.
686
+ log.warn "Could not read #{filename}, #{e.message}. You probably want to set `--charset`."
687
+ return ''
688
+ end
689
+ data.force_encoding('binary') if data.respond_to?(:force_encoding)
690
+ retried = true
691
+ retry
692
+ end
693
+
694
+ def translate(data)
695
+ text = YARD::I18n::Text.new(data, :have_header => true)
696
+ text.translate(YARD::Registry.locale(locale))
697
+ end
698
+ end
699
+ end
700
+
701
+ module I18n
702
+ # @private
703
+ class Locale
704
+ def load(locale_directory)
705
+ return false if @name.nil?
706
+
707
+ po_file = File.join(locale_directory, "#{@name}.po")
708
+ return false unless File.exist?(po_file)
709
+
710
+ begin
711
+ require "gettext/tools/poparser"
712
+ require "gettext/runtime/mofile"
713
+ rescue LoadError
714
+ log.warn "Need gettext gem for i18n feature:"
715
+ log.warn " gem install gettext"
716
+ return false
717
+ end
718
+
719
+ parser = GetText::PoParser.new
720
+ parser.report_warning = false
721
+ data = GetText::MoFile.new
722
+ parser.parse_file(po_file, data)
723
+ @messages.merge!(data)
724
+ true
725
+ end
726
+ end
727
+ end
728
+
729
+ # @private
730
+ module Registry
731
+ DEFAULT_PO_DIR = "po"
732
+ class << self
733
+ def locale(name)
734
+ thread_local_store.locale(name)
735
+ end
736
+
737
+ attr_accessor :po_dir
738
+ undef po_dir, po_dir=
739
+ def po_dir=(dir) Thread.current[:__yard_po_dir__] = dir end
740
+ def po_dir
741
+ Thread.current[:__yard_po_dir__] ||= DEFAULT_PO_DIR
742
+ end
743
+ end
744
+ end
745
+
746
+ # @private
747
+ class RegistryStore
748
+ def initialize
749
+ @file = nil
750
+ @checksums = {}
751
+ @store = {}
752
+ @proxy_types = {}
753
+ @object_types = {:root => [:root]}
754
+ @notfound = {}
755
+ @loaded_objects = 0
756
+ @available_objects = 0
757
+ @locales = {}
758
+ @store[:root] = CodeObjects::RootObject.allocate
759
+ @store[:root].send(:initialize, nil, :root)
760
+ end
761
+
762
+ def locale(name)
763
+ @locales[name] ||= load_locale(name)
764
+ end
765
+
766
+ def load_locale(name)
767
+ locale = I18n::Locale.new(name)
768
+ locale.load(Registry.po_dir)
769
+ locale
770
+ end
286
771
  end
287
772
  end