@tracearr/translations 0.1.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 (572) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +116 -0
  3. package/dist/config.d.ts +4510 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +164 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/config.mobile.d.ts +4511 -0
  8. package/dist/config.mobile.d.ts.map +1 -0
  9. package/dist/config.mobile.js +587 -0
  10. package/dist/config.mobile.js.map +1 -0
  11. package/dist/formatting.d.ts +120 -0
  12. package/dist/formatting.d.ts.map +1 -0
  13. package/dist/formatting.js +265 -0
  14. package/dist/formatting.js.map +1 -0
  15. package/dist/index.d.ts +46 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +49 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/language.d.ts +101 -0
  20. package/dist/language.d.ts.map +1 -0
  21. package/dist/language.js +241 -0
  22. package/dist/language.js.map +1 -0
  23. package/dist/locales/af/common.json +276 -0
  24. package/dist/locales/af/mobile.json +60 -0
  25. package/dist/locales/af/nav.json +22 -0
  26. package/dist/locales/af/notifications.json +262 -0
  27. package/dist/locales/af/pages.json +324 -0
  28. package/dist/locales/af/settings.json +243 -0
  29. package/dist/locales/af-ZA/common.json +308 -0
  30. package/dist/locales/af-ZA/mobile.json +178 -0
  31. package/dist/locales/af-ZA/nav.json +28 -0
  32. package/dist/locales/af-ZA/notifications.json +279 -0
  33. package/dist/locales/af-ZA/pages.json +953 -0
  34. package/dist/locales/af-ZA/settings.json +487 -0
  35. package/dist/locales/ar/common.json +276 -0
  36. package/dist/locales/ar/mobile.json +60 -0
  37. package/dist/locales/ar/nav.json +22 -0
  38. package/dist/locales/ar/notifications.json +262 -0
  39. package/dist/locales/ar/pages.json +324 -0
  40. package/dist/locales/ar/settings.json +243 -0
  41. package/dist/locales/ar-SA/common.json +308 -0
  42. package/dist/locales/ar-SA/mobile.json +178 -0
  43. package/dist/locales/ar-SA/nav.json +28 -0
  44. package/dist/locales/ar-SA/notifications.json +279 -0
  45. package/dist/locales/ar-SA/pages.json +953 -0
  46. package/dist/locales/ar-SA/settings.json +487 -0
  47. package/dist/locales/ca/common.json +276 -0
  48. package/dist/locales/ca/mobile.json +60 -0
  49. package/dist/locales/ca/nav.json +22 -0
  50. package/dist/locales/ca/notifications.json +262 -0
  51. package/dist/locales/ca/pages.json +324 -0
  52. package/dist/locales/ca/settings.json +243 -0
  53. package/dist/locales/ca-ES/common.json +308 -0
  54. package/dist/locales/ca-ES/mobile.json +178 -0
  55. package/dist/locales/ca-ES/nav.json +28 -0
  56. package/dist/locales/ca-ES/notifications.json +279 -0
  57. package/dist/locales/ca-ES/pages.json +953 -0
  58. package/dist/locales/ca-ES/settings.json +487 -0
  59. package/dist/locales/cs/common.json +276 -0
  60. package/dist/locales/cs/mobile.json +60 -0
  61. package/dist/locales/cs/nav.json +22 -0
  62. package/dist/locales/cs/notifications.json +262 -0
  63. package/dist/locales/cs/pages.json +324 -0
  64. package/dist/locales/cs/settings.json +243 -0
  65. package/dist/locales/cs-CZ/common.json +308 -0
  66. package/dist/locales/cs-CZ/mobile.json +178 -0
  67. package/dist/locales/cs-CZ/nav.json +28 -0
  68. package/dist/locales/cs-CZ/notifications.json +279 -0
  69. package/dist/locales/cs-CZ/pages.json +953 -0
  70. package/dist/locales/cs-CZ/settings.json +487 -0
  71. package/dist/locales/da/common.json +276 -0
  72. package/dist/locales/da/mobile.json +60 -0
  73. package/dist/locales/da/nav.json +22 -0
  74. package/dist/locales/da/notifications.json +262 -0
  75. package/dist/locales/da/pages.json +324 -0
  76. package/dist/locales/da/settings.json +243 -0
  77. package/dist/locales/da-DK/common.json +308 -0
  78. package/dist/locales/da-DK/mobile.json +178 -0
  79. package/dist/locales/da-DK/nav.json +28 -0
  80. package/dist/locales/da-DK/notifications.json +279 -0
  81. package/dist/locales/da-DK/pages.json +953 -0
  82. package/dist/locales/da-DK/settings.json +487 -0
  83. package/dist/locales/de/common.json +276 -0
  84. package/dist/locales/de/mobile.json +60 -0
  85. package/dist/locales/de/nav.json +22 -0
  86. package/dist/locales/de/notifications.json +262 -0
  87. package/dist/locales/de/pages.json +324 -0
  88. package/dist/locales/de/settings.json +243 -0
  89. package/dist/locales/de-DE/common.json +308 -0
  90. package/dist/locales/de-DE/mobile.json +178 -0
  91. package/dist/locales/de-DE/nav.json +28 -0
  92. package/dist/locales/de-DE/notifications.json +279 -0
  93. package/dist/locales/de-DE/pages.json +953 -0
  94. package/dist/locales/de-DE/settings.json +487 -0
  95. package/dist/locales/el/common.json +276 -0
  96. package/dist/locales/el/mobile.json +60 -0
  97. package/dist/locales/el/nav.json +22 -0
  98. package/dist/locales/el/notifications.json +262 -0
  99. package/dist/locales/el/pages.json +324 -0
  100. package/dist/locales/el/settings.json +243 -0
  101. package/dist/locales/el-GR/common.json +308 -0
  102. package/dist/locales/el-GR/mobile.json +178 -0
  103. package/dist/locales/el-GR/nav.json +28 -0
  104. package/dist/locales/el-GR/notifications.json +279 -0
  105. package/dist/locales/el-GR/pages.json +953 -0
  106. package/dist/locales/el-GR/settings.json +487 -0
  107. package/dist/locales/en/common.json +308 -0
  108. package/dist/locales/en/mobile.json +178 -0
  109. package/dist/locales/en/nav.json +28 -0
  110. package/dist/locales/en/notifications.json +279 -0
  111. package/dist/locales/en/pages.json +953 -0
  112. package/dist/locales/en/settings.json +487 -0
  113. package/dist/locales/en-US/common.json +308 -0
  114. package/dist/locales/en-US/mobile.json +178 -0
  115. package/dist/locales/en-US/nav.json +28 -0
  116. package/dist/locales/en-US/notifications.json +279 -0
  117. package/dist/locales/en-US/pages.json +953 -0
  118. package/dist/locales/en-US/settings.json +487 -0
  119. package/dist/locales/es/common.json +276 -0
  120. package/dist/locales/es/mobile.json +60 -0
  121. package/dist/locales/es/nav.json +22 -0
  122. package/dist/locales/es/notifications.json +262 -0
  123. package/dist/locales/es/pages.json +324 -0
  124. package/dist/locales/es/settings.json +243 -0
  125. package/dist/locales/es-ES/common.json +308 -0
  126. package/dist/locales/es-ES/mobile.json +178 -0
  127. package/dist/locales/es-ES/nav.json +28 -0
  128. package/dist/locales/es-ES/notifications.json +279 -0
  129. package/dist/locales/es-ES/pages.json +953 -0
  130. package/dist/locales/es-ES/settings.json +487 -0
  131. package/dist/locales/fi/common.json +276 -0
  132. package/dist/locales/fi/mobile.json +60 -0
  133. package/dist/locales/fi/nav.json +22 -0
  134. package/dist/locales/fi/notifications.json +262 -0
  135. package/dist/locales/fi/pages.json +324 -0
  136. package/dist/locales/fi/settings.json +243 -0
  137. package/dist/locales/fi-FI/common.json +308 -0
  138. package/dist/locales/fi-FI/mobile.json +178 -0
  139. package/dist/locales/fi-FI/nav.json +28 -0
  140. package/dist/locales/fi-FI/notifications.json +279 -0
  141. package/dist/locales/fi-FI/pages.json +953 -0
  142. package/dist/locales/fi-FI/settings.json +487 -0
  143. package/dist/locales/fr/common.json +276 -0
  144. package/dist/locales/fr/mobile.json +60 -0
  145. package/dist/locales/fr/nav.json +22 -0
  146. package/dist/locales/fr/notifications.json +262 -0
  147. package/dist/locales/fr/pages.json +324 -0
  148. package/dist/locales/fr/settings.json +243 -0
  149. package/dist/locales/fr-FR/common.json +308 -0
  150. package/dist/locales/fr-FR/mobile.json +178 -0
  151. package/dist/locales/fr-FR/nav.json +28 -0
  152. package/dist/locales/fr-FR/notifications.json +279 -0
  153. package/dist/locales/fr-FR/pages.json +953 -0
  154. package/dist/locales/fr-FR/settings.json +487 -0
  155. package/dist/locales/he/common.json +276 -0
  156. package/dist/locales/he/mobile.json +60 -0
  157. package/dist/locales/he/nav.json +22 -0
  158. package/dist/locales/he/notifications.json +262 -0
  159. package/dist/locales/he/pages.json +324 -0
  160. package/dist/locales/he/settings.json +243 -0
  161. package/dist/locales/he-IL/common.json +308 -0
  162. package/dist/locales/he-IL/mobile.json +178 -0
  163. package/dist/locales/he-IL/nav.json +28 -0
  164. package/dist/locales/he-IL/notifications.json +279 -0
  165. package/dist/locales/he-IL/pages.json +953 -0
  166. package/dist/locales/he-IL/settings.json +487 -0
  167. package/dist/locales/hu/common.json +276 -0
  168. package/dist/locales/hu/mobile.json +60 -0
  169. package/dist/locales/hu/nav.json +22 -0
  170. package/dist/locales/hu/notifications.json +262 -0
  171. package/dist/locales/hu/pages.json +324 -0
  172. package/dist/locales/hu/settings.json +243 -0
  173. package/dist/locales/hu-HU/common.json +308 -0
  174. package/dist/locales/hu-HU/mobile.json +178 -0
  175. package/dist/locales/hu-HU/nav.json +28 -0
  176. package/dist/locales/hu-HU/notifications.json +279 -0
  177. package/dist/locales/hu-HU/pages.json +953 -0
  178. package/dist/locales/hu-HU/settings.json +487 -0
  179. package/dist/locales/it/common.json +276 -0
  180. package/dist/locales/it/mobile.json +60 -0
  181. package/dist/locales/it/nav.json +22 -0
  182. package/dist/locales/it/notifications.json +262 -0
  183. package/dist/locales/it/pages.json +324 -0
  184. package/dist/locales/it/settings.json +243 -0
  185. package/dist/locales/it-IT/common.json +308 -0
  186. package/dist/locales/it-IT/mobile.json +178 -0
  187. package/dist/locales/it-IT/nav.json +28 -0
  188. package/dist/locales/it-IT/notifications.json +279 -0
  189. package/dist/locales/it-IT/pages.json +953 -0
  190. package/dist/locales/it-IT/settings.json +487 -0
  191. package/dist/locales/ja/common.json +276 -0
  192. package/dist/locales/ja/mobile.json +60 -0
  193. package/dist/locales/ja/nav.json +22 -0
  194. package/dist/locales/ja/notifications.json +262 -0
  195. package/dist/locales/ja/pages.json +324 -0
  196. package/dist/locales/ja/settings.json +243 -0
  197. package/dist/locales/ja-JP/common.json +308 -0
  198. package/dist/locales/ja-JP/mobile.json +178 -0
  199. package/dist/locales/ja-JP/nav.json +28 -0
  200. package/dist/locales/ja-JP/notifications.json +279 -0
  201. package/dist/locales/ja-JP/pages.json +953 -0
  202. package/dist/locales/ja-JP/settings.json +487 -0
  203. package/dist/locales/ko/common.json +276 -0
  204. package/dist/locales/ko/mobile.json +60 -0
  205. package/dist/locales/ko/nav.json +22 -0
  206. package/dist/locales/ko/notifications.json +262 -0
  207. package/dist/locales/ko/pages.json +324 -0
  208. package/dist/locales/ko/settings.json +243 -0
  209. package/dist/locales/ko-KR/common.json +308 -0
  210. package/dist/locales/ko-KR/mobile.json +178 -0
  211. package/dist/locales/ko-KR/nav.json +28 -0
  212. package/dist/locales/ko-KR/notifications.json +279 -0
  213. package/dist/locales/ko-KR/pages.json +953 -0
  214. package/dist/locales/ko-KR/settings.json +487 -0
  215. package/dist/locales/nl/common.json +276 -0
  216. package/dist/locales/nl/mobile.json +60 -0
  217. package/dist/locales/nl/nav.json +22 -0
  218. package/dist/locales/nl/notifications.json +262 -0
  219. package/dist/locales/nl/pages.json +324 -0
  220. package/dist/locales/nl/settings.json +243 -0
  221. package/dist/locales/nl-NL/common.json +308 -0
  222. package/dist/locales/nl-NL/mobile.json +178 -0
  223. package/dist/locales/nl-NL/nav.json +28 -0
  224. package/dist/locales/nl-NL/notifications.json +279 -0
  225. package/dist/locales/nl-NL/pages.json +953 -0
  226. package/dist/locales/nl-NL/settings.json +487 -0
  227. package/dist/locales/no/common.json +276 -0
  228. package/dist/locales/no/mobile.json +60 -0
  229. package/dist/locales/no/nav.json +22 -0
  230. package/dist/locales/no/notifications.json +262 -0
  231. package/dist/locales/no/pages.json +324 -0
  232. package/dist/locales/no/settings.json +243 -0
  233. package/dist/locales/no-NO/common.json +308 -0
  234. package/dist/locales/no-NO/mobile.json +178 -0
  235. package/dist/locales/no-NO/nav.json +28 -0
  236. package/dist/locales/no-NO/notifications.json +279 -0
  237. package/dist/locales/no-NO/pages.json +953 -0
  238. package/dist/locales/no-NO/settings.json +487 -0
  239. package/dist/locales/pl/common.json +276 -0
  240. package/dist/locales/pl/mobile.json +60 -0
  241. package/dist/locales/pl/nav.json +22 -0
  242. package/dist/locales/pl/notifications.json +262 -0
  243. package/dist/locales/pl/pages.json +324 -0
  244. package/dist/locales/pl/settings.json +243 -0
  245. package/dist/locales/pl-PL/common.json +308 -0
  246. package/dist/locales/pl-PL/mobile.json +178 -0
  247. package/dist/locales/pl-PL/nav.json +28 -0
  248. package/dist/locales/pl-PL/notifications.json +279 -0
  249. package/dist/locales/pl-PL/pages.json +953 -0
  250. package/dist/locales/pl-PL/settings.json +487 -0
  251. package/dist/locales/pt/common.json +276 -0
  252. package/dist/locales/pt/mobile.json +60 -0
  253. package/dist/locales/pt/nav.json +22 -0
  254. package/dist/locales/pt/notifications.json +262 -0
  255. package/dist/locales/pt/pages.json +324 -0
  256. package/dist/locales/pt/settings.json +243 -0
  257. package/dist/locales/pt-BR/common.json +308 -0
  258. package/dist/locales/pt-BR/mobile.json +178 -0
  259. package/dist/locales/pt-BR/nav.json +28 -0
  260. package/dist/locales/pt-BR/notifications.json +279 -0
  261. package/dist/locales/pt-BR/pages.json +953 -0
  262. package/dist/locales/pt-BR/settings.json +487 -0
  263. package/dist/locales/pt-PT/common.json +308 -0
  264. package/dist/locales/pt-PT/mobile.json +178 -0
  265. package/dist/locales/pt-PT/nav.json +28 -0
  266. package/dist/locales/pt-PT/notifications.json +279 -0
  267. package/dist/locales/pt-PT/pages.json +953 -0
  268. package/dist/locales/pt-PT/settings.json +487 -0
  269. package/dist/locales/ro/common.json +276 -0
  270. package/dist/locales/ro/mobile.json +60 -0
  271. package/dist/locales/ro/nav.json +22 -0
  272. package/dist/locales/ro/notifications.json +262 -0
  273. package/dist/locales/ro/pages.json +324 -0
  274. package/dist/locales/ro/settings.json +243 -0
  275. package/dist/locales/ro-RO/common.json +308 -0
  276. package/dist/locales/ro-RO/mobile.json +178 -0
  277. package/dist/locales/ro-RO/nav.json +28 -0
  278. package/dist/locales/ro-RO/notifications.json +279 -0
  279. package/dist/locales/ro-RO/pages.json +953 -0
  280. package/dist/locales/ro-RO/settings.json +487 -0
  281. package/dist/locales/ru/common.json +276 -0
  282. package/dist/locales/ru/mobile.json +60 -0
  283. package/dist/locales/ru/nav.json +22 -0
  284. package/dist/locales/ru/notifications.json +262 -0
  285. package/dist/locales/ru/pages.json +324 -0
  286. package/dist/locales/ru/settings.json +243 -0
  287. package/dist/locales/ru-RU/common.json +308 -0
  288. package/dist/locales/ru-RU/mobile.json +178 -0
  289. package/dist/locales/ru-RU/nav.json +28 -0
  290. package/dist/locales/ru-RU/notifications.json +279 -0
  291. package/dist/locales/ru-RU/pages.json +953 -0
  292. package/dist/locales/ru-RU/settings.json +487 -0
  293. package/dist/locales/sr/common.json +276 -0
  294. package/dist/locales/sr/mobile.json +60 -0
  295. package/dist/locales/sr/nav.json +22 -0
  296. package/dist/locales/sr/notifications.json +262 -0
  297. package/dist/locales/sr/pages.json +324 -0
  298. package/dist/locales/sr/settings.json +243 -0
  299. package/dist/locales/sr-SP/common.json +308 -0
  300. package/dist/locales/sr-SP/mobile.json +178 -0
  301. package/dist/locales/sr-SP/nav.json +28 -0
  302. package/dist/locales/sr-SP/notifications.json +279 -0
  303. package/dist/locales/sr-SP/pages.json +953 -0
  304. package/dist/locales/sr-SP/settings.json +487 -0
  305. package/dist/locales/sv/common.json +276 -0
  306. package/dist/locales/sv/mobile.json +60 -0
  307. package/dist/locales/sv/nav.json +22 -0
  308. package/dist/locales/sv/notifications.json +262 -0
  309. package/dist/locales/sv/pages.json +324 -0
  310. package/dist/locales/sv/settings.json +243 -0
  311. package/dist/locales/sv-SE/common.json +308 -0
  312. package/dist/locales/sv-SE/mobile.json +178 -0
  313. package/dist/locales/sv-SE/nav.json +28 -0
  314. package/dist/locales/sv-SE/notifications.json +279 -0
  315. package/dist/locales/sv-SE/pages.json +953 -0
  316. package/dist/locales/sv-SE/settings.json +487 -0
  317. package/dist/locales/tr/common.json +276 -0
  318. package/dist/locales/tr/mobile.json +60 -0
  319. package/dist/locales/tr/nav.json +22 -0
  320. package/dist/locales/tr/notifications.json +262 -0
  321. package/dist/locales/tr/pages.json +324 -0
  322. package/dist/locales/tr/settings.json +243 -0
  323. package/dist/locales/tr-TR/common.json +308 -0
  324. package/dist/locales/tr-TR/mobile.json +178 -0
  325. package/dist/locales/tr-TR/nav.json +28 -0
  326. package/dist/locales/tr-TR/notifications.json +279 -0
  327. package/dist/locales/tr-TR/pages.json +953 -0
  328. package/dist/locales/tr-TR/settings.json +487 -0
  329. package/dist/locales/uk/common.json +276 -0
  330. package/dist/locales/uk/mobile.json +60 -0
  331. package/dist/locales/uk/nav.json +22 -0
  332. package/dist/locales/uk/notifications.json +262 -0
  333. package/dist/locales/uk/pages.json +324 -0
  334. package/dist/locales/uk/settings.json +243 -0
  335. package/dist/locales/uk-UA/common.json +308 -0
  336. package/dist/locales/uk-UA/mobile.json +178 -0
  337. package/dist/locales/uk-UA/nav.json +28 -0
  338. package/dist/locales/uk-UA/notifications.json +279 -0
  339. package/dist/locales/uk-UA/pages.json +953 -0
  340. package/dist/locales/uk-UA/settings.json +487 -0
  341. package/dist/locales/vi/common.json +276 -0
  342. package/dist/locales/vi/mobile.json +60 -0
  343. package/dist/locales/vi/nav.json +22 -0
  344. package/dist/locales/vi/notifications.json +262 -0
  345. package/dist/locales/vi/pages.json +324 -0
  346. package/dist/locales/vi/settings.json +243 -0
  347. package/dist/locales/vi-VN/common.json +308 -0
  348. package/dist/locales/vi-VN/mobile.json +178 -0
  349. package/dist/locales/vi-VN/nav.json +28 -0
  350. package/dist/locales/vi-VN/notifications.json +279 -0
  351. package/dist/locales/vi-VN/pages.json +953 -0
  352. package/dist/locales/vi-VN/settings.json +487 -0
  353. package/dist/locales/zh/common.json +276 -0
  354. package/dist/locales/zh/mobile.json +60 -0
  355. package/dist/locales/zh/nav.json +22 -0
  356. package/dist/locales/zh/notifications.json +262 -0
  357. package/dist/locales/zh/pages.json +324 -0
  358. package/dist/locales/zh/settings.json +243 -0
  359. package/dist/locales/zh-CN/common.json +308 -0
  360. package/dist/locales/zh-CN/mobile.json +178 -0
  361. package/dist/locales/zh-CN/nav.json +28 -0
  362. package/dist/locales/zh-CN/notifications.json +279 -0
  363. package/dist/locales/zh-CN/pages.json +953 -0
  364. package/dist/locales/zh-CN/settings.json +487 -0
  365. package/dist/locales/zh-TW/common.json +308 -0
  366. package/dist/locales/zh-TW/mobile.json +178 -0
  367. package/dist/locales/zh-TW/nav.json +28 -0
  368. package/dist/locales/zh-TW/notifications.json +279 -0
  369. package/dist/locales/zh-TW/pages.json +953 -0
  370. package/dist/locales/zh-TW/settings.json +487 -0
  371. package/dist/mobile.d.ts +11 -0
  372. package/dist/mobile.d.ts.map +1 -0
  373. package/dist/mobile.js +14 -0
  374. package/dist/mobile.js.map +1 -0
  375. package/dist/types.d.ts +54 -0
  376. package/dist/types.d.ts.map +1 -0
  377. package/dist/types.js +5 -0
  378. package/dist/types.js.map +1 -0
  379. package/package.json +57 -0
  380. package/src/config.mobile.ts +647 -0
  381. package/src/config.ts +209 -0
  382. package/src/formatting.ts +297 -0
  383. package/src/index.ts +105 -0
  384. package/src/language.ts +270 -0
  385. package/src/locales/af-ZA/common.json +308 -0
  386. package/src/locales/af-ZA/mobile.json +178 -0
  387. package/src/locales/af-ZA/nav.json +28 -0
  388. package/src/locales/af-ZA/notifications.json +279 -0
  389. package/src/locales/af-ZA/pages.json +953 -0
  390. package/src/locales/af-ZA/settings.json +487 -0
  391. package/src/locales/ar-SA/common.json +308 -0
  392. package/src/locales/ar-SA/mobile.json +178 -0
  393. package/src/locales/ar-SA/nav.json +28 -0
  394. package/src/locales/ar-SA/notifications.json +279 -0
  395. package/src/locales/ar-SA/pages.json +953 -0
  396. package/src/locales/ar-SA/settings.json +487 -0
  397. package/src/locales/ca-ES/common.json +308 -0
  398. package/src/locales/ca-ES/mobile.json +178 -0
  399. package/src/locales/ca-ES/nav.json +28 -0
  400. package/src/locales/ca-ES/notifications.json +279 -0
  401. package/src/locales/ca-ES/pages.json +953 -0
  402. package/src/locales/ca-ES/settings.json +487 -0
  403. package/src/locales/cs-CZ/common.json +308 -0
  404. package/src/locales/cs-CZ/mobile.json +178 -0
  405. package/src/locales/cs-CZ/nav.json +28 -0
  406. package/src/locales/cs-CZ/notifications.json +279 -0
  407. package/src/locales/cs-CZ/pages.json +953 -0
  408. package/src/locales/cs-CZ/settings.json +487 -0
  409. package/src/locales/da-DK/common.json +308 -0
  410. package/src/locales/da-DK/mobile.json +178 -0
  411. package/src/locales/da-DK/nav.json +28 -0
  412. package/src/locales/da-DK/notifications.json +279 -0
  413. package/src/locales/da-DK/pages.json +953 -0
  414. package/src/locales/da-DK/settings.json +487 -0
  415. package/src/locales/de-DE/common.json +308 -0
  416. package/src/locales/de-DE/mobile.json +178 -0
  417. package/src/locales/de-DE/nav.json +28 -0
  418. package/src/locales/de-DE/notifications.json +279 -0
  419. package/src/locales/de-DE/pages.json +953 -0
  420. package/src/locales/de-DE/settings.json +487 -0
  421. package/src/locales/el-GR/common.json +308 -0
  422. package/src/locales/el-GR/mobile.json +178 -0
  423. package/src/locales/el-GR/nav.json +28 -0
  424. package/src/locales/el-GR/notifications.json +279 -0
  425. package/src/locales/el-GR/pages.json +953 -0
  426. package/src/locales/el-GR/settings.json +487 -0
  427. package/src/locales/en/common.json +308 -0
  428. package/src/locales/en/mobile.json +178 -0
  429. package/src/locales/en/nav.json +28 -0
  430. package/src/locales/en/notifications.json +279 -0
  431. package/src/locales/en/pages.json +953 -0
  432. package/src/locales/en/settings.json +487 -0
  433. package/src/locales/en-US/common.json +308 -0
  434. package/src/locales/en-US/mobile.json +178 -0
  435. package/src/locales/en-US/nav.json +28 -0
  436. package/src/locales/en-US/notifications.json +279 -0
  437. package/src/locales/en-US/pages.json +953 -0
  438. package/src/locales/en-US/settings.json +487 -0
  439. package/src/locales/es-ES/common.json +308 -0
  440. package/src/locales/es-ES/mobile.json +178 -0
  441. package/src/locales/es-ES/nav.json +28 -0
  442. package/src/locales/es-ES/notifications.json +279 -0
  443. package/src/locales/es-ES/pages.json +953 -0
  444. package/src/locales/es-ES/settings.json +487 -0
  445. package/src/locales/fi-FI/common.json +308 -0
  446. package/src/locales/fi-FI/mobile.json +178 -0
  447. package/src/locales/fi-FI/nav.json +28 -0
  448. package/src/locales/fi-FI/notifications.json +279 -0
  449. package/src/locales/fi-FI/pages.json +953 -0
  450. package/src/locales/fi-FI/settings.json +487 -0
  451. package/src/locales/fr-FR/common.json +308 -0
  452. package/src/locales/fr-FR/mobile.json +178 -0
  453. package/src/locales/fr-FR/nav.json +28 -0
  454. package/src/locales/fr-FR/notifications.json +279 -0
  455. package/src/locales/fr-FR/pages.json +953 -0
  456. package/src/locales/fr-FR/settings.json +487 -0
  457. package/src/locales/he-IL/common.json +308 -0
  458. package/src/locales/he-IL/mobile.json +178 -0
  459. package/src/locales/he-IL/nav.json +28 -0
  460. package/src/locales/he-IL/notifications.json +279 -0
  461. package/src/locales/he-IL/pages.json +953 -0
  462. package/src/locales/he-IL/settings.json +487 -0
  463. package/src/locales/hu-HU/common.json +308 -0
  464. package/src/locales/hu-HU/mobile.json +178 -0
  465. package/src/locales/hu-HU/nav.json +28 -0
  466. package/src/locales/hu-HU/notifications.json +279 -0
  467. package/src/locales/hu-HU/pages.json +953 -0
  468. package/src/locales/hu-HU/settings.json +487 -0
  469. package/src/locales/it-IT/common.json +308 -0
  470. package/src/locales/it-IT/mobile.json +178 -0
  471. package/src/locales/it-IT/nav.json +28 -0
  472. package/src/locales/it-IT/notifications.json +279 -0
  473. package/src/locales/it-IT/pages.json +953 -0
  474. package/src/locales/it-IT/settings.json +487 -0
  475. package/src/locales/ja-JP/common.json +308 -0
  476. package/src/locales/ja-JP/mobile.json +178 -0
  477. package/src/locales/ja-JP/nav.json +28 -0
  478. package/src/locales/ja-JP/notifications.json +279 -0
  479. package/src/locales/ja-JP/pages.json +953 -0
  480. package/src/locales/ja-JP/settings.json +487 -0
  481. package/src/locales/ko-KR/common.json +308 -0
  482. package/src/locales/ko-KR/mobile.json +178 -0
  483. package/src/locales/ko-KR/nav.json +28 -0
  484. package/src/locales/ko-KR/notifications.json +279 -0
  485. package/src/locales/ko-KR/pages.json +953 -0
  486. package/src/locales/ko-KR/settings.json +487 -0
  487. package/src/locales/nl-NL/common.json +308 -0
  488. package/src/locales/nl-NL/mobile.json +178 -0
  489. package/src/locales/nl-NL/nav.json +28 -0
  490. package/src/locales/nl-NL/notifications.json +279 -0
  491. package/src/locales/nl-NL/pages.json +953 -0
  492. package/src/locales/nl-NL/settings.json +487 -0
  493. package/src/locales/no-NO/common.json +308 -0
  494. package/src/locales/no-NO/mobile.json +178 -0
  495. package/src/locales/no-NO/nav.json +28 -0
  496. package/src/locales/no-NO/notifications.json +279 -0
  497. package/src/locales/no-NO/pages.json +953 -0
  498. package/src/locales/no-NO/settings.json +487 -0
  499. package/src/locales/pl-PL/common.json +308 -0
  500. package/src/locales/pl-PL/mobile.json +178 -0
  501. package/src/locales/pl-PL/nav.json +28 -0
  502. package/src/locales/pl-PL/notifications.json +279 -0
  503. package/src/locales/pl-PL/pages.json +953 -0
  504. package/src/locales/pl-PL/settings.json +487 -0
  505. package/src/locales/pt-BR/common.json +308 -0
  506. package/src/locales/pt-BR/mobile.json +178 -0
  507. package/src/locales/pt-BR/nav.json +28 -0
  508. package/src/locales/pt-BR/notifications.json +279 -0
  509. package/src/locales/pt-BR/pages.json +953 -0
  510. package/src/locales/pt-BR/settings.json +487 -0
  511. package/src/locales/pt-PT/common.json +308 -0
  512. package/src/locales/pt-PT/mobile.json +178 -0
  513. package/src/locales/pt-PT/nav.json +28 -0
  514. package/src/locales/pt-PT/notifications.json +279 -0
  515. package/src/locales/pt-PT/pages.json +953 -0
  516. package/src/locales/pt-PT/settings.json +487 -0
  517. package/src/locales/ro-RO/common.json +308 -0
  518. package/src/locales/ro-RO/mobile.json +178 -0
  519. package/src/locales/ro-RO/nav.json +28 -0
  520. package/src/locales/ro-RO/notifications.json +279 -0
  521. package/src/locales/ro-RO/pages.json +953 -0
  522. package/src/locales/ro-RO/settings.json +487 -0
  523. package/src/locales/ru-RU/common.json +308 -0
  524. package/src/locales/ru-RU/mobile.json +178 -0
  525. package/src/locales/ru-RU/nav.json +28 -0
  526. package/src/locales/ru-RU/notifications.json +279 -0
  527. package/src/locales/ru-RU/pages.json +953 -0
  528. package/src/locales/ru-RU/settings.json +487 -0
  529. package/src/locales/sr-SP/common.json +308 -0
  530. package/src/locales/sr-SP/mobile.json +178 -0
  531. package/src/locales/sr-SP/nav.json +28 -0
  532. package/src/locales/sr-SP/notifications.json +279 -0
  533. package/src/locales/sr-SP/pages.json +953 -0
  534. package/src/locales/sr-SP/settings.json +487 -0
  535. package/src/locales/sv-SE/common.json +308 -0
  536. package/src/locales/sv-SE/mobile.json +178 -0
  537. package/src/locales/sv-SE/nav.json +28 -0
  538. package/src/locales/sv-SE/notifications.json +279 -0
  539. package/src/locales/sv-SE/pages.json +953 -0
  540. package/src/locales/sv-SE/settings.json +487 -0
  541. package/src/locales/tr-TR/common.json +308 -0
  542. package/src/locales/tr-TR/mobile.json +178 -0
  543. package/src/locales/tr-TR/nav.json +28 -0
  544. package/src/locales/tr-TR/notifications.json +279 -0
  545. package/src/locales/tr-TR/pages.json +953 -0
  546. package/src/locales/tr-TR/settings.json +487 -0
  547. package/src/locales/uk-UA/common.json +308 -0
  548. package/src/locales/uk-UA/mobile.json +178 -0
  549. package/src/locales/uk-UA/nav.json +28 -0
  550. package/src/locales/uk-UA/notifications.json +279 -0
  551. package/src/locales/uk-UA/pages.json +953 -0
  552. package/src/locales/uk-UA/settings.json +487 -0
  553. package/src/locales/vi-VN/common.json +308 -0
  554. package/src/locales/vi-VN/mobile.json +178 -0
  555. package/src/locales/vi-VN/nav.json +28 -0
  556. package/src/locales/vi-VN/notifications.json +279 -0
  557. package/src/locales/vi-VN/pages.json +953 -0
  558. package/src/locales/vi-VN/settings.json +487 -0
  559. package/src/locales/zh-CN/common.json +308 -0
  560. package/src/locales/zh-CN/mobile.json +178 -0
  561. package/src/locales/zh-CN/nav.json +28 -0
  562. package/src/locales/zh-CN/notifications.json +279 -0
  563. package/src/locales/zh-CN/pages.json +953 -0
  564. package/src/locales/zh-CN/settings.json +487 -0
  565. package/src/locales/zh-TW/common.json +308 -0
  566. package/src/locales/zh-TW/mobile.json +178 -0
  567. package/src/locales/zh-TW/nav.json +28 -0
  568. package/src/locales/zh-TW/notifications.json +279 -0
  569. package/src/locales/zh-TW/pages.json +953 -0
  570. package/src/locales/zh-TW/settings.json +487 -0
  571. package/src/mobile.ts +69 -0
  572. package/src/types.ts +86 -0
