@quenty/clienttranslator 14.19.0 → 14.19.1

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 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
+ ## [14.19.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/clienttranslator@14.19.0...@quenty/clienttranslator@14.19.1) (2025-04-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Add types to packages ([2374fb2](https://github.com/Quenty/NevermoreEngine/commit/2374fb2b043cfbe0e9b507b3316eec46a4e353a0))
12
+
13
+
14
+
15
+
16
+
6
17
  # [14.19.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/clienttranslator@14.18.2...@quenty/clienttranslator@14.19.0) (2025-04-02)
7
18
 
8
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/clienttranslator",
3
- "version": "14.19.0",
3
+ "version": "14.19.1",
4
4
  "description": "Gets local translator for player",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -25,20 +25,20 @@
25
25
  "Quenty"
26
26
  ],
27
27
  "dependencies": {
28
- "@quenty/blend": "^12.18.0",
29
- "@quenty/instanceutils": "^13.17.0",
30
- "@quenty/loader": "^10.8.0",
31
- "@quenty/maid": "^3.4.0",
32
- "@quenty/promise": "^10.10.1",
33
- "@quenty/promisemaid": "^5.10.1",
34
- "@quenty/pseudolocalize": "^3.4.0",
35
- "@quenty/rx": "^13.17.0",
36
- "@quenty/string": "^3.3.1",
37
- "@quenty/table": "^3.7.1",
38
- "@quenty/valueobject": "^13.17.0"
28
+ "@quenty/blend": "^12.18.1",
29
+ "@quenty/instanceutils": "^13.17.1",
30
+ "@quenty/loader": "^10.8.1",
31
+ "@quenty/maid": "^3.4.1",
32
+ "@quenty/promise": "^10.10.2",
33
+ "@quenty/promisemaid": "^5.10.2",
34
+ "@quenty/pseudolocalize": "^3.4.1",
35
+ "@quenty/rx": "^13.17.1",
36
+ "@quenty/string": "^3.3.2",
37
+ "@quenty/table": "^3.7.2",
38
+ "@quenty/valueobject": "^13.17.1"
39
39
  },
40
40
  "publishConfig": {
41
41
  "access": "public"
42
42
  },
43
- "gitHead": "e8ea56930e65322fcffc05a1556d5df988068f0b"
43
+ "gitHead": "78c3ac0ab08dd18085b6e6e6e4f745e76ed99f68"
44
44
  }
