packnga 0.9.4 → 0.9.5

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/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