grosser-fast_gettext 0.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.
- data/README.markdown +61 -0
- data/VERSION.yml +4 -0
- data/lib/fast_gettext.rb +59 -0
- data/lib/fast_gettext/mo_file.rb +48 -0
- data/lib/fast_gettext/storage.rb +24 -0
- data/spec/fast_gettext/mo_file_spec.rb +25 -0
- data/spec/fast_gettext/storage_spec.rb +29 -0
- data/spec/fast_gettext_spec.rb +43 -0
- data/spec/locale/de/LC_MESSAGES/test.mo +0 -0
- data/spec/locale/en/LC_MESSAGES/test.mo +0 -0
- data/spec/spec_helper.rb +21 -0
- metadata +71 -0
data/README.markdown
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
FastGettext
|
2
|
+
===========
|
3
|
+
GetText but fast + simple + threadsave!
|
4
|
+
|
5
|
+
Setup
|
6
|
+
=====
|
7
|
+
sudo gem install grosser-fast_gettext -s http://gems.github.com/
|
8
|
+
|
9
|
+
Tell Gettext where your mo-files lie:
|
10
|
+
#e.g. for locale/de/LC_MESSAGES/my_app.mo
|
11
|
+
FastGettext.add_text_domain('my_app',:path=>'locale')
|
12
|
+
|
13
|
+
Choose text domain, and locale for translation
|
14
|
+
FastGettext.text_domain = 'my_app'
|
15
|
+
FastGettext.locale = 'de'
|
16
|
+
|
17
|
+
Start translating
|
18
|
+
include FastGettext
|
19
|
+
_('Car') == 'Auto'
|
20
|
+
_('not-found') == 'not-found'
|
21
|
+
s_('Namespace|no-found') == 'not-found'
|
22
|
+
n_('Axis','Axis',3) == 'Achsen' #German plural of Axis
|
23
|
+
|
24
|
+
Thread-safety
|
25
|
+
=============
|
26
|
+
locale/text_domain/available_locales are not shared between threads.
|
27
|
+
But text_domains is, so that found translations can be reused.
|
28
|
+
|
29
|
+
Speed
|
30
|
+
=====
|
31
|
+
FastGettext
|
32
|
+
small:
|
33
|
+
1.000000 0.130000 1.130000 ( 1.132578)
|
34
|
+
mapped: 8620K writeable/private: 5588K shared: 28K
|
35
|
+
|
36
|
+
large:
|
37
|
+
1.060000 0.100000 1.160000 ( 1.163962)
|
38
|
+
mapped: 8620K writeable/private: 5588K shared: 28K
|
39
|
+
|
40
|
+
|
41
|
+
GetText
|
42
|
+
small:
|
43
|
+
3.220000 0.260000 3.480000 ( 3.478093)
|
44
|
+
mapped: 9036K writeable/private: 6004K shared: 28K
|
45
|
+
|
46
|
+
large:
|
47
|
+
3.280000 0.230000 3.510000 ( 3.511891)
|
48
|
+
mapped: 9156K writeable/private: 6124K shared: 28K
|
49
|
+
|
50
|
+
|
51
|
+
Updating translations
|
52
|
+
=====================
|
53
|
+
ATM you have to use the [original GetText](http://github.com/mutoh/gettext) to create and manage your po/mo-files.
|
54
|
+
|
55
|
+
Author
|
56
|
+
======
|
57
|
+
Mofile parsing from Masao Mutoh, see vender/README
|
58
|
+
|
59
|
+
Michael Grosser
|
60
|
+
grosser.michael@gmail.com
|
61
|
+
Hereby placed under public domain, do what you want, just do not hold me accountable...
|
data/VERSION.yml
ADDED
data/lib/fast_gettext.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fast_gettext/mo_file'
|
2
|
+
require 'fast_gettext/storage'
|
3
|
+
|
4
|
+
module FastGettext
|
5
|
+
include FastGettext::Storage
|
6
|
+
|
7
|
+
extend self
|
8
|
+
def self.included(mod) #:nodoc:
|
9
|
+
mod.extend self
|
10
|
+
end
|
11
|
+
|
12
|
+
LOCALE_REX = /^[a-z]{2}$|^[a-z]{2}_[A-Z]{2}$/
|
13
|
+
NAMESPACE_SEPERATOR = '|'
|
14
|
+
|
15
|
+
def _(translate)
|
16
|
+
current_mo[translate] || translate
|
17
|
+
end
|
18
|
+
|
19
|
+
#translate pluralized
|
20
|
+
def n_(singular,plural,count)
|
21
|
+
if translation = current_mo.plural(singular,plural,count)
|
22
|
+
translation
|
23
|
+
else
|
24
|
+
count > 1 ? plural : singular
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#translate, but discard namespace if nothing was found
|
29
|
+
# Car|Tire -> Tire if no translation could be found
|
30
|
+
def s_(translate,seperator=nil)
|
31
|
+
if translation = current_mo[translate]
|
32
|
+
translation
|
33
|
+
else
|
34
|
+
translate.split(seperator||NAMESPACE_SEPERATOR).last
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_text_domain(name,options)
|
39
|
+
self.text_domains ||= {}
|
40
|
+
domain = self.text_domains[name] = {:path=>options.delete(:path),:mo_files=>{}}
|
41
|
+
|
42
|
+
# parse all .mo files with the right name, that sit in locale/LC_MESSAGES folders
|
43
|
+
Dir[File.join(domain[:path],'*')].each do |locale_folder|
|
44
|
+
next unless File.basename(locale_folder) =~ LOCALE_REX
|
45
|
+
mo_file = File.join(locale_folder,'LC_MESSAGES',"#{name}.mo")
|
46
|
+
next unless File.exist? mo_file
|
47
|
+
locale = File.basename(locale_folder)
|
48
|
+
domain[:mo_files][locale] = MoFile.new(mo_file)
|
49
|
+
end
|
50
|
+
domain
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def current_mo
|
56
|
+
mo = text_domains[text_domain][:mo_files][locale] rescue nil
|
57
|
+
mo || {}
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'..','..','vendor','mofile')
|
2
|
+
module FastGettext
|
3
|
+
class MoFile
|
4
|
+
PLURAL_SEPERATOR = "\000"
|
5
|
+
|
6
|
+
def initialize(file)
|
7
|
+
@data = GetText::MOFile.open(file, "UTF-8")
|
8
|
+
make_singular_and_plural_available
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@data[key]
|
13
|
+
end
|
14
|
+
|
15
|
+
def plural(singular,plural,count)
|
16
|
+
translations = plural_translations(singular,plural)
|
17
|
+
|
18
|
+
if count > 1
|
19
|
+
translations[1] || self[plural]
|
20
|
+
else
|
21
|
+
translations[0] || self[singular]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
#(if plural==singular, prefer singular)
|
28
|
+
def make_singular_and_plural_available
|
29
|
+
@data.each do |key,translation|
|
30
|
+
next unless key.include? PLURAL_SEPERATOR
|
31
|
+
singular, plural = split_plurals(key)
|
32
|
+
translation = split_plurals(translation)
|
33
|
+
@data[singular] ||= translation[0]
|
34
|
+
@data[plural] ||= translation[1]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def split_plurals(singular_plural)
|
39
|
+
singular_plural.split(PLURAL_SEPERATOR,2)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Car, Cars => [Auto,Autos] or []
|
43
|
+
def plural_translations(singular,plural)
|
44
|
+
plurals = self[singular+PLURAL_SEPERATOR+plural]
|
45
|
+
if plurals then split_plurals(plurals) else [] end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module FastGettext
|
2
|
+
module Storage
|
3
|
+
[:locale,:text_domain,:available_locales].each do |method|
|
4
|
+
key = "FastGettext.#{method}"
|
5
|
+
define_method method do
|
6
|
+
Thread.current[key]
|
7
|
+
end
|
8
|
+
define_method "#{method}=" do |value|
|
9
|
+
Thread.current[key] = value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
#NOT THREADSAFE, for speed/caching
|
14
|
+
@@text_domains = {}
|
15
|
+
|
16
|
+
def text_domains
|
17
|
+
@@text_domains
|
18
|
+
end
|
19
|
+
|
20
|
+
def text_domains=(value)
|
21
|
+
@@text_domains=value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
current_folder = File.dirname(__FILE__)
|
2
|
+
require File.join(current_folder,'..','spec_helper')
|
3
|
+
|
4
|
+
include FastGettext
|
5
|
+
de_file = File.join(current_folder,'..','locale','de','LC_MESSAGES','test.mo')
|
6
|
+
de = MoFile.new(de_file)
|
7
|
+
|
8
|
+
describe MoFile do
|
9
|
+
before :all do
|
10
|
+
File.exist?(de_file).should == true
|
11
|
+
end
|
12
|
+
it "parses a file" do
|
13
|
+
de['car'].should == 'Auto'
|
14
|
+
end
|
15
|
+
it "stores untranslated values as nil" do
|
16
|
+
de['Car|Model'].should == nil
|
17
|
+
end
|
18
|
+
it "finds pluralized values" do
|
19
|
+
de.plural('Axis','Axis',1).should == 'Achse' #singular
|
20
|
+
de.plural('Axis','Axis',2).should == 'Achsen' #plurals
|
21
|
+
end
|
22
|
+
it "can access plurals through []" do
|
23
|
+
de['Axis'].should == 'Achse' #singular
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
current_folder = File.dirname(__FILE__)
|
2
|
+
require File.join(current_folder,'..','spec_helper')
|
3
|
+
|
4
|
+
include FastGettext::Storage
|
5
|
+
|
6
|
+
describe Storage do
|
7
|
+
def thread_save(method)
|
8
|
+
send("#{method}=",1)
|
9
|
+
|
10
|
+
# mess around with other threads
|
11
|
+
threads = []
|
12
|
+
100.times do |i|
|
13
|
+
threads << Thread.new {send("#{method}=",i)}
|
14
|
+
end
|
15
|
+
threads.each(&:join)
|
16
|
+
|
17
|
+
send(method) == 1
|
18
|
+
end
|
19
|
+
|
20
|
+
[:locale, :available_locales, :text_domain].each do |method|
|
21
|
+
it "stores #{method} thread-save" do
|
22
|
+
thread_save(:locale).should == true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not store text_domains thread-save" do
|
27
|
+
thread_save(:text_domains).should == false
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
include FastGettext
|
4
|
+
FastGettext.add_text_domain('test',:path=>File.join(File.dirname(__FILE__),'locale'))
|
5
|
+
FastGettext.text_domain = 'test'
|
6
|
+
FastGettext.available_locales = ['en','de']
|
7
|
+
FastGettext.locale = 'de'
|
8
|
+
|
9
|
+
include FastGettext
|
10
|
+
|
11
|
+
describe FastGettext do
|
12
|
+
describe :_ do
|
13
|
+
it "translates simple text" do
|
14
|
+
_('car').should == 'Auto'
|
15
|
+
end
|
16
|
+
it "returns msgid if not translation was found" do
|
17
|
+
_('NOT|FOUND').should == 'NOT|FOUND'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe :n_ do
|
22
|
+
it "translates pluralized" do
|
23
|
+
n_('Axis','Axis',1).should == 'Achse'
|
24
|
+
n_('Axis','Axis',2).should == 'Achsen'
|
25
|
+
end
|
26
|
+
it "returns the appropriate msgid if no translation was found" do
|
27
|
+
n_('NOTFOUND','NOTFOUNDs',1).should == 'NOTFOUND'
|
28
|
+
n_('NOTFOUND','NOTFOUNDs',2).should == 'NOTFOUNDs'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe :s_ do
|
33
|
+
it "translates simple text" do
|
34
|
+
_('car').should == 'Auto'
|
35
|
+
end
|
36
|
+
it "returns cleaned msgid if a translation was not found" do
|
37
|
+
s_("XXX|not found").should == "not found"
|
38
|
+
end
|
39
|
+
it "can use a custom seperator" do
|
40
|
+
s_("XXX/not found",'/').should == "not found"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
Binary file
|
Binary file
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# ---- requirements
|
2
|
+
$LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
|
3
|
+
require 'fast_gettext'
|
4
|
+
|
5
|
+
# ---- bugfix
|
6
|
+
#`exit?': undefined method `run?' for Test::Unit:Module (NoMethodError)
|
7
|
+
#can be solved with require test/unit but this will result in extra test-output
|
8
|
+
module Test
|
9
|
+
module Unit
|
10
|
+
def self.run?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# ---- Helpers
|
17
|
+
def pending_it(text,&block)
|
18
|
+
it text do
|
19
|
+
pending(&block)
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grosser-fast_gettext
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Grosser
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-19 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: grosser.michael@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- README.markdown
|
27
|
+
- lib/fast_gettext.rb
|
28
|
+
- lib/fast_gettext
|
29
|
+
- lib/fast_gettext/storage.rb
|
30
|
+
- lib/fast_gettext/mo_file.rb
|
31
|
+
- spec/fast_gettext_spec.rb
|
32
|
+
- spec/spec_helper.rb
|
33
|
+
- spec/locale
|
34
|
+
- spec/locale/en
|
35
|
+
- spec/locale/en/LC_MESSAGES
|
36
|
+
- spec/locale/en/LC_MESSAGES/test.mo
|
37
|
+
- spec/locale/de
|
38
|
+
- spec/locale/de/LC_MESSAGES
|
39
|
+
- spec/locale/de/LC_MESSAGES/test.mo
|
40
|
+
- spec/fast_gettext
|
41
|
+
- spec/fast_gettext/storage_spec.rb
|
42
|
+
- spec/fast_gettext/mo_file_spec.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/grosser/fast_gettext
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --inline-source
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.2.0
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: A simple, fast and threadsafe implementation of GetText
|
70
|
+
test_files: []
|
71
|
+
|