merb_global 0.0.4.2 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,3 +1,9 @@
1
+ === 0.0.5
2
+ * Handles lang_REGION as lang
3
+ * Renamed providers into message providers and translate_to into localize
4
+ * Added support for localization of dates and numbers
5
+ * Updated specs
6
+
1
7
  === 0.0.4
2
8
  * Changed database format (to allow importing/exporting)
3
9
  * Added importing/exporting (EXPERIMENTAL)
data/Rakefile CHANGED
@@ -2,10 +2,11 @@ require 'rubygems'
2
2
  require 'rake/gempackagetask'
3
3
  require 'rake/rdoctask'
4
4
  require 'spec/rake/spectask'
5
+ require 'fileutils'
5
6
 
6
7
  PLUGIN = "merb_global"
7
8
  NAME = "merb_global"
8
- GEM_VERSION = "0.0.4.2"
9
+ GEM_VERSION = "0.0.5"
9
10
  AUTHORS = ["Alex Coles", "Maciej Piechotka"]
10
11
  EMAIL = "alex@alexcolesportfolio.com"
11
12
  HOMEPAGE = "http://trac.ikonoklastik.com/merb_global/"
@@ -20,6 +21,7 @@ spec = Gem::Specification.new do |s|
20
21
  s.authors = AUTHORS
21
22
  s.email = EMAIL
22
23
  s.homepage = HOMEPAGE
24
+ s.rubyforge_project = 'merb-global'
23
25
  s.add_dependency('merb-core', '>= 0.9.1')
24
26
  s.add_dependency('treetop', '>= 1.2.3') # Tested on 1.2.3
25
27
  s.require_path = 'lib'
@@ -50,6 +52,17 @@ Rake::RDocTask.new do |rd|
50
52
  rd.rdoc_files.include "lib/**/*.rb"
51
53
  end
52
54
 
55
+ desc "Creates database for examples"
56
+ task :populate_db do
57
+ pwd = File.dirname __FILE__
58
+ db = "#{pwd}/examples/database.db"
59
+ sh %{sqlite3 #{db} < #{pwd}/examples/database.sql}
60
+ FileUtils.cp db, "#{pwd}/examples/active_record_example/database.db"
61
+ FileUtils.cp db, "#{pwd}/examples/data_mapper_example/database.db"
62
+ FileUtils.cp db, "#{pwd}/examples/sequel_example/database.db"
63
+ end
64
+ task :gem => :populate_db
65
+
53
66
  desc "Run all specs"
54
67
  Spec::Rake::SpecTask.new('specs') do |st|
55
68
  st.libs = ['lib', 'spec']
@@ -1,5 +1,5 @@
1
1
  #<% if false %>
2
- class Merb::Global::Providers::ActiveRecord
2
+ class Merb::Global::MessageProviders::ActiveRecord
3
3
  #<% end %>
4
4
  class AddTranslationsMigration < ActiveRecord::Migration
5
5
  def self.up
@@ -1,34 +1,47 @@
1
1
  require 'merb_global/config'
2
2
  require 'merb_global/plural'
3
- require 'merb_global/provider'
4
- require 'merb_global/providers'
5
- require 'merb_global/transfer'
3
+ require 'merb_global/date_providers'
4
+ require 'merb_global/message_providers'
5
+ require 'merb_global/numeric_providers'
6
6
 
7
7
  module Merb
8
8
  module Global
9
- attr_accessor :lang, :provider
9
+ attr_accessor :lang, :message_provider, :date_provider, :numeric_provider
10
10
 
11
11
  def lang #:nodoc:
12
12
  @lang ||= 'en'
13
13
  end
14
14
 
15
- def provider #:nodoc:
16
- @provider ||= Merb::Global::Providers.provider
15
+ def message_provider #:nodoc:
16
+ @message_provider ||= Merb::Global::MessageProviders.provider
17
+ end
18
+
19
+ def date_provider #:nodoc:
20
+ @date_provider ||= Merb::Global::DateProviders.provider
21
+ end
22
+
23
+ def numeric_provider #:nodoc:
24
+ @numeric_provider ||= Merb::Global::NumericProviders.provider
17
25
  end
18
26
 
19
27
  # call-seq:
20
- # _(singular, opts) => translated
21
- # _(singlular, plural, opts) => translated
28
+ # _(singular, opts) => translated message
29
+ # _(singlular, plural, opts) => translated message
30
+ # _(date, format) => localized date
31
+ # _(number) => localized number
22
32
  #
23
33
  # Translate a string.
24
34
  # ==== Parameters
25
35
  # singular<String>:: A string to translate
26
36
  # plural<String>:: A plural form of string
27
37
  # opts<Hash>:: An options hash (see below)
38
+ # date<~strftime>:: A date to localize
39
+ # format<String>:: A format of string (should be compatibile with strftime)
40
+ # number<Numeric>:: A numeber to localize
28
41
  #
29
42
  # ==== Options (opts)
30
43
  # :lang<String>:: A language to translate on
31
- # :n<Fixnum>:: A number of objects
44
+ # :n<Fixnum>:: A number of objects (for messages)
32
45
  #
33
46
  # ==== Returns
34
47
  # translated<String>:: A translated string
@@ -38,12 +51,30 @@ module Merb
38
51
  def _(*args)
39
52
  opts = {:lang => self.lang, :n => 1}
40
53
  opts.merge! args.pop if args.last.is_a? Hash
41
- if args.size == 1
42
- self.provider.translate_to args[0], nil, opts
43
- elsif args.size == 2
44
- self.provider.translate_to args[0], args[1], opts
54
+ if args.first.respond_to? :strftime
55
+ if args.size == 2
56
+ self.date_provider.localize opts[:lang], args[0], args[1]
57
+ else
58
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
59
+ end
60
+ elsif args.first.is_a? Numeric
61
+ if args.size == 1
62
+ self.numeric_provider.localize opts[:lang], args.first
63
+ else
64
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1)"
65
+ end
66
+ elsif args.first.is_a? String
67
+ if args.size == 1
68
+ self.message_provider.localize args[0], nil, opts
69
+ elsif args.size == 2
70
+ self.message_provider.localize args[0], args[1], opts
71
+ else
72
+ raise ArgumentError,
73
+ "wrong number of arguments (#{args.size} for 1-2)"
74
+ end
45
75
  else