@@ -21,7 +21,7 @@ function LocalizationEntryParserUtils.decodeFromInstance(tableName, sourceLocale
21
21
  local lookupTable = {}
22
22
  local baseKey = ""
23
23
 
24
- for _, descendant in pairs(folder:GetDescendants()) do
24
+ for _, descendant in folder:GetDescendants() do
25
25
  if descendant:IsA("StringValue") then
26
26
  local localeId = LocalizationEntryParserUtils._parseLocaleFromName(descendant.Name)
27
27
  local decodedTable = HttpService:JSONDecode(descendant.Value)
@@ -36,7 +36,7 @@ function LocalizationEntryParserUtils.decodeFromInstance(tableName, sourceLocale
36
36
  end
37
37
 
38
38
  local results = {}
39
- for _, item in pairs(lookupTable) do
39
+ for _, item in lookupTable do
40
40
  table.insert(results, item)
41
41
  end
42
42
  return results
@@ -50,10 +50,17 @@ function LocalizationEntryParserUtils.decodeFromTable(tableName, localeId, dataT
50
50
  local lookupTable = {}
51
51
 
52
52
  local baseKey = ""
53
- LocalizationEntryParserUtils._parseTableToResultsList(lookupTable, localeId, localeId, baseKey, dataTable, tableName)
53
+ LocalizationEntryParserUtils._parseTableToResultsList(
54
+ lookupTable,
55
+ localeId,
56
+ localeId,
57
+ baseKey,
58
+ dataTable,
59
+ tableName
60
+ )
54
61
 
55
62
  local results = {}
56
- for _, item in pairs(lookupTable) do
63
+ for _, item in lookupTable do
57
64
  table.insert(results, item)
58
65
  end
59
66
  return results
@@ -75,7 +82,7 @@ function LocalizationEntryParserUtils._parseTableToResultsList(lookupTable, sour
75
82
  assert(type(dataTable) == "table", "Bad dataTable")
76
83
  assert(type(tableName) == "string", "Bad tableName")
77
84
 
78
- for index, text in pairs(dataTable) do
85
+ for index, text in dataTable do
79
86
  local key = baseKey .. index
80
87
  if type(text) == "table" then
81
88
  LocalizationEntryParserUtils._parseTableToResultsList(lookupTable, sourceLocaleId, localeId, key .. ".", text, tableName)
@@ -92,9 +99,9 @@ function LocalizationEntryParserUtils._parseTableToResultsList(lookupTable, sour
92
99
  Values = {
93
100
  [localeId] = text;
94
101
  };
95
- };
102
+ }
96
103
 
97
- lookupTable[key] = found;
104
+ lookupTable[key] = found
98
105
  end
99
106
 
100
107
  -- Ensure assignment
@@ -1,3 +1,4 @@
1
+ --!strict
1
2
  --[=[
2
3
  Utility function that loads a translator from a folder or a table.
3
4
 
@@ -27,16 +28,33 @@ local TranslationKeyUtils = require("TranslationKeyUtils")
27
28
  local TranslatorService = require("TranslatorService")
28
29
  local ValueObject = require("ValueObject")
29
30
  local NumberLocalizationUtils = require("NumberLocalizationUtils")
31
+ local _ServiceBag = require("ServiceBag")
32
+ local _Observable = require("Observable")
33
+ local _Promise = require("Promise")
30
34
 
31
35
  local JSONTranslator = {}
32
36
  JSONTranslator.ClassName = "JSONTranslator"
33
37
  JSONTranslator.ServiceName = "JSONTranslator"
34
38
  JSONTranslator.__index = JSONTranslator
35
39
 
40
+ export type JSONTranslator = typeof(setmetatable(
41
+ {} :: {
42
+ _maid: Maid.Maid,
43
+ _serviceBag: _ServiceBag.ServiceBag,
44
+ _translatorService: TranslatorService.TranslatorService,
45
+ _translatorName: string,
46
+ _entries: { [string]: any },
47
+ _localizationTable: any,
48
+ _localTranslator: ValueObject.ValueObject<any>,
49
+ _sourceTranslator: ValueObject.ValueObject<any>,
50
+ },
51
+ JSONTranslator
52
+ ))
53
+
36
54
  --[=[
37
55
  Constructs a new JSONTranslator from the given args.
38
56
 
39
- ```lua
57
+ ```
40
58
  local translator = JSONTranslator.new("MyTranslator", en", {
41
59
  actions = {
42
60
  respawn = "Respawn {playerName}";
@@ -63,7 +81,7 @@ JSONTranslator.__index = JSONTranslator
63
81
  @param dataTable table
64
82
  @return JSONTranslator
65
83
  ]=]
66
- function JSONTranslator.new(translatorName, localeId, dataTable)
84
+ function JSONTranslator.new(translatorName: string, localeId: string, dataTable): JSONTranslator
67
85
  assert(type(translatorName) == "string", "Bad translatorName")
68
86
 
69
87
  local self = setmetatable({}, JSONTranslator)
@@ -81,12 +99,12 @@ function JSONTranslator.new(translatorName, localeId, dataTable)
81
99
  error("Must pass a localeId and dataTable")
82
100
  end
83
101
 
84
- return self
102
+ return self :: any
85
103
  end
86
104
 
87
- function JSONTranslator:Init(serviceBag)
105
+ function JSONTranslator.Init(self: JSONTranslator, serviceBag: _ServiceBag.ServiceBag)
88
106
  self._serviceBag = assert(serviceBag, "No serviceBag")
89
- self._translatorService = self._serviceBag:GetService(TranslatorService)
107
+ self._translatorService = self._serviceBag:GetService(TranslatorService) :: any
90
108
 
91
109
  self._maid = Maid.new()
92
110
  self._localTranslator = self._maid:Add(ValueObject.new(nil))
@@ -94,8 +112,8 @@ function JSONTranslator:Init(serviceBag)
94
112
 
95
113
  self._localizationTable = self._translatorService:GetLocalizationTable()
96
114
 
97
- for _, item in pairs(self._entries) do
98
- for localeId, text in pairs(item.Values) do
115
+ for _, item in self._entries do
116
+ for localeId, text in item.Values do
99
117
  self._localizationTable:SetEntryValue(item.Key, item.Source, item.Context, localeId, text)
100
118
  end
101
119
  self._localizationTable:SetEntryExample(item.Key, item.Source, item.Context, item.Example)
@@ -105,84 +123,107 @@ function JSONTranslator:Init(serviceBag)
105
123
  self._maid:GiveTask(self._translatorService:ObserveLocaleId():Subscribe(function(localeId)
106
124
  self._localTranslator.Value = self._localizationTable:GetTranslator(localeId)
107
125
  end))
108
- self._maid:GiveTask(RxInstanceUtils.observeProperty(self._localizationTable, "SourceLocaleId"):Subscribe(function(localeId)
109
- self._sourceTranslator.Value = self._localizationTable:GetTranslator(localeId)
110
- end))
126
+ self._maid:GiveTask(
127
+ RxInstanceUtils.observeProperty(self._localizationTable, "SourceLocaleId"):Subscribe(function(localeId)
128
+ self._sourceTranslator.Value = self._localizationTable:GetTranslator(localeId)
129
+ end)
130
+ )
111
131
  end
112
132
 
113
- function JSONTranslator:ObserveNumber(number)
133
+ function JSONTranslator.ObserveNumber(self: JSONTranslator, number: number): _Observable.Observable<string>
114
134
  return Rx.combineLatest({
115
- localeId = self:ObserveLocaleId();
116
- number = number;
135
+ localeId = self:ObserveLocaleId(),
136
+ number = number,
117
137
  }):Pipe({
118
138
  Rx.map(function(state)
119
139
  return NumberLocalizationUtils.localize(state.number, state.localeId)
120
- end)
121
- })
140
+ end) :: any,
141
+ }) :: any
122
142
  end
123
143
 
124
- function JSONTranslator:ObserveAbbreviatedNumber(number, roundingBehaviourType, numSignificantDigits)
144
+ function JSONTranslator.ObserveAbbreviatedNumber(
145
+ self: JSONTranslator,
146
+ number: number,
147
+ roundingBehaviourType,
148
+ numSignificantDigits: number?
149
+ )
125
150
  return Rx.combineLatest({
126
- localeId = self:ObserveLocaleId();
127
- roundingBehaviourType = roundingBehaviourType;
128
- numSignificantDigits = numSignificantDigits;
129
- number = number;
151
+ localeId = self:ObserveLocaleId(),
152
+ roundingBehaviourType = roundingBehaviourType,
153
+ numSignificantDigits = numSignificantDigits,
154
+ number = number,
130
155
  }):Pipe({
131
156
  Rx.map(function(state)
132
157
  return NumberLocalizationUtils.abbreviate(
133
158
  state.number,
134
159
  state.localeId,
135
160
  state.roundingBehaviourType,
136
- state.numSignificantDigits)
137
- end)
161
+ state.numSignificantDigits
162
+ )
163
+ end) :: any,
138
164
  })
139
165
  end
140
166
 
141
-
142
167
  --[=[
143
168
  Observes the translated value
144
169
  @param translationKey string
145
170
  @param translationArgs table? -- May have observables (or convertable to observables) in it.
146
171
  @return Observable<string>
147
172
  ]=]
148
- function JSONTranslator:ObserveFormatByKey(translationKey, translationArgs)
149
- assert(self ~= JSONTranslator, "Construct a new version of this class to use it")
173
+ function JSONTranslator.ObserveFormatByKey(
174
+ self: JSONTranslator,
175
+ translationKey: string,
176
+ translationArgs
177
+ ): _Observable.Observable<string>
178
+ assert((self :: any) ~= JSONTranslator, "Construct a new version of this class to use it")
150
179
  assert(type(translationKey) == "string", "Key must be a string")
151
180
 
152
181
  return Rx.combineLatest({
153
- cloudTranslator = self:ObserveTranslator();
154
- translationKey = translationKey;
155
- translationArgs = self:_observeArgs(translationArgs);
182
+ cloudTranslator = self:ObserveTranslator(),
183
+ translationKey = translationKey,
184
+ translationArgs = self:_observeArgs(translationArgs),
156
185
  }):Pipe({
157
- Rx.switchMap(function(mainState)
186
+ Rx.switchMap(function(mainState): any
158
187
  if mainState.cloudTranslator then
159
188
  return self._translatorService:ObserveLocaleId():Pipe({
160
189
  Rx.map(function()
161
- return self:_doTranslation(mainState.cloudTranslator, mainState.translationKey, mainState.translationArgs)
162
- end);
190
+ return self:_doTranslation(
191
+ mainState.cloudTranslator,
192
+ mainState.translationKey,
193
+ mainState.translationArgs
194
+ )
195
+ end) :: any,
163
196
  })
164
197
  end
165
198
 
166
199
  -- Fall back to local or source translator
167
200
  return Rx.combineLatest({
168
- localTranslator = self._localTranslator:Observe();
169
- sourceTranslator = self._sourceTranslator:Observe();
201
+ localTranslator = self._localTranslator:Observe(),
202
+ sourceTranslator = self._sourceTranslator:Observe(),
170
203
  }):Pipe({
171
- Rx.map(function(state)
204
+ Rx.map(function(state): string?
172
205
  if state.localTranslator then
173
- return self:_doTranslation(state.localTranslator, mainState.translationKey, mainState.translationArgs)
206
+ return self:_doTranslation(
207
+ state.localTranslator,
208
+ mainState.translationKey,
209
+ mainState.translationArgs
210
+ )
174
211
  elseif state.sourceTranslator then
175
- return self:_doTranslation(state.sourceTranslator, mainState.translationKey, mainState.translationArgs)
212
+ return self:_doTranslation(
213
+ state.sourceTranslator,
214
+ mainState.translationKey,
215
+ mainState.translationArgs
216
+ )
176
217
  else
177
218
  return nil
178
219
  end
179
- end);
220
+ end) :: any,
180
221
  Rx.where(function(value)
181
222
  return value ~= nil
182
- end);
223
+ end) :: any,
183
224
  })
184
- end)
185
- })
225
+ end) :: any,
226
+ }) :: any
186
227
  end
