hallelujah-gettext_activerecord 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/COPYING +55 -0
  2. data/ChangeLog +21 -0
  3. data/README.rdoc +175 -0
  4. data/Rakefile +120 -0
  5. data/lib/gettext_activerecord.rb +24 -0
  6. data/lib/gettext_activerecord/base.rb +73 -0
  7. data/lib/gettext_activerecord/i18n.rb +27 -0
  8. data/lib/gettext_activerecord/migration.rb +16 -0
  9. data/lib/gettext_activerecord/parser.rb +215 -0
  10. data/lib/gettext_activerecord/schema_definitions.rb +28 -0
  11. data/lib/gettext_activerecord/tools.rb +30 -0
  12. data/lib/gettext_activerecord/validations.rb +191 -0
  13. data/lib/gettext_activerecord/version.rb +12 -0
  14. data/po/bg/gettext_activerecord.po +115 -0
  15. data/po/bs/gettext_activerecord.po +125 -0
  16. data/po/ca/gettext_activerecord.po +116 -0
  17. data/po/cs/gettext_activerecord.po +124 -0
  18. data/po/de/gettext_activerecord.po +117 -0
  19. data/po/el/gettext_activerecord.po +115 -0
  20. data/po/eo/gettext_activerecord.po +116 -0
  21. data/po/es/gettext_activerecord.po +116 -0
  22. data/po/et/gettext_activerecord.po +116 -0
  23. data/po/fr/gettext_activerecord.po +118 -0
  24. data/po/gettext_activerecord.pot +113 -0
  25. data/po/hr/gettext_activerecord.po +125 -0
  26. data/po/hu/gettext_activerecord.po +116 -0
  27. data/po/it/gettext_activerecord.po +122 -0
  28. data/po/ja/gettext_activerecord.po +116 -0
  29. data/po/ko/gettext_activerecord.po +123 -0
  30. data/po/lv/gettext_activerecord.po +116 -0
  31. data/po/nb/gettext_activerecord.po +117 -0
  32. data/po/nl/gettext_activerecord.po +117 -0
  33. data/po/pt_BR/gettext_activerecord.po +117 -0
  34. data/po/ru/gettext_activerecord.po +117 -0
  35. data/po/sr/gettext_activerecord.po +117 -0
  36. data/po/ua/gettext_activerecord.po +120 -0
  37. data/po/vi/gettext_activerecord.po +116 -0
  38. data/po/zh/gettext_activerecord.po +119 -0
  39. data/po/zh_TW/gettext_activerecord.po +119 -0
  40. data/sample/README.rdoc +9 -0
  41. data/sample/Rakefile +32 -0
  42. data/sample/book.rb +3 -0
  43. data/sample/config/database.yml +3 -0
  44. data/sample/db/schema.rb +5 -0
  45. data/sample/po/ja/sample_ar.po +29 -0
  46. data/sample/po/sample_ar.pot +29 -0
  47. data/sample/sample.rb +22 -0
  48. metadata +119 -0