46
- raise ArgumentError, "wrong number of arguments (#{args.size} for 1-2)"
76
+ raise ArgumentError,
77
+ "wrong type of arguments - see documentation for details"
47
78
  end
48
79
  end
49
80
  end
@@ -1,7 +1,7 @@
1
1
  require 'merb_global/base'
2
2
 
3
3
  module Merb
4
- class Controller
4
+ class Controller
5
5
  include Merb::Global
6
6
  class_inheritable_accessor :_language
7
7
 
@@ -9,7 +9,7 @@ module Merb
9
9
  # Set up the language
10
10
  accept_language = self.request.env['HTTP_ACCEPT_LANGUAGE']
11
11
  self.lang = params[:language] ||
12
- (self._language && self._language.call) ||
12
+ (self._language && self.instance_eval(&self._language)) ||
13
13
  begin
14
14
  unless accept_language.nil?
15
15
  accept_language = accept_language.split(',')
@@ -25,15 +25,30 @@ module Merb
25
25
  end
26
26
  accept_language.sort! {|lang_a, lang_b| lang_a[1] <=> lang_b[1]}
27
27
  accept_language.collect! {|lang| lang[0]}
28
- accept_language.reject! do |lang|
29
- lang != '*' and not self.provider.support? lang
28
+ accept_language_orig = accept_language.dup
29
+ accept_language.collect! do |lang|
30
+ if lang == '*'
31
+ '*'
32
+ elsif self.message_provider.support? lang
33
+ lang
34
+ elsif lang.include? '-'
35
+ lang = lang.split('-')[0]
36
+ if self.message_provider.support? lang
37
+ lang
38
+ else
39
+ nil
40
+ end
41
+ else
42
+ nil
43
+ end
30
44
  end
45
+ accept_language.reject! {|lang| lang.nil?}
31
46
  unless accept_language.empty?
32
47
  unless accept_language.last == '*'
33
48
  accept_language.last
34
49
  else
35
50
  accept_language.pop
36
- self.provider.choose accept_language
51
+ self.message_provider.choose accept_language
37
52
  end
38
53
  end
39
54
  end