187
228
 
188
229
  --[=[
@@ -197,8 +238,8 @@ end
197
238
  @param args table?
198
239
  @return Promise<string>
199
240
  ]=]
200
- function JSONTranslator:PromiseFormatByKey(translationKey, args)
201
- assert(self ~= JSONTranslator, "Construct a new version of this class to use it")
241
+ function JSONTranslator.PromiseFormatByKey(self: JSONTranslator, translationKey: string, args)
242
+ assert((self :: any) ~= JSONTranslator, "Construct a new version of this class to use it")
202
243
  assert(type(translationKey) == "string", "Key must be a string")
203
244
 
204
245
  -- Always waits for full translator to be loaded since we only get one shot
@@ -207,11 +248,20 @@ function JSONTranslator:PromiseFormatByKey(translationKey, args)
207
248
  end)
208
249
  end
209
250
 
210
- function JSONTranslator:PromiseTranslator()
251
+ --[=[
252
+ Returns a promise that will resolve once the Roblox translator is loaded from the cloud.
253
+ @return Promise<Translator>
254
+ ]=]
255
+ function JSONTranslator.PromiseTranslator(self: JSONTranslator): _Promise.Promise<Translator>
211
256
  return self._translatorService:PromiseTranslator()
212
257
  end
213
258
 
214
- function JSONTranslator:ObserveTranslator()
259
+ --[=[
260
+ Observes the current Roblox translator for this translator.
261
+
262
+ @return Observable<Translator>
263
+ ]=]
264
+ function JSONTranslator.ObserveTranslator(self: JSONTranslator): _Observable.Observable<Translator>
215
265
  return self._translatorService:ObserveTranslator()
216
266
  end
217
267
 
@@ -220,7 +270,7 @@ end
220
270
 
221
271
  @return Observable<string>
222
272
  ]=]
223
- function JSONTranslator:ObserveLocaleId()
273
+ function JSONTranslator.ObserveLocaleId(self: JSONTranslator): _Observable.Observable<string>
224
274
  return self._translatorService:ObserveLocaleId()
225
275
  end
226
276
 
@@ -235,7 +285,14 @@ end
235
285
  @param localeId string
236
286
  @param text string
237
287
  ]=]
238
- function JSONTranslator:SetEntryValue(translationKey, source, context, localeId, text)
288
+ function JSONTranslator.SetEntryValue(
289
+ self: JSONTranslator,
290
+ translationKey: string,
291
+ source: string,
292
+ context: string,
293
+ localeId: string,
294
+ text: string
295
+ )
239
296
  assert(type(translationKey) == "string", "Bad translationKey")
240
297
  assert(type(source) == "string", "Bad source")
241
298
  assert(type(context) == "string", "Bad context")
@@ -245,18 +302,44 @@ function JSONTranslator:SetEntryValue(translationKey, source, context, localeId,
245
302
  self._localizationTable:SetEntryValue(translationKey, source, context, localeId, text or source)
246
303
 
247
304
  if RunService:IsStudio() then
248
- self._localizationTable:SetEntryValue(translationKey, source, context, PseudoLocalize.getDefaultPseudoLocaleId(), PseudoLocalize.pseudoLocalize(text))
305
+ self._localizationTable:SetEntryValue(
306
+ translationKey,
307
+ source,
308
+ context,
309
+ PseudoLocalize.getDefaultPseudoLocaleId(),
310
+ PseudoLocalize.pseudoLocalize(text)
311
+ )
249
312
  end
250
313
  end
251
314
 
252
- function JSONTranslator:ObserveTranslation(prefix, text, translationArgs)
315
+ --[=[
316
+ Observes a translation key and formats it with the given args.
317
+
318
+ @param prefix string
319
+ @param text string
320
+ @param translationArgs table?
321
+ @return Observable<string>
322
+ ]=]
323
+ function JSONTranslator.ObserveTranslation(
324
+ self: JSONTranslator,
325
+ prefix: string,
326
+ text: string,
327
+ translationArgs
328
+ ): _Observable.Observable<string>
253
329
  assert(type(prefix) == "string", "Bad text")
254
330
  assert(type(text) == "string", "Bad text")
255
331
 
256
332
  return self:ObserveFormatByKey(self:ToTranslationKey(prefix, text), translationArgs)
257
333
  end
258
334
 
259
- function JSONTranslator:ToTranslationKey(prefix, text)
335
+ --[=[
336
+ Converts the given prefix and text into a translation key.
337
+
338
+ @param prefix string
339
+ @param text string
340
+ @return string
341
+ ]=]
342
+ function JSONTranslator.ToTranslationKey(self: JSONTranslator, prefix: string, text: string): string
260
343
  assert(type(prefix) == "string", "Bad text")
261
344
  assert(type(text) == "string", "Bad text")
262
345
 
@@ -274,16 +357,16 @@ end
274
357
 
275
358
  @return string
276
359
  ]=]
277
- function JSONTranslator:GetLocaleId()
360
+ function JSONTranslator.GetLocaleId(self: JSONTranslator): string
278
361
  return self._translatorService:GetLocaleId()
279
362
  end
280
363
 
281
364
  --[=[
282
365
  Gets the localization table the translation is using.
283
366
 
284
- @return LocalizaitonTable
367
+ @return LocalizationTable
285
368
  ]=]
