rdoc 8.0.0 → 8.1.0
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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +1 -3
- data/RI.md +75 -75
- data/exe/rdoc +2 -2
- data/lib/rdoc/code_object/alias.rb +71 -69
- data/lib/rdoc/code_object/any_method.rb +305 -303
- data/lib/rdoc/code_object/attr.rb +150 -148
- data/lib/rdoc/code_object/class_module.rb +798 -792
- data/lib/rdoc/code_object/constant.rb +175 -173
- data/lib/rdoc/code_object/context/section.rb +142 -138
- data/lib/rdoc/code_object/context.rb +926 -958
- data/lib/rdoc/code_object/extend.rb +7 -5
- data/lib/rdoc/code_object/include.rb +7 -5
- data/lib/rdoc/code_object/method_attr.rb +326 -319
- data/lib/rdoc/code_object/mixin.rb +97 -95
- data/lib/rdoc/code_object/normal_class.rb +77 -78
- data/lib/rdoc/code_object/normal_module.rb +61 -59
- data/lib/rdoc/code_object/require.rb +23 -39
- data/lib/rdoc/code_object/single_class.rb +21 -19
- data/lib/rdoc/code_object/top_level.rb +212 -219
- data/lib/rdoc/code_object.rb +305 -303
- data/lib/rdoc/comment.rb +275 -273
- data/lib/rdoc/cross_reference.rb +192 -190
- data/lib/rdoc/encoding.rb +105 -103
- data/lib/rdoc/erb_partial.rb +13 -11
- data/lib/rdoc/erbio.rb +29 -27
- data/lib/rdoc/generator/aliki.rb +161 -153
- data/lib/rdoc/generator/darkfish.rb +645 -635
- data/lib/rdoc/generator/json_index.rb +233 -229
- data/lib/rdoc/generator/markup.rb +164 -146
- data/lib/rdoc/generator/pot/message_extractor.rb +57 -51
- data/lib/rdoc/generator/pot/po.rb +52 -51
- data/lib/rdoc/generator/pot/po_entry.rb +138 -132
- data/lib/rdoc/generator/pot.rb +85 -81
- data/lib/rdoc/generator/ri.rb +23 -19
- data/lib/rdoc/generator/template/aliki/DESIGN.md +6 -4
- data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_head.rhtml +10 -10
- data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +4 -4
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +207 -178
- data/lib/rdoc/generator/template/aliki/js/aliki.js +60 -84
- data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
- data/lib/rdoc/generator.rb +48 -46
- data/lib/rdoc/i18n/locale.rb +99 -95
- data/lib/rdoc/i18n/text.rb +109 -105
- data/lib/rdoc/i18n.rb +7 -5
- data/lib/rdoc/markdown/byte_runtime.rb +80 -0
- data/lib/rdoc/markdown.kpeg +15 -11
- data/lib/rdoc/markdown.rb +40 -47
- data/lib/rdoc/markup/block_quote.rb +12 -8
- data/lib/rdoc/markup/document.rb +127 -123
- data/lib/rdoc/markup/formatter.rb +219 -215
- data/lib/rdoc/markup/include.rb +33 -29
- data/lib/rdoc/markup/indented_paragraph.rb +37 -33
- data/lib/rdoc/markup/inline_parser.rb +281 -277
- data/lib/rdoc/markup/list.rb +80 -88
- data/lib/rdoc/markup/list_item.rb +73 -85
- data/lib/rdoc/markup/paragraph.rb +23 -19
- data/lib/rdoc/markup/parser.rb +501 -497
- data/lib/rdoc/markup/pre_process.rb +283 -279
- data/lib/rdoc/markup/raw.rb +2 -2
- data/lib/rdoc/markup/rule.rb +16 -12
- data/lib/rdoc/markup/to_ansi.rb +143 -139
- data/lib/rdoc/markup/to_bs.rb +72 -68
- data/lib/rdoc/markup/to_html.rb +594 -565
- data/lib/rdoc/markup/to_html_crossref.rb +234 -230
- data/lib/rdoc/markup/to_html_snippet.rb +232 -227
- data/lib/rdoc/markup/to_joined_paragraph.rb +36 -32
- data/lib/rdoc/markup/to_label.rb +63 -59
- data/lib/rdoc/markup/to_markdown.rb +212 -208
- data/lib/rdoc/markup/to_rdoc.rb +336 -332
- data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
- data/lib/rdoc/markup/to_test.rb +60 -56
- data/lib/rdoc/markup/to_tt_only.rb +84 -80
- data/lib/rdoc/markup/verbatim.rb +62 -58
- data/lib/rdoc/markup.rb +198 -196
- data/lib/rdoc/options.rb +1063 -1061
- data/lib/rdoc/parser/c.rb +1039 -1037
- data/lib/rdoc/parser/changelog.rb +319 -315
- data/lib/rdoc/parser/markdown.rb +17 -13
- data/lib/rdoc/parser/rbs.rb +239 -235
- data/lib/rdoc/parser/rd.rb +17 -13
- data/lib/rdoc/parser/ruby.rb +1245 -1124
- data/lib/rdoc/parser/ruby_colorizer.rb +263 -213
- data/lib/rdoc/parser/simple.rb +31 -27
- data/lib/rdoc/parser/text.rb +12 -8
- data/lib/rdoc/parser.rb +228 -220
- data/lib/rdoc/rbs_helper.rb +1 -1
- data/lib/rdoc/rd/inline.rb +57 -53
- data/lib/rdoc/rd.rb +90 -88
- data/lib/rdoc/rdoc.rb +500 -491
- data/lib/rdoc/ri/driver.rb +1140 -1135
- data/lib/rdoc/ri/formatter.rb +7 -3
- data/lib/rdoc/ri/paths.rb +140 -136
- data/lib/rdoc/ri/servlet.rb +354 -350
- data/lib/rdoc/ri/store.rb +4 -2
- data/lib/rdoc/ri/task.rb +55 -51
- data/lib/rdoc/ri.rb +14 -12
- data/lib/rdoc/rubygems_hook.rb +183 -181
- data/lib/rdoc/server.rb +349 -347
- data/lib/rdoc/stats/normal.rb +46 -42
- data/lib/rdoc/stats/quiet.rb +39 -35
- data/lib/rdoc/stats/verbose.rb +35 -31
- data/lib/rdoc/stats.rb +365 -363
- data/lib/rdoc/store.rb +888 -902
- data/lib/rdoc/task.rb +260 -256
- data/lib/rdoc/text.rb +135 -133
- data/lib/rdoc/token_stream.rb +101 -93
- data/lib/rdoc/tom_doc.rb +203 -201
- data/lib/rdoc/version.rb +1 -1
- metadata +4 -5
- data/lib/rdoc/markdown/literals.kpeg +0 -21
- data/lib/rdoc/markdown/literals.rb +0 -454
|
@@ -47,6 +47,8 @@ function createSearchInstance(input, result) {
|
|
|
47
47
|
html += `<span class="search-type search-type-${this.escapeHTML(typeClass)}">${typeLabel}</span>`;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
html += '</p>';
|
|
51
|
+
|
|
50
52
|
if (result.snippet)
|
|
51
53
|
html += `<div class="search-snippet">${result.snippet}</div>`;
|
|
52
54
|
|
|
@@ -75,10 +77,15 @@ function createSearchInstance(input, result) {
|
|
|
75
77
|
|
|
76
78
|
function hookSearch() {
|
|
77
79
|
const input = document.querySelector('#search-field');
|
|
78
|
-
const result = document.querySelector('#search-results
|
|
80
|
+
const result = document.querySelector('#search-results');
|
|
79
81
|
|
|
80
82
|
if (!input || !result) return; // Exit if search elements not found
|
|
81
83
|
|
|
84
|
+
const container = input.closest('[data-search-container]');
|
|
85
|
+
const searchToggle = document.querySelector('#search-toggle');
|
|
86
|
+
const searchEmpty = container && container.querySelector('.search-empty');
|
|
87
|
+
let focusBeforeOpen = null;
|
|
88
|
+
|
|
82
89
|
const search_section = document.querySelector('#search-section');
|
|
83
90
|
if (search_section) {
|
|
84
91
|
search_section.classList.remove("initially-hidden");
|
|
@@ -87,23 +94,65 @@ function hookSearch() {
|
|
|
87
94
|
const search = createSearchInstance(input, result);
|
|
88
95
|
if (!search) return;
|
|
89
96
|
|
|
97
|
+
if (searchEmpty) {
|
|
98
|
+
const originalRenderItem = search.renderItem;
|
|
99
|
+
search.renderItem = function(result) {
|
|
100
|
+
searchEmpty.style.display = 'none';
|
|
101
|
+
return originalRenderItem.call(this, result);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const showSearchSurface = () => {
|
|
106
|
+
if (container) container.classList.add('is-open');
|
|
107
|
+
if (searchToggle) searchToggle.setAttribute('aria-expanded', 'true');
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const openSearch = () => {
|
|
111
|
+
focusBeforeOpen = document.activeElement;
|
|
112
|
+
showSearchSurface();
|
|
113
|
+
input.focus({ preventScroll: true });
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const closeSearch = (restoreFocus = true) => {
|
|
117
|
+
if (container) container.classList.remove('is-open');
|
|
118
|
+
if (searchToggle) searchToggle.setAttribute('aria-expanded', 'false');
|
|
119
|
+
search.hide();
|
|
120
|
+
input.blur();
|
|
121
|
+
|
|
122
|
+
if (restoreFocus && focusBeforeOpen && typeof focusBeforeOpen.focus === 'function') {
|
|
123
|
+
focusBeforeOpen.focus({ preventScroll: true });
|
|
124
|
+
}
|
|
125
|
+
focusBeforeOpen = null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (searchToggle) searchToggle.addEventListener('click', openSearch);
|
|
129
|
+
if (container) {
|
|
130
|
+
container.querySelectorAll('[data-search-close]').forEach((control) => {
|
|
131
|
+
control.addEventListener('click', () => closeSearch());
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
hookFocus(openSearch);
|
|
136
|
+
|
|
90
137
|
// Hide search results when clicking outside the search area
|
|
91
138
|
document.addEventListener('click', (e) => {
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
139
|
+
if (container && container.contains(e.target)) return;
|
|
140
|
+
|
|
141
|
+
search.hide();
|
|
142
|
+
if (container && container.classList.contains('is-open')) closeSearch(false);
|
|
95
143
|
});
|
|
96
144
|
|
|
97
|
-
//
|
|
145
|
+
// Close the search surface on Escape
|
|
98
146
|
document.addEventListener('keydown', (e) => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
147
|
+
const isOpen = container && container.classList.contains('is-open');
|
|
148
|
+
if (e.key === 'Escape' && (isOpen || input.matches(":focus"))) {
|
|
149
|
+
closeSearch();
|
|
102
150
|
}
|
|
103
151
|
});
|
|
104
152
|
|
|
105
153
|
// Show search results when focusing on input (if there's a query)
|
|
106
154
|
input.addEventListener('focus', () => {
|
|
155
|
+
showSearchSurface();
|
|
107
156
|
if (input.value.trim()) {
|
|
108
157
|
search.show();
|
|
109
158
|
}
|
|
@@ -114,6 +163,7 @@ function hookSearch() {
|
|
|
114
163
|
const urlParams = new URLSearchParams(window.location.search);
|
|
115
164
|
const queryParam = urlParams.get('q');
|
|
116
165
|
if (queryParam) {
|
|
166
|
+
openSearch();
|
|
117
167
|
input.value = queryParam;
|
|
118
168
|
search.search(queryParam, false);
|
|
119
169
|
}
|
|
@@ -122,14 +172,14 @@ function hookSearch() {
|
|
|
122
172
|
|
|
123
173
|
/* ===== Keyboard Shortcuts ===== */
|
|
124
174
|
|
|
125
|
-
function hookFocus() {
|
|
175
|
+
function hookFocus(openSearch) {
|
|
126
176
|
document.addEventListener("keydown", (event) => {
|
|
127
177
|
if (document.activeElement.tagName === 'INPUT') {
|
|
128
178
|
return;
|
|
129
179
|
}
|
|
130
180
|
if (event.key === "/") {
|
|
131
181
|
event.preventDefault();
|
|
132
|
-
|
|
182
|
+
openSearch();
|
|
133
183
|
}
|
|
134
184
|
});
|
|
135
185
|
}
|
|
@@ -328,78 +378,6 @@ function setHeadingSelfLinkScrollHandlers() {
|
|
|
328
378
|
})
|
|
329
379
|
}
|
|
330
380
|
|
|
331
|
-
/* ===== Mobile Search Modal ===== */
|
|
332
|
-
|
|
333
|
-
function hookSearchModal() {
|
|
334
|
-
const searchToggle = document.querySelector('#search-toggle');
|
|
335
|
-
const searchModal = document.querySelector('#search-modal');
|
|
336
|
-
const searchModalClose = document.querySelector('#search-modal-close');
|
|
337
|
-
const searchModalBackdrop = document.querySelector('.search-modal-backdrop');
|
|
338
|
-
const searchInput = document.querySelector('#search-field-mobile');
|
|
339
|
-
const searchResults = document.querySelector('#search-results-mobile');
|
|
340
|
-
const searchEmpty = document.querySelector('.search-modal-empty');
|
|
341
|
-
|
|
342
|
-
if (!searchToggle || !searchModal) return;
|
|
343
|
-
|
|
344
|
-
// Initialize search for mobile modal
|
|
345
|
-
const mobileSearch = createSearchInstance(searchInput, searchResults);
|
|
346
|
-
if (!mobileSearch) return;
|
|
347
|
-
|
|
348
|
-
// Hide empty state when there are results
|
|
349
|
-
const originalRenderItem = mobileSearch.renderItem;
|
|
350
|
-
mobileSearch.renderItem = function(result) {
|
|
351
|
-
if (searchEmpty) searchEmpty.style.display = 'none';
|
|
352
|
-
return originalRenderItem.call(this, result);
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
const openSearchModal = () => {
|
|
356
|
-
searchModal.hidden = false;
|
|
357
|
-
document.body.style.overflow = 'hidden';
|
|
358
|
-
// Focus input after animation
|
|
359
|
-
setTimeout(() => {
|
|
360
|
-
if (searchInput) searchInput.focus();
|
|
361
|
-
}, 100);
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
const closeSearchModal = () => {
|
|
365
|
-
searchModal.hidden = true;
|
|
366
|
-
document.body.style.overflow = '';
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
// Open on button click
|
|
370
|
-
searchToggle.addEventListener('click', openSearchModal);
|
|
371
|
-
|
|
372
|
-
// Close on close button click
|
|
373
|
-
if (searchModalClose) {
|
|
374
|
-
searchModalClose.addEventListener('click', closeSearchModal);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Close on backdrop click
|
|
378
|
-
if (searchModalBackdrop) {
|
|
379
|
-
searchModalBackdrop.addEventListener('click', closeSearchModal);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Close on Escape key
|
|
383
|
-
document.addEventListener('keydown', (e) => {
|
|
384
|
-
if (e.key === 'Escape' && !searchModal.hidden) {
|
|
385
|
-
closeSearchModal();
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
// Check for ?q= URL parameter on mobile and open modal
|
|
390
|
-
if (typeof URLSearchParams !== 'undefined') {
|
|
391
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
392
|
-
const queryParam = urlParams.get('q');
|
|
393
|
-
const isSmallViewport = window.matchMedia("(max-width: 1023px)").matches;
|
|
394
|
-
|
|
395
|
-
if (queryParam && isSmallViewport) {
|
|
396
|
-
openSearchModal();
|
|
397
|
-
searchInput.value = queryParam;
|
|
398
|
-
mobileSearch.search(queryParam, false);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
381
|
/* ===== Code Block Copy Functionality ===== */
|
|
404
382
|
|
|
405
383
|
function createCopyButton() {
|
|
@@ -503,11 +481,9 @@ function showCopySuccess(button) {
|
|
|
503
481
|
document.addEventListener('DOMContentLoaded', () => {
|
|
504
482
|
hookSourceViews();
|
|
505
483
|
hookSearch();
|
|
506
|
-
hookFocus();
|
|
507
484
|
hookSidebar();
|
|
508
485
|
generateToc();
|
|
509
486
|
setHeadingSelfLinkScrollHandlers();
|
|
510
487
|
hookTocActiveHighlighting();
|
|
511
|
-
hookSearchModal();
|
|
512
488
|
wrapCodeBlocksWithCopyButton();
|
|
513
489
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<footer id="validator-badges" role="contentinfo">
|
|
2
2
|
<p><a href="https://validator.w3.org/check/referer">Validate</a></p>
|
|
3
|
-
<p>Generated by <a href="https://ruby.github.io/rdoc/">RDoc</a> <%=
|
|
3
|
+
<p>Generated by <a href="https://ruby.github.io/rdoc/">RDoc</a> <%= VERSION %>.</p>
|
|
4
4
|
<p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.</p>
|
|
5
5
|
</footer>
|
data/lib/rdoc/generator.rb
CHANGED
|
@@ -1,52 +1,54 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
# RDoc
|
|
5
|
-
#
|
|
6
|
-
# RDoc::Generator::
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# for
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# RDoc
|
|
35
|
-
#
|
|
36
|
-
# RDoc
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
2
|
+
module RDoc
|
|
3
|
+
##
|
|
4
|
+
# RDoc uses generators to turn parsed source code in the form of an
|
|
5
|
+
# RDoc::CodeObject tree into some form of output. RDoc comes with the HTML
|
|
6
|
+
# generator RDoc::Generator::Darkfish and an ri data generator
|
|
7
|
+
# RDoc::Generator::RI.
|
|
8
|
+
#
|
|
9
|
+
# == Registering a Generator
|
|
10
|
+
#
|
|
11
|
+
# Generators are registered by calling RDoc::RDoc.add_generator with the class
|
|
12
|
+
# of the generator:
|
|
13
|
+
#
|
|
14
|
+
# class My::Awesome::Generator
|
|
15
|
+
# RDoc::RDoc.add_generator self
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# == Adding Options to +rdoc+
|
|
19
|
+
#
|
|
20
|
+
# Before option processing in +rdoc+, RDoc::Options will call ::setup_options
|
|
21
|
+
# on the generator class with an RDoc::Options instance. The generator can
|
|
22
|
+
# use RDoc::Options#option_parser to add command-line options to the +rdoc+
|
|
23
|
+
# tool. See RDoc::Options@Custom+Options for an example and see OptionParser
|
|
24
|
+
# for details on how to add options.
|
|
25
|
+
#
|
|
26
|
+
# You can extend the RDoc::Options instance with additional accessors for your
|
|
27
|
+
# generator.
|
|
28
|
+
#
|
|
29
|
+
# == Generator Instantiation
|
|
30
|
+
#
|
|
31
|
+
# After parsing, RDoc::RDoc will instantiate a generator by calling
|
|
32
|
+
# #initialize with an RDoc::Store instance and an RDoc::Options instance.
|
|
33
|
+
#
|
|
34
|
+
# The RDoc::Store instance holds documentation for parsed source code. In
|
|
35
|
+
# RDoc 3 and earlier the RDoc::TopLevel class held this data. When upgrading
|
|
36
|
+
# a generator from RDoc 3 and earlier you should only need to replace
|
|
37
|
+
# RDoc::TopLevel with the store instance.
|
|
38
|
+
#
|
|
39
|
+
# RDoc will then call #generate on the generator instance. You can use the
|
|
40
|
+
# various methods on RDoc::Store and in the RDoc::CodeObject tree to create
|
|
41
|
+
# your desired output format.
|
|
41
42
|
|
|
42
|
-
module
|
|
43
|
+
module Generator
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
autoload :Markup, "#{__dir__}/generator/markup"
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
autoload :Aliki, "#{__dir__}/generator/aliki"
|
|
48
|
+
autoload :Darkfish, "#{__dir__}/generator/darkfish"
|
|
49
|
+
autoload :JsonIndex, "#{__dir__}/generator/json_index"
|
|
50
|
+
autoload :RI, "#{__dir__}/generator/ri"
|
|
51
|
+
autoload :POT, "#{__dir__}/generator/pot"
|
|
51
52
|
|
|
53
|
+
end
|
|
52
54
|
end
|
data/lib/rdoc/i18n/locale.rb
CHANGED
|
@@ -1,102 +1,106 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
# This object provides the following two features:
|
|
6
|
-
#
|
|
7
|
-
# * Loads translated messages from .po file.
|
|
8
|
-
# * Translates a message into the locale.
|
|
9
|
-
|
|
10
|
-
class RDoc::I18n::Locale
|
|
11
|
-
|
|
12
|
-
@@locales = {} # :nodoc:
|
|
13
|
-
|
|
14
|
-
class << self
|
|
15
|
-
|
|
2
|
+
module RDoc
|
|
3
|
+
module I18n
|
|
16
4
|
##
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
def [](locale_name)
|
|
20
|
-
@@locales[locale_name] ||= new(locale_name)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
##
|
|
24
|
-
# Sets the locale object for +locale_name+.
|
|
5
|
+
# A message container for a locale.
|
|
25
6
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
7
|
+
# This object provides the following two features:
|
|
8
|
+
#
|
|
9
|
+
# * Loads translated messages from .po file.
|
|
10
|
+
# * Translates a message into the locale.
|
|
11
|
+
|
|
12
|
+
class Locale
|
|
13
|
+
|
|
14
|
+
@@locales = {} # :nodoc:
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Returns the locale object for +locale_name+.
|
|
20
|
+
|
|
21
|
+
def [](locale_name)
|
|
22
|
+
@@locales[locale_name] ||= new(locale_name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Sets the locale object for +locale_name+.
|
|
27
|
+
#
|
|
28
|
+
# Normally, this method is not used. This method is useful for
|
|
29
|
+
# testing.
|
|
30
|
+
|
|
31
|
+
def []=(locale_name, locale)
|
|
32
|
+
@@locales[locale_name] = locale
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# The name of the locale. It uses IETF language tag format
|
|
39
|
+
# +[language[_territory][.codeset][@modifier]]+.
|
|
40
|
+
#
|
|
41
|
+
# See also {BCP 47 - Tags for Identifying
|
|
42
|
+
# Languages}[http://tools.ietf.org/rfc/bcp/bcp47.txt].
|
|
43
|
+
|
|
44
|
+
attr_reader :name
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Creates a new locale object for +name+ locale. +name+ must
|
|
48
|
+
# follow IETF language tag format.
|
|
49
|
+
|
|
50
|
+
def initialize(name)
|
|
51
|
+
@name = name
|
|
52
|
+
@messages = {}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Loads translation messages from +locale_directory+/+@name+/rdoc.po
|
|
57
|
+
# or +locale_directory+/+@name+.po. The former has high priority.
|
|
58
|
+
#
|
|
59
|
+
# This method requires gettext gem for parsing .po file. If you
|
|
60
|
+
# don't have gettext gem, this method doesn't load .po file. This
|
|
61
|
+
# method warns and returns +false+.
|
|
62
|
+
#
|
|
63
|
+
# Returns +true+ if succeeded, +false+ otherwise.
|
|
64
|
+
|
|
65
|
+
def load(locale_directory)
|
|
66
|
+
return false if @name.nil?
|
|
67
|
+
|
|
68
|
+
po_file_candidates = [
|
|
69
|
+
File.join(locale_directory, @name, 'rdoc.po'),
|
|
70
|
+
File.join(locale_directory, "#{@name}.po"),
|
|
71
|
+
]
|
|
72
|
+
po_file = po_file_candidates.find do |po_file_candidate|
|
|
73
|
+
File.exist?(po_file_candidate)
|
|
74
|
+
end
|
|
75
|
+
return false unless po_file
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
require 'gettext/po_parser'
|
|
79
|
+
require 'gettext/mo'
|
|
80
|
+
rescue LoadError
|
|
81
|
+
warn('Need gettext gem for i18n feature:')
|
|
82
|
+
warn(' gem install gettext')
|
|
83
|
+
return false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
po_parser = GetText::POParser.new
|
|
87
|
+
messages = GetText::MO.new
|
|
88
|
+
po_parser.report_warning = false
|
|
89
|
+
po_parser.parse_file(po_file, messages)
|
|
90
|
+
|
|
91
|
+
@messages.merge!(messages)
|
|
92
|
+
|
|
93
|
+
true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
##
|
|
97
|
+
# Translates the +message+ into locale. If there is no translation
|
|
98
|
+
# messages for +message+ in locale, +message+ itself is returned.
|
|
99
|
+
|
|
100
|
+
def translate(message)
|
|
101
|
+
@messages[message] || message
|
|
102
|
+
end
|
|
52
103
|
|
|
53
|
-
##
|
|
54
|
-
# Loads translation messages from +locale_directory+/+@name+/rdoc.po
|
|
55
|
-
# or +locale_directory+/+@name+.po. The former has high priority.
|
|
56
|
-
#
|
|
57
|
-
# This method requires gettext gem for parsing .po file. If you
|
|
58
|
-
# don't have gettext gem, this method doesn't load .po file. This
|
|
59
|
-
# method warns and returns +false+.
|
|
60
|
-
#
|
|
61
|
-
# Returns +true+ if succeeded, +false+ otherwise.
|
|
62
|
-
|
|
63
|
-
def load(locale_directory)
|
|
64
|
-
return false if @name.nil?
|
|
65
|
-
|
|
66
|
-
po_file_candidates = [
|
|
67
|
-
File.join(locale_directory, @name, 'rdoc.po'),
|
|
68
|
-
File.join(locale_directory, "#{@name}.po"),
|
|
69
|
-
]
|
|
70
|
-
po_file = po_file_candidates.find do |po_file_candidate|
|
|
71
|
-
File.exist?(po_file_candidate)
|
|
72
|
-
end
|
|
73
|
-
return false unless po_file
|
|
74
|
-
|
|
75
|
-
begin
|
|
76
|
-
require 'gettext/po_parser'
|
|
77
|
-
require 'gettext/mo'
|
|
78
|
-
rescue LoadError
|
|
79
|
-
warn('Need gettext gem for i18n feature:')
|
|
80
|
-
warn(' gem install gettext')
|
|
81
|
-
return false
|
|
82
104
|
end
|
|
83
|
-
|
|
84
|
-
po_parser = GetText::POParser.new
|
|
85
|
-
messages = GetText::MO.new
|
|
86
|
-
po_parser.report_warning = false
|
|
87
|
-
po_parser.parse_file(po_file, messages)
|
|
88
|
-
|
|
89
|
-
@messages.merge!(messages)
|
|
90
|
-
|
|
91
|
-
true
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
##
|
|
95
|
-
# Translates the +message+ into locale. If there is no translation
|
|
96
|
-
# messages for +message+ in locale, +message+ itself is returned.
|
|
97
|
-
|
|
98
|
-
def translate(message)
|
|
99
|
-
@messages[message] || message
|
|
100
105
|
end
|
|
101
|
-
|
|
102
106
|
end
|