lastobelus-merb_global 0.0.7 → 0.0.8

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.
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