286
- function JSONTranslator:GetLocalizationTable()
369
+ function JSONTranslator.GetLocalizationTable(self: JSONTranslator): LocalizationTable
287
370
  return self._localizationTable
288
371
  end
289
372
 
@@ -291,7 +374,7 @@ end
291
374
  Returns a promise that will resolve once the translator is loaded from the cloud.
292
375
  @return Promise
293
376
  ]=]
294
- function JSONTranslator:PromiseLoaded()
377
+ function JSONTranslator.PromiseLoaded(self: JSONTranslator): _Promise.Promise<()>
295
378
  return self:PromiseTranslator()
296
379
  end
297
380
 
@@ -307,8 +390,8 @@ end
307
390
  @param args table?
308
391
  @return string
309
392
  ]=]
310
- function JSONTranslator:FormatByKey(translationKey, args)
311
- assert(self ~= JSONTranslator, "Construct a new version of this class to use it")
393
+ function JSONTranslator.FormatByKey(self: JSONTranslator, translationKey: string, args): string
394
+ assert((self :: any) ~= JSONTranslator, "Construct a new version of this class to use it")
312
395
  assert(type(translationKey) == "string", "Key must be a string")
313
396
 
314
397
  local translator = self._translatorService:GetTranslator()
@@ -319,24 +402,29 @@ function JSONTranslator:FormatByKey(translationKey, args)
319
402
  return self:_doTranslation(translator, translationKey, args)
320
403
  end
321
404
 
322
- function JSONTranslator:_observeArgs(translationArgs)
405
+ function JSONTranslator._observeArgs(_self: JSONTranslator, translationArgs): _Observable.Observable<any>
323
406
  if translationArgs == nil then
324
407
  return Rx.of(nil)
325
408
  end
326
409
 
327
410
  local args = {}
328
- for argKey, value in pairs(translationArgs) do
411
+ for argKey, value in translationArgs do
329
412
  args[argKey] = Blend.toPropertyObservable(value) or Rx.of(value)
330
413
  end
331
414
 
332
415
  return Rx.combineLatest(args)
333
416
  end
334
417
 
335
- function JSONTranslator:_doTranslation(translator, translationKey, args)
418
+ function JSONTranslator._doTranslation(
419
+ self: JSONTranslator,
420
+ translator: Translator,
421
+ translationKey: string,
422
+ args
423
+ ): string
336
424
  assert(typeof(translator) == "Instance", "Bad translator")
337
425
  assert(type(translationKey) == "string", "Bad translationKey")
338
426
 
339
- local translation
427
+ local translation: string
340
428
  local ok, err = pcall(function()
341
429
  translation = translator:FormatByKey(translationKey, args)
342
430
  end)
@@ -371,7 +459,7 @@ function JSONTranslator:_doTranslation(translator, translationKey, args)
371
459
  end)
372
460
  end
373
461
 
374
- if ok and not err then
462
+ if ok and not err and translation then
375
463
  return translation
376
464
  end
377
465
 
@@ -382,9 +470,9 @@ end
382
470
  Cleans up the translator and deletes the localization table if it exists.
383
471
  Should be called by [ServiceBag]
384
472
  ]=]
385
- function JSONTranslator:Destroy()
473
+ function JSONTranslator.Destroy(self: JSONTranslator)
386
474
  self._maid:DoCleaning()
387
- setmetatable(self, nil)
475
+ setmetatable(self :: any, nil)
388
476
  end
389
477
 
390
478
  return JSONTranslator
@@ -38,145 +38,145 @@ local localeInfos = {}
38
38
  localeInfos["en-us"] = {
39
39
  [DECIMAL_SEPARATOR] = ".",
40
40
  [GROUP_DELIMITER] = ",",
41
- { 1, "", },
42
- { 1e3, "K", },
43
- { 1e6, "M", },
44
- { 1e9, "B", },
41
+ { 1, "" },
42
+ { 1e3, "K" },
43
+ { 1e6, "M" },
44
+ { 1e9, "B" },
45
45
  }
46
46
 
47
47
  localeInfos["es-es"] = {
48
48
  [DECIMAL_SEPARATOR] = ",",
49
49
  [GROUP_DELIMITER] = ".",
50
- { 1, "", },
51
- { 1e3, " mil", },
52
- { 1e6, " M", },
50
+ { 1, "" },
51
+ { 1e3, " mil" },
52
+ { 1e6, " M" },
53
53
  }
54
54
 
55
55
  localeInfos["fr-fr"] = {
56
56
  [DECIMAL_SEPARATOR] = ",",
57
57
  [GROUP_DELIMITER] = " ",
58
- { 1, "", },
59
- { 1e3, " k", },
60
- { 1e6, " M", },
61
- { 1e9, " Md", },
58
+ { 1, "" },
59
+ { 1e3, " k" },
60
+ { 1e6, " M" },
61
+ { 1e9, " Md" },
62
62
  }
63
63
 
64
64
  localeInfos["de-de"] = {
65
65
  [DECIMAL_SEPARATOR] = ",",
66
66
  [GROUP_DELIMITER] = " ",
67
- { 1, "", },
68
- { 1e3, " Tsd.", },
69
- { 1e6, " Mio.", },
70
- { 1e9, " Mrd.", },
67
+ { 1, "" },
68
+ { 1e3, " Tsd." },
69
+ { 1e6, " Mio." },
70
+ { 1e9, " Mrd." },
71
71
  }
72
72
 
73
73
  localeInfos["pt-br"] = {
74
74
  [DECIMAL_SEPARATOR] = ",",
75
75
  [GROUP_DELIMITER] = ".",
76
- { 1, "", },
77
- { 1e3, " mil", },
78
- { 1e6, " mi", },
79
- { 1e9, " bi", },
76
+ { 1, "" },
77
+ { 1e3, " mil" },
78
+ { 1e6, " mi" },
79
+ { 1e9, " bi" },
80
80
  }
81
81
 
82
82
  localeInfos["zh-cn"] = {
83
83
  [DECIMAL_SEPARATOR] = ".",
84
84
  [GROUP_DELIMITER] = ",", -- Chinese commonly uses 3 digit groupings, despite 10000s rule
85
- { 1, "", },
86
- { 1e3, "千", },
87
- { 1e4, "万", },
88
- { 1e8, "亿", },
85
+ { 1, "" },
86
+ { 1e3, "千" },
87
+ { 1e4, "万" },
88
+ { 1e8, "亿" },
89
89
  }
90
90
 
