localite 0.3 → 0.5.6
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/Manifest +44 -0
- data/VERSION +1 -1
- data/bin/list-tr +10 -0
- data/lib/localite.rb +161 -43
- data/lib/localite/filter.rb +23 -64
- data/lib/localite/format.rb +36 -0
- data/lib/localite/node_filter.rb +197 -0
- data/lib/localite/scopes.rb +93 -0
- data/lib/localite/settings.rb +139 -17
- data/lib/localite/storage.rb +132 -0
- data/lib/localite/template.rb +29 -25
- data/lib/localite/tr.rb +292 -0
- data/lib/localite/translate.rb +46 -20
- data/localite.gemspec +9 -6
- data/localite.tmproj +3 -99
- data/tasks/echoe.rake +1 -1
- data/test/i18n/de.tr +5 -0
- data/test/i18n/en.tr +16 -0
- data/test/rails/Rakefile +10 -0
- data/test/rails/app/controllers/application_controller.rb +10 -0
- data/test/rails/app/controllers/localite_controller.rb +20 -0
- data/test/rails/app/views/localite/index.html.erb +19 -0
- data/test/rails/app/views/localite/template.de.html.erb +1 -0
- data/test/rails/app/views/localite/template.en.html.erb +1 -0
- data/test/rails/config/boot.rb +110 -0
- data/test/rails/config/environment.rb +50 -0
- data/test/rails/config/environments/development.rb +17 -0
- data/test/rails/config/environments/production.rb +28 -0
- data/test/rails/config/environments/test.rb +28 -0
- data/test/rails/config/initializers/new_rails_defaults.rb +21 -0
- data/test/rails/config/initializers/session_store.rb +15 -0
- data/test/rails/config/locales/de.yml +4 -0
- data/test/rails/config/locales/en.yml +4 -0
- data/test/rails/config/routes.rb +13 -0
- data/test/rails/script/console +3 -0
- data/test/rails/script/server +3 -0
- data/test/rails/test/functional/localite_controller_test.rb +56 -0
- data/test/rails/test/test_helper.rb +38 -0
- data/test/test.rb +4 -1
- metadata +51 -11
- data/lib/localite/scope.rb +0 -140
- data/test/i18n/de.yml +0 -15
@@ -0,0 +1,36 @@
|
|
1
|
+
module Localite::Format
|
2
|
+
#
|
3
|
+
# convert from text into target format
|
4
|
+
def self.text(s)
|
5
|
+
case s
|
6
|
+
when /html:(.*)/m then $1
|
7
|
+
when /text:(.*)/m then $1
|
8
|
+
else s
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.html(s)
|
13
|
+
case s
|
14
|
+
when /html:(.*)/m then $1
|
15
|
+
when /text:(.*)/m then CGI.escapeHTML($1)
|
16
|
+
else CGI.escapeHTML(s)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Localite::Format::Etest
|
22
|
+
def test_format
|
23
|
+
assert_equal "xxx", Localite::Format.html("xxx")
|
24
|
+
assert_equal "xxx", Localite::Format.html("html:xxx")
|
25
|
+
assert_equal "xxx", Localite::Format.html("text:xxx")
|
26
|
+
|
27
|
+
assert_equal "<>", Localite::Format.html("text:<>")
|
28
|
+
assert_equal "<>", Localite::Format.html("<>")
|
29
|
+
assert_equal "<>", Localite::Format.html("html:<>")
|
30
|
+
|
31
|
+
assert_equal "xxx\nyyy", Localite::Format.html("xxx\nyyy")
|
32
|
+
assert_equal "xxx\nyyy", Localite::Format.html("html:xxx\nyyy")
|
33
|
+
assert_equal "xxx\nyyy", Localite::Format.html("text:xxx\nyyy")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
3
|
+
module Localite::NodeFilter
|
4
|
+
#
|
5
|
+
# set up filter for this action.
|
6
|
+
def self.filter(controller, &block)
|
7
|
+
yield
|
8
|
+
|
9
|
+
return unless controller.response.headers["Content-Type"] =~ /text\/html/
|
10
|
+
r = controller.response
|
11
|
+
r.body = filter_body r.body, Localite.current_locale
|
12
|
+
rescue
|
13
|
+
if Rails.env.development?
|
14
|
+
controller.response.body = "Caught exception: " + CGI::escapeHTML($!.inspect)
|
15
|
+
end
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(klass)
|
20
|
+
klass.send :around_filter, self
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def self.filter_body(body, locale)
|
26
|
+
if body =~ /(<body[^>]*>)(.*)(<\/body>)/m
|
27
|
+
$`.html_safe +
|
28
|
+
$1.html_safe +
|
29
|
+
filter_node($2, locale).html_safe +
|
30
|
+
$3.html_safe +
|
31
|
+
$'.html_safe
|
32
|
+
else
|
33
|
+
filter_node(body, locale).html_safe
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.filter_node(body, locale)
|
38
|
+
locale = locale.to_s
|
39
|
+
body = fb_mark(body)
|
40
|
+
|
41
|
+
doc = Nokogiri.HTML "<html><body>#{body}</body></html>"
|
42
|
+
doc.css("[lang]").each do |node|
|
43
|
+
next unless locale != node["lang"]
|
44
|
+
node.remove
|
45
|
+
end
|
46
|
+
|
47
|
+
doc = doc.css("body").inner_html
|
48
|
+
doc = fb_unmark(doc)
|
49
|
+
doc.html_safe
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# for <fb:XXXXXX> tags to survive Nokogiri's HTML parsing we rename
|
54
|
+
# them into something non-namespacy. THIS IS A HACK! I wished I knew
|
55
|
+
# how to make
|
56
|
+
#
|
57
|
+
# a) Nokogiri.XML to let entities live, or, preferredly
|
58
|
+
# b) Nokogiri.HTML to let namespaces survive
|
59
|
+
#
|
60
|
+
FB = "fb:"
|
61
|
+
FB_RE = /(<|<\/)fb:/
|
62
|
+
FB_MARKER = "fb_marker_0xjgh_123_"
|
63
|
+
FB_MARKER_RE = Regexp.new "(<|<\/)#{FB_MARKER}"
|
64
|
+
|
65
|
+
def self.fb_mark(s)
|
66
|
+
s.gsub(FB_RE) { $1 + FB_MARKER }
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.fb_unmark(s)
|
70
|
+
s.gsub(FB_MARKER_RE) { $1 + FB }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module Localite::NodeFilter::Etest
|
75
|
+
def normalize_xml(str)
|
76
|
+
str.
|
77
|
+
gsub(/>(.*?)</m) do |s| ">#{$1.gsub(/\s+/m, " ")}<" end. # normalize spaces between tags
|
78
|
+
gsub(/>\s*</m, ">\n<"). # normalize spaces in empty text nodes
|
79
|
+
gsub(/"/, "'") # normalize ' and "
|
80
|
+
end
|
81
|
+
|
82
|
+
def assert_filtered(locale, src, expected)
|
83
|
+
filtered = Localite::NodeFilter.filter_body(src, locale)
|
84
|
+
filtered = normalize_xml(filtered)
|
85
|
+
|
86
|
+
expected = normalize_xml(expected)
|
87
|
+
|
88
|
+
if expected == filtered
|
89
|
+
assert true
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
puts "expected:\n\n" + expected + "\n\n"
|
94
|
+
puts "filtered:\n\n" + filtered + "\n\n"
|
95
|
+
|
96
|
+
assert_equal expected, filtered
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
def test_normalize_xml
|
101
|
+
assert_equal "<p>ppp</p>", normalize_xml("<p>ppp</p>")
|
102
|
+
assert_equal "<p>a b c </p>\n", normalize_xml("<p>a b c </p>\n")
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_simple_html
|
106
|
+
assert_filtered :de, "<p>ppp</p>", "<p>ppp</p>"
|
107
|
+
assert_filtered :en, "<p>ppp</p>", "<p>ppp</p>"
|
108
|
+
assert_filtered :fr, "<p>ppp</p>", "<p>ppp</p>"
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_simple_html_w_lang
|
112
|
+
assert_filtered :de, "<p lang='de'>ppp</p>", "<p lang='de'>ppp</p>"
|
113
|
+
assert_filtered :en, "<p lang='de'>ppp</p>", ""
|
114
|
+
assert_filtered :fr, "<p lang='de'>ppp</p>", ""
|
115
|
+
|
116
|
+
assert_filtered :de, "<p lang='en'>ppp</p>", ""
|
117
|
+
assert_filtered :en, "<p lang='en'>ppp</p>", "<p lang='en'>ppp</p>"
|
118
|
+
assert_filtered :fr, "<p lang='en'>ppp</p>", ""
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_fbml_tags
|
122
|
+
assert_filtered :de, "<fb:p lang='de'>ppp</fb:p>", "<fb:p lang='de'>ppp</fb:p>"
|
123
|
+
assert_filtered :en, "<fb:p lang='de'>ppp</fb:p>", ""
|
124
|
+
assert_filtered :fr, "<fb:p lang='de'>ppp</fb:p>", ""
|
125
|
+
|
126
|
+
assert_filtered :de, "<fb:p lang='en'>ppp</fb:p>", ""
|
127
|
+
assert_filtered :en, "<fb:p lang='en'>ppp</fb:p>", "<fb:p lang='en'>ppp</fb:p>"
|
128
|
+
assert_filtered :fr, "<fb:p lang='en'>ppp</fb:p>", ""
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_umlauts
|
132
|
+
assert_filtered :de, "<p lang='de'>Ä Ö</p>", "<p lang='de'>Ä Ö</p>"
|
133
|
+
|
134
|
+
#
|
135
|
+
# Nokogiri translates UTF-8 umlauts into entities. That is ok (for now)
|
136
|
+
assert_filtered :de, "<p lang='de'>Ä Ö</p>", "<p lang='de'>Ä Ö</p>"
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_full_html_match
|
140
|
+
src = <<-HTML
|
141
|
+
<!--Force IE6 into quirks mode with this comment tag-->
|
142
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
143
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
144
|
+
<head>
|
145
|
+
<title>socially.io</title>
|
146
|
+
</head>
|
147
|
+
<body x="y">
|
148
|
+
<p lang='de'>Ä Ö</p>", "<p lang='de'>Ä Ö</p>
|
149
|
+
</body>
|
150
|
+
</html>
|
151
|
+
HTML
|
152
|
+
|
153
|
+
expected = <<-HTML
|
154
|
+
<!--Force IE6 into quirks mode with this comment tag-->
|
155
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
156
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
157
|
+
<head>
|
158
|
+
<title>socially.io</title>
|
159
|
+
</head>
|
160
|
+
<body x="y">
|
161
|
+
<p lang='de'>Ä Ö</p>", "<p lang='de'>Ä Ö</p>
|
162
|
+
</body>
|
163
|
+
</html>
|
164
|
+
HTML
|
165
|
+
|
166
|
+
assert_filtered :de, src, expected
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_full_html_miss
|
170
|
+
src = <<-HTML
|
171
|
+
<!--Force IE6 into quirks mode with this comment tag-->
|
172
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
173
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
174
|
+
<head>
|
175
|
+
<title>socially.io</title>
|
176
|
+
</head>
|
177
|
+
<body x="y">
|
178
|
+
<p lang='de'>Ä Ö</p>
|
179
|
+
</body>
|
180
|
+
</html>
|
181
|
+
HTML
|
182
|
+
|
183
|
+
expected = <<-HTML
|
184
|
+
<!--Force IE6 into quirks mode with this comment tag-->
|
185
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
186
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
187
|
+
<head>
|
188
|
+
<title>socially.io</title>
|
189
|
+
</head>
|
190
|
+
<body x="y">
|
191
|
+
</body>
|
192
|
+
</html>
|
193
|
+
HTML
|
194
|
+
|
195
|
+
assert_filtered :en, src, expected
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
class Localite::Scopes < Array
|
2
|
+
def initialize
|
3
|
+
@prebuilt = []
|
4
|
+
end
|
5
|
+
|
6
|
+
def push(*array)
|
7
|
+
array.each do |s|
|
8
|
+
if @prebuilt.last
|
9
|
+
@prebuilt.push("#{@prebuilt.last}.#{s}")
|
10
|
+
else
|
11
|
+
@prebuilt.push(s.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
super(s)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def pop(*array)
|
19
|
+
array.each do
|
20
|
+
@prebuilt.pop
|
21
|
+
super()
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(s)
|
26
|
+
@prebuilt.reverse_each do |entry|
|
27
|
+
yield "#{entry}.#{s}"
|
28
|
+
end
|
29
|
+
|
30
|
+
yield s.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def first(s)
|
34
|
+
if @prebuilt.last
|
35
|
+
"#{@prebuilt.last}.#{s}"
|
36
|
+
else
|
37
|
+
s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
join(":")
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
to_s.inspect
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module Localite::Scopes::Etest
|
51
|
+
Scopes = Localite::Scopes
|
52
|
+
|
53
|
+
def test_scope
|
54
|
+
scope = Scopes.new
|
55
|
+
|
56
|
+
scope.push("a")
|
57
|
+
scope.push("b")
|
58
|
+
scope.push("b", "str")
|
59
|
+
|
60
|
+
assert_equal %w(a b b str), scope
|
61
|
+
assert_equal %w(a.b.b.str a.b.b a.b a), scope.instance_variable_get("@prebuilt").reverse
|
62
|
+
assert_equal "a:b:b:str", scope.to_s
|
63
|
+
assert_equal "\"a:b:b:str\"", scope.inspect
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_more_scopes
|
67
|
+
Localite.scope("a", :b, "b") do
|
68
|
+
r = []
|
69
|
+
Localite.current_scope.each("str") do |scoped|
|
70
|
+
r << scoped
|
71
|
+
end
|
72
|
+
assert_equal %w(a.b.b.str a.b.str a.str str), r
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_more_scopes_w_dots
|
77
|
+
Localite.scope("a", :b, "b.c.d") do
|
78
|
+
r = []
|
79
|
+
Localite.current_scope.each("str.y") do |scoped|
|
80
|
+
r << scoped
|
81
|
+
end
|
82
|
+
assert_equal %w(a.b.b.c.d.str.y a.b.str.y a.str.y str.y), r
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_empty_scopes
|
87
|
+
r = []
|
88
|
+
Localite.current_scope.each("str.y") do |scoped|
|
89
|
+
r << scoped
|
90
|
+
end
|
91
|
+
assert_equal %w(str.y), r
|
92
|
+
end
|
93
|
+
end
|
data/lib/localite/settings.rb
CHANGED
@@ -1,37 +1,159 @@
|
|
1
|
+
#
|
2
|
+
# Localite provides three dimensions for translation keys. A call like
|
3
|
+
# that:
|
4
|
+
#
|
5
|
+
# Localite.locale(:de) do
|
6
|
+
# Localite.format(:fbml) do
|
7
|
+
# Localite.scope("outer") do
|
8
|
+
# Localite.scope("scope") do
|
9
|
+
# "msg".t
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# looks up the following entries in these formats and languages, in
|
16
|
+
# that order:
|
17
|
+
#
|
18
|
+
# - "fbml.de.outer.scope.msg"
|
19
|
+
# - "fbml.en.outer.scope.msg"
|
20
|
+
# - "html.de.outer.scope.msg"
|
21
|
+
# - "html.en.outer.scope.msg"
|
22
|
+
# - "text.de.outer.scope.msg"
|
23
|
+
# - "text.en.outer.scope.msg"
|
24
|
+
# - "fbml.de.scope.msg"
|
25
|
+
# - "fbml.en.scope.msg"
|
26
|
+
# - "html.de.scope.msg"
|
27
|
+
# - "html.en.scope.msg"
|
28
|
+
# - "text.de.scope.msg"
|
29
|
+
# - "text.en.scope.msg"
|
30
|
+
# - "fbml.de.msg"
|
31
|
+
# - "fbml.en.msg"
|
32
|
+
# - "html.de.msg"
|
33
|
+
# - "html.en.msg"
|
34
|
+
# - "text.de.msg"
|
35
|
+
# - "text.en.msg"
|
36
|
+
#
|
1
37
|
module Localite::Settings
|
2
38
|
#
|
39
|
+
# == set/return the locale ==========================================
|
40
|
+
|
41
|
+
#
|
3
42
|
# Returns the base locale; e.g. :en
|
4
43
|
def base
|
5
|
-
|
44
|
+
:en
|
6
45
|
end
|
7
46
|
|
8
47
|
#
|
9
48
|
# returns the current locale; defaults to the base locale
|
10
|
-
def
|
11
|
-
|
49
|
+
def current_locale
|
50
|
+
Thread.current[:"localite:locale"] || base
|
12
51
|
end
|
13
52
|
|
14
|
-
|
15
|
-
|
16
|
-
def available?(locale)
|
17
|
-
locale && I18n.backend.available_locales.include?(locale.to_sym)
|
53
|
+
def current_locale=(locale)
|
54
|
+
I18n.locale = Thread.current[:"localite:locale"] = locale
|
18
55
|
end
|
19
56
|
|
20
57
|
#
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
locale = locale.to_sym
|
25
|
-
I18n.locale = available?(locale) ? locale : base
|
58
|
+
# runs a block in the changed locale
|
59
|
+
def locale(locale, &block)
|
60
|
+
scope :locale => locale, &block
|
26
61
|
end
|
27
62
|
|
28
63
|
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
64
|
+
# scope allows to set a scope around a translation. A scoped
|
65
|
+
# translation
|
66
|
+
#
|
67
|
+
# Localite.scope("scope") do
|
68
|
+
# "msg".t
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# will look up "scope.msg" and "msg", in that order, and return the
|
72
|
+
# first matching translation in the current locale. Scopes can be
|
73
|
+
# stacked; looking up a scoped translation
|
74
|
+
#
|
75
|
+
# Localite.scope("outer") do
|
76
|
+
# Localite.scope("scope") do
|
77
|
+
# "msg".t
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# will look up "outer.scope.msg", "scope.msg", "msg".
|
82
|
+
#
|
83
|
+
# If no translation will be found we look up the same entries in the base
|
84
|
+
# locale.
|
85
|
+
def scope(*args, &block)
|
86
|
+
options = if args.last.is_a?(Hash)
|
87
|
+
args.pop
|
88
|
+
else
|
89
|
+
{}
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# set locale
|
94
|
+
if locale = options[:locale]
|
95
|
+
old_locale = self.current_locale
|
96
|
+
self.current_locale = if I18n.backend.available_locales.include?(locale = locale.to_sym)
|
97
|
+
locale
|
98
|
+
else
|
99
|
+
base
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# set format
|
105
|
+
if format = options[:format]
|
106
|
+
old_format = self.current_format
|
107
|
+
self.current_format = format
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# adjust scope (from the remaining arguments)
|
112
|
+
current_scope.push(*args)
|
113
|
+
|
33
114
|
yield
|
34
115
|
ensure
|
35
|
-
|
116
|
+
current_scope.pop(*args)
|
117
|
+
self.current_format = old_format if old_format
|
118
|
+
self.current_locale = old_locale if old_locale
|
119
|
+
end
|
120
|
+
|
121
|
+
def scope!(*args, &block)
|
122
|
+
old = current_scope
|
123
|
+
Thread.current[:"localite:scopes"] = Localite::Scopes.new
|
124
|
+
|
125
|
+
scope(*args, &block)
|
126
|
+
ensure
|
127
|
+
Thread.current[:"localite:scopes"] = old
|
128
|
+
end
|
129
|
+
|
130
|
+
def current_scope
|
131
|
+
Thread.current[:"localite:scopes"] ||= Localite::Scopes.new
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# == format setting =================================================
|
136
|
+
|
137
|
+
#
|
138
|
+
# The format setting defines how the template engine deals with its
|
139
|
+
# parameters. In :html mode all parameters will be subject to HTML
|
140
|
+
# escaping, while in :text mode the parameters remain unchanged.
|
141
|
+
#
|
142
|
+
def format(format, &block)
|
143
|
+
scope :format => format, &block
|
144
|
+
end
|
145
|
+
|
146
|
+
def current_format
|
147
|
+
Thread.current[:"localite:format"] || :text
|
148
|
+
end
|
149
|
+
|
150
|
+
def current_format=(fmt)
|
151
|
+
Thread.current[:"localite:format"] = fmt
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
# == Inspect ========================================================
|
156
|
+
def inspect
|
157
|
+
"<Localite: [#{current_locale}/#{current_format}]: #{Localite.current_scope.inspect}"
|
36
158
|
end
|
37
159
|
end
|