package/src/config.ts ADDED
@@ -0,0 +1,209 @@
1
+ /// <reference types="vite/client" />
2
+ import i18n from 'i18next';
3
+ import { initReactI18next } from 'react-i18next';
4
+ import { _registerLocaleLoader } from './language.js';
5
+
6
+ // ============================================================================
7
+ // English Translations (Base Language — always bundled)
8
+ // ============================================================================
9
+ import commonEn from './locales/en/common.json' with { type: 'json' };
10
+ import notificationsEn from './locales/en/notifications.json' with { type: 'json' };
11
+ import settingsEn from './locales/en/settings.json' with { type: 'json' };
12
+ import navEn from './locales/en/nav.json' with { type: 'json' };
13
+ import pagesEn from './locales/en/pages.json' with { type: 'json' };
14
+ import mobileEn from './locales/en/mobile.json' with { type: 'json' };
15
+
16
+ export const defaultNS = 'common';
17
+ export const namespaces = [
18
+ 'common',
19
+ 'notifications',
20
+ 'settings',
21
+ 'nav',
22
+ 'pages',
23
+ 'mobile',
24
+ ] as const;
25
+ export type Namespace = (typeof namespaces)[number];
26
+
27
+ // English is always available statically
28
+ export const resources = {
29
+ en: {
30
+ common: commonEn,
31
+ notifications: notificationsEn,
32
+ settings: settingsEn,
33
+ nav: navEn,
34
+ pages: pagesEn,
35
+ mobile: mobileEn,
36
+ },
37
+ } as const;
38
+
39
+ export const supportedLanguages = [
40
+ 'en',
41
+ 'af-ZA',
42
+ 'ar-SA',
43
+ 'ca-ES',
44
+ 'cs-CZ',
45
+ 'da-DK',
46
+ 'de-DE',
47
+ 'el-GR',
48
+ 'es-ES',
49
+ 'fi-FI',
50
+ 'fr-FR',
51
+ 'he-IL',
52
+ 'hu-HU',
53
+ 'it-IT',
54
+ 'ja-JP',
55
+ 'ko-KR',
56
+ 'nl-NL',
57
+ 'no-NO',
58
+ 'pl-PL',
59
+ 'pt-BR',
60
+ 'pt-PT',
61
+ 'ro-RO',
62
+ 'ru-RU',
63
+ 'sr-SP',
64
+ 'sv-SE',
65
+ 'tr-TR',
66
+ 'uk-UA',
67
+ 'vi-VN',
68
+ 'zh-CN',
69
+ 'zh-TW',
70
+ ] as const;
71
+ export type SupportedLanguage = (typeof supportedLanguages)[number];
72
+
73
+ // ============================================================================
74
+ // Lazy Locale Loaders
75
+ // Vite discovers all locale JSON files at build time via import.meta.glob and
76
+ // creates separate chunks for each. Only the active language is downloaded.
77
+ // The *-* pattern matches locale-format dirs (af-ZA, zh-CN) but skips en/.
78
+ // ============================================================================
79
+
80
+ type LocaleData = Record<string, Record<string, unknown>>;
81
+ type LocaleLoader = () => Promise<LocaleData>;
82
+
83
+ const localeModules = import.meta.glob<{ default: Record<string, unknown> }>(
84
+ './locales/*-*/*.json'
85
+ );
86
+
87
+ const localeLoaders: Record<string, LocaleLoader> = Object.fromEntries(
88
+ supportedLanguages
89
+ .filter((l) => l !== 'en')
90
+ .map((locale) => [
91
+ locale,
92
+ async () => {
93
+ const bundles: Record<string, Record<string, unknown>> = {};
94
+ await Promise.all(
95
+ namespaces.map(async (ns) => {
96
+ const path = `./locales/${locale}/${ns}.json`;
97
+ const loader = localeModules[path];
98
+ if (loader) {
99
+ const mod = await loader();
100
+ bundles[ns] = mod.default;
101
+ }
102
+ })
103
+ );
104
+ return bundles;
105
+ },
106
+ ])
107
+ );
108
+
109
+ // ============================================================================
110
+ // Locale Loading
111
+ // ============================================================================
112
+
113
+ const loadedLocales = new Set<string>(['en']);
114
+
115
+ /**
116
+ * Load a locale's translation data on-demand.
117
+ * English is always available (statically bundled). Other locales are fetched
118
+ * as separate chunks and registered with i18next via addResourceBundle.
119
+ */
120
+ export async function loadLocale(lang: string): Promise<void> {
121
+ if (loadedLocales.has(lang)) return;
122
+
123
+ const loader = localeLoaders[lang];
124
+ if (!loader) return;
125
+ try {
126
+ const bundles = await loader();
127
+ for (const [ns, data] of Object.entries(bundles)) {
128
+ i18n.addResourceBundle(lang, ns, data, true, true);
129
+ }
130
+ loadedLocales.add(lang);
131
+ } catch (err) {
132
+ console.error(`[i18n] Failed to load locale "${lang}", falling back to English`, err);
133
+ }
134
+ }
135
+
136
+ // Register the Vite-based locale loader so language.ts can use it
137
+ _registerLocaleLoader(loadLocale);
138
+
139
+ // ============================================================================
140
+ // i18next Configuration
141
+ // ============================================================================
142
+
143
+ export const defaultI18nConfig = {
144
+ lng: 'en',
145
+ fallbackLng: 'en',
146
+ defaultNS,
147
+ ns: namespaces,
148
+ resources,
149
+ interpolation: {
150
+ escapeValue: false, // React already escapes values
151
+ },
152
+ // Returning the key is better than empty string for debugging
153
+ returnNull: false,
154
+ returnEmptyString: false,
155
+ } as const;
156
+
157
+ // Prevents race conditions when initI18n is called concurrently
158
+ let initPromise: Promise<typeof i18n> | null = null;
159
+
160
+ /**
161
+ * Initialize i18next. Call once at app startup before rendering.
162
+ * Safe to call concurrently - multiple calls share the same initialization.
163
+ *
164
+ * For non-English languages, the locale is loaded on-demand after init
165
+ * so only the active language's translations are downloaded.
166
+ */
167
+ export interface InitI18nOptions {
168
+ lng?: SupportedLanguage;
169
+ }
170
+
171
+ export async function initI18n(options?: InitI18nOptions): Promise<typeof i18n> {
172
+ if (i18n.isInitialized) {
173
+ // If already initialized but a different locale is requested, load it
174
+ const targetLng = options?.lng || 'en';
175
+ if (targetLng !== 'en') {
176
+ await loadLocale(targetLng);
177
+ }
178
+ return i18n;
179
+ }
180
+
181
+ if (initPromise) {
182
+ return initPromise;
183
+ }
184
+
185
+ const targetLng = options?.lng || 'en';
186
+
187
+ initPromise = i18n
188
+ .use(initReactI18next)
189
+ .init({
190
+ ...defaultI18nConfig,
191
+ ...options,
192
+ })
193
+ .then(async () => {
194
+ // Load non-English locale data after init
195
+ if (targetLng !== 'en') {
196
+ await loadLocale(targetLng);
197
+ }
198
+ initPromise = null;
199
+ return i18n;
200
+ })
201
+ .catch((error: unknown) => {
202
+ initPromise = null;
203
+ throw error;
204
+ });
205
+
206
+ return initPromise;
207
+ }
208
+
209
+ export { i18n };
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Date, time, and number formatting utilities.
3
+ *
4
+ * Uses the native Intl API for locale-aware formatting.
5
+ * Works consistently across web and React Native environments.
6
+ */
7
+
8
+ import i18n from 'i18next';
9
+
10
+ // ============================================================================
11
+ // Date/Time Formatting
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Format options for dates and times.
16
+ */
17
+ export type DateFormatStyle = 'short' | 'medium' | 'long' | 'full';
18
+
19
+ /**
20
+ * Format a date using the current locale.
21
+ *
22
+ * @param date - Date to format
23
+ * @param style - Format style ('short', 'medium', 'long', 'full')
24
+ * @returns Formatted date string
25
+ *
26
+ * @example
27
+ * formatDate(new Date(), 'short') // "1/2/26" (en-US)
28
+ * formatDate(new Date(), 'long') // "January 2, 2026" (en-US)
29
+ */
30
+ export function formatDate(date: Date | number, style: DateFormatStyle = 'medium'): string {
31
+ const locale = i18n.language || 'en';
32
+ const d = typeof date === 'number' ? new Date(date) : date;
33
+
34
+ if (isNaN(d.getTime())) {
35
+ return '';
36
+ }
37
+
38
+ return new Intl.DateTimeFormat(locale, {
39
+ dateStyle: style,
40
+ }).format(d);
41
+ }
42
+
43
+ /**
44
+ * Format a time using the current locale.
45
+ *
46
+ * @param date - Date to format
47
+ * @param style - Format style ('short', 'medium', 'long', 'full')
48
+ * @returns Formatted time string
49
+ *
50
+ * @example
51
+ * formatTime(new Date(), 'short') // "3:30 PM" (en-US)
52
+ */
53
+ export function formatTime(date: Date | number, style: DateFormatStyle = 'short'): string {
54
+ const locale = i18n.language || 'en';
55
+ const d = typeof date === 'number' ? new Date(date) : date;
56
+
57
+ if (isNaN(d.getTime())) {
58
+ return '';
59
+ }
60
+
61
+ return new Intl.DateTimeFormat(locale, {
62
+ timeStyle: style,
63
+ }).format(d);
64
+ }
65
+
66
+ /**
67
+ * Format a date and time using the current locale.
68
+ *
69
+ * @param date - Date to format
70
+ * @param dateStyle - Date format style
71
+ * @param timeStyle - Time format style
72
+ * @returns Formatted datetime string
73
+ *
74
+ * @example
75
+ * formatDateTime(new Date(), 'short', 'short') // "1/2/26, 3:30 PM" (en-US)
76
+ */
77
+ export function formatDateTime(
78
+ date: Date | number,
79
+ dateStyle: DateFormatStyle = 'short',
80
+ timeStyle: DateFormatStyle = 'short'
81
+ ): string {
82
+ const locale = i18n.language || 'en';
83
+ const d = typeof date === 'number' ? new Date(date) : date;
84
+
85
+ if (isNaN(d.getTime())) {
86
+ return '';
87
+ }
88
+
89
+ return new Intl.DateTimeFormat(locale, {
90
+ dateStyle,
91
+ timeStyle,
92
+ }).format(d);
93
+ }
94
+
95
+ // ============================================================================
96
+ // Relative Time Formatting
97
+ // ============================================================================
98
+
99
+ /**
100
+ * Format a relative time (e.g., "2 hours ago", "in 3 days").
101
+ *
102
+ * @param date - Date to compare to now
103
+ * @param now - Reference time (defaults to current time)
104
+ * @returns Formatted relative time string
105
+ *
106
+ * @example
107
+ * formatRelativeTime(Date.now() - 3600000) // "1 hour ago"
108
+ * formatRelativeTime(Date.now() + 86400000) // "in 1 day"
109
+ */
110
+ export function formatRelativeTime(date: Date | number, now: Date | number = Date.now()): string {
111
+ const locale = i18n.language || 'en';
112
+ const d = typeof date === 'number' ? date : date.getTime();
113
+ const n = typeof now === 'number' ? now : now.getTime();
114
+
115
+ if (isNaN(d) || isNaN(n)) {
116
+ return '';
117
+ }
118
+
119
+ const diff = d - n;
120
+ const absDiff = Math.abs(diff);
121
+
122
+ // Select appropriate unit
123
+ const seconds = absDiff / 1000;
124
+ const minutes = seconds / 60;
125
+ const hours = minutes / 60;
126
+ const days = hours / 24;
127
+ const weeks = days / 7;
128
+ const months = days / 30;
129
+ const years = days / 365;
130
+
131
+ let value: number;
132
+ let unit: Intl.RelativeTimeFormatUnit;
133
+
134
+ if (seconds < 60) {
135
+ value = Math.round(seconds);
136
+ unit = 'second';
137
+ } else if (minutes < 60) {
138
+ value = Math.round(minutes);
139
+ unit = 'minute';
140
+ } else if (hours < 24) {
141
+ value = Math.round(hours);
142
+ unit = 'hour';
143
+ } else if (days < 7) {
144
+ value = Math.round(days);
145
+ unit = 'day';
146
+ } else if (weeks < 4) {
147
+ value = Math.round(weeks);
148
+ unit = 'week';
149
+ } else if (months < 12) {
150
+ value = Math.round(months);
151
+ unit = 'month';
152
+ } else {
153
+ value = Math.round(years);
154
+ unit = 'year';
155
+ }
156
+
157
+ // Adjust sign for past/future
158
+ const signedValue = diff < 0 ? -value : value;
159
+
160
+ return new Intl.RelativeTimeFormat(locale, {
161
+ numeric: 'auto',
162
+ }).format(signedValue, unit);
163
+ }
164
+
165
+ // ============================================================================
166
+ // Duration Formatting
167
+ // ============================================================================
168
+
169
+ /**
170
+ * Format a duration in milliseconds to a human-readable string.
171
+ *
172
+ * @param ms - Duration in milliseconds
173
+ * @param options - Formatting options
174
+ * @returns Formatted duration string
175
+ *
176
+ * @example
177
+ * formatDuration(3661000) // "1h 1m 1s"
178
+ * formatDuration(3661000, { style: 'long' }) // "1 hour, 1 minute, 1 second"
179
+ */
180
+ export function formatDuration(
181
+ ms: number,
182
+ options: { style?: 'short' | 'long'; maxUnits?: number } = {}
183
+ ): string {
184
+ const { style = 'short', maxUnits = 3 } = options;
185
+
186
+ if (ms < 0) ms = 0;
187
+
188
+ const seconds = Math.floor(ms / 1000) % 60;
189
+ const minutes = Math.floor(ms / 60000) % 60;
190
+ const hours = Math.floor(ms / 3600000) % 24;
191
+ const days = Math.floor(ms / 86400000);
192
+
193
+ const parts: string[] = [];
194
+
195
+ if (style === 'short') {
196
+ if (days > 0) parts.push(`${days}d`);
197
+ if (hours > 0) parts.push(`${hours}h`);
198
+ if (minutes > 0) parts.push(`${minutes}m`);
199
+ if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);
200
+ } else {
201
+ // Long style - use i18n translation keys with pluralization
202
+ if (days > 0) parts.push(i18n.t('common:duration.day', { count: days }));
203
+ if (hours > 0) parts.push(i18n.t('common:duration.hour', { count: hours }));
204
+ if (minutes > 0) parts.push(i18n.t('common:duration.minute', { count: minutes }));
205
+ if (seconds > 0 || parts.length === 0)
206
+ parts.push(i18n.t('common:duration.second', { count: seconds }));
207
+ }
208
+
209
+ return parts.slice(0, maxUnits).join(style === 'short' ? ' ' : ', ');
210
+ }
211
+
212
+ // ============================================================================
213
+ // Number Formatting
214
+ // ============================================================================
215
+
216
+ /**
217
+ * Format a number using the current locale.
218
+ *
219
+ * @param value - Number to format
220
+ * @param options - Intl.NumberFormat options
221
+ * @returns Formatted number string
222
+ *
223
+ * @example
224
+ * formatNumber(1234567.89) // "1,234,567.89" (en-US)
225
+ */
226
+ export function formatNumber(value: number, options?: Intl.NumberFormatOptions): string {
227
+ const locale = i18n.language || 'en';
228
+ return new Intl.NumberFormat(locale, options).format(value);
229
+ }
230
+
231
+ /**
232
+ * Format a number as a percentage.
233
+ *
234
+ * @param value - Number to format (0-1 or 0-100 depending on usePercent)
235
+ * @param decimals - Number of decimal places
236
+ * @param usePercent - If true, value is already a percentage (0-100)
237
+ * @returns Formatted percentage string
238
+ *
239
+ * @example
240
+ * formatPercent(0.75) // "75%"
241
+ * formatPercent(75, 1, true) // "75.0%"
242
+ */
243
+ export function formatPercent(value: number, decimals = 0, usePercent = false): string {
244
+ const locale = i18n.language || 'en';
245
+ const normalizedValue = usePercent ? value / 100 : value;
246
+
247
+ return new Intl.NumberFormat(locale, {
248
+ style: 'percent',
249
+ minimumFractionDigits: decimals,
250
+ maximumFractionDigits: decimals,
251
+ }).format(normalizedValue);
252
+ }
253
+
254
+ /**
255
+ * Format bytes to a human-readable size string.
256
+ *
257
+ * @param bytes - Number of bytes
258
+ * @param decimals - Number of decimal places
259
+ * @returns Formatted size string
260
+ *
261
+ * @example
262
+ * formatBytes(1024) // "1 KB"
263
+ * formatBytes(1536, 1) // "1.5 KB"
264
+ */
265
+ export function formatBytes(bytes: number, decimals = 1): string {
266
+ if (bytes === 0) return '0 B';
267
+ if (bytes < 0) bytes = 0;
268
+
269
+ const k = 1024;
270
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
271
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
272
+ const value = bytes / Math.pow(k, i);
273
+
274
+ return `${formatNumber(value, { maximumFractionDigits: decimals })} ${sizes[i]}`;
275
+ }
276
+
277
+ /**
278
+ * Format a bitrate to a human-readable string.
279
+ *
280
+ * @param bitsPerSecond - Bitrate in bits per second
281
+ * @param decimals - Number of decimal places
282
+ * @returns Formatted bitrate string
283
+ *
284
+ * @example
285
+ * formatBitrate(8000000) // "8 Mbps"
286
+ */
287
+ export function formatBitrate(bitsPerSecond: number, decimals = 1): string {
288
+ if (bitsPerSecond === 0) return '0 bps';
289
+ if (bitsPerSecond < 0) bitsPerSecond = 0;
290
+
291
+ const k = 1000;
292
+ const sizes = ['bps', 'Kbps', 'Mbps', 'Gbps'];
293
+ const i = Math.min(Math.floor(Math.log(bitsPerSecond) / Math.log(k)), sizes.length - 1);
294
+ const value = bitsPerSecond / Math.pow(k, i);
295
+
296
+ return `${formatNumber(value, { maximumFractionDigits: decimals })} ${sizes[i]}`;
297
+ }
package/src/index.ts ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @tracearr/translations
3
+ *
4
+ * Shared i18n translations package for Tracearr web and mobile apps.
5
+ * Provides type-safe translation keys with IDE autocomplete.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { initI18n, useTranslation } from '@tracearr/translations';
10
+ *
11
+ * // Initialize once at app startup
12
+ * await initI18n();
13
+ *
14
+ * // Use in components
15
+ * function MyComponent() {
16
+ * const { t } = useTranslation();
17
+ * return <button>{t('common.actions.save')}</button>;
18
+ * }
19
+ *
20
+ * // With namespaces
21
+ * function RulesComponent() {
22
+ * const { t } = useTranslation('pages');
23
+ * return <span>{t('rules.impossibleTravel')}</span>;
24
+ * }
25
+ * ```
26
+ *
27
+ * @example Language detection
28
+ * ```tsx
29
+ * import { detectLanguage, changeLanguage, languageNames } from '@tracearr/translations';
30
+ *
31
+ * // Detect user's preferred language
32
+ * const lang = await detectLanguage();
33
+ *
34
+ * // Change language
35
+ * await changeLanguage('es-ES');
36
+ *
37
+ * // Get available languages with display names
38
+ * Object.entries(languageNames).map(([code, name]) => ({ code, name }));
39
+ * ```
40
+ */
41
+
42
+ // Core i18n exports
43
+ export {
44
+ i18n,
45
+ initI18n,
46
+ loadLocale,
47
+ defaultI18nConfig,
48
+ defaultNS,
49
+ namespaces,
50
+ resources,
51
+ supportedLanguages,
52
+ type SupportedLanguage,
53
+ type Namespace,
54
+ } from './config.js';
55
+
56
+ // Language detection and management
57
+ export {
58
+ languageNames,
59
+ getSupportedLanguages,
60
+ isLanguageSupported,
61
+ resolveLocale,
62
+ detectLanguage,
63
+ detectSystemLanguage,
64
+ changeLanguage,
65
+ getCurrentLanguage,
66
+ getLanguageDisplayName,
67
+ } from './language.js';
68
+
69
+ // Type exports for type-safe translations
70
+ export type {
71
+ AllResources,
72
+ TranslationResources,
73
+ CommonTranslations,
74
+ NotificationsTranslations,
75
+ SettingsTranslations,
76
+ NavTranslations,
77
+ PagesTranslations,
78
+ MobileTranslations,
79
+ // Utility types for custom type-safe operations
80
+ NestedKeyOf,
81
+ TranslationValue,
82
+ CommonKey,
83
+ NotificationsKey,
84
+ SettingsKey,
85
+ NavKey,
86
+ PagesKey,
87
+ MobileKey,
88
+ } from './types.js';
89
+
90
+ // Formatting utilities
91
+ export {
92
+ formatDate,
93
+ formatTime,
94
+ formatDateTime,
95
+ formatRelativeTime,
96
+ formatDuration,
97
+ formatNumber,
98
+ formatPercent,
99
+ formatBytes,
100
+ formatBitrate,
101
+ type DateFormatStyle,
102
+ } from './formatting.js';
103
+
104
+ // Re-export react-i18next hooks for convenience
105
+ export { useTranslation, Trans, I18nextProvider } from 'react-i18next';