babilu 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ require "babilu"
2
+ I18n.reload!
3
+ Babilu.generate
@@ -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,16 @@
1
+ module I18n
2
+ class << self
3
+ def all_translations
4
+ backend.all_translations
5
+ end
6
+ end
7
+
8
+ module Backend
9
+ class Simple
10
+ def all_translations
11
+ send(:init_translations) unless initialized?
12
+ translations
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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();
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :i18n_js do
3
+ # # Task goes here
4
+ # end
@@ -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
@@ -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
+ }