91
91
  localeInfos["zh-cjv"] = {
92
92
  [DECIMAL_SEPARATOR] = ".",
93
93
  [GROUP_DELIMITER] = ",",
94
- { 1, "", },
95
- { 1e3, "千", },
96
- { 1e4, "万", },
97
- { 1e8, "亿", },
94
+ { 1, "" },
95
+ { 1e3, "千" },
96
+ { 1e4, "万" },
97
+ { 1e8, "亿" },
98
98
  }
99
99
 
100
100
  localeInfos["zh-tw"] = {
101
101
  [DECIMAL_SEPARATOR] = ".",
102
102
  [GROUP_DELIMITER] = ",",
103
- { 1, "", },
104
- { 1e3, "千", },
105
- { 1e4, "萬", },
106
- { 1e8, "億", },
103
+ { 1, "" },
104
+ { 1e3, "千" },
105
+ { 1e4, "萬" },
106
+ { 1e8, "億" },
107
107
  }
108
108
 
109
109
  localeInfos["ko-kr"] = {
110
110
  [DECIMAL_SEPARATOR] = ".",
111
111
  [GROUP_DELIMITER] = ",",
112
- { 1, "", },
113
- { 1e3, "천", },
114
- { 1e4, "만", },
115
- { 1e8, "억", },
112
+ { 1, "" },
113
+ { 1e3, "천" },
114
+ { 1e4, "만" },
115
+ { 1e8, "억" },
116
116
  }
117
117
 
118
118
  localeInfos["ja-jp"] = {
119
119
  [DECIMAL_SEPARATOR] = ".",
120
120
  [GROUP_DELIMITER] = ",",
121
- { 1, "", },
122
- { 1e3, "千", },
123
- { 1e4, "万", },
124
- { 1e8, "億", },
121
+ { 1, "" },
122
+ { 1e3, "千" },
123
+ { 1e4, "万" },
124
+ { 1e8, "億" },
125
125
  }
126
126
 
127
127
  localeInfos["it-it"] = {
128
128
  [DECIMAL_SEPARATOR] = ",",
129
129
  [GROUP_DELIMITER] = " ",
130
- { 1, "", },
131
- { 1e3, " mila", },
132
- { 1e6, " Mln", },
133
- { 1e9, " Mld", },
130
+ { 1, "" },
131
+ { 1e3, " mila" },
132
+ { 1e6, " Mln" },
133
+ { 1e9, " Mld" },
134
134
  }
135
135
 
136
136
  localeInfos["ru-ru"] = {
137
137
  [DECIMAL_SEPARATOR] = ",",
138
138
  [GROUP_DELIMITER] = ".",
139
- { 1, "", },
140
- { 1e3, " тыс", },
141
- { 1e6, " млн", },
142
- { 1e9, " млрд", },
139
+ { 1, "" },
140
+ { 1e3, " тыс" },
141
+ { 1e6, " млн" },
142
+ { 1e9, " млрд" },
143
143
  }
144
144
 
145
145
  localeInfos["id-id"] = {
146
146
  [DECIMAL_SEPARATOR] = ",",
147
147
  [GROUP_DELIMITER] = ".",
148
- { 1, "", },
149
- { 1e3, " rb", },
150
- { 1e6, " jt", },
151
- { 1e9, " M", },
148
+ { 1, "" },
149
+ { 1e3, " rb" },
150
+ { 1e6, " jt" },
151
+ { 1e9, " M" },
152
152
  }
153
153
 
154
154
  localeInfos["vi-vn"] = {
155
155
  [DECIMAL_SEPARATOR] = ".",
156
156
  [GROUP_DELIMITER] = " ",
157
- { 1, "", },
158
- { 1e3, " N", },
159
- { 1e6, " Tr", },
160
- { 1e9, " T", },
157
+ { 1, "" },
158
+ { 1e3, " N" },
159
+ { 1e6, " Tr" },
160
+ { 1e9, " T" },
161
161
  }
162
162
 
163
163
  localeInfos["th-th"] = {
164
164
  [DECIMAL_SEPARATOR] = ".",
165
165
  [GROUP_DELIMITER] = ",",
166
- { 1, "", },
167
- { 1e3, " พ", },
168
- { 1e4, " ม", },
169
- { 1e5, " ส", },
170
- { 1e6, " ล", },
166
+ { 1, "" },
167
+ { 1e3, " พ" },
168
+ { 1e4, " ม" },
169
+ { 1e5, " ส" },
170
+ { 1e6, " ล" },
171
171
  }
172
172
 
173
173
  localeInfos["tr-tr"] = {
174
174
  [DECIMAL_SEPARATOR] = ",",
175
175
  [GROUP_DELIMITER] = ".",
176
- { 1, "", },
177
- { 1e3, " B", },
178
- { 1e6, " Mn", },
179
- { 1e9, " Mr", },
176
+ { 1, "" },
177
+ { 1e3, " B" },
178
+ { 1e6, " Mn" },
179
+ { 1e9, " Mr" },
180
180
  }
181
181
 
182
182
  -- Aliases for languages that use the same mappings.
@@ -184,12 +184,12 @@ localeInfos["en"] = localeInfos["en-us"]
184
184
  localeInfos["en-gb"] = localeInfos["en-us"]
185
185
  localeInfos["es-mx"] = localeInfos["es-es"]
186
186
 
187
- local function findDecimalPointIndex(numberStr)
187
+ local function findDecimalPointIndex(numberStr: string)
188
188
  return string.find(numberStr, "%.") or #numberStr + 1
189
189
  end
190
190
 
191
191
  -- Find the base 10 offset needed to make 0.1 <= abs(number) < 1
192
- local function findDecimalOffset(number)
192
+ local function findDecimalOffset(number: number)
193
193
  if number == 0 then
194
194
  return 0
195
195
  end
@@ -198,19 +198,23 @@ local function findDecimalOffset(number)
198
198
  return -(offsetToOnesRange + 1) -- Offset one more (or less) digit
199
199
  end
200
200
 
201
- local function roundToSignificantDigits(number, significantDigits, roundingBehaviourType)
201
+ local function roundToSignificantDigits(
202
+ number: number,
203
+ significantDigits: number,
204
+ roundingBehaviourType: RoundingBehaviourTypes.RoundingBehaviourType
205
+ )
202
206
  local offset = findDecimalOffset(number)
203
- local multiplier = 10^(significantDigits + offset)
207
+ local multiplier = 10 ^ (significantDigits + offset)
204
208
  local significand
205
209
  if roundingBehaviourType == RoundingBehaviourTypes.TRUNCATE then
206
210
  significand = math.modf(number * multiplier)
207
211
  else
208
212
  significand = math.floor(number * multiplier + 0.5)
