nulogy-gettext_i18n_rails 0.4.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +16 -0
- data/Gemfile.lock +126 -0
- data/Rakefile +24 -0
- data/Readme.md +226 -0
- data/VERSION +1 -0
- data/gettext_i18n_rails.gemspec +65 -0
- data/init.rb +14 -0
- data/lib/gettext_i18n_rails.rb +33 -0
- data/lib/gettext_i18n_rails/action_controller.rb +8 -0
- data/lib/gettext_i18n_rails/active_record.rb +19 -0
- data/lib/gettext_i18n_rails/backend.rb +67 -0
- data/lib/gettext_i18n_rails/base_parser.rb +41 -0
- data/lib/gettext_i18n_rails/haml_parser.rb +15 -0
- data/lib/gettext_i18n_rails/hamlet_parser.rb +16 -0
- data/lib/gettext_i18n_rails/html_safe_translations.rb +29 -0
- data/lib/gettext_i18n_rails/i18n_hacks.rb +25 -0
- data/lib/gettext_i18n_rails/model_attributes_finder.rb +108 -0
- data/lib/gettext_i18n_rails/railtie.rb +22 -0
- data/lib/gettext_i18n_rails/ruby_gettext_extractor.rb +144 -0
- data/lib/gettext_i18n_rails/slim_parser.rb +15 -0
- data/lib/gettext_i18n_rails/string_interpolate_fix.rb +20 -0
- data/lib/gettext_i18n_rails/tasks.rb +127 -0
- data/lib/tasks/gettext_rails_i18n.rake +1 -0
- data/spec/gettext_i18n_rails/action_controller_spec.rb +54 -0
- data/spec/gettext_i18n_rails/active_record_spec.rb +85 -0
- data/spec/gettext_i18n_rails/backend_spec.rb +56 -0
- data/spec/gettext_i18n_rails/haml_parser_spec.rb +39 -0
- data/spec/gettext_i18n_rails/hamlet_parser_spec.rb +33 -0
- data/spec/gettext_i18n_rails/slim_parser_spec.rb +40 -0
- data/spec/gettext_i18n_rails/string_interpolate_fix_spec.rb +32 -0
- data/spec/gettext_i18n_rails_spec.rb +84 -0
- data/spec/spec_helper.rb +39 -0
- metadata +110 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'gettext_i18n_rails/base_parser'
|
2
|
+
|
3
|
+
module GettextI18nRails
|
4
|
+
class SlimParser < BaseParser
|
5
|
+
def self.extension
|
6
|
+
"slim"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.convert_to_code(text)
|
10
|
+
Slim::Engine.new.call(text)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
GetText::RGetText.add_parser(GettextI18nRails::SlimParser)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
needed = "".respond_to?(:html_safe) and
|
2
|
+
(
|
3
|
+
"".html_safe % {:x => '<br/>'} == '<br/>' or
|
4
|
+
not ("".html_safe % {:x=>'a'}).html_safe?
|
5
|
+
)
|
6
|
+
|
7
|
+
if needed
|
8
|
+
class String
|
9
|
+
alias :interpolate_without_html_safe :%
|
10
|
+
|
11
|
+
def %(*args)
|
12
|
+
if args.first.is_a?(Hash) and html_safe?
|
13
|
+
safe_replacement = Hash[args.first.map{|k,v| [k,ERB::Util.h(v)] }]
|
14
|
+
interpolate_without_html_safe(safe_replacement).html_safe
|
15
|
+
else
|
16
|
+
interpolate_without_html_safe(*args).dup # make sure its not html_safe
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,127 @@
|
|
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 => :environment do
|
9
|
+
load_gettext
|
10
|
+
GetText.create_mofiles(true, locale_path, locale_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Update pot/po files."
|
14
|
+
task :find => :environment do
|
15
|
+
load_gettext
|
16
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__),'..','..','lib')
|
17
|
+
require 'gettext_i18n_rails/haml_parser'
|
18
|
+
require 'gettext_i18n_rails/slim_parser'
|
19
|
+
require 'gettext_i18n_rails/hamlet_parser'
|
20
|
+
|
21
|
+
|
22
|
+
if GetText.respond_to? :update_pofiles_org
|
23
|
+
if defined?(Rails.application)
|
24
|
+
msgmerge = Rails.application.config.gettext_i18n_rails.msgmerge
|
25
|
+
end
|
26
|
+
msgmerge ||= %w[--sort-output --no-location --no-wrap]
|
27
|
+
|
28
|
+
GetText.update_pofiles_org(
|
29
|
+
text_domain,
|
30
|
+
files_to_translate,
|
31
|
+
"version 0.0.1",
|
32
|
+
:po_root => locale_path,
|
33
|
+
:msgmerge => msgmerge
|
34
|
+
)
|
35
|
+
else #we are on a version < 2.0
|
36
|
+
puts "install new GetText with gettext:install to gain more features..."
|
37
|
+
#kill ar parser...
|
38
|
+
require 'gettext/parser/active_record'
|
39
|
+
module GetText
|
40
|
+
module ActiveRecordParser
|
41
|
+
module_function
|
42
|
+
def init(x);end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#parse files.. (models are simply parsed as ruby files)
|
47
|
+
GetText.update_pofiles(
|
48
|
+
text_domain,
|
49
|
+
files_to_translate,
|
50
|
+
"version 0.0.1",
|
51
|
+
locale_path
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# This is more of an example, ignoring
|
57
|
+
# the columns/tables that mostly do not need translation.
|
58
|
+
# This can also be done with GetText::ActiveRecord
|
59
|
+
# but this crashed too often for me, and
|
60
|
+
# IMO which column should/should-not be translated does not
|
61
|
+
# belong into the model
|
62
|
+
#
|
63
|
+
# You can get your translations from GetText::ActiveRecord
|
64
|
+
# by adding this to you gettext:find task
|
65
|
+
#
|
66
|
+
# require 'active_record'
|
67
|
+
# gem "gettext_activerecord", '>=0.1.0' #download and install from github
|
68
|
+
# require 'gettext_activerecord/parser'
|
69
|
+
desc "write the model attributes to <locale_path>/model_attributes.rb"
|
70
|
+
task :store_model_attributes => :environment do
|
71
|
+
FastGettext.silence_errors
|
72
|
+
|
73
|
+
require 'gettext_i18n_rails/model_attributes_finder'
|
74
|
+
require 'gettext_i18n_rails/active_record'
|
75
|
+
|
76
|
+
storage_file = "#{locale_path}/model_attributes.rb"
|
77
|
+
puts "writing model translations to: #{storage_file}"
|
78
|
+
|
79
|
+
ignore_tables = [/^sitemap_/, /_versions$/, 'schema_migrations', 'sessions', 'delayed_jobs']
|
80
|
+
GettextI18nRails.store_model_attributes(
|
81
|
+
:to => storage_file,
|
82
|
+
:ignore_columns => ['id', 'type', 'created_at', 'updated_at'],
|
83
|
+
:ignore_tables => ignore_tables
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "add a new language"
|
88
|
+
task :add_language, [:language] => :environment do |_, args|
|
89
|
+
language = args.language || ENV["LANGUAGE"]
|
90
|
+
|
91
|
+
# Let's do some pre-verification of the environment.
|
92
|
+
if language.nil?
|
93
|
+
puts "You need to specify the language to add. Either 'LANGUAGE=eo rake gettext:add_languange' or 'rake gettext:add_languange[eo]'"
|
94
|
+
next
|
95
|
+
end
|
96
|
+
pot = File.join(locale_path, "#{text_domain}.pot")
|
97
|
+
if !File.exists? pot
|
98
|
+
puts "You don't have a pot file yet, you probably should run 'rake gettext:find' at least once. Tried '#{pot}'."
|
99
|
+
next
|
100
|
+
end
|
101
|
+
|
102
|
+
# Create the directory for the new language.
|
103
|
+
dir = File.join(locale_path, language)
|
104
|
+
puts "Creating directory #{dir}"
|
105
|
+
Dir.mkdir dir
|
106
|
+
|
107
|
+
# Create the po file for the new language.
|
108
|
+
new_po = File.join(locale_path, language, "#{text_domain}.po")
|
109
|
+
puts "Initializing #{new_po} from #{pot}."
|
110
|
+
system "msginit --locale=#{language} --input=#{pot} --output=#{new_po}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def locale_path
|
114
|
+
FastGettext.translation_repositories[text_domain].instance_variable_get(:@options)[:path]
|
115
|
+
rescue
|
116
|
+
File.join(Rails.root, "locale")
|
117
|
+
end
|
118
|
+
|
119
|
+
def text_domain
|
120
|
+
# if your textdomain is not 'app': require the environment before calling e.g. gettext:find OR add TEXTDOMAIN=my_domain
|
121
|
+
ENV['TEXTDOMAIN'] || (FastGettext.text_domain rescue nil) || "app"
|
122
|
+
end
|
123
|
+
|
124
|
+
def files_to_translate
|
125
|
+
Dir.glob("{app,lib,config,#{locale_path}}/**/*.{rb,erb,haml,slim}")
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../gettext_i18n_rails/tasks")
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
FastGettext.silence_errors
|
4
|
+
|
5
|
+
describe ActionController::Base do
|
6
|
+
def reset!
|
7
|
+
fake_session = {}
|
8
|
+
@c.stub!(:session).and_return fake_session
|
9
|
+
fake_cookies = {}
|
10
|
+
@c.stub!(:cookies).and_return fake_cookies
|
11
|
+
@c.params = {}
|
12
|
+
@c.request = stub(:env => {})
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
#controller
|
17
|
+
@c = ActionController::Base.new
|
18
|
+
reset!
|
19
|
+
|
20
|
+
#locale
|
21
|
+
FastGettext.available_locales = nil
|
22
|
+
FastGettext.locale = I18n.default_locale = 'fr'
|
23
|
+
FastGettext.available_locales = ['fr','en']
|
24
|
+
end
|
25
|
+
|
26
|
+
it "changes the locale" do
|
27
|
+
@c.params = {:locale=>'en'}
|
28
|
+
@c.set_gettext_locale
|
29
|
+
@c.session[:locale].should == 'en'
|
30
|
+
FastGettext.locale.should == 'en'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "stays with default locale when none was found" do
|
34
|
+
@c.set_gettext_locale
|
35
|
+
@c.session[:locale].should == 'fr'
|
36
|
+
FastGettext.locale.should == 'fr'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "locale isn't cached over request" do
|
40
|
+
@c.params = {:locale=>'en'}
|
41
|
+
@c.set_gettext_locale
|
42
|
+
@c.session[:locale].should == 'en'
|
43
|
+
|
44
|
+
reset!
|
45
|
+
@c.set_gettext_locale
|
46
|
+
@c.session[:locale].should == 'fr'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "reads the locale from the HTTP_ACCEPT_LANGUAGE" do
|
50
|
+
@c.request.stub!(:env).and_return 'HTTP_ACCEPT_LANGUAGE'=>'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3'
|
51
|
+
@c.set_gettext_locale
|
52
|
+
FastGettext.locale.should == 'en'
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require "spec_helper"
|
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
|
+
|
16
|
+
create_table :parts, :force=>true do |t|
|
17
|
+
t.string :name
|
18
|
+
t.references :car_seat
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
ActiveRecord::Base.extend GettextI18nRails::ActiveRecord
|
23
|
+
|
24
|
+
class CarSeat < ActiveRecord::Base
|
25
|
+
validates_presence_of :seat_color, :message=>"translate me"
|
26
|
+
has_many :parts
|
27
|
+
accepts_nested_attributes_for :parts
|
28
|
+
end
|
29
|
+
|
30
|
+
class Part < ActiveRecord::Base
|
31
|
+
belongs_to :car_seat
|
32
|
+
end
|
33
|
+
|
34
|
+
describe ActiveRecord::Base do
|
35
|
+
before do
|
36
|
+
FastGettext.current_cache = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
describe :human_name do
|
40
|
+
it "is translated through FastGettext" do
|
41
|
+
CarSeat.should_receive(:_).with('car seat').and_return('Autositz')
|
42
|
+
CarSeat.human_name.should == 'Autositz'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe :human_attribute_name do
|
47
|
+
it "translates attributes through FastGettext" do
|
48
|
+
CarSeat.should_receive(:s_).with('CarSeat|Seat color').and_return('Sitz farbe')
|
49
|
+
CarSeat.human_attribute_name(:seat_color).should == 'Sitz farbe'
|
50
|
+
end
|
51
|
+
|
52
|
+
it "translates nested attributes through FastGettext" do
|
53
|
+
CarSeat.should_receive(:s_).with('CarSeat|Parts|Name').and_return('Handle')
|
54
|
+
CarSeat.human_attribute_name(:"parts.name").should == 'Handle'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'error messages' do
|
59
|
+
let(:model){
|
60
|
+
c = CarSeat.new
|
61
|
+
c.valid?
|
62
|
+
c
|
63
|
+
}
|
64
|
+
|
65
|
+
it "translates error messages" do
|
66
|
+
FastGettext.stub!(:current_repository).and_return('translate me'=>"Übersetz mich!")
|
67
|
+
FastGettext._('translate me').should == "Übersetz mich!"
|
68
|
+
model.errors[:seat_color].should == ["Übersetz mich!"]
|
69
|
+
end
|
70
|
+
|
71
|
+
it "translates scoped error messages" do
|
72
|
+
pending 'scope is no longer added in 3.x' if ActiveRecord::VERSION::MAJOR >= 3
|
73
|
+
FastGettext.stub!(:current_repository).and_return('activerecord.errors.translate me'=>"Übersetz mich!")
|
74
|
+
FastGettext._('activerecord.errors.translate me').should == "Übersetz mich!"
|
75
|
+
model.errors[:seat_color].should == ["Übersetz mich!"]
|
76
|
+
end
|
77
|
+
|
78
|
+
it "translates error messages with %{fn}" do
|
79
|
+
pending
|
80
|
+
FastGettext.stub!(:current_repository).and_return('translate me'=>"Übersetz %{fn} mich!")
|
81
|
+
FastGettext._('translate me').should == "Übersetz %{fn} mich!"
|
82
|
+
model.errors[:seat_color].should == ["Übersetz car_seat mich!"]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
FastGettext.silence_errors
|
4
|
+
|
5
|
+
describe GettextI18nRails::Backend do
|
6
|
+
it "redirects calls to another I18n backend" do
|
7
|
+
subject.backend.should_receive(:xxx).with(1,2)
|
8
|
+
subject.xxx(1,2)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe :available_locales do
|
12
|
+
it "maps them to FastGettext" do
|
13
|
+
FastGettext.should_receive(:available_locales).and_return [:xxx]
|
14
|
+
subject.available_locales.should == [:xxx]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "and returns an empty array when FastGettext.available_locales is nil" do
|
18
|
+
FastGettext.should_receive(:available_locales).and_return nil
|
19
|
+
subject.available_locales.should == []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe :translate do
|
24
|
+
it "uses gettext when the key is translatable" do
|
25
|
+
FastGettext.stub(:current_repository).and_return 'xy.z.u'=>'a'
|
26
|
+
subject.translate('xx','u',:scope=>['xy','z']).should == 'a'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "interpolates options" do
|
30
|
+
FastGettext.stub(:current_repository).and_return 'ab.c'=>'a%{a}b'
|
31
|
+
subject.translate('xx','c',:scope=>['ab'], :a => 'X').should == 'aXb'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can translate with gettext using symbols" do
|
35
|
+
FastGettext.stub(:current_repository).and_return 'xy.z.v'=>'a'
|
36
|
+
subject.translate('xx',:v ,:scope=>['xy','z']).should == 'a'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can translate with gettext using a flat scope" do
|
40
|
+
FastGettext.stub(:current_repository).and_return 'xy.z.x'=>'a'
|
41
|
+
subject.translate('xx',:x ,:scope=>'xy.z').should == 'a'
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO NameError is raised <-> wtf ?
|
45
|
+
xit "uses the super when the key is not translatable" do
|
46
|
+
lambda{subject.translate('xx','y',:scope=>['xy','z'])}.should raise_error(I18n::MissingTranslationData)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe :interpolate do
|
51
|
+
it "act as an identity function for an array" do
|
52
|
+
translation = [:month, :day, :year]
|
53
|
+
subject.send(:interpolate, translation, {}).should == translation
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "gettext_i18n_rails/haml_parser"
|
3
|
+
|
4
|
+
describe GettextI18nRails::HamlParser do
|
5
|
+
let(:parser){ GettextI18nRails::HamlParser }
|
6
|
+
|
7
|
+
describe "#target?" do
|
8
|
+
it "targets .haml" do
|
9
|
+
parser.target?('foo/bar/xxx.haml').should == true
|
10
|
+
end
|
11
|
+
|
12
|
+
it "does not target anything else" do
|
13
|
+
parser.target?('foo/bar/xxx.erb').should == false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#parse" do
|
18
|
+
it "finds messages in haml" do
|
19
|
+
with_file '= _("xxxx")' do |path|
|
20
|
+
parser.parse(path, []).should == [
|
21
|
+
["xxxx", "#{path}:1"]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "ignores 1.9 errors" do
|
27
|
+
with_file '= _("xxxx", x: 1)' do |path|
|
28
|
+
$stderr.should_receive(:puts).with{|x| x =~ /file ignored/ }
|
29
|
+
parser.parse(path, [1]).should == [1]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "does not find messages in text" do
|
34
|
+
with_file '_("xxxx")' do |path|
|
35
|
+
parser.parse(path, []).should == []
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "gettext_i18n_rails/hamlet_parser"
|
3
|
+
|
4
|
+
describe GettextI18nRails::HamletParser do
|
5
|
+
let(:parser){ GettextI18nRails::HamletParser }
|
6
|
+
|
7
|
+
describe "#target?" do
|
8
|
+
it "targets .hamlet" do
|
9
|
+
parser.target?('foo/bar/xxx.hamlet').should == true
|
10
|
+
end
|
11
|
+
|
12
|
+
it "does not target anything else" do
|
13
|
+
parser.target?('foo/bar/xxx.erb').should == false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#parse" do
|
18
|
+
it "finds messages in slim" do
|
19
|
+
with_file '<div>= _("xxxx")' do |path|
|
20
|
+
parser.parse(path, []).should == [
|
21
|
+
["xxxx", "#{path}:1"]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not find messages in text" do
|
27
|
+
with_file '<div> _("xxxx")' do |path|
|
28
|
+
parser.parse(path, []).should == []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "gettext_i18n_rails/slim_parser"
|
3
|
+
|
4
|
+
describe GettextI18nRails::SlimParser do
|
5
|
+
let(:parser){ GettextI18nRails::SlimParser }
|
6
|
+
|
7
|
+
describe "#target?" do
|
8
|
+
it "targets .slim" do
|
9
|
+
parser.target?('foo/bar/xxx.slim').should == true
|
10
|
+
end
|
11
|
+
|
12
|
+
it "does not target anything else" do
|
13
|
+
parser.target?('foo/bar/xxx.erb').should == false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#parse" do
|
18
|
+
it "finds messages in slim" do
|
19
|
+
with_file 'div = _("xxxx")' do |path|
|
20
|
+
parser.parse(path, []).should == [
|
21
|
+
["xxxx", "#{path}:1"]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
xit "can parse 1.9 syntax" do
|
27
|
+
with_file 'div = _("xxxx", foo: :bar)' do |path|
|
28
|
+
parser.parse(path, []).should == [
|
29
|
+
["xxxx", "#{path}:1"]
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "does not find messages in text" do
|
35
|
+
with_file 'div _("xxxx")' do |path|
|
36
|
+
parser.parse(path, []).should == []
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|