@quenty/metricutils 2.4.3 → 2.5.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +16 -54
  2. package/README.md +80 -2
  3. package/deploy.nevermore.json +10 -0
  4. package/package.json +22 -4
  5. package/src/Shared/Definitions/MetricUnitDefinitions.lua +1082 -0
  6. package/src/Shared/Definitions/MetricUnitDefinitions.spec.lua +55 -0
  7. package/src/Shared/Definitions/MetricUnitDimensionUtils.lua +164 -0
  8. package/src/Shared/Definitions/MetricUnitDimensionUtils.spec.lua +49 -0
  9. package/src/Shared/Definitions/MetricUnitKinds.lua +237 -0
  10. package/src/Shared/Definitions/MetricUnitKinds.spec.lua +51 -0
  11. package/src/Shared/Formatting/MetricUnitDisplayUnitUtils.lua +129 -0
  12. package/src/Shared/Formatting/MetricUnitFormatUtils.lua +924 -0
  13. package/src/Shared/Formatting/MetricUnitFormatUtils.spec.lua +280 -0
  14. package/src/Shared/Formatting/RxMetricUnitFormatUtils.lua +62 -0
  15. package/src/Shared/Localization/MetricUnitFallbackStrings.lua +289 -0
  16. package/src/Shared/Localization/MetricUnitFallbackStrings.spec.lua +82 -0
  17. package/src/Shared/Localization/MetricUnitTranslator/ar.json.txt +182 -0
  18. package/src/Shared/Localization/MetricUnitTranslator/de.json.txt +182 -0
  19. package/src/Shared/Localization/MetricUnitTranslator/en.json.txt +182 -0
  20. package/src/Shared/Localization/MetricUnitTranslator/es.json.txt +182 -0
  21. package/src/Shared/Localization/MetricUnitTranslator/fr.json.txt +182 -0
  22. package/src/Shared/Localization/MetricUnitTranslator/id.json.txt +182 -0
  23. package/src/Shared/Localization/MetricUnitTranslator/init.lua +92 -0
  24. package/src/Shared/Localization/MetricUnitTranslator/it.json.txt +182 -0
  25. package/src/Shared/Localization/MetricUnitTranslator/ja.json.txt +182 -0
  26. package/src/Shared/Localization/MetricUnitTranslator/ko.json.txt +182 -0
  27. package/src/Shared/Localization/MetricUnitTranslator/pl.json.txt +182 -0
  28. package/src/Shared/Localization/MetricUnitTranslator/pt.json.txt +182 -0
  29. package/src/Shared/Localization/MetricUnitTranslator/ru.json.txt +182 -0
  30. package/src/Shared/Localization/MetricUnitTranslator/th.json.txt +182 -0
  31. package/src/Shared/Localization/MetricUnitTranslator/tr.json.txt +182 -0
  32. package/src/Shared/Localization/MetricUnitTranslator/vi.json.txt +182 -0
  33. package/src/Shared/Localization/MetricUnitTranslator/zh-cn.json.txt +182 -0
  34. package/src/Shared/Localization/MetricUnitTranslator/zh-tw.json.txt +182 -0
  35. package/src/Shared/Localization/MetricUnitTranslator.spec.lua +309 -0
  36. package/src/Shared/Localization/PluralCategoryUtils.lua +103 -0
  37. package/src/Shared/Localization/PluralCategoryUtils.spec.lua +77 -0
  38. package/src/Shared/Logarithmic/DecibelUtils.lua +72 -0
  39. package/src/Shared/Logarithmic/DecibelUtils.spec.lua +35 -0
  40. package/src/Shared/MetricUnit.Angles.spec.lua +80 -0
  41. package/src/Shared/MetricUnit.Customary.spec.lua +104 -0
  42. package/src/Shared/MetricUnit.Derived.spec.lua +173 -0
  43. package/src/Shared/MetricUnit.Display.spec.lua +153 -0
  44. package/src/Shared/MetricUnit.Electrical.spec.lua +114 -0
  45. package/src/Shared/MetricUnit.Math.spec.lua +112 -0
  46. package/src/Shared/MetricUnit.Roblox.spec.lua +34 -0
  47. package/src/Shared/MetricUnit.SI.spec.lua +127 -0
  48. package/src/Shared/MetricUnit.Spring.spec.lua +94 -0
  49. package/src/Shared/MetricUnit.Temperature.spec.lua +212 -0
  50. package/src/Shared/MetricUnit.Time.spec.lua +202 -0
  51. package/src/Shared/MetricUnit.Units.spec.lua +210 -0
  52. package/src/Shared/MetricUnit.lua +1730 -0
  53. package/src/Shared/MetricUnit.spec.lua +362 -0
  54. package/src/Shared/MetricUtils.lua +47 -16
  55. package/src/Shared/MetricUtils.spec.lua +76 -0
  56. package/src/Shared/Parsing/MetricUnitParseUtils.lua +799 -0
  57. package/src/Shared/Parsing/MetricUnitParseUtils.spec.lua +189 -0
  58. package/src/Shared/Systems/MetricUnitSystem.lua +322 -0
  59. package/src/Shared/Systems/MetricUnitSystem.spec.lua +181 -0
  60. package/src/Shared/Systems/MetricUnitSystems.lua +225 -0
  61. package/src/jest.config.lua +3 -0
  62. package/src/node_modules.project.json +7 -0
  63. package/test/default.project.json +25 -0
  64. package/test/scripts/Server/ServerMain.server.lua +14 -0
