ofm_gettext 2.0.0 → 2.0.1

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.
Files changed (45) hide show
  1. data/COPYING +56 -0
  2. data/ChangeLog-1 +2016 -0
  3. data/NEWS-1 +414 -0
  4. data/gettext.gemspec +7 -8
  5. data/lib/gettext/runtime/class_info.rb +69 -0
  6. data/lib/gettext/runtime/locale_path.rb +123 -0
  7. data/lib/gettext/runtime/mofile.rb +332 -0
  8. data/lib/gettext/runtime/textdomain.rb +179 -0
  9. data/lib/gettext/runtime/textdomain_group.rb +26 -0
  10. data/lib/gettext/runtime/textdomain_manager.rb +214 -0
  11. data/lib/gettext/tools/parser/erb.rb +54 -0
  12. data/lib/gettext/tools/parser/glade.rb +100 -0
  13. data/lib/gettext/tools/parser/ruby.rb +226 -0
  14. data/lib/gettext/tools/pomessage.rb +199 -0
  15. data/lib/gettext/tools/poparser.rb +358 -0
  16. data/po/uk/rgettext.po +143 -0
  17. data/samples/cgi/po/uk/helloerb1.po +62 -0
  18. data/samples/cgi/po/uk/helloerb2.po +54 -0
  19. data/samples/cgi/po/uk/hellolib.po +26 -0
  20. data/samples/cgi/po/uk/main.po +84 -0
  21. data/samples/po/uk/hello.po +22 -0
  22. data/samples/po/uk/hello2.po +30 -0
  23. data/samples/po/uk/hello_glade2.po +34 -0
  24. data/samples/po/uk/hello_gtk.po +22 -0
  25. data/samples/po/uk/hello_noop.po +26 -0
  26. data/samples/po/uk/hello_plural.po +29 -0
  27. data/samples/po/uk/hello_tk.po +26 -0
  28. data/test/po/ja/test3.po +19 -0
  29. data/test/po/li/plural_error.po +27 -0
  30. data/test/test_locale_path.rb +76 -0
  31. data/test/test_po_generation.rb +22 -0
  32. data/test/test_pomessage.rb +101 -0
  33. data/test/test_textdomain_bind.rb +39 -0
  34. data/test/test_thread.rb +43 -0
  35. data/test/tools/files/app.pot +0 -0
  36. data/test/tools/files/de/app.po +0 -0
  37. data/test/tools/files/en/app.po +0 -0
  38. data/test/tools/files/en/test.po +21 -0
  39. data/test/tools/files/simple_1.po +2 -0
  40. data/test/tools/files/simple_2.po +2 -0
  41. data/test/tools/files/simple_translation.rb +3 -0
  42. data/test/tools/files/version.po +7 -0
  43. data/test/tools/test.pot +21 -0
  44. data/test/tools/test_tools.rb +63 -0
  45. metadata +380 -335
