babilu 0.2.2
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/MIT-LICENSE +20 -0
- data/README +31 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/init.rb +3 -0
- data/lib/babilu.rb +57 -0
- data/lib/i18n_extensions.rb +16 -0
- data/lib/javascripts/babilu.js +96 -0
- data/lib/tasks/i18n_js_tasks.rake +4 -0
- data/test/babilu_test.rb +51 -0
- data/test/js/JSSpec.css +214 -0
- data/test/js/JSSpec.js +1411 -0
- data/test/js/diff_match_patch.js +1 -0
- data/test/js/specs.js +155 -0
- data/test/js/test.html +14 -0
- data/test/test_helper.rb +5 -0
- metadata +96 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
Babilu
|
2
|
+
=======
|
3
|
+
|
4
|
+
Babilu converts all your translations into JavaScript so you can use them on
|
5
|
+
the client side. It mimicks the Ruby/Rails I18n API and works in pretty much the
|
6
|
+
same way:
|
7
|
+
|
8
|
+
//JavaScript
|
9
|
+
I18n.defaultLocale // "en"
|
10
|
+
I18n.locale // Whatever the locale has been set to on the server
|
11
|
+
|
12
|
+
I18n.t('hello') // "Hello World"
|
13
|
+
I18n.t('messages.invalid', {scope:['activerecord', 'errors']}) // "is invalid"
|
14
|
+
I18n.t('activerecord.errors.template.header', {count:4, model:'pony'}) // "4 errors prohibited this pony from being saved"
|
15
|
+
|
16
|
+
I18n.translations.en.ponies = {one: 'I have a pony', other: 'I have %{count} ponies'};
|
17
|
+
I18n.t('ponies', {count:5}) // "I have 5 ponies"
|
18
|
+
|
19
|
+
The only difference is that because JavaScript doesn't have symbols we can't
|
20
|
+
easily differentiate between keys and values in the "defaultValue" option. A workaround
|
21
|
+
is used in which strings starting with ":" are considered to be keys and are used for
|
22
|
+
looking up additional translations:
|
23
|
+
|
24
|
+
I18n.t('doesntexist', {defaultValue:'humbaba'}) // "humbaba"
|
25
|
+
I18n.t('doesntexist', {defaultValue:':hello'}) // "Hello world"
|
26
|
+
I18n.t('doesntexist', {defaultValue:[':alsodoesntexist', 'The Sasqutch: Fact or Fiction?']}) // "The Sasquatch: Fact or Fiction?"
|
27
|
+
|
28
|
+
This plugin depends on http://github.com/toretore/lucy
|
29
|
+
|
30
|
+
|
31
|
+
Copyright (c) 2008 Tore Darell, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the Babilu plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the Babilu plugin.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'Babilu'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'jeweler'
|
26
|
+
Jeweler::Tasks.new do |gem|
|
27
|
+
# gem is a Gem::Specification. See http://docs.rubygems.org/read/chapter/20 for more options
|
28
|
+
gem.name = "babilu"
|
29
|
+
gem.homepage = "http://github.com/toretore/babilu"
|
30
|
+
gem.license = "MIT"
|
31
|
+
gem.summary = %Q{Rails plugin for javascript i18n}
|
32
|
+
gem.description = %Q{Babilu converts all your translations into JavaScript so you can use them on the client side. It mimicks the Ruby/Rails I18n API and works in pretty much the same way}
|
33
|
+
gem.email = ""
|
34
|
+
gem.authors = ["Tore Darell"]
|
35
|
+
gem.add_runtime_dependency 'lucy'
|
36
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
37
|
+
end
|
38
|
+
Jeweler::RubygemsDotOrgTasks.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.2
|
data/init.rb
ADDED
data/lib/babilu.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "lucy"
|
2
|
+
require "i18n_extensions"
|
3
|
+
module Babilu
|
4
|
+
|
5
|
+
JAVASCRIPT = File.read(File.join(File.dirname(__FILE__), 'javascripts', 'babilu.js'))
|
6
|
+
|
7
|
+
def self.generate
|
8
|
+
Lucy.generate("locales") do |g|
|
9
|
+
g.namespace = "I18n"
|
10
|
+
g[:defaultLocale] = default_locale
|
11
|
+
g[:translations] = translations
|
12
|
+
g << methods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.translations
|
17
|
+
I18n.all_translations
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.default_locale
|
21
|
+
I18n.default_locale
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.methods
|
25
|
+
JAVASCRIPT
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
module ControllerMethods
|
30
|
+
|
31
|
+
def self.included(controller)
|
32
|
+
controller.send(:after_filter, :set_locale_cookie)
|
33
|
+
controller.send(:after_filter, :generate_locale_javascript) if Rails.env.development?
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def set_locale_cookie
|
39
|
+
cookies[:locale] = I18n.locale.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
#In development mode, re-generate locale data on each request
|
43
|
+
def generate_locale_javascript
|
44
|
+
Babilu.generate
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class Railtie < Rails::Railtie
|
50
|
+
config.before_configuration do
|
51
|
+
config.action_view.javascript_expansions[:defaults] ||= [] << 'locales'
|
52
|
+
end
|
53
|
+
end if defined?(Rails::Railtie)
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
ActionController::Base.send(:include, Babilu::ControllerMethods)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
(function(){
|
2
|
+
|
3
|
+
var interpolatePattern = /%\{([^}]+)\}/g;
|
4
|
+
|
5
|
+
//Replace %{foo} with obj.foo
|
6
|
+
function interpolate(str, obj){
|
7
|
+
return str.replace(interpolatePattern, function(){
|
8
|
+
return typeof obj[arguments[1]] == 'undefined' ? arguments[0] : obj[arguments[1]];
|
9
|
+
});
|
10
|
+
};
|
11
|
+
|
12
|
+
//Split "foo.bar" to ["foo", "bar"] if key is a string
|
13
|
+
function keyToArray(key){
|
14
|
+
if (!key) return [];
|
15
|
+
if (typeof key != "string") return key;
|
16
|
+
return key.split('.');
|
17
|
+
};
|
18
|
+
|
19
|
+
function locale(){
|
20
|
+
return I18n.locale || I18n.defaultLocale;
|
21
|
+
};
|
22
|
+
|
23
|
+
function getLocaleFromCookie(){
|
24
|
+
var cookies = document.cookie.split(/\s*;\s*/),
|
25
|
+
i, pair, locale;
|
26
|
+
for (i = 0; i < cookies.length; i++) {
|
27
|
+
pair = cookies[i].split('=');
|
28
|
+
if (pair[0] === 'locale') { locale = pair[1]; break; }
|
29
|
+
}
|
30
|
+
return locale;
|
31
|
+
};
|
32
|
+
|
33
|
+
|
34
|
+
I18n.init = function(){
|
35
|
+
this.locale = getLocaleFromCookie();
|
36
|
+
};
|
37
|
+
|
38
|
+
//Works mostly the same as the Ruby equivalent, except there are
|
39
|
+
//no symbols in JavaScript, so keys are always strings. The only time
|
40
|
+
//this makes a difference is when differentiating between keys and values
|
41
|
+
//in the defaultValue option. Strings starting with ":" will be considered
|
42
|
+
//to be keys and used for lookup, while other strings are returned as-is.
|
43
|
+
I18n.translate = function(key, opts){
|
44
|
+
if (typeof key != "string") { //Bulk lookup
|
45
|
+
var a = [], i;
|
46
|
+
for (i=0; i<key.length; i++) {
|
47
|
+
a.push(this.translate(key[i], opts));
|
48
|
+
}
|
49
|
+
return a;
|
50
|
+
} else {
|
51
|
+
opts = opts || {};
|
52
|
+
opts.defaultValue = opts.defaultValue || null;
|
53
|
+
key = keyToArray(opts.scope).concat(keyToArray(key));
|
54
|
+
var value = this.lookup(key, opts.defaultValue);
|
55
|
+
if (typeof value != "string" && value) value = this.pluralize(value, opts.count);
|
56
|
+
if (typeof value == "string") value = interpolate(value, opts);
|
57
|
+
return value;
|
58
|
+
}
|
59
|
+
};
|
60
|
+
|
61
|
+
I18n.t = I18n.translate;
|
62
|
+
|
63
|
+
//Looks up a translation using an array of strings where the last
|
64
|
+
//is the key and any string before that define the scope. The current
|
65
|
+
//locale is always prepended and does not need to be provided. The second
|
66
|
+
//parameter is an array of strings used as defaults if the key can not be
|
67
|
+
//found. If a key starts with ":" it is used as a key for lookup.
|
68
|
+
//This method does not perform pluralization or interpolation.
|
69
|
+
I18n.lookup = function(keys, defaults){
|
70
|
+
var i = 0, value = this.translations[locale()];
|
71
|
+
defaults = typeof defaults == "string" ? [defaults] : (defaults || []);
|
72
|
+
while (keys[i]) {
|
73
|
+
value = value && value[keys[i]];
|
74
|
+
i++;
|
75
|
+
}
|
76
|
+
if (value){
|
77
|
+
return value;
|
78
|
+
} else {
|
79
|
+
if (defaults.length == 0) {
|
80
|
+
return null;
|
81
|
+
} else if (defaults[0].substr(0,1) == ':') {
|
82
|
+
return this.lookup(keys.slice(0,keys.length-1).concat(keyToArray(defaults[0].substr(1))), defaults.slice(1));
|
83
|
+
} else {
|
84
|
+
return defaults[0];
|
85
|
+
}
|
86
|
+
}
|
87
|
+
};
|
88
|
+
|
89
|
+
I18n.pluralize = function(value, count){
|
90
|
+
if (typeof count != 'number') return value;
|
91
|
+
return count == 1 ? value.one : value.other;
|
92
|
+
};
|
93
|
+
|
94
|
+
})();
|
95
|
+
|
96
|
+
I18n.init();
|
data/test/babilu_test.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
require 'babilu'
|
3
|
+
|
4
|
+
module SharedSetup
|
5
|
+
|
6
|
+
TRANSLATIONS = {
|
7
|
+
:en => {
|
8
|
+
:hello => "Hello World"
|
9
|
+
},
|
10
|
+
:no => {
|
11
|
+
:hello => "Hei verden"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
def setup
|
16
|
+
I18n.load_path = []
|
17
|
+
I18n.backend = I18n::Backend::Simple.new
|
18
|
+
I18n.default_locale = :en
|
19
|
+
TRANSLATIONS.each{|l,t| I18n.backend.store_translations(l,t) }
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class BabiluI18nExtensionsTest < Test::Unit::TestCase
|
25
|
+
|
26
|
+
include SharedSetup
|
27
|
+
|
28
|
+
def test_all_translations_should_be_defined_on_simple_backend
|
29
|
+
assert I18n::Backend::Simple.instance_methods.include?("all_translations")
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_all_translations_should_return_a_hash_with_all_translations_in_all_locales
|
33
|
+
assert_equal TRANSLATIONS, I18n.all_translations
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
class BabiluTest < Test::Unit::TestCase
|
40
|
+
|
41
|
+
include SharedSetup
|
42
|
+
|
43
|
+
def test_translations_should_return_all_translations
|
44
|
+
assert_equal TRANSLATIONS, Babilu.translations
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_default_locale_should_use_i18n_default_locale
|
48
|
+
assert_equal I18n.default_locale, Babilu.default_locale
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/test/js/JSSpec.css
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
@CHARSET "UTF-8";
|
2
|
+
|
3
|
+
/* --------------------
|
4
|
+
* @Layout
|
5
|
+
*/
|
6
|
+
|
7
|
+
html {
|
8
|
+
overflow: hidden;
|
9
|
+
}
|
10
|
+
|
11
|
+
body, #jsspec_container {
|
12
|
+
overflow: hidden;
|
13
|
+
padding: 0;
|
14
|
+
margin: 0;
|
15
|
+
width: 100%;
|
16
|
+
height: 100%;
|
17
|
+
}
|
18
|
+
|
19
|
+
#title {
|
20
|
+
padding: 0;
|
21
|
+
margin: 0;
|
22
|
+
position: absolute;
|
23
|
+
top: 0px;
|
24
|
+
left: 0px;
|
25
|
+
width: 100%;
|
26
|
+
height: 40px;
|
27
|
+
overflow: hidden;
|
28
|
+
}
|
29
|
+
|
30
|
+
#list {
|
31
|
+
padding: 0;
|
32
|
+
margin: 0;
|
33
|
+
position: absolute;
|
34
|
+
top: 40px;
|
35
|
+
left: 0px;
|
36
|
+
bottom: 0px;
|
37
|
+
overflow: auto;
|
38
|
+
width: 250px;
|
39
|
+
_height:expression(document.body.clientHeight-40);
|
40
|
+
}
|
41
|
+
|
42
|
+
#log {
|
43
|
+
padding: 0;
|
44
|
+
margin: 0;
|
45
|
+
position: absolute;
|
46
|
+
top: 40px;
|
47
|
+
left: 250px;
|
48
|
+
right: 0px;
|
49
|
+
bottom: 0px;
|
50
|
+
overflow: auto;
|
51
|
+
_height:expression(document.body.clientHeight-40);
|
52
|
+
_width:expression(document.body.clientWidth-250);
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
/* --------------------
|
58
|
+
* @Decorations and colors
|
59
|
+
*/
|
60
|
+
* {
|
61
|
+
padding: 0;
|
62
|
+
margin: 0;
|
63
|
+
font-family: "Lucida Grande", Helvetica, sans-serif;
|
64
|
+
}
|
65
|
+
|
66
|
+
li {
|
67
|
+
list-style: none;
|
68
|
+
}
|
69
|
+
|
70
|
+
/* hiding subtitles */
|
71
|
+
h2 {
|
72
|
+
display: none;
|
73
|
+
}
|
74
|
+
|
75
|
+
/* title section */
|
76
|
+
div#title {
|
77
|
+
padding: 0em 0.5em;
|
78
|
+
}
|
79
|
+
|
80
|
+
div#title h1 {
|
81
|
+
font-size: 1.5em;
|
82
|
+
float: left;
|
83
|
+
}
|
84
|
+
|
85
|
+
div#title ul li {
|
86
|
+
float: left;
|
87
|
+
padding: 0.5em 0em 0.5em 0.75em;
|
88
|
+
}
|
89
|
+
|
90
|
+
div#title p {
|
91
|
+
float:right;
|
92
|
+
margin-right:1em;
|
93
|
+
font-size: 0.75em;
|
94
|
+
}
|
95
|
+
|
96
|
+
/* spec container */
|
97
|
+
ul.specs {
|
98
|
+
margin: 0.5em;
|
99
|
+
}
|
100
|
+
ul.specs li {
|
101
|
+
margin-bottom: 0.1em;
|
102
|
+
}
|
103
|
+
|
104
|
+
/* spec title */
|
105
|
+
ul.specs li h3 {
|
106
|
+
font-weight: bold;
|
107
|
+
font-size: 0.75em;
|
108
|
+
padding: 0.2em 1em;
|
109
|
+
}
|
110
|
+
|
111
|
+
/* example container */
|
112
|
+
ul.examples li {
|
113
|
+
border-style: solid;
|
114
|
+
border-width: 0px 0px 1px 5px;
|
115
|
+
margin: 0.2em 0em 0.2em 1em;
|
116
|
+
}
|
117
|
+
|
118
|
+
/* example title */
|
119
|
+
ul.examples li h4 {
|
120
|
+
font-weight: normal;
|
121
|
+
font-size: 0.75em;
|
122
|
+
margin-left: 1em;
|
123
|
+
}
|
124
|
+
|
125
|
+
/* example explaination */
|
126
|
+
ul.examples li div {
|
127
|
+
padding: 1em 2em;
|
128
|
+
font-size: 0.75em;
|
129
|
+
}
|
130
|
+
|
131
|
+
/* styles for ongoing, success, failure, error */
|
132
|
+
div.success, div.success a {
|
133
|
+
color: #FFFFFF;
|
134
|
+
background-color: #65C400;
|
135
|
+
}
|
136
|
+
|
137
|
+
ul.specs li.success h3, ul.specs li.success h3 a {
|
138
|
+
color: #FFFFFF;
|
139
|
+
background-color: #65C400;
|
140
|
+
}
|
141
|
+
|
142
|
+
ul.examples li.success, ul.examples li.success a {
|
143
|
+
color: #3D7700;
|
144
|
+
background-color: #DBFFB4;
|
145
|
+
border-color: #65C400;
|
146
|
+
}
|
147
|
+
|
148
|
+
div.exception, div.exception a {
|
149
|
+
color: #FFFFFF;
|
150
|
+
background-color: #C20000;
|
151
|
+
}
|
152
|
+
|
153
|
+
ul.specs li.exception h3, ul.specs li.exception h3 a {
|
154
|
+
color: #FFFFFF;
|
155
|
+
background-color: #C20000;
|
156
|
+
}
|
157
|
+
|
158
|
+
ul.examples li.exception, ul.examples li.exception a {
|
159
|
+
color: #C20000;
|
160
|
+
background-color: #FFFBD3;
|
161
|
+
border-color: #C20000;
|
162
|
+
}
|
163
|
+
|
164
|
+
div.ongoing, div.ongoing a {
|
165
|
+
color: #000000;
|
166
|
+
background-color: #FFFF80;
|
167
|
+
}
|
168
|
+
|
169
|
+
ul.specs li.ongoing h3, ul.specs li.ongoing h3 a {
|
170
|
+
color: #000000;
|
171
|
+
background-color: #FFFF80;
|
172
|
+
}
|
173
|
+
|
174
|
+
ul.examples li.ongoing, ul.examples li.ongoing a {
|
175
|
+
color: #000000;
|
176
|
+
background-color: #FFFF80;
|
177
|
+
border-color: #DDDD00;
|
178
|
+
}
|
179
|
+
|
180
|
+
|
181
|
+
|
182
|
+
/* --------------------
|
183
|
+
* values
|
184
|
+
*/
|
185
|
+
.number_value, .string_value, .regexp_value, .boolean_value, .dom_value {
|
186
|
+
font-family: monospace;
|
187
|
+
color: blue;
|
188
|
+
}
|
189
|
+
.object_value, .array_value {
|
190
|
+
line-height: 2em;
|
191
|
+
padding: 0.1em 0.2em;
|
192
|
+
margin: 0.1em 0;
|
193
|
+
}
|
194
|
+
.date_value {
|
195
|
+
font-family: monospace;
|
196
|
+
color: olive;
|
197
|
+
}
|
198
|
+
.undefined_value, .null_value {
|
199
|
+
font-style: italic;
|
200
|
+
color: blue;
|
201
|
+
}
|
202
|
+
.dom_attr_name {
|
203
|
+
}
|
204
|
+
.dom_attr_value {
|
205
|
+
color: red;
|
206
|
+
}
|
207
|
+
.dom_path {
|
208
|
+
font-size: 0.75em;
|
209
|
+
color: gray;
|
210
|
+
}
|
211
|
+
strong {
|
212
|
+
font-weight: normal;
|
213
|
+
background-color: #FFC6C6;
|
214
|
+
}
|