209
213
  end
210
- return significand / multiplier;
214
+ return significand / multiplier
211
215
  end
212
216
 
213
- local function addGroupDelimiters(numberStr, delimiter)
217
+ local function addGroupDelimiters(numberStr, delimiter: string): string
214
218
  local formatted = numberStr
215
219
  local delimiterSubStr = string.format("%%1%s%%2", delimiter)
216
220
  while true do
@@ -223,7 +227,7 @@ local function addGroupDelimiters(numberStr, delimiter)
223
227
  return formatted
224
228
  end
225
229
 
226
- local function findDenominationEntry(localeInfo, number, roundingBehaviourType)
230
+ local function findDenominationEntry(localeInfo, number: number, roundingBehaviourType)
227
231
  local denominationEntry = localeInfo[1] -- Default to base denominations
228
232
  local absOfNumber = math.abs(number)
229
233
  for i = #localeInfo, 2, -1 do
@@ -232,7 +236,7 @@ local function findDenominationEntry(localeInfo, number, roundingBehaviourType)
232
236
  if roundingBehaviourType == RoundingBehaviourTypes.TRUNCATE then
233
237
  baseValue = entry[1]
234
238
  else
235
- baseValue = entry[1] - (localeInfo[i - 1][1]) / 2
239
+ baseValue = entry[1] - localeInfo[i - 1][1] / 2
236
240
  end
237
241
  if baseValue <= absOfNumber then
238
242
  denominationEntry = entry
@@ -242,7 +246,7 @@ local function findDenominationEntry(localeInfo, number, roundingBehaviourType)
242
246
  return denominationEntry
243
247
  end
244
248
 
245
- function NumberLocalizationUtils.localize(number, locale)
249
+ function NumberLocalizationUtils.localize(number: number, locale: string): string
246
250
  if number == 0 then
247
251
  return "0"
248
252
  end
@@ -250,15 +254,20 @@ function NumberLocalizationUtils.localize(number, locale)
250
254
  local localeInfo = localeInfos[locale]
251
255
  if not localeInfo then
252
256
  localeInfo = localeInfos[DEFAULT_LOCALE]
253
- warn(string.format("[NumberLocalizationUtils] - Locale not found: '%s', reverting to '%s' instead.",
254
- tostring(locale), DEFAULT_LOCALE))
257
+ warn(
258
+ string.format(
259
+ "[NumberLocalizationUtils] - Locale not found: '%s', reverting to '%s' instead.",
260
+ tostring(locale),
261
+ DEFAULT_LOCALE
262
+ )
263
+ )
255
264
  end
256
265
 
257
- if localeInfo.groupDelimiter then
258
- return addGroupDelimiters(number, localeInfo.groupDelimiter)
259
- end
266
+ if localeInfo.groupDelimiter then
267
+ return addGroupDelimiters(number, localeInfo.groupDelimiter)
268
+ end
260
269
 
261
- return number
270
+ return tostring(number)
262
271
  end
263
272
 
264
273
  --[=[
@@ -272,11 +281,14 @@ end
272
281
  @param locale string
273
282
  @param roundingBehaviourType RoundingBehaviourType?
274
283
  @param numSignificantDigits number?
284
+ @return string
275
285
  ]=]
276
- function NumberLocalizationUtils.abbreviate(number, locale, roundingBehaviourType, numSignificantDigits)
286
+ function NumberLocalizationUtils.abbreviate(number: number, locale: string, roundingBehaviourType: RoundingBehaviourTypes.RoundingBehaviourType?, numSignificantDigits: number?): string
277
287
  assert(type(number) == "number", "Bad number")
288
+ local roundingBehavior = roundingBehaviourType or RoundingBehaviourTypes.ROUND_TO_CLOSEST
289
+ local significantDigits = numSignificantDigits or 3
278
290
 
279
- if roundingBehaviourType == RoundingBehaviourTypes.NONE then
291
+ if roundingBehavior == RoundingBehaviourTypes.NONE then
280
292
  return NumberLocalizationUtils.localize(number, locale)
281
293
  end
282
294
 
