jerome-localized-frontend 0.1
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 +54 -0
- data/Rakefile +22 -0
- data/TODO +2 -0
- data/init.rb +1 -0
- data/lib/localized_frontend.rb +15 -0
- data/lib/localized_frontend/active_record_extension.rb +13 -0
- data/lib/localized_frontend/active_record_extension/error_messages.rb +29 -0
- data/lib/localized_frontend/active_record_extension/translatable_column_names.rb +34 -0
- data/lib/localized_frontend/core.rb +62 -0
- data/lib/localized_frontend/date_helper.rb +36 -0
- data/lib/localized_frontend/form_labels.rb +20 -0
- data/lib/localized_frontend/inflector_extension.rb +131 -0
- data/lib/localized_frontend/strftime.rb +36 -0
- data/lib/localized_frontend/to_sentence.rb +19 -0
- data/lib/translations/french.yml +59 -0
- data/lib/translations/norwegian.yml +79 -0
- data/test/active_record_test.rb +25 -0
- data/test/everything_else_test.rb +13 -0
- data/test/form_labels_test.rb +41 -0
- data/test/inflector_test.rb +38 -0
- data/test/strftime_test.rb +29 -0
- data/test/test_helper.rb +77 -0
- data/test/translated_test.rb +28 -0
- metadata +76 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 August Lilleaas
|
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,54 @@
|
|
1
|
+
= Localized Frontend
|
2
|
+
|
3
|
+
http://gitorious.org/projects/localized-frontend/
|
4
|
+
http://github.com/leethal/localized-frontend/
|
5
|
+
|
6
|
+
Localizing the front-end of single language Rails apps.
|
7
|
+
|
8
|
+
The localizations are stored in YAML files. Create a spanish.yml file, call
|
9
|
+
LocalizedFrontend.in :spanish somewhere in your app (probably in environment.rb or an
|
10
|
+
initializer), and rejoice!
|
11
|
+
|
12
|
+
It localizes inflectors (pluralize, singularize etc), Active Record error messages, month
|
13
|
+
and day names, form labels (<%= f.label :foo %>) and Array#to_sentence.
|
14
|
+
|
15
|
+
== To contributors
|
16
|
+
|
17
|
+
Feel free to submit your own translations and localizations! You can mail me patches on
|
18
|
+
augustlilleaas@gmail.com, or you can fork the Git repository on
|
19
|
+
http://github.com/leethal/localized-frontend/ and send a pull request. Or write it down on
|
20
|
+
paper and mail it to me. Your call!
|
21
|
+
|
22
|
+
Keep in mind that the YAML file is whiny - certain keys and values are required in
|
23
|
+
order to avoid errors being raised. At the moment, the tests are hardcoded in Norwegian.
|
24
|
+
That should probably change, so that one can set the language in an environment
|
25
|
+
variable and run the tests in whatever language one might prefer.
|
26
|
+
|
27
|
+
Also, the code is fairly hard to grok, as it's mostly monkey patches. Feel free to improve
|
28
|
+
the readability of the code!
|
29
|
+
|
30
|
+
== What it does
|
31
|
+
|
32
|
+
Here's a short list of features (examples are in norwegian)
|
33
|
+
|
34
|
+
Localizing %b, %B, %a and %A in Time#strftime, Date#strftime and DateTime#strftime
|
35
|
+
|
36
|
+
* Time.now.strftime('%b') => okt
|
37
|
+
* Time.now.strftime('%b') => oktober
|
38
|
+
* Time.now.strftime('%a') => søn
|
39
|
+
* Time.now.strftime('%A') => søndag
|
40
|
+
|
41
|
+
Localized inflections. "bil".pluralize => "biler"
|
42
|
+
|
43
|
+
Localizing attribute names in ActiveRecord.
|
44
|
+
|
45
|
+
class Question < ActiveRecord::Base
|
46
|
+
localized_fields :question => "Spørsmål", :answer => "Svar"
|
47
|
+
end
|
48
|
+
|
49
|
+
== TODO
|
50
|
+
|
51
|
+
* Make tests runnable in any language. In other words, don't let tests be hardcoded in Norwegian.
|
52
|
+
|
53
|
+
|
54
|
+
Copyright (c) 2008 August Lilleaas, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
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 translated plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the translated plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Translated'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
data/TODO
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'localized_frontend'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'localized_frontend/core'
|
2
|
+
require 'localized_frontend/inflector_extension'
|
3
|
+
require 'localized_frontend/active_record_extension'
|
4
|
+
require 'localized_frontend/strftime'
|
5
|
+
require 'localized_frontend/to_sentence'
|
6
|
+
require 'localized_frontend/form_labels'
|
7
|
+
require 'localized_frontend/date_helper'
|
8
|
+
|
9
|
+
module LocalizedFrontend
|
10
|
+
# Raised when a localization method is called but the language isn't set. Set it with
|
11
|
+
# <tt>LocalizedFrontend.in</tt>.
|
12
|
+
class LanguageNotSet < StandardError; end
|
13
|
+
|
14
|
+
include Core
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
%w(translatable_column_names error_messages).each do |extension|
|
2
|
+
require File.join(File.dirname(__FILE__), 'active_record_extension', extension)
|
3
|
+
end
|
4
|
+
|
5
|
+
module LocalizedFrontend
|
6
|
+
# Active Record extensions. Refer to child modules for the rock 'n roll.
|
7
|
+
module ActiveRecordExtension
|
8
|
+
def self.included(base)
|
9
|
+
include TranslatableColumnNames
|
10
|
+
include ErrorMessages
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
module ActiveRecordExtension
|
3
|
+
# Hooks into Active Record's validations so that error messages returns a localized version of
|
4
|
+
# the column names, if defined. See TranslatableColumnNames for information on how to define
|
5
|
+
# these localizations.
|
6
|
+
module ErrorMessages
|
7
|
+
def self.included(base)
|
8
|
+
replace_default_error_messages
|
9
|
+
hook_human_attribute_name
|
10
|
+
end
|
11
|
+
|
12
|
+
# Merge the localized definitions into Active Record's definitions.
|
13
|
+
def self.replace_default_error_messages
|
14
|
+
ActiveRecord::Errors.default_error_messages.merge!(LocalizedFrontend['validation_error_messages'].symbolize_keys)
|
15
|
+
end
|
16
|
+
|
17
|
+
# human_attribute_name is what Active Record calls when looking for the attribute name on
|
18
|
+
# error messages. This method injects a localization attempt that fallbacks on the default
|
19
|
+
# column name representation if no localization was found.
|
20
|
+
def self.hook_human_attribute_name
|
21
|
+
ActiveRecord::Base.class_eval do
|
22
|
+
def self.human_attribute_name(attribute)
|
23
|
+
localized_fields[attribute] || attribute.humanize
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
module ActiveRecordExtension
|
3
|
+
# Makes localized attribute names defineable in models. Example:
|
4
|
+
#
|
5
|
+
# class Post < ActiveRecord::Base
|
6
|
+
# localized_fields :title => 'Smuuk smiik smakeke'
|
7
|
+
# end
|
8
|
+
module TranslatableColumnNames
|
9
|
+
def self.included(base)
|
10
|
+
ActiveRecord::Base.class_inheritable_accessor :localized_attributes
|
11
|
+
ActiveRecord::Base.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
# Stores a set of localized attributes in a hash, so that these localized variants can be
|
16
|
+
# used to localize validation error messages.
|
17
|
+
#
|
18
|
+
# class Post < ActiveRecord::Base
|
19
|
+
# localized_fields :title => 'Smuuk smiik smakeke'
|
20
|
+
# validates_presence_of :title
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# p = Post.new
|
24
|
+
# p.valid?
|
25
|
+
# p.errors.full_messages # => ['Smuuk smiik smakeke is required']
|
26
|
+
# # (fyi, the 'is required' part can also be localized)
|
27
|
+
def localized_fields(options = {})
|
28
|
+
self.localized_attributes ||= HashWithIndifferentAccess.new
|
29
|
+
self.localized_attributes.merge!(options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
# The main module that adds class methods to LocalizedFrontend and creates the hooks we need. See
|
3
|
+
# the individual methods for more information.
|
4
|
+
module Core
|
5
|
+
# Creates the LocalizedFrontend.language accessor and stuff.
|
6
|
+
def self.included(base)
|
7
|
+
base.mattr_accessor :language
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# Sets the current language.
|
13
|
+
#
|
14
|
+
# LocalizedFrontend.in :french
|
15
|
+
def in(language)
|
16
|
+
self.language = language
|
17
|
+
first_time_inclusion
|
18
|
+
change_language!
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the current language as a hash, or raises LanguageNotSet if (woha) the language isn't set.
|
22
|
+
def current_language(reload = false)
|
23
|
+
begin
|
24
|
+
@current_language = nil if reload
|
25
|
+
@current_language ||= YAML::load_file(File.join(File.dirname(__FILE__), "..", "translations", "#{language}.yml"))
|
26
|
+
rescue Errno::ENOENT
|
27
|
+
raise LocalizedFrontend::LanguageNotSet
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# A shortcut to current language. These are equivalents:
|
32
|
+
#
|
33
|
+
# LocalizedFrontend.current_language['foos']
|
34
|
+
# LocalizedFrontend['foos']
|
35
|
+
def [](key)
|
36
|
+
current_language[key]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Dirty. Fix, please.
|
42
|
+
def first_time_inclusion
|
43
|
+
return if @included
|
44
|
+
|
45
|
+
LocalizedFrontend.class_eval do
|
46
|
+
include InflectorExtension
|
47
|
+
include ActiveRecordExtension
|
48
|
+
include Strftime
|
49
|
+
include ToSentence
|
50
|
+
include FormLabels
|
51
|
+
end
|
52
|
+
|
53
|
+
@included = true
|
54
|
+
end
|
55
|
+
|
56
|
+
def change_language!
|
57
|
+
# Reload the current language hash.
|
58
|
+
current_language(true)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
module DateHelper
|
3
|
+
def self.included(base)
|
4
|
+
ActionView::Helpers::DateHelper.class_eval do
|
5
|
+
def select_month_with_localization(date, options = {}, html_options = {})
|
6
|
+
val = date ? (date.kind_of?(Fixnum) ? date : date.month) : ''
|
7
|
+
if options[:use_hidden]
|
8
|
+
hidden_html(options[:field_name] || 'month', val, options)
|
9
|
+
else
|
10
|
+
month_options = []
|
11
|
+
month_names = options[:use_month_names] || (options[:use_short_month] ? LocalizedFrontend['month_names']['abbreviated']: LocalizedFrontend['month_names']['full'])
|
12
|
+
month_names.unshift(nil) if month_names.size < 13
|
13
|
+
1.upto(12) do |month_number|
|
14
|
+
month_name = if options[:use_month_numbers]
|
15
|
+
month_number
|
16
|
+
elsif options[:add_month_numbers]
|
17
|
+
month_number.to_s + ' - ' + month_names[month_number]
|
18
|
+
else
|
19
|
+
month_names[month_number]
|
20
|
+
end
|
21
|
+
|
22
|
+
month_options << ((val == month_number) ?
|
23
|
+
content_tag(:option, month_name, :value => month_number, :selected => "selected") :
|
24
|
+
content_tag(:option, month_name, :value => month_number)
|
25
|
+
)
|
26
|
+
month_options << "\n"
|
27
|
+
end
|
28
|
+
select_html(options[:field_name] || 'month', month_options.join, options, html_options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
ActionView::Helpers::DateHelper.alias_method_chain :select_month, :localization
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
# The default form builders label method tries to find a localized definition
|
3
|
+
# of the attribute.
|
4
|
+
#
|
5
|
+
# <%= f.label :title %> (returns 'Localized Version of Title That Is Defined In the Model')
|
6
|
+
module FormLabels
|
7
|
+
def self.included(base)
|
8
|
+
ActionView::Helpers::FormBuilder.class_eval do
|
9
|
+
def label_with_localization(method, text = nil, options = {})
|
10
|
+
if @object
|
11
|
+
text ||= @object.class.localized_attributes ? @object.class.localized_attributes[method] : nil
|
12
|
+
end
|
13
|
+
label_without_localization(method, text, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
ActionView::Helpers::FormBuilder.alias_method_chain :label, :localization
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
# Handles localization of inflections (pluralize, singularize etc.)
|
3
|
+
module InflectorExtension
|
4
|
+
def self.included(base)
|
5
|
+
add_localized_pluralizations_to_string
|
6
|
+
extend_inflector_class
|
7
|
+
initialize_localized_rules
|
8
|
+
set_localized_inflections
|
9
|
+
end
|
10
|
+
|
11
|
+
# Adds the local_puralize and local_singularize methods to String. These methods are
|
12
|
+
# used to get a localized pluralization/singularization.
|
13
|
+
#
|
14
|
+
# LocalizedFrontend.in :norwegian
|
15
|
+
# "car".pluralize # => "cars"
|
16
|
+
# "car".local_pluralize # => "carer"
|
17
|
+
# "bil".local_pluralize # => "biler"
|
18
|
+
def self.add_localized_pluralizations_to_string
|
19
|
+
String.class_eval { include StringInstanceMethods }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Hooks into the inflector instance and adds the methods we need in order to get
|
23
|
+
# localized inflections. The real meat is the in_localized_scope method.
|
24
|
+
def self.extend_inflector_class
|
25
|
+
Inflector.inflections.class.class_eval { attr_accessor :localize }
|
26
|
+
Inflector.inflections.instance_eval { extend InflectorInstanceMethods }
|
27
|
+
end
|
28
|
+
|
29
|
+
# We store the localized inflections in a separate set of instance variables. See
|
30
|
+
# InflectorInstanceMethods
|
31
|
+
def self.initialize_localized_rules
|
32
|
+
Inflector.inflections.instance_eval do
|
33
|
+
@localized_plurals, @localized_singulars, @localized_uncountables = [], [], []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# The real meat that loops through the defined inflections and hooks them into the
|
38
|
+
# monkey patched Inflector.
|
39
|
+
def self.set_localized_inflections
|
40
|
+
Inflector.inflections.plurals
|
41
|
+
Inflector.inflections.in_localized_scope do
|
42
|
+
Inflector.inflections.current_scope('plurals')
|
43
|
+
end
|
44
|
+
Inflector.inflections.in_localized_scope do
|
45
|
+
%w(singular plural irregular).each do |type|
|
46
|
+
next unless LocalizedFrontend['inflections'][type]
|
47
|
+
|
48
|
+
LocalizedFrontend['inflections'][type].each do |rule, replacement|
|
49
|
+
Inflector.inflections.send(type, case_insensitive_regular_expression(rule), replacement)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Inflector.inflections.uncountable(LocalizedFrontend['inflections']['uncountable'])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# That's what the 'true' means. Case insensitive.
|
58
|
+
def self.case_insensitive_regular_expression(rule)
|
59
|
+
Regexp.new(rule, true)
|
60
|
+
end
|
61
|
+
|
62
|
+
# The in_localized_scope method is the only thing you need here. Everything else is obscure
|
63
|
+
# and retarded.
|
64
|
+
#
|
65
|
+
# The methods +plural+, +singular+ and +uncountable+ is a simple re-definition of the original
|
66
|
+
# method in Inflector. The change is simple - use methods instead of instance variables. So, when
|
67
|
+
# the +plural+ method used to do @plurals.insert, we change it so that it does plurals.insert. Then,
|
68
|
+
# these methods (+singulars+, +plurals+ and +uncountables+) are returning the inflections we want in
|
69
|
+
# the language we request. If +Inflector.inflections.localize+ is set to true, we'll get
|
70
|
+
# @localized_plurals. If it's set to false, we get the default @plurals (which the original,
|
71
|
+
# un-overrided methods was originally using).
|
72
|
+
#
|
73
|
+
# +irregulars+ are not re-defined, as that method is calling the +plural+ and +singular+ methods
|
74
|
+
# anyway. Which we have already overrided.
|
75
|
+
module InflectorInstanceMethods
|
76
|
+
# Sets the scope as localized. This is what String#local_pluralize and friends does.
|
77
|
+
#
|
78
|
+
# "car".pluralize # => "cars"
|
79
|
+
# "car".localized_pluralize # => "carer"
|
80
|
+
# Inflector.inflections.in_localized_scope { "car".pluralize } # => "carer"
|
81
|
+
def in_localized_scope(&block)
|
82
|
+
self.localize = true
|
83
|
+
result = yield
|
84
|
+
self.localize = false
|
85
|
+
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
def current_scope(type)
|
90
|
+
prefix = localize ? "localized_" : nil
|
91
|
+
instance_variable_get("@#{prefix}#{type}")
|
92
|
+
end
|
93
|
+
|
94
|
+
def singulars
|
95
|
+
current_scope(:singulars)
|
96
|
+
end
|
97
|
+
|
98
|
+
def plurals
|
99
|
+
current_scope(:plurals)
|
100
|
+
end
|
101
|
+
|
102
|
+
def uncountables
|
103
|
+
current_scope(:uncountables)
|
104
|
+
end
|
105
|
+
|
106
|
+
def plural(rule, replacement)
|
107
|
+
plurals.insert(0, [rule, replacement])
|
108
|
+
end
|
109
|
+
|
110
|
+
def singular(rule, replacement)
|
111
|
+
singulars.insert(0, [rule, replacement])
|
112
|
+
end
|
113
|
+
|
114
|
+
def uncountable(*words)
|
115
|
+
(uncountables << words).flatten!
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
module StringInstanceMethods
|
120
|
+
# A localized version of String#pluralize
|
121
|
+
def local_pluralize
|
122
|
+
Inflector.inflections.in_localized_scope { pluralize }
|
123
|
+
end
|
124
|
+
|
125
|
+
# A localized version of String#singularize
|
126
|
+
def local_singularize
|
127
|
+
Inflector.inflections.in_localized_scope { singularize }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
# Localizes Time#strftime, Date#strftime and DateTime#strftime.
|
3
|
+
module Strftime
|
4
|
+
# Includes the InstanceMethods module into Time, Date and DateTime.
|
5
|
+
def self.included(base)
|
6
|
+
[Time, Date, DateTime].each do |klass|
|
7
|
+
klass.class_eval { include InstanceMethods }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def self.included(base)
|
13
|
+
base.alias_method_chain :strftime, :localization
|
14
|
+
end
|
15
|
+
|
16
|
+
# A rather ugly hack that gsubs the keys we're looking for and replaces it with
|
17
|
+
# localized strings, and then passes the format string to the original strftime
|
18
|
+
# method. "%Y %B" turns into, say, "%Y Juni", and is passed to strftime.
|
19
|
+
#
|
20
|
+
# Time/Date/DateTime#strftime_without_localization can be called in order to
|
21
|
+
# bypass this.
|
22
|
+
def strftime_with_localization(format = '%F')
|
23
|
+
_weekday = self.wday - 1
|
24
|
+
_month = self.month - 1
|
25
|
+
|
26
|
+
format.gsub!(/%a/, LocalizedFrontend['day_names']['abbreviated'][_weekday])
|
27
|
+
format.gsub!(/%A/, LocalizedFrontend['day_names']['full'][_weekday])
|
28
|
+
|
29
|
+
format.gsub!(/%b/, LocalizedFrontend['month_names']['abbreviated'][_month])
|
30
|
+
format.gsub!(/%B/, LocalizedFrontend['month_names']['full'][_month])
|
31
|
+
|
32
|
+
strftime_without_localization(format)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module LocalizedFrontend
|
2
|
+
# Localizes the connector and the skip_last_comma settings for Array#to_sentence
|
3
|
+
module ToSentence
|
4
|
+
def self.included(base)
|
5
|
+
Array.class_eval { include InstanceMethods }
|
6
|
+
end
|
7
|
+
|
8
|
+
module InstanceMethods
|
9
|
+
def self.included(base)
|
10
|
+
base.alias_method_chain :to_sentence, :localization
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_sentence_with_localization(options = {})
|
14
|
+
options.reverse_merge! :connector => LocalizedFrontend['to_sentence']['connector'], :skip_last_comma => LocalizedFrontend['to_sentence']['skip_last_comma']
|
15
|
+
to_sentence_without_localization(options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
day_names:
|
2
|
+
abbreviated:
|
3
|
+
- Dim
|
4
|
+
- Lun
|
5
|
+
- Mar
|
6
|
+
- Mer
|
7
|
+
- Jeu
|
8
|
+
- Ven
|
9
|
+
- Sam
|
10
|
+
full:
|
11
|
+
- Dimanche
|
12
|
+
- Lundi
|
13
|
+
- Mardi
|
14
|
+
- Mercredi
|
15
|
+
- Jeudi
|
16
|
+
- Vendredi
|
17
|
+
- Samedi
|
18
|
+
|
19
|
+
month_names:
|
20
|
+
abbreviated:
|
21
|
+
- jan
|
22
|
+
- fév
|
23
|
+
- mar
|
24
|
+
- avr
|
25
|
+
- mai
|
26
|
+
- jun
|
27
|
+
- jul
|
28
|
+
- aou
|
29
|
+
- sep
|
30
|
+
- oct
|
31
|
+
- nov
|
32
|
+
- dec
|
33
|
+
full:
|
34
|
+
- janvier
|
35
|
+
- février
|
36
|
+
- mars
|
37
|
+
- avril
|
38
|
+
- mai
|
39
|
+
- juin
|
40
|
+
- juillet
|
41
|
+
- août
|
42
|
+
- septembre
|
43
|
+
- octobre
|
44
|
+
- novembre
|
45
|
+
- décembre
|
46
|
+
|
47
|
+
inflections:
|
48
|
+
plural:
|
49
|
+
'$': 's'
|
50
|
+
singular:
|
51
|
+
's$': ''
|
52
|
+
uncountable:
|
53
|
+
|
54
|
+
to_sentence:
|
55
|
+
connector: et
|
56
|
+
skip_last_comma: true
|
57
|
+
|
58
|
+
validation_error_messages:
|
59
|
+
inclusion: n'est pas dans la liste
|
@@ -0,0 +1,79 @@
|
|
1
|
+
day_names:
|
2
|
+
abbreviated:
|
3
|
+
- Søn
|
4
|
+
- Man
|
5
|
+
- Tir
|
6
|
+
- Ons
|
7
|
+
- Tor
|
8
|
+
- Fre
|
9
|
+
- Lør
|
10
|
+
full:
|
11
|
+
- Søndag
|
12
|
+
- Mandag
|
13
|
+
- Tirsdag
|
14
|
+
- Onsdag
|
15
|
+
- Torsdag
|
16
|
+
- Fredag
|
17
|
+
- Lørdag
|
18
|
+
|
19
|
+
month_names:
|
20
|
+
abbreviated:
|
21
|
+
- Jan
|
22
|
+
- Feb
|
23
|
+
- Mar
|
24
|
+
- Apr
|
25
|
+
- Mai
|
26
|
+
- Jun
|
27
|
+
- Jul
|
28
|
+
- Aug
|
29
|
+
- Sep
|
30
|
+
- Okt
|
31
|
+
- Nov
|
32
|
+
- Des
|
33
|
+
full:
|
34
|
+
- Januar
|
35
|
+
- Februar
|
36
|
+
- Mars
|
37
|
+
- April
|
38
|
+
- Mai
|
39
|
+
- Juni
|
40
|
+
- Juli
|
41
|
+
- August
|
42
|
+
- September
|
43
|
+
- Oktober
|
44
|
+
- November
|
45
|
+
- Desember
|
46
|
+
|
47
|
+
inflections:
|
48
|
+
plural:
|
49
|
+
'$': 'er'
|
50
|
+
singular:
|
51
|
+
'er$': ''
|
52
|
+
uncountable:
|
53
|
+
- hus
|
54
|
+
- tak
|
55
|
+
|
56
|
+
to_sentence:
|
57
|
+
connector: og
|
58
|
+
skip_last_comma: true
|
59
|
+
|
60
|
+
validation_error_messages:
|
61
|
+
inclusion: er ikke i lista
|
62
|
+
exclusion: er reservert
|
63
|
+
invalid: er ugyldig
|
64
|
+
confirmation: stemmer ikke overens
|
65
|
+
accepted: må aksepteres
|
66
|
+
empty: må fylles ut
|
67
|
+
blank: må fylles ut
|
68
|
+
too_long: er for lang (maks %d tegn)
|
69
|
+
too_short: er for kort (minst %d tegn)
|
70
|
+
wrong_length: har feil lengde (skal være %d tegn)
|
71
|
+
taken: er allerede tatt
|
72
|
+
not_a_number: må være et tall
|
73
|
+
greater_than: må være høyere enn %d
|
74
|
+
greater_than_or_equal_to: må være høyere enn eller lik %d
|
75
|
+
equal_to: må være %d
|
76
|
+
less_than: må være mindre enn %d
|
77
|
+
less_than_or_equal_to: må være mindre enn eller lik %d
|
78
|
+
odd: må være et oddetall
|
79
|
+
even: må være et partall
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class ActiveRecordExtensionTest < Test::Unit::TestCase
|
4
|
+
def test_table_name_pluralization
|
5
|
+
assert_equal 'posts', Post.table_name
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_setting_localized_fields
|
9
|
+
assert_equal 'Tittel', Post.localized_fields[:title]
|
10
|
+
assert_equal 'Tittel', Post.localized_fields['title']
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_overriding_default_error_messages
|
14
|
+
assert_equal ActiveRecord::Errors.default_error_messages[:too_short], LocalizedFrontend['validation_error_messages']['too_short']
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_actual_error_message_output
|
18
|
+
message = ActiveRecord::Errors.default_error_messages[:blank]
|
19
|
+
p = Post.new
|
20
|
+
p.valid?
|
21
|
+
|
22
|
+
assert_equal message, p.errors.on(:title)
|
23
|
+
assert p.errors.full_messages.include?("Tittel #{message}")
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
2
|
+
|
3
|
+
class EverythingElseTest < Test::Unit::TestCase
|
4
|
+
def test_to_sentence
|
5
|
+
assert_equal 'Per, Pål og Espen Askeladd', ['Per', 'Pål', 'Espen Askeladd'].to_sentence
|
6
|
+
assert_equal 'Du, jeg, eller meg', ['Du', 'jeg', 'meg'].to_sentence(:connector => 'eller', :skip_last_comma => false)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_to_sentence_again
|
10
|
+
assert_equal 'Per, Pål og Espen Askeladd', ['Per', 'Pål', 'Espen Askeladd'].to_sentence
|
11
|
+
assert_equal 'Du, jeg, eller meg', ['Du', 'jeg', 'meg'].to_sentence(:connector => 'eller', :skip_last_comma => false)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
2
|
+
|
3
|
+
class TranslatedTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
LocalizedFrontend.in :norwegian
|
6
|
+
|
7
|
+
@controller = DummyController.new
|
8
|
+
@request = @controller.request
|
9
|
+
|
10
|
+
@view = ActionView::Base.new
|
11
|
+
@view.assigns['controller'] = @controller
|
12
|
+
@view.assigns['_request'] = @request
|
13
|
+
@view.assigns['_params'] = @request.params
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_localized_label
|
17
|
+
render '<% form_for(Post.new) do |f| %><%= f.label :excerpt %><%= f.label :cheese %><% end %>'
|
18
|
+
assert @output =~ Regexp.new('<label for="post_excerpt">Ingress</label>')
|
19
|
+
#raise @output.inspect
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_original_label
|
23
|
+
render '<% form_for(Post.new) do |f| %><%= f.label :excerpt %><%= f.label :cheese %><% end %>'
|
24
|
+
assert @output =~ Regexp.new('<label for="post_cheese">Cheese</label>')
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_unlocalized_model
|
28
|
+
render '<% form_for(UnlocalizedFoo.new) do |f| %><%= f.label :foo %><% end %>'
|
29
|
+
assert @output =~ Regexp.new('<label for="unlocalized_foo_foo">Foo</label>')
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_symbol
|
33
|
+
render '<% form_for(:foo) do |f| %><%= f.label :bar %><% end %>'
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def render(template)
|
39
|
+
@output = @view.render_template(ActionView::InlineTemplate.new(@view, template))
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
2
|
+
|
3
|
+
class InflectorTest < Test::Unit::TestCase
|
4
|
+
def test_original_english_inflections
|
5
|
+
assert_equal 'posts', 'post'.pluralize
|
6
|
+
assert_equal 'post', 'posts'.singularize
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_singularize
|
10
|
+
{"biler" => "bil", "katter" => "katt"}.each do |plural, singular|
|
11
|
+
assert_equal singular, plural.local_singularize
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_pluralize
|
16
|
+
{"bil" => "biler", "katt" => "katter"}.each do |singular, plural|
|
17
|
+
assert_equal plural, singular.local_pluralize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_irregular
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_pluralize_uncountable
|
26
|
+
%w(hus tak).each do |uncountable|
|
27
|
+
assert_equal uncountable, uncountable.local_pluralize
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_adding_inflections
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_case_insensivity
|
36
|
+
assert_equal "BIL", "BILER".local_singularize
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
2
|
+
|
3
|
+
class StrftimeTest < Test::Unit::TestCase
|
4
|
+
def test_localized_strftime_on_time
|
5
|
+
assert_localized_strftime Time.now
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_localized_strftime_on_date
|
9
|
+
assert_localized_strftime Date.today
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_localized_stfrtime_on_datetime
|
13
|
+
assert_localized_strftime DateTime.class_eval { today }
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_localized_stfrtime_on_april_fools_day
|
17
|
+
assert_equal Time.utc(2008,4,1).strftime("%B"), "April"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def assert_localized_strftime(time)
|
23
|
+
assert_equal LocalizedFrontend['month_names']['abbreviated'][time.month-1], time.strftime("%b")
|
24
|
+
assert_equal LocalizedFrontend['month_names']['full'][time.month-1], time.strftime("%B")
|
25
|
+
|
26
|
+
assert_equal LocalizedFrontend['day_names']['abbreviated'][time.wday-1], time.strftime("%a")
|
27
|
+
assert_equal LocalizedFrontend['day_names']['full'][time.wday-1], time.strftime("%A")
|
28
|
+
end
|
29
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_record'
|
5
|
+
require 'action_controller'
|
6
|
+
require 'action_controller/test_process'
|
7
|
+
|
8
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "localized_frontend")
|
9
|
+
LocalizedFrontend.in :norwegian
|
10
|
+
|
11
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
12
|
+
ActiveRecord::Schema.define(:version => 1) do
|
13
|
+
create_table :posts do |t|
|
14
|
+
t.string :title, :cheese
|
15
|
+
t.text :excerpt, :body
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table(:unlocalized_foos) {|t| t.string :foo }
|
19
|
+
end
|
20
|
+
|
21
|
+
class Post < ActiveRecord::Base
|
22
|
+
localized_fields :title => "Tittel", :excerpt => "Ingress", :body => "Brødtekst"
|
23
|
+
|
24
|
+
validates_presence_of :title, :excerpt, :body
|
25
|
+
end
|
26
|
+
|
27
|
+
class UnlocalizedFoo < ActiveRecord::Base
|
28
|
+
end
|
29
|
+
|
30
|
+
# The following is ninjaed from will_paginate.
|
31
|
+
ActionController::Routing::Routes.draw do |map|
|
32
|
+
map.resources :posts, :unlocalized_foos
|
33
|
+
map.connect ':controller/:action/:id'
|
34
|
+
end
|
35
|
+
ActionController::Base.perform_caching = false
|
36
|
+
|
37
|
+
class ActionView::Base
|
38
|
+
def protect_against_forgery?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
class DummyRequest
|
43
|
+
attr_accessor :symbolized_path_parameters
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
@get = true
|
47
|
+
@params = {}
|
48
|
+
@symbolized_path_parameters = { :controller => 'foo', :action => 'bar' }
|
49
|
+
end
|
50
|
+
|
51
|
+
def relative_url_root
|
52
|
+
''
|
53
|
+
end
|
54
|
+
|
55
|
+
def params(more = nil)
|
56
|
+
@params.update(more) if more
|
57
|
+
@params
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class DummyController
|
62
|
+
attr_reader :request
|
63
|
+
attr_accessor :controller_name
|
64
|
+
|
65
|
+
def initialize
|
66
|
+
@request = DummyRequest.new
|
67
|
+
@url = ActionController::UrlRewriter.new(@request, @request.params)
|
68
|
+
end
|
69
|
+
|
70
|
+
def params
|
71
|
+
@request.params
|
72
|
+
end
|
73
|
+
|
74
|
+
def url_for(params)
|
75
|
+
@url.rewrite(params)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
2
|
+
|
3
|
+
class TranslatedTest < Test::Unit::TestCase
|
4
|
+
def test_setting_language
|
5
|
+
LocalizedFrontend.in :norwegian
|
6
|
+
assert_equal :norwegian, LocalizedFrontend.language
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_fails_with_no_language_set
|
10
|
+
LocalizedFrontend.language = nil
|
11
|
+
assert_raises(LocalizedFrontend::LanguageNotSet) { LocalizedFrontend.current_language(true) }
|
12
|
+
assert_raises(LocalizedFrontend::LanguageNotSet) { LocalizedFrontend['month_names'] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_current_language
|
16
|
+
assert LocalizedFrontend.current_language.is_a?(Hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_accessing_with_brackets
|
20
|
+
assert_equal LocalizedFrontend['month_names'], LocalizedFrontend.current_language['month_names']
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_changing_language
|
24
|
+
norwegian = LocalizedFrontend.current_language.dup
|
25
|
+
LocalizedFrontend.in :french
|
26
|
+
assert_not_equal norwegian, LocalizedFrontend.current_language
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jerome-localized-frontend
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- August Lilleaas
|
8
|
+
- "J\xC3\xA9r\xC3\xB4me Lipowicz"
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2008-10-10 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description:
|
18
|
+
email: augustlilleaas@gmail.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- init.rb
|
27
|
+
- MIT-LICENSE
|
28
|
+
- Rakefile
|
29
|
+
- README
|
30
|
+
- TODO
|
31
|
+
- lib/localized_frontend.rb
|
32
|
+
- lib/localized_frontend/active_record_extension.rb
|
33
|
+
- lib/localized_frontend/core.rb
|
34
|
+
- lib/localized_frontend/date_helper.rb
|
35
|
+
- lib/localized_frontend/form_labels.rb
|
36
|
+
- lib/localized_frontend/inflector_extension.rb
|
37
|
+
- lib/localized_frontend/strftime.rb
|
38
|
+
- lib/localized_frontend/to_sentence.rb
|
39
|
+
- lib/translations/french.yml
|
40
|
+
- lib/translations/norwegian.yml
|
41
|
+
- lib/localized_frontend/active_record_extension/error_messages.rb
|
42
|
+
- lib/localized_frontend/active_record_extension/translatable_column_names.rb
|
43
|
+
has_rdoc: false
|
44
|
+
homepage: http://github.com/leethal/localized-frontend/
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.2.0
|
66
|
+
signing_key:
|
67
|
+
specification_version: 2
|
68
|
+
summary: Localize the frontend of single language Rails apps.
|
69
|
+
test_files:
|
70
|
+
- test/active_record_test.rb
|
71
|
+
- test/everything_else_test.rb
|
72
|
+
- test/form_labels_test.rb
|
73
|
+
- test/inflector_test.rb
|
74
|
+
- test/strftime_test.rb
|
75
|
+
- test/test_helper.rb
|
76
|
+
- test/translated_test.rb
|