/
localizer.rb
152 lines (130 loc) · 4.81 KB
/
localizer.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
module Formtastic
# Implementation for looking up localized values within Formtastic using I18n, if no
# explicit value (like the `:label` option) is set and I18n-lookups are enabled in the
# configuration.
#
# You can subclass this to implement your own Localizer, and configure Formtastic to use this
# localizer with:
#
# Formtastic::FormBuilder.i18n_localizer
#
# Enabled/disable i18n lookups completely with:
#
# Formtastic::FormBuilder.i18n_lookups_by_default = true/false
#
# Lookup priority:
#
# 'formtastic.%{type}.%{model}.%{action}.%{attribute}'
# 'formtastic.%{type}.%{model}.%{attribute}'
# 'formtastic.%{type}.%{attribute}'
#
# Example:
#
# 'formtastic.labels.post.edit.title'
# 'formtastic.labels.post.title'
# 'formtastic.labels.title'
class Localizer
class Cache
def get(key)
cache[key]
end
def has_key?(key)
cache.has_key?(key)
end
def set(key, result)
cache[key] = result
end
def cache
@cache ||= {}
end
def clear!
cache.clear
end
end
attr_accessor :builder
def self.cache
@cache ||= Cache.new
end
def initialize(current_builder)
self.builder = current_builder
end
def localize(key, value, type, options = {}) # @private
key = value if value.is_a?(::Symbol)
if value.is_a?(::String)
escape_html_entities(value)
else
use_i18n = value.nil? ? i18n_lookups_by_default : (value != false)
use_cache = i18n_cache_lookups
cache = self.class.cache
if use_i18n
model_name, nested_model_name = normalize_model_name(builder.model_name.underscore)
action_name = builder.template.params[:action].to_s rescue ''
attribute_name = key.to_s
# look in the cache first
if use_cache
cache_key = [::I18n.locale, action_name, model_name, nested_model_name, attribute_name, key, value, type, options]
return cache.get(cache_key) if cache.has_key?(cache_key)
end
defaults = Formtastic::I18n::SCOPES.reject do |i18n_scope|
nested_model_name.nil? && i18n_scope.match(/nested_model/)
end.collect do |i18n_scope|
i18n_path = i18n_scope.dup
i18n_path.gsub!('%{action}', action_name)
i18n_path.gsub!('%{model}', model_name)
i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
i18n_path.gsub!('%{attribute}', attribute_name)
i18n_path.tr!('..', '.')
i18n_path.to_sym
end
defaults << ''
defaults.uniq!
default_key = defaults.shift
i18n_value = Formtastic::I18n.t(default_key,
options.merge(:default => defaults, :scope => type.to_s.pluralize.to_sym))
i18n_value = i18n_value.is_a?(::String) ? i18n_value : nil
if i18n_value.blank? && type == :label
# This is effectively what Rails label helper does for i18n lookup
options[:scope] = [:helpers, type]
options[:default] = defaults
i18n_value = ::I18n.t(default_key, options)
end
# save the result to the cache
result = (i18n_value.is_a?(::String) && i18n_value.present?) ? escape_html_entities(i18n_value) : nil
cache.set(cache_key, result) if use_cache
result
end
end
end
protected
def normalize_model_name(name)
if !name =~ /\[/ && builder.respond_to?(:parent_builder) && builder.parent_builder.object_name
# Rails 3.1 nested builder case
[builder.parent_builder.object_name.to_s, name]
elsif name =~ /(.+)\[(.+)\]/
# Rails 3 (and 3.1?) nested builder case with :post rather than @post
[$1, $2]
elsif builder.respond_to?(:options) && builder.options.key?(:as)
[builder.options[:as].to_s]
elsif builder.respond_to?(:options) && builder.options.key?(:parent_builder)
# Rails 3.0 nested builder work-around case, where :parent_builder is provided by f.semantic_form_for
[builder.options[:parent_builder].object_name.to_s, name]
else
# Non-nested case
[name]
end
end
def escape_html_entities(string) # @private
if (builder.escape_html_entities_in_hints_and_labels) ||
(self.respond_to?(:escape_html_entities_in_hints_and_labels) && escape_html_entities_in_hints_and_labels)
string = builder.template.escape_once(string) unless string.respond_to?(:html_safe?) && string.html_safe? == true # Accept html_safe flag as indicator to skip escaping
end
string
end
def i18n_lookups_by_default
builder.i18n_lookups_by_default
end
def i18n_cache_lookups
builder.i18n_cache_lookups
end
end
end