@@ -0,0 +1,179 @@
1
+ # encoding: utf-8
2
+
3
+ =begin
4
+ textdomain.rb - GetText::Textdomain
5
+
6
+ Copyright (C) 2001-2009 Masao Mutoh
7
+ Copyright (C) 2001-2003 Masahiro Sakai
8
+
9
+ Masahiro Sakai <s01397ms@sfc.keio.ac.jp>
10
+ Masao Mutoh <mutomasa at gmail.com>
11
+
12
+ You may redistribute it and/or modify it under the same
13
+ license terms as Ruby or LGPL.
14
+ =end
15
+
16
+ require 'gettext/core_ext/string'
17
+ require 'gettext/runtime/mofile'
18
+ require 'gettext/runtime/locale_path'
19
+
20
+ module GetText
21
+ # GetText::TextDomain class manages mo-files of a textdomain.
22
+ #
23
+ # Usually, you don't need to use this class directly.
24
+ #
25
+ # Notice: This class is unstable. APIs will be changed.
26
+ class TextDomain
27
+
28
+ attr_reader :output_charset
29
+ attr_reader :mofiles
30
+ attr_reader :name
31
+
32
+ @@cached = ! $DEBUG
33
+ # Cache the mo-file or not.
34
+ # Default is true. If $DEBUG is set then false.
35
+ def self.cached?
36
+ @@cached
37
+ end
38
+
39
+ # Set to cache the mo-file or not.
40
+ # * val: true if cached, otherwise false.
41
+ def self.cached=(val)
42
+ @@cached = val
43
+ end
44
+
45
+ # Add default locale path. Usually you should use GetText.add_default_locale_path instead.
46
+ # * path: a new locale path. (e.g.) "/usr/share/locale/%{lang}/LC_MESSAGES/%{name}.mo"
47
+ # ('locale' => "ja_JP", 'name' => "textdomain")
48
+ # * Returns: the new DEFAULT_LOCALE_PATHS
49
+ def self.add_default_locale_path(path)
50
+ warn "Deprecated. Use GetText::LocalePath.add_default_rule instead."
51
+ LocalePath.add_default_rule(path)
52
+ end
53
+
54
+ # Creates a new GetText::TextDomain.
55
+ # * name: the textdomain name.
56
+ # * topdir: the locale path ("%{topdir}/%{lang}/LC_MESSAGES/%{name}.mo") or nil.
57
+ # * output_charset: output charset.
58
+ # * Returns: a newly created GetText::TextDomain object.
59
+ def initialize(name, topdir = nil, output_charset = nil)
60
+ @name, @output_charset = name, output_charset
61
+
62
+ @locale_path = LocalePath.new(@name, topdir)
63
+ @mofiles = {}
64
+ end
65
+
66
+ # Translates the translated string.
67
+ # * lang: Locale::Tag::Simple's subclass.
68
+ # * msgid: the original message.
69
+ # * Returns: the translated string or nil.
70
+ def translate_singluar_message(lang, msgid)
71
+ return "" if msgid == "" or msgid.nil?
72
+
73
+ lang_key = lang.to_s
74
+
75
+ mofile = nil
76
+ if self.class.cached?
77
+ mofile = @mofiles[lang_key]
78
+ end
79
+ unless mofile
80
+ mofile = load_mo(lang)
81
+ end
82
+
83
+ if (! mofile) or (mofile ==:empty)
84
+ return nil
85
+ end
86
+
87
+ msgstr = mofile[msgid]
88
+ if msgstr and (msgstr.size > 0)
89
+ msgstr
90
+ elsif msgid.include?("\000")
91
+ # Check "aaa\000bbb" and show warning but return the singluar part.
92
+ ret = nil
93
+ msgid_single = msgid.split("\000")[0]
94
+ mofile.each{|key, val|
95
+ if key =~ /^#{Regexp.quote(msgid_single)}\000/
96
+ # Usually, this is not caused to make po-files from rgettext.
97
+ warn %Q[Warning: n_("#{msgid_single}", "#{msgid.split("\000")[1]}") and n_("#{key.gsub(/\000/, '", "')}") are duplicated.]
98
+ ret = val
99
+ break
100
+ end
101
+ }
102
+ ret
103
+ else
104
+ ret = nil
105
+ mofile.each{|key, val|
106
+ if key =~ /^#{Regexp.quote(msgid)}\000/
107
+ ret = val.split("\000")[0]
108
+ break
109
+ end
110
+ }
111
+ ret
112
+ end
113
+ end
114
+
115
+ DEFAULT_PLURAL_CALC = Proc.new{|n| n != 1}
116
+ DEFAULT_SINGLE_CALC = Proc.new{|n| 0}
117
+
118
+ # Translates the translated string.
119
+ # * lang: Locale::Tag::Simple's subclass.
120
+ # * msgid: the original message.
121
+ # * msgid_plural: the original message(plural).
122
+ # * Returns: the translated string as an Array ([[msgstr1, msgstr2, ...], cond]) or nil.
123
+ def translate_plural_message(lang, msgid, msgid_plural) #:nodoc:
124
+ key = msgid + "\000" + msgid_plural
125
+ msg = translate_singluar_message(lang, key)
126
+ ret = nil
127
+ if ! msg
128
+ ret = nil
129
+ elsif msg.include?("\000")
130
+ # [[msgstr[0], msgstr[1], msgstr[2],...], cond]
131
+ mofile = @mofiles[lang.to_posix.to_s]
132
+ cond = (mofile and mofile != :empty) ? mofile.plural_as_proc : DEFAULT_PLURAL_CALC
133
+ ret = [msg.split("\000"), cond]
134
+ else
135
+ ret = [[msg], DEFAULT_SINGLE_CALC]
136
+ end
137
+ ret
138
+ end
139
+
140
+ # Clear cached mofiles.
141
+ def clear
142
+ @mofiles = {}
143
+ end
144
+
145
+ # Set output_charset.
146
+ # * charset: output charset.
147
+ def output_charset=(charset)
148
+ @output_charset = charset
149
+ clear
150
+ end
151
+
152
+ private
153
+ # Load a mo-file from the file.
154
+ # lang is the subclass of Locale::Tag::Simple.
155
+ def load_mo(lang)
156
+ lang = lang.to_posix unless lang.kind_of? Locale::Tag::Posix
157
+ lang_key = lang.to_s
158
+
159
+ mofile = @mofiles[lang_key]
160
+ if mofile
161
+ if mofile == :empty
162
+ return :empty
163
+ elsif ! self.class.cached?
164
+ mofile.update!
165
+ end
166
+ return mofile
167
+ end
168
+
169
+ path = @locale_path.current_path(lang)
170
+
171
+ if path
172
+ charset = @output_charset || lang.charset || Locale.charset || "UTF-8"
173
+ @mofiles[lang_key] = MOFile.open(path, charset)
174
+ else
175
+ @mofiles[lang_key] = :empty
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ =begin
4
+ gettext/textdomain_group - GetText::TextDomainGroup class
5
+
6
+ Copyright (C) 2009 Masao Mutoh
7
+
8
+ You may redistribute it and/or modify it under the same
9
+ license terms as Ruby or LGPL.
10
+
11
+ =end
12
+
13
+ module GetText
14
+
15
+ class TextDomainGroup
16
+ attr_reader :textdomains
17
+
18
+ def initialize
19
+ @textdomains = []
20
+ end
21
+
22
+ def add(textdomain)
23
+ @textdomains.unshift(textdomain) unless @textdomains.include? textdomain
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,214 @@
1
+ # encoding: utf-8
2
+
3
+ =begin
4
+ gettext/textdomain_manager - GetText::TextDomainManager class
5
+
6
+ Copyright (C) 2009 Masao Mutoh
7
+
8
+ You may redistribute it and/or modify it under the same
9
+ license terms as Ruby or LGPL.
10
+
11
+ =end
12
+
13
+ require 'gettext/runtime/class_info'
14
+ require 'gettext/runtime/textdomain'
15
+ require 'gettext/runtime/textdomain_group'
16
+
17
+ module GetText
18
+
19
+ module TextDomainManager
20
+
21
+ @@textdomain_pool = {}
22
+ @@textdomain_group_pool = {}
23
+
24
+ @@output_charset = nil
25
+ @@gettext_classes = []
26
+
27
+ @@singular_message_cache = {}
28
+ @@plural_message_cache = {}
29
+ @@cached = ! $DEBUG
30
+
31
+ extend self
32
+
33
+ # Find textdomain by name
34
+ def textdomain_pool(domainname)
35
+ @@textdomain_pool[domainname]
36
+ end
37
+
38
+ # Set the value whether cache messages or not.
39
+ # true to cache messages, otherwise false.
40
+ #
41
+ # Default is true. If $DEBUG is false, messages are not checked even if
42
+ # this value is true.
43
+ def cached=(val)
44
+ @@cached = val
45
+ TextDomain.cached = val
46
+ end
47
+
48
+ # Return the cached value.
49
+ def cached?
50
+ TextDomain.cached?
51
+ end
52
+
53
+ # Gets the output charset.
54
+ def output_charset
55
+ @@output_charset
56
+ end
57
+
58
+ # Sets the output charset.The program can have a output charset.
59
+ def output_charset=(charset)
60
+ @@output_charset = charset
61
+ @@textdomain_pool.each do |key, textdomain|
62
+ textdomain.output_charset = charset
63
+ end
64
+ end
65
+
66
+ # bind textdomain to the class.
67
+ def bind_to(klass, domainname, options = {})
68
+ warn "Bind the domain '#{domainname}' to '#{klass}'. " if $DEBUG
69
+
70
+ charset = options[:output_charset] || self.output_charset
71
+ textdomain = create_or_find_textdomain(domainname,options[:path],charset)
72
+ target_klass = ClassInfo.normalize_class(klass)
73
+ create_or_find_textdomain_group(target_klass).add(textdomain)
74
+ @@gettext_classes << target_klass unless @@gettext_classes.include? target_klass
75
+
76
+ textdomain
77
+ end
78
+
79
+ def each_textdomains(klass) #:nodoc:
80
+ lang = Locale.candidates[0]
81
+ ClassInfo.related_classes(klass, @@gettext_classes).each do |target|
82
+ msg = nil
83
+ if group = @@textdomain_group_pool[target]
84
+ group.textdomains.each do |textdomain|
85
+ yield textdomain, lang
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # Translates msgid, but if there are no localized text,
92
+ # it returns a last part of msgid separeted "div" or whole of the msgid with no "div".
93
+ #
94
+ # * msgid: the message id.
95
+ # * div: separator or nil.
96
+ # * Returns: the localized text by msgid. If there are no localized text,
97
+ # it returns a last part of msgid separeted "div".
98
+ def translate_singluar_message(klass, msgid, div = nil)
99
+ klass = ClassInfo.normalize_class(klass)
100
+ key = [Locale.current, klass, msgid, div].hash
101
+ msg = @@singular_message_cache[key]
102
+ return msg if msg and @@cached
103
+ # Find messages from related classes.
104
+ each_textdomains(klass) do |textdomain, lang|
105
+ msg = textdomain.translate_singluar_message(lang, msgid)
106
+ break if msg
107
+ end
108
+
109
+ # If not found, return msgid.
110
+ msg ||= msgid
111
+ if div and msg == msgid
112
+ if index = msg.rindex(div)
113
+ msg = msg[(index + 1)..-1]
114
+ end
115
+ end
116
+ @@singular_message_cache[key] = msg
117
+ end
118
+
119
+ # This function is similar to the get_singluar_message function
120
+ # as it finds the message catalogs in the same way.
121
+ # But it takes two extra arguments for plural form.
122
+ # The msgid parameter must contain the singular form of the string to be converted.
123
+ # It is also used as the key for the search in the catalog.
124
+ # The msgid_plural parameter is the plural form.
125
+ # The parameter n is used to determine the plural form.
126
+ # If no message catalog is found msgid1 is returned if n == 1, otherwise msgid2.
127
+ # And if msgid includes "div", it returns a last part of msgid separeted "div".
128
+ #
129
+ # * msgid: the singular form with "div". (e.g. "Special|An apple", "An apple")
130
+ # * msgid_plural: the plural form. (e.g. "%{num} Apples")
131
+ # * n: a number used to determine the plural form.
132
+ # * div: the separator. Default is "|".
133
+ # * Returns: the localized text which key is msgid_plural if n is plural(follow plural-rule) or msgid.
134
+ # "plural-rule" is defined in po-file.
135
+ #
136
+ # or
137
+ #
138
+ # * [msgid, msgid_plural] : msgid and msgid_plural an Array
139
+ # * n: a number used to determine the plural form.
140
+ # * div: the separator. Default is "|".
141
+ def translate_plural_message(klass, arg1, arg2, arg3 = "|", arg4 = "|")
142
+ klass = ClassInfo.normalize_class(klass)
143
+ # parse arguments
144
+ if arg1.kind_of?(Array)
145
+ msgid = arg1[0]
146
+ msgid_plural = arg1[1]
147
+ n = arg2
148
+ if arg3 and arg3.kind_of? Numeric
149
+ raise ArgumentError, _("ngettext: 3rd parmeter is wrong: value = %{number}") % {:number => arg3}
150
+ end
151
+ div = arg3
152
+ else
153
+ msgid = arg1
154
+ msgid_plural = arg2
155
+ raise ArgumentError, _("ngettext: 3rd parameter should be a number, not nil.") unless arg3
156
+ n = arg3
157
+ div = arg4
158
+ end
159
+
160
+ key = [Locale.current, klass, msgid, msgid_plural, div].hash
161
+ msgs = @@plural_message_cache[key]
162
+ unless (msgs and @@cached)
163
+ # Find messages from related classes.
164
+ msgs = nil
165
+ each_textdomains(klass) do |textdomain, lang|
166
+ msgs = textdomain.translate_plural_message(lang, msgid, msgid_plural)
167
+ break if msgs
168
+ end
169
+
170
+ msgs = [[msgid, msgid_plural], TextDomain::DEFAULT_PLURAL_CALC] unless msgs
171
+
172
+ msgstrs = msgs[0]
173
+ if div and msgstrs[0] == msgid and index = msgstrs[0].rindex(div)
174
+ msgstrs[0] = msgstrs[0][(index + 1)..-1]
175
+ end
176
+ @@plural_message_cache[key] = msgs
177
+ end
178
+
179
+ # Return the singular or plural message.
180
+ msgstrs = msgs[0]
181
+ plural = msgs[1].call(n)
182
+ return msgstrs[plural] if plural.kind_of?(Numeric)
183
+ return plural ? msgstrs[1] : msgstrs[0]
184
+ end
185
+
186
+ # for testing.
187
+ def clear_all_textdomains
188
+ @@textdomain_pool = {}
189
+ @@textdomain_group_pool = {}
190
+ @@gettext_classes = []
191
+ clear_caches
192
+ end
193
+
194
+ # for testing.
195
+ def clear_caches
196
+ @@singular_message_cache = {}
197
+ @@plural_message_cache = {}
198
+ end
199
+
200
+ def create_or_find_textdomain_group(klass) #:nodoc:
201
+ group = @@textdomain_group_pool[klass]
202
+ return group if group
203
+
204
+ @@textdomain_group_pool[klass] = TextDomainGroup.new
205
+ end
206
+
207
+ def create_or_find_textdomain(name, path, charset)#:nodoc:
208
+ textdomain = @@textdomain_pool[name]
209
+ return textdomain if textdomain
210
+
211
+ @@textdomain_pool[name] = TextDomain.new(name, path, charset)
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ =begin
4
+ parser/erb.rb - parser for ERB
5
+
6
+ Copyright (C) 2005-2009 Masao Mutoh
7
+
8
+ You may redistribute it and/or modify it under the same
9
+ license terms as Ruby or LGPL.
10
+ =end
11
+
12
+ require 'erb'
13
+ require 'gettext/tools/parser/ruby'
14
+
15
+ module GetText
16
+ module ErbParser
17
+ extend self
18
+
19
+ @config = {
20
+ :extnames => ['.rhtml', '.erb']
21
+ }
22
+
23
+ # Sets some preferences to parse ERB files.
24
+ # * config: a Hash of the config. It can takes some values below:
25
+ # * :extnames: An Array of target files extension. Default is [".rhtml"].
26
+ def init(config)
27
+ config.each{|k, v|
28
+ @config[k] = v
29
+ }
30
+ end
31
+
32
+ def parse(file, targets = []) # :nodoc:
33
+ src = ERB.new(IO.readlines(file).join).src
34
+ # Remove magic comment prepended by erb in Ruby 1.9.
35
+ src.sub!(/\A#.*?coding[:=].*?\n/, '') if src.respond_to?(:encode)
36
+ erb = src.split(/$/)
37
+ RubyParser.parse_lines(file, erb, targets)
38
+ end
39
+
40
+ def target?(file) # :nodoc:
41
+ @config[:extnames].each do |v|
42
+ return true if File.extname(file) == v
43
+ end
44
+ false
45
+ end
46
+ end
47
+ end
48
+
49
+ if __FILE__ == $0
50
+ # ex) ruby glade.rhtml foo.rhtml bar.rhtml
51
+ ARGV.each do |file|
52
+ p GetText::ErbParser.parse(file)
53
+ end
54
+ end