lastobelus-merb_global 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/activerecord_generators/translations_migration/USAGE +4 -0
  2. data/activerecord_generators/translations_migration/templates/translations_migration.erb +30 -0
  3. data/activerecord_generators/translations_migration/translations_migration_generator.rb +31 -0
  4. data/examples/active_record_example/README.txt +9 -0
  5. data/examples/active_record_example/application.rb +5 -0
  6. data/examples/active_record_example/config/database.yml +9 -0
  7. data/examples/active_record_example/config/framework.rb +5 -0
  8. data/examples/active_record_example/config/init.rb +24 -0
  9. data/examples/active_record_example/config/plugins.yml +3 -0
  10. data/examples/data_mapper_example/README.txt +9 -0
  11. data/examples/data_mapper_example/application.rb +5 -0
  12. data/examples/data_mapper_example/config/database.yml +9 -0
  13. data/examples/data_mapper_example/config/framework.rb +5 -0
  14. data/examples/data_mapper_example/config/init.rb +24 -0
  15. data/examples/data_mapper_example/config/plugins.yml +3 -0
  16. data/examples/database.sql +28 -0
  17. data/examples/gettext_example/README.txt +9 -0
  18. data/examples/gettext_example/application.rb +8 -0
  19. data/examples/gettext_example/config/framework.rb +5 -0
  20. data/examples/gettext_example/config/init.rb +24 -0
  21. data/examples/gettext_example/config/plugins.yml +4 -0
  22. data/examples/gettext_example/locale/merbapp.pot +21 -0
  23. data/examples/gettext_example/locale/pl/LC_MESSAGES/merbapp.mo +0 -0
  24. data/examples/gettext_example/locale/pl.po +23 -0
  25. data/examples/mock_example/README.txt +9 -0
  26. data/examples/mock_example/application.rb +5 -0
  27. data/examples/mock_example/config/framework.rb +5 -0
  28. data/examples/mock_example/config/init.rb +23 -0
  29. data/examples/mock_example/config/plugins.yml +3 -0
  30. data/examples/sequel_example/README.txt +9 -0
  31. data/examples/sequel_example/application.rb +5 -0
  32. data/examples/sequel_example/config/database.yml +9 -0
  33. data/examples/sequel_example/config/framework.rb +5 -0
  34. data/examples/sequel_example/config/init.rb +24 -0
  35. data/examples/sequel_example/config/plugins.yml +3 -0
  36. data/examples/yaml_example/README.txt +9 -0
  37. data/examples/yaml_example/application.rb +5 -0
  38. data/examples/yaml_example/config/framework.rb +5 -0
  39. data/examples/yaml_example/config/init.rb +24 -0
  40. data/examples/yaml_example/config/plugins.yml +4 -0
  41. data/examples/yaml_example/locale/en.yaml +2 -0
  42. data/examples/yaml_example/locale/pl.yaml +2 -0
  43. data/lib/merb_global/base.rb +105 -0
  44. data/lib/merb_global/config.rb +36 -0
  45. data/lib/merb_global/controller.rb +38 -0
  46. data/lib/merb_global/date_providers/fork.rb +35 -0
  47. data/lib/merb_global/date_providers.rb +47 -0
  48. data/lib/merb_global/locale.rb +139 -0
  49. data/lib/merb_global/merbrake.rb +37 -0
  50. data/lib/merb_global/message_providers/active_record.rb +113 -0
  51. data/lib/merb_global/message_providers/data_mapper.rb +113 -0
  52. data/lib/merb_global/message_providers/gettext.rb +123 -0
  53. data/lib/merb_global/message_providers/gettext.treetop +60 -0
  54. data/lib/merb_global/message_providers/mock.rb +17 -0
  55. data/lib/merb_global/message_providers/sequel.rb +99 -0
  56. data/lib/merb_global/message_providers/yaml.rb +92 -0
  57. data/lib/merb_global/message_providers.rb +146 -0
  58. data/lib/merb_global/numeric_providers/fork.rb +35 -0
  59. data/lib/merb_global/numeric_providers/java.rb +15 -0
  60. data/lib/merb_global/numeric_providers.rb +48 -0
  61. data/lib/merb_global/plural.rb +20 -0
  62. data/lib/merb_global/plural.treetop +267 -0
  63. data/lib/merb_global/providers.rb +40 -0
  64. data/lib/merb_global.rb +8 -0
  65. data/sequel_generators/translations_migration/USAGE +4 -0
  66. data/sequel_generators/translations_migration/templates/translations_migration.erb +28 -0
  67. data/sequel_generators/translations_migration/translations_migration_generator.rb +32 -0
  68. metadata +92 -1
