grosser-fast_gettext 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|