@@ -0,0 +1,27 @@
1
+ =begin
2
+ lib/gettext_activerecord/i18n.rb - GetText for ActiveRecord's I18n.
3
+
4
+ Copyright (C) 2009 Masao Mutoh
5
+
6
+ You may redistribute it and/or modify it under the same
7
+ license terms as Ruby.
8
+ =end
9
+
10
+ module I18n #:nodoc:
11
+ class << self
12
+ include GetText
13
+ # gettext_activerecord doesn't define backend. So it can be used with another backend.
14
+ def translate_with_gettext_activerecord(key, options = {}) #:nodoc:
15
+ if options[:scope] == [:activerecord, :errors]
16
+ options[:attribute] = key.to_s.split(".")[3]
17
+ options # This value will be used in ActiveRecord::Base::Errors.localize_error_messages
18
+ else
19
+ translate_without_gettext_activerecord(key, options)
20
+ end
21
+ end
22
+ alias_method_chain :translate, :gettext_activerecord #:nodoc:
23
+ alias_method :t_with_gettext_activerecord, :translate_with_gettext_activerecord #:nodoc:
24
+ alias_method_chain :t, :gettext_activerecord #:nodoc:
25
+
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ =begin
2
+ lib/gettext_activerecord/migration.rb - GetText for ActiveRecord::Migration
3
+
4
+ Copyright (C) 2009 Masao Mutoh
5
+
6
+ You may redistribute it and/or modify it under the same
7
+ license terms as Ruby.
8
+
9
+ =end
10
+
11
+ module ActiveRecord #:nodoc:
12
+ class Migration
13
+ extend GetText
14
+ include GetText
15
+ end
16
+ end
@@ -0,0 +1,215 @@
1
+ =begin
2
+ lib/gettext_activerecord/parser.rb - parser for ActiveRecord
3
+
4
+ Copyright (C) 2005-2009 Masao Mutoh
5
+
6
+ You may redistribute it and/or modify it under the same
7
+ license terms as Ruby.
8
+
9
+ $Id$
10
+ =end
11
+
12
+ require 'gettext'
13
+ require 'gettext/tools/rgettext'
14
+ require 'gettext/parser/ruby'
15
+
16
+ include GetText
17
+
18
+ ActiveRecord::Base.instance_eval do
19
+ alias inherited_without_log inherited
20
+
21
+ def inherited(subclass)
22
+ puts "registering an ActiveRecord model for later processing: #{subclass}" if $DEBUG
23
+ active_record_classes_list << "#{subclass}" unless subclass.name.empty?
24
+ inherited_without_log(subclass)
25
+ end
26
+
27
+ def active_record_classes_list
28
+ $active_record_classes_list ||= []
29
+ end
30
+
31
+ def reset_active_record_classes_list
32
+ $active_record_classes_list = []
33
+ end
34
+ end
35
+
36
+ module GetText
37
+ module ActiveRecordParser
38
+ extend GetText
39
+ include GetText
40
+ bindtextdomain "gettext_activerecord"
41
+
42
+ @config = {
43
+ :db_yml => "config/database.yml",
44
+ :db_mode => "development",
45
+ :activerecord_classes => ["ActiveRecord::Base"],
46
+ :untranslate_classes => ["ActiveRecord::Base", "ActiveRecord::SessionStore::Session"],
47
+ :untranslate_columns => ["id"],
48
+ :untranslate_table_name => false,
49
+ :use_classname => true,
50
+ }
51
+
52
+ @ar_re = nil
53
+
54
+ module_function
55
+ def require_rails(file) # :nodoc:
56
+ begin
57
+ require file
58
+ rescue MissingSourceFile
59
+ $stderr.puts _("'%{file}' is not found.") % {:file => file}
60
+ end
61
+ end
62
+
63
+ # Sets some preferences to parse ActiveRecord files.
64
+ #
65
+ # * config: a Hash of the config. It can takes some values below:
66
+ # * :use_classname - If true, the msgids of ActiveRecord become "ClassName|FieldName" (e.g. "Article|Title"). Otherwise the ClassName is not used (e.g. "Title"). Default is true.
67
+ # * :db_yml - the path of database.yml. Default is "config/database.yml".
68
+ # * :db_mode - the mode of the database. Default is "development"
69
+ # * :activerecord_classes - an Array of the superclass of the models. The classes should be String value. Default is ["ActiveRecord::Base"]
70
+ # * :untranslate_classes - an Array of the modules/class names which is ignored as the msgid.
71
+ # * :untranslate_columns - an Array of the column names which is ignored as the msgid.
72
+ # * :untranslate_table_name - a Boolean that avoids table name to be translated if it is true ... Generally, we don't have to translate table_name, do we? Maybe it is not true..... but it is a test
73
+ # * :adapter - the options for ActiveRecord::Base.establish_connection. If this value is set, :db_yml option is ignored.
74
+ # * :host - ditto
75
+ # * :username - ditto
76
+ # * :password - ditto
77
+ # * :database - ditto
78
+ # * :socket - ditto
79
+ # * :encoding - ditto
80
+ #
81
+ # "ClassName|FieldName" uses GetText.sgettext. So you don't need to translate the left-side of "|".
82
+ # See <Documents for Translators for more details(http://www.yotabanana.com/hiki/ruby-gettext-translate.html)>.
83
+ def init(config)
84
+ puts "\nconfig: #{config.inspect}\n\n" if $DEBUG
85
+ if config
86
+ config.each{|k, v|
87
+ @config[k] = v
88
+ }
89
+ end
90
+ @ar_re = /class.*(#{@config[:activerecord_classes].join("|")})/
91
+ end
92
+
93
+ def translatable_class?(klass)
94
+ if klass.is_a?(Class) && klass < ActiveRecord::Base
95
+ if klass.untranslate_all? || klass.abstract_class? || @config[:untranslate_classes].include?(klass.name)
96
+ false
97
+ else
98
+ true
99
+ end
100
+ else
101
+ true
102
+ end
103
+ end
104
+
105
+ def translatable_column?(klass, columnname)
106
+ ! (klass.untranslate?(columnname) || @config[:untranslate_columns].include?(columnname))
107
+ end
108
+
109
+ def parse(file, targets = []) # :nodoc:
110
+ puts "parse file #{file}" if $DEBUG
111
+
112
+ GetText.locale = "en"
113
+ old_constants = Object.constants
114
+ begin
115
+ eval(open(file).read, TOPLEVEL_BINDING)
116
+ rescue
117
+ $stderr.puts _("Ignored '%{file}'. Solve dependencies first.") % {:file => file}
118
+ $stderr.puts $!
119
+ end
120
+ #loaded_constants = Object.constants - old_constants
121
+ loaded_constants = ActiveRecord::Base.active_record_classes_list
122
+ ActiveRecord::Base.reset_active_record_classes_list
123
+ loaded_constants.each do |classname|
124
+ klass = eval(classname, TOPLEVEL_BINDING)
125
+ if translatable_class?(klass)
126
+ puts "processing class #{klass.name}" if $DEBUG
127
+ add_target(targets, file, ActiveSupport::Inflector.singularize(klass.table_name.gsub(/_/, " "))) unless @config[:untranslate_table_name]
128
+ unless klass.class_name == classname
129
+ add_target(targets, file, ActiveSupport::Inflector.singularize(klass.to_s_with_gettext.gsub(/_/, " ").downcase))
130
+ end
131
+ begin
132
+ klass.columns.each do |column|
133
+ if translatable_column?(klass, column.name)
134
+ if @config[:use_classname]
135
+ msgid = klass.to_s_with_gettext + "|" + klass.human_attribute_name(column.name)
136
+ else
137
+ msgid = klass.human_attribute_name(column.name)
138
+ end
139
+ add_target(targets, file, msgid)
140
+ end
141
+ end
142
+ rescue
143
+ $stderr.puts _("No database is available.")
144
+ $stderr.puts $!
145
+ end
146
+ end
147
+ end
148
+ if RubyParser.target?(file)
149
+ targets = RubyParser.parse(file, targets)
150
+ end
151
+ targets.uniq!
152
+ targets
153
+ end
154
+
155
+ def add_target(targets, file, msgid) # :nodoc:
156
+ file_lineno = "#{file}:-"
157
+ key_existed = targets.assoc(msgid)
158
+ if key_existed
159
+ unless targets[targets.index(key_existed)].include?(file_lineno)
160
+ targets[targets.index(key_existed)] = key_existed << file_lineno
161
+ end
162
+ else
163
+ targets << [msgid, "#{file}:-"]
164
+ end
165
+ targets
166
+ end
167
+
168
+ def target?(file) # :nodoc:
169
+ init(nil) unless @ar_re
170
+ data = IO.readlines(file)
171
+ data.each do |v|
172
+ if @ar_re =~ v
173
+ unless ActiveRecord::Base.connected?
174
+ begin
175
+ require 'rubygems'
176
+ rescue LoadError
177
+ $stderr.puts _("rubygems are not found.") if $DEBUG
178
+ end
179
+ begin
180
+ ENV["RAILS_ENV"] = @config[:db_mode]
181
+ require 'config/boot.rb'
182
+ require 'config/environment.rb'
183
+ require_rails 'activesupport'
184
+ require_rails 'gettext_activerecord'
185
+ rescue LoadError
186
+ require_rails 'rubygems'
187
+ gem 'activerecord'
188
+ require_rails 'activesupport'
189
+ require_rails 'active_record'
190
+ require_rails 'gettext_activerecord'
191
+ end
192
+ begin
193
+ yaml = YAML.load(IO.read(@config[:db_yml]))
194
+ if yaml[@config[:db_mode]]
195
+ ActiveRecord::Base.establish_connection(yaml[@config[:db_mode]])
196
+ else
197
+ ActiveRecord::Base.establish_connection(yaml)
198
+ end
199
+ rescue
200
+ if @config[:adapter]
201
+ ActiveRecord::Base.establish_connection(@config)
202
+ else
203
+ return false
204
+ end
205
+ end
206
+ end
207
+ return true
208
+ end
209
+ end
210
+ false
211
+ end
212
+ end
213
+
214
+ RGetText.add_parser(GetText::ActiveRecordParser)
215
+ end
@@ -0,0 +1,28 @@
1
+ =begin
2
+ lib/gettext_activerecord/schema_definitions.rb - GetText for ActiveRecord::ConnectionAdapters::Column
3
+
4
+ Copyright (C) 2009 Masao Mutoh
5
+
6
+ You may redistribute it and/or modify it under the same
7
+ license terms as Ruby.
8
+
9
+ =end
10
+
11
+ module ActiveRecord #:nodoc:
12
+ module ConnectionAdapters #:nodoc:
13
+ # An abstract definition of a column in a table.
14
+ class Column
15
+ attr_accessor :table_class
16
+ alias :human_name_witout_localized :human_name
17
+
18
+ def human_name_with_gettext_activerecord
19
+ if table_class
20
+ table_class.human_attribute_name(@name)
21
+ else
22
+ @name.humanize
23
+ end
24
+ end
25
+ alias_method_chain :human_name, :gettext_activerecord
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ =begin
2
+ tools.rb - Utility functions
3
+
4
+ Copyright (C) 2009 Masao Mutoh
5
+
6
+ You may redistribute it and/or modify it under the same
7
+ license terms as Ruby.
8
+ =end
9
+
10
+ require 'gettext/tools'
11
+ require 'gettext_activerecord'
12
+ require 'gettext_activerecord/parser'
13
+
14
+ module GetText
15
+ extend self
16
+
17
+ alias :create_mofiles_org :create_mofiles #:nodoc:
18
+ alias :update_pofiles_org :update_pofiles #:nodoc:
19
+
20
+ # update_pofiles for ActiveRecord.
21
+ # In this method, GetText::ActiveRecordParser.init is called
22
+ # with "options".
23
+ # (e.g.)
24
+ # GetText.update_po_files("foo", Dir.glob("lib/**/*"), "1.0.0", :untranslate_classes = ["UntranslateClass"]
25
+ def update_pofiles(textdomain, files, app_version, options = {})
26
+ GetText::ActiveRecordParser.init(options)
27
+ GetText.update_pofiles_org(textdomain, files, app_version, options)
28
+ end
29
+
30
+ end
@@ -0,0 +1,191 @@
1
+ =begin
2
+ lib/gettext_activerecord/validations.rb - GetText for ActiveRecord
3
+
4
+ Copyright (C) 2006-2009 Masao Mutoh
5
+
6
+ You may redistribute it and/or modify it under the same
7
+ license terms as Ruby.
8
+
9
+ $Id$
10
+ =end
11
+
12
+ module ActiveRecord #:nodoc:
13
+ class RecordInvalid < ActiveRecordError #:nodoc:
14
+ attr_reader :record
15
+ include GetText
16
+ bindtextdomain "gettext_activerecord"
17
+
18
+ def initialize(record)
19
+ @record = record
20
+ super(_("Validation failed: %{error_messages}") %
21
+ {:error_messages => @record.errors.full_messages.join(", ")})
22
+ end
23
+ end
24
+
25
+ module Validations # :nodoc:
26
+ class << self
27
+ def real_included(base)
28
+ base.extend ClassMethods
29
+ base.class_eval{
30
+ include GetText
31
+ def gettext(str) #:nodoc:
32
+ _(str)
33
+ end
34
+ class << self
35
+ def human_attribute_name_with_gettext_activerecord(attribute_key_name) #:nodoc:
36
+ s_("#{self.to_s_with_gettext}|#{attribute_key_name.humanize}")
37
+ end
38
+ alias_method_chain :human_attribute_name, :gettext_activerecord
39
+
40
+ def human_attribute_table_name_for_error(table_name) #:nodoc:
41
+ _(table_name.gsub(/_/, " "))
42
+ end
43
+ end
44
+ }
45
+ end
46
+ end
47
+
48
+ if respond_to? :included
49
+ class << self
50
+ def included_with_gettext_activerecord(base) # :nodoc:
51
+ unless base <= ActiveRecord::Base
52
+ included_without_gettext_activerecord(base)
53
+ end
54
+ real_included(base)
55
+ end
56
+ alias_method_chain :included, :gettext_activerecord
57
+ end
58
+ else
59
+ class << self
60
+ # Since rails-1.2.0.
61
+ def append_features_with_gettext_activerecord(base) # :nodoc:
62
+ unless base <= ActiveRecord::Base
63
+ append_features_without_gettext_activerecord(base)
64
+ end
65
+ real_included(base)
66
+ end
67
+ alias_method_chain :append_features, :gettext_activerecord
68
+ end
69
+ end
70
+ end
71
+
72
+ # activerecord-1.14.3/lib/active_record/validations.rb
73
+ class Errors #:nodoc:
74
+ include GetText
75
+
76
+ textdomain "gettext_activerecord"
77
+
78
+ class << self
79
+ include GetText
80
+
81
+ def default_error_messages_with_gettext_activerecord
82
+ @@default_error_messages || {}
83
+ end
84
+ alias_method_chain :default_error_messages, :gettext_activerecord
85
+
86
+ # To use other backends, gettext_activerecord doesn't use backend architecture.
87
+ # You can use GetText with other backends.
88
+ @@default_error_messages = {
89
+ :inclusion => N_("%{attribute} is not included in the list"),
90
+ :exclusion => N_("%{attribute} is reserved"),
91
+ :invalid => N_("%{attribute} is invalid"),
92
+ :confirmation => N_("%{attribute} doesn't match confirmation"),
93
+ :accepted => N_("%{attribute} must be accepted"),
94
+ :empty => N_("%{attribute} can't be empty"),
95
+ :blank => N_("%{attribute} can't be blank"),
96
+ :too_long => N_("%{attribute} is too long (maximum is %{count} characters)"),
97
+ :too_short => N_("%{attribute} is too short (minimum is %{count} characters)"),
98
+ :wrong_length => N_("%{attribute} is the wrong length (should be %{count} characters)"),
99
+ :taken => N_("%{attribute} has already been taken"),
100
+ :not_a_number => N_("%{attribute} is not a number"),
101
+ :greater_than => N_("%{attribute} must be greater than %{count}"),
102
+ :greater_than_or_equal_to => N_("%{attribute} must be greater than or equal to %{count}"),
103
+ :equal_to => N_("%{attribute} must be equal to %{count}"),
104
+ :less_than => N_("%{attribute} must be less than %{count}"),
105
+ :less_than_or_equal_to => N_("%{attribute} must be less than or equal to %{count}"),
106
+ :odd => N_("%{attribute} must be odd"),
107
+ :even => N_("%{attribute} must be even")
108
+ }
109
+ end
110
+
111
+ def each_with_gettext_activerecord #:nodoc:
112
+ @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, localize_error_message(attr, msg, false) } }
113
+ end
114
+ alias_method_chain :each, :gettext_activerecord
115
+
116
+ # Returns error messages.
117
+ # * Returns nil, if no errors are associated with the specified attribute.
118
+ # * Returns the error message, if one error is associated with the specified attribute.
119
+ # * Returns an array of error messages, if more than one error is associated with the specified attribute.
120
+ # And for GetText,
121
+ # * If the error messages include %{fn}, it returns formatted text such as "foo %{fn}" => "foo Field"
122
+ # * else, the error messages are prepended the field name such as "foo" => "foo" (Same as default behavior).
123
+ # Note that this behaviour is different from full_messages.
124
+ def on_with_gettext_activerecord(attribute)
125
+ # e.g.) foo field: "%{fn} foo" => "Foo foo", "foo" => "foo".
126
+ errors = localize_error_messages(false)[attribute.to_s]
127
+ return nil if errors.nil?
128
+ errors.size == 1 ? errors.first : errors
129
+ end
130
+ alias_method_chain :on, :gettext_activerecord
131
+ alias :[] :on
132
+
133
+ # Returns all the full error messages in an array.
134
+ # * If the error messages include %{fn}, it returns formatted text such as "foo %{fn}" => "foo Field"
135
+ # * else, the error messages are prepended the field name such as "foo" => "Field foo" (Same as default behavior).
136
+ # As L10n, first one is recommanded because the order of subject,verb and others are not same in languages.
137
+ def full_messages_with_gettext_activerecord
138
+ full_messages = []
139
+ errors = localize_error_messages
140
+ errors.each_key do |attr|
141
+ errors[attr].each do |msg|
142
+ next if msg.nil?
143
+ full_messages << msg
144
+ end
145
+ end
146
+ full_messages
147
+ end
148
+ alias_method_chain :full_messages, :gettext_activerecord
149
+
150
+ private
151
+ def localize_error_message(attr, obj, append_field) # :nodoc:
152
+ msgid, count, value = obj, 0, ""
153
+ if obj.kind_of? Hash
154
+ msgid = obj[:default].select{|v| v.is_a? String}[0]
155
+ unless msgid
156
+ symbol = obj[:default][0].to_s.split(".").last.to_sym
157
+ msgid = @@default_error_messages[symbol]
158
+ end
159
+ #attr, count, value = obj[:attribute], obj[:count], obj[:value]
160
+ count, value = obj[:count], obj[:value]
161
+ attr = obj[:attribute] if obj[:attribute]
162
+ end
163
+ msgstr = @base.gettext(msgid)
164
+ msgstr = _(msgid) if msgstr == msgid
165
+ msgstr = msgstr.gsub("%{fn}", "%{attribute}").gsub("%d", "%{count}").gsub("%{val}", "%{value}") # for backward compatibility.
166
+ if attr == "base"
167
+ full_message = msgstr
168
+ elsif /%\{attribute\}/ =~ msgstr
169
+ full_message = msgstr % {:attribute => @base.class.human_attribute_name(attr)}
170
+ elsif append_field
171
+ full_message = @base.class.human_attribute_name(attr) + " " + msgstr
172
+ else
173
+ full_message = msgstr
174
+ end
175
+ full_message % {:count => count, :value => value}
176
+ end
177
+
178
+ def localize_error_messages(append_field = true) # :nodoc:
179
+ # e.g.) foo field: "%{fn} foo" => "Foo foo", "foo" => "Foo foo".
180
+ errors = {}
181
+ each_without_gettext_activerecord {|attr, msg|
182
+ next if msg.nil?
183
+ errors[attr] ||= []
184
+ errors[attr] << localize_error_message(attr, msg, append_field)
185
+ }
186
+ errors
187
+ end
188
+
189
+ end
190
+ end
191
+