@@ -0,0 +1,182 @@
1
+ {
2
+ "units": {
3
+ "microsecond": {"long":{"zero":"{n} 微秒","one":"{n} 微秒","two":"{n} 微秒","few":"{n} 微秒","many":"{n} 微秒","other":"{n} 微秒"},"short":{"zero":"{n} 微秒","one":"{n} 微秒","two":"{n} 微秒","few":"{n} 微秒","many":"{n} 微秒","other":"{n} 微秒"}},
4
+ "hertz": {"long":{"zero":"{n} 赫茲","one":"{n} 赫茲","two":"{n} 赫茲","few":"{n} 赫茲","many":"{n} 赫茲","other":"{n} 赫茲"},"short":{"zero":"{n} 赫茲","one":"{n} 赫茲","two":"{n} 赫茲","few":"{n} 赫茲","many":"{n} 赫茲","other":"{n} 赫茲"}},
5
+ "percent": {"long":{"zero":"{n}%","one":"{n}%","two":"{n}%","few":"{n}%","many":"{n}%","other":"{n}%"},"short":{"zero":"{n}%","one":"{n}%","two":"{n}%","few":"{n}%","many":"{n}%","other":"{n}%"}},
6
+ "permille": {"long":{"zero":"{n}‰","one":"{n}‰","two":"{n}‰","few":"{n}‰","many":"{n}‰","other":"{n}‰"},"short":{"zero":"{n}‰","one":"{n}‰","two":"{n}‰","few":"{n}‰","many":"{n}‰","other":"{n}‰"}},
7
+ "millimeter": {"long":{"zero":"{n} 公釐","one":"{n} 公釐","two":"{n} 公釐","few":"{n} 公釐","many":"{n} 公釐","other":"{n} 公釐"},"short":{"zero":"{n} 公釐","one":"{n} 公釐","two":"{n} 公釐","few":"{n} 公釐","many":"{n} 公釐","other":"{n} 公釐"}},
8
+ "centimeter": {"long":{"zero":"{n} 公分","one":"{n} 公分","two":"{n} 公分","few":"{n} 公分","many":"{n} 公分","other":"{n} 公分"},"short":{"zero":"{n} 公分","one":"{n} 公分","two":"{n} 公分","few":"{n} 公分","many":"{n} 公分","other":"{n} 公分"}},
9
+ "meter": {"long":{"zero":"{n} 公尺","one":"{n} 公尺","two":"{n} 公尺","few":"{n} 公尺","many":"{n} 公尺","other":"{n} 公尺"},"short":{"zero":"{n} 公尺","one":"{n} 公尺","two":"{n} 公尺","few":"{n} 公尺","many":"{n} 公尺","other":"{n} 公尺"}},
10
+ "kilometer": {"long":{"zero":"{n} 公里","one":"{n} 公里","two":"{n} 公里","few":"{n} 公里","many":"{n} 公里","other":"{n} 公里"},"short":{"zero":"{n} 公里","one":"{n} 公里","two":"{n} 公里","few":"{n} 公里","many":"{n} 公里","other":"{n} 公里"}},
11
+ "inch": {"long":{"zero":"{n} 英寸","one":"{n} 英寸","two":"{n} 英寸","few":"{n} 英寸","many":"{n} 英寸","other":"{n} 英寸"},"short":{"zero":"{n} 吋","one":"{n} 吋","two":"{n} 吋","few":"{n} 吋","many":"{n} 吋","other":"{n} 吋"}},
12
+ "foot": {"long":{"zero":"{n} 英尺","one":"{n} 英尺","two":"{n} 英尺","few":"{n} 英尺","many":"{n} 英尺","other":"{n} 英尺"},"short":{"zero":"{n}英尺","one":"{n}英尺","two":"{n}英尺","few":"{n}英尺","many":"{n}英尺","other":"{n}英尺"}},
13
+ "yard": {"long":{"zero":"{n} 碼","one":"{n} 碼","two":"{n} 碼","few":"{n} 碼","many":"{n} 碼","other":"{n} 碼"},"short":{"zero":"{n} 碼","one":"{n} 碼","two":"{n} 碼","few":"{n} 碼","many":"{n} 碼","other":"{n} 碼"}},
14
+ "mile": {"long":{"zero":"{n} 英里","one":"{n} 英里","two":"{n} 英里","few":"{n} 英里","many":"{n} 英里","other":"{n} 英里"},"short":{"zero":"{n} 英里","one":"{n} 英里","two":"{n} 英里","few":"{n} 英里","many":"{n} 英里","other":"{n} 英里"}},
15
+ "stud": {"long":{"zero":"{n} studs","one":"{n} studs","two":"{n} studs","few":"{n} studs","many":"{n} studs","other":"{n} studs"},"short":{"zero":"{n} studs","one":"{n} studs","two":"{n} studs","few":"{n} studs","many":"{n} studs","other":"{n} studs"}},
16
+ "squareMillimeter": {"long":{"zero":"{n} 平方公釐","one":"{n} 平方公釐","two":"{n} 平方公釐","few":"{n} 平方公釐","many":"{n} 平方公釐","other":"{n} 平方公釐"},"short":{"zero":"{n} 公釐²","one":"{n} 公釐²","two":"{n} 公釐²","few":"{n} 公釐²","many":"{n} 公釐²","other":"{n} 公釐²"}},
17
+ "squareCentimeter": {"long":{"zero":"{n} 平方公分","one":"{n} 平方公分","two":"{n} 平方公分","few":"{n} 平方公分","many":"{n} 平方公分","other":"{n} 平方公分"},"short":{"zero":"{n} 平方公分","one":"{n} 平方公分","two":"{n} 平方公分","few":"{n} 平方公分","many":"{n} 平方公分","other":"{n} 平方公分"}},
18
+ "squareMeter": {"long":{"zero":"{n} 平方公尺","one":"{n} 平方公尺","two":"{n} 平方公尺","few":"{n} 平方公尺","many":"{n} 平方公尺","other":"{n} 平方公尺"},"short":{"zero":"{n} 平方公尺","one":"{n} 平方公尺","two":"{n} 平方公尺","few":"{n} 平方公尺","many":"{n} 平方公尺","other":"{n} 平方公尺"}},
19
+ "hectare": {"long":{"zero":"{n} 公頃","one":"{n} 公頃","two":"{n} 公頃","few":"{n} 公頃","many":"{n} 公頃","other":"{n} 公頃"},"short":{"zero":"{n} 公頃","one":"{n} 公頃","two":"{n} 公頃","few":"{n} 公頃","many":"{n} 公頃","other":"{n} 公頃"}},
20
+ "squareKilometer": {"long":{"zero":"{n} 平方公里","one":"{n} 平方公里","two":"{n} 平方公里","few":"{n} 平方公里","many":"{n} 平方公里","other":"{n} 平方公里"},"short":{"zero":"{n} 平方公里","one":"{n} 平方公里","two":"{n} 平方公里","few":"{n} 平方公里","many":"{n} 平方公里","other":"{n} 平方公里"}},
21
+ "squareFoot": {"long":{"zero":"{n} 平方英尺","one":"{n} 平方英尺","two":"{n} 平方英尺","few":"{n} 平方英尺","many":"{n} 平方英尺","other":"{n} 平方英尺"},"short":{"zero":"{n} 平方英尺","one":"{n} 平方英尺","two":"{n} 平方英尺","few":"{n} 平方英尺","many":"{n} 平方英尺","other":"{n} 平方英尺"}},
22
+ "acre": {"long":{"zero":"{n} 英畝","one":"{n} 英畝","two":"{n} 英畝","few":"{n} 英畝","many":"{n} 英畝","other":"{n} 英畝"},"short":{"zero":"{n} 英畝","one":"{n} 英畝","two":"{n} 英畝","few":"{n} 英畝","many":"{n} 英畝","other":"{n} 英畝"}},
23
+ "milliliter": {"long":{"zero":"{n} 毫升","one":"{n} 毫升","two":"{n} 毫升","few":"{n} 毫升","many":"{n} 毫升","other":"{n} 毫升"},"short":{"zero":"{n} 毫升","one":"{n} 毫升","two":"{n} 毫升","few":"{n} 毫升","many":"{n} 毫升","other":"{n} 毫升"}},
24
+ "liter": {"long":{"zero":"{n} 公升","one":"{n} 公升","two":"{n} 公升","few":"{n} 公升","many":"{n} 公升","other":"{n} 公升"},"short":{"zero":"{n} 升","one":"{n} 升","two":"{n} 升","few":"{n} 升","many":"{n} 升","other":"{n} 升"}},
25
+ "cubicMeter": {"long":{"zero":"{n} 立方公尺","one":"{n} 立方公尺","two":"{n} 立方公尺","few":"{n} 立方公尺","many":"{n} 立方公尺","other":"{n} 立方公尺"},"short":{"zero":"{n} 立方公尺","one":"{n} 立方公尺","two":"{n} 立方公尺","few":"{n} 立方公尺","many":"{n} 立方公尺","other":"{n} 立方公尺"}},
26
+ "usFluidOunce": {"long":{"zero":"{n} 液盎司","one":"{n} 液盎司","two":"{n} 液盎司","few":"{n} 液盎司","many":"{n} 液盎司","other":"{n} 液盎司"},"short":{"zero":"{n} 液盎司","one":"{n} 液盎司","two":"{n} 液盎司","few":"{n} 液盎司","many":"{n} 液盎司","other":"{n} 液盎司"}},
27
+ "usGallon": {"long":{"zero":"{n} 加侖","one":"{n} 加侖","two":"{n} 加侖","few":"{n} 加侖","many":"{n} 加侖","other":"{n} 加侖"},"short":{"zero":"{n} 加侖","one":"{n} 加侖","two":"{n} 加侖","few":"{n} 加侖","many":"{n} 加侖","other":"{n} 加侖"}},
28
+ "imperialGallon": {"long":{"zero":"{n} 英制加侖","one":"{n} 英制加侖","two":"{n} 英制加侖","few":"{n} 英制加侖","many":"{n} 英制加侖","other":"{n} 英制加侖"},"short":{"zero":"{n} 英制加侖","one":"{n} 英制加侖","two":"{n} 英制加侖","few":"{n} 英制加侖","many":"{n} 英制加侖","other":"{n} 英制加侖"}},
29
+ "bit": {"long":{"zero":"{n} bit","one":"{n} bit","two":"{n} bit","few":"{n} bit","many":"{n} bit","other":"{n} bit"},"short":{"zero":"{n} bit","one":"{n} bit","two":"{n} bit","few":"{n} bit","many":"{n} bit","other":"{n} bit"}},
30
+ "kilobit": {"long":{"zero":"{n} kb","one":"{n} kb","two":"{n} kb","few":"{n} kb","many":"{n} kb","other":"{n} kb"},"short":{"zero":"{n} kb","one":"{n} kb","two":"{n} kb","few":"{n} kb","many":"{n} kb","other":"{n} kb"}},
31
+ "megabit": {"long":{"zero":"{n} Mb","one":"{n} Mb","two":"{n} Mb","few":"{n} Mb","many":"{n} Mb","other":"{n} Mb"},"short":{"zero":"{n} Mb","one":"{n} Mb","two":"{n} Mb","few":"{n} Mb","many":"{n} Mb","other":"{n} Mb"}},
32
+ "gigabit": {"long":{"zero":"{n} Gb","one":"{n} Gb","two":"{n} Gb","few":"{n} Gb","many":"{n} Gb","other":"{n} Gb"},"short":{"zero":"{n} Gb","one":"{n} Gb","two":"{n} Gb","few":"{n} Gb","many":"{n} Gb","other":"{n} Gb"}},
33
+ "byte": {"long":{"zero":"{n} byte","one":"{n} byte","two":"{n} byte","few":"{n} byte","many":"{n} byte","other":"{n} byte"},"short":{"zero":"{n} byte","one":"{n} byte","two":"{n} byte","few":"{n} byte","many":"{n} byte","other":"{n} byte"}},
34
+ "kilobyte": {"long":{"zero":"{n} kB","one":"{n} kB","two":"{n} kB","few":"{n} kB","many":"{n} kB","other":"{n} kB"},"short":{"zero":"{n} kB","one":"{n} kB","two":"{n} kB","few":"{n} kB","many":"{n} kB","other":"{n} kB"}},
35
+ "megabyte": {"long":{"zero":"{n} MB","one":"{n} MB","two":"{n} MB","few":"{n} MB","many":"{n} MB","other":"{n} MB"},"short":{"zero":"{n} MB","one":"{n} MB","two":"{n} MB","few":"{n} MB","many":"{n} MB","other":"{n} MB"}},
36
+ "gigabyte": {"long":{"zero":"{n} GB","one":"{n} GB","two":"{n} GB","few":"{n} GB","many":"{n} GB","other":"{n} GB"},"short":{"zero":"{n} GB","one":"{n} GB","two":"{n} GB","few":"{n} GB","many":"{n} GB","other":"{n} GB"}},
37
+ "bitPerSecond": {"long":{"zero":"每秒 {n} bit","one":"每秒 {n} bit","two":"每秒 {n} bit","few":"每秒 {n} bit","many":"每秒 {n} bit","other":"每秒 {n} bit"},"short":{"zero":"{n} bit/秒","one":"{n} bit/秒","two":"{n} bit/秒","few":"{n} bit/秒","many":"{n} bit/秒","other":"{n} bit/秒"}},
38
+ "kilobitPerSecond": {"long":{"zero":"每秒 {n} kb","one":"每秒 {n} kb","two":"每秒 {n} kb","few":"每秒 {n} kb","many":"每秒 {n} kb","other":"每秒 {n} kb"},"short":{"zero":"{n} kb/秒","one":"{n} kb/秒","two":"{n} kb/秒","few":"{n} kb/秒","many":"{n} kb/秒","other":"{n} kb/秒"}},
39
+ "megabitPerSecond": {"long":{"zero":"每秒 {n} Mb","one":"每秒 {n} Mb","two":"每秒 {n} Mb","few":"每秒 {n} Mb","many":"每秒 {n} Mb","other":"每秒 {n} Mb"},"short":{"zero":"{n} Mb/秒","one":"{n} Mb/秒","two":"{n} Mb/秒","few":"{n} Mb/秒","many":"{n} Mb/秒","other":"{n} Mb/秒"}},
40
+ "gigabitPerSecond": {"long":{"zero":"每秒 {n} Gb","one":"每秒 {n} Gb","two":"每秒 {n} Gb","few":"每秒 {n} Gb","many":"每秒 {n} Gb","other":"每秒 {n} Gb"},"short":{"zero":"{n} Gb/秒","one":"{n} Gb/秒","two":"{n} Gb/秒","few":"{n} Gb/秒","many":"{n} Gb/秒","other":"{n} Gb/秒"}},
41
+ "bytePerSecond": {"long":{"zero":"每秒 {n} byte","one":"每秒 {n} byte","two":"每秒 {n} byte","few":"每秒 {n} byte","many":"每秒 {n} byte","other":"每秒 {n} byte"},"short":{"zero":"{n} byte/秒","one":"{n} byte/秒","two":"{n} byte/秒","few":"{n} byte/秒","many":"{n} byte/秒","other":"{n} byte/秒"}},
42
+ "kilobytePerSecond": {"long":{"zero":"每秒 {n} kB","one":"每秒 {n} kB","two":"每秒 {n} kB","few":"每秒 {n} kB","many":"每秒 {n} kB","other":"每秒 {n} kB"},"short":{"zero":"{n} kB/秒","one":"{n} kB/秒","two":"{n} kB/秒","few":"{n} kB/秒","many":"{n} kB/秒","other":"{n} kB/秒"}},
43
+ "milligram": {"long":{"zero":"{n} 毫克","one":"{n} 毫克","two":"{n} 毫克","few":"{n} 毫克","many":"{n} 毫克","other":"{n} 毫克"},"short":{"zero":"{n} 毫克","one":"{n} 毫克","two":"{n} 毫克","few":"{n} 毫克","many":"{n} 毫克","other":"{n} 毫克"}},
44
+ "gram": {"long":{"zero":"{n} 克","one":"{n} 克","two":"{n} 克","few":"{n} 克","many":"{n} 克","other":"{n} 克"},"short":{"zero":"{n} 克","one":"{n} 克","two":"{n} 克","few":"{n} 克","many":"{n} 克","other":"{n} 克"}},
45
+ "kilogram": {"long":{"zero":"{n} 公斤","one":"{n} 公斤","two":"{n} 公斤","few":"{n} 公斤","many":"{n} 公斤","other":"{n} 公斤"},"short":{"zero":"{n} 公斤","one":"{n} 公斤","two":"{n} 公斤","few":"{n} 公斤","many":"{n} 公斤","other":"{n} 公斤"}},
46
+ "tonne": {"long":{"zero":"{n} 公噸","one":"{n} 公噸","two":"{n} 公噸","few":"{n} 公噸","many":"{n} 公噸","other":"{n} 公噸"},"short":{"zero":"{n} 公噸","one":"{n} 公噸","two":"{n} 公噸","few":"{n} 公噸","many":"{n} 公噸","other":"{n} 公噸"}},
47
+ "ounce": {"long":{"zero":"{n} 盎司","one":"{n} 盎司","two":"{n} 盎司","few":"{n} 盎司","many":"{n} 盎司","other":"{n} 盎司"},"short":{"zero":"{n} 盎司","one":"{n} 盎司","two":"{n} 盎司","few":"{n} 盎司","many":"{n} 盎司","other":"{n} 盎司"}},
48
+ "pound": {"long":{"zero":"{n} 磅","one":"{n} 磅","two":"{n} 磅","few":"{n} 磅","many":"{n} 磅","other":"{n} 磅"},"short":{"zero":"{n} 磅","one":"{n} 磅","two":"{n} 磅","few":"{n} 磅","many":"{n} 磅","other":"{n} 磅"}},
49
+ "stone": {"long":{"zero":"{n} 英石","one":"{n} 英石","two":"{n} 英石","few":"{n} 英石","many":"{n} 英石","other":"{n} 英石"},"short":{"zero":"{n} 英石","one":"{n} 英石","two":"{n} 英石","few":"{n} 英石","many":"{n} 英石","other":"{n} 英石"}},
50
+ "robloxMassUnit": {"long":{"zero":"{n} Roblox mass units","one":"{n} Roblox mass units","two":"{n} Roblox mass units","few":"{n} Roblox mass units","many":"{n} Roblox mass units","other":"{n} Roblox mass units"},"short":{"zero":"{n} RMU","one":"{n} RMU","two":"{n} RMU","few":"{n} RMU","many":"{n} RMU","other":"{n} RMU"}},
51
+ "meterPerSecond": {"long":{"zero":"每秒 {n} 公尺","one":"每秒 {n} 公尺","two":"每秒 {n} 公尺","few":"每秒 {n} 公尺","many":"每秒 {n} 公尺","other":"每秒 {n} 公尺"},"short":{"zero":"{n} 公尺/秒","one":"{n} 公尺/秒","two":"{n} 公尺/秒","few":"{n} 公尺/秒","many":"{n} 公尺/秒","other":"{n} 公尺/秒"}},
52
+ "kilometerPerHour": {"long":{"zero":"每小時 {n} 公里","one":"每小時 {n} 公里","two":"每小時 {n} 公里","few":"每小時 {n} 公里","many":"每小時 {n} 公里","other":"每小時 {n} 公里"},"short":{"zero":"{n} 公里/小時","one":"{n} 公里/小時","two":"{n} 公里/小時","few":"{n} 公里/小時","many":"{n} 公里/小時","other":"{n} 公里/小時"}},
53
+ "milePerHour": {"long":{"zero":"每小時 {n} 英里","one":"每小時 {n} 英里","two":"每小時 {n} 英里","few":"每小時 {n} 英里","many":"每小時 {n} 英里","other":"每小時 {n} 英里"},"short":{"zero":"{n} 英里/小時","one":"{n} 英里/小時","two":"{n} 英里/小時","few":"{n} 英里/小時","many":"{n} 英里/小時","other":"{n} 英里/小時"}},
54
+ "knot": {"long":{"zero":"{n} 節","one":"{n} 節","two":"{n} 節","few":"{n} 節","many":"{n} 節","other":"{n} 節"},"short":{"zero":"{n} 節","one":"{n} 節","two":"{n} 節","few":"{n} 節","many":"{n} 節","other":"{n} 節"}},
55
+ "studPerSecond": {"long":{"zero":"每秒 {n} studs","one":"每秒 {n} studs","two":"每秒 {n} studs","few":"每秒 {n} studs","many":"每秒 {n} studs","other":"每秒 {n} studs"},"short":{"zero":"{n} studs/秒","one":"{n} studs/秒","two":"{n} studs/秒","few":"{n} studs/秒","many":"{n} studs/秒","other":"{n} studs/秒"}},
56
+ "meterPerSecondSquared": {"long":{"zero":"每平方秒 {n} 公尺","one":"每平方秒 {n} 公尺","two":"每平方秒 {n} 公尺","few":"每平方秒 {n} 公尺","many":"每平方秒 {n} 公尺","other":"每平方秒 {n} 公尺"},"short":{"zero":"{n} 公尺/平方秒","one":"{n} 公尺/平方秒","two":"{n} 公尺/平方秒","few":"{n} 公尺/平方秒","many":"{n} 公尺/平方秒","other":"{n} 公尺/平方秒"}},
57
+ "studPerSecondSquared": {"long":{"zero":"每秒² {n} studs","one":"每秒² {n} studs","two":"每秒² {n} studs","few":"每秒² {n} studs","many":"每秒² {n} studs","other":"每秒² {n} studs"},"short":{"zero":"{n} studs/秒²","one":"{n} studs/秒²","two":"{n} studs/秒²","few":"{n} studs/秒²","many":"{n} studs/秒²","other":"{n} studs/秒²"}},
58
+ "newton": {"long":{"zero":"{n} 牛頓","one":"{n} 牛頓","two":"{n} 牛頓","few":"{n} 牛頓","many":"{n} 牛頓","other":"{n} 牛頓"},"short":{"zero":"{n} N","one":"{n} N","two":"{n} N","few":"{n} N","many":"{n} N","other":"{n} N"}},
59
+ "poundForce": {"long":{"zero":"{n} 磅力","one":"{n} 磅力","two":"{n} 磅力","few":"{n} 磅力","many":"{n} 磅力","other":"{n} 磅力"},"short":{"zero":"{n} lbf","one":"{n} lbf","two":"{n} lbf","few":"{n} lbf","many":"{n} lbf","other":"{n} lbf"}},
60
+ "joule": {"long":{"zero":"{n} 焦耳","one":"{n} 焦耳","two":"{n} 焦耳","few":"{n} 焦耳","many":"{n} 焦耳","other":"{n} 焦耳"},"short":{"zero":"{n} 焦","one":"{n} 焦","two":"{n} 焦","few":"{n} 焦","many":"{n} 焦","other":"{n} 焦"}},
61
+ "kilojoule": {"long":{"zero":"{n} 千焦耳","one":"{n} 千焦耳","two":"{n} 千焦耳","few":"{n} 千焦耳","many":"{n} 千焦耳","other":"{n} 千焦耳"},"short":{"zero":"{n} 千焦","one":"{n} 千焦","two":"{n} 千焦","few":"{n} 千焦","many":"{n} 千焦","other":"{n} 千焦"}},
62
+ "kilowattHour": {"long":{"zero":"{n} 千瓦小時","one":"{n} 千瓦小時","two":"{n} 千瓦小時","few":"{n} 千瓦小時","many":"{n} 千瓦小時","other":"{n} 千瓦小時"},"short":{"zero":"{n} 千瓦小時","one":"{n} 千瓦小時","two":"{n} 千瓦小時","few":"{n} 千瓦小時","many":"{n} 千瓦小時","other":"{n} 千瓦小時"}},
63
+ "kilocalorie": {"long":{"zero":"{n} 千卡路里","one":"{n} 千卡路里","two":"{n} 千卡路里","few":"{n} 千卡路里","many":"{n} 千卡路里","other":"{n} 千卡路里"},"short":{"zero":"{n} 千卡","one":"{n} 千卡","two":"{n} 千卡","few":"{n} 千卡","many":"{n} 千卡","other":"{n} 千卡"}},
64
+ "watt": {"long":{"zero":"{n} 瓦特","one":"{n} 瓦特","two":"{n} 瓦特","few":"{n} 瓦特","many":"{n} 瓦特","other":"{n} 瓦特"},"short":{"zero":"{n} 瓦","one":"{n} 瓦","two":"{n} 瓦","few":"{n} 瓦","many":"{n} 瓦","other":"{n} 瓦"}},
65
+ "kilowatt": {"long":{"zero":"{n} 千瓦特","one":"{n} 千瓦特","two":"{n} 千瓦特","few":"{n} 千瓦特","many":"{n} 千瓦特","other":"{n} 千瓦特"},"short":{"zero":"{n} 千瓦","one":"{n} 千瓦","two":"{n} 千瓦","few":"{n} 千瓦","many":"{n} 千瓦","other":"{n} 千瓦"}},
66
+ "megawatt": {"long":{"zero":"{n} 百萬瓦特","one":"{n} 百萬瓦特","two":"{n} 百萬瓦特","few":"{n} 百萬瓦特","many":"{n} 百萬瓦特","other":"{n} 百萬瓦特"},"short":{"zero":"{n} 百萬瓦","one":"{n} 百萬瓦","two":"{n} 百萬瓦","few":"{n} 百萬瓦","many":"{n} 百萬瓦","other":"{n} 百萬瓦"}},
67
+ "pascal": {"long":{"zero":"{n} 帕斯卡","one":"{n} 帕斯卡","two":"{n} 帕斯卡","few":"{n} 帕斯卡","many":"{n} 帕斯卡","other":"{n} 帕斯卡"},"short":{"zero":"{n} 帕","one":"{n} 帕","two":"{n} 帕","few":"{n} 帕","many":"{n} 帕","other":"{n} 帕"}},
68
+ "kilopascal": {"long":{"zero":"{n} 千帕","one":"{n} 千帕","two":"{n} 千帕","few":"{n} 千帕","many":"{n} 千帕","other":"{n} 千帕"},"short":{"zero":"{n} 千帕","one":"{n} 千帕","two":"{n} 千帕","few":"{n} 千帕","many":"{n} 千帕","other":"{n} 千帕"}},
69
+ "megapascal": {"long":{"zero":"{n} 兆帕","one":"{n} 兆帕","two":"{n} 兆帕","few":"{n} 兆帕","many":"{n} 兆帕","other":"{n} 兆帕"},"short":{"zero":"{n} 兆帕","one":"{n} 兆帕","two":"{n} 兆帕","few":"{n} 兆帕","many":"{n} 兆帕","other":"{n} 兆帕"}},
70
+ "bar": {"long":{"zero":"{n} 巴","one":"{n} 巴","two":"{n} 巴","few":"{n} 巴","many":"{n} 巴","other":"{n} 巴"},"short":{"zero":"{n} 巴","one":"{n} 巴","two":"{n} 巴","few":"{n} 巴","many":"{n} 巴","other":"{n} 巴"}},
71
+ "poundForcePerSquareInch": {"long":{"zero":"每平方英寸 {n} 磅力","one":"每平方英寸 {n} 磅力","two":"每平方英寸 {n} 磅力","few":"每平方英寸 {n} 磅力","many":"每平方英寸 {n} 磅力","other":"每平方英寸 {n} 磅力"},"short":{"zero":"{n} 磅力/平方英寸","one":"{n} 磅力/平方英寸","two":"{n} 磅力/平方英寸","few":"{n} 磅力/平方英寸","many":"{n} 磅力/平方英寸","other":"{n} 磅力/平方英寸"}},
72
+ "kilogramPerCubicMeter": {"long":{"zero":"每立方公尺 {n} 公斤","one":"每立方公尺 {n} 公斤","two":"每立方公尺 {n} 公斤","few":"每立方公尺 {n} 公斤","many":"每立方公尺 {n} 公斤","other":"每立方公尺 {n} 公斤"},"short":{"zero":"{n} 公斤/立方公尺","one":"{n} 公斤/立方公尺","two":"{n} 公斤/立方公尺","few":"{n} 公斤/立方公尺","many":"{n} 公斤/立方公尺","other":"{n} 公斤/立方公尺"}},
73
+ "gramPerCubicCentimeter": {"long":{"zero":"每立方公分 {n} 克","one":"每立方公分 {n} 克","two":"每立方公分 {n} 克","few":"每立方公分 {n} 克","many":"每立方公分 {n} 克","other":"每立方公分 {n} 克"},"short":{"zero":"{n} 克/立方公分","one":"{n} 克/立方公分","two":"{n} 克/立方公分","few":"{n} 克/立方公分","many":"{n} 克/立方公分","other":"{n} 克/立方公分"}},
74
+ "kelvin": {"long":{"zero":"{n} 克耳文","one":"{n} 克耳文","two":"{n} 克耳文","few":"{n} 克耳文","many":"{n} 克耳文","other":"{n} 克耳文"},"short":{"zero":"{n} 克耳文","one":"{n} 克耳文","two":"{n} 克耳文","few":"{n} 克耳文","many":"{n} 克耳文","other":"{n} 克耳文"}},
75
+ "celsius": {"long":{"zero":"攝氏 {n} 度","one":"攝氏 {n} 度","two":"攝氏 {n} 度","few":"攝氏 {n} 度","many":"攝氏 {n} 度","other":"攝氏 {n} 度"},"short":{"zero":"{n}°C","one":"{n}°C","two":"{n}°C","few":"{n}°C","many":"{n}°C","other":"{n}°C"}},
76
+ "fahrenheit": {"long":{"zero":"華氏 {n} 度","one":"華氏 {n} 度","two":"華氏 {n} 度","few":"華氏 {n} 度","many":"華氏 {n} 度","other":"華氏 {n} 度"},"short":{"zero":"{n}°F","one":"{n}°F","two":"{n}°F","few":"{n}°F","many":"{n}°F","other":"{n}°F"}},
77
+ "radian": {"long":{"zero":"{n} 弧度","one":"{n} 弧度","two":"{n} 弧度","few":"{n} 弧度","many":"{n} 弧度","other":"{n} 弧度"},"short":{"zero":"{n} 弧度","one":"{n} 弧度","two":"{n} 弧度","few":"{n} 弧度","many":"{n} 弧度","other":"{n} 弧度"}},
78
+ "degree": {"long":{"zero":"{n} 度","one":"{n} 度","two":"{n} 度","few":"{n} 度","many":"{n} 度","other":"{n} 度"},"short":{"zero":"{n} 度","one":"{n} 度","two":"{n} 度","few":"{n} 度","many":"{n} 度","other":"{n} 度"}},
79
+ "revolution": {"long":{"zero":"{n} 圈","one":"{n} 圈","two":"{n} 圈","few":"{n} 圈","many":"{n} 圈","other":"{n} 圈"},"short":{"zero":"{n} 圈","one":"{n} 圈","two":"{n} 圈","few":"{n} 圈","many":"{n} 圈","other":"{n} 圈"}},
80
+ "radianPerSecond": {"long":{"zero":"每秒 {n} 弧度","one":"每秒 {n} 弧度","two":"每秒 {n} 弧度","few":"每秒 {n} 弧度","many":"每秒 {n} 弧度","other":"每秒 {n} 弧度"},"short":{"zero":"{n} 弧度/秒","one":"{n} 弧度/秒","two":"{n} 弧度/秒","few":"{n} 弧度/秒","many":"{n} 弧度/秒","other":"{n} 弧度/秒"}},
81
+ "degreePerSecond": {"long":{"zero":"每秒 {n} 度","one":"每秒 {n} 度","two":"每秒 {n} 度","few":"每秒 {n} 度","many":"每秒 {n} 度","other":"每秒 {n} 度"},"short":{"zero":"{n} 度/秒","one":"{n} 度/秒","two":"{n} 度/秒","few":"{n} 度/秒","many":"{n} 度/秒","other":"{n} 度/秒"}},
82
+ "revolutionPerMinute": {"long":{"zero":"每分鐘 {n} 圈","one":"每分鐘 {n} 圈","two":"每分鐘 {n} 圈","few":"每分鐘 {n} 圈","many":"每分鐘 {n} 圈","other":"每分鐘 {n} 圈"},"short":{"zero":"{n} 圈/分鐘","one":"{n} 圈/分鐘","two":"{n} 圈/分鐘","few":"{n} 圈/分鐘","many":"{n} 圈/分鐘","other":"{n} 圈/分鐘"}},
83
+ "radianPerSecondSquared": {"long":{"zero":"每秒² {n} 弧度","one":"每秒² {n} 弧度","two":"每秒² {n} 弧度","few":"每秒² {n} 弧度","many":"每秒² {n} 弧度","other":"每秒² {n} 弧度"},"short":{"zero":"{n} 弧度/秒²","one":"{n} 弧度/秒²","two":"{n} 弧度/秒²","few":"{n} 弧度/秒²","many":"{n} 弧度/秒²","other":"{n} 弧度/秒²"}},
84
+ "milliampere": {"long":{"zero":"{n} 毫安培","one":"{n} 毫安培","two":"{n} 毫安培","few":"{n} 毫安培","many":"{n} 毫安培","other":"{n} 毫安培"},"short":{"zero":"{n} 毫安培","one":"{n} 毫安培","two":"{n} 毫安培","few":"{n} 毫安培","many":"{n} 毫安培","other":"{n} 毫安培"}},
85
+ "ampere": {"long":{"zero":"{n} 安培","one":"{n} 安培","two":"{n} 安培","few":"{n} 安培","many":"{n} 安培","other":"{n} 安培"},"short":{"zero":"{n} 安培","one":"{n} 安培","two":"{n} 安培","few":"{n} 安培","many":"{n} 安培","other":"{n} 安培"}},
86
+ "coulomb": {"long":{"zero":"{n} 庫侖","one":"{n} 庫侖","two":"{n} 庫侖","few":"{n} 庫侖","many":"{n} 庫侖","other":"{n} 庫侖"},"short":{"zero":"{n} 庫侖","one":"{n} 庫侖","two":"{n} 庫侖","few":"{n} 庫侖","many":"{n} 庫侖","other":"{n} 庫侖"}},
87
+ "volt": {"long":{"zero":"{n} 伏特","one":"{n} 伏特","two":"{n} 伏特","few":"{n} 伏特","many":"{n} 伏特","other":"{n} 伏特"},"short":{"zero":"{n} 伏特","one":"{n} 伏特","two":"{n} 伏特","few":"{n} 伏特","many":"{n} 伏特","other":"{n} 伏特"}},
88
+ "ohm": {"long":{"zero":"{n} 歐姆","one":"{n} 歐姆","two":"{n} 歐姆","few":"{n} 歐姆","many":"{n} 歐姆","other":"{n} 歐姆"},"short":{"zero":"{n} 歐姆","one":"{n} 歐姆","two":"{n} 歐姆","few":"{n} 歐姆","many":"{n} 歐姆","other":"{n} 歐姆"}},
89
+ "siemens": {"long":{"zero":"{n} 西門子","one":"{n} 西門子","two":"{n} 西門子","few":"{n} 西門子","many":"{n} 西門子","other":"{n} 西門子"},"short":{"zero":"{n} S","one":"{n} S","two":"{n} S","few":"{n} S","many":"{n} S","other":"{n} S"}},
90
+ "farad": {"long":{"zero":"{n} 法拉","one":"{n} 法拉","two":"{n} 法拉","few":"{n} 法拉","many":"{n} 法拉","other":"{n} 法拉"},"short":{"zero":"{n} 法拉","one":"{n} 法拉","two":"{n} 法拉","few":"{n} 法拉","many":"{n} 法拉","other":"{n} 法拉"}},
91
+ "henry": {"long":{"zero":"{n} 亨利","one":"{n} 亨利","two":"{n} 亨利","few":"{n} 亨利","many":"{n} 亨利","other":"{n} 亨利"},"short":{"zero":"{n} 亨利","one":"{n} 亨利","two":"{n} 亨利","few":"{n} 亨利","many":"{n} 亨利","other":"{n} 亨利"}},
92
+ "weber": {"long":{"zero":"{n} 韋伯","one":"{n} 韋伯","two":"{n} 韋伯","few":"{n} 韋伯","many":"{n} 韋伯","other":"{n} 韋伯"},"short":{"zero":"{n} Wb","one":"{n} Wb","two":"{n} Wb","few":"{n} Wb","many":"{n} Wb","other":"{n} Wb"}},
93
+ "tesla": {"long":{"zero":"{n} 特斯拉","one":"{n} 特斯拉","two":"{n} 特斯拉","few":"{n} 特斯拉","many":"{n} 特斯拉","other":"{n} 特斯拉"},"short":{"zero":"{n} T","one":"{n} T","two":"{n} T","few":"{n} T","many":"{n} T","other":"{n} T"}},
94
+ "becquerel": {"long":{"zero":"{n} 貝克勒","one":"{n} 貝克勒","two":"{n} 貝克勒","few":"{n} 貝克勒","many":"{n} 貝克勒","other":"{n} 貝克勒"},"short":{"zero":"{n} Bq","one":"{n} Bq","two":"{n} Bq","few":"{n} Bq","many":"{n} Bq","other":"{n} Bq"}},
95
+ "mole": {"long":{"zero":"{n} 莫耳","one":"{n} 莫耳","two":"{n} 莫耳","few":"{n} 莫耳","many":"{n} 莫耳","other":"{n} 莫耳"},"short":{"zero":"{n} 莫耳","one":"{n} 莫耳","two":"{n} 莫耳","few":"{n} 莫耳","many":"{n} 莫耳","other":"{n} 莫耳"}},
96
+ "gramPerMole": {"long":{"zero":"每莫耳 {n} 克","one":"每莫耳 {n} 克","two":"每莫耳 {n} 克","few":"每莫耳 {n} 克","many":"每莫耳 {n} 克","other":"每莫耳 {n} 克"},"short":{"zero":"{n} 克/莫耳","one":"{n} 克/莫耳","two":"{n} 克/莫耳","few":"{n} 克/莫耳","many":"{n} 克/莫耳","other":"{n} 克/莫耳"}},
97
+ "molePerLiter": {"long":{"zero":"每公升 {n} 莫耳","one":"每公升 {n} 莫耳","two":"每公升 {n} 莫耳","few":"每公升 {n} 莫耳","many":"每公升 {n} 莫耳","other":"每公升 {n} 莫耳"},"short":{"zero":"{n} 莫耳/升","one":"{n} 莫耳/升","two":"{n} 莫耳/升","few":"{n} 莫耳/升","many":"{n} 莫耳/升","other":"{n} 莫耳/升"}},
98
+ "millimolePerLiter": {"long":{"zero":"每公升 {n} 毫莫耳","one":"每公升 {n} 毫莫耳","two":"每公升 {n} 毫莫耳","few":"每公升 {n} 毫莫耳","many":"每公升 {n} 毫莫耳","other":"每公升 {n} 毫莫耳"},"short":{"zero":"{n} 毫莫耳/公升","one":"{n} 毫莫耳/公升","two":"{n} 毫莫耳/公升","few":"{n} 毫莫耳/公升","many":"{n} 毫莫耳/公升","other":"{n} 毫莫耳/公升"}},
99
+ "katal": {"long":{"zero":"{n} 開特","one":"{n} 開特","two":"{n} 開特","few":"{n} 開特","many":"{n} 開特","other":"{n} 開特"},"short":{"zero":"{n} kat","one":"{n} kat","two":"{n} kat","few":"{n} kat","many":"{n} kat","other":"{n} kat"}},
100
+ "candela": {"long":{"zero":"{n} 燭光","one":"{n} 燭光","two":"{n} 燭光","few":"{n} 燭光","many":"{n} 燭光","other":"{n} 燭光"},"short":{"zero":"{n} 燭光","one":"{n} 燭光","two":"{n} 燭光","few":"{n} 燭光","many":"{n} 燭光","other":"{n} 燭光"}},
101
+ "steradian": {"long":{"zero":"{n} 球面度","one":"{n} 球面度","two":"{n} 球面度","few":"{n} 球面度","many":"{n} 球面度","other":"{n} 球面度"},"short":{"zero":"{n} sr","one":"{n} sr","two":"{n} sr","few":"{n} sr","many":"{n} sr","other":"{n} sr"}},
102
+ "lumen": {"long":{"zero":"{n} lm","one":"{n} lm","two":"{n} lm","few":"{n} lm","many":"{n} lm","other":"{n} lm"},"short":{"zero":"{n} lm","one":"{n} lm","two":"{n} lm","few":"{n} lm","many":"{n} lm","other":"{n} lm"}},
103
+ "lux": {"long":{"zero":"{n} 勒克斯","one":"{n} 勒克斯","two":"{n} 勒克斯","few":"{n} 勒克斯","many":"{n} 勒克斯","other":"{n} 勒克斯"},"short":{"zero":"{n} 勒克斯","one":"{n} 勒克斯","two":"{n} 勒克斯","few":"{n} 勒克斯","many":"{n} 勒克斯","other":"{n} 勒克斯"}},
104
+ "gray": {"long":{"zero":"{n} 戈雷","one":"{n} 戈雷","two":"{n} 戈雷","few":"{n} 戈雷","many":"{n} 戈雷","other":"{n} 戈雷"},"short":{"zero":"{n} Gy","one":"{n} Gy","two":"{n} Gy","few":"{n} Gy","many":"{n} Gy","other":"{n} Gy"}},
105
+ "sievert": {"long":{"zero":"{n} 西弗","one":"{n} 西弗","two":"{n} 西弗","few":"{n} 西弗","many":"{n} 西弗","other":"{n} 西弗"},"short":{"zero":"{n} Sv","one":"{n} Sv","two":"{n} Sv","few":"{n} Sv","many":"{n} Sv","other":"{n} Sv"}},
106
+ "kilohm": {"long":{"zero":"{n} 歐姆 公里","one":"{n} 歐姆 公里","two":"{n} 歐姆 公里","few":"{n} 歐姆 公里","many":"{n} 歐姆 公里","other":"{n} 歐姆 公里"},"short":{"zero":"{n} 歐姆 公里","one":"{n} 歐姆 公里","two":"{n} 歐姆 公里","few":"{n} 歐姆 公里","many":"{n} 歐姆 公里","other":"{n} 歐姆 公里"}},
107
+ "megohm": {"long":{"zero":"{n} 歐姆 兆米","one":"{n} 歐姆 兆米","two":"{n} 歐姆 兆米","few":"{n} 歐姆 兆米","many":"{n} 歐姆 兆米","other":"{n} 歐姆 兆米"},"short":{"zero":"{n} 歐姆 兆米","one":"{n} 歐姆 兆米","two":"{n} 歐姆 兆米","few":"{n} 歐姆 兆米","many":"{n} 歐姆 兆米","other":"{n} 歐姆 兆米"}},
108
+ "nauticalMile": {"long":{"zero":"{n} 海里","one":"{n} 海里","two":"{n} 海里","few":"{n} 海里","many":"{n} 海里","other":"{n} 海里"},"short":{"zero":"{n} 海里","one":"{n} 海里","two":"{n} 海里","few":"{n} 海里","many":"{n} 海里","other":"{n} 海里"}},
109
+ "literPer100Kilometers": {"long":{"zero":"每 100 公里 {n} 公升","one":"每 100 公里 {n} 公升","two":"每 100 公里 {n} 公升","few":"每 100 公里 {n} 公升","many":"每 100 公里 {n} 公升","other":"每 100 公里 {n} 公升"},"short":{"zero":"{n} 升/100 公里","one":"{n} 升/100 公里","two":"{n} 升/100 公里","few":"{n} 升/100 公里","many":"{n} 升/100 公里","other":"{n} 升/100 公里"}},
110
+ "squareInch": {"long":{"zero":"{n} 平方英寸","one":"{n} 平方英寸","two":"{n} 平方英寸","few":"{n} 平方英寸","many":"{n} 平方英寸","other":"{n} 平方英寸"},"short":{"zero":"{n} 平方英寸","one":"{n} 平方英寸","two":"{n} 平方英寸","few":"{n} 平方英寸","many":"{n} 平方英寸","other":"{n} 平方英寸"}},
111
+ "squareYard": {"long":{"zero":"{n} 平方碼","one":"{n} 平方碼","two":"{n} 平方碼","few":"{n} 平方碼","many":"{n} 平方碼","other":"{n} 平方碼"},"short":{"zero":"{n} 平方碼","one":"{n} 平方碼","two":"{n} 平方碼","few":"{n} 平方碼","many":"{n} 平方碼","other":"{n} 平方碼"}},
112
+ "squareMile": {"long":{"zero":"{n} 平方英里","one":"{n} 平方英里","two":"{n} 平方英里","few":"{n} 平方英里","many":"{n} 平方英里","other":"{n} 平方英里"},"short":{"zero":"{n} 平方英里","one":"{n} 平方英里","two":"{n} 平方英里","few":"{n} 平方英里","many":"{n} 平方英里","other":"{n} 平方英里"}},
113
+ "cubicInch": {"long":{"zero":"{n} 立方英寸","one":"{n} 立方英寸","two":"{n} 立方英寸","few":"{n} 立方英寸","many":"{n} 立方英寸","other":"{n} 立方英寸"},"short":{"zero":"{n} 立方英寸","one":"{n} 立方英寸","two":"{n} 立方英寸","few":"{n} 立方英寸","many":"{n} 立方英寸","other":"{n} 立方英寸"}},
114
+ "cubicFoot": {"long":{"zero":"{n} 立方英尺","one":"{n} 立方英尺","two":"{n} 立方英尺","few":"{n} 立方英尺","many":"{n} 立方英尺","other":"{n} 立方英尺"},"short":{"zero":"{n} 立方英尺","one":"{n} 立方英尺","two":"{n} 立方英尺","few":"{n} 立方英尺","many":"{n} 立方英尺","other":"{n} 立方英尺"}},
115
+ "cubicYard": {"long":{"zero":"{n} 立方碼","one":"{n} 立方碼","two":"{n} 立方碼","few":"{n} 立方碼","many":"{n} 立方碼","other":"{n} 立方碼"},"short":{"zero":"{n} 立方碼","one":"{n} 立方碼","two":"{n} 立方碼","few":"{n} 立方碼","many":"{n} 立方碼","other":"{n} 立方碼"}},
116
+ "usTeaspoon": {"long":{"zero":"{n} 茶匙","one":"{n} 茶匙","two":"{n} 茶匙","few":"{n} 茶匙","many":"{n} 茶匙","other":"{n} 茶匙"},"short":{"zero":"{n} 茶匙","one":"{n} 茶匙","two":"{n} 茶匙","few":"{n} 茶匙","many":"{n} 茶匙","other":"{n} 茶匙"}},
117
+ "usTablespoon": {"long":{"zero":"{n} 湯匙","one":"{n} 湯匙","two":"{n} 湯匙","few":"{n} 湯匙","many":"{n} 湯匙","other":"{n} 湯匙"},"short":{"zero":"{n} 湯匙","one":"{n} 湯匙","two":"{n} 湯匙","few":"{n} 湯匙","many":"{n} 湯匙","other":"{n} 湯匙"}},
118
+ "usCup": {"long":{"zero":"{n} 杯","one":"{n} 杯","two":"{n} 杯","few":"{n} 杯","many":"{n} 杯","other":"{n} 杯"},"short":{"zero":"{n} 杯","one":"{n} 杯","two":"{n} 杯","few":"{n} 杯","many":"{n} 杯","other":"{n} 杯"}},
119
+ "usPint": {"long":{"zero":"{n} 品脫","one":"{n} 品脫","two":"{n} 品脫","few":"{n} 品脫","many":"{n} 品脫","other":"{n} 品脫"},"short":{"zero":"{n} 品脫","one":"{n} 品脫","two":"{n} 品脫","few":"{n} 品脫","many":"{n} 品脫","other":"{n} 品脫"}},
120
+ "usQuart": {"long":{"zero":"{n} 夸脫","one":"{n} 夸脫","two":"{n} 夸脫","few":"{n} 夸脫","many":"{n} 夸脫","other":"{n} 夸脫"},"short":{"zero":"{n} 夸脫","one":"{n} 夸脫","two":"{n} 夸脫","few":"{n} 夸脫","many":"{n} 夸脫","other":"{n} 夸脫"}},
121
+ "imperialFluidOunce": {"long":{"zero":"{n} 英液盎司","one":"{n} 英液盎司","two":"{n} 英液盎司","few":"{n} 英液盎司","many":"{n} 英液盎司","other":"{n} 英液盎司"},"short":{"zero":"{n} 英液盎司","one":"{n} 英液盎司","two":"{n} 英液盎司","few":"{n} 英液盎司","many":"{n} 英液盎司","other":"{n} 英液盎司"}},
122
+ "imperialPint": {"long":{"zero":"{n} pt Imp.","one":"{n} pt Imp.","two":"{n} pt Imp.","few":"{n} pt Imp.","many":"{n} pt Imp.","other":"{n} pt Imp."},"short":{"zero":"{n} pt Imp.","one":"{n} pt Imp.","two":"{n} pt Imp.","few":"{n} pt Imp.","many":"{n} pt Imp.","other":"{n} pt Imp."}},
123
+ "imperialQuart": {"long":{"zero":"{n} 英制夸脫","one":"{n} 英制夸脫","two":"{n} 英制夸脫","few":"{n} 英制夸脫","many":"{n} 英制夸脫","other":"{n} 英制夸脫"},"short":{"zero":"{n} 英制夸脫","one":"{n} 英制夸脫","two":"{n} 英制夸脫","few":"{n} 英制夸脫","many":"{n} 英制夸脫","other":"{n} 英制夸脫"}},
124
+ "shortTon": {"long":{"zero":"{n} 美噸","one":"{n} 美噸","two":"{n} 美噸","few":"{n} 美噸","many":"{n} 美噸","other":"{n} 美噸"},"short":{"zero":"{n} 美噸","one":"{n} 美噸","two":"{n} 美噸","few":"{n} 美噸","many":"{n} 美噸","other":"{n} 美噸"}},
125
+ "footPerSecond": {"long":{"zero":"每秒 {n} 英尺","one":"每秒 {n} 英尺","two":"每秒 {n} 英尺","few":"每秒 {n} 英尺","many":"每秒 {n} 英尺","other":"每秒 {n} 英尺"},"short":{"zero":"{n}英尺/秒","one":"{n}英尺/秒","two":"{n}英尺/秒","few":"{n}英尺/秒","many":"{n}英尺/秒","other":"{n}英尺/秒"}},
126
+ "footPerSecondSquared": {"long":{"zero":"每秒² {n} 英尺","one":"每秒² {n} 英尺","two":"每秒² {n} 英尺","few":"每秒² {n} 英尺","many":"每秒² {n} 英尺","other":"每秒² {n} 英尺"},"short":{"zero":"{n}英尺/秒²","one":"{n}英尺/秒²","two":"{n}英尺/秒²","few":"{n}英尺/秒²","many":"{n}英尺/秒²","other":"{n}英尺/秒²"}},
127
+ "newtonMeter": {"long":{"zero":"{n} 牛頓米","one":"{n} 牛頓米","two":"{n} 牛頓米","few":"{n} 牛頓米","many":"{n} 牛頓米","other":"{n} 牛頓米"},"short":{"zero":"{n} 牛頓米","one":"{n} 牛頓米","two":"{n} 牛頓米","few":"{n} 牛頓米","many":"{n} 牛頓米","other":"{n} 牛頓米"}},
128
+ "britishThermalUnit": {"long":{"zero":"{n} 英熱單位","one":"{n} 英熱單位","two":"{n} 英熱單位","few":"{n} 英熱單位","many":"{n} 英熱單位","other":"{n} 英熱單位"},"short":{"zero":"{n} 英熱單位","one":"{n} 英熱單位","two":"{n} 英熱單位","few":"{n} 英熱單位","many":"{n} 英熱單位","other":"{n} 英熱單位"}},
129
+ "poundFoot": {"long":{"zero":"{n} 磅英尺","one":"{n} 磅英尺","two":"{n} 磅英尺","few":"{n} 磅英尺","many":"{n} 磅英尺","other":"{n} 磅英尺"},"short":{"zero":"{n} 磅英尺","one":"{n} 磅英尺","two":"{n} 磅英尺","few":"{n} 磅英尺","many":"{n} 磅英尺","other":"{n} 磅英尺"}},
130
+ "metricHorsepower": {"long":{"zero":"{n} PS","one":"{n} PS","two":"{n} PS","few":"{n} PS","many":"{n} PS","other":"{n} PS"},"short":{"zero":"{n} PS","one":"{n} PS","two":"{n} PS","few":"{n} PS","many":"{n} PS","other":"{n} PS"}},
131
+ "horsepower": {"long":{"zero":"{n} 匹馬力","one":"{n} 匹馬力","two":"{n} 匹馬力","few":"{n} 匹馬力","many":"{n} 匹馬力","other":"{n} 匹馬力"},"short":{"zero":"{n} 匹","one":"{n} 匹","two":"{n} 匹","few":"{n} 匹","many":"{n} 匹","other":"{n} 匹"}},
132
+ "inchOfMercury": {"long":{"zero":"{n} 英寸汞柱","one":"{n} 英寸汞柱","two":"{n} 英寸汞柱","few":"{n} 英寸汞柱","many":"{n} 英寸汞柱","other":"{n} 英寸汞柱"},"short":{"zero":"{n} 英寸汞柱","one":"{n} 英寸汞柱","two":"{n} 英寸汞柱","few":"{n} 英寸汞柱","many":"{n} 英寸汞柱","other":"{n} 英寸汞柱"}},
133
+ "poundPerCubicFoot": {"long":{"zero":"每立方英尺 {n} 磅","one":"每立方英尺 {n} 磅","two":"每立方英尺 {n} 磅","few":"每立方英尺 {n} 磅","many":"每立方英尺 {n} 磅","other":"每立方英尺 {n} 磅"},"short":{"zero":"{n} 磅/立方英尺","one":"{n} 磅/立方英尺","two":"{n} 磅/立方英尺","few":"{n} 磅/立方英尺","many":"{n} 磅/立方英尺","other":"{n} 磅/立方英尺"}},
134
+ "kilometerPerLiter": {"long":{"zero":"每公升 {n} 公里","one":"每公升 {n} 公里","two":"每公升 {n} 公里","few":"每公升 {n} 公里","many":"每公升 {n} 公里","other":"每公升 {n} 公里"},"short":{"zero":"{n} 公里/升","one":"{n} 公里/升","two":"{n} 公里/升","few":"{n} 公里/升","many":"{n} 公里/升","other":"{n} 公里/升"}},
135
+ "milePerUsGallon": {"long":{"zero":"每加侖 {n} 英里","one":"每加侖 {n} 英里","two":"每加侖 {n} 英里","few":"每加侖 {n} 英里","many":"每加侖 {n} 英里","other":"每加侖 {n} 英里"},"short":{"zero":"{n} 英里/加侖","one":"{n} 英里/加侖","two":"{n} 英里/加侖","few":"{n} 英里/加侖","many":"{n} 英里/加侖","other":"{n} 英里/加侖"}},
136
+ "milePerImperialGallon": {"long":{"zero":"每英制加侖 {n} 英里","one":"每英制加侖 {n} 英里","two":"每英制加侖 {n} 英里","few":"每英制加侖 {n} 英里","many":"每英制加侖 {n} 英里","other":"每英制加侖 {n} 英里"},"short":{"zero":"{n} 英里/英制加侖","one":"{n} 英里/英制加侖","two":"{n} 英里/英制加侖","few":"{n} 英里/英制加侖","many":"{n} 英里/英制加侖","other":"{n} 英里/英制加侖"}},
137
+ "rankine": {"long":{"zero":"{n} °R","one":"{n} °R","two":"{n} °R","few":"{n} °R","many":"{n} °R","other":"{n} °R"},"short":{"zero":"{n} °R","one":"{n} °R","two":"{n} °R","few":"{n} °R","many":"{n} °R","other":"{n} °R"}}
138
+ },
139
+ "per": {
140
+ "second": {"long":"每秒 {n}","short":"{n}/秒"},
141
+ "minute": {"long":"每分鐘 {n}","short":"{n}/分鐘"},
142
+ "hour": {"long":"每小時 {n}","short":"{n}/小時"},
143
+ "day": {"long":"每天 {n}","short":"{n}/天"},
144
+ "week": {"long":"每週 {n}","short":"{n}/週"},
145
+ "month": {"long":"每月 {n}","short":"{n}/月"},
146
+ "year": {"long":"每年 {n}","short":"{n}/年"}
147
+ },
148
+ "prefixes": {
149
+ "quecto": {"long":"{unit} 奎米","short":"{unit} 奎米"},
150
+ "ronto": {"long":"{unit} 戎米","short":"{unit} 戎米"},
151
+ "yocto": {"long":"{unit} 麼米","short":"{unit} 麼米"},
152
+ "zepto": {"long":"{unit} 仄米","short":"{unit} 仄米"},
153
+ "atto": {"long":"{unit} 阿米","short":"{unit} 阿米"},
154
+ "femto": {"long":"{unit} 飛米","short":"{unit} 飛米"},
155
+ "pico": {"long":"{unit} 皮米","short":"{unit} 皮米"},
156
+ "nano": {"long":"{unit} 奈米","short":"{unit} 奈米"},
157
+ "micro": {"long":"{unit} 微米","short":"{unit} 微米"},
158
+ "milli": {"long":"{unit} 公釐","short":"{unit} 公釐"},
159
+ "centi": {"long":"{unit} 公分","short":"{unit} 公分"},
160
+ "deci": {"long":"{unit} 公寸","short":"{unit} 公寸"},
161
+ "deca": {"long":"{unit} 公丈","short":"{unit} 公丈"},
162
+ "hecto": {"long":"{unit} 公引","short":"{unit} 公引"},
163
+ "kilo": {"long":"{unit} 公里","short":"{unit} 公里"},
164
+ "mega": {"long":"{unit} 兆米","short":"{unit} 兆米"},
165
+ "giga": {"long":"{unit} 吉米","short":"{unit} 吉米"},
166
+ "tera": {"long":"{unit} 太米","short":"{unit} 太米"},
167
+ "peta": {"long":"{unit} 拍米","short":"{unit} 拍米"},
168
+ "exa": {"long":"{unit} 艾米","short":"{unit} 艾米"},
169
+ "zetta": {"long":"{unit} 皆米","short":"{unit} 皆米"},
170
+ "yotta": {"long":"{unit} 佑米","short":"{unit} 佑米"},
171
+ "ronna": {"long":"{unit} 羅米","short":"{unit} 羅米"},
172
+ "quetta": {"long":"{unit} 巋米","short":"{unit} 巋米"},
173
+ "kibi": {"long":"{unit} 千位元組","short":"Ki{unit}"},
174
+ "mebi": {"long":"{unit} 百萬位元組","short":"Mi{unit}"},
175
+ "gibi": {"long":"{unit} 吉位元組","short":"Gi{unit}"},
176
+ "tebi": {"long":"{unit} 兆位元組","short":"Ti{unit}"},
177
+ "pebi": {"long":"{unit} 拍位元組","short":"Pi{unit}"},
178
+ "exbi": {"long":"{unit} 艾位元組","short":"Ei{unit}"},
179
+ "zebi": {"long":"{unit} 皆位元組","short":"Zi{unit}"},
180
+ "yobi": {"long":"{unit} 佑位元組","short":"Yi{unit}"}
181
+ }
182
+ }
@@ -0,0 +1,309 @@
1
+ --!strict
2
+ --[[
3
+ @class MetricUnitTranslator.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local Jest = require("Jest")
9
+ local MetricUnit = require("MetricUnit")
10
+ local MetricUnitDefinitions = require("MetricUnitDefinitions")
11
+ local MetricUnitFallbackStrings = require("MetricUnitFallbackStrings")
12
+ local MetricUnitFormatUtils = require("MetricUnitFormatUtils")
13
+ local PluralCategoryUtils = require("PluralCategoryUtils")
14
+
15
+ local describe = Jest.Globals.describe
16
+ local expect = Jest.Globals.expect
17
+ local it = Jest.Globals.it
18
+
19
+ local NB = MetricUnitFormatUtils.NO_BREAK_SPACE
20
+ local CATEGORIES = { "zero", "one", "two", "few", "many", "other" }
21
+ local LANGUAGES =
22
+ { "en", "es", "fr", "de", "pt", "it", "ru", "pl", "tr", "id", "vi", "th", "ar", "zh-cn", "zh-tw", "ja", "ko" }
23
+
24
+ -- A number in each plural category the language uses, as CLDR's samples give them
25
+ local SAMPLES: { [string]: { [string]: number } } = {
26
+ en = { one = 1, other = 2 },
27
+ es = { one = 1, many = 1000000, other = 2 },
28
+ fr = { one = 1, many = 1000000, other = 2 },
29
+ de = { one = 1, other = 2 },
30
+ pt = { one = 1, many = 1000000, other = 2 },
31
+ it = { one = 1, many = 1000000, other = 2 },
32
+ ru = { one = 1, few = 2, many = 5, other = 1.5 },
33
+ pl = { one = 1, few = 2, many = 5, other = 1.5 },
34
+ tr = { one = 1, other = 2 },
35
+ id = { other = 1 },
36
+ vi = { one = 1, other = 2 },
37
+ th = { other = 1 },
38
+ ar = { zero = 0, one = 1, two = 2, few = 3, many = 11, other = 100 },
39
+ ["zh-cn"] = { other = 1 },
40
+ ["zh-tw"] = { other = 1 },
41
+ ja = { other = 1 },
42
+ ko = { other = 1 },
43
+ }
44
+
45
+ -- The locale files are StringValues under the translator module, so these read them in Roblox only
46
+ local function readLocale(language: string): any?
47
+ if typeof(script) ~= "Instance" then
48
+ return nil
49
+ end
50
+ local folder = (script :: any).Parent:FindFirstChild("MetricUnitTranslator")
51
+ local file = folder and folder:FindFirstChild(language .. ".json")
52
+ if not file then
53
+ return nil
54
+ end
55
+ return game:GetService("HttpService"):JSONDecode(file.Value)
56
+ end
57
+
58
+ -- A translator over one decoded locale file, for formatting every locale without streaming it
59
+ local function fileTranslator(language: string, data: any): any
60
+ local function lookup(key: string): string?
61
+ local node = data
62
+ for part in string.gmatch(key, "[^%.]+") do
63
+ node = if type(node) == "table" then node[part] else nil
64
+ end
65
+ return if type(node) == "string" then node else nil
66
+ end
67
+ return {
68
+ GetLocaleId = function()
69
+ return language
70
+ end,
71
+ FormatByKey = function(_self: any, key: string, args: { [string]: any }?): string
72
+ local text = lookup(key)
73
+ if not text then
74
+ error("No key " .. key)
75
+ end
76
+ return (string.gsub(text, "{(%a+)}", function(name)
77
+ return (args :: any)[name]
78
+ end))
79
+ end,
80
+ ObserveFormatByKey = function()
81
+ error("unused")
82
+ end,
83
+ }
84
+ end
85
+
86
+ local function substitute(template: string, numberText: string): string
87
+ return (string.gsub(template, "{n}", function()
88
+ return numberText
89
+ end))
90
+ end
91
+
92
+ describe("MetricUnitTranslator locale files", function()
93
+ it("ships every locale, each with every key the English source has, long and short", function()
94
+ local english = readLocale("en")
95
+ if english == nil then
96
+ return
97
+ end
98
+ for _, language in LANGUAGES do
99
+ local locale = readLocale(language)
100
+ assert(locale, string.format("No locale file for %s", language))
101
+ for id in english.units do
102
+ local entry = locale.units[id]
103
+ assert(entry, string.format("%s has no units.%s", language, id))
104
+ for _, display in { "long", "short" } do
105
+ for _, category in CATEGORIES do
106
+ local text = entry[display] and entry[display][category]
107
+ if type(text) ~= "string" or text == "" then
108
+ error(string.format("%s has no units.%s.%s.%s", language, id, display, category))
109
+ end
110
+ end
111
+ end
112
+ end
113
+ for name in english.prefixes do
114
+ for _, display in { "long", "short" } do
115
+ local text = locale.prefixes[name] and locale.prefixes[name][display]
116
+ if type(text) ~= "string" or string.find(text, "{unit}", 1, true) == nil then
117
+ error(string.format("%s has no prefixes.%s.%s", language, name, display))
118
+ end
119
+ end
120
+ end
121
+ for id in english.per do
122
+ for _, display in { "long", "short" } do
123
+ local text = locale.per[id] and locale.per[id][display]
124
+ if type(text) ~= "string" or string.find(text, "{n}", 1, true) == nil then
125
+ error(string.format("%s has no per.%s.%s", language, id, display))
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end)
131
+
132
+ it("has an English source that matches the definitions' names and symbols, and every prefix", function()
133
+ local english = readLocale("en")
134
+ if english == nil then
135
+ return
136
+ end
137
+ local named: { [string]: boolean } = {}
138
+ for _, unit in MetricUnitDefinitions.getUnits() do
139
+ local names = unit.names
140
+ local entry = english.units[unit.id]
141
+ local composedFromItsPrefix = unit.prefix ~= nil and entry == nil
142
+ if names and not composedFromItsPrefix then
143
+ named[unit.id] = true
144
+ assert(entry, string.format("en.json has no key for %s", unit.id))
145
+ local fallback = MetricUnitFallbackStrings.getUnitStringsForLocale("en-us")[unit.id] :: any
146
+ for _, category in CATEGORIES do
147
+ local isOne = category == "one"
148
+ expect(entry.long[category]).toBe("{n}" .. NB .. (if isOne then names.one else names.other))
149
+ local short = if isOne then fallback.short.one else fallback.short.other
150
+ expect(entry.short[category]).toBe((string.gsub(short, "%%s", "{n}")))
151
+ end
152
+ end
153
+ end
154
+ for id in english.units do
155
+ expect(named[id]).toBe(true)
156
+ end
157
+ for _, prefix in MetricUnitDefinitions.getPrefixes() do
158
+ expect(english.prefixes[prefix.name]).toEqual({
159
+ long = prefix.name .. "{unit}",
160
+ short = prefix.symbol .. "{unit}",
161
+ })
162
+ end
163
+ for id, phrases in english.per do
164
+ local unit = MetricUnitDefinitions.getUnit(id)
165
+ assert(unit, id)
166
+ expect(phrases.long).toBe("{n} per " .. id)
167
+ expect(phrases.short).toBe("{n}/" .. unit.symbol)
168
+ end
169
+ end)
170
+
171
+ it("formats a sample in every plural category of every locale", function()
172
+ for _, language in LANGUAGES do
173
+ local data = readLocale(language)
174
+ if data == nil then
175
+ return
176
+ end
177
+ local translator = fileTranslator(language, data)
178
+ for category, sample in SAMPLES[language] do
179
+ expect(PluralCategoryUtils.getPluralCategory(language, sample, nil)).toBe(category)
180
+ local numberText = MetricUnitFormatUtils.formatNumber(sample, { locale = language })
181
+ for _, display in { "long", "short" } do
182
+ local options: MetricUnit.FormatOptions =
183
+ { unit = "kilometer", unitDisplay = display :: any, translator = translator }
184
+ local expected = substitute(data.units.kilometer[display][category], numberText)
185
+ expect(MetricUnit.format(MetricUnit.kilometers(sample), options)).toBe(expected)
186
+ end
187
+ end
188
+ end
189
+ end)
190
+
191
+ it("writes a prefixed unit from its prefix and the unit's own name", function()
192
+ local expectations: { [string]: { string } } = {
193
+ de = { "5 Kilovolt", "5 kV", "3 Nanometer" },
194
+ fr = { "5 kilovolts", "5 kV", "3 nanomètres" },
195
+ ru = { "5 киловольт", "5 кВ", "3 нанометра" },
196
+ ja = { "5 キロボルト", "5 kV", "3 ナノメートル" },
197
+ }
198
+ for language, expected in expectations do
199
+ local data = readLocale(language)
200
+ if data == nil then
201
+ return
202
+ end
203
+ local translator = fileTranslator(language, data)
204
+ local function write(value: MetricUnit.MetricUnit, unit: string, display: string): string
205
+ local options: any = { unit = unit, unitDisplay = display, translator = translator, locale = language }
206
+ return (string.gsub(MetricUnit.format(value, options), NB, " "))
207
+ end
208
+ expect(data.units.kilovolt).toBeNil()
209
+ expect(write(MetricUnit.new(5000, "volt"), "kilovolt", "long")).toBe(expected[1])
210
+ expect(write(MetricUnit.new(5000, "volt"), "kilovolt", "short")).toBe(expected[2])
211
+ expect(write(MetricUnit.new(3, "nanometer" :: any), "nanometer", "long")).toBe(expected[3])
212
+ end
213
+ end)
214
+
215
+ it("writes rates per a time unit in the translator's language", function()
216
+ local data = readLocale("ru")
217
+ if data == nil then
218
+ return
219
+ end
220
+ local options: MetricUnit.FormatOptions = {
221
+ unit = "kilometer",
222
+ perUnit = "hour",
223
+ translator = fileTranslator("ru", data),
224
+ }
225
+ local numerator = substitute(data.units.kilometer.short.many, "5")
226
+ expect(MetricUnit.format(MetricUnit.new(5, "kilometerPerHour"), options)).toBe(
227
+ substitute(data.per.hour.short, numerator)
228
+ )
229
+ end)
230
+ end)
231
+
232
+ describe("MetricUnitTranslator", function()
233
+ local function streamed(language: string, check: (translator: any, data: any) -> ())
234
+ if typeof(script) ~= "Instance" then
235
+ return
236
+ end
237
+ local TranslatorTestUtils = require("TranslatorTestUtils")
238
+ local controller = TranslatorTestUtils.setup()
239
+ controller.setForcedLocaleId(language)
240
+ local folder = (script :: any).Parent.MetricUnitTranslator
241
+ local translator = controller.awaitLoaded(controller.newTranslatorFromInstance(folder))
242
+ controller.awaitEntriesWritten()
243
+ check(translator, readLocale(language))
244
+ controller:Destroy()
245
+ end
246
+
247
+ it("streams Spanish names to MetricUnit.format through a real translator", function()
248
+ streamed("es", function(translator)
249
+ local options: MetricUnit.FormatOptions = { unitDisplay = "long", translator = translator }
250
+ expect(MetricUnit.format(MetricUnit.new(3, "mile"):ToUSCustomary(), options)).toBe("3" .. NB .. "millas")
251
+ expect(MetricUnit.format(MetricUnit.kilometers(1), options)).toBe("1" .. NB .. "kilómetro")
252
+ end)
253
+ end)
254
+
255
+ for _, language in { "ar", "ru", "ja" } do
256
+ it(
257
+ string.format("streams %s through a real translator, long and short, in every plural category", language),
258
+ function()
259
+ streamed(language, function(translator, data)
260
+ for category, sample in SAMPLES[language] do
261
+ local numberText = MetricUnitFormatUtils.formatNumber(sample, { locale = language })
262
+ for _, display in { "long", "short" } do
263
+ local options: MetricUnit.FormatOptions =
264
+ { unit = "kilometer", unitDisplay = display :: any, translator = translator }
265
+ local expected = substitute(data.units.kilometer[display][category], numberText)
266
+ expect(MetricUnit.format(MetricUnit.kilometers(sample), options)).toBe(expected)
267
+ end
268
+ end
269
+ end)
270
+ end
271
+ )
272
+ end
273
+ end)
274
+
275
+ describe("No-break spaces", function()
276
+ it("join a number to its unit", function()
277
+ expect(MetricUnit.format(MetricUnit.kilometers(5))).toBe("5" .. NB .. "km")
278
+ expect(MetricUnit.format(MetricUnit.kilometers(5), { unitDisplay = "long" })).toBe("5" .. NB .. "kilometers")
279
+ expect(MetricUnit.format(MetricUnit.minutes(90))).toBe("1" .. NB .. "hour, 30" .. NB .. "minutes")
280
+ expect(MetricUnit.format(MetricUnit.percent(45), { unit = "percent" })).toBe("45%")
281
+ expect((MetricUnit.parse("5" .. NB .. "km") :: any):In("meter")).toBe(5000)
282
+ end)
283
+
284
+ it("measure as wide as a space in Roblox's fonts", function()
285
+ local dataModel: any = game
286
+ if typeof(dataModel) ~= "Instance" then
287
+ return
288
+ end
289
+ local TextService = (dataModel :: any):GetService("TextService")
290
+ local bounds = Vector2.new(1000, 100)
291
+ for _, font in { Enum.Font.SourceSans, Enum.Font.Gotham, Enum.Font.Arial } do
292
+ local plainWidth = TextService:GetTextSize("5 km", 18, font, bounds).X
293
+ local noBreakWidth = TextService:GetTextSize("5" .. NB .. "km", 18, font, bounds).X
294
+ expect(noBreakWidth).toBe(plainWidth)
295
+ end
296
+ end)
297
+ end)
298
+
299
+ describe("MetricUnitFormatUtils.formatUnit with a translator", function()
300
+ it("takes its locale from the translator, for numbers and plural forms", function()
301
+ local data = readLocale("ru")
302
+ if data == nil then
303
+ return
304
+ end
305
+ local translator = fileTranslator("ru", data)
306
+ local text = MetricUnitFormatUtils.formatUnit("meter", 3, { translator = translator, unitDisplay = "long" })
307
+ expect(text).toBe(substitute(data.units.meter.long.few, "3"))
308
+ end)
309
+ end)