/
i18n.rb
166 lines (144 loc) Β· 5.9 KB
/
i18n.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# See Pagy::I18n API documentation https://ddnexus.github.io/pagy/docs/api/i18n
# frozen_string_literal: true
require 'yaml'
class Pagy
# Pagy i18n implementation, compatible with the I18n gem, just a lot faster and lighter
module I18n
extend self
# Pluralization rules
module P11n
# Pluralization variables
from0to1 = (0..1).to_a.freeze
from2to4 = (2..4).to_a.freeze
from3to10 = (3..10).to_a.freeze
from5to9 = (5..9).to_a.freeze
from11to14 = (11..14).to_a.freeze
from11to99 = (11..99).to_a.freeze
from12to14 = (12..14).to_a.freeze
from0to1_from5to9 = from0to1 + from5to9
# Store the proc defining each pluralization RULE
# Logic adapted from https://github.com/svenfuchs/rails-i18n
RULE = {
arabic:
lambda do |n = 0|
mod100 = n % 100
case
when n == 0 then 'zero' # rubocop:disable Style/NumericPredicate
when n == 1 then 'one'
when n == 2 then 'two'
when from3to10.include?(mod100) then 'few'
when from11to99.include?(mod100) then 'many'
else 'other'
end
end,
east_slavic:
lambda do |n = 0|
mod10 = n % 10
mod100 = n % 100
case
when mod10 == 1 && mod100 != 11 then 'one'
when from2to4.include?(mod10) && !from12to14.include?(mod100) then 'few'
when mod10 == 0 || from5to9.include?(mod10) || from11to14.include?(mod100) then 'many' # rubocop:disable Style/NumericPredicate
else 'other'
end
end,
one_other:
->(n) { n == 1 ? 'one' : 'other' }, # default RULE
one_two_other:
lambda do |n|
case n
when 1 then 'one'
when 2 then 'two'
else 'other'
end
end,
one_upto_two_other:
->(n) { n && n >= 0 && n < 2 ? 'one' : 'other' },
other:
->(*) { 'other' },
polish:
lambda do |n = 0|
mod10 = n % 10
mod100 = n % 100
case
when n == 1 then 'one'
when from2to4.include?(mod10) && !from12to14.include?(mod100) then 'few'
when from0to1_from5to9.include?(mod10) || from12to14.include?(mod100) then 'many'
else 'other'
end
end,
west_slavic:
lambda do |n|
case n
when 1 then 'one'
when *from2to4 then 'few'
else 'other'
end
end
}.freeze
# Store the RULE to apply to each LOCALE
# the :one_other RULE is the default for locales missing from this list
LOCALE = Hash.new(RULE[:one_other]).tap do |hash|
hash['ar'] = RULE[:arabic]
hash['be'] = RULE[:east_slavic]
hash['bs'] = RULE[:east_slavic]
hash['cs'] = RULE[:west_slavic]
hash['id'] = RULE[:other]
hash['fr'] = RULE[:one_upto_two_other]
hash['hr'] = RULE[:east_slavic]
hash['ja'] = RULE[:other]
hash['km'] = RULE[:other]
hash['ko'] = RULE[:other]
hash['pl'] = RULE[:polish]
hash['ru'] = RULE[:east_slavic]
hash['sr'] = RULE[:east_slavic]
hash['sv'] = RULE[:one_two_other]
hash['sv-SE'] = RULE[:one_two_other]
hash['tr'] = RULE[:other]
hash['uk'] = RULE[:east_slavic]
hash['vi'] = RULE[:other]
hash['zh-CN'] = RULE[:other]
hash['zh-HK'] = RULE[:other]
hash['zh-TW'] = RULE[:other]
end.freeze
end
# Stores the i18n DATA structure for each loaded locale
# default on the first locale DATA
DATA = Hash.new { |hash, _| hash.first[1] }
private
# Create a flat hash with dotted notation keys
def flatten(initial, prefix = '')
initial.each.reduce({}) do |hash, (key, value)|
hash.merge!(value.is_a?(Hash) ? flatten(value, "#{prefix}#{key}.") : { "#{prefix}#{key}" => value })
end
end
# Build the DATA hash out of the passed locales
def build(*locales)
locales.each do |locale|
locale[:filepath] ||= Pagy.root.join('locales', "#{locale[:locale]}.yml")
locale[:pluralize] ||= P11n::LOCALE[locale[:locale]]
dictionary = YAML.safe_load(File.read(locale[:filepath], encoding: 'UTF-8'))
raise I18nError, %(expected :locale "#{locale[:locale]}" not found in :filepath "#{locale[:filepath].inspect}") \
unless dictionary.key?(locale[:locale])
DATA[locale[:locale]] = [flatten(dictionary[locale[:locale]]), locale[:pluralize]]
end
end
# Build the default at require time
build(locale: 'en')
public
# Public method to configure the locales: overrides the default, build the DATA and freezes it
def load(*locales)
DATA.clear
build(*locales)
DATA.freeze
end
# Translate and pluralize the key with the locale DATA
def translate(locale, key, opts = {})
data, pluralize = DATA[locale]
translation = data[key] || (opts[:count] && data[key += ".#{pluralize.call(opts[:count])}"]) \
or return %([translation missing: "#{key}"])
translation.gsub(/%{[^}]+?}/) { |match| opts[:"#{match[2..-2]}"] || match }
end
alias t translate
end
end