From 3bcb19af143618cc2ba6b51e1cdece308222c700 Mon Sep 17 00:00:00 2001 From: Tobias Kugel <78074722+tobikugel@users.noreply.github.com> Date: Sat, 7 Jun 2025 11:52:48 -0300 Subject: [PATCH] Feature/migrate i18n service to injectable service (#4829) * Migrate i18n service to injectable service * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/endpoints/ai/ai.module.ts | 2 ++ .../src/app/endpoints/benchmarks/benchmarks.module.ts | 2 ++ apps/api/src/app/endpoints/public/public.module.ts | 2 ++ apps/api/src/app/portfolio/portfolio.module.ts | 2 ++ apps/api/src/app/portfolio/portfolio.service.ts | 4 ++++ apps/api/src/app/user/user.module.ts | 2 ++ apps/api/src/app/user/user.service.ts | 5 +++-- .../models/rules/emergency-fund/emergency-fund-setup.ts | 2 +- .../models/rules/fees/fee-ratio-initial-investment.ts | 2 +- apps/api/src/services/i18n/i18n.module.ts | 9 +++++++++ apps/api/src/services/i18n/i18n.service.ts | 3 ++- 12 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 apps/api/src/services/i18n/i18n.module.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c09a4d16d..51597cd60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Migrated the `i18n` service to use `@Injectable()` - Improved the language localization for German (`de`) - Upgraded `nestjs` from version `11.1.0` to `11.1.3` diff --git a/apps/api/src/app/endpoints/ai/ai.module.ts b/apps/api/src/app/endpoints/ai/ai.module.ts index b6f9941ad..42480efd7 100644 --- a/apps/api/src/app/endpoints/ai/ai.module.ts +++ b/apps/api/src/app/endpoints/ai/ai.module.ts @@ -12,6 +12,7 @@ import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.mo import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { I18nModule } from '@ghostfolio/api/services/i18n/i18n.module'; import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; @@ -32,6 +33,7 @@ import { AiService } from './ai.service'; ConfigurationModule, DataProviderModule, ExchangeRateDataModule, + I18nModule, ImpersonationModule, MarketDataModule, OrderModule, diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts index a0f443621..8bdf79035 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts @@ -15,6 +15,7 @@ import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.s import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { I18nModule } from '@ghostfolio/api/services/i18n/i18n.module'; import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; @@ -35,6 +36,7 @@ import { BenchmarksService } from './benchmarks.service'; ConfigurationModule, DataProviderModule, ExchangeRateDataModule, + I18nModule, ImpersonationModule, MarketDataModule, OrderModule, diff --git a/apps/api/src/app/endpoints/public/public.module.ts b/apps/api/src/app/endpoints/public/public.module.ts index cf4fd3d18..19e281dde 100644 --- a/apps/api/src/app/endpoints/public/public.module.ts +++ b/apps/api/src/app/endpoints/public/public.module.ts @@ -12,6 +12,7 @@ import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { I18nModule } from '@ghostfolio/api/services/i18n/i18n.module'; import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; @@ -29,6 +30,7 @@ import { PublicController } from './public.controller'; BenchmarkModule, DataProviderModule, ExchangeRateDataModule, + I18nModule, ImpersonationModule, MarketDataModule, OrderModule, diff --git a/apps/api/src/app/portfolio/portfolio.module.ts b/apps/api/src/app/portfolio/portfolio.module.ts index 0f64b2f6a..6dd5811a3 100644 --- a/apps/api/src/app/portfolio/portfolio.module.ts +++ b/apps/api/src/app/portfolio/portfolio.module.ts @@ -13,6 +13,7 @@ import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.mo import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { I18nModule } from '@ghostfolio/api/services/i18n/i18n.module'; import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; @@ -39,6 +40,7 @@ import { RulesService } from './rules.service'; DataGatheringModule, DataProviderModule, ExchangeRateDataModule, + I18nModule, ImpersonationModule, MarketDataModule, OrderModule, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index eb6abc224..a43499e5b 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -23,6 +23,7 @@ import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/ru import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { @@ -105,6 +106,7 @@ export class PortfolioService { private readonly calculatorFactory: PortfolioCalculatorFactory, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly i18nService: I18nService, private readonly impersonationService: ImpersonationService, private readonly orderService: OrderService, @Inject(REQUEST) private readonly request: RequestWithUser, @@ -1319,6 +1321,7 @@ export class PortfolioService { [ new EmergencyFundSetup( this.exchangeRateDataService, + this.i18nService, userSettings.language, this.getTotalEmergencyFund({ userSettings, @@ -1333,6 +1336,7 @@ export class PortfolioService { [ new FeeRatioInitialInvestment( this.exchangeRateDataService, + this.i18nService, userSettings.language, summary.committedFunds, summary.fees diff --git a/apps/api/src/app/user/user.module.ts b/apps/api/src/app/user/user.module.ts index 063cfef82..8a21b0a55 100644 --- a/apps/api/src/app/user/user.module.ts +++ b/apps/api/src/app/user/user.module.ts @@ -1,6 +1,7 @@ import { OrderModule } from '@ghostfolio/api/app/order/order.module'; import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { I18nModule } from '@ghostfolio/api/services/i18n/i18n.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; @@ -16,6 +17,7 @@ import { UserService } from './user.service'; exports: [UserService], imports: [ ConfigurationModule, + I18nModule, JwtModule.register({ secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '30 days' } diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 21790182f..4b04647b5 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -52,11 +52,10 @@ import { sortBy, without } from 'lodash'; @Injectable() export class UserService { - private i18nService = new I18nService(); - public constructor( private readonly configurationService: ConfigurationService, private readonly eventEmitter: EventEmitter2, + private readonly i18nService: I18nService, private readonly orderService: OrderService, private readonly prismaService: PrismaService, private readonly propertyService: PropertyService, @@ -298,6 +297,7 @@ export class UserService { undefined ).getSettings(user.Settings.settings), EmergencyFundSetup: new EmergencyFundSetup( + undefined, undefined, undefined, undefined @@ -306,6 +306,7 @@ export class UserService { undefined, undefined, undefined, + undefined, undefined ).getSettings(user.Settings.settings), RegionalMarketClusterRiskAsiaPacific: diff --git a/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts b/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts index a91e68425..67c0f297e 100644 --- a/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts +++ b/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts @@ -6,10 +6,10 @@ import { UserSettings } from '@ghostfolio/common/interfaces'; export class EmergencyFundSetup extends Rule { private emergencyFund: number; - private i18nService = new I18nService(); public constructor( protected exchangeRateDataService: ExchangeRateDataService, + private i18nService: I18nService, languageCode: string, emergencyFund: number ) { diff --git a/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts b/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts index 4298eed44..89fe32dbf 100644 --- a/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts +++ b/apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts @@ -6,11 +6,11 @@ import { UserSettings } from '@ghostfolio/common/interfaces'; export class FeeRatioInitialInvestment extends Rule { private fees: number; - private i18nService = new I18nService(); private totalInvestment: number; public constructor( protected exchangeRateDataService: ExchangeRateDataService, + private i18nService: I18nService, languageCode: string, totalInvestment: number, fees: number diff --git a/apps/api/src/services/i18n/i18n.module.ts b/apps/api/src/services/i18n/i18n.module.ts new file mode 100644 index 000000000..68211de40 --- /dev/null +++ b/apps/api/src/services/i18n/i18n.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { I18nService } from './i18n.service'; + +@Module({ + exports: [I18nService], + providers: [I18nService] +}) +export class I18nModule {} diff --git a/apps/api/src/services/i18n/i18n.service.ts b/apps/api/src/services/i18n/i18n.service.ts index 533abf2e2..a0389ab36 100644 --- a/apps/api/src/services/i18n/i18n.service.ts +++ b/apps/api/src/services/i18n/i18n.service.ts @@ -1,10 +1,11 @@ import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; -import { Logger } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import * as cheerio from 'cheerio'; import { readFileSync, readdirSync } from 'fs'; import { join } from 'path'; +@Injectable() export class I18nService { private localesPath = join(__dirname, 'assets', 'locales'); private translations: { [locale: string]: cheerio.CheerioAPI } = {};