@@ -284,41 +296,43 @@ function NumberLocalizationUtils.abbreviate(number, locale, roundingBehaviourTyp
284
296
  return "0"
285
297
  end
286
298
 
287
- if roundingBehaviourType == nil then
288
- roundingBehaviourType = RoundingBehaviourTypes.ROUND_TO_CLOSEST
289
- end
290
-
291
- if numSignificantDigits == nil then
292
- numSignificantDigits = 3
293
- end
294
-
295
299
  local localeInfo = localeInfos[locale]
296
300
  if not localeInfo then
297
301
  localeInfo = localeInfos[DEFAULT_LOCALE]
298
- warn(string.format("[NumberLocalizationUtils] - Locale not found: '%s', reverting to '%s' instead.",
299
- tostring(locale), DEFAULT_LOCALE))
302
+ warn(
303
+ string.format(
304
+ "[NumberLocalizationUtils] - Locale not found: '%s', reverting to '%s' instead.",
305
+ tostring(locale),
306
+ DEFAULT_LOCALE
307
+ )
308
+ )
300
309
  end
301
310
 
302
311
  -- select which denomination we are going to use
303
- local denominationEntry = findDenominationEntry(localeInfo, number, roundingBehaviourType)
312
+ local denominationEntry = findDenominationEntry(localeInfo, number, roundingBehavior)
304
313
  local baseValue = denominationEntry[1]
305
314
  local symbol = denominationEntry[2]
306
315
 
307
316
  -- Round to required significant digits
308
- local significantQuotient = roundToSignificantDigits(number / baseValue, numSignificantDigits, roundingBehaviourType)
317
+ local significantQuotient = roundToSignificantDigits(number / baseValue, significantDigits, roundingBehavior)
309
318
 
310
319
  -- trim decimal points
311
320
  local trimmedQuotientString
312
321
  local symbolsAboveDecimal = math.ceil(math.log10(significantQuotient))
313
- local maxDecimals = math.max(1, numSignificantDigits - symbolsAboveDecimal)
322
+ local maxDecimals = math.max(1, significantDigits - symbolsAboveDecimal)
314
323
  local trimmedQuotient
315
- local roundingFactor = 10^maxDecimals
316
- if roundingBehaviourType == RoundingBehaviourTypes.TRUNCATE then
324
+ local roundingFactor = 10 ^ maxDecimals
325
+ if roundingBehavior == RoundingBehaviourTypes.TRUNCATE then
317
326
  trimmedQuotient = math.modf(significantQuotient * roundingFactor) / roundingFactor
318
- elseif roundingBehaviourType == RoundingBehaviourTypes.ROUND_TO_CLOSEST then
327
+ elseif roundingBehavior == RoundingBehaviourTypes.ROUND_TO_CLOSEST then
319
328
  trimmedQuotient = math.floor(significantQuotient * roundingFactor + 0.5) / roundingFactor
320
329
  else
321
- error(string.format("[NumberLocalizationUtils.abbreviate] - Unknown roundingBehaviourType %q", tostring(roundingBehaviourType)))
330
+ error(
331
+ string.format(
332
+ "[NumberLocalizationUtils.abbreviate] - Unknown roundingBehaviourType %q",
333
+ tostring(roundingBehavior)
334
+ )
335
+ )
322
336
  end
323
337
 
324
338
  trimmedQuotientString = tostring(trimmedQuotient)
@@ -340,4 +354,4 @@ function NumberLocalizationUtils.abbreviate(number, locale, roundingBehaviourTyp
340
354
  end
341
355
  end
342
356
 
343
- return NumberLocalizationUtils
357
+ return NumberLocalizationUtils
@@ -8,13 +8,13 @@ local describe = Jest.Globals.describe
8
8
  local expect = Jest.Globals.expect
9
9
  local it = Jest.Globals.it
10
10
 
11
- local function checkLocale(locale, responseMapping)
12
- for input, output in pairs(responseMapping) do
11
+ local function checkLocale(locale: string, responseMapping)
12
+ for input, output in responseMapping do
13
13
  expect(NumberLocalizationUtils.localize(input, locale)).toBe(output)
14
14
  end
15
15
  end
16
16
 
17
- local function checkValid_en_zh(locale)
17
+ local function checkValid_en_zh(locale: string)
18
18
  checkLocale(locale, {
19
19
  [0] = "0",
20
20
  [1] = "1",
@@ -105,8 +105,8 @@ describe("NumberLocalizationUtils.abbreviate", function()
105
105
  [-1499.99] = "-1.4K",
106
106
  }
107
107
 
108
- for input, output in pairs(roundToZeroMap) do
108
+ for input, output in roundToZeroMap do
109
109
  expect(NumberLocalizationUtils.abbreviate(input, "en-us", RoundingBehaviourTypes.TRUNCATE)).toBe(output)
110
110
  end
111
111
  end)
112
- end)
112
+ end)
@@ -1,3 +1,4 @@
1
+ --!strict
1
2
  --[=[
2
3
  @class RoundingBehaviourTypes
3
4
  ]=]
@@ -6,8 +7,16 @@ local require = require(script.Parent.loader).load(script)
6
7
 
7
8
  local Table = require("Table")
8
9
 
10
+ export type RoundingBehaviourType = "roundToClosest" | "truncate" | "None"
11
+
12
+ export type RoundingBehaviourTypeMap = {
13
+ ROUND_TO_CLOSEST: "roundToClosest",
14
+ TRUNCATE: "truncate",
15
+ NONE: "none",
16
+ }
17
+
9
18
  return Table.readonly({
10
- ROUND_TO_CLOSEST = "roundToClosest";
11
- TRUNCATE = "truncate";
12
- NONE = "None"
13
- })
19
+ ROUND_TO_CLOSEST = "roundToClosest",
20
+ TRUNCATE = "truncate",
21
+ NONE = "none",
22
+ } :: RoundingBehaviourTypeMap)
@@ -1,3 +1,4 @@
1
+ --!strict
1
2
  --[=[
2
3
  Handles selecting the right locale/translator for Studio, and Roblox games.
3
4
 
@@ -10,17 +11,33 @@ local Players = game:GetService("Players")
10
11
  local RunService = game:GetService("RunService")
11
12
  local LocalizationService = game:GetService("LocalizationService")
12
13
 
13
- local RxInstanceUtils = require("RxInstanceUtils")
14
- local Rx = require("Rx")
15
14
  local LocalizationServiceUtils = require("LocalizationServiceUtils")
16
- local ValueObject = require("ValueObject")
17
15
  local Maid = require("Maid")
18
16
  local Promise = require("Promise")
17
+ local Rx = require("Rx")
18
+ local RxInstanceUtils = require("RxInstanceUtils")
19
+ local ValueObject = require("ValueObject")
20
+ local _Observable = require("Observable")
21
+ local _ServiceBag = require("ServiceBag")
19
22
 
20
23
  local TranslatorService = {}
21
24
  TranslatorService.ServiceName = "TranslatorService"
22
25
 
23
- function TranslatorService:Init(serviceBag)
26
+ export type TranslatorService = typeof(setmetatable(
27
+ {} :: {
28
+ _maid: Maid.Maid,
29
+ _serviceBag: _ServiceBag.ServiceBag,
30
+ _translator: ValueObject.ValueObject<Translator>,
31
+ _localizationTable: LocalizationTable?,
32
+ _pendingTranslatorPromise: Promise.Promise<Translator>?,
33
+ _localeIdValue: ValueObject.ValueObject<string>?,
34
+ _loadedPlayerObservable: _Observable.Observable<Player>?,
35
+ _loadedPlayer: Player?,
36
+ },
37
+ { __index = TranslatorService }
38
+ ))
39
+
40
+ function TranslatorService.Init(self: TranslatorService, serviceBag: _ServiceBag.ServiceBag)
24
41
  assert(not self._serviceBag, "Already initialized")
25
42
  self._serviceBag = assert(serviceBag, "No serviceBag")
26
43
  self._maid = Maid.new()
@@ -29,7 +46,7 @@ function TranslatorService:Init(serviceBag)
29
46
  self._translator:Mount(self:_observeTranslatorImpl())
30
47
  end
31
48
 
32
- function TranslatorService:GetLocalizationTable()
49
+ function TranslatorService.GetLocalizationTable(self: TranslatorService): LocalizationTable
33
50
  if self._localizationTable then
34
51
  return self._localizationTable
35
52
  end
@@ -47,7 +64,7 @@ function TranslatorService:GetLocalizationTable()
47
64
  return localizationTable
48
65
  end
49
66
 
50
- function TranslatorService:_getLocalizationTableName()
67
+ function TranslatorService._getLocalizationTableName(_self: TranslatorService): string
51
68
  if RunService:IsServer() then
52
69
  return "GeneratedJSONTable_Server"
53
70
  else
@@ -60,7 +77,7 @@ end
60
77
 
61
78
  @return Observable<Translator>
62
79
  ]=]
63
- function TranslatorService:ObserveTranslator()
80
+ function TranslatorService.ObserveTranslator(self: TranslatorService): _Observable.Observable<Translator>
64
81
  return self._translator:Observe()
65
82
  end
66
83
 