@@ -0,0 +1,35 @@
1
+ require 'inline'
2
+
3
+ module Merb
4
+ module Global
5
+ module DateProviders
6
+ class Fork
7
+ include Merb::Global::DateProviders::Base
8
+
9
+ def localize(lang, date, format)
10
+ pipe_rd, pipe_wr = IO.pipe
11
+ pid = fork do
12
+ pipe_rd.close
13
+ setlocale(lang)
14
+ pipe_wr.write(date.strftime(format))
15
+ pipe_wr.flush
16
+ end
17
+ pipe_wr.close
18
+ Process.wait(pid)
19
+ pipe_rd.read
20
+ end
21
+
22
+ inline do |builder|
23
+ builder.include '<locale.h>'
24
+ builder.c <<C
25
+ void set_locale(const char *locale)
26
+ {
27
+ setlocale(LC_ALL, locale);
28
+ }
29
+ C
30
+ end
31
+ private :set_locale
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ require 'merb_global/providers'
2
+
3
+ module Merb
4
+ module Global
5
+ module DateProviders
6
+ include Providers
7
+ # call-seq:
8
+ # provider => provider
9
+ #
10
+ # Returns the provider of required type
11
+ #
12
+ # ==== Returns
13
+ # provider<Provider>:: Returns provider
14
+ def self.provider
15
+ @@provider ||= self[Merb::Global.config(:date_provider, 'fork')]
16
+ end
17
+ # Merb-global is able to handle localization in different ways.
18
+ # Providers are the interface.
19
+ #
20
+ # Please note that it is not required to include this module - despite it
21
+ # is recomended both as a documentation part and the more customized
22
+ # error messages.
23
+ module Base
24
+ ##
25
+ #
26
+ # Localize date using format as in strftime
27
+ def localize(lang, date, format)
28
+ raise NoMethodError.new('method localize has not been implemented')
29
+ end
30
+ end
31
+ end
32
+ # Perform the registration
33
+ #
34
+ # ==== Parameters
35
+ # name<~to_sym>:: Name under which it is registred
36
+ def self.DateProvider(provider_name)
37
+ Module.new do
38
+ @@rb_date_provider_name = provider_name
39
+ include Merb::Global::DateProviders::Base
40
+
41
+ def self.included(klass)
42
+ Merb::Global::DateProviders.register @@rb_date_provider_name, klass
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,25 +1,25 @@
1
1
  require 'fileutils'
2
2
 
3
3
  namespace :merb_global do
4
-
4
+
5
5
  task :merb_start do
6
6
  Merb.start_environment :adapter => 'runner',
7
7
  :environment => ENV['MERB_ENV'] || 'development'
8
8
  end
9
-
9
+
10
10
  desc 'Create migration'
11
11
  task :migration => :merb_start do
12
- Merb::Global::Providers.provider.create!
12
+ Merb::Global::MessageProviders.provider.create!
13
13
  end
14
-
14
+
15
15
  desc 'Transfer the translations from one provider to another'
16
16
  task :transfer => :merb_start do
17
17
  from = Merb::Global.config 'source', 'gettext'
18
18
  into = Merb::Global.config 'provider', 'gettext'
19
19
  if from == 'gettext' and into == 'gettext'
20
- Dir[Merb::Global::Providers.localedir + '/*.po'].each do |file|
20
+ Dir[Merb::Global::MessageProviders.localedir + '/*.po'].each do |file|
21
21
  lang = File.basename file, '.po'
