nelumba-i18n 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +127 -0
- data/Rakefile +7 -0
- data/lib/nelumba-i18n.rb +11 -0
- data/lib/nelumba-i18n/i18n.rb +166 -0
- data/lib/nelumba-i18n/locales.rb +122 -0
- data/lib/nelumba-i18n/locales/en/grammar.yml +230 -0
- data/lib/nelumba-i18n/locales/en/lexicon.yml +135 -0
- data/lib/nelumba-i18n/locales/es/grammar.yml +202 -0
- data/lib/nelumba-i18n/locales/es/lexicon.yml +312 -0
- data/lib/nelumba-i18n/version.rb +3 -0
- data/nelumba-i18n.gemspec +24 -0
- data/spec/en/post_spec.rb +144 -0
- data/spec/helper.rb +20 -0
- data/test/LAYOUT +6 -0
- data/test/en/favorite/object.yml +403 -0
- data/test/en/post/actor_object.yml +10 -0
- data/test/en/post/object.yml +495 -0
- metadata +120 -0
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in ostatus.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem "rake" # rakefile
|
8
|
+
gem "minitest", "4.7.0" # test framework (specified here for prior rubies)
|
9
|
+
gem "ansi" # minitest colors
|
10
|
+
gem "turn" # minitest output
|
11
|
+
gem "mocha" # stubs
|
12
|
+
|
13
|
+
gem "awesome_print"
|
14
|
+
gem "debugger"
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
lotus-i18n (0.0.1)
|
5
|
+
i18n
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ansi (1.4.3)
|
11
|
+
awesome_print (1.1.0)
|
12
|
+
columnize (0.3.6)
|
13
|
+
debugger (1.6.0)
|
14
|
+
columnize (>= 0.3.1)
|
15
|
+
debugger-linecache (~> 1.2.0)
|
16
|
+
debugger-ruby_core_source (~> 1.2.1)
|
17
|
+
debugger-linecache (1.2.0)
|
18
|
+
debugger-ruby_core_source (1.2.3)
|
19
|
+
diff-lcs (1.1.3)
|
20
|
+
i18n (0.6.4)
|
21
|
+
metaclass (0.0.1)
|
22
|
+
minitest (4.7.0)
|
23
|
+
mocha (0.14.0)
|
24
|
+
metaclass (~> 0.0.1)
|
25
|
+
rake (10.1.0)
|
26
|
+
rspec (2.10.0)
|
27
|
+
rspec-core (~> 2.10.0)
|
28
|
+
rspec-expectations (~> 2.10.0)
|
29
|
+
rspec-mocks (~> 2.10.0)
|
30
|
+
rspec-core (2.10.1)
|
31
|
+
rspec-expectations (2.10.0)
|
32
|
+
diff-lcs (~> 1.1.3)
|
33
|
+
rspec-mocks (2.10.1)
|
34
|
+
turn (0.9.6)
|
35
|
+
ansi
|
36
|
+
|
37
|
+
PLATFORMS
|
38
|
+
ruby
|
39
|
+
|
40
|
+
DEPENDENCIES
|
41
|
+
ansi
|
42
|
+
awesome_print
|
43
|
+
debugger
|
44
|
+
lotus-i18n!
|
45
|
+
minitest (= 4.7.0)
|
46
|
+
mocha
|
47
|
+
rake
|
48
|
+
rspec (~> 2.10.0)
|
49
|
+
turn
|
data/LICENSE
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
nelumba-i18n
|
2
|
+
|
3
|
+
To the extent possible under law, the author(s) have dedicated all
|
4
|
+
copyright and related and neighboring rights to this software to the
|
5
|
+
public domain worldwide. This software is distributed without any warranty.
|
6
|
+
|
7
|
+
Creative Commons Legal Code
|
8
|
+
|
9
|
+
CC0 1.0 Universal
|
10
|
+
|
11
|
+
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
12
|
+
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
13
|
+
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
14
|
+
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
15
|
+
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
16
|
+
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
17
|
+
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
18
|
+
HEREUNDER.
|
19
|
+
|
20
|
+
Statement of Purpose
|
21
|
+
|
22
|
+
The laws of most jurisdictions throughout the world automatically confer
|
23
|
+
exclusive Copyright and Related Rights (defined below) upon the creator
|
24
|
+
and subsequent owner(s) (each and all, an "owner") of an original work of
|
25
|
+
authorship and/or a database (each, a "Work").
|
26
|
+
|
27
|
+
Certain owners wish to permanently relinquish those rights to a Work for
|
28
|
+
the purpose of contributing to a commons of creative, cultural and
|
29
|
+
scientific works ("Commons") that the public can reliably and without fear
|
30
|
+
of later claims of infringement build upon, modify, incorporate in other
|
31
|
+
works, reuse and redistribute as freely as possible in any form whatsoever
|
32
|
+
and for any purposes, including without limitation commercial purposes.
|
33
|
+
These owners may contribute to the Commons to promote the ideal of a free
|
34
|
+
culture and the further production of creative, cultural and scientific
|
35
|
+
works, or to gain reputation or greater distribution for their Work in
|
36
|
+
part through the use and efforts of others.
|
37
|
+
|
38
|
+
For these and/or other purposes and motivations, and without any
|
39
|
+
expectation of additional consideration or compensation, the person
|
40
|
+
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
41
|
+
is an owner of Copyright and Related Rights in the Work, voluntarily
|
42
|
+
elects to apply CC0 to the Work and publicly distribute the Work under its
|
43
|
+
terms, with knowledge of his or her Copyright and Related Rights in the
|
44
|
+
Work and the meaning and intended legal effect of CC0 on those rights.
|
45
|
+
|
46
|
+
1. Copyright and Related Rights. A Work made available under CC0 may be
|
47
|
+
protected by copyright and related or neighboring rights ("Copyright and
|
48
|
+
Related Rights"). Copyright and Related Rights include, but are not
|
49
|
+
limited to, the following:
|
50
|
+
|
51
|
+
i. the right to reproduce, adapt, distribute, perform, display,
|
52
|
+
communicate, and translate a Work;
|
53
|
+
ii. moral rights retained by the original author(s) and/or performer(s);
|
54
|
+
iii. publicity and privacy rights pertaining to a person's image or
|
55
|
+
likeness depicted in a Work;
|
56
|
+
iv. rights protecting against unfair competition in regards to a Work,
|
57
|
+
subject to the limitations in paragraph 4(a), below;
|
58
|
+
v. rights protecting the extraction, dissemination, use and reuse of data
|
59
|
+
in a Work;
|
60
|
+
vi. database rights (such as those arising under Directive 96/9/EC of the
|
61
|
+
European Parliament and of the Council of 11 March 1996 on the legal
|
62
|
+
protection of databases, and under any national implementation
|
63
|
+
thereof, including any amended or successor version of such
|
64
|
+
directive); and
|
65
|
+
vii. other similar, equivalent or corresponding rights throughout the
|
66
|
+
world based on applicable law or treaty, and any national
|
67
|
+
implementations thereof.
|
68
|
+
|
69
|
+
2. Waiver. To the greatest extent permitted by, but not in contravention
|
70
|
+
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
71
|
+
irrevocably and unconditionally waives, abandons, and surrenders all of
|
72
|
+
Affirmer's Copyright and Related Rights and associated claims and causes
|
73
|
+
of action, whether now known or unknown (including existing as well as
|
74
|
+
future claims and causes of action), in the Work (i) in all territories
|
75
|
+
worldwide, (ii) for the maximum duration provided by applicable law or
|
76
|
+
treaty (including future time extensions), (iii) in any current or future
|
77
|
+
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
78
|
+
including without limitation commercial, advertising or promotional
|
79
|
+
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
80
|
+
member of the public at large and to the detriment of Affirmer's heirs and
|
81
|
+
successors, fully intending that such Waiver shall not be subject to
|
82
|
+
revocation, rescission, cancellation, termination, or any other legal or
|
83
|
+
equitable action to disrupt the quiet enjoyment of the Work by the public
|
84
|
+
as contemplated by Affirmer's express Statement of Purpose.
|
85
|
+
|
86
|
+
3. Public License Fallback. Should any part of the Waiver for any reason
|
87
|
+
be judged legally invalid or ineffective under applicable law, then the
|
88
|
+
Waiver shall be preserved to the maximum extent permitted taking into
|
89
|
+
account Affirmer's express Statement of Purpose. In addition, to the
|
90
|
+
extent the Waiver is so judged Affirmer hereby grants to each affected
|
91
|
+
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
92
|
+
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
93
|
+
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
94
|
+
maximum duration provided by applicable law or treaty (including future
|
95
|
+
time extensions), (iii) in any current or future medium and for any number
|
96
|
+
of copies, and (iv) for any purpose whatsoever, including without
|
97
|
+
limitation commercial, advertising or promotional purposes (the
|
98
|
+
"License"). The License shall be deemed effective as of the date CC0 was
|
99
|
+
applied by Affirmer to the Work. Should any part of the License for any
|
100
|
+
reason be judged legally invalid or ineffective under applicable law, such
|
101
|
+
partial invalidity or ineffectiveness shall not invalidate the remainder
|
102
|
+
of the License, and in such case Affirmer hereby affirms that he or she
|
103
|
+
will not (i) exercise any of his or her remaining Copyright and Related
|
104
|
+
Rights in the Work or (ii) assert any associated claims and causes of
|
105
|
+
action with respect to the Work, in either case contrary to Affirmer's
|
106
|
+
express Statement of Purpose.
|
107
|
+
|
108
|
+
4. Limitations and Disclaimers.
|
109
|
+
|
110
|
+
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
111
|
+
surrendered, licensed or otherwise affected by this document.
|
112
|
+
b. Affirmer offers the Work as-is and makes no representations or
|
113
|
+
warranties of any kind concerning the Work, express, implied,
|
114
|
+
statutory or otherwise, including without limitation warranties of
|
115
|
+
title, merchantability, fitness for a particular purpose, non
|
116
|
+
infringement, or the absence of latent or other defects, accuracy, or
|
117
|
+
the present or absence of errors, whether or not discoverable, all to
|
118
|
+
the greatest extent permissible under applicable law.
|
119
|
+
c. Affirmer disclaims responsibility for clearing rights of other persons
|
120
|
+
that may apply to the Work or any use thereof, including without
|
121
|
+
limitation any person's Copyright and Related Rights in the Work.
|
122
|
+
Further, Affirmer disclaims responsibility for obtaining any necessary
|
123
|
+
consents, permissions or other rights required for any use of the
|
124
|
+
Work.
|
125
|
+
d. Affirmer understands and acknowledges that Creative Commons is not a
|
126
|
+
party to this document and has no duty or obligation with respect to
|
127
|
+
this CC0 or use of the Work.
|
data/Rakefile
ADDED
data/lib/nelumba-i18n.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# The main module for all Nelumba related functionality.
|
2
|
+
module Nelumba
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'nelumba-i18n/i18n'
|
6
|
+
require 'nelumba-i18n/locales'
|
7
|
+
|
8
|
+
# Add nelumba locales to the I18n load path
|
9
|
+
I18n.load_path += Dir[File.expand_path("../nelumba-i18n/locales/**/lexicon.{yml}", __FILE__)]
|
10
|
+
|
11
|
+
Dir[File.join(File.dirname(__FILE__), "nelumba-i18n", "locales", "*.rb")].each {|file| require file }
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# The main module for all Nelumba related functionality.
|
2
|
+
module Nelumba
|
3
|
+
# The internationalization and localization module.
|
4
|
+
module I18n
|
5
|
+
require 'i18n'
|
6
|
+
|
7
|
+
# Translate the given token to the locale's translation.
|
8
|
+
def self.translate(*args)
|
9
|
+
::I18n.t(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Translates the given verb for the current locale.
|
13
|
+
def self.verb(*args)
|
14
|
+
token = args.shift
|
15
|
+
self.translate("verb.#{token}", *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Translates the given object as a singular for the current locale.
|
19
|
+
def self.single_object(*args)
|
20
|
+
token = args.shift
|
21
|
+
options = args.last if args.last.is_a? Hash
|
22
|
+
self.translate("object.singular_#{token}", *args)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Translates the given object as a plural for the current locale.
|
26
|
+
def self.plural_object(*args)
|
27
|
+
token = args.shift
|
28
|
+
options = args.last if args.last.is_a? Hash
|
29
|
+
self.translate("object.plural_#{token}", *args)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Translates the given object for the current locale.
|
33
|
+
def self.object(*args)
|
34
|
+
token = args.shift
|
35
|
+
self.translate("object.#{token}", *args)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Translates the given pronoun for the current locale.
|
39
|
+
def self.pronoun(*args)
|
40
|
+
token = args.shift
|
41
|
+
self.translate("pronoun.#{token}", *args)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Translates the given possessive pronoun for the current locale.
|
45
|
+
def self.possessive_pronoun(*args)
|
46
|
+
token = args.shift
|
47
|
+
self.translate("possessive_pronoun.#{token}", *args)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Translates the given action for the current locale.
|
51
|
+
def self.action(*args)
|
52
|
+
token = args.shift
|
53
|
+
self.translate("action.#{token}", *args)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Translates the given category for the current locale.
|
57
|
+
def self.category(*args)
|
58
|
+
token = args.shift
|
59
|
+
self.translate("category.#{token}", *args)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Translates the given field for the current locale.
|
63
|
+
def self.field(*args)
|
64
|
+
token = args.shift
|
65
|
+
self.translate("field.#{token}", *args)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Localize the given object for the current locale.
|
69
|
+
def self.localize(*args)
|
70
|
+
::I18n.l(*args)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Produces a sentence describing the given Nelumba::Activity.
|
74
|
+
def self.sentence(options = {})
|
75
|
+
options ||= {}
|
76
|
+
|
77
|
+
components = {}
|
78
|
+
|
79
|
+
if options[:actor].is_a? Symbol
|
80
|
+
components[:actor_pronoun] = options[:actor]
|
81
|
+
else
|
82
|
+
components[:actor] = options[:actor]
|
83
|
+
end
|
84
|
+
|
85
|
+
if options[:actors].is_a? Symbol
|
86
|
+
components[:actors_pronoun] = options[:actors]
|
87
|
+
else
|
88
|
+
components[:actors] = options[:actors]
|
89
|
+
end
|
90
|
+
|
91
|
+
if options[:object_owner].is_a? Symbol
|
92
|
+
components[:object_owner_pronoun] = options[:object_owner]
|
93
|
+
else
|
94
|
+
components[:object_owner] = options[:object_owner]
|
95
|
+
end
|
96
|
+
|
97
|
+
if options[:object_owners].is_a? Symbol
|
98
|
+
components[:object_owners_pronoun] = options[:object_owners]
|
99
|
+
else
|
100
|
+
components[:object_owners] = options[:object_owners]
|
101
|
+
end
|
102
|
+
|
103
|
+
if options[:target_owner].is_a? Symbol
|
104
|
+
components[:target_owner_pronoun] = options[:target_owner]
|
105
|
+
else
|
106
|
+
components[:target_owner] = options[:target_owner]
|
107
|
+
end
|
108
|
+
|
109
|
+
if options[:target_owners].is_a? Symbol
|
110
|
+
components[:target_owners_pronoun] = options[:target_owners]
|
111
|
+
else
|
112
|
+
components[:target_owners] = options[:target_owners]
|
113
|
+
end
|
114
|
+
|
115
|
+
components[:target] = options[:target]
|
116
|
+
components[:targets] = options[:targets]
|
117
|
+
components[:object] = options[:object]
|
118
|
+
components[:objects] = options[:objects]
|
119
|
+
components[:verb] = options[:verb]
|
120
|
+
components[:action] = options[:action]
|
121
|
+
components[:field] = options[:field]
|
122
|
+
|
123
|
+
components.keys.each do |key|
|
124
|
+
if components[key].nil?
|
125
|
+
components.delete key
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
rules = Nelumba::Locales.rules(options)
|
130
|
+
result = ""
|
131
|
+
|
132
|
+
rules.each do |hash|
|
133
|
+
if components.keys.select{|e| !hash["for"].include?(e.to_s)}.empty? &&
|
134
|
+
components.keys.count == hash["for"].count
|
135
|
+
if hash["match"] && hash["match"].count > 0
|
136
|
+
matches = hash["match"]
|
137
|
+
|
138
|
+
violations = matches.select do |rule|
|
139
|
+
if rule[0] && components[rule[0].intern]
|
140
|
+
!components[rule[0].intern].match(Regexp.new(rule[1]))
|
141
|
+
else
|
142
|
+
false
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
next unless violations.empty?
|
147
|
+
end
|
148
|
+
|
149
|
+
result = hash["do"]
|
150
|
+
components.each do |component, value|
|
151
|
+
if value.is_a? Symbol
|
152
|
+
result = result.gsub /[\.a-zA-Z_]*%#{Regexp.escape(component.to_s)}%[\.a-zA-Z_]*/ do |match|
|
153
|
+
self.translate(match.gsub("%#{component.to_s}%", value.to_s).strip, options)
|
154
|
+
end
|
155
|
+
else
|
156
|
+
result = result.gsub("%#{component.to_s}%", value.to_s)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
break
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
result
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Nelumba
|
2
|
+
# Modules that will generate sentences dependent on the locale.
|
3
|
+
module Locales
|
4
|
+
# Returns the metadata used to build grammatical structures for the given
|
5
|
+
# locale.
|
6
|
+
#
|
7
|
+
# options:
|
8
|
+
# :locale => The locale to use. Defaults to :en (English).
|
9
|
+
#
|
10
|
+
# Returns: an array of grammar rules. Each rule is a hash described below:
|
11
|
+
# "for" => gives an array of strings for components that must be available
|
12
|
+
# in order to generate this sentence.
|
13
|
+
# "do" => gives the string that serves as the grammatical structure.
|
14
|
+
# "match" => an array of patterns to match against component values. if
|
15
|
+
# the component does not match, then this rule is not used.
|
16
|
+
# For instance, a rule that looks for an 's' at the end of
|
17
|
+
# a word so as to decide between a rule that add "'s" or "'"
|
18
|
+
# to the end of a word to make it possessive. You would have
|
19
|
+
# a rule that matches that component against "s$". The patterns
|
20
|
+
# are regular expression strings.
|
21
|
+
def self.grammar(options = {})
|
22
|
+
filename = self.grammar_path(options)
|
23
|
+
|
24
|
+
@@grammar ||= {}
|
25
|
+
@@grammar[filename] ||= YAML.load_file(filename)[self.locale(options)]
|
26
|
+
@@grammar[filename]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the default locale for the system.
|
30
|
+
def self.default_locale
|
31
|
+
default = ::I18n.default_locale.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the locale passed through options if that locale exists, otherwise
|
35
|
+
# it returns the default locale.
|
36
|
+
def self.locale(options = {})
|
37
|
+
options ||= {}
|
38
|
+
|
39
|
+
locale = options[:locale].to_s || ::I18n.locale.to_s
|
40
|
+
|
41
|
+
default = ::I18n.default_locale.to_s
|
42
|
+
|
43
|
+
# Use the system default locale and then English as fallback
|
44
|
+
filename = ::File.join(::File.dirname(__FILE__), 'locales', locale, 'grammar.yml')
|
45
|
+
unless ::File.exist? filename
|
46
|
+
locale = default
|
47
|
+
filename = ::File.join(::File.dirname(__FILE__), 'locales', locale, 'grammar.yml')
|
48
|
+
unless ::File.exist? filename
|
49
|
+
locale = "en"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
locale
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the path to the grammar descriptions for the given locale or the
|
57
|
+
# default locale if none is given or the given locale cannot be found.
|
58
|
+
def self.grammar_path(options = {})
|
59
|
+
::File.join(::File.dirname(__FILE__), 'locales', self.locale(options), 'grammar.yml')
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the full list of rules for the given locale.
|
63
|
+
def self.rules(options = {})
|
64
|
+
locale = self.locale(options)
|
65
|
+
|
66
|
+
@@rules ||= {}
|
67
|
+
return @@rules[locale] if @@rules[locale]
|
68
|
+
|
69
|
+
grammar = self.grammar(options)
|
70
|
+
|
71
|
+
rules = []
|
72
|
+
grammar["rules"].each do |rule|
|
73
|
+
if rule["match"] && rule["match"].count > 0 &&
|
74
|
+
!rule["match"][0].is_a?(Array)
|
75
|
+
rule["match"] = [rule["match"]]
|
76
|
+
end
|
77
|
+
|
78
|
+
if rule["do"].match /\$([^\$]+)\$/
|
79
|
+
patterns = rule["do"].split('$')
|
80
|
+
patterns << "" if patterns.length.even?
|
81
|
+
number_of_subrules = (patterns.length - 1)/2
|
82
|
+
subrules = patterns.select.each_with_index { |_,i| i.odd? }
|
83
|
+
|
84
|
+
subrules.map! do |subrule|
|
85
|
+
grammar["subrules"][subrule]
|
86
|
+
end
|
87
|
+
|
88
|
+
subrules = subrules[0].product(*subrules[1..-1])
|
89
|
+
|
90
|
+
subrules.each do |set|
|
91
|
+
new_rule = {}
|
92
|
+
new_rule["for"] = set.map{|subrule|(subrule["for"] || []).dup}.concat(rule["for"]||[]).flatten.uniq
|
93
|
+
|
94
|
+
new_rule["match"] = set.map do |subrule|
|
95
|
+
if subrule["match"] && subrule["match"].count > 0 &&
|
96
|
+
!subrule["match"][0].is_a?(Array)
|
97
|
+
subrule["match"] = [subrule["match"]]
|
98
|
+
end
|
99
|
+
(subrule["match"] || [[]]).dup
|
100
|
+
end.inject(&:concat).concat(rule["match"]||[[]]).uniq.select{|e|!e.empty?}
|
101
|
+
|
102
|
+
new_rule["do"] = ""
|
103
|
+
patterns.each_with_index do |pattern, i|
|
104
|
+
if i.even?
|
105
|
+
new_rule["do"] << pattern
|
106
|
+
else
|
107
|
+
new_rule["do"] << set[(i-1)/2]["do"]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
rules << new_rule
|
111
|
+
end
|
112
|
+
else
|
113
|
+
rule["for"] ||= []
|
114
|
+
rule["match"] ||= [[]]
|
115
|
+
rules << rule
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
@@rules[locale] = rules
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|