@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.
- package/LICENSE +661 -0
- package/README.md +116 -0
- package/dist/config.d.ts +4510 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +164 -0
- package/dist/config.js.map +1 -0
- package/dist/config.mobile.d.ts +4511 -0
- package/dist/config.mobile.d.ts.map +1 -0
- package/dist/config.mobile.js +587 -0
- package/dist/config.mobile.js.map +1 -0
- package/dist/formatting.d.ts +120 -0
- package/dist/formatting.d.ts.map +1 -0
- package/dist/formatting.js +265 -0
- package/dist/formatting.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/language.d.ts +101 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +241 -0
- package/dist/language.js.map +1 -0
- package/dist/locales/af/common.json +276 -0
- package/dist/locales/af/mobile.json +60 -0
- package/dist/locales/af/nav.json +22 -0
- package/dist/locales/af/notifications.json +262 -0
- package/dist/locales/af/pages.json +324 -0
- package/dist/locales/af/settings.json +243 -0
- package/dist/locales/af-ZA/common.json +308 -0
- package/dist/locales/af-ZA/mobile.json +178 -0
- package/dist/locales/af-ZA/nav.json +28 -0
- package/dist/locales/af-ZA/notifications.json +279 -0
- package/dist/locales/af-ZA/pages.json +953 -0
- package/dist/locales/af-ZA/settings.json +487 -0
- package/dist/locales/ar/common.json +276 -0
- package/dist/locales/ar/mobile.json +60 -0
- package/dist/locales/ar/nav.json +22 -0
- package/dist/locales/ar/notifications.json +262 -0
- package/dist/locales/ar/pages.json +324 -0
- package/dist/locales/ar/settings.json +243 -0
- package/dist/locales/ar-SA/common.json +308 -0
- package/dist/locales/ar-SA/mobile.json +178 -0
- package/dist/locales/ar-SA/nav.json +28 -0
- package/dist/locales/ar-SA/notifications.json +279 -0
- package/dist/locales/ar-SA/pages.json +953 -0
- package/dist/locales/ar-SA/settings.json +487 -0
- package/dist/locales/ca/common.json +276 -0
- package/dist/locales/ca/mobile.json +60 -0
- package/dist/locales/ca/nav.json +22 -0
- package/dist/locales/ca/notifications.json +262 -0
- package/dist/locales/ca/pages.json +324 -0
- package/dist/locales/ca/settings.json +243 -0
- package/dist/locales/ca-ES/common.json +308 -0
- package/dist/locales/ca-ES/mobile.json +178 -0
- package/dist/locales/ca-ES/nav.json +28 -0
- package/dist/locales/ca-ES/notifications.json +279 -0
- package/dist/locales/ca-ES/pages.json +953 -0
- package/dist/locales/ca-ES/settings.json +487 -0
- package/dist/locales/cs/common.json +276 -0
- package/dist/locales/cs/mobile.json +60 -0
- package/dist/locales/cs/nav.json +22 -0
- package/dist/locales/cs/notifications.json +262 -0
- package/dist/locales/cs/pages.json +324 -0
- package/dist/locales/cs/settings.json +243 -0
- package/dist/locales/cs-CZ/common.json +308 -0
- package/dist/locales/cs-CZ/mobile.json +178 -0
- package/dist/locales/cs-CZ/nav.json +28 -0
- package/dist/locales/cs-CZ/notifications.json +279 -0
- package/dist/locales/cs-CZ/pages.json +953 -0
- package/dist/locales/cs-CZ/settings.json +487 -0
- package/dist/locales/da/common.json +276 -0
- package/dist/locales/da/mobile.json +60 -0
- package/dist/locales/da/nav.json +22 -0
- package/dist/locales/da/notifications.json +262 -0
- package/dist/locales/da/pages.json +324 -0
- package/dist/locales/da/settings.json +243 -0
- package/dist/locales/da-DK/common.json +308 -0
- package/dist/locales/da-DK/mobile.json +178 -0
- package/dist/locales/da-DK/nav.json +28 -0
- package/dist/locales/da-DK/notifications.json +279 -0
- package/dist/locales/da-DK/pages.json +953 -0
- package/dist/locales/da-DK/settings.json +487 -0
- package/dist/locales/de/common.json +276 -0
- package/dist/locales/de/mobile.json +60 -0
- package/dist/locales/de/nav.json +22 -0
- package/dist/locales/de/notifications.json +262 -0
- package/dist/locales/de/pages.json +324 -0
- package/dist/locales/de/settings.json +243 -0
- package/dist/locales/de-DE/common.json +308 -0
- package/dist/locales/de-DE/mobile.json +178 -0
- package/dist/locales/de-DE/nav.json +28 -0
- package/dist/locales/de-DE/notifications.json +279 -0
- package/dist/locales/de-DE/pages.json +953 -0
- package/dist/locales/de-DE/settings.json +487 -0
- package/dist/locales/el/common.json +276 -0
- package/dist/locales/el/mobile.json +60 -0
- package/dist/locales/el/nav.json +22 -0
- package/dist/locales/el/notifications.json +262 -0
- package/dist/locales/el/pages.json +324 -0
- package/dist/locales/el/settings.json +243 -0
- package/dist/locales/el-GR/common.json +308 -0
- package/dist/locales/el-GR/mobile.json +178 -0
- package/dist/locales/el-GR/nav.json +28 -0
- package/dist/locales/el-GR/notifications.json +279 -0
- package/dist/locales/el-GR/pages.json +953 -0
- package/dist/locales/el-GR/settings.json +487 -0
- package/dist/locales/en/common.json +308 -0
- package/dist/locales/en/mobile.json +178 -0
- package/dist/locales/en/nav.json +28 -0
- package/dist/locales/en/notifications.json +279 -0
- package/dist/locales/en/pages.json +953 -0
- package/dist/locales/en/settings.json +487 -0
- package/dist/locales/en-US/common.json +308 -0
- package/dist/locales/en-US/mobile.json +178 -0
- package/dist/locales/en-US/nav.json +28 -0
- package/dist/locales/en-US/notifications.json +279 -0
- package/dist/locales/en-US/pages.json +953 -0
- package/dist/locales/en-US/settings.json +487 -0
- package/dist/locales/es/common.json +276 -0
- package/dist/locales/es/mobile.json +60 -0
- package/dist/locales/es/nav.json +22 -0
- package/dist/locales/es/notifications.json +262 -0
- package/dist/locales/es/pages.json +324 -0
- package/dist/locales/es/settings.json +243 -0
- package/dist/locales/es-ES/common.json +308 -0
- package/dist/locales/es-ES/mobile.json +178 -0
- package/dist/locales/es-ES/nav.json +28 -0
- package/dist/locales/es-ES/notifications.json +279 -0
- package/dist/locales/es-ES/pages.json +953 -0
- package/dist/locales/es-ES/settings.json +487 -0
- package/dist/locales/fi/common.json +276 -0
- package/dist/locales/fi/mobile.json +60 -0
- package/dist/locales/fi/nav.json +22 -0
- package/dist/locales/fi/notifications.json +262 -0
- package/dist/locales/fi/pages.json +324 -0
- package/dist/locales/fi/settings.json +243 -0
- package/dist/locales/fi-FI/common.json +308 -0
- package/dist/locales/fi-FI/mobile.json +178 -0
- package/dist/locales/fi-FI/nav.json +28 -0
- package/dist/locales/fi-FI/notifications.json +279 -0
- package/dist/locales/fi-FI/pages.json +953 -0
- package/dist/locales/fi-FI/settings.json +487 -0
- package/dist/locales/fr/common.json +276 -0
- package/dist/locales/fr/mobile.json +60 -0
- package/dist/locales/fr/nav.json +22 -0
- package/dist/locales/fr/notifications.json +262 -0
- package/dist/locales/fr/pages.json +324 -0
- package/dist/locales/fr/settings.json +243 -0
- package/dist/locales/fr-FR/common.json +308 -0
- package/dist/locales/fr-FR/mobile.json +178 -0
- package/dist/locales/fr-FR/nav.json +28 -0
- package/dist/locales/fr-FR/notifications.json +279 -0
- package/dist/locales/fr-FR/pages.json +953 -0
- package/dist/locales/fr-FR/settings.json +487 -0
- package/dist/locales/he/common.json +276 -0
- package/dist/locales/he/mobile.json +60 -0
- package/dist/locales/he/nav.json +22 -0
- package/dist/locales/he/notifications.json +262 -0
- package/dist/locales/he/pages.json +324 -0
- package/dist/locales/he/settings.json +243 -0
- package/dist/locales/he-IL/common.json +308 -0
- package/dist/locales/he-IL/mobile.json +178 -0
- package/dist/locales/he-IL/nav.json +28 -0
- package/dist/locales/he-IL/notifications.json +279 -0
- package/dist/locales/he-IL/pages.json +953 -0
- package/dist/locales/he-IL/settings.json +487 -0
- package/dist/locales/hu/common.json +276 -0
- package/dist/locales/hu/mobile.json +60 -0
- package/dist/locales/hu/nav.json +22 -0
- package/dist/locales/hu/notifications.json +262 -0
- package/dist/locales/hu/pages.json +324 -0
- package/dist/locales/hu/settings.json +243 -0
- package/dist/locales/hu-HU/common.json +308 -0
- package/dist/locales/hu-HU/mobile.json +178 -0
- package/dist/locales/hu-HU/nav.json +28 -0
- package/dist/locales/hu-HU/notifications.json +279 -0
- package/dist/locales/hu-HU/pages.json +953 -0
- package/dist/locales/hu-HU/settings.json +487 -0
- package/dist/locales/it/common.json +276 -0
- package/dist/locales/it/mobile.json +60 -0
- package/dist/locales/it/nav.json +22 -0
- package/dist/locales/it/notifications.json +262 -0
- package/dist/locales/it/pages.json +324 -0
- package/dist/locales/it/settings.json +243 -0
- package/dist/locales/it-IT/common.json +308 -0
- package/dist/locales/it-IT/mobile.json +178 -0
- package/dist/locales/it-IT/nav.json +28 -0
- package/dist/locales/it-IT/notifications.json +279 -0
- package/dist/locales/it-IT/pages.json +953 -0
- package/dist/locales/it-IT/settings.json +487 -0
- package/dist/locales/ja/common.json +276 -0
- package/dist/locales/ja/mobile.json +60 -0
- package/dist/locales/ja/nav.json +22 -0
- package/dist/locales/ja/notifications.json +262 -0
- package/dist/locales/ja/pages.json +324 -0
- package/dist/locales/ja/settings.json +243 -0
- package/dist/locales/ja-JP/common.json +308 -0
- package/dist/locales/ja-JP/mobile.json +178 -0
- package/dist/locales/ja-JP/nav.json +28 -0
- package/dist/locales/ja-JP/notifications.json +279 -0
- package/dist/locales/ja-JP/pages.json +953 -0
- package/dist/locales/ja-JP/settings.json +487 -0
- package/dist/locales/ko/common.json +276 -0
- package/dist/locales/ko/mobile.json +60 -0
- package/dist/locales/ko/nav.json +22 -0
- package/dist/locales/ko/notifications.json +262 -0
- package/dist/locales/ko/pages.json +324 -0
- package/dist/locales/ko/settings.json +243 -0
- package/dist/locales/ko-KR/common.json +308 -0
- package/dist/locales/ko-KR/mobile.json +178 -0
- package/dist/locales/ko-KR/nav.json +28 -0
- package/dist/locales/ko-KR/notifications.json +279 -0
- package/dist/locales/ko-KR/pages.json +953 -0
- package/dist/locales/ko-KR/settings.json +487 -0
- package/dist/locales/nl/common.json +276 -0
- package/dist/locales/nl/mobile.json +60 -0
- package/dist/locales/nl/nav.json +22 -0
- package/dist/locales/nl/notifications.json +262 -0
- package/dist/locales/nl/pages.json +324 -0
- package/dist/locales/nl/settings.json +243 -0
- package/dist/locales/nl-NL/common.json +308 -0
- package/dist/locales/nl-NL/mobile.json +178 -0
- package/dist/locales/nl-NL/nav.json +28 -0
- package/dist/locales/nl-NL/notifications.json +279 -0
- package/dist/locales/nl-NL/pages.json +953 -0
- package/dist/locales/nl-NL/settings.json +487 -0
- package/dist/locales/no/common.json +276 -0
- package/dist/locales/no/mobile.json +60 -0
- package/dist/locales/no/nav.json +22 -0
- package/dist/locales/no/notifications.json +262 -0
- package/dist/locales/no/pages.json +324 -0
- package/dist/locales/no/settings.json +243 -0
- package/dist/locales/no-NO/common.json +308 -0
- package/dist/locales/no-NO/mobile.json +178 -0
- package/dist/locales/no-NO/nav.json +28 -0
- package/dist/locales/no-NO/notifications.json +279 -0
- package/dist/locales/no-NO/pages.json +953 -0
- package/dist/locales/no-NO/settings.json +487 -0
- package/dist/locales/pl/common.json +276 -0
- package/dist/locales/pl/mobile.json +60 -0
- package/dist/locales/pl/nav.json +22 -0
- package/dist/locales/pl/notifications.json +262 -0
- package/dist/locales/pl/pages.json +324 -0
- package/dist/locales/pl/settings.json +243 -0
- package/dist/locales/pl-PL/common.json +308 -0
- package/dist/locales/pl-PL/mobile.json +178 -0
- package/dist/locales/pl-PL/nav.json +28 -0
- package/dist/locales/pl-PL/notifications.json +279 -0
- package/dist/locales/pl-PL/pages.json +953 -0
- package/dist/locales/pl-PL/settings.json +487 -0
- package/dist/locales/pt/common.json +276 -0
- package/dist/locales/pt/mobile.json +60 -0
- package/dist/locales/pt/nav.json +22 -0
- package/dist/locales/pt/notifications.json +262 -0
- package/dist/locales/pt/pages.json +324 -0
- package/dist/locales/pt/settings.json +243 -0
- package/dist/locales/pt-BR/common.json +308 -0
- package/dist/locales/pt-BR/mobile.json +178 -0
- package/dist/locales/pt-BR/nav.json +28 -0
- package/dist/locales/pt-BR/notifications.json +279 -0
- package/dist/locales/pt-BR/pages.json +953 -0
- package/dist/locales/pt-BR/settings.json +487 -0
- package/dist/locales/pt-PT/common.json +308 -0
- package/dist/locales/pt-PT/mobile.json +178 -0
- package/dist/locales/pt-PT/nav.json +28 -0
- package/dist/locales/pt-PT/notifications.json +279 -0
- package/dist/locales/pt-PT/pages.json +953 -0
- package/dist/locales/pt-PT/settings.json +487 -0
- package/dist/locales/ro/common.json +276 -0
- package/dist/locales/ro/mobile.json +60 -0
- package/dist/locales/ro/nav.json +22 -0
- package/dist/locales/ro/notifications.json +262 -0
- package/dist/locales/ro/pages.json +324 -0
- package/dist/locales/ro/settings.json +243 -0
- package/dist/locales/ro-RO/common.json +308 -0
- package/dist/locales/ro-RO/mobile.json +178 -0
- package/dist/locales/ro-RO/nav.json +28 -0
- package/dist/locales/ro-RO/notifications.json +279 -0
- package/dist/locales/ro-RO/pages.json +953 -0
- package/dist/locales/ro-RO/settings.json +487 -0
- package/dist/locales/ru/common.json +276 -0
- package/dist/locales/ru/mobile.json +60 -0
- package/dist/locales/ru/nav.json +22 -0
- package/dist/locales/ru/notifications.json +262 -0
- package/dist/locales/ru/pages.json +324 -0
- package/dist/locales/ru/settings.json +243 -0
- package/dist/locales/ru-RU/common.json +308 -0
- package/dist/locales/ru-RU/mobile.json +178 -0
- package/dist/locales/ru-RU/nav.json +28 -0
- package/dist/locales/ru-RU/notifications.json +279 -0
- package/dist/locales/ru-RU/pages.json +953 -0
- package/dist/locales/ru-RU/settings.json +487 -0
- package/dist/locales/sr/common.json +276 -0
- package/dist/locales/sr/mobile.json +60 -0
- package/dist/locales/sr/nav.json +22 -0
- package/dist/locales/sr/notifications.json +262 -0
- package/dist/locales/sr/pages.json +324 -0
- package/dist/locales/sr/settings.json +243 -0
- package/dist/locales/sr-SP/common.json +308 -0
- package/dist/locales/sr-SP/mobile.json +178 -0
- package/dist/locales/sr-SP/nav.json +28 -0
- package/dist/locales/sr-SP/notifications.json +279 -0
- package/dist/locales/sr-SP/pages.json +953 -0
- package/dist/locales/sr-SP/settings.json +487 -0
- package/dist/locales/sv/common.json +276 -0
- package/dist/locales/sv/mobile.json +60 -0
- package/dist/locales/sv/nav.json +22 -0
- package/dist/locales/sv/notifications.json +262 -0
- package/dist/locales/sv/pages.json +324 -0
- package/dist/locales/sv/settings.json +243 -0
- package/dist/locales/sv-SE/common.json +308 -0
- package/dist/locales/sv-SE/mobile.json +178 -0
- package/dist/locales/sv-SE/nav.json +28 -0
- package/dist/locales/sv-SE/notifications.json +279 -0
- package/dist/locales/sv-SE/pages.json +953 -0
- package/dist/locales/sv-SE/settings.json +487 -0
- package/dist/locales/tr/common.json +276 -0
- package/dist/locales/tr/mobile.json +60 -0
- package/dist/locales/tr/nav.json +22 -0
- package/dist/locales/tr/notifications.json +262 -0
- package/dist/locales/tr/pages.json +324 -0
- package/dist/locales/tr/settings.json +243 -0
- package/dist/locales/tr-TR/common.json +308 -0
- package/dist/locales/tr-TR/mobile.json +178 -0
- package/dist/locales/tr-TR/nav.json +28 -0
- package/dist/locales/tr-TR/notifications.json +279 -0
- package/dist/locales/tr-TR/pages.json +953 -0
- package/dist/locales/tr-TR/settings.json +487 -0
- package/dist/locales/uk/common.json +276 -0
- package/dist/locales/uk/mobile.json +60 -0
- package/dist/locales/uk/nav.json +22 -0
- package/dist/locales/uk/notifications.json +262 -0
- package/dist/locales/uk/pages.json +324 -0
- package/dist/locales/uk/settings.json +243 -0
- package/dist/locales/uk-UA/common.json +308 -0
- package/dist/locales/uk-UA/mobile.json +178 -0
- package/dist/locales/uk-UA/nav.json +28 -0
- package/dist/locales/uk-UA/notifications.json +279 -0
- package/dist/locales/uk-UA/pages.json +953 -0
- package/dist/locales/uk-UA/settings.json +487 -0
- package/dist/locales/vi/common.json +276 -0
- package/dist/locales/vi/mobile.json +60 -0
- package/dist/locales/vi/nav.json +22 -0
- package/dist/locales/vi/notifications.json +262 -0
- package/dist/locales/vi/pages.json +324 -0
- package/dist/locales/vi/settings.json +243 -0
- package/dist/locales/vi-VN/common.json +308 -0
- package/dist/locales/vi-VN/mobile.json +178 -0
- package/dist/locales/vi-VN/nav.json +28 -0
- package/dist/locales/vi-VN/notifications.json +279 -0
- package/dist/locales/vi-VN/pages.json +953 -0
- package/dist/locales/vi-VN/settings.json +487 -0
- package/dist/locales/zh/common.json +276 -0
- package/dist/locales/zh/mobile.json +60 -0
- package/dist/locales/zh/nav.json +22 -0
- package/dist/locales/zh/notifications.json +262 -0
- package/dist/locales/zh/pages.json +324 -0
- package/dist/locales/zh/settings.json +243 -0
- package/dist/locales/zh-CN/common.json +308 -0
- package/dist/locales/zh-CN/mobile.json +178 -0
- package/dist/locales/zh-CN/nav.json +28 -0
- package/dist/locales/zh-CN/notifications.json +279 -0
- package/dist/locales/zh-CN/pages.json +953 -0
- package/dist/locales/zh-CN/settings.json +487 -0
- package/dist/locales/zh-TW/common.json +308 -0
- package/dist/locales/zh-TW/mobile.json +178 -0
- package/dist/locales/zh-TW/nav.json +28 -0
- package/dist/locales/zh-TW/notifications.json +279 -0
- package/dist/locales/zh-TW/pages.json +953 -0
- package/dist/locales/zh-TW/settings.json +487 -0
- package/dist/mobile.d.ts +11 -0
- package/dist/mobile.d.ts.map +1 -0
- package/dist/mobile.js +14 -0
- package/dist/mobile.js.map +1 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
- package/src/config.mobile.ts +647 -0
- package/src/config.ts +209 -0
- package/src/formatting.ts +297 -0
- package/src/index.ts +105 -0
- package/src/language.ts +270 -0
- package/src/locales/af-ZA/common.json +308 -0
- package/src/locales/af-ZA/mobile.json +178 -0
- package/src/locales/af-ZA/nav.json +28 -0
- package/src/locales/af-ZA/notifications.json +279 -0
- package/src/locales/af-ZA/pages.json +953 -0
- package/src/locales/af-ZA/settings.json +487 -0
- package/src/locales/ar-SA/common.json +308 -0
- package/src/locales/ar-SA/mobile.json +178 -0
- package/src/locales/ar-SA/nav.json +28 -0
- package/src/locales/ar-SA/notifications.json +279 -0
- package/src/locales/ar-SA/pages.json +953 -0
- package/src/locales/ar-SA/settings.json +487 -0
- package/src/locales/ca-ES/common.json +308 -0
- package/src/locales/ca-ES/mobile.json +178 -0
- package/src/locales/ca-ES/nav.json +28 -0
- package/src/locales/ca-ES/notifications.json +279 -0
- package/src/locales/ca-ES/pages.json +953 -0
- package/src/locales/ca-ES/settings.json +487 -0
- package/src/locales/cs-CZ/common.json +308 -0
- package/src/locales/cs-CZ/mobile.json +178 -0
- package/src/locales/cs-CZ/nav.json +28 -0
- package/src/locales/cs-CZ/notifications.json +279 -0
- package/src/locales/cs-CZ/pages.json +953 -0
- package/src/locales/cs-CZ/settings.json +487 -0
- package/src/locales/da-DK/common.json +308 -0
- package/src/locales/da-DK/mobile.json +178 -0
- package/src/locales/da-DK/nav.json +28 -0
- package/src/locales/da-DK/notifications.json +279 -0
- package/src/locales/da-DK/pages.json +953 -0
- package/src/locales/da-DK/settings.json +487 -0
- package/src/locales/de-DE/common.json +308 -0
- package/src/locales/de-DE/mobile.json +178 -0
- package/src/locales/de-DE/nav.json +28 -0
- package/src/locales/de-DE/notifications.json +279 -0
- package/src/locales/de-DE/pages.json +953 -0
- package/src/locales/de-DE/settings.json +487 -0
- package/src/locales/el-GR/common.json +308 -0
- package/src/locales/el-GR/mobile.json +178 -0
- package/src/locales/el-GR/nav.json +28 -0
- package/src/locales/el-GR/notifications.json +279 -0
- package/src/locales/el-GR/pages.json +953 -0
- package/src/locales/el-GR/settings.json +487 -0
- package/src/locales/en/common.json +308 -0
- package/src/locales/en/mobile.json +178 -0
- package/src/locales/en/nav.json +28 -0
- package/src/locales/en/notifications.json +279 -0
- package/src/locales/en/pages.json +953 -0
- package/src/locales/en/settings.json +487 -0
- package/src/locales/en-US/common.json +308 -0
- package/src/locales/en-US/mobile.json +178 -0
- package/src/locales/en-US/nav.json +28 -0
- package/src/locales/en-US/notifications.json +279 -0
- package/src/locales/en-US/pages.json +953 -0
- package/src/locales/en-US/settings.json +487 -0
- package/src/locales/es-ES/common.json +308 -0
- package/src/locales/es-ES/mobile.json +178 -0
- package/src/locales/es-ES/nav.json +28 -0
- package/src/locales/es-ES/notifications.json +279 -0
- package/src/locales/es-ES/pages.json +953 -0
- package/src/locales/es-ES/settings.json +487 -0
- package/src/locales/fi-FI/common.json +308 -0
- package/src/locales/fi-FI/mobile.json +178 -0
- package/src/locales/fi-FI/nav.json +28 -0
- package/src/locales/fi-FI/notifications.json +279 -0
- package/src/locales/fi-FI/pages.json +953 -0
- package/src/locales/fi-FI/settings.json +487 -0
- package/src/locales/fr-FR/common.json +308 -0
- package/src/locales/fr-FR/mobile.json +178 -0
- package/src/locales/fr-FR/nav.json +28 -0
- package/src/locales/fr-FR/notifications.json +279 -0
- package/src/locales/fr-FR/pages.json +953 -0
- package/src/locales/fr-FR/settings.json +487 -0
- package/src/locales/he-IL/common.json +308 -0
- package/src/locales/he-IL/mobile.json +178 -0
- package/src/locales/he-IL/nav.json +28 -0
- package/src/locales/he-IL/notifications.json +279 -0
- package/src/locales/he-IL/pages.json +953 -0
- package/src/locales/he-IL/settings.json +487 -0
- package/src/locales/hu-HU/common.json +308 -0
- package/src/locales/hu-HU/mobile.json +178 -0
- package/src/locales/hu-HU/nav.json +28 -0
- package/src/locales/hu-HU/notifications.json +279 -0
- package/src/locales/hu-HU/pages.json +953 -0
- package/src/locales/hu-HU/settings.json +487 -0
- package/src/locales/it-IT/common.json +308 -0
- package/src/locales/it-IT/mobile.json +178 -0
- package/src/locales/it-IT/nav.json +28 -0
- package/src/locales/it-IT/notifications.json +279 -0
- package/src/locales/it-IT/pages.json +953 -0
- package/src/locales/it-IT/settings.json +487 -0
- package/src/locales/ja-JP/common.json +308 -0
- package/src/locales/ja-JP/mobile.json +178 -0
- package/src/locales/ja-JP/nav.json +28 -0
- package/src/locales/ja-JP/notifications.json +279 -0
- package/src/locales/ja-JP/pages.json +953 -0
- package/src/locales/ja-JP/settings.json +487 -0
- package/src/locales/ko-KR/common.json +308 -0
- package/src/locales/ko-KR/mobile.json +178 -0
- package/src/locales/ko-KR/nav.json +28 -0
- package/src/locales/ko-KR/notifications.json +279 -0
- package/src/locales/ko-KR/pages.json +953 -0
- package/src/locales/ko-KR/settings.json +487 -0
- package/src/locales/nl-NL/common.json +308 -0
- package/src/locales/nl-NL/mobile.json +178 -0
- package/src/locales/nl-NL/nav.json +28 -0
- package/src/locales/nl-NL/notifications.json +279 -0
- package/src/locales/nl-NL/pages.json +953 -0
- package/src/locales/nl-NL/settings.json +487 -0
- package/src/locales/no-NO/common.json +308 -0
- package/src/locales/no-NO/mobile.json +178 -0
- package/src/locales/no-NO/nav.json +28 -0
- package/src/locales/no-NO/notifications.json +279 -0
- package/src/locales/no-NO/pages.json +953 -0
- package/src/locales/no-NO/settings.json +487 -0
- package/src/locales/pl-PL/common.json +308 -0
- package/src/locales/pl-PL/mobile.json +178 -0
- package/src/locales/pl-PL/nav.json +28 -0
- package/src/locales/pl-PL/notifications.json +279 -0
- package/src/locales/pl-PL/pages.json +953 -0
- package/src/locales/pl-PL/settings.json +487 -0
- package/src/locales/pt-BR/common.json +308 -0
- package/src/locales/pt-BR/mobile.json +178 -0
- package/src/locales/pt-BR/nav.json +28 -0
- package/src/locales/pt-BR/notifications.json +279 -0
- package/src/locales/pt-BR/pages.json +953 -0
- package/src/locales/pt-BR/settings.json +487 -0
- package/src/locales/pt-PT/common.json +308 -0
- package/src/locales/pt-PT/mobile.json +178 -0
- package/src/locales/pt-PT/nav.json +28 -0
- package/src/locales/pt-PT/notifications.json +279 -0
- package/src/locales/pt-PT/pages.json +953 -0
- package/src/locales/pt-PT/settings.json +487 -0
- package/src/locales/ro-RO/common.json +308 -0
- package/src/locales/ro-RO/mobile.json +178 -0
- package/src/locales/ro-RO/nav.json +28 -0
- package/src/locales/ro-RO/notifications.json +279 -0
- package/src/locales/ro-RO/pages.json +953 -0
- package/src/locales/ro-RO/settings.json +487 -0
- package/src/locales/ru-RU/common.json +308 -0
- package/src/locales/ru-RU/mobile.json +178 -0
- package/src/locales/ru-RU/nav.json +28 -0
- package/src/locales/ru-RU/notifications.json +279 -0
- package/src/locales/ru-RU/pages.json +953 -0
- package/src/locales/ru-RU/settings.json +487 -0
- package/src/locales/sr-SP/common.json +308 -0
- package/src/locales/sr-SP/mobile.json +178 -0
- package/src/locales/sr-SP/nav.json +28 -0
- package/src/locales/sr-SP/notifications.json +279 -0
- package/src/locales/sr-SP/pages.json +953 -0
- package/src/locales/sr-SP/settings.json +487 -0
- package/src/locales/sv-SE/common.json +308 -0
- package/src/locales/sv-SE/mobile.json +178 -0
- package/src/locales/sv-SE/nav.json +28 -0
- package/src/locales/sv-SE/notifications.json +279 -0
- package/src/locales/sv-SE/pages.json +953 -0
- package/src/locales/sv-SE/settings.json +487 -0
- package/src/locales/tr-TR/common.json +308 -0
- package/src/locales/tr-TR/mobile.json +178 -0
- package/src/locales/tr-TR/nav.json +28 -0
- package/src/locales/tr-TR/notifications.json +279 -0
- package/src/locales/tr-TR/pages.json +953 -0
- package/src/locales/tr-TR/settings.json +487 -0
- package/src/locales/uk-UA/common.json +308 -0
- package/src/locales/uk-UA/mobile.json +178 -0
- package/src/locales/uk-UA/nav.json +28 -0
- package/src/locales/uk-UA/notifications.json +279 -0
- package/src/locales/uk-UA/pages.json +953 -0
- package/src/locales/uk-UA/settings.json +487 -0
- package/src/locales/vi-VN/common.json +308 -0
- package/src/locales/vi-VN/mobile.json +178 -0
- package/src/locales/vi-VN/nav.json +28 -0
- package/src/locales/vi-VN/notifications.json +279 -0
- package/src/locales/vi-VN/pages.json +953 -0
- package/src/locales/vi-VN/settings.json +487 -0
- package/src/locales/zh-CN/common.json +308 -0
- package/src/locales/zh-CN/mobile.json +178 -0
- package/src/locales/zh-CN/nav.json +28 -0
- package/src/locales/zh-CN/notifications.json +279 -0
- package/src/locales/zh-CN/pages.json +953 -0
- package/src/locales/zh-CN/settings.json +487 -0
- package/src/locales/zh-TW/common.json +308 -0
- package/src/locales/zh-TW/mobile.json +178 -0
- package/src/locales/zh-TW/nav.json +28 -0
- package/src/locales/zh-TW/notifications.json +279 -0
- package/src/locales/zh-TW/pages.json +953 -0
- package/src/locales/zh-TW/settings.json +487 -0
- package/src/mobile.ts +69 -0
- 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';
|