22
- lang_dir = File.join(Merb::Global::Providers.localedir,
22
+ lang_dir = File.join(Merb::Global::MessageProviders.localedir,
23
23
  lang, 'LC_MESSAGES')
24
24
  FileUtils.mkdir_p lang_dir
25
25
  domain = Merb::Global.config([:gettext, :domain], 'merbapp')
@@ -28,10 +28,10 @@ namespace :merb_global do
28
28
  elsif from == into
29
29
  Merb.logger.error 'Tried transfer from and into the same provider'
30
30
  else
31
- from = Merb::Global::Providers[from]
32
- into = Merb::Global::Providers[into]
31
+ from = Merb::Global::MessageProviders[from]
32
+ into = Merb::Global::MessageProviders[into]
33
33
  Merb::Global::Provider.transfer from, into
34
34
  end
35
35
  end
36
-
37
- end
36
+
37
+ end
@@ -7,13 +7,13 @@ require 'merb_global/plural'
7
7
 
8
8
  module Merb
9
9
  module Global
10
- module Providers
10
+ module MessageProviders
11
11
  class ActiveRecord #:nodoc: all
12
- include Merb::Global::Provider
13
- include Merb::Global::Provider::Importer
14
- include Merb::Global::Provider::Exporter
15
-
16
- def translate_to(singular, plural, opts)
12
+ include Merb::Global::MessageProviders::Base
13
+ include Merb::Global::MessageProviders::Base::Importer
14
+ include Merb::Global::MessageProviders::Base::Exporter
15
+
16
+ def localize(singular, plural, opts)
17
17
  language = Language.find :first,
18
18
  :conditions => {:name => opts[:lang]}
19
19
  unless language.nil?
@@ -22,7 +22,7 @@ module Merb
22
22
  translation = Translation.find [language.id, singular, n]
23
23
  else
24
24
  translation = Translation.find [language.id, singular, nil]
25
- end
25
+ end
26
26
  return translation.msgstr
27
27
  end rescue nil
28
28
  return opts[:n] > 1 ? plural : singular # Fallback if not in database
@@ -57,54 +57,61 @@ module Merb
57
57
  # "(#{except.join(',')})"])
58
58
  end
59
59
 
60
- def import(exporter, export_data)
60
+ def import
61
+ data = {}
61
62
  Language.transaction do
62
63
  Translation.transaction do
63
- Language.find(:all).each do |language|
64
- exporter.export_language export_data, language.name,
65
- language.nplural,
66
- language.plural do |lang|
67
- language.translations.each do |translation|
68
- exporter.export_string lang, translation.msgid,
69
- translation.msgid_plural,
70
- translation.msgstr_index,
71
- translation.msgstr
72
- end
64
+ Language.find(:all).each do |lang|
65
+ data[lang.name] = lang_hash = {
66
+ :plural => lang.plural,
67
+ :nplural => lang.nplural
68
+ }
69
+ lang.translations.each do |translation|
70
+ lang_hash[translation.msgid] ||= {
71
+ :plural => translation.msgid_plural
72
+ }
73
+ lang_hash[translation.msgid][translation.msgstr_index] =
74
+ translation.msgstr
73
75
  end
74
76
  end
75
77
  end
76
78
  end
79
+ data
77
80
  end
78
81
 
79
- def export
82
+ def export(data)
80
83
  Language.transaction do
81
84
  Translation.transaction do
82
- Language.delete_all
83
85
  Translation.delete_all
84
- yield nil
86
+ Language.delete_all
87
+ data.each do |lang_name, lang|
88
+ lang_id = Language.create!(:name => lang_name,
89
+ :plural => lang[:plural],
90
+ :nplural => lang[:nplural]).id
91
+ lang.each do |msgid, msgstrs|
92
+ if msgid.is_a? String
93
+ plural = msgstrs[:plural]
94
+ msgstrs.each do |index, msgstr|
95
+ if index.nil? or index.is_a? Fixnum
96
+ Translation.create! :language_id => lang_id,
97
+ :msgid => msgid,
98
+ :msgid_plural => plural,
99
+ :msgstr => msgstr,
100
+ :msgstr_index => index
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
85
106
  end
86
107
  end
87
108
  end
88
109
 
89
- def export_language(export_data, language, nplural, plural)
90
- yield Language.create!(:language => language, :nplural => nplural,
91
- :plural => plural).id
92
- end
93
-
94
- def export_string(language_id, msgid, msgid_plural,
95
- msgstr, msgstr_index)
96
- Translation.create! :language_id => language_id,
97
- :msgid => msgid,
98
- :msgid_plural => msgid_plural,
99
- :msgstr => msgstr,
100
- :msgstr_index => msgstr_index
101
- end
102
-
103
110
  class Language < ::ActiveRecord::Base
104
111
  set_table_name :merb_global_languages
105
112
  has_many :translations,
106
113
  :class_name =>
107
- "::Merb::Global::Providers::ActiveRecord::Translation"
114
+ "::Merb::Global::MessageProviders::ActiveRecord::Translation"
108
115
  end
109
116
 
110
117
  class Translation < ::ActiveRecord::Base
@@ -1,16 +1,16 @@
1
- require 'data_mapper'
1
+ require 'dm-core'
2
2
  require 'dm-aggregates'
3
3
  require 'merb_global/plural'
4
4
 
5
5
  module Merb
6
6
  module Global
7
- module Providers
7
+ module MessageProviders
8
8
  class DataMapper #:nodoc: all
9
- include Merb::Global::Provider
10
- include Merb::Global::Provider::Importer
11
- include Merb::Global::Provider::Exporter
12
-
13
- def translate_to(singular, plural, opts)
9
+ include Merb::Global::MessageProviders
10
+ include Merb::Global::MessageProviders::Base::Importer
11
+ include Merb::Global::MessageProviders::Base::Exporter
12
+
13
+ def localize(singular, plural, opts)
14
14
  # I hope it's from MemCache
15
15
  language = Language.first :name => opts[:lang]
16
16
  unless language.nil?
@@ -43,50 +43,54 @@ module Merb
43
43
  Language.first(:name.not => except).name
44
44
  end
45
45
 
46
- def import(exporter, export_data)
46
+ def import
47
+ data = {}
47
48
  ::DataMapper::Transaction.new(Language, Translation) do
48
49
  Language.all.each do |language|
49
- exporter.export_language export_data, language.name,
50
- language.nplural,
51
- language.plural do |lang|
52
- language.translations.each do |translation|
53
- exporter.export_string lang, translation.msgid,
54
- translation.msgid_plural,
55
- translation.msgstr_index,
56
- translation.msgstr
57
- end
50
+ data[language.name] = lang_hash = {
51
+ :plural => language.plural,
52
+ :nplural => language.nplural
53
+ }
54
+ language.translations(:fields => Translation.properties.to_a).
55
+ each do |translation|
56
+ lang_hash[translation.msgid] ||= {
57
+ :plural => translation.msgid_plural
58
+ }
59
+ lang_hash[translation.msgid][translation.msgstr_index] =
60
+ translation.msgstr
58
61
  end
59
62
  end
60
63
  end
64
+ data
61
65
  end
62
66
 
63
- def export
67
+ def export(data)
64
68
  ::DataMapper::Transaction.new(Language, Translation) do
65
- Language.all.each {|language| language.destroy}
66
69
  Translation.all.each {|translation| translation.destroy}
67
- yield nil
70
+ Language.all.each {|language| language.destroy}
71
+ data.each do |lang_name, lang|
72
+ lang_obj = Language.create!(:name => lang_name,
73
+ :plural => lang[:plural],
74
+ :nplural => lang[:nplural])
75
+ lang.each do |msgid, msgstr_hash|
76
+ if msgstr_hash.is_a? Hash
77
+ plural = msgstr_hash[:plural]
78
+ msgstr_hash.each do |msgstr_index, msgstr|
79
+ if msgstr_index.nil? or msgstr_index.is_a? Fixnum
80
+ Translation.create!(:language_id => lang_obj.id,
81
+ :msgid => msgid,
82
+ :msgid_plural => plural,
83
+ :msgstr => msgstr,
84
+ :msgstr_index => msgstr_index) or
85
+ raise
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
68
91
  end
69
92
  end
70
93
 
71
- def export_language(export_data, language, nplural, plural)
72
- lang = Language.new :language => language, :nplural => nplural,
73
- :plural => plural
74
- lang.save
75
- raise if lang.new_record?
76
- yield lang.id
77
- end
78
-
79
- def export_string(language_id, msgid, msgid_plural,
80
- msgstr, msgstr_index)
81
- trans = Translation.new :language_id => language_id,
82
- :msgid => msgid,
83
- :msgid_plural => msgid_plural,
84
- :msgstr => msgstr,
85
- :msgstr_index => msgstr_index
86
- trans.save
87
- raise if lang.new_record?
88
- end
89
-
90
94
  # When table structure becomes stable it *should* be documented
91
95
  class Language
92
96
  include ::DataMapper::Resource
@@ -97,7 +101,7 @@ module Merb
97
101
  property :plural, Text, :lazy => false
98
102
  # validates_is_unique :name
99
103
  has n, :translations,
100
- :class_name => "Merb::Global::Providers::DataMapper::Translation",
104
+ :class_name => "Merb::Global::MessageProviders::DataMapper::Translation",
101
105
  :child_key => [:language_id]
102
106
  end
103
107
 
@@ -105,12 +109,6 @@ module Merb
105
109
  include ::DataMapper::Resource
106
110
  storage_names[:default] = 'merb_global_translations'
107
111
  property :language_id, Integer, :nullable => false, :key => true
108
- # Sould it be propery :msgid, :text?
109
- # This form should be faster. However:
110
- # - collision may appear (despite being unpropable)
111
- # - it may be wrong optimalisation
112
- # As far I'll leave it in this form. If anybody could measure the
113
- # speed of both methods it will be appreciate.
114
112
  property :msgid, Text, :nullable => false, :key => true
115
113
  property :msgid_plural, Text, :lazy => true
116
114
  property :msgstr, Text, :nullable => false, :lazy => false