@@ -69,7 +86,7 @@ end
69
86
 
70
87
  @return Observable<Translator>
71
88
  ]=]
72
- function TranslatorService:PromiseTranslator()
89
+ function TranslatorService.PromiseTranslator(self: TranslatorService): Promise.Promise<Translator>
73
90
  local found = self._translator.Value
74
91
  if found then
75
92
  return Promise.resolved(found)
@@ -95,7 +112,7 @@ function TranslatorService:PromiseTranslator()
95
112
  end
96
113
  end)
97
114
 
98
- maid:GiveTask(self._translator:Observe():Subscribe(function(translator)
115
+ maid:GiveTask(self._translator:Observe():Subscribe(function(translator: Translator)
99
116
  if translator then
100
117
  promise:Resolve(translator)
101
118
  end
@@ -109,7 +126,7 @@ end
109
126
 
110
127
  @return Translator?
111
128
  ]=]
112
- function TranslatorService:GetTranslator()
129
+ function TranslatorService.GetTranslator(self: TranslatorService): Translator?
113
130
  return self._translator.Value
114
131
  end
115
132
 
@@ -118,34 +135,34 @@ end
118
135
 
119
136
  @return Observable<string>
120
137
  ]=]
121
- function TranslatorService:ObserveLocaleId()
138
+ function TranslatorService.ObserveLocaleId(self: TranslatorService): _Observable.Observable<string>
122
139
  if self._localeIdValue then
123
140
  return self._localeIdValue:Observe()
124
141
  end
125
142
 
126
- self._localeIdValue = self._maid:Add(ValueObject.new("en-us", "string"))
143
+ local valueObject = self._maid:Add(ValueObject.new("en-us", "string"))
127
144
 
128
- self._localeIdValue:Mount(self._translator:Observe():Pipe({
129
- Rx.switchMap(function(translator)
145
+ valueObject:Mount(self._translator:Observe():Pipe({
146
+ Rx.switchMap(function(translator: Translator): any
130
147
  if translator then
131
148
  return RxInstanceUtils.observeProperty(translator, "LocaleId")
132
149
  else
133
150
  -- Fallback
134
151
  return self:_observeLoadedPlayer():Pipe({
135
- Rx.switchMap(function(player)
152
+ Rx.switchMap(function(player: Player)
136
153
  if player then
137
154
  return RxInstanceUtils.observeProperty(player, "LocaleId")
138
155
  else
139
156
  return RxInstanceUtils.observeProperty(LocalizationService, "RobloxLocaleId")
140
157
  end
141
- end)
158
+ end) :: any,
142
159
  })
143
160
  end
144
- end);
145
- Rx.distinct();
161
+ end) :: any,
162
+ Rx.distinct(),
146
163
  }))
147
-
148
- return self._localeIdValue:Observe()
164
+ self._localeIdValue = valueObject
165
+ return valueObject:Observe()
149
166
  end
150
167
 
151
168
  --[=[
@@ -153,7 +170,7 @@ end
153
170
 
154
171
  @return string
155
172
  ]=]
156
- function TranslatorService:GetLocaleId()
173
+ function TranslatorService.GetLocaleId(self: TranslatorService): string
157
174
  local found = self._translator.Value
158
175
  if found then
159
176
  return found.LocaleId
@@ -168,52 +185,53 @@ function TranslatorService:GetLocaleId()
168
185
  end
169
186
  end
170
187
 
171
- function TranslatorService:_observeTranslatorImpl()
188
+ function TranslatorService._observeTranslatorImpl(self: TranslatorService): _Observable.Observable<Translator>
172
189
  return self:_observeLoadedPlayer():Pipe({
173
- Rx.switchMap(function(loadedPlayer)
190
+ Rx.switchMap(function(loadedPlayer: Player): any
174
191
  if loadedPlayer then
175
192
  return Rx.fromPromise(LocalizationServiceUtils.promisePlayerTranslator(loadedPlayer))
176
193
  end
177
194
 
178
195
  return RxInstanceUtils.observeProperty(LocalizationService, "RobloxLocaleId"):Pipe({
179
- Rx.switchMap(function(localeId)
196
+ Rx.switchMap(function(localeId: string): any
180
197
  -- This can actually take a while (20-30 seconds)
181
198
  return Rx.fromPromise(LocalizationServiceUtils.promiseTranslatorForLocale(localeId))
182
- end)
199
+ end) :: any,
183
200
  })
184
- end);
185
- })
201
+ end) :: any,
202
+ }) :: any
186
203
  end
187
204
 
188
- function TranslatorService:_observeLoadedPlayer()
205
+ function TranslatorService._observeLoadedPlayer(self: TranslatorService): _Observable.Observable<Player>
189
206
  if self._loadedPlayerObservable then
190
207
  return self._loadedPlayerObservable
191
208
  end
192
209
 
193
- self._loadedPlayerObservable = RxInstanceUtils.observeProperty(Players, "LocalPlayer"):Pipe({
194
- Rx.switchMap(function(player)
210
+ local observable: any = RxInstanceUtils.observeProperty(Players, "LocalPlayer"):Pipe({
211
+ Rx.switchMap(function(player: Player): any
195
212
  if not player then
196
213
  return Rx.of(nil)
197
214
  end
198
215
 
199
216
  return RxInstanceUtils.observeProperty(player, "LocaleId"):Pipe({
200
- Rx.map(function(localeId)
217
+ Rx.map(function(localeId): Player?
201
218
  if localeId == "" then
202
219
  return nil
203
220
  else
204
221
  return player
205
222
  end
206
- end);
223
+ end) :: any,
207
224
  })
208
- end);
209
- Rx.distinct();
210
- Rx.cache();
225
+ end) :: any,
226
+ Rx.distinct() :: any,
227
+ Rx.cache() :: any,
211
228
  })
229
+ self._loadedPlayerObservable = observable
212
230
 
213
- return self._loadedPlayerObservable
231
+ return observable
214
232
  end
215
233
 
216
- function TranslatorService:Destroy()
234
+ function TranslatorService.Destroy(self: TranslatorService)
217
235
  self._maid:DoCleaning()
218
236
  end
219
237
 
@@ -39,7 +39,7 @@ function LocalizationServiceUtils.promiseTranslatorForLocale(localeId)
39
39
  end)
40
40
  end
41
41
 
42
- function LocalizationServiceUtils.promisePlayerTranslator(player)
42
+ function LocalizationServiceUtils.promisePlayerTranslator(player: Player)
43
43
  local promiseTranslator = Promise.spawn(function(resolve, reject)
44
44
  local translator = nil
45
45
  local ok, err = pcall(function()