@quenty/clienttranslator 6.3.1 → 7.0.0
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.
- package/CHANGELOG.md +11 -0
- package/package.json +6 -6
- package/src/Client/JSONTranslator.lua +8 -3
- package/src/Client/NumberLocalizationUtils.lua +317 -0
- package/src/Client/NumberLocalizationUtils.spec.lua +110 -0
- package/src/Client/RoundingBehaviourTypes.lua +12 -0
- package/src/Shared/JsonToLocalizationTable.lua +42 -14
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [7.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/clienttranslator@6.3.1...@quenty/clienttranslator@7.0.0) (2022-08-14)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add NumberLocalizationUtils based upon Roblox's localization code. ([89e75db](https://github.com/Quenty/NevermoreEngine/commit/89e75db2d1c69c2030568290195a5a9bbba5517a))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [6.3.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/clienttranslator@6.3.0...@quenty/clienttranslator@6.3.1) (2022-08-11)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package @quenty/clienttranslator
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/clienttranslator",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Gets local translator for player",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -25,16 +25,16 @@
|
|
|
25
25
|
"Quenty"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@quenty/blend": "^
|
|
29
|
-
"@quenty/instanceutils": "^
|
|
28
|
+
"@quenty/blend": "^5.0.0",
|
|
29
|
+
"@quenty/instanceutils": "^6.0.0",
|
|
30
30
|
"@quenty/loader": "^5.0.0",
|
|
31
31
|
"@quenty/maid": "^2.4.0",
|
|
32
32
|
"@quenty/promise": "^5.1.0",
|
|
33
|
-
"@quenty/pseudolocalize": "^
|
|
34
|
-
"@quenty/rx": "^
|
|
33
|
+
"@quenty/pseudolocalize": "^3.0.0",
|
|
34
|
+
"@quenty/rx": "^6.0.0"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "dbb62609f980983cc32da90acfef13e30ed41113"
|
|
40
40
|
}
|
|
@@ -20,13 +20,14 @@ local RxInstanceUtils = require("RxInstanceUtils")
|
|
|
20
20
|
|
|
21
21
|
local JSONTranslator = {}
|
|
22
22
|
JSONTranslator.ClassName = "JSONTranslator"
|
|
23
|
+
JSONTranslator.ServiceName = "JSONTranslator"
|
|
23
24
|
JSONTranslator.__index = JSONTranslator
|
|
24
25
|
|
|
25
26
|
--[=[
|
|
26
27
|
Constructs a new JSONTranslator from the given args.
|
|
27
28
|
|
|
28
29
|
```lua
|
|
29
|
-
local translator = JSONTranslator.new("en", {
|
|
30
|
+
local translator = JSONTranslator.new("MyTranslator", en", {
|
|
30
31
|
actions = {
|
|
31
32
|
respawn = "Respawn {playerName}";
|
|
32
33
|
};
|
|
@@ -40,14 +41,18 @@ JSONTranslator.__index = JSONTranslator
|
|
|
40
41
|
-- assume there is an `en.json` underneath the script with valid JSON.
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
@param translatorName string -- Name of the translator. Used for source.
|
|
43
45
|
@param ... any
|
|
44
46
|
@return JSONTranslator
|
|
45
47
|
]=]
|
|
46
|
-
function JSONTranslator.new(...)
|
|
48
|
+
function JSONTranslator.new(translatorName, ...)
|
|
47
49
|
local self = setmetatable({}, JSONTranslator)
|
|
48
50
|
|
|
51
|
+
assert(type(translatorName) == "string", "Bad translatorName")
|
|
52
|
+
self.ServiceName = translatorName
|
|
53
|
+
|
|
49
54
|
-- Cache localizaiton table, because it can take 10-20ms to load.
|
|
50
|
-
self._localizationTable = JsonToLocalizationTable.toLocalizationTable(...)
|
|
55
|
+
self._localizationTable = JsonToLocalizationTable.toLocalizationTable(translatorName, ...)
|
|
51
56
|
self._englishTranslator = self._localizationTable:GetTranslator("en")
|
|
52
57
|
self._fallbacks = {}
|
|
53
58
|
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
NumberLocalizationUtils script from Roblox's player scripts in handling the leaderboard.
|
|
3
|
+
|
|
4
|
+
Example locale-sensitive number formatting:
|
|
5
|
+
https://docs.oracle.com/cd/E19455-01/806-0169/overview-9/index.html
|
|
6
|
+
|
|
7
|
+
Locale specification:
|
|
8
|
+
[DECIMAL_SEPARATOR] = string for decimal point, if needed
|
|
9
|
+
[GROUP_DELIMITER] = string for groupings of numbers left of the decimal
|
|
10
|
+
List section = abbreviations for language, in increasing order
|
|
11
|
+
|
|
12
|
+
Missing features in this code:
|
|
13
|
+
- No support for differences in number of digits per GROUP_DELIMITER.
|
|
14
|
+
Some Chinese dialects group by 10000 instead of 1000.
|
|
15
|
+
- No support for variable differences in number of digits per GROUP_DELIMITER.
|
|
16
|
+
Indian natural language groups the first 3 to left of decimal, then every 2 after that.
|
|
17
|
+
|
|
18
|
+
See https://en.wikipedia.org/wiki/Decimal_separator#Digit_grouping
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@class NumberLocalizationUtils
|
|
22
|
+
]=]
|
|
23
|
+
|
|
24
|
+
local require = require(script.Parent.loader).load(script)
|
|
25
|
+
|
|
26
|
+
local RoundingBehaviourTypes = require("RoundingBehaviourTypes")
|
|
27
|
+
|
|
28
|
+
local NumberLocalizationUtils = {}
|
|
29
|
+
|
|
30
|
+
local DEFAULT_LOCALE = "en-us"
|
|
31
|
+
|
|
32
|
+
-- Separator aliases to help avoid spelling errors
|
|
33
|
+
local DECIMAL_SEPARATOR = "decimalSeparator"
|
|
34
|
+
local GROUP_DELIMITER = "groupDelimiter"
|
|
35
|
+
|
|
36
|
+
local localeInfos = {}
|
|
37
|
+
|
|
38
|
+
localeInfos["en-us"] = {
|
|
39
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
40
|
+
[GROUP_DELIMITER] = ",",
|
|
41
|
+
{ 1, "", },
|
|
42
|
+
{ 1e3, "K", },
|
|
43
|
+
{ 1e6, "M", },
|
|
44
|
+
{ 1e9, "B", },
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
localeInfos["es-es"] = {
|
|
48
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
49
|
+
[GROUP_DELIMITER] = ".",
|
|
50
|
+
{ 1, "", },
|
|
51
|
+
{ 1e3, " mil", },
|
|
52
|
+
{ 1e6, " M", },
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
localeInfos["fr-fr"] = {
|
|
56
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
57
|
+
[GROUP_DELIMITER] = " ",
|
|
58
|
+
{ 1, "", },
|
|
59
|
+
{ 1e3, " k", },
|
|
60
|
+
{ 1e6, " M", },
|
|
61
|
+
{ 1e9, " Md", },
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
localeInfos["de-de"] = {
|
|
65
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
66
|
+
[GROUP_DELIMITER] = " ",
|
|
67
|
+
{ 1, "", },
|
|
68
|
+
{ 1e3, " Tsd.", },
|
|
69
|
+
{ 1e6, " Mio.", },
|
|
70
|
+
{ 1e9, " Mrd.", },
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
localeInfos["pt-br"] = {
|
|
74
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
75
|
+
[GROUP_DELIMITER] = ".",
|
|
76
|
+
{ 1, "", },
|
|
77
|
+
{ 1e3, " mil", },
|
|
78
|
+
{ 1e6, " mi", },
|
|
79
|
+
{ 1e9, " bi", },
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
localeInfos["zh-cn"] = {
|
|
83
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
84
|
+
[GROUP_DELIMITER] = ",", -- Chinese commonly uses 3 digit groupings, despite 10000s rule
|
|
85
|
+
{ 1, "", },
|
|
86
|
+
{ 1e3, "千", },
|
|
87
|
+
{ 1e4, "万", },
|
|
88
|
+
{ 1e8, "亿", },
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
localeInfos["zh-cjv"] = {
|
|
92
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
93
|
+
[GROUP_DELIMITER] = ",",
|
|
94
|
+
{ 1, "", },
|
|
95
|
+
{ 1e3, "千", },
|
|
96
|
+
{ 1e4, "万", },
|
|
97
|
+
{ 1e8, "亿", },
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
localeInfos["zh-tw"] = {
|
|
101
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
102
|
+
[GROUP_DELIMITER] = ",",
|
|
103
|
+
{ 1, "", },
|
|
104
|
+
{ 1e3, "千", },
|
|
105
|
+
{ 1e4, "萬", },
|
|
106
|
+
{ 1e8, "億", },
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
localeInfos["ko-kr"] = {
|
|
110
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
111
|
+
[GROUP_DELIMITER] = ",",
|
|
112
|
+
{ 1, "", },
|
|
113
|
+
{ 1e3, "천", },
|
|
114
|
+
{ 1e4, "만", },
|
|
115
|
+
{ 1e8, "억", },
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
localeInfos["ja-jp"] = {
|
|
119
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
120
|
+
[GROUP_DELIMITER] = ",",
|
|
121
|
+
{ 1, "", },
|
|
122
|
+
{ 1e3, "千", },
|
|
123
|
+
{ 1e4, "万", },
|
|
124
|
+
{ 1e8, "億", },
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
localeInfos["it-it"] = {
|
|
128
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
129
|
+
[GROUP_DELIMITER] = " ",
|
|
130
|
+
{ 1, "", },
|
|
131
|
+
{ 1e3, " mila", },
|
|
132
|
+
{ 1e6, " Mln", },
|
|
133
|
+
{ 1e9, " Mld", },
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
localeInfos["ru-ru"] = {
|
|
137
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
138
|
+
[GROUP_DELIMITER] = ".",
|
|
139
|
+
{ 1, "", },
|
|
140
|
+
{ 1e3, " тыс", },
|
|
141
|
+
{ 1e6, " млн", },
|
|
142
|
+
{ 1e9, " млрд", },
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
localeInfos["id-id"] = {
|
|
146
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
147
|
+
[GROUP_DELIMITER] = ".",
|
|
148
|
+
{ 1, "", },
|
|
149
|
+
{ 1e3, " rb", },
|
|
150
|
+
{ 1e6, " jt", },
|
|
151
|
+
{ 1e9, " M", },
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
localeInfos["vi-vn"] = {
|
|
155
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
156
|
+
[GROUP_DELIMITER] = " ",
|
|
157
|
+
{ 1, "", },
|
|
158
|
+
{ 1e3, " N", },
|
|
159
|
+
{ 1e6, " Tr", },
|
|
160
|
+
{ 1e9, " T", },
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
localeInfos["th-th"] = {
|
|
164
|
+
[DECIMAL_SEPARATOR] = ".",
|
|
165
|
+
[GROUP_DELIMITER] = ",",
|
|
166
|
+
{ 1, "", },
|
|
167
|
+
{ 1e3, " พ", },
|
|
168
|
+
{ 1e4, " ม", },
|
|
169
|
+
{ 1e5, " ส", },
|
|
170
|
+
{ 1e6, " ล", },
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
localeInfos["tr-tr"] = {
|
|
174
|
+
[DECIMAL_SEPARATOR] = ",",
|
|
175
|
+
[GROUP_DELIMITER] = ".",
|
|
176
|
+
{ 1, "", },
|
|
177
|
+
{ 1e3, " B", },
|
|
178
|
+
{ 1e6, " Mn", },
|
|
179
|
+
{ 1e9, " Mr", },
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
-- Aliases for languages that use the same mappings.
|
|
183
|
+
localeInfos["en-gb"] = localeInfos["en-us"]
|
|
184
|
+
localeInfos["es-mx"] = localeInfos["es-es"]
|
|
185
|
+
|
|
186
|
+
local function findDecimalPointIndex(numberStr)
|
|
187
|
+
return string.find(numberStr, "%.") or #numberStr + 1
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
-- Find the base 10 offset needed to make 0.1 <= abs(number) < 1
|
|
191
|
+
local function findDecimalOffset(number)
|
|
192
|
+
if number == 0 then
|
|
193
|
+
return 0
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
local offsetToOnesRange = math.floor(math.log10(math.abs(number)))
|
|
197
|
+
return -(offsetToOnesRange + 1) -- Offset one more (or less) digit
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
local function roundToSignificantDigits(number, significantDigits, roundingBehaviourType)
|
|
201
|
+
local offset = findDecimalOffset(number)
|
|
202
|
+
local multiplier = 10^(significantDigits + offset)
|
|
203
|
+
local significand
|
|
204
|
+
if roundingBehaviourType == RoundingBehaviourTypes.Truncate then
|
|
205
|
+
significand = math.modf(number * multiplier)
|
|
206
|
+
else
|
|
207
|
+
significand = math.floor(number * multiplier + 0.5)
|
|
208
|
+
end
|
|
209
|
+
return significand / multiplier;
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
local function addGroupDelimiters(numberStr, delimiter)
|
|
213
|
+
local formatted = numberStr
|
|
214
|
+
local delimiterSubStr = string.format("%%1%s%%2", delimiter)
|
|
215
|
+
while true do
|
|
216
|
+
local lFormatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", delimiterSubStr)
|
|
217
|
+
formatted = lFormatted
|
|
218
|
+
if k == 0 then
|
|
219
|
+
break
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
return formatted
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
local function findDenominationEntry(localeInfo, number, roundingBehaviourType)
|
|
226
|
+
local denominationEntry = localeInfo[1] -- Default to base denominations
|
|
227
|
+
local absOfNumber = math.abs(number)
|
|
228
|
+
for i = #localeInfo, 2, -1 do
|
|
229
|
+
local entry = localeInfo[i]
|
|
230
|
+
local baseValue
|
|
231
|
+
if roundingBehaviourType == RoundingBehaviourTypes.Truncate then
|
|
232
|
+
baseValue = entry[1]
|
|
233
|
+
else
|
|
234
|
+
baseValue = entry[1] - (localeInfo[i - 1][1]) / 2
|
|
235
|
+
end
|
|
236
|
+
if baseValue <= absOfNumber then
|
|
237
|
+
denominationEntry = entry
|
|
238
|
+
break
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
return denominationEntry
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
function NumberLocalizationUtils.localize(number, locale)
|
|
245
|
+
if number == 0 then
|
|
246
|
+
return "0"
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
local localeInfo = localeInfos[locale]
|
|
250
|
+
if not localeInfo then
|
|
251
|
+
localeInfo = localeInfos[DEFAULT_LOCALE]
|
|
252
|
+
warn(string.format("[NumberLocalizationUtils] - Locale not found: '%s', reverting to '%s' instead.",
|
|
253
|
+
tostring(locale), DEFAULT_LOCALE))
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
if localeInfo.groupDelimiter then
|
|
257
|
+
return addGroupDelimiters(number, localeInfo.groupDelimiter)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
return number
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
function NumberLocalizationUtils.abbreviate(number, locale, roundingBehaviourType, numSignificantDigits)
|
|
264
|
+
if number == 0 then
|
|
265
|
+
return "0"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
if roundingBehaviourType == nil then
|
|
269
|
+
roundingBehaviourType = RoundingBehaviourTypes.RoundToClosest
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
if numSignificantDigits == nil then
|
|
273
|
+
numSignificantDigits = 3
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
local localeInfo = localeInfos[locale]
|
|
277
|
+
if not localeInfo then
|
|
278
|
+
localeInfo = localeInfos[DEFAULT_LOCALE]
|
|
279
|
+
warn(string.format("[NumberLocalizationUtils] - Locale not found: '%s', reverting to '%s' instead.",
|
|
280
|
+
tostring(locale), DEFAULT_LOCALE))
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
-- select which denomination we are going to use
|
|
284
|
+
local denominationEntry = findDenominationEntry(localeInfo, number, roundingBehaviourType)
|
|
285
|
+
local baseValue = denominationEntry[1]
|
|
286
|
+
local symbol = denominationEntry[2]
|
|
287
|
+
|
|
288
|
+
-- Round to required significant digits
|
|
289
|
+
local significantQuotient = roundToSignificantDigits(number / baseValue, numSignificantDigits, roundingBehaviourType)
|
|
290
|
+
|
|
291
|
+
-- trim to 1 decimal point
|
|
292
|
+
local trimmedQuotient
|
|
293
|
+
if roundingBehaviourType == RoundingBehaviourTypes.Truncate then
|
|
294
|
+
trimmedQuotient = math.modf(significantQuotient * 10) / 10
|
|
295
|
+
else
|
|
296
|
+
trimmedQuotient = math.floor(significantQuotient * 10 + 0.5) / 10
|
|
297
|
+
end
|
|
298
|
+
local trimmedQuotientString = tostring(trimmedQuotient)
|
|
299
|
+
|
|
300
|
+
-- Split the string into integer and fraction parts
|
|
301
|
+
local decimalPointIndex = findDecimalPointIndex(trimmedQuotientString)
|
|
302
|
+
local integerPart = string.sub(trimmedQuotientString, 1, decimalPointIndex - 1)
|
|
303
|
+
local fractionPart = string.sub(trimmedQuotientString, decimalPointIndex + 1, #trimmedQuotientString)
|
|
304
|
+
|
|
305
|
+
-- Add group delimiters to integer part
|
|
306
|
+
if localeInfo.groupDelimiter then
|
|
307
|
+
integerPart = addGroupDelimiters(integerPart, localeInfo.groupDelimiter)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
if #fractionPart > 0 then
|
|
311
|
+
return integerPart .. localeInfo.decimalSeparator .. fractionPart .. symbol
|
|
312
|
+
else
|
|
313
|
+
return integerPart .. symbol
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
return NumberLocalizationUtils
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
local require = require(script.Parent.loader).load(script)
|
|
2
|
+
|
|
3
|
+
local NumberLocalizationUtils = require("NumberLocalizationUtils")
|
|
4
|
+
local RoundingBehaviour = require("RoundingBehaviourTypes")
|
|
5
|
+
|
|
6
|
+
return function()
|
|
7
|
+
|
|
8
|
+
local function checkLocale(locale, responseMapping)
|
|
9
|
+
for input, output in pairs(responseMapping) do
|
|
10
|
+
expect(NumberLocalizationUtils.localize(input, locale)).to.equal(output)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
local function checkValid_en_zh(locale)
|
|
15
|
+
checkLocale(locale, {
|
|
16
|
+
[0] = "0",
|
|
17
|
+
[1] = "1",
|
|
18
|
+
[25] = "25",
|
|
19
|
+
[364] = "364",
|
|
20
|
+
[4120] = "4,120",
|
|
21
|
+
[57860] = "57,860",
|
|
22
|
+
[624390] = "624,390",
|
|
23
|
+
[7857000] = "7,857,000",
|
|
24
|
+
[-12345678] = "-12,345,678",
|
|
25
|
+
[23987.45678] = "23,987.45678",
|
|
26
|
+
[-12.3456] = "-12.3456",
|
|
27
|
+
[-23987.45678] = "-23,987.45678",
|
|
28
|
+
})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe("NumberLocalizationUtils.localize", function()
|
|
32
|
+
-- it("should default to en-us when locale is not recognized", function()
|
|
33
|
+
-- local logs = Logging.capture(function()
|
|
34
|
+
-- checkValid_en_zh("bad_locale")
|
|
35
|
+
-- end)
|
|
36
|
+
-- expect(string.match(logs.warnings[1], "^Warning: Locale not found:") ~= nil).to.equal(true)
|
|
37
|
+
-- end)
|
|
38
|
+
|
|
39
|
+
-- it("should default to en-us when locale is nil", function()
|
|
40
|
+
-- local logs = Logging.capture(function()
|
|
41
|
+
-- checkValid_en_zh(nil)
|
|
42
|
+
-- end)
|
|
43
|
+
-- expect(string.match(logs.warnings[1], "^Warning: Locale not found:") ~= nil).to.equal(true)
|
|
44
|
+
-- end)
|
|
45
|
+
|
|
46
|
+
-- it("should default to en-us when locale is empty", function()
|
|
47
|
+
-- local logs = Logging.capture(function()
|
|
48
|
+
-- checkValid_en_zh("")
|
|
49
|
+
-- end)
|
|
50
|
+
-- expect(string.match(logs.warnings[1], "^Warning: Locale not found:") ~= nil).to.equal(true)
|
|
51
|
+
-- end)
|
|
52
|
+
|
|
53
|
+
it("should localize correctly. (en-us)", function()
|
|
54
|
+
checkValid_en_zh("en-us")
|
|
55
|
+
end)
|
|
56
|
+
|
|
57
|
+
it("should localize correctly. (en-gb)", function()
|
|
58
|
+
checkValid_en_zh("en-gb")
|
|
59
|
+
end)
|
|
60
|
+
|
|
61
|
+
it("should localize correctly. (zh-cn)", function()
|
|
62
|
+
checkValid_en_zh("zh-cn")
|
|
63
|
+
end)
|
|
64
|
+
|
|
65
|
+
it("should localize correctly. (zh-tw)", function()
|
|
66
|
+
checkValid_en_zh("zh-tw")
|
|
67
|
+
end)
|
|
68
|
+
end)
|
|
69
|
+
|
|
70
|
+
describe("NumberLocalizationUtils.abbreviate", function()
|
|
71
|
+
it("should round towards zero when using RoundingBehaviour.Truncate", function()
|
|
72
|
+
local roundToZeroMap = {
|
|
73
|
+
[0] = "0",
|
|
74
|
+
[1] = "1",
|
|
75
|
+
[25] = "25",
|
|
76
|
+
[364] = "364",
|
|
77
|
+
[4120] = "4.1K",
|
|
78
|
+
[57860] = "57.8K",
|
|
79
|
+
[624390] = "624K",
|
|
80
|
+
[999999] = "999K",
|
|
81
|
+
[7857000] = "7.8M",
|
|
82
|
+
[8e7] = "80M",
|
|
83
|
+
[9e8] = "900M",
|
|
84
|
+
[1e9] = "1B",
|
|
85
|
+
[1e12] = "1,000B",
|
|
86
|
+
[-0] = "0",
|
|
87
|
+
[-1] = "-1",
|
|
88
|
+
[-25] = "-25",
|
|
89
|
+
[-364] = "-364",
|
|
90
|
+
[-4120] = "-4.1K",
|
|
91
|
+
[-57860] = "-57.8K",
|
|
92
|
+
[-624390] = "-624K",
|
|
93
|
+
[-999999] = "-999K",
|
|
94
|
+
[-7857000] = "-7.8M",
|
|
95
|
+
[-8e7] = "-80M",
|
|
96
|
+
[-9e8] = "-900M",
|
|
97
|
+
[-1e9] = "-1B",
|
|
98
|
+
[-1e12] = "-1,000B",
|
|
99
|
+
[1.1] = "1.1",
|
|
100
|
+
[1499.99] = "1.4K",
|
|
101
|
+
[-1.1] = "-1.1",
|
|
102
|
+
[-1499.99] = "-1.4K",
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for input, output in pairs(roundToZeroMap) do
|
|
106
|
+
expect(NumberLocalizationUtils.abbreviate(input, "en-us", RoundingBehaviour.Truncate)).to.equal(output)
|
|
107
|
+
end
|
|
108
|
+
end)
|
|
109
|
+
end)
|
|
110
|
+
end
|
|
@@ -15,11 +15,13 @@ local LOCALIZATION_TABLE_NAME = "GeneratedJSONTable"
|
|
|
15
15
|
|
|
16
16
|
--[[
|
|
17
17
|
Recursively iterates through the object to construct strings and add it to the localization table
|
|
18
|
+
|
|
19
|
+
@param localizationTable LocalizationTable
|
|
18
20
|
@param localeId string -- The localizationid to add
|
|
19
21
|
@param baseKey string -- the key to add
|
|
20
22
|
@param object any -- The value to iterate over
|
|
21
23
|
]]
|
|
22
|
-
local function recurseAdd(localizationTable, localeId, baseKey, object)
|
|
24
|
+
local function recurseAdd(localizationTable, localeId, baseKey, object, tableName)
|
|
23
25
|
if baseKey ~= "" then
|
|
24
26
|
baseKey = baseKey .. "."
|
|
25
27
|
end
|
|
@@ -27,10 +29,13 @@ local function recurseAdd(localizationTable, localeId, baseKey, object)
|
|
|
27
29
|
for index, value in pairs(object) do
|
|
28
30
|
local key = baseKey .. index
|
|
29
31
|
if type(value) == "table" then
|
|
30
|
-
recurseAdd(localizationTable, localeId, key, value)
|
|
32
|
+
recurseAdd(localizationTable, localeId, key, value, tableName)
|
|
31
33
|
elseif type(value) == "string" then
|
|
32
34
|
local source = ""
|
|
33
|
-
|
|
35
|
+
|
|
36
|
+
-- Guarantee the context is unique. This is important because Roblox will not
|
|
37
|
+
-- allow something with the same source without a differing context value.
|
|
38
|
+
local context = tableName .. "." .. key
|
|
34
39
|
|
|
35
40
|
if localeId == "en" then
|
|
36
41
|
source = value
|
|
@@ -45,6 +50,7 @@ end
|
|
|
45
50
|
|
|
46
51
|
--[=[
|
|
47
52
|
Extracts the locale from the name
|
|
53
|
+
|
|
48
54
|
@param name string -- The name to parse
|
|
49
55
|
@return string -- The locale
|
|
50
56
|
]=]
|
|
@@ -56,6 +62,12 @@ function JsonToLocalizationTable.localeFromName(name)
|
|
|
56
62
|
end
|
|
57
63
|
end
|
|
58
64
|
|
|
65
|
+
--[=[
|
|
66
|
+
Gets or creates the global localization table. If the game isn't running (i.e. test mode), then
|
|
67
|
+
we'll just not parent it.
|
|
68
|
+
|
|
69
|
+
@return string -- The locale
|
|
70
|
+
]=]
|
|
59
71
|
function JsonToLocalizationTable.getOrCreateLocalizationTable()
|
|
60
72
|
local localizationTable = LocalizationService:FindFirstChild(LOCALIZATION_TABLE_NAME)
|
|
61
73
|
|
|
@@ -72,19 +84,23 @@ function JsonToLocalizationTable.getOrCreateLocalizationTable()
|
|
|
72
84
|
end
|
|
73
85
|
|
|
74
86
|
--[=[
|
|
75
|
-
Loads a folder into a localization table
|
|
87
|
+
Loads a folder into a localization table.
|
|
88
|
+
|
|
89
|
+
@param tableName string -- Used for source
|
|
76
90
|
@param folder Folder -- A Roblox folder with StringValues containing JSON, named with the localization in mind
|
|
77
91
|
]=]
|
|
78
|
-
function JsonToLocalizationTable.loadFolder(folder)
|
|
92
|
+
function JsonToLocalizationTable.loadFolder(tableName, folder)
|
|
93
|
+
assert(type(tableName) == "string", "Bad tableName")
|
|
94
|
+
|
|
79
95
|
local localizationTable = JsonToLocalizationTable.getOrCreateLocalizationTable()
|
|
80
96
|
|
|
81
97
|
for _, item in pairs(folder:GetDescendants()) do
|
|
82
98
|
if item:IsA("StringValue") then
|
|
83
99
|
local localeId = JsonToLocalizationTable.localeFromName(item.Name)
|
|
84
|
-
JsonToLocalizationTable.addJsonToTable(localizationTable, localeId, item.Value)
|
|
100
|
+
JsonToLocalizationTable.addJsonToTable(localizationTable, localeId, item.Value, tableName)
|
|
85
101
|
elseif item:IsA("ModuleScript") then
|
|
86
102
|
local localeId = JsonToLocalizationTable.localeFromName(item.Name)
|
|
87
|
-
recurseAdd(localizationTable, localeId, "", require(item))
|
|
103
|
+
recurseAdd(localizationTable, localeId, "", require(item), tableName)
|
|
88
104
|
end
|
|
89
105
|
end
|
|
90
106
|
return localizationTable
|
|
@@ -92,17 +108,21 @@ end
|
|
|
92
108
|
|
|
93
109
|
--[=[
|
|
94
110
|
Extracts the locale from the folder, or a locale and table.
|
|
111
|
+
|
|
112
|
+
@param tableName string -- Used for source
|
|
95
113
|
@param first Instance | string
|
|
96
114
|
@param second table?
|
|
97
115
|
@return LocalizationTable
|
|
98
116
|
]=]
|
|
99
|
-
function JsonToLocalizationTable.toLocalizationTable(first, second)
|
|
117
|
+
function JsonToLocalizationTable.toLocalizationTable(tableName, first, second)
|
|
118
|
+
assert(type(tableName) == "string", "Bad tableName")
|
|
119
|
+
|
|
100
120
|
if typeof(first) == "Instance" then
|
|
101
|
-
local result = JsonToLocalizationTable.loadFolder(first)
|
|
121
|
+
local result = JsonToLocalizationTable.loadFolder(tableName, first)
|
|
102
122
|
-- result.Name = ("JSONTable_%s"):format(first.Name)
|
|
103
123
|
return result
|
|
104
124
|
elseif type(first) == "string" and type(second) == "table" then
|
|
105
|
-
local result = JsonToLocalizationTable.loadTable(first, second)
|
|
125
|
+
local result = JsonToLocalizationTable.loadTable(tableName, first, second)
|
|
106
126
|
return result
|
|
107
127
|
else
|
|
108
128
|
error("Bad args")
|
|
@@ -111,27 +131,35 @@ end
|
|
|
111
131
|
|
|
112
132
|
--[=[
|
|
113
133
|
Extracts the locale from the name
|
|
134
|
+
|
|
135
|
+
@param tableName string -- Used for source
|
|
114
136
|
@param localeId string -- the defaultlocaleId
|
|
115
137
|
@param dataTable table -- Data table to load from
|
|
116
138
|
@return LocalizationTable
|
|
117
139
|
]=]
|
|
118
|
-
function JsonToLocalizationTable.loadTable(localeId, dataTable)
|
|
140
|
+
function JsonToLocalizationTable.loadTable(tableName, localeId, dataTable)
|
|
141
|
+
assert(type(tableName) == "string", "Bad tableName")
|
|
142
|
+
|
|
119
143
|
local localizationTable = JsonToLocalizationTable.getOrCreateLocalizationTable()
|
|
120
144
|
|
|
121
|
-
recurseAdd(localizationTable, localeId, "", dataTable)
|
|
145
|
+
recurseAdd(localizationTable, localeId, "", dataTable, tableName)
|
|
122
146
|
|
|
123
147
|
return localizationTable
|
|
124
148
|
end
|
|
125
149
|
|
|
126
150
|
--[=[
|
|
127
151
|
Adds json to a localization table
|
|
152
|
+
|
|
128
153
|
@param localizationTable LocalizationTable -- The localization table to add to
|
|
129
154
|
@param localeId string -- The localeId to use
|
|
130
155
|
@param json string -- The json to add with
|
|
156
|
+
@param tableName string -- Used for source
|
|
131
157
|
]=]
|
|
132
|
-
function JsonToLocalizationTable.addJsonToTable(localizationTable, localeId, json)
|
|
158
|
+
function JsonToLocalizationTable.addJsonToTable(localizationTable, localeId, json, tableName)
|
|
159
|
+
assert(type(tableName) == "string", "Bad tableName")
|
|
160
|
+
|
|
133
161
|
local decodedTable = HttpService:JSONDecode(json)
|
|
134
|
-
recurseAdd(localizationTable, localeId, "", decodedTable)
|
|
162
|
+
recurseAdd(localizationTable, localeId, "", decodedTable, tableName)
|
|
135
163
|
end
|
|
136
164
|
|
|
137
165
|
return JsonToLocalizationTable
|