From 0ccc03208514aaf4a4228f60118553252bed3e33 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 17 Sep 2024 15:50:21 +0300 Subject: [PATCH 01/26] Implement fs multiplication backend for i18next --- .../i18next/fs.multilocation.backend.js | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 workers/loc.api/i18next/fs.multilocation.backend.js diff --git a/workers/loc.api/i18next/fs.multilocation.backend.js b/workers/loc.api/i18next/fs.multilocation.backend.js new file mode 100644 index 00000000..ae324f1a --- /dev/null +++ b/workers/loc.api/i18next/fs.multilocation.backend.js @@ -0,0 +1,58 @@ +'use strict' + +const Backend = require('i18next-fs-backend') +const { merge, min } = require('lib-js-util-base') + +/** + * Extend the main fs Backend to provide the ability + * to load and merge translations in bfx-reports-framework + */ +class FsMultilocationBackend extends Backend { + async read (language, namespace, callback) { + try { + const loadPaths = this.options.loadPaths + const errors = [] + const dataArr = [] + const timestamps = [] + + for (const loadPath of loadPaths) { + this.options.loadPath = loadPath + + await new Promise((resolve) => { + super.read(language, namespace, (err, data, timestamp) => { + if (err) { + errors.push(err) + resolve() + + return + } + if (data) { + dataArr.push(data) + } + if (timestamp) { + timestamps.push(timestamp) + } + + resolve() + }) + }) + } + + if (loadPaths.length === errors.length) { + callback(errors[0], false) + + return + } + + callback( + null, + merge({}, ...dataArr), + min(timestamps) + ) + } catch (err) { + callback(err, false) + } + } +} + +module.exports = FsMultilocationBackend From 6e287f2a49804a9c66fd537ed1ee7ee5a4a417d4 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 17 Sep 2024 15:52:33 +0300 Subject: [PATCH 02/26] Implement module to get i18next configured instance --- workers/loc.api/i18next/index.js | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 workers/loc.api/i18next/index.js diff --git a/workers/loc.api/i18next/index.js b/workers/loc.api/i18next/index.js new file mode 100644 index 00000000..a2de28e8 --- /dev/null +++ b/workers/loc.api/i18next/index.js @@ -0,0 +1,51 @@ +'use strict' + +const i18next = require('i18next') +const { merge } = require('lib-js-util-base') +const path = require('path') +const fs = require('fs') + +const FsMultilocationBackend = require('./fs.multilocation.backend') + +module.exports = async (params) => { + const { + i18nextConfigs, + transPaths + } = params ?? {} + + const configs = merge( + { + fallbackLng: 'en', + ns: ['email', 'pdf'], + defaultNS: 'email', + preload: [...transPaths.reduce((accum, transPath) => { + const allFileNames = fs.readdirSync(transPath) + + for (const fileName of allFileNames) { + const filePath = path.join(transPath, fileName) + const stats = fs.lstatSync(filePath) + + if (!stats.isDirectory()) { + continue + } + + accum.add(fileName) + } + + return accum + }, new Set())], + backend: { + loadPaths: transPaths.map((transPath) => ( + path.join(transPath, '{{lng}}/{{ns}}.json') + )) + } + }, + i18nextConfigs + ) + + await i18next + .use(FsMultilocationBackend) + .init(configs) + + return i18next +} From 2e555c793b522f161d3bbb924a19ab2a1f225d93 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 17 Sep 2024 15:53:49 +0300 Subject: [PATCH 03/26] Add simple locales for i18next --- locales/en/email.json | 5 +++++ locales/en/pdf.json | 5 +++++ locales/ru/email.json | 5 +++++ locales/ru/pdf.json | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 locales/en/email.json create mode 100644 locales/en/pdf.json create mode 100644 locales/ru/email.json create mode 100644 locales/ru/pdf.json diff --git a/locales/en/email.json b/locales/en/email.json new file mode 100644 index 00000000..efd031cb --- /dev/null +++ b/locales/en/email.json @@ -0,0 +1,5 @@ +{ + "template": { + "subject": "Your report is ready" + } +} diff --git a/locales/en/pdf.json b/locales/en/pdf.json new file mode 100644 index 00000000..544ad138 --- /dev/null +++ b/locales/en/pdf.json @@ -0,0 +1,5 @@ +{ + "template": { + "title": "Report" + } +} diff --git a/locales/ru/email.json b/locales/ru/email.json new file mode 100644 index 00000000..fd716124 --- /dev/null +++ b/locales/ru/email.json @@ -0,0 +1,5 @@ +{ + "template": { + "subject": "Ваш отчет готов" + } +} diff --git a/locales/ru/pdf.json b/locales/ru/pdf.json new file mode 100644 index 00000000..441f124e --- /dev/null +++ b/locales/ru/pdf.json @@ -0,0 +1,5 @@ +{ + "template": { + "title": "Отчет" + } +} From a6cbbab388f361bc5143101c22aeaf95bc5c1e12 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 17 Sep 2024 15:56:33 +0300 Subject: [PATCH 04/26] Add initialization flow for i18next into entrypoint --- workers/api.service.report.wrk.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/workers/api.service.report.wrk.js b/workers/api.service.report.wrk.js index 9ae4ff23..712cc61b 100644 --- a/workers/api.service.report.wrk.js +++ b/workers/api.service.report.wrk.js @@ -46,6 +46,7 @@ const TYPES = require('./loc.api/di/types') const { setLoggerDeps } = require('./loc.api/logger/logger-deps') +const getI18next = require('./loc.api/i18next') const { PDFBufferUnderElectronCreationError } = require('./loc.api/errors') @@ -60,6 +61,7 @@ class WrkReportServiceApi extends WrkApi { this.coreDeps = [] this.appDeps = [] + this.transPaths = [] this.loadDIConfig() this.loadCoreDeps() @@ -70,6 +72,11 @@ class WrkReportServiceApi extends WrkApi { this.start() } + addTransLocation (transPath) { + const _transPath = transPath ?? path.join(__dirname, '../locales') + this.transPaths.push(_transPath) + } + loadDIConfig (cont = container) { const conf = this.conf[this.group] @@ -88,6 +95,15 @@ class WrkReportServiceApi extends WrkApi { this.container.load(...this.appDeps) } + async getI18next (i18nextConfigs) { + const i18next = await getI18next({ + i18nextConfigs, + transPaths: this.transPaths + }) + + return i18next + } + getPluginCtx (type) { const ctx = super.getPluginCtx(type) @@ -124,6 +140,7 @@ class WrkReportServiceApi extends WrkApi { init () { super.init() + this.addTransLocation() const dbPathAbsolute = path.isAbsolute(argv.dbFolder) ? argv.dbFolder @@ -179,6 +196,9 @@ class WrkReportServiceApi extends WrkApi { rService.ctx = rService.caller.getCtx() } + // TODO: need to set into DI + const i18next = await this.getI18next() + this.loadAppDeps({ rService, processorQueue, From dbcda7a774ee0be0c804b161e34df6c645da33f0 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 17 Sep 2024 15:57:31 +0300 Subject: [PATCH 05/26] Add i18next and i18next-fs-backend to prod deps --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 595de9c8..5234a6f7 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "bitfinex-api-node": "6.0.0", "colors": "1.4.0", "csv": "5.5.3", + "i18next": "23.15.1", + "i18next-fs-backend": "2.3.2", "inversify": "6.0.1", "js-yaml": "4.1.0", "lib-js-util-base": "git+https://github.com/bitfinexcom/lib-js-util-base.git", From a5c52170fc6e716bafff698cb784637a7f1d0fd2 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 13:52:25 +0300 Subject: [PATCH 06/26] Add di type for I18next --- workers/loc.api/di/types.js | 1 + 1 file changed, 1 insertion(+) diff --git a/workers/loc.api/di/types.js b/workers/loc.api/di/types.js index 693bb2f6..09b5b67a 100644 --- a/workers/loc.api/di/types.js +++ b/workers/loc.api/di/types.js @@ -8,6 +8,7 @@ module.exports = { Container: Symbol.for('Container'), LoggerFactory: Symbol.for('LoggerFactory'), Logger: Symbol.for('Logger'), + I18next: Symbol.for('I18next'), RService: Symbol.for('RService'), DeflateFac: Symbol.for('DeflateFac'), GrcSlackFac: Symbol.for('GrcSlackFac'), From 6a62f3942899079ab4764eb9855051b8131a3ea1 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 13:53:20 +0300 Subject: [PATCH 07/26] Add I18next service to di --- workers/loc.api/di/app.deps.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/workers/loc.api/di/app.deps.js b/workers/loc.api/di/app.deps.js index c66db1d3..619e7f74 100644 --- a/workers/loc.api/di/app.deps.js +++ b/workers/loc.api/di/app.deps.js @@ -40,11 +40,13 @@ module.exports = ({ aggregatorQueue, deflateFac, grcSlackFac, - link + link, + i18next }) => { return new ContainerModule((bind) => { bind(TYPES.RService).toConstantValue(rService) bind(TYPES.RootPath).toConstantValue(rService.ctx.rootPath) + bind(TYPES.I18next).toConstantValue(i18next) bind(TYPES.RServiceDepsSchema).toConstantValue([ ['_responder', TYPES.Responder], ['_getREST', TYPES.GetREST], @@ -52,7 +54,8 @@ module.exports = ({ ['_prepareApiResponse', TYPES.PrepareApiResponse], ['_generateReportFile', TYPES.GenerateReportFile], ['_hasGrcService', TYPES.HasGrcService], - ['_weightedAveragesReport', TYPES.WeightedAveragesReport] + ['_weightedAveragesReport', TYPES.WeightedAveragesReport], + ['_i18next', TYPES.I18next] ]) bind(TYPES.RServiceDepsSchemaAliase) .toDynamicValue((ctx) => { From 025bf755ccd5a826825b0f732c8c6c5cb30bd52d Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 13:54:51 +0300 Subject: [PATCH 08/26] Inject i18next into di --- workers/api.service.report.wrk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/api.service.report.wrk.js b/workers/api.service.report.wrk.js index 712cc61b..01a2b573 100644 --- a/workers/api.service.report.wrk.js +++ b/workers/api.service.report.wrk.js @@ -196,7 +196,6 @@ class WrkReportServiceApi extends WrkApi { rService.ctx = rService.caller.getCtx() } - // TODO: need to set into DI const i18next = await this.getI18next() this.loadAppDeps({ @@ -206,6 +205,7 @@ class WrkReportServiceApi extends WrkApi { link: this.grc_bfx.link, deflateFac: this.deflate_gzip, grcSlackFac, + i18next, ...deps }) From 75bf9ae7538984dc5ae6f61946a81a565ef90a20 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:01:34 +0300 Subject: [PATCH 09/26] Improve i18next factory --- workers/loc.api/i18next/index.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/workers/loc.api/i18next/index.js b/workers/loc.api/i18next/index.js index a2de28e8..f891aaac 100644 --- a/workers/loc.api/i18next/index.js +++ b/workers/loc.api/i18next/index.js @@ -7,7 +7,13 @@ const fs = require('fs') const FsMultilocationBackend = require('./fs.multilocation.backend') -module.exports = async (params) => { +let i18nextInstance = null + +module.exports = (params) => { + if (i18nextInstance) { + return i18nextInstance + } + const { i18nextConfigs, transPaths @@ -43,9 +49,10 @@ module.exports = async (params) => { i18nextConfigs ) - await i18next + const promise = i18next .use(FsMultilocationBackend) .init(configs) + i18nextInstance = i18next - return i18next + return promise.then(() => i18next) } From 5693739e727b27293e21a85ed40eeb3a5d28760b Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:04:16 +0300 Subject: [PATCH 10/26] Add en translation in json --- locales/en/email.json | 12 +++++++++++- locales/en/pdf.json | 13 ++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/locales/en/email.json b/locales/en/email.json index efd031cb..b86690a3 100644 --- a/locales/en/email.json +++ b/locales/en/email.json @@ -1,5 +1,15 @@ { "template": { - "subject": "Your report is ready" + "subject": "Your report is ready", + "btnText": "Download Report", + "fileName": "File name", + "unauth": "Your file could not be completed, please try again", + "readyForDownload": "The report you request is ready for download", + "ifDidNotInitAction": "If you did not initiate this action and you suspect that your account may be compromised, please", + "freezeAccount": "freeze your account", + "contactSupport": "and contact support", + "youCan": "You can", + "download": "download", + "pgpSignature": "a PGP digital signature file" } } diff --git a/locales/en/pdf.json b/locales/en/pdf.json index 544ad138..37fdc566 100644 --- a/locales/en/pdf.json +++ b/locales/en/pdf.json @@ -1,5 +1,16 @@ { "template": { - "title": "Report" + "title": "Report", + "statementDetails": "Statement Details", + "statementDate": "Statement date", + "snapshotAt": "Snapshot at", + "period": "Period", + "username": "Username", + "email": "Email", + "errorMessage": "Your file could not be completed, please try again", + "reportGenAt": "Report generated at", + "copyright": "Copyright © 2013-2024 iFinex Inc. All rights reserved.", + "page": "Page", + "from": "from" } } From 1e558f508d65a6e3e626be64df48a8a59ca66e55 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:04:42 +0300 Subject: [PATCH 11/26] Add ru translation in json --- locales/ru/email.json | 12 +++++++++++- locales/ru/pdf.json | 13 ++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/locales/ru/email.json b/locales/ru/email.json index fd716124..e53c1326 100644 --- a/locales/ru/email.json +++ b/locales/ru/email.json @@ -1,5 +1,15 @@ { "template": { - "subject": "Ваш отчет готов" + "subject": "Ваш отчет готов", + "btnText": "Скачать Отчет", + "fileName": "Имя файла", + "unauth": "Ваш файл не может быть завершен, пожалуйста, попробуйте еще раз", + "readyForDownload": "Запрашиваемый вами отчет готов к загрузке", + "ifDidNotInitAction": "Если вы не инициировали это действие и подозреваете, что ваша учетная запись может быть взломана, пожалуйста,", + "freezeAccount": "заблокируйте ее", + "contactSupport": "и обратитесь в службу поддержки", + "youCan": "Вы можете", + "download": "скачать", + "pgpSignature": "файл цифровой подписи PGP" } } diff --git a/locales/ru/pdf.json b/locales/ru/pdf.json index 441f124e..e04d9f2b 100644 --- a/locales/ru/pdf.json +++ b/locales/ru/pdf.json @@ -1,5 +1,16 @@ { "template": { - "title": "Отчет" + "title": "Отчет", + "statementDetails": "Детали ведомости", + "statementDate": "Дата ведомости", + "snapshotAt": "Снимок в", + "period": "Период", + "username": "Имя пользователя", + "email": "Эл. почта", + "errorMessage": "Ваш файл не может быть завершен, пожалуйста, попробуйте еще раз", + "reportGenAt": "Отчет сгенерирован в", + "copyright": "Авторское право © 2013-2024 iFinex Inc. Все права защищены.", + "page": "Стр.", + "from": "из" } } From 708859d8c01147c70c849bcbdc7bff0affc73a5a Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:05:07 +0300 Subject: [PATCH 12/26] Add es translation in json --- locales/es-EM/email.json | 15 +++++++++++++++ locales/es-EM/pdf.json | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 locales/es-EM/email.json create mode 100644 locales/es-EM/pdf.json diff --git a/locales/es-EM/email.json b/locales/es-EM/email.json new file mode 100644 index 00000000..01e10af2 --- /dev/null +++ b/locales/es-EM/email.json @@ -0,0 +1,15 @@ +{ + "template": { + "subject": "Tu reporte esta listo", + "btnText": "Descargar el Informe", + "fileName": "Nombre del archivo", + "unauth": "Tu archivo no se pudo completar, intente de nuevo por favor.", + "readyForDownload": "Tu reporte esta listo para ser descargado", + "ifDidNotInitAction": "Si no realizaste esta acción y sospechas que tu cuenta puede estar comprometida, por favor", + "freezeAccount": "congela tu cuenta", + "contactSupport": "y contacta soporte", + "youCan": "Puedes", + "download": "descargar", + "pgpSignature": "un archivo con firma digital PGP" + } +} diff --git a/locales/es-EM/pdf.json b/locales/es-EM/pdf.json new file mode 100644 index 00000000..7764d527 --- /dev/null +++ b/locales/es-EM/pdf.json @@ -0,0 +1,16 @@ +{ + "template": { + "title": "Reporte", + "statementDetails": "Detalles del Estado", + "statementDate": "Fecha del Estado", + "snapshotAt": "Captura al", + "period": "Periodo", + "username": "Nombre de Usuario", + "email": "Correo", + "errorMessage": "Tu archivo no pudo ser completado, por favor inténtalo de nuevo", + "reportGenAt": "Reporte generado al", + "copyright": "Copyright © 2013-2024 iFinex Inc. Todos los derechos reservados.", + "page": "Página", + "from": "de" + } +} From d98bf82636e317b6d280f9750b3bf219ec550383 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:05:26 +0300 Subject: [PATCH 13/26] Add pt translation in json --- locales/pt-BR/email.json | 15 +++++++++++++++ locales/pt-BR/pdf.json | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 locales/pt-BR/email.json create mode 100644 locales/pt-BR/pdf.json diff --git a/locales/pt-BR/email.json b/locales/pt-BR/email.json new file mode 100644 index 00000000..383c132f --- /dev/null +++ b/locales/pt-BR/email.json @@ -0,0 +1,15 @@ +{ + "template": { + "subject": "Seu relatório está pronto", + "btnText": "Baixar Relatório", + "fileName": "Nome do arquivo", + "unauth": "Não foi possível concluir seu arquivo, tente novamente", + "readyForDownload": "O relatório solicitado está pronto para download", + "ifDidNotInitAction": "Se você não iniciou esta ação e suspeita que sua conta pode estar comprometida", + "freezeAccount": "por favor, congele sua conta", + "contactSupport": "e entre em contato com o suporte", + "youCan": "Você pode", + "download": "baixar", + "pgpSignature": "um arquivo de assinatura digital PGP" + } +} diff --git a/locales/pt-BR/pdf.json b/locales/pt-BR/pdf.json new file mode 100644 index 00000000..f33b377e --- /dev/null +++ b/locales/pt-BR/pdf.json @@ -0,0 +1,16 @@ +{ + "template": { + "title": "Informes", + "statementDetails": "Detalhes da declaração", + "statementDate": "Data da declaração", + "snapshotAt": "Captura de", + "period": "Período", + "username": "Nome de Usuário", + "email": "e-mail", + "errorMessage": "Seu arquivo não pode ser completado, por favor tente novamente", + "reportGenAt": "Informe gerado em ", + "copyright": "Copyright © 2013-2024 iFinex Inc. Todos os direitos reservados.", + "page": "Página", + "from": "de" + } +} From a01b29cf6f7dc152c9abbc8d90479a26b543d0de Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:05:47 +0300 Subject: [PATCH 14/26] Add tr translation in json --- locales/tr/email.json | 15 +++++++++++++++ locales/tr/pdf.json | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 locales/tr/email.json create mode 100644 locales/tr/pdf.json diff --git a/locales/tr/email.json b/locales/tr/email.json new file mode 100644 index 00000000..d9237f34 --- /dev/null +++ b/locales/tr/email.json @@ -0,0 +1,15 @@ +{ + "template": { + "subject": "Raporunuz hazır", + "btnText": "Raporu İndir", + "fileName": "Dosya adı", + "unauth": "Dosyanız tamamlanamadı, lütfen tekrar deneyin", + "readyForDownload": "İstediğiniz rapor indirilmeye hazır", + "ifDidNotInitAction": "Bu işlemi siz başlatmadıysanız ve hesabınızın güvenliğinin ihlal edilmiş olabileceğinden şüpheleniyorsanız, lütfen", + "freezeAccount": "hesabınızı dondurun", + "contactSupport": "ve destek ekibiyle iletişime geçin", + "youCan": "Yapabilirsiniz", + "download": "indir", + "pgpSignature": "bir PGP dijital imza dosyası" + } +} diff --git a/locales/tr/pdf.json b/locales/tr/pdf.json new file mode 100644 index 00000000..2813bc70 --- /dev/null +++ b/locales/tr/pdf.json @@ -0,0 +1,16 @@ +{ + "template": { + "title": "Rapor", + "statementDetails": "Beyanname Detayları", + "statementDate": "Beyanname Tarihi", + "snapshotAt": "Anlık Görüntü:", + "period": "Dönem", + "username": "Kullanıcı Adı", + "email": "Email", + "errorMessage": "Dosyanız tamamlanamadı, lütfen tekrar deneyin", + "reportGenAt": "Rapor şu tarihte oluşturuldu:", + "copyright": "Telif Hakkı © 2013-2024 iFinex Inc. Tüm hakları saklıdır.", + "page": "Sayfa", + "from": "İtibaren" + } +} From b111e4594d101514c90e00b5fed12fbc62a257ca Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:06:09 +0300 Subject: [PATCH 15/26] Add zh-cn translation in json --- locales/zh-CN/email.json | 15 +++++++++++++++ locales/zh-CN/pdf.json | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 locales/zh-CN/email.json create mode 100644 locales/zh-CN/pdf.json diff --git a/locales/zh-CN/email.json b/locales/zh-CN/email.json new file mode 100644 index 00000000..2b18df1b --- /dev/null +++ b/locales/zh-CN/email.json @@ -0,0 +1,15 @@ +{ + "template": { + "subject": "您的报告已备妥", + "btnText": "下载报告档案", + "fileName": "档案名称", + "unauth": "档案未下载成功,请重试一次", + "readyForDownload": "您请求的报告已可下载", + "ifDidNotInitAction": "如果您未进行此操作并且怀疑您的帐户已遭盗用,请", + "freezeAccount": "冻结您的帐户", + "contactSupport": "以及联络客服人员", + "youCan": "您可以", + "download": "下载", + "pgpSignature": "PGP数位签名档" + } +} diff --git a/locales/zh-CN/pdf.json b/locales/zh-CN/pdf.json new file mode 100644 index 00000000..579aca9b --- /dev/null +++ b/locales/zh-CN/pdf.json @@ -0,0 +1,16 @@ +{ + "template": { + "title": "报告", + "statementDetails": "结算明细", + "statementDate": "结算日期", + "snapshotAt": "快照时间", + "period": "期间", + "username": "用户名", + "email": "电子邮箱", + "errorMessage": "无法建立档案,请重试", + "reportGenAt": "报告创建时间", + "copyright": "Copyright © 2013-2024 iFinex Inc. All rights reserved.", + "page": "页数", + "from": "从" + } +} From 6f8dd41a033b9731f9b8c7a44e6a233baa784c2b Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 18 Sep 2024 14:06:26 +0300 Subject: [PATCH 16/26] Add zh-tw translation in json --- locales/zh-TW/email.json | 15 +++++++++++++++ locales/zh-TW/pdf.json | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 locales/zh-TW/email.json create mode 100644 locales/zh-TW/pdf.json diff --git a/locales/zh-TW/email.json b/locales/zh-TW/email.json new file mode 100644 index 00000000..25956fd4 --- /dev/null +++ b/locales/zh-TW/email.json @@ -0,0 +1,15 @@ +{ + "template": { + "subject": "您的報告已備妥", + "btnText": "下載报告檔案", + "fileName": "檔案名稱", + "unauth": "檔案未下載成功,請重試一次", + "readyForDownload": "您請求的報告已可下載", + "ifDidNotInitAction": "如果您未進行此操作並且懷疑您的帳戶已遭盜用,請", + "freezeAccount": "凍結您的帳戶", + "contactSupport": "以及聯絡客服人員", + "youCan": "您可以", + "download": "下載", + "pgpSignature": "PGP數位簽名檔案" + } +} diff --git a/locales/zh-TW/pdf.json b/locales/zh-TW/pdf.json new file mode 100644 index 00000000..fae8c1f8 --- /dev/null +++ b/locales/zh-TW/pdf.json @@ -0,0 +1,16 @@ +{ + "template": { + "title": "報告", + "statementDetails": "結算明細", + "statementDate": "結算日期", + "snapshotAt": "快照時間:", + "period": "期間", + "username": "用戶名", + "email": "電子郵箱", + "errorMessage": "無法建立檔案,請重試", + "reportGenAt": "報告建立時間:", + "copyright": "Copyright © 2013-2024 iFinex Inc. All rights reserved.", + "page": "頁數", + "from": "從" + } +} From b6cb2e840117ae547b84b047013416efc50c391b Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:43:03 +0300 Subject: [PATCH 17/26] Inject I18next to SendMail service --- workers/loc.api/di/app.deps.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/workers/loc.api/di/app.deps.js b/workers/loc.api/di/app.deps.js index 619e7f74..4a57dd8c 100644 --- a/workers/loc.api/di/app.deps.js +++ b/workers/loc.api/di/app.deps.js @@ -167,7 +167,10 @@ module.exports = ({ bind(TYPES.SendMail).toConstantValue( bindDepsToFn( sendMail, - [TYPES.GrcBfxReq] + [ + TYPES.GrcBfxReq, + TYPES.I18next + ] ) ) bind(TYPES.Processor) From f96d84c21504b463ce835dc8cd5391f9c1d3f2d1 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:45:03 +0300 Subject: [PATCH 18/26] Rework pug translator to use i18next api --- workers/loc.api/helpers/get-translator.js | 65 +++++++---------------- 1 file changed, 18 insertions(+), 47 deletions(-) diff --git a/workers/loc.api/helpers/get-translator.js b/workers/loc.api/helpers/get-translator.js index 80f5e343..0a1b919d 100644 --- a/workers/loc.api/helpers/get-translator.js +++ b/workers/loc.api/helpers/get-translator.js @@ -1,60 +1,31 @@ 'use strict' -const LANGUAGES = require('./languages') - -const getTranslator = (params) => { +const getTranslator = (deps, commonOpts) => { const { - language = 'en', - translations, - isNotDefaultTranslatorUsed = false - } = params ?? {} - - const normLang = LANGUAGES?.[language] ?? 'en' - const translatorByDefault = ( - !isNotDefaultTranslatorUsed && - getTranslator({ - language: 'en', - translations, - isNotDefaultTranslatorUsed: true - }) - ) + i18next + } = deps ?? {} return (defVal = '', opts) => { const prop = typeof opts === 'string' ? opts : opts?.prop + const options = ( + opts && + typeof opts === 'object' + ) + ? opts + : {} + const defaultValue = defVal ?? + commonOpts?.defaultValue ?? + options?.defaultValue ?? + '' - if ( - !translations?.[normLang] || - typeof translations[normLang] !== 'object' || - Object.keys(translations[normLang]) === 0 || - typeof prop !== 'string' || - !prop - ) { - return translatorByDefault - ? translatorByDefault(defVal, prop) - : defVal - } - - const res = prop.split('.').reduce((accum, curr) => { - if ( - typeof accum[curr] === 'object' || - typeof accum[curr] === 'string' || - Number.isFinite(accum[curr]) - ) { - return accum[curr] - } - - return accum - }, translations[normLang]) - - if (typeof res === 'object') { - return translatorByDefault - ? translatorByDefault(defVal, prop) - : defVal - } + return i18next.t(prop, { + ...commonOpts, + ...options, - return res + defaultValue + }) } } From 45f334526813dec6d71637fa19de2fce115bafaa Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:46:13 +0300 Subject: [PATCH 19/26] Rework SendMail service to use i18next api --- workers/loc.api/queue/send-mail/index.js | 48 +++++++++++++++--------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/workers/loc.api/queue/send-mail/index.js b/workers/loc.api/queue/send-mail/index.js index 18e533ba..1dfadd91 100644 --- a/workers/loc.api/queue/send-mail/index.js +++ b/workers/loc.api/queue/send-mail/index.js @@ -1,23 +1,33 @@ 'use strict' const path = require('path') -const fs = require('fs') const pug = require('pug') -const yaml = require('js-yaml') const getTranslator = require('../../helpers/get-translator') -const LANGUAGES = require('../../helpers/languages') +const TRANSLATION_NAMESPACES = require( + '../../i18next/translation.namespaces' +) const basePathToViews = path.join(__dirname, 'views') -const pathToTrans = path.join( - __dirname, - 'translations/email.yml' -) -const translations = yaml.load( - fs.readFileSync(pathToTrans, 'utf8') -) -module.exports = (grcBfxReq) => { +const SENDGRID_WORKER_SUPPORTED_LNGS = { + en: 'en', + 'en-US': 'en', + ru: 'ru', + 'ru-RU': 'ru', + zh: 'zh-CN', + 'zh-CN': 'zh-CN', + 'zh-TW': 'zh-TW', + pt: 'pt-BR', + 'pt-PT': 'pt-BR', + 'pt-BR': 'pt-BR' +} + +const _getSendgridLng = (lng) => { + return SENDGRID_WORKER_SUPPORTED_LNGS[lng] ?? lng +} + +module.exports = (grcBfxReq, i18next) => { return async ( configs, to, @@ -30,12 +40,14 @@ module.exports = (grcBfxReq) => { const { presigned_url: url, language = 'en' - } = { ...data } - const normLang = LANGUAGES?.[language] ?? 'en' - const translate = getTranslator({ - language: normLang, - translations - }) + } = data ?? {} + const translate = getTranslator( + { i18next }, + { + lng: language, + ns: TRANSLATION_NAMESPACES.EMAIL + } + ) const subject = translate( configs.subject, 'template.subject' @@ -60,7 +72,7 @@ module.exports = (grcBfxReq) => { text, subject, button, - language: normLang + language: _getSendgridLng(language) } return grcBfxReq({ From e9ef8ea1780d08e012472566cf24bce032e3b241 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:47:43 +0300 Subject: [PATCH 20/26] Add translation namespaces --- workers/loc.api/i18next/translation.namespaces.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 workers/loc.api/i18next/translation.namespaces.js diff --git a/workers/loc.api/i18next/translation.namespaces.js b/workers/loc.api/i18next/translation.namespaces.js new file mode 100644 index 00000000..9932066f --- /dev/null +++ b/workers/loc.api/i18next/translation.namespaces.js @@ -0,0 +1,8 @@ +'use strict' + +const TRANSLATION_NAMESPACES = { + EMAIL: 'email', + PDF: 'pdf' +} + +module.exports = TRANSLATION_NAMESPACES From b488fe1a492a3f734b904cc0e5512abec2c163ee Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:48:39 +0300 Subject: [PATCH 21/26] Use common translation namespaces in i18next service --- workers/loc.api/i18next/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workers/loc.api/i18next/index.js b/workers/loc.api/i18next/index.js index f891aaac..6baaba7f 100644 --- a/workers/loc.api/i18next/index.js +++ b/workers/loc.api/i18next/index.js @@ -6,6 +6,7 @@ const path = require('path') const fs = require('fs') const FsMultilocationBackend = require('./fs.multilocation.backend') +const TRANSLATION_NAMESPACES = require('./translation.namespaces') let i18nextInstance = null @@ -22,7 +23,7 @@ module.exports = (params) => { const configs = merge( { fallbackLng: 'en', - ns: ['email', 'pdf'], + ns: Object.values(TRANSLATION_NAMESPACES), defaultNS: 'email', preload: [...transPaths.reduce((accum, transPath) => { const allFileNames = fs.readdirSync(transPath) From 584ac807e0ea7a7f51025a283a08a2f12d46a679 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:49:38 +0300 Subject: [PATCH 22/26] Rework PdfWriter service to use i18next api --- .../generate-report-file/pdf-writer/index.js | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/workers/loc.api/generate-report-file/pdf-writer/index.js b/workers/loc.api/generate-report-file/pdf-writer/index.js index a04beaa3..7b062ed1 100644 --- a/workers/loc.api/generate-report-file/pdf-writer/index.js +++ b/workers/loc.api/generate-report-file/pdf-writer/index.js @@ -4,8 +4,6 @@ const { Transform } = require('stream') const path = require('path') const fs = require('fs') const pug = require('pug') -const yaml = require('js-yaml') -const { merge } = require('lib-js-util-base') const getCompleteFileName = require('../../queue/helpers/get-complete-file-name') const getTranslator = require('../../helpers/get-translator') @@ -16,6 +14,9 @@ const { GrcPDFAvailabilityError } = require('../../errors') const TEMPLATE_FILE_NAMES = require('./template-file-names') +const TRANSLATION_NAMESPACES = require( + '../../i18next/translation.namespaces' +) const placeholderPattern = /\$\{[a-zA-Z0-9]+\}/g const pathToFonts = path.join(__dirname, 'templates/fonts') @@ -31,27 +32,27 @@ const { decorateInjectable } = require('../../di/utils') const depsTypes = (TYPES) => [ TYPES.ROOT_FOLDER_PATH, TYPES.HasGrcService, - TYPES.GrcBfxReq + TYPES.GrcBfxReq, + TYPES.I18next ] class PdfWriter { #fonts = this.#renderFontsTemplate(fontsTemplate, base64Fonts) - #translationsArr = [] - #translations = {} #templatePaths = new Map() #templates = new Map() constructor ( rootFolderPath, hasGrcService, - grcBfxReq + grcBfxReq, + i18next ) { this.rootFolderPath = rootFolderPath this.hasGrcService = hasGrcService this.grcBfxReq = grcBfxReq + this.i18next = i18next this.isElectronjsEnv = false - this.addTranslations() this.addTemplates() this.compileTemplate() } @@ -192,29 +193,13 @@ class PdfWriter { } #getTranslator (language) { - return getTranslator({ - language, - translations: this.#translations - }) - } - - addTranslations (trans = this.loadTranslations()) { - const translationsArr = Array.isArray(trans) - ? trans - : [trans] - - this.#translationsArr.push(...translationsArr) - this.#translations = merge({}, ...this.#translationsArr) - } - - loadTranslations ( - pathToTrans = path.join(__dirname, 'translations.yml') - ) { - const translations = yaml.load( - fs.readFileSync(pathToTrans, 'utf8') + return getTranslator( + { i18next: this.i18next }, + { + lng: language, + ns: TRANSLATION_NAMESPACES.PDF + } ) - - return translations } addTemplates (params) { @@ -285,17 +270,36 @@ class PdfWriter { } #getAvailableLanguages () { - const languages = Object.keys(this.#translations) + return this.i18next.options.preload + } - if (languages.every((lang) => lang !== 'en')) { - languages.push('en') - } + #getTemplateKey (templateFileName, language) { + const lng = this.#getTemplateLngKey(language) - return languages + return `${templateFileName}:${lng}` } - #getTemplateKey (templateFileName, language) { - return `${templateFileName}:${language}` + #getTemplateLngKey (language) { + const lngs = this.#getAvailableLanguages() + + if (lngs.some((lng) => lng === language)) { + return language + } + + const lng = lngs.find((lng) => ( + lng.startsWith(language) || + language.startsWith(lng) + )) + + if (lng) { + return lng + } + + const normalizedLng = language.replace(/-\S*/, '') + + return lngs.find( + (lng) => lng.startsWith(normalizedLng) + ) ?? language } #renderFontsTemplate ( From 7d31910f264dbdfb9218b8024b7b0c2bca79e47e Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:51:49 +0300 Subject: [PATCH 23/26] Remove redundant email translation yaml file --- .../queue/send-mail/translations/email.yml | 97 ------------------- 1 file changed, 97 deletions(-) delete mode 100644 workers/loc.api/queue/send-mail/translations/email.yml diff --git a/workers/loc.api/queue/send-mail/translations/email.yml b/workers/loc.api/queue/send-mail/translations/email.yml deleted file mode 100644 index 3880f74e..00000000 --- a/workers/loc.api/queue/send-mail/translations/email.yml +++ /dev/null @@ -1,97 +0,0 @@ -en: - template: - subject: Your report is ready - btnText: Download Report - fileName: File name - unauth: Your file could not be completed, please try again - readyForDownload: The report you request is ready for download - ifDidNotInitAction: If you did not initiate this action and you suspect that your account may be compromised, please - freezeAccount: freeze your account - contactSupport: and contact support - youCan: You can - download: download - pgpSignature: a PGP digital signature file - -ru: - template: - subject: Ваш отчет готов - btnText: Скачать Отчет - fileName: Имя файла - unauth: Ваш файл не может быть завершен, пожалуйста, попробуйте еще раз - readyForDownload: Запрашиваемый вами отчет готов к загрузке - ifDidNotInitAction: Если вы не инициировали это действие и подозреваете, что ваша учетная запись может быть взломана, пожалуйста, - freezeAccount: заблокируйте ее - contactSupport: и обратитесь в службу поддержки - youCan: Вы можете - download: скачать - pgpSignature: файл цифровой подписи PGP - -zh-CN: - template: - subject: 您的报告已备妥 - btnText: 下载报告档案 - fileName: 档案名称 - unauth: 档案未下载成功,请重试一次 - readyForDownload: 您请求的报告已可下载 - ifDidNotInitAction: 如果您未进行此操作并且怀疑您的帐户已遭盗用,请 - freezeAccount: 冻结您的帐户 - contactSupport: 以及联络客服人员 - youCan: 您可以 - download: 下载 - pgpSignature: PGP数位签名档 - -zh-TW: - template: - subject: 您的報告已備妥 - btnText: 下載报告檔案 - fileName: 檔案名稱 - unauth: 檔案未下載成功,請重試一次 - readyForDownload: 您請求的報告已可下載 - ifDidNotInitAction: 如果您未進行此操作並且懷疑您的帳戶已遭盜用,請 - freezeAccount: 凍結您的帳戶 - contactSupport: 以及聯絡客服人員 - youCan: 您可以 - download: 下載 - pgpSignature: PGP數位簽名檔案 - -es-EM: - template: - subject: Tu reporte esta listo - btnText: Descargar el Informe - fileName: Nombre del archivo - unauth: Tu archivo no se pudo completar, intente de nuevo por favor. - readyForDownload: Tu reporte esta listo para ser descargado - ifDidNotInitAction: Si no realizaste esta acción y sospechas que tu cuenta puede estar comprometida, por favor - freezeAccount: congela tu cuenta - contactSupport: y contacta soporte - youCan: Puedes - download: descargar - pgpSignature: un archivo con firma digital PGP - -tr: - template: - subject: Raporunuz hazır - btnText: Raporu İndir - fileName: Dosya adı - unauth: Dosyanız tamamlanamadı, lütfen tekrar deneyin - readyForDownload: İstediğiniz rapor indirilmeye hazır - ifDidNotInitAction: Bu işlemi siz başlatmadıysanız ve hesabınızın güvenliğinin ihlal edilmiş olabileceğinden şüpheleniyorsanız, lütfen - freezeAccount: hesabınızı dondurun - contactSupport: ve destek ekibiyle iletişime geçin - youCan: Yapabilirsiniz - download: indir - pgpSignature: bir PGP dijital imza dosyası - -pt-BR: - template: - subject: Seu relatório está pronto - btnText: Baixar Relatório - fileName: Nome do arquivo - unauth: Não foi possível concluir seu arquivo, tente novamente - readyForDownload: O relatório solicitado está pronto para download - ifDidNotInitAction: Se você não iniciou esta ação e suspeita que sua conta pode estar comprometida - freezeAccount: por favor, congele sua conta - contactSupport: e entre em contato com o suporte - youCan: Você pode - download: baixar - pgpSignature: um arquivo de assinatura digital PGP From ca3d2f2b5742d3e051a201ccd6f2ae9ced97fbdf Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:52:09 +0300 Subject: [PATCH 24/26] Remove redundant pdf translation yaml file --- .../pdf-writer/translations.yml | 104 ------------------ 1 file changed, 104 deletions(-) delete mode 100644 workers/loc.api/generate-report-file/pdf-writer/translations.yml diff --git a/workers/loc.api/generate-report-file/pdf-writer/translations.yml b/workers/loc.api/generate-report-file/pdf-writer/translations.yml deleted file mode 100644 index 6969e504..00000000 --- a/workers/loc.api/generate-report-file/pdf-writer/translations.yml +++ /dev/null @@ -1,104 +0,0 @@ -en: - template: - title: Report - statementDetails: Statement Details - statementDate: Statement date - snapshotAt: Snapshot at - period: Period - username: Username - email: Email - errorMessage: Your file could not be completed, please try again - reportGenAt: Report generated at - copyright: Copyright © 2013-2024 iFinex Inc. All rights reserved. - page: Page - from: from - -ru: - template: - title: Отчет - statementDetails: Детали ведомости - statementDate: Дата ведомости - snapshotAt: Снимок в - period: Период - username: Имя пользователя - email: Эл. почта - errorMessage: Ваш файл не может быть завершен, пожалуйста, попробуйте еще раз - reportGenAt: Отчет сгенерирован в - copyright: Авторское право © 2013-2024 iFinex Inc. Все права защищены. - page: Стр. - from: из - -zh-CN: - template: - title: 报告 - statementDetails: 结算明细 - statementDate: 结算日期 - snapshotAt: 快照时间 - period: 期间 - username: 用户名 - email: 电子邮箱 - errorMessage: 无法建立档案,请重试 - reportGenAt: 报告创建时间 - copyright: Copyright © 2013-2024 iFinex Inc. All rights reserved. - page: 页数 - from: 从 - -zh-TW: - template: - title: 報告 - statementDetails: 結算明細 - statementDate: 結算日期 - snapshotAt: 快照時間: - period: 期間 - username: 用戶名 - email: 電子郵箱 - errorMessage: 無法建立檔案,請重試 - reportGenAt: 報告建立時間: - copyright: Copyright © 2013-2024 iFinex Inc. All rights reserved. - page: 頁數 - from: 從 - -es-EM: - template: - title: Reporte - statementDetails: Detalles del Estado - statementDate: Fecha del Estado - snapshotAt: Captura al - period: Periodo - username: Nombre de Usuario - email: Correo - errorMessage: Tu archivo no pudo ser completado, por favor inténtalo de nuevo - reportGenAt: Reporte generado al - copyright: Copyright © 2013-2024 iFinex Inc. Todos los derechos reservados. - page: Página - from: de - -tr: - template: - title: Rapor - statementDetails: Beyanname Detayları - statementDate: Beyanname Tarihi - snapshotAt: 'Anlık Görüntü:' - period: Dönem - username: Kullanıcı Adı - email: Email - errorMessage: Dosyanız tamamlanamadı, lütfen tekrar deneyin - reportGenAt: 'Rapor şu tarihte oluşturuldu:' - copyright: Telif Hakkı © 2013-2024 iFinex Inc. Tüm hakları saklıdır. - page: Sayfa - from: İtibaren - -pt-BR: - template: - title: Informes - statementDetails: Detalhes da declaração - statementDate: Data da declaração - snapshotAt: Captura de - period: Período - username: Nome de Usuário - email: e-mail - errorMessage: Seu arquivo não pode ser completado, por favor tente novamente - reportGenAt: 'Informe gerado em ' - copyright: Copyright © 2013-2024 iFinex Inc. Todos os direitos reservados. - page: Página - from: de From 728a069646eb6aeae2fb4b965b2ca87830f09fd2 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:52:57 +0300 Subject: [PATCH 25/26] Remove redundant language file --- workers/loc.api/helpers/index.js | 4 +--- workers/loc.api/helpers/languages.js | 16 ---------------- 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 workers/loc.api/helpers/languages.js diff --git a/workers/loc.api/helpers/index.js b/workers/loc.api/helpers/index.js index 254743fe..5da352d6 100644 --- a/workers/loc.api/helpers/index.js +++ b/workers/loc.api/helpers/index.js @@ -68,7 +68,6 @@ const getDataFromApi = require('./get-data-from-api') const splitSymbolPairs = require('./split-symbol-pairs') const FOREX_SYMBS = require('./forex.symbs') const getTranslator = require('./get-translator') -const LANGUAGES = require('./languages') module.exports = { getREST, @@ -124,6 +123,5 @@ module.exports = { parsePositionsAuditId, splitSymbolPairs, FOREX_SYMBS, - getTranslator, - LANGUAGES + getTranslator } diff --git a/workers/loc.api/helpers/languages.js b/workers/loc.api/helpers/languages.js deleted file mode 100644 index 12013361..00000000 --- a/workers/loc.api/helpers/languages.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -module.exports = { - en: 'en', - 'en-US': 'en', - ru: 'ru', - 'zh-CN': 'zh-CN', - 'zh-TW': 'zh-TW', - tr: 'tr', - 'tr-TR': 'tr', - es: 'es-EM', - 'es-EM': 'es-EM', - pt: 'pt-BR', - 'pt-PT': 'pt-BR', - 'pt-BR': 'pt-BR' -} From 79f5af05614cfa03a6eb45145af6f88bc8dee8b2 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 19 Sep 2024 13:53:47 +0300 Subject: [PATCH 26/26] Remove redundant js-yaml dep --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 5234a6f7..db61a1bb 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "i18next": "23.15.1", "i18next-fs-backend": "2.3.2", "inversify": "6.0.1", - "js-yaml": "4.1.0", "lib-js-util-base": "git+https://github.com/bitfinexcom/lib-js-util-base.git", "lru": "3.1.0", "moment": "2.29.4",