@@ -0,0 +1,60 @@
1
+ module Merb
2
+ module Global
3
+ module MessageProviders
4
+ grammar GetText
5
+ rule po_file
6
+ entry* {
7
+ def to_hash
8
+ hash = {}
9
+ elements.each {|entry| hash.merge! entry.to_hash}
10
+ hash
11
+ end
12
+ }
13
+ end
14
+
15
+ rule entry
16
+ whitespaces*
17
+ "msgid" whitespaces msgid:strings
18
+ "msgstr" whitespaces msgstr:strings {
19
+ def to_hash
20
+ {msgid.to_string => {:plural => nil, nil => msgstr.to_string}}
21
+ end
22
+ }
23
+ /
24
+ whitespaces*
25
+ "msgid" whitespaces msgid:strings
26
+ "msgid_plural" whitespaces msgid_plural:strings
27
+ msgstrs:("msgstr[" number:[0-9]+ "]" whitespaces strings)+ {
28
+ def to_hash
29
+ hash = {:plural => msgid_plural.to_string}
30
+ msgstrs.elements.each do |msgstr|
31
+ hash[msgstr.number.text_value.to_i] = msgstr.strings.to_string
32
+ end
33
+ {msgid.to_string => hash}
34
+ end
35
+ }
36
+ end
37
+
38
+ rule strings
39
+ (string whitespaces?)+ {
40
+ def to_string
41
+ elements.collect {|element| element.string.to_string}.join
42
+ end
43
+ }
44
+ end
45
+
46
+ rule string
47
+ '"' content:((!'"' ('\"' / .))*) '"' {
48
+ def to_string
49
+ content.text_value.gsub(/\\n/, "\n")
50
+ end
51
+ }
52
+ end
53
+
54
+ rule whitespaces
55
+ (" " / "\t" / "\n" / ('#' (!"\n" .)* "\n") )+
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,17 @@
1
+ module Merb
2
+ module Global
3
+ module MessageProviders
4
+ class Mock #:nodoc:
5
+ include Merb::Global::MessageProviders::Base
6
+
7
+ def localize(singular, plural, n, locale)
8
+ n > 1 ? plural : singular
9
+ end
10
+
11
+ def create!
12
+ nil # It's mock after all ;)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,99 @@
1
+ require 'sequel'
2
+ require 'merb_global/plural'
3
+
4
+ module Merb
5
+ module Global
6
+ module MessageProviders
7
+ class Sequel #:nodoc: all
8
+ include Merb::Global::MessageProviders::Base
9
+ include Merb::Global::MessageProviders::Base::Importer
10
+ include Merb::Global::MessageProviders::Base::Exporter
11
+
12
+ def localize(singular, plural, n, locale)
13
+ # I hope it's from MemCache
14
+ language = Language[:name => locale.to_s]
15
+ unless language.nil?
16
+ unless plural.nil?
17
+ pn = Plural.which_form n, language[:plural]
18
+ translation = Translation[language.pk, singular, pn]
19
+ else
20
+ translation = Translation[language.pk, singular, nil]
21
+ end
22
+ return translation[:msgstr] unless translation.nil?
23
+ end
24
+ return n > 1 ? plural : singular # Fallback if not in database
25
+ end
26
+
27
+ def create!
28
+ migration_exists = Dir[File.join(Merb.root, 'schema',
29
+ 'migrations', "*.rb")].detect do |f|
30
+ f =~ /translations\.rb/
31
+ end
32
+ if migration_exists
33
+ puts "\nThe Translation Migration File already exists\n\n"
34
+ else
35
+ sh %{merb-gen translations_migration}
36
+ end
37
+ end
38
+
39
+ def import
40
+ data = {}
41
+ DB.transaction do
42
+ Language.each do |lang|
43
+ data[lang[:name]] = lang_hash = {
44
+ :plural => lang[:plural],
45
+ :nplural => lang[:nplural]
46
+ }
47
+ lang.translations.each do |translation|
48
+ lang_hash[translation[:msgid]] ||= {
49
+ :plural => translation[:msgid_plural]
50
+ }
51
+ lang_hash[translation[:msgid]][translation[:msgstr_index]] =
52
+ translation[:msgstr]
53
+ end
54
+ end
55
+ end
56
+ data
57
+ end
58
+
59
+ def export(data)
60
+ DB.transaction do
61
+ Language.delete_all
62
+ Translation.delete_all
63
+ data.each do |lang_name, lang|
64
+ lang_obj = Language.create(:name => lang_name,
65
+ :plural => lang[:plural],
66
+ :nplural => lang[:nplural]) or raise
67
+ lang_id = lang_obj[:id]
68
+ lang.each do |msgid, msgstrs|
69
+ if msgid.is_a? String
70
+ plural = msgstrs[:plural]
71
+ msgstrs.each do |index, msgstr|
72
+ if index.nil? or index.is_a? Fixnum
73
+ Translation.create(:language_id => lang_id,
74
+ :msgid => msgid,
75
+ :msgid_plural => plural,
76
+ :msgstr => msgstr,
77
+ :msgstr_index => index) or raise
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ class Language < ::Sequel::Model(:merb_global_languages)
87
+ set_primary_key :id
88
+ has_many :translations,
89
+ :class => "Merb::Global::MessageProviders::Sequel::Translation",
90
+ :key => :language_id
91
+ end
92
+
93
+ class Translation < ::Sequel::Model(:merb_global_translations)
94
+ set_primary_key :language_id, :msgid, :msgstr_index
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,92 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ module Merb
5
+ module Global
6
+ module MessageProviders
7
+ class Yaml #:nodoc:
8
+ include Merb::Global::MessageProviders::Base
9
+ include Merb::Global::MessageProviders::Base::Importer
10
+ include Merb::Global::MessageProviders::Base::Exporter
11
+
12
+ def initialize
13
+ # Not synchronized - make GC do it's work (may be not optimal
14
+ # but I don't think that some problem will occure).
15
+ # Shouldn't it be sort of cache with some expiration limit?
16
+ @lang = Hash.new
17
+ end
18
+
19
+ def localize(singular, plural, n, locale)
20
+ unless Merb.environment == "development"
21
+ lang = @lang
22
+ else
23
+ lang = {}
24
+ end
25
+
26
+ unless lang.include? locale
27
+ file = File.join Merb::Global::MessageProviders.localedir,
28
+ locale.to_s + '.yaml'
29
+ if File.exist? file
30
+ lang[locale] = YAML.load_file file
31
+ else
32
+ # TODO: Check if it not opens security risk
33
+ lang[locale] = nil
34
+ end
35
+ end
36
+
37
+ unless lang[locale].nil?
38
+ lang = lang[locale]
39
+ unless lang[singular].nil?
40
+ unless plural.nil?
41
+ n = Merb::Global::Plural.which_form n, lang[:plural]
42
+ return lang[singular][n] unless lang[singular][n].nil?
43
+ else
44
+ return lang[singular] unless lang[singular].nil?
45
+ end
46
+ end
47
+ end
48
+ return n > 1 ? plural : singular
49
+ end
50
+
51
+ def create!
52
+ FileUtils.mkdir_p Merb::Global::MessageProviders.localedir
53
+ end
54
+
55
+ def import
56
+ data = {}
57
+ Dir[Merb::Global::MessageProviders.localedir +
58
+ '/*.yaml'].each do |file|
59
+ lang_name = File.basename file, '.yaml'
60
+ data[lang_name] = lang = YAML.load_file(file)
61
+ lang.each do |msgid, msgstr|
62
+ if msgstr.is_a? String and msgid.is_a? String
63
+ lang[msgid] = {nil => msgstr, :plural => nil}
64
+ end
65
+ end
66
+ end
67
+ data
68
+ end
69
+
70
+ def export(data)
71
+ File.unlink *Dir[Merb::Global::MessageProviders.localedir +
72
+ '/*.yaml']
73
+ data.each do |lang_name, lang_orig|
74
+ lang = {}
75
+ lang_orig.each do |msgid, msgstr_hash|
76
+ lang[msgid] = {}
77
+ msgstr_hash.each do |msgstr_index, msgstr|
78
+ if msgstr_index.nil?
79
+ lang[msgid] = msgstr
80
+ else
81
+ lang[msgid][msgstr_index] = msgstr
82
+ end
83
+ end
84
+ end
85
+ YAML.dump File.join(Merb::Global::MessageProviders.localedir,
86
+ lang_name + '.yaml')
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,146 @@
1
+ require 'merb_global/providers'
2
+
3
+ module Merb
4
+ module Global
5
+ module MessageProviders
6
+ include Providers
7
+ # call-seq:
8
+ # localedir => localdir
9
+ #
10
+ # Returns the directory where locales are stored for file-backed
11
+ # providers (such as gettext or yaml)
12
+ #
13
+ # ==== Returns
14
+ # localedir<String>>:: Directory where the locales are stored
15
+ def self.localedir
16
+ localedir =
17
+ if Merb::Global.config :flat
18
+ 'locale'
19
+ else
20
+ Merb::Global.config :localedir, File.join('app', 'locale')
21
+ end
22
+ File.join Merb.root, localedir
23
+ end
24
+ # call-seq:
25
+ # provider => provider
26
+ #
27
+ # Returns the provider of required type
28
+ #
29
+ # ==== Returns
30
+ # provider<Provider>:: Returns provider
31
+ def self.provider
32
+ @@provider ||= self[Merb::Global.config(:message_provider, 'gettext')]
33
+ end
34
+ # Merb-global is able to store the translations in different types of
35
+ # storage. An interface between merb_global and those storages are
36
+ # providers.
37
+ #
38
+ # Please note that it is not required to include this module - despite it
39
+ # is recomended both as a documentation part and the more customized
40
+ # error messages.
41
+ module Base
42
+ # call-seq:
43
+ # localize(singular, plural, opts) => translated
44
+ #
45
+ # Translate string using specific provider.
46
+ # It should be overloaded by the implementor.
47
+ #
48
+ # Do not use this method directly - use Merb::Global._ instead
49
+ #
50
+ # ==== Parameters
51
+ # singular<String>:: A string to translate
52
+ # plural<String>:: A plural form of string (nil if only singular)
53
+ # n<Fixnum>:: A number of objects
54
+ # locale<Locale>:: A locale to which translate
55
+ #
56
+ # ==== Returns
57
+ # translated<String>:: A translated string
58
+ #
59
+ # ==== Raises
60
+ # NoMethodError:: Raised by default implementation. Should not be thrown.
61
+ def localize(singular, plural, n, locale)
62
+ raise NoMethodError.new('method localize has not been implemented')
63
+ end
64
+ # This method creates basic files and/or directory structures
65
+ # (for example it adds migration) needed for provider to work.
66
+ #
67
+ # It is called from Rakefile.
68
+ def create!
69
+ raise NoMethodError.new('method create! has not been implemented')
70
+ end
71
+ ##
72
+ # Transfer data from importer into exporter
73
+ #
74
+ # ==== Parameters
75
+ # importer<Importer>:: The provider providing the information
76
+ # exporter<Exporter>:: The provider receiving the information
77
+ def self.transfer(importer, exporter)
78
+ exporter.export importer.import
79
+ end
80
+ ##
81
+ # Importer is a provider through which one can iterate.
82
+ # Therefore it is possible to import data from this source
83
+ module Importer
84
+ ##
85
+ # This method import the data into a specified format from source.
86
+ # The format is nearly dump of the current YAML format.
87
+ #
88
+ # ==== Returns
89
+ # data<~each>:: Data in the specified format.
90
+ #
91
+ # ==== Raises
92
+ # NoMethodError:: Raised by default implementation.
93
+ # Should not be thrown.
94
+ def import # TODO: Describe the format
95
+ raise NoMethodError.new('method import has not been implemented')
96
+ end
97
+ end
98
+ ##
99
+ # Some sources are not only read-only but one can write to them.
100
+ # The provider is exporter if it handles this sort of source.
101
+ module Exporter
102
+ ##
103
+ # The method export the data from specified format into destination
104
+ # The format is nearly dump of the current YAML format.
105
+ #
106
+ # ==== Parameters
107
+ # data<~each>:: Data in the specified format.
108
+ #
109
+ # ==== Raises
110
+ # NoMethodError:: Raised by default implementation.
111
+ # Should not be thrown.
112
+ def export(data) # TODO: Describe the format
113
+ raise NoMethodError.new('method import has not been implemented')
114
+ end
115
+ end
116
+ end
117
+ end
118
+ # Perform the registration
119
+ #
120
+ # ==== Parameters
121
+ # provider_name<~to_sym>:: Name under which it is registred
122
+ # opts<Array[Symbol]>:: Additional imformations
123
+ #
124
+ # ==== Options
125
+ # importer:: Can perform import
126
+ # exporter:: Can perform export
127
+ def self.MessageProvider(provider_name, *opts)
128
+ Module.new do
129
+ @@mg_message_provider_name = provider_name
130
+
131
+ include Merb::Global::MessageProviders::Base
132
+ if opts.include? :importer
133
+ include Merb::Global::MessageProviders::Base::Importer
134
+ end
135
+ if opts.include? :exporter
136
+ include Merb::Global::MessageProviders::Base::Exporter
137
+ end
138
+
139
+ def self.included(klass)
140
+ Merb::Global::MessageProviders.register @@mg_message_provider_name,
141
+ klass
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,35 @@
1
+ require 'inline'
2
+
3
+ module Merb
4
+ module Global
5
+ module NumericProviders
6
+ class Fork
7
+ include Merb::Global::DateProviders::Base
8
+
9
+ def localize(lang, number)
10
+ pipe_rd, pipe_wr = IO.pipe
11
+ pid = fork do
12
+ pipe_rd.close
13
+ setlocale(lang.to_s)
14
+ pipe_wr.write(number)
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,15 @@
1
+ require 'java'
2
+
3
+ module Merb
4
+ module Global
5
+ module NumericProviders
6
+ class Java
7
+ include Merb::Global::DateProviders::Base
8
+
9
+ def localize(locale, number)
10
+ java.text.NumberFormat.instance(locale).format(number)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,48 @@
1
+ require 'merb_global/providers'
2
+
3
+ module Merb
4
+ module Global
5
+ module NumericProviders
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(:numeric_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, number)
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.NumericProvider(provider_name)
37
+ Module.new do
38
+ @@rb_numeric_provider_name = provider_name
39
+ include Merb::Global::NumericProviders::Base
40
+
41
+ def self.included(klass)
42
+ Merb::Global::NumericProviders.register @@rb_numeric_provider_name,
43
+ klass
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ require 'treetop'
2
+
3
+ module Merb
4
+ module Global
5
+ module Plural
6
+ path = (Pathname.new(__FILE__).dirname.expand_path + 'plural')
7
+ @parser = Treetop.load(path.to_s).new
8
+
9
+ # Returns which form should be returned
10
+ # ==== Parameters
11
+ # n<Fixnum>:: A number of elements
12
+ # plural<String>:: Expression
13
+ # ==== Returns
14
+ # Fixnum:: Which form should be translated
15
+ def self.which_form(n, plural)
16
+ @parser.parse(plural).to_lambda.call(n)
17
+ end
18
+ end
19
+ end
20
+ end