nulogy-gettext_i18n_rails 0.4.6.1
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/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
|