gettext_i18n_rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|