hallelujah-gettext_activerecord 2.0.4

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.
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
+