gettext_i18n_rails 0.1.0
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/README.markdown +139 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/gettext_i18n_rails.gemspec +64 -0
- data/lib/gettext_i18n_rails.rb +21 -0
- data/lib/gettext_i18n_rails/action_controller.rb +6 -0
- data/lib/gettext_i18n_rails/active_record.rb +17 -0
- data/lib/gettext_i18n_rails/backend.rb +47 -0
- data/lib/gettext_i18n_rails/haml_parser.rb +43 -0
- data/lib/gettext_i18n_rails/i18n_hacks.rb +10 -0
- data/lib/gettext_i18n_rails/model_attributes_finder.rb +46 -0
- data/lib/gettext_i18n_rails/ruby_gettext_extractor.rb +136 -0
- data/lib/tasks/gettext_rails_i18n.rake +74 -0
- data/rails/init.rb +10 -0
- data/spec/gettext_i18n_rails/action_controller_spec.rb +40 -0
- data/spec/gettext_i18n_rails/active_record_spec.rb +52 -0
- data/spec/gettext_i18n_rails/backend_spec.rb +41 -0
- data/spec/gettext_i18n_rails_spec.rb +47 -0
- data/spec/spec_helper.rb +17 -0
- metadata +95 -0
data/README.markdown
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
Simple [FastGettext](http://github.com/grosser/fast_gettext) / Rails integration.
|
2
|
+
|
3
|
+
Do all translations you want with FastGettext, use any other I18n backend as extension/fallback.
|
4
|
+
|
5
|
+
Rails does: `I18n.t('weir.rails.syntax.i.hate')`
|
6
|
+
We do: `_('Just translate my damn text!')`
|
7
|
+
To use I18n calls define a `weir.rails.syntax.i.hate` translation.
|
8
|
+
|
9
|
+
[See it working in the example application.](https://github.com/grosser/gettext_i18n_rails_example)
|
10
|
+
|
11
|
+
Setup
|
12
|
+
=====
|
13
|
+
###Installation
|
14
|
+
As plugin: ` script/plugin install git://github.com/grosser/gettext_i18n_rails.git `
|
15
|
+
Or Gem: ` sudo gem install gettext_i18n_rails `
|
16
|
+
|
17
|
+
[FastGettext](http://github.com/grosser/fast_gettext): ` sudo gem install fast_gettext `
|
18
|
+
|
19
|
+
### Want to find used messages in your ruby files ?
|
20
|
+
GetText 1.93 or GetText 2.0: ` sudo gem install gettext `
|
21
|
+
GetText 2.0 will render 1.93 unusable, so only install if you do not have apps that use 1.93!
|
22
|
+
|
23
|
+
` sudo gem install ruby_parser `
|
24
|
+
|
25
|
+
### Locales & initialisation
|
26
|
+
Copy default locales with dates/sentence-connectors/AR-errors you want from e.g.
|
27
|
+
[rails i18n](http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale/) into 'config/locales'
|
28
|
+
|
29
|
+
#environment.rb
|
30
|
+
Rails::Initializer.run do |config|
|
31
|
+
...
|
32
|
+
config.gem "fast_gettext", :version => '~>0.4.17'
|
33
|
+
#only used for mo/po file generation in development, !do not load(:lib=>false), will needlessly eat ram!
|
34
|
+
config.gem "gettext", :lib => false, :version => '>=1.9.3'
|
35
|
+
end
|
36
|
+
|
37
|
+
#config/initialisers/fast_gettext.rb
|
38
|
+
FastGettext.add_text_domain 'app', :path => 'locale'
|
39
|
+
FastGettext.default_available_locales = ['en','de'] #all you want to allow
|
40
|
+
FastGettext.default_text_domain = 'app'
|
41
|
+
|
42
|
+
#application_controller
|
43
|
+
class ApplicationController < ...
|
44
|
+
before_filter :set_gettext_locale
|
45
|
+
|
46
|
+
Translating
|
47
|
+
===========
|
48
|
+
###Getting started
|
49
|
+
####Option A: Traditional mo/po files
|
50
|
+
- use some _('translations')
|
51
|
+
- run `rake gettext:find`, to let GetText find all translations used
|
52
|
+
- (optional) run `rake gettext:store_model_attributes`, to parse the database for columns that can be translated
|
53
|
+
- if this is your first translation: `cp locale/app.pot locale/de/app.po` for every locale you want to use
|
54
|
+
- translate messages in 'locale/de/app.po' (leave msgstr blank and msgstr == msgid)
|
55
|
+
new translations will be marked "fuzzy", search for this and remove it, so that they will be used.
|
56
|
+
Obsolete translations are marked with ~#, they usually can be removed since they are no longer needed
|
57
|
+
- run `rake gettext:pack` to write GetText format translation files
|
58
|
+
|
59
|
+
####Option B: Database
|
60
|
+
This is the most scalable method, since all translators can work simultanousely and online.
|
61
|
+
|
62
|
+
Most easy to use with the [translation database Rails engine](http://github.com/grosser/translation_db_engine).
|
63
|
+
FastGettext setup would look like:
|
64
|
+
include FastGettext::TranslationRepository::Db.require_models #load and include default models
|
65
|
+
FastGettext.add_text_domain 'app', :type=>:db, :model=>TranslationKey
|
66
|
+
Translations can be edited under `/translation_keys`
|
67
|
+
|
68
|
+
###I18n
|
69
|
+
|
70
|
+
I18n.locale <==> FastGettext.locale.to_sym
|
71
|
+
I18n.locale = :de <==> FastGettext.locale = 'de'
|
72
|
+
|
73
|
+
Any call to I18n that matches a gettext key will be translated through FastGettext.
|
74
|
+
|
75
|
+
Namespaces
|
76
|
+
==========
|
77
|
+
Car|Model means Model in namespace Car.
|
78
|
+
You do not have to translate this into english "Model", if you use the
|
79
|
+
namespace-aware translation
|
80
|
+
s_('Car|Model') == 'Model' #when no translation was found
|
81
|
+
|
82
|
+
ActiveRecord - error messages
|
83
|
+
=============================
|
84
|
+
ActiveRecord error messages are translated through Rails::I18n, but
|
85
|
+
model names and model attributes are translated through FastGettext.
|
86
|
+
Therefore a validation error on a BigCar's wheels_size needs `_('big car')` and `_('BigCar|Wheels size')`
|
87
|
+
to display localized.
|
88
|
+
|
89
|
+
The model/attribute translations can be found through `rake gettext:store_model_attributes`,
|
90
|
+
(which ignores some commonly untranslated columns like id,type,xxx_count,...).
|
91
|
+
|
92
|
+
Error messages can be translated through FastGettext, if the ':message' is a translation-id or the matching Rails I18n key is translated.
|
93
|
+
In any other case they go through the SimpleBackend.
|
94
|
+
|
95
|
+
####Option A:
|
96
|
+
Define a translation for "I need my rating!" and use it as message.
|
97
|
+
validates_inclusion_of :rating, :in=>1..5, :message=>N_('I need my rating!')
|
98
|
+
|
99
|
+
####Option B:
|
100
|
+
Do not use :message
|
101
|
+
validates_inclusion_of :rating, :in=>1..5
|
102
|
+
and make a translation for the I18n key: `activerecord.errors.models.rating.attributes.rating.inclusion`
|
103
|
+
|
104
|
+
####Option C:
|
105
|
+
Add a translation to each config/locales/*.yml files
|
106
|
+
en:
|
107
|
+
activerecord:
|
108
|
+
errors:
|
109
|
+
models:
|
110
|
+
rating:
|
111
|
+
attributes:
|
112
|
+
rating:
|
113
|
+
inclusion: " -- please choose!"
|
114
|
+
The [rails I18n guide](http://guides.rubyonrails.org/i18n.html) can help with Option B and C.
|
115
|
+
|
116
|
+
Plurals
|
117
|
+
=======
|
118
|
+
FastGettext supports pluralization
|
119
|
+
n_('Apple','Apples',3) == 'Apples'
|
120
|
+
|
121
|
+
Unfound translations
|
122
|
+
====================
|
123
|
+
Sometimes translations like `_("x"+"u")` cannot be fond. You have 4 options:
|
124
|
+
|
125
|
+
- add `N_('xu')` somewhere else in the code, so the parser sees it
|
126
|
+
- add `N_('xu')` in a totally seperate file like `locale/unfound_translations.rb`, so the parser sees it
|
127
|
+
- use the [gettext_test_log rails plugin ](http://github.com/grosser/gettext_test_log) to find all translations that where used while testing
|
128
|
+
- add a Logger to a translation Chain, so every unfound translations is logged ([example]((http://github.com/grosser/fast_gettext)))
|
129
|
+
|
130
|
+
|
131
|
+
Contributors
|
132
|
+
======
|
133
|
+
- [ruby gettext extractor](http://github.com/retoo/ruby_gettext_extractor/tree/master) from [retoo](http://github.com/retoo)
|
134
|
+
- [Paul McMahon](http://github.com/pwim)
|
135
|
+
- [Duncan Mac-Vicar P](http://duncan.mac-vicar.com/blog/)
|
136
|
+
|
137
|
+
[Michael Grosser](http://pragmatig.wordpress.com)
|
138
|
+
grosser.michael@gmail.com
|
139
|
+
Hereby placed under public domain, do what you want, just do not hold me accountable...
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color']}
|
3
|
+
|
4
|
+
task :default do
|
5
|
+
# test with 2.x
|
6
|
+
puts `VERSION='~>2' rake spec RSPEC_COLOR=1`
|
7
|
+
|
8
|
+
# gem 'activerecord', '>=3' did not work for me, but just require gets the right version...
|
9
|
+
require 'active_record'
|
10
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
11
|
+
puts `rake spec RSPEC_COLOR=1`
|
12
|
+
else
|
13
|
+
'install rails 3 to get full test coverage...'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'jeweler'
|
19
|
+
project_name = 'gettext_i18n_rails'
|
20
|
+
Jeweler::Tasks.new do |gem|
|
21
|
+
gem.name = project_name
|
22
|
+
gem.summary = "Simple FastGettext Rails integration."
|
23
|
+
gem.email = "grosser.michael@gmail.com"
|
24
|
+
gem.homepage = "http://github.com/grosser/#{project_name}"
|
25
|
+
gem.authors = ["Michael Grosser"]
|
26
|
+
gem.add_dependency 'fast_gettext'
|
27
|
+
end
|
28
|
+
|
29
|
+
Jeweler::GemcutterTasks.new
|
30
|
+
rescue LoadError
|
31
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
|
32
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{gettext_i18n_rails}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Michael Grosser"]
|
12
|
+
s.date = %q{2010-05-23}
|
13
|
+
s.email = %q{grosser.michael@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.markdown"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
"README.markdown",
|
19
|
+
"Rakefile",
|
20
|
+
"VERSION",
|
21
|
+
"gettext_i18n_rails.gemspec",
|
22
|
+
"lib/gettext_i18n_rails.rb",
|
23
|
+
"lib/gettext_i18n_rails/action_controller.rb",
|
24
|
+
"lib/gettext_i18n_rails/active_record.rb",
|
25
|
+
"lib/gettext_i18n_rails/backend.rb",
|
26
|
+
"lib/gettext_i18n_rails/haml_parser.rb",
|
27
|
+
"lib/gettext_i18n_rails/i18n_hacks.rb",
|
28
|
+
"lib/gettext_i18n_rails/model_attributes_finder.rb",
|
29
|
+
"lib/gettext_i18n_rails/ruby_gettext_extractor.rb",
|
30
|
+
"lib/tasks/gettext_rails_i18n.rake",
|
31
|
+
"rails/init.rb",
|
32
|
+
"spec/gettext_i18n_rails/action_controller_spec.rb",
|
33
|
+
"spec/gettext_i18n_rails/active_record_spec.rb",
|
34
|
+
"spec/gettext_i18n_rails/backend_spec.rb",
|
35
|
+
"spec/gettext_i18n_rails_spec.rb",
|
36
|
+
"spec/spec_helper.rb"
|
37
|
+
]
|
38
|
+
s.homepage = %q{http://github.com/grosser/gettext_i18n_rails}
|
39
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
s.rubygems_version = %q{1.3.6}
|
42
|
+
s.summary = %q{Simple FastGettext Rails integration.}
|
43
|
+
s.test_files = [
|
44
|
+
"spec/spec_helper.rb",
|
45
|
+
"spec/gettext_i18n_rails/active_record_spec.rb",
|
46
|
+
"spec/gettext_i18n_rails/backend_spec.rb",
|
47
|
+
"spec/gettext_i18n_rails/action_controller_spec.rb",
|
48
|
+
"spec/gettext_i18n_rails_spec.rb"
|
49
|
+
]
|
50
|
+
|
51
|
+
if s.respond_to? :specification_version then
|
52
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
56
|
+
s.add_runtime_dependency(%q<fast_gettext>, [">= 0"])
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<fast_gettext>, [">= 0"])
|
59
|
+
end
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<fast_gettext>, [">= 0"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GettextI18nRails
|
2
|
+
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
3
|
+
|
4
|
+
extend self
|
5
|
+
end
|
6
|
+
|
7
|
+
begin
|
8
|
+
gem 'fast_gettext', '>=0.4.8'
|
9
|
+
rescue LoadError
|
10
|
+
gem 'grosser-fast_gettext', '>=0.4.8'
|
11
|
+
end
|
12
|
+
|
13
|
+
# include translations into all the places it needs to go...
|
14
|
+
Object.send(:include,FastGettext::Translation)
|
15
|
+
|
16
|
+
require 'gettext_i18n_rails/backend'
|
17
|
+
I18n.backend = GettextI18nRails::Backend.new
|
18
|
+
|
19
|
+
require 'gettext_i18n_rails/i18n_hacks'
|
20
|
+
require 'gettext_i18n_rails/active_record'
|
21
|
+
require 'gettext_i18n_rails/action_controller'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class ActiveRecord::Base
|
2
|
+
# CarDealer.sales_count -> s_('CarDealer|Sales count') -> 'Sales count' if no translation was found
|
3
|
+
def self.human_attribute_name(attribute, *args)
|
4
|
+
s_(gettext_translation_for_attribute_name(attribute))
|
5
|
+
end
|
6
|
+
|
7
|
+
# CarDealer -> _('car dealer')
|
8
|
+
def self.human_name(*args)
|
9
|
+
_(self.to_s.underscore.gsub('_',' '))
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def self.gettext_translation_for_attribute_name(attribute)
|
15
|
+
"#{self}|#{attribute.to_s.gsub('_',' ').capitalize}"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module GettextI18nRails
|
2
|
+
#translates i18n calls to gettext calls
|
3
|
+
class Backend
|
4
|
+
@@translate_defaults = true
|
5
|
+
cattr_accessor :translate_defaults
|
6
|
+
attr_accessor :backend
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
self.backend = I18n::Backend::Simple.new(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def available_locales
|
13
|
+
FastGettext.available_locales || []
|
14
|
+
end
|
15
|
+
|
16
|
+
def translate(locale, key, options)
|
17
|
+
flat_key = flatten_key key, options
|
18
|
+
if FastGettext.key_exist?(flat_key)
|
19
|
+
raise "no yet build..." if options[:locale]
|
20
|
+
_(flat_key)
|
21
|
+
else
|
22
|
+
if self.class.translate_defaults
|
23
|
+
[*options[:default]].each do |default|
|
24
|
+
#try the more specific key first e.g. 'activerecord.errors.my custom message'
|
25
|
+
flat_key = flatten_key default, options
|
26
|
+
return FastGettext._(flat_key) if FastGettext.key_exist?(flat_key)
|
27
|
+
|
28
|
+
#try the short key thereafter e.g. 'my custom message'
|
29
|
+
return FastGettext._(default) if FastGettext.key_exist?(default)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
backend.translate locale, key, options
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(method, *args)
|
37
|
+
backend.send(method, *args)
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def flatten_key key, options
|
43
|
+
scope = [*(options[:scope] || [])]
|
44
|
+
scope.empty? ? key.to_s : "#{scope*'.'}.#{key}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'gettext/utils'
|
2
|
+
begin
|
3
|
+
require 'gettext/tools/rgettext'
|
4
|
+
rescue LoadError #version prior to 2.0
|
5
|
+
require 'gettext/rgettext'
|
6
|
+
end
|
7
|
+
|
8
|
+
module GettextI18nRails
|
9
|
+
module HamlParser
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def target?(file)
|
13
|
+
File.extname(file) == '.haml'
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(file, msgids = [])
|
17
|
+
return msgids unless load_haml
|
18
|
+
require 'gettext_i18n_rails/ruby_gettext_extractor'
|
19
|
+
|
20
|
+
text = IO.readlines(file).join
|
21
|
+
|
22
|
+
haml = Haml::Engine.new(text)
|
23
|
+
code = haml.precompiled
|
24
|
+
return RubyGettextExtractor.parse_string(code, file, msgids)
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_haml
|
28
|
+
return true if @haml_loaded
|
29
|
+
begin
|
30
|
+
require "#{RAILS_ROOT}/vendor/plugins/haml/lib/haml"
|
31
|
+
rescue LoadError
|
32
|
+
begin
|
33
|
+
require 'haml' # From gem
|
34
|
+
rescue LoadError
|
35
|
+
puts "A haml file was found, but haml library could not be found, so nothing will be parsed..."
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
@haml_loaded = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
GetText::RGetText.add_parser(GettextI18nRails::HamlParser)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module GettextI18nRails
|
2
|
+
#write all found models/columns to a file where GetTexts ruby parser can find them
|
3
|
+
def store_model_attributes(options)
|
4
|
+
file = options[:to] || 'locale/model_attributes.rb'
|
5
|
+
File.open(file,'w') do |f|
|
6
|
+
f.puts "#DO NOT MODIFY! AUTOMATICALLY GENERATED FILE!"
|
7
|
+
ModelAttributesFinder.new.find(options).each do |table_name,column_names|
|
8
|
+
#model name
|
9
|
+
model = table_name.singularize.camelcase.constantize
|
10
|
+
f.puts("_('#{model.to_s.underscore.gsub('_',' ')}')") #!Keep in sync with ActiveRecord::Base.human_name
|
11
|
+
|
12
|
+
#all columns namespaced under the model
|
13
|
+
column_names.each do |attribute|
|
14
|
+
translation = model.gettext_translation_for_attribute_name(attribute)
|
15
|
+
f.puts("_('#{translation}')")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
f.puts "#DO NOT MODIFY! AUTOMATICALLY GENERATED FILE!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ModelAttributesFinder
|
23
|
+
# options:
|
24
|
+
# :ignore_tables => ['cars',/_settings$/,...]
|
25
|
+
# :ignore_columns => ['id',/_id$/,...]
|
26
|
+
# current connection ---> {'cars'=>['model_name','type'],...}
|
27
|
+
def find(options)
|
28
|
+
found = Hash.new([])
|
29
|
+
|
30
|
+
connection = ActiveRecord::Base.connection
|
31
|
+
connection.tables.each do |table_name|
|
32
|
+
next if ignored?(table_name,options[:ignore_tables])
|
33
|
+
connection.columns(table_name).each do |column|
|
34
|
+
found[table_name] += [column.name] unless ignored?(column.name,options[:ignore_columns])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
found
|
39
|
+
end
|
40
|
+
|
41
|
+
def ignored?(name,patterns)
|
42
|
+
return false unless patterns
|
43
|
+
patterns.detect{|p|p.to_s==name.to_s or (p.is_a?(Regexp) and name=~p)}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# new ruby parser from retoo, that should help extracting "#{_('xxx')}", which is needed especially when parsing haml files
|
2
|
+
#
|
3
|
+
#!/usr/bin/ruby
|
4
|
+
# parser/ruby.rb - look for gettext msg strings in ruby files
|
5
|
+
# Copyright (C) 2009 Reto Schüttel <reto (ät) schuettel (dot) ch>
|
6
|
+
# You may redistribute it and/or modify it under the same license terms as Ruby.
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'ruby_parser'
|
10
|
+
|
11
|
+
module RubyGettextExtractor
|
12
|
+
extend self
|
13
|
+
|
14
|
+
def parse(file, targets = []) # :nodoc:
|
15
|
+
content = File.read(file)
|
16
|
+
parse_string(content, file, targets)
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_string(content, file, targets=[])
|
20
|
+
# file is just for information in error messages
|
21
|
+
parser = Extractor.new(file, targets)
|
22
|
+
parser.run(content)
|
23
|
+
end
|
24
|
+
|
25
|
+
def target?(file) # :nodoc:
|
26
|
+
return file =~ /\.rb$/
|
27
|
+
end
|
28
|
+
|
29
|
+
class Extractor < RubyParser
|
30
|
+
def initialize(filename, targets)
|
31
|
+
@filename = filename
|
32
|
+
@targets = Hash.new
|
33
|
+
@results = []
|
34
|
+
|
35
|
+
targets.each do |a|
|
36
|
+
k, v = a
|
37
|
+
# things go wrong if k already exists, but this
|
38
|
+
# should not happen (according to the gettext doc)
|
39
|
+
@targets[k] = a
|
40
|
+
@results << a
|
41
|
+
end
|
42
|
+
|
43
|
+
super()
|
44
|
+
end
|
45
|
+
|
46
|
+
def run(content)
|
47
|
+
self.parse(content)
|
48
|
+
return @results
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_string(node)
|
52
|
+
if node.first == :str
|
53
|
+
return node.last
|
54
|
+
elsif node.first == :call
|
55
|
+
type, recv, meth, args = node
|
56
|
+
|
57
|
+
# node has to be in form of "string"+("other_string")
|
58
|
+
return nil unless recv && meth == :+
|
59
|
+
|
60
|
+
# descent recurrsivly to determine the 'receiver' of the string concatination
|
61
|
+
# "foo" + "bar" + baz" is
|
62
|
+
# ("foo".+("bar")).+("baz")
|
63
|
+
first_part = extract_string(recv)
|
64
|
+
|
65
|
+
if args.first == :arglist && args.size == 2
|
66
|
+
second_part = extract_string(args.last)
|
67
|
+
|
68
|
+
return nil if second_part.nil?
|
69
|
+
|
70
|
+
return first_part.to_s + second_part.to_s
|
71
|
+
else
|
72
|
+
raise "uuh?"
|
73
|
+
end
|
74
|
+
else
|
75
|
+
return nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def extract_key(args, seperator)
|
80
|
+
key = nil
|
81
|
+
if args.size == 2
|
82
|
+
key = extract_string(args.value)
|
83
|
+
else
|
84
|
+
# this could be n_("aaa","aaa2",1)
|
85
|
+
# all strings arguemnts are extracted and joined with \004 or \000
|
86
|
+
|
87
|
+
arguments = args[1..(-1)]
|
88
|
+
|
89
|
+
res = []
|
90
|
+
arguments.each do |a|
|
91
|
+
str = extract_string(a)
|
92
|
+
# only add strings
|
93
|
+
res << str if str
|
94
|
+
end
|
95
|
+
|
96
|
+
return nil if res.empty?
|
97
|
+
key = res.join(seperator)
|
98
|
+
end
|
99
|
+
|
100
|
+
return nil unless key
|
101
|
+
|
102
|
+
key.gsub!("\n", '\n')
|
103
|
+
key.gsub!("\t", '\t')
|
104
|
+
key.gsub!("\0", '\0')
|
105
|
+
|
106
|
+
return key
|
107
|
+
end
|
108
|
+
|
109
|
+
def new_call recv, meth, args = nil
|
110
|
+
# we dont care if the method is called on a a object
|
111
|
+
if recv.nil?
|
112
|
+
if (meth == :_ || meth == :p_ || meth == :N_ || meth == :pgettext)
|
113
|
+
key = extract_key(args, "\004")
|
114
|
+
elsif meth == :n_
|
115
|
+
key = extract_key(args, "\000")
|
116
|
+
else
|
117
|
+
# skip
|
118
|
+
end
|
119
|
+
|
120
|
+
if key
|
121
|
+
res = @targets[key]
|
122
|
+
|
123
|
+
unless res
|
124
|
+
res = [key]
|
125
|
+
@results << res
|
126
|
+
@targets[key] = res
|
127
|
+
end
|
128
|
+
|
129
|
+
res << "#{@filename}:#{lexer.lineno}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
super recv, meth, args
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
namespace :gettext do
|
2
|
+
def load_gettext
|
3
|
+
require 'gettext'
|
4
|
+
require 'gettext/utils'
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Create mo-files for L10n"
|
8
|
+
task :pack do
|
9
|
+
load_gettext
|
10
|
+
GetText.create_mofiles(true, "locale", "locale")
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Update pot/po files."
|
14
|
+
task :find do
|
15
|
+
load_gettext
|
16
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__),'..','..','lib')
|
17
|
+
require 'gettext_i18n_rails/haml_parser'
|
18
|
+
|
19
|
+
if GetText.respond_to? :update_pofiles_org
|
20
|
+
GetText.update_pofiles_org(
|
21
|
+
"app",
|
22
|
+
Dir.glob("{app,lib,config,locale}/**/*.{rb,erb,haml}"),
|
23
|
+
"version 0.0.1",
|
24
|
+
:po_root => 'locale',
|
25
|
+
:msgmerge=>['--sort-output']
|
26
|
+
)
|
27
|
+
else #we are on a version < 2.0
|
28
|
+
puts "install new GetText with gettext:install to gain more features..."
|
29
|
+
#kill ar parser...
|
30
|
+
require 'gettext/parser/active_record'
|
31
|
+
module GetText
|
32
|
+
module ActiveRecordParser
|
33
|
+
module_function
|
34
|
+
def init(x);end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#parse files.. (models are simply parsed as ruby files)
|
39
|
+
GetText.update_pofiles(
|
40
|
+
"app",
|
41
|
+
Dir.glob("{app,lib,config,locale}/**/*.{rb,erb,haml}"),
|
42
|
+
"version 0.0.1",
|
43
|
+
'locale'
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# This is more of an example, ignoring
|
49
|
+
# the columns/tables that mostly do not need translation.
|
50
|
+
# This can also be done with GetText::ActiveRecord
|
51
|
+
# but this crashed too often for me, and
|
52
|
+
# IMO which column should/should-not be translated does not
|
53
|
+
# belong into the model
|
54
|
+
#
|
55
|
+
# You can get your translations from GetText::ActiveRecord
|
56
|
+
# by adding this to you gettext:find task
|
57
|
+
#
|
58
|
+
# require 'active_record'
|
59
|
+
# gem "gettext_activerecord", '>=0.1.0' #download and install from github
|
60
|
+
# require 'gettext_activerecord/parser'
|
61
|
+
desc "write the locale/model_attributes.rb"
|
62
|
+
task :store_model_attributes => :environment do
|
63
|
+
FastGettext.silence_errors
|
64
|
+
require 'gettext_i18n_rails/model_attributes_finder'
|
65
|
+
storage_file = 'locale/model_attributes.rb'
|
66
|
+
puts "writing model translations to: #{storage_file}"
|
67
|
+
ignore_tables = [/^sitemap_/, /_versions$/, 'schema_migrations', 'sessions']
|
68
|
+
GettextI18nRails.store_model_attributes(
|
69
|
+
:to => storage_file,
|
70
|
+
:ignore_columns => [/_id$/, 'id', 'type', 'created_at', 'updated_at'],
|
71
|
+
:ignore_tables => ignore_tables
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
begin
|
2
|
+
require 'config/initializers/session_store'
|
3
|
+
rescue LoadError
|
4
|
+
# weird bug, when run with rake rails reports error that session
|
5
|
+
# store is not configured, this fixes it somewhat...
|
6
|
+
end
|
7
|
+
|
8
|
+
#requires fast_gettext to be present.
|
9
|
+
#We give rails a chance to install it using rake gems:install, by loading it later.
|
10
|
+
config.after_initialize { require 'gettext_i18n_rails' }
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
FastGettext.silence_errors
|
4
|
+
|
5
|
+
describe ActionController::Base do
|
6
|
+
before do
|
7
|
+
#controller
|
8
|
+
@c = ActionController::Base.new
|
9
|
+
fake_session = {}
|
10
|
+
@c.stub!(:session).and_return fake_session
|
11
|
+
fake_cookies = {}
|
12
|
+
@c.stub!(:cookies).and_return fake_cookies
|
13
|
+
@c.params = {}
|
14
|
+
@c.request = stub(:env => {})
|
15
|
+
|
16
|
+
#locale
|
17
|
+
FastGettext.available_locales = nil
|
18
|
+
FastGettext.locale = 'fr'
|
19
|
+
FastGettext.available_locales = ['fr','en']
|
20
|
+
end
|
21
|
+
|
22
|
+
it "changes the locale" do
|
23
|
+
@c.params = {:locale=>'en'}
|
24
|
+
@c.set_gettext_locale
|
25
|
+
@c.session[:locale].should == 'en'
|
26
|
+
FastGettext.locale.should == 'en'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "stays with default locale when none was found" do
|
30
|
+
@c.set_gettext_locale
|
31
|
+
@c.session[:locale].should == 'fr'
|
32
|
+
FastGettext.locale.should == 'fr'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "reads the locale from the HTTP_ACCEPT_LANGUAGE" do
|
36
|
+
@c.request.stub!(:env).and_return 'HTTP_ACCEPT_LANGUAGE'=>'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3'
|
37
|
+
@c.set_gettext_locale
|
38
|
+
FastGettext.locale.should == 'en'
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
3
|
+
|
4
|
+
FastGettext.silence_errors
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection({
|
7
|
+
:adapter => "sqlite3",
|
8
|
+
:database => ":memory:",
|
9
|
+
})
|
10
|
+
|
11
|
+
ActiveRecord::Schema.define(:version => 1) do
|
12
|
+
create_table :car_seats, :force=>true do |t|
|
13
|
+
t.string :seat_color
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class CarSeat < ActiveRecord::Base
|
18
|
+
validates_presence_of :seat_color, :message=>"translate me"
|
19
|
+
end
|
20
|
+
|
21
|
+
describe ActiveRecord::Base do
|
22
|
+
before do
|
23
|
+
FastGettext.current_cache = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "has a human name that is translated through FastGettext" do
|
27
|
+
CarSeat.should_receive(:_).with('car seat').and_return('Autositz')
|
28
|
+
CarSeat.human_name.should == 'Autositz'
|
29
|
+
end
|
30
|
+
|
31
|
+
it "translates attributes through FastGettext" do
|
32
|
+
CarSeat.should_receive(:s_).with('CarSeat|Seat color').and_return('Sitz farbe')
|
33
|
+
CarSeat.human_attribute_name(:seat_color).should == 'Sitz farbe'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "translates error messages" do
|
37
|
+
FastGettext.stub!(:current_repository).and_return('translate me'=>"Übersetz mich!")
|
38
|
+
FastGettext._('translate me').should == "Übersetz mich!"
|
39
|
+
c = CarSeat.new
|
40
|
+
c.valid?
|
41
|
+
c.errors.on(:seat_color).should == "Übersetz mich!"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "translates scoped error messages" do
|
45
|
+
pending 'scope is no longer added in 3.x' if ActiveRecord::VERSION::MAJOR >= 3
|
46
|
+
FastGettext.stub!(:current_repository).and_return('activerecord.errors.translate me'=>"Übersetz mich!")
|
47
|
+
FastGettext._('activerecord.errors.translate me').should == "Übersetz mich!"
|
48
|
+
c = CarSeat.new
|
49
|
+
c.valid?
|
50
|
+
c.errors.on(:seat_color).should == "Übersetz mich!"
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path("../spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe GettextI18nRails::Backend do
|
4
|
+
it "redirects calls to another I18n backend" do
|
5
|
+
subject.backend.should_receive(:xxx).with(1,2)
|
6
|
+
subject.xxx(1,2)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe :available_locales do
|
10
|
+
it "maps them to FastGettext" do
|
11
|
+
FastGettext.should_receive(:available_locales).and_return [:xxx]
|
12
|
+
subject.available_locales.should == [:xxx]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "and_return an epmty array when FastGettext.available_locales is nil" do
|
16
|
+
FastGettext.should_receive(:available_locales)
|
17
|
+
subject.available_locales.should == []
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe :translate do
|
22
|
+
it "uses gettext when the key is translateable" do
|
23
|
+
FastGettext.should_receive(:current_repository).and_return 'xy.z.u'=>'a'
|
24
|
+
subject.translate('xx','u',:scope=>['xy','z']).should == 'a'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can translate with gettext using symbols" do
|
28
|
+
FastGettext.should_receive(:current_repository).and_return 'xy.z.v'=>'a'
|
29
|
+
subject.translate('xx',:v ,:scope=>['xy','z']).should == 'a'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can translate with gettext using a flat scope" do
|
33
|
+
FastGettext.should_receive(:current_repository).and_return 'xy.z.x'=>'a'
|
34
|
+
subject.translate('xx',:x ,:scope=>'xy.z').should == 'a'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "uses the super when the key is not translateable" do
|
38
|
+
lambda{subject.translate('xx','y',:scope=>['xy','z'])}.should raise_error(I18n::MissingTranslationData)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
FastGettext.silence_errors
|
4
|
+
|
5
|
+
describe GettextI18nRails do
|
6
|
+
it "extends all classes with fast_gettext" do
|
7
|
+
_('test')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "sets up out backend" do
|
11
|
+
I18n.backend.is_a?(GettextI18nRails::Backend).should be_true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a VERSION" do
|
15
|
+
GettextI18nRails::VERSION.should =~ /^\d+\.\d+\.\d+$/
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'FastGettext I18n interaction' do
|
19
|
+
before do
|
20
|
+
FastGettext.available_locales = nil
|
21
|
+
FastGettext.locale = 'de'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "links FastGettext with I18n locale" do
|
25
|
+
FastGettext.locale = 'xx'
|
26
|
+
I18n.locale.should == :xx
|
27
|
+
end
|
28
|
+
|
29
|
+
it "does not set an not-accepted locale to I18n.locale" do
|
30
|
+
FastGettext.available_locales = ['de']
|
31
|
+
FastGettext.locale = 'xx'
|
32
|
+
I18n.locale.should == :de
|
33
|
+
end
|
34
|
+
|
35
|
+
it "links I18n.locale and FastGettext.locale" do
|
36
|
+
I18n.locale = :yy
|
37
|
+
FastGettext.locale.should == 'yy'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "does not set a non-available locale thorugh I18n.locale" do
|
41
|
+
FastGettext.available_locales = ['de']
|
42
|
+
I18n.locale = :xx
|
43
|
+
FastGettext.locale.should == 'de'
|
44
|
+
I18n.locale.should == :de
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
if ENV['VERSION']
|
3
|
+
puts "running VERSION #{ENV['VERSION']}"
|
4
|
+
gem 'activerecord', ENV['VERSION']
|
5
|
+
gem 'activesupport', ENV['VERSION']
|
6
|
+
gem 'actionpack', ENV['VERSION']
|
7
|
+
gem 'actionmailer', ENV['VERSION']
|
8
|
+
end
|
9
|
+
|
10
|
+
$LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
|
11
|
+
|
12
|
+
require 'active_support'
|
13
|
+
require 'active_record'
|
14
|
+
require 'action_controller'
|
15
|
+
require 'action_mailer'
|
16
|
+
require 'fast_gettext'
|
17
|
+
require 'gettext_i18n_rails'
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gettext_i18n_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Michael Grosser
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-23 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: fast_gettext
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
description:
|
33
|
+
email: grosser.michael@gmail.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README.markdown
|
40
|
+
files:
|
41
|
+
- README.markdown
|
42
|
+
- Rakefile
|
43
|
+
- VERSION
|
44
|
+
- gettext_i18n_rails.gemspec
|
45
|
+
- lib/gettext_i18n_rails.rb
|
46
|
+
- lib/gettext_i18n_rails/action_controller.rb
|
47
|
+
- lib/gettext_i18n_rails/active_record.rb
|
48
|
+
- lib/gettext_i18n_rails/backend.rb
|
49
|
+
- lib/gettext_i18n_rails/haml_parser.rb
|
50
|
+
- lib/gettext_i18n_rails/i18n_hacks.rb
|
51
|
+
- lib/gettext_i18n_rails/model_attributes_finder.rb
|
52
|
+
- lib/gettext_i18n_rails/ruby_gettext_extractor.rb
|
53
|
+
- lib/tasks/gettext_rails_i18n.rake
|
54
|
+
- rails/init.rb
|
55
|
+
- spec/gettext_i18n_rails/action_controller_spec.rb
|
56
|
+
- spec/gettext_i18n_rails/active_record_spec.rb
|
57
|
+
- spec/gettext_i18n_rails/backend_spec.rb
|
58
|
+
- spec/gettext_i18n_rails_spec.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/grosser/gettext_i18n_rails
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options:
|
66
|
+
- --charset=UTF-8
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.6
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Simple FastGettext Rails integration.
|
90
|
+
test_files:
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
- spec/gettext_i18n_rails/active_record_spec.rb
|
93
|
+
- spec/gettext_i18n_rails/backend_spec.rb
|
94
|
+
- spec/gettext_i18n_rails/action_controller_spec.rb
|
95
|
+
- spec/gettext_i18n_rails_spec.rb
|