grosser-pomo 0.4.1 → 0.5.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.
- data/README.markdown +7 -3
- data/VERSION.yml +2 -2
- data/lib/pomo/mo_file.rb +62 -0
- data/lib/pomo/po_file.rb +1 -7
- data/lib/pomo.rb +7 -0
- data/spec/files/complex.mo +0 -0
- data/spec/files/empty.mo +0 -0
- data/spec/files/plural.mo +0 -0
- data/spec/files/singular.mo +0 -0
- data/spec/files/singular_2.mo +0 -0
- data/spec/pomo/mo_file_spec.rb +58 -0
- data/vendor/README.rdoc +236 -0
- data/vendor/iconv.rb +107 -0
- data/vendor/mofile.rb +296 -0
- metadata +14 -3
data/README.markdown
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
A simple and extendable .mo and .po file parser/generator.
|
2
|
-
--mo file parser and writer are missing atm--
|
3
2
|
|
4
|
-
Advanteges over
|
3
|
+
Advanteges over original [mo](http://github.com/mutoh/gettext/blob/abf96713327cc4c5d35f0a772f3b75ff4819450c/lib/gettext/mofile.rb) / [po](http://github.com/mutoh/gettext/blob/abf96713327cc4c5d35f0a772f3b75ff4819450c/lib/gettext/poparser.rb)-parser:
|
5
4
|
|
6
5
|
- simple architecture + easy to extend/modify
|
7
6
|
- emtpy msgstr translations are read
|
@@ -9,6 +8,8 @@ Advanteges over [original po-parser](http://github.com/mutoh/gettext/blob/abf967
|
|
9
8
|
- fuzzy can be set/unset
|
10
9
|
- multiple translations can be combined in a new po file(with comments and fuzzy and ...)
|
11
10
|
- po files can be written from any kind of input
|
11
|
+
- easy mo-file handling/merging
|
12
|
+
- po/mo file handling is identical, if you know one, you know both
|
12
13
|
|
13
14
|
Setup
|
14
15
|
=====
|
@@ -24,6 +25,7 @@ Setup
|
|
24
25
|
#or write a new po file (unique by msgid)...
|
25
26
|
File.open('xxx.po','w){|f|f.print(Pomo::PoFile.to_text(translations))}
|
26
27
|
|
28
|
+
|
27
29
|
###Instance interface
|
28
30
|
p = PoMo::PoFile.new
|
29
31
|
p.add_translations_from_text(File.read('...'))
|
@@ -31,10 +33,12 @@ Setup
|
|
31
33
|
p.translations
|
32
34
|
p.to_text
|
33
35
|
|
36
|
+
`Pomo::MoFile` behaves identical.
|
37
|
+
|
34
38
|
TODO
|
35
39
|
====
|
36
40
|
- extracting of version/pluralisation_rule/plurals/translator... (from msgid "")
|
37
|
-
-
|
41
|
+
- the vendor/mofile is really complex, maybe it can be refactored (also some parts are not needed)
|
38
42
|
|
39
43
|
Author
|
40
44
|
======
|
data/VERSION.yml
CHANGED
data/lib/pomo/mo_file.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'pomo/translation'
|
2
|
+
require File.join(File.dirname(__FILE__),'..','..','vendor','mofile')
|
3
|
+
|
4
|
+
module Pomo
|
5
|
+
class MoFile
|
6
|
+
PLURAL_SEPERATOR = "\000"
|
7
|
+
|
8
|
+
def self.parse(text)
|
9
|
+
MoFile.new.add_translations_from_text(text)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.to_text(translations)
|
13
|
+
m = MoFile.new(:translations=>translations)
|
14
|
+
m.to_text
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :translations
|
18
|
+
|
19
|
+
def initialize(options = {})
|
20
|
+
@translations = options[:translations] || []
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_translations_from_text(text)
|
24
|
+
text = StringIO.new(text)
|
25
|
+
@translations += Pomo::GetText::MOFile.open(text, "UTF-8").map do |msgid,msgstr|
|
26
|
+
translation = Translation.new
|
27
|
+
if plural? msgid or plural? msgstr
|
28
|
+
translation.msgid = split_plural(msgid)
|
29
|
+
translation.msgstr = split_plural(msgstr)
|
30
|
+
else
|
31
|
+
translation.msgid = msgid
|
32
|
+
translation.msgstr = msgstr
|
33
|
+
end
|
34
|
+
translation
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_text
|
39
|
+
m = Pomo::GetText::MOFile.new
|
40
|
+
Pomo.unique_translations(translations).each {|t| m[plural_to_string(t.msgid)] = plural_to_string(t.msgstr)}
|
41
|
+
|
42
|
+
io = StringIO.new
|
43
|
+
m.save_to_stream io
|
44
|
+
io.rewind
|
45
|
+
io.read
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def plural_to_string(plural_or_singular)
|
51
|
+
[*plural_or_singular] * PLURAL_SEPERATOR
|
52
|
+
end
|
53
|
+
|
54
|
+
def plural? string
|
55
|
+
string.include? PLURAL_SEPERATOR
|
56
|
+
end
|
57
|
+
|
58
|
+
def split_plural string
|
59
|
+
string.split PLURAL_SEPERATOR
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/pomo/po_file.rb
CHANGED
@@ -11,12 +11,6 @@ module Pomo
|
|
11
11
|
p.to_text
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.unique_translations(translations)
|
15
|
-
last_seen_at_index = {}
|
16
|
-
translations.each_with_index {|translation,index|last_seen_at_index[translation.msgid]=index}
|
17
|
-
last_seen_at_index.values.sort.map{|index| translations[index]}
|
18
|
-
end
|
19
|
-
|
20
14
|
attr_reader :translations
|
21
15
|
|
22
16
|
def initialize(options = {})
|
@@ -44,7 +38,7 @@ module Pomo
|
|
44
38
|
end
|
45
39
|
|
46
40
|
def to_text
|
47
|
-
|
41
|
+
Pomo.unique_translations(translations).map {|translation|
|
48
42
|
comment = translation.comment.to_s.split(/\n|\r\n/).map{|line|"##{line}\n"}*''
|
49
43
|
msgid_and_msgstr = if translation.plural?
|
50
44
|
msgids =
|
data/lib/pomo.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
1
|
require 'pomo/po_file'
|
2
2
|
module Pomo
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def self.unique_translations(translations)
|
6
|
+
last_seen_at_index = {}
|
7
|
+
translations.each_with_index {|translation,index|last_seen_at_index[translation.msgid]=index}
|
8
|
+
last_seen_at_index.values.sort.map{|index| translations[index]}
|
9
|
+
end
|
3
10
|
end
|
Binary file
|
data/spec/files/empty.mo
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
require 'pomo/mo_file'
|
3
|
+
|
4
|
+
include Pomo
|
5
|
+
describe Pomo::MoFile do
|
6
|
+
it "parses empty mo file" do
|
7
|
+
MoFile.parse(File.read('spec/files/empty.mo')).should == []
|
8
|
+
end
|
9
|
+
|
10
|
+
it "parses empty strings" do
|
11
|
+
MoFile.parse(File.read('spec/files/empty.mo')).should == []
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reads singulars" do
|
15
|
+
t = MoFile.parse(File.read('spec/files/singular.mo'))[0]
|
16
|
+
t.to_hash.should == {:msgid=>'Back',:msgstr=>'Zurück'}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "reads plurals" do
|
20
|
+
t = MoFile.parse(File.read('spec/files/plural.mo'))[0]
|
21
|
+
t.to_hash.should == {:msgid=>['Axis','Axis'],:msgstr=>['Achse','Achsen']}
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'instance methods' do
|
25
|
+
it "combines multiple translations" do
|
26
|
+
m = MoFile.new
|
27
|
+
m.add_translations_from_text(File.read('spec/files/plural.mo'))
|
28
|
+
m.add_translations_from_text(File.read('spec/files/singular.mo'))
|
29
|
+
m.should have(2).translations
|
30
|
+
m.translations[0].msgid.should_not == m.translations[1].msgid
|
31
|
+
end
|
32
|
+
|
33
|
+
it "can be initialized with translations" do
|
34
|
+
m = MoFile.new(:translations=>['x'])
|
35
|
+
m.translations.should == ['x']
|
36
|
+
end
|
37
|
+
|
38
|
+
it "does not generate duplicate translations" do
|
39
|
+
second_version = File.read('spec/files/singular_2.mo')
|
40
|
+
m = MoFile.new
|
41
|
+
m.add_translations_from_text(File.read('spec/files/singular.mo'))
|
42
|
+
m.add_translations_from_text(second_version)
|
43
|
+
m.to_text.should == second_version
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "reads metadata" do
|
48
|
+
meta = MoFile.parse(File.read('spec/files/complex.mo')).detect {|t|t.msgid == ''}
|
49
|
+
meta.msgstr.should_not be_empty
|
50
|
+
end
|
51
|
+
|
52
|
+
describe :to_text do
|
53
|
+
it "writes singulars" do
|
54
|
+
text = File.read('spec/files/singular.mo')
|
55
|
+
MoFile.to_text(MoFile.parse(text)).should == text
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/vendor/README.rdoc
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
= Ruby-GetText-Package
|
2
|
+
|
3
|
+
Ruby-GetText-Package is a Localization(L10n) library and tool
|
4
|
+
which is modeled after the GNU gettext package.
|
5
|
+
|
6
|
+
This library translates original messages to localized
|
7
|
+
messages using client-side locale information(environment
|
8
|
+
variable or CGI variable).
|
9
|
+
|
10
|
+
The tools for developers support creating, useing, and modifying
|
11
|
+
localized message files(message catalogs).
|
12
|
+
|
13
|
+
((*Rails*))
|
14
|
+
Rails support has been removed.
|
15
|
+
Rails / ActiveRecord specific code now lives in gettext_rails and gettext_activerecord.
|
16
|
+
|
17
|
+
== Website
|
18
|
+
* homepage[http://www.yotabanana.com/hiki/ruby-gettext.html]
|
19
|
+
* on rubyforge[http://gettext/rubyforge.org/]
|
20
|
+
* on github[http://github.com/gettext/]
|
21
|
+
|
22
|
+
== Features
|
23
|
+
* Simple APIs(similar to GNU gettext)
|
24
|
+
|
25
|
+
* rgettext creates po-files from
|
26
|
+
* ruby scripts
|
27
|
+
* glade-2 XML file(.glade)
|
28
|
+
* ERB file(.rhtml, .erb)
|
29
|
+
* Anything (with your own parsers)
|
30
|
+
* The po-files are compatible to GNU gettext.
|
31
|
+
|
32
|
+
* rmsgfmt creates a mo-file from a po-file.
|
33
|
+
The mo-file is compatible to GNU gettext(msgfmt).
|
34
|
+
|
35
|
+
* textdomain's scope is adapt to ruby class/module mechanism.
|
36
|
+
* A class/module can have plural textdomains.
|
37
|
+
* a message is looked up in its class/module and ancestors.
|
38
|
+
|
39
|
+
* CGI support (gettext/cgi)
|
40
|
+
* Locale is retrieved from client informations
|
41
|
+
(HTTP_ACCEPT_LANGUAGE, HTTP_ACCEPT_CHARSET, QUERY_STRING(lang), Cookies(lang)).
|
42
|
+
|
43
|
+
* String%() is extended to use named argument such as <tt>%{foo}" %{:foo => 1}</tt>.
|
44
|
+
Notes that Ruby-1.9.x supports this format by itself.
|
45
|
+
|
46
|
+
== Requirements
|
47
|
+
* {Ruby 1.8.3 or later}[http://www.ruby-lang.org]
|
48
|
+
* {Rubygems}[http://www.rubygems.org/]
|
49
|
+
* {locale gem}[http://rubyforge.org/projects/locale/]
|
50
|
+
* $ gem install locale
|
51
|
+
* (for development only)
|
52
|
+
* {GNU gettext 0.10.35 or later}[http://www.gnu.org/software/gettext/gettext.html]
|
53
|
+
* {Racc-1.4.3 or later}[http://www.ruby-lang.org/raa/list.rhtml?name=racc]
|
54
|
+
* (for compiling src/rmsgfmt.ry only)
|
55
|
+
|
56
|
+
== Install
|
57
|
+
* Uninstall old gettext if exists.
|
58
|
+
(sudo/su on POSIX system)
|
59
|
+
gem uninstall gettext
|
60
|
+
|
61
|
+
* gem
|
62
|
+
#from github (edge/unstable)
|
63
|
+
(sudo/su on POSIX system)
|
64
|
+
gem install locale
|
65
|
+
gem install mutoh-gettext -s http://gems.github.com/
|
66
|
+
|
67
|
+
#from rubyforge (stable)
|
68
|
+
(sudo/su on POSIX system)
|
69
|
+
gem install locale
|
70
|
+
gem install gettext
|
71
|
+
|
72
|
+
* download tar-ball
|
73
|
+
# De-Compress archive and enter its top directory.
|
74
|
+
(sudo/su on POSIX system)
|
75
|
+
ruby setup.rb
|
76
|
+
|
77
|
+
You can also install files in your favorite directory by
|
78
|
+
supplying setup.rb some options. Try <tt>ruby setup.rb --help</tt>.
|
79
|
+
|
80
|
+
== Usage
|
81
|
+
===Translation
|
82
|
+
- _: Basic translation method
|
83
|
+
Translates the message.
|
84
|
+
_("Hello")
|
85
|
+
|
86
|
+
The gettext methods comes in 3 combinable flavors
|
87
|
+
- n: Pluralized
|
88
|
+
Returns singular or plural form, depending on how many you have.
|
89
|
+
n_("Apple", "%{num} Apples", 3)
|
90
|
+
n_(["Apple", "%{num} Apples"], 3)
|
91
|
+
|
92
|
+
- p: context aware
|
93
|
+
A context is a prefix to your translation, usefull when one word has different meanings, depending on its context.
|
94
|
+
p_("Printer","Open") <=> p_("File","Open")
|
95
|
+
is the same as s_("Printer|Open") <=> s_("File|Open")
|
96
|
+
|
97
|
+
- s: without context
|
98
|
+
If a translation could not be found, return the msgid without context.
|
99
|
+
s_("Printer|Open") => "Öffnen" #translation found
|
100
|
+
s_("Printer|Open") => "Open" #translation not found
|
101
|
+
|
102
|
+
- combinations
|
103
|
+
np_("Fruit", "Apple", "%{num} Apples", 3)
|
104
|
+
ns_("Fruit|Apple","%{num} Apples", 3)
|
105
|
+
|
106
|
+
np_(["Fruit","Apple","%{num} Apples"], 3)
|
107
|
+
ns_(["Fruit|Apple","%{num} Apples"], 3)
|
108
|
+
|
109
|
+
- N_, Nn_: Makes dynamic translation messages readable for the gettext parser.
|
110
|
+
<tt>_(fruit)</tt> cannot be understood by the gettext parser. To help the parser find all your translations,
|
111
|
+
you can add <tt>fruit = N_("Apple")</tt> which does not translate, but tells the parser: "Apple" needs translation.
|
112
|
+
|
113
|
+
fruit = N_("Apple") # same as fruit = "Apple"
|
114
|
+
_(fruit) # does a normal translation
|
115
|
+
|
116
|
+
fruits = Nn_("Apple", "%{num} Apples")
|
117
|
+
n_(fruits, 3)
|
118
|
+
|
119
|
+
=== Locale / Domain
|
120
|
+
GetText stores the locale your are using
|
121
|
+
GetText.locale = "en_US" # translate into english from now on
|
122
|
+
GetText.locale # => en_US
|
123
|
+
Or
|
124
|
+
include GetText
|
125
|
+
set_locale "en_US"
|
126
|
+
|
127
|
+
Each locale can have different sets of translations (text domains) (e.g. Financial terms + Human-resource terms)
|
128
|
+
GetText.bindtextdomain('financial')
|
129
|
+
Or
|
130
|
+
include GetText
|
131
|
+
bindtextdomain('financial')
|
132
|
+
|
133
|
+
For more details and options, have a look at the samples folder or
|
134
|
+
consult the tutorial[http://www.yotabanana.com/hiki/ruby-gettext-howto.html].
|
135
|
+
|
136
|
+
|
137
|
+
== License
|
138
|
+
This program is licenced under the same licence as Ruby.
|
139
|
+
(See the file 'COPYING'.)
|
140
|
+
|
141
|
+
* mofile.rb
|
142
|
+
* Copyright (C) 2001-2009 Masao Mutoh <mutoh at highwhay.ne.jp>
|
143
|
+
* Copyright (C) 2001,2002 Masahiro Sakai <s01397ms at sfc.keio.ac.jp>
|
144
|
+
|
145
|
+
* gettext.rb
|
146
|
+
* Copyright (C) 2001-2009 Masao Mutoh <mutoh at highwhay.ne.jp>
|
147
|
+
* Copyright (C) 2001,2002 Masahiro Sakai <s01397ms at sfc.keio.ac.jp>
|
148
|
+
|
149
|
+
* rgettext
|
150
|
+
* Copyright (C) 2001-2009 Masao Mutoh <mutoh at highwhay.ne.jp>
|
151
|
+
* Copyright (C) 2001,2002 Yasushi Shoji <yashi at atmark-techno.com>
|
152
|
+
|
153
|
+
* setup.rb
|
154
|
+
* Copyright (C) 2000-2005 Minero Aoki <aamine at loveruby.net>
|
155
|
+
* This file is released under LGPL. See the top of the install.rb.
|
156
|
+
|
157
|
+
* Others
|
158
|
+
* Copyright (C) 2001-2009 Masao Mutoh <mutoh at highwhay.ne.jp>
|
159
|
+
|
160
|
+
|
161
|
+
== Translators
|
162
|
+
* Bosnian(bs) - Sanjin Sehic <saserr at gmail.com>
|
163
|
+
* Bulgarian(bg) - Sava Chankov <sava.chankov at gmail.com>
|
164
|
+
* Catalan(ca) - Ramon Salvadó <rsalvado at gnuine.com>
|
165
|
+
* Chinese(Simplified)(zh_CN)
|
166
|
+
* Yang Bob <bob.yang.dev at gmail.com> (current)
|
167
|
+
* Yingfeng <blogyingfeng at gmail.com>
|
168
|
+
* Chinese(Traditional)(zh_TW)
|
169
|
+
* Yang Bob <bob.yang.dev at gmail.com> (current)
|
170
|
+
* LIN CHUNG-YI <xmarsh at gmail.com>
|
171
|
+
* Croatian(hr) - Sanjin Sehic <saserr at gmail.com>
|
172
|
+
* Czech(cs) - Karel Miarka <kajism at yahoo.com>
|
173
|
+
* Dutch(nl) - Menno Jonkers <ruby-gettext at jonkers.com>
|
174
|
+
* Esperanto(eo) - Malte Milatz <malte at gmx-topmail.de>
|
175
|
+
* Estonian(et) - Erkki Eilonen <erkki at itech.ee>
|
176
|
+
* French(fr)
|
177
|
+
* Vincent Isambart <vincent.isambart at gmail.com> (current)
|
178
|
+
* David Sulc <davidsulc at gmail.com>
|
179
|
+
* Laurent Sansonetti <laurent.sansonetti at gmail.com>
|
180
|
+
* German(de)
|
181
|
+
* Patrick Lenz <patrick at limited-overload.de> (current)
|
182
|
+
* Detlef Reichl <detlef.reichl at gmx.org>
|
183
|
+
* Sven Herzberg <herzi at abi02.de>
|
184
|
+
* Sascha Ebach <se at digitale-wertschoepfung.de>
|
185
|
+
* Greek(el) - Vassilis Rizopoulos <damphyr at gmx.net>
|
186
|
+
* Hungarian(hu) - Tamás Tompa <tompata at gmail.com>
|
187
|
+
* Italian(it)
|
188
|
+
* Marco Lazzeri <marco.lazzeri at gmail.com>
|
189
|
+
* Gabriele Renzi <surrender_it at yahoo.it>
|
190
|
+
* Japanese(ja) - Masao Mutoh <mutoh at highway.ne.jp>
|
191
|
+
* Korean(ko) - Gyoung-Yoon Noh <nohmad at gmail.com>
|
192
|
+
* Latvian(lv) - Aivars Akots <aivars.akots at gmail.com>
|
193
|
+
* Norwegian(nb) - Runar Ingebrigtsen <runar at mopo.no>
|
194
|
+
* Portuguese(Brazil)(pt_BR)
|
195
|
+
* Antonio S. de A. Terceiro <terceiro at softwarelivre.org> (current)
|
196
|
+
* Joao Pedrosa <joaopedrosa at gmail.com>
|
197
|
+
* Russian(ru) - Yuri Kozlov <kozlov.y at gmail.com>
|
198
|
+
* Serbian(sr) - Slobodan Paunović" <slobodan.paunovic at gmail.com>
|
199
|
+
* Spanish(es)
|
200
|
+
* David Espada <davinci at escomposlinux.org> (current)
|
201
|
+
* David Moreno Garza <damog at damog.net>
|
202
|
+
* Swedish(sv) - Nikolai Weibull <mailing-lists.ruby-talk at rawuncut.elitemail.org>
|
203
|
+
* Ukrainian(ua) - Alex Rootoff <rootoff at pisem.net>
|
204
|
+
* Vietnamese(vi) - Ngoc Dao Thanh <ngocdaothanh at gmail.com>
|
205
|
+
|
206
|
+
== Status of translations
|
207
|
+
* Bosnian(bs) - 1.90.0 (old)
|
208
|
+
* Bulgarian(bg) - 2.0.0pre1 (new)
|
209
|
+
* Catalan(ca) - 2.0.0pre1
|
210
|
+
* Croatian(hr) - 1.90.0 (old)
|
211
|
+
* Chinese(zh_CN) - 2.0.0pre1
|
212
|
+
* Chinese(zh_TW) - 2.0.0pre1
|
213
|
+
* Czech(cs) - 1.9.0 (old)
|
214
|
+
* Dutch(nl) - 1.90.0 (old)
|
215
|
+
* English(default) - 1.90.0 (old)
|
216
|
+
* Esperanto(eo) - 2.0.0pre1
|
217
|
+
* Estonian(et) - 2.0.0pre1
|
218
|
+
* French(fr) - 2.0.0pre1
|
219
|
+
* German(de) - 2.0.0pre1
|
220
|
+
* Greek(el) - 2.0.0pre1
|
221
|
+
* Hungarian(hu) - 2.0.0pre1
|
222
|
+
* Italian(it) - 1.6.0 (old)
|
223
|
+
* Japanese(ja) - 2.0.0pre1
|
224
|
+
* Korean(ko) - 1.9.0 (old)
|
225
|
+
* Latvian(lv) - 2.0.0pre1 (new)
|
226
|
+
* Norwegian(nb) - 2.0.0pre1
|
227
|
+
* Portuguese(Brazil)(pt_BR) - 2.0.0pre1
|
228
|
+
* Russian(ru) - 2.0.0pre1
|
229
|
+
* Serbian(sr) - 1.91.0 (old)
|
230
|
+
* Spanish(es) - 2.0.0pre1
|
231
|
+
* Swedish(sv) - 0.8.0 (too much old)
|
232
|
+
* Ukrainian(ua) - 2.0.0pre1
|
233
|
+
* Vietnamese(vi) - 2.0.0pre1
|
234
|
+
|
235
|
+
== Maintainer
|
236
|
+
Masao Mutoh <mutoh at highway.ne.jp>
|
data/vendor/iconv.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
=begin
|
2
|
+
iconv.rb - Pseudo Iconv class. Supports Iconv.iconv, Iconv.conv.
|
3
|
+
|
4
|
+
For Matz Ruby:
|
5
|
+
If you don't have iconv but glib2, this library uses glib2 iconv functions.
|
6
|
+
|
7
|
+
For JRuby:
|
8
|
+
Use Java String class to convert strings.
|
9
|
+
|
10
|
+
Copyright (C) 2004-2007 Masao Mutoh
|
11
|
+
|
12
|
+
You may redistribute it and/or modify it under the same
|
13
|
+
license terms as Ruby.
|
14
|
+
|
15
|
+
$Id: iconv.rb,v 1.6 2007/11/08 14:21:22 mutoh Exp $
|
16
|
+
=end
|
17
|
+
|
18
|
+
#Modifications
|
19
|
+
#wrapped inside FastGettext namespace to reduce conflic
|
20
|
+
|
21
|
+
begin
|
22
|
+
require 'iconv'
|
23
|
+
rescue LoadError
|
24
|
+
# Provides Iconv.iconv which normally is provided through Ruby/GLib(1) functions.
|
25
|
+
# This library is required for 'gettext'.
|
26
|
+
# If you require 'gettext/iconv', it tries to call Ruby/GLib function
|
27
|
+
# when it doesn't find original Iconv class(iconv.so) it adds a pseudo class.
|
28
|
+
#
|
29
|
+
# One-click Ruby Installer for Win32 hadn’t had iconv and there hadn’t been a way to install iconv.so itself for Win32.
|
30
|
+
# And JRuby hadn’t had Iconv.
|
31
|
+
# I’ve not checked them currently, but if they’ve supported iconv now, we don’t need this anymore...
|
32
|
+
#
|
33
|
+
# (1) Ruby/GLib is a module which is provided from Ruby-GNOME2 Project.
|
34
|
+
# You can get binaries for Win32(One-Click Ruby Installer).
|
35
|
+
# <URL: http://ruby-gnome2.sourceforge.jp/>
|
36
|
+
module Pomo
|
37
|
+
class Iconv2
|
38
|
+
module Failure; end
|
39
|
+
class InvalidEncoding < ArgumentError; include Failure; end
|
40
|
+
class IllegalSequence < ArgumentError; include Failure; end
|
41
|
+
class InvalidCharacter < ArgumentError; include Failure; end
|
42
|
+
|
43
|
+
if RUBY_PLATFORM =~ /java/
|
44
|
+
def self.conv(to, from, str)
|
45
|
+
raise InvalidCharacter, "the 3rd argument is nil" unless str
|
46
|
+
begin
|
47
|
+
str = java.lang.String.new(str.unpack("C*").to_java(:byte), from)
|
48
|
+
str.getBytes(to).to_ary.pack("C*")
|
49
|
+
rescue java.io.UnsupportedEncodingException
|
50
|
+
raise InvalidEncoding
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
begin
|
55
|
+
require 'glib2'
|
56
|
+
|
57
|
+
def self.check_glib_version?(major, minor, micro) # :nodoc:
|
58
|
+
(GLib::BINDING_VERSION[0] > major ||
|
59
|
+
(GLib::BINDING_VERSION[0] == major &&
|
60
|
+
GLib::BINDING_VERSION[1] > minor) ||
|
61
|
+
(GLib::BINDING_VERSION[0] == major &&
|
62
|
+
GLib::BINDING_VERSION[1] == minor &&
|
63
|
+
GLib::BINDING_VERSION[2] >= micro))
|
64
|
+
end
|
65
|
+
|
66
|
+
if check_glib_version?(0, 11, 0)
|
67
|
+
# This is a function equivalent of Iconv.iconv.
|
68
|
+
# * to: encoding name for destination
|
69
|
+
# * from: encoding name for source
|
70
|
+
# * str: strings to be converted
|
71
|
+
# * Returns: Returns an Array of converted strings.
|
72
|
+
def self.conv(to, from, str)
|
73
|
+
begin
|
74
|
+
GLib.convert(str, to, from)
|
75
|
+
rescue GLib::ConvertError => e
|
76
|
+
case e.code
|
77
|
+
when GLib::ConvertError::NO_CONVERSION
|
78
|
+
raise InvalidEncoding.new(str)
|
79
|
+
when GLib::ConvertError::ILLEGAL_SEQUENCE
|
80
|
+
raise IllegalSequence.new(str)
|
81
|
+
else
|
82
|
+
raise InvalidCharacter.new(str)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
else
|
87
|
+
def self.conv(to, from, str) # :nodoc:
|
88
|
+
begin
|
89
|
+
GLib.convert(str, to, from)
|
90
|
+
rescue
|
91
|
+
raise IllegalSequence.new(str)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
rescue LoadError
|
96
|
+
def self.conv(to, from, str) # :nodoc:
|
97
|
+
warn "Iconv was not found." if $DEBUG
|
98
|
+
str
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
def self.iconv(to, from, str)
|
103
|
+
conv(to, from, str).split(//)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/vendor/mofile.rb
ADDED
@@ -0,0 +1,296 @@
|
|
1
|
+
=begin
|
2
|
+
mofile.rb - A simple class for operating GNU MO file.
|
3
|
+
|
4
|
+
Copyright (C) 2003-2008 Masao Mutoh
|
5
|
+
Copyright (C) 2002 Masahiro Sakai, Masao Mutoh
|
6
|
+
Copyright (C) 2001 Masahiro Sakai
|
7
|
+
|
8
|
+
Masahiro Sakai <s01397ms at sfc.keio.ac.jp>
|
9
|
+
Masao Mutoh <mutoh at highway.ne.jp>
|
10
|
+
|
11
|
+
You can redistribute this file and/or modify it under the same term
|
12
|
+
of Ruby. License of Ruby is included with Ruby distribution in
|
13
|
+
the file "README".
|
14
|
+
|
15
|
+
$Id: mo.rb,v 1.10 2008/06/17 16:40:52 mutoh Exp $
|
16
|
+
=end
|
17
|
+
|
18
|
+
require File.join(File.dirname(__FILE__),'iconv')
|
19
|
+
require 'stringio'
|
20
|
+
|
21
|
+
#Modifications:
|
22
|
+
# use Iconv or FastGettext::Icvon
|
23
|
+
|
24
|
+
module Pomo
|
25
|
+
module GetText
|
26
|
+
class MOFile < Hash
|
27
|
+
class InvalidFormat < RuntimeError; end;
|
28
|
+
|
29
|
+
attr_reader :filename
|
30
|
+
|
31
|
+
Header = Struct.new(:magic,
|
32
|
+
:revision,
|
33
|
+
:nstrings,
|
34
|
+
:orig_table_offset,
|
35
|
+
:translated_table_offset,
|
36
|
+
:hash_table_size,
|
37
|
+
:hash_table_offset)
|
38
|
+
|
39
|
+
# The following are only used in .mo files
|
40
|
+
# with minor revision >= 1.
|
41
|
+
class HeaderRev1 < Header
|
42
|
+
attr_accessor :n_sysdep_segments,
|
43
|
+
:sysdep_segments_offset,
|
44
|
+
:n_sysdep_strings,
|
45
|
+
:orig_sysdep_tab_offset,
|
46
|
+
:trans_sysdep_tab_offset
|
47
|
+
end
|
48
|
+
|
49
|
+
MAGIC_BIG_ENDIAN = "\x95\x04\x12\xde"
|
50
|
+
MAGIC_LITTLE_ENDIAN = "\xde\x12\x04\x95"
|
51
|
+
|
52
|
+
def self.open(arg = nil, output_charset = nil)
|
53
|
+
result = self.new(output_charset)
|
54
|
+
result.load(arg)
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(output_charset = nil)
|
58
|
+
@filename = nil
|
59
|
+
@last_modified = nil
|
60
|
+
@little_endian = true
|
61
|
+
@output_charset = output_charset
|
62
|
+
super()
|
63
|
+
end
|
64
|
+
|
65
|
+
def update!
|
66
|
+
if FileTest.exist?(@filename)
|
67
|
+
st = File.stat(@filename)
|
68
|
+
load(@filename) unless (@last_modified == [st.ctime, st.mtime])
|
69
|
+
else
|
70
|
+
warn "#{@filename} was lost." if $DEBUG
|
71
|
+
clear
|
72
|
+
end
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def load(arg)
|
77
|
+
if arg.kind_of? String
|
78
|
+
begin
|
79
|
+
st = File.stat(arg)
|
80
|
+
@last_modified = [st.ctime, st.mtime]
|
81
|
+
rescue Exception
|
82
|
+
end
|
83
|
+
load_from_file(arg)
|
84
|
+
else
|
85
|
+
load_from_stream(arg)
|
86
|
+
end
|
87
|
+
@filename = arg
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def load_from_stream(io)
|
92
|
+
magic = io.read(4)
|
93
|
+
case magic
|
94
|
+
when MAGIC_BIG_ENDIAN
|
95
|
+
@little_endian = false
|
96
|
+
when MAGIC_LITTLE_ENDIAN
|
97
|
+
@little_endian = true
|
98
|
+
else
|
99
|
+
raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
|
100
|
+
end
|
101
|
+
|
102
|
+
endian_type6 = @little_endian ? 'V6' : 'N6'
|
103
|
+
endian_type_astr = @little_endian ? 'V*' : 'N*'
|
104
|
+
|
105
|
+
header = HeaderRev1.new(magic, *(io.read(4 * 6).unpack(endian_type6)))
|
106
|
+
|
107
|
+
if header.revision == 1
|
108
|
+
# FIXME: It doesn't support sysdep correctly.
|
109
|
+
header.n_sysdep_segments = io.read(4).unpack(endian_type6)
|
110
|
+
header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
|
111
|
+
header.n_sysdep_strings = io.read(4).unpack(endian_type6)
|
112
|
+
header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
|
113
|
+
header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
|
114
|
+
elsif header.revision > 1
|
115
|
+
raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
|
116
|
+
end
|
117
|
+
io.pos = header.orig_table_offset
|
118
|
+
orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
|
119
|
+
|
120
|
+
io.pos = header.translated_table_offset
|
121
|
+
trans_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
|
122
|
+
|
123
|
+
original_strings = Array.new(header.nstrings)
|
124
|
+
for i in 0...header.nstrings
|
125
|
+
io.pos = orig_table_data[i * 2 + 1]
|
126
|
+
original_strings[i] = io.read(orig_table_data[i * 2 + 0])
|
127
|
+
end
|
128
|
+
|
129
|
+
clear
|
130
|
+
for i in 0...header.nstrings
|
131
|
+
io.pos = trans_table_data[i * 2 + 1]
|
132
|
+
str = io.read(trans_table_data[i * 2 + 0])
|
133
|
+
|
134
|
+
if (! original_strings[i]) || original_strings[i] == ""
|
135
|
+
if str
|
136
|
+
@charset = nil
|
137
|
+
@nplurals = nil
|
138
|
+
@plural = nil
|
139
|
+
str.each_line{|line|
|
140
|
+
if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
|
141
|
+
@charset = $1
|
142
|
+
elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
|
143
|
+
@nplurals = $1
|
144
|
+
@plural = $2
|
145
|
+
end
|
146
|
+
break if @charset and @nplurals
|
147
|
+
}
|
148
|
+
@nplurals = "1" unless @nplurals
|
149
|
+
@plural = "0" unless @plural
|
150
|
+
end
|
151
|
+
else
|
152
|
+
if @output_charset
|
153
|
+
begin
|
154
|
+
iconv = Iconv || FastGettext::Iconv
|
155
|
+
str = iconv.conv(@output_charset, @charset, str) if @charset
|
156
|
+
rescue iconv::Failure
|
157
|
+
if $DEBUG
|
158
|
+
warn "@charset = ", @charset
|
159
|
+
warn"@output_charset = ", @output_charset
|
160
|
+
warn "msgid = ", original_strings[i]
|
161
|
+
warn "msgstr = ", str
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
self[original_strings[i]] = str.freeze
|
167
|
+
end
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
# Is this number a prime number ?
|
172
|
+
# http://apidock.com/ruby/Prime
|
173
|
+
def prime?(number)
|
174
|
+
('1' * number) !~ /^1?$|^(11+?)\1+$/
|
175
|
+
end
|
176
|
+
|
177
|
+
def next_prime(seed)
|
178
|
+
require 'mathn'
|
179
|
+
prime = Prime.new
|
180
|
+
while current = prime.succ
|
181
|
+
return current if current > seed
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# From gettext-0.12.1/gettext-runtime/intl/hash-string.h
|
186
|
+
# Defines the so called `hashpjw' function by P.J. Weinberger
|
187
|
+
# [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
|
188
|
+
# 1986, 1987 Bell Telephone Laboratories, Inc.]
|
189
|
+
HASHWORDBITS = 32
|
190
|
+
def hash_string(str)
|
191
|
+
hval = 0
|
192
|
+
i = 0
|
193
|
+
str.each_byte do |b|
|
194
|
+
break if b == '\0'
|
195
|
+
hval <<= 4
|
196
|
+
hval += b.to_i
|
197
|
+
g = hval & (0xf << (HASHWORDBITS - 4))
|
198
|
+
if (g != 0)
|
199
|
+
hval ^= g >> (HASHWORDBITS - 8)
|
200
|
+
hval ^= g
|
201
|
+
end
|
202
|
+
end
|
203
|
+
hval
|
204
|
+
end
|
205
|
+
|
206
|
+
def save_to_stream(io)
|
207
|
+
#Save data as little endian format.
|
208
|
+
header_size = 4 * 7
|
209
|
+
table_size = 4 * 2 * size
|
210
|
+
|
211
|
+
hash_table_size = next_prime((size * 4) / 3)
|
212
|
+
hash_table_size = 3 if hash_table_size <= 2
|
213
|
+
header = Header.new(
|
214
|
+
MAGIC_LITTLE_ENDIAN, # magic
|
215
|
+
0, # revision
|
216
|
+
size, # nstrings
|
217
|
+
header_size, # orig_table_offset
|
218
|
+
header_size + table_size, # translated_table_offset
|
219
|
+
hash_table_size, # hash_table_size
|
220
|
+
header_size + table_size * 2 # hash_table_offset
|
221
|
+
)
|
222
|
+
io.write(header.to_a.pack('a4V*'))
|
223
|
+
|
224
|
+
ary = to_a
|
225
|
+
ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
|
226
|
+
|
227
|
+
pos = header.hash_table_size * 4 + header.hash_table_offset
|
228
|
+
|
229
|
+
orig_table_data = Array.new()
|
230
|
+
ary.each{|item, _|
|
231
|
+
orig_table_data.push(item.size)
|
232
|
+
orig_table_data.push(pos)
|
233
|
+
pos += item.size + 1 # +1 is <NUL>
|
234
|
+
}
|
235
|
+
io.write(orig_table_data.pack('V*'))
|
236
|
+
|
237
|
+
trans_table_data = Array.new()
|
238
|
+
ary.each{|_, item|
|
239
|
+
trans_table_data.push(item.size)
|
240
|
+
trans_table_data.push(pos)
|
241
|
+
pos += item.size + 1 # +1 is <NUL>
|
242
|
+
}
|
243
|
+
io.write(trans_table_data.pack('V*'))
|
244
|
+
|
245
|
+
hash_tab = Array.new(hash_table_size)
|
246
|
+
j = 0
|
247
|
+
ary[0...size].each {|key, _|
|
248
|
+
hash_val = hash_string(key)
|
249
|
+
idx = hash_val % hash_table_size
|
250
|
+
if hash_tab[idx] != nil
|
251
|
+
incr = 1 + (hash_val % (hash_table_size - 2))
|
252
|
+
begin
|
253
|
+
if (idx >= hash_table_size - incr)
|
254
|
+
idx -= hash_table_size - incr
|
255
|
+
else
|
256
|
+
idx += incr
|
257
|
+
end
|
258
|
+
end until (hash_tab[idx] == nil)
|
259
|
+
end
|
260
|
+
hash_tab[idx] = j + 1
|
261
|
+
j += 1
|
262
|
+
}
|
263
|
+
hash_tab.collect!{|i| i ? i : 0}
|
264
|
+
|
265
|
+
io.write(hash_tab.pack('V*'))
|
266
|
+
|
267
|
+
ary.each{|item, _| io.write(item); io.write("\0") }
|
268
|
+
ary.each{|_, item| io.write(item); io.write("\0") }
|
269
|
+
|
270
|
+
self
|
271
|
+
end
|
272
|
+
|
273
|
+
def load_from_file(filename)
|
274
|
+
@filename = filename
|
275
|
+
begin
|
276
|
+
File.open(filename, 'rb'){|f| load_from_stream(f)}
|
277
|
+
rescue => e
|
278
|
+
e.set_backtrace("File: #{@filename}")
|
279
|
+
raise e
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def save_to_file(filename)
|
284
|
+
File.open(filename, 'wb'){|f| save_to_stream(f)}
|
285
|
+
end
|
286
|
+
|
287
|
+
def set_comment(msgid_or_sym, comment)
|
288
|
+
#Do nothing
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
attr_accessor :little_endian, :path, :last_modified
|
293
|
+
attr_reader :charset, :nplurals, :plural
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grosser-pomo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
@@ -22,16 +22,27 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
-
- VERSION.yml
|
26
25
|
- README.markdown
|
26
|
+
- VERSION.yml
|
27
27
|
- lib/pomo
|
28
|
+
- lib/pomo.rb
|
29
|
+
- lib/pomo/mo_file.rb
|
28
30
|
- lib/pomo/po_file.rb
|
29
31
|
- lib/pomo/translation.rb
|
30
|
-
-
|
32
|
+
- spec/files
|
33
|
+
- spec/files/complex.mo
|
34
|
+
- spec/files/empty.mo
|
35
|
+
- spec/files/plural.mo
|
36
|
+
- spec/files/singular.mo
|
37
|
+
- spec/files/singular_2.mo
|
31
38
|
- spec/pomo
|
39
|
+
- spec/pomo/mo_file_spec.rb
|
32
40
|
- spec/pomo/po_file_spec.rb
|
33
41
|
- spec/pomo/translation_spec.rb
|
34
42
|
- spec/spec_helper.rb
|
43
|
+
- vendor/README.rdoc
|
44
|
+
- vendor/iconv.rb
|
45
|
+
- vendor/mofile.rb
|
35
46
|
has_rdoc: true
|
36
47
|
homepage: http://github.com/grosser/pomo
|
37
48
|
post_install_message:
|