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