From 3c9ee8b65ba0553e03f18f648855a0c3f55492ba Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 2 Jun 2025 22:25:38 +0200 Subject: [PATCH] Implement language code --- apps/api/src/app/auth/jwt.strategy.ts | 14 ++++++- .../api/src/app/endpoints/ai/ai.controller.ts | 10 +---- .../src/app/portfolio/portfolio.service.ts | 1 + .../app/subscription/subscription.service.ts | 2 +- apps/api/src/app/user/user.service.ts | 1 + apps/api/src/models/rule.ts | 9 +++++ .../fees/fee-ratio-initial-investment.ts | 39 +++++++++---------- .../access-table/access-table.component.ts | 3 +- .../admin-settings.component.ts | 10 ++--- apps/client/src/app/pages/i18n/i18n-page.html | 4 +- 10 files changed, 51 insertions(+), 42 deletions(-) diff --git a/apps/api/src/app/auth/jwt.strategy.ts b/apps/api/src/app/auth/jwt.strategy.ts index 7a3fb224b..3d466082a 100644 --- a/apps/api/src/app/auth/jwt.strategy.ts +++ b/apps/api/src/app/auth/jwt.strategy.ts @@ -1,7 +1,11 @@ import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; -import { HEADER_KEY_TIMEZONE } from '@ghostfolio/common/config'; +import { + DEFAULT_CURRENCY, + DEFAULT_LANGUAGE_CODE, + HEADER_KEY_TIMEZONE +} from '@ghostfolio/common/config'; import { hasRole } from '@ghostfolio/common/permissions'; import { HttpException, Injectable } from '@nestjs/common'; @@ -52,6 +56,14 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { }); } + if (!user.Settings.settings.baseCurrency) { + user.Settings.settings.baseCurrency = DEFAULT_CURRENCY; + } + + if (!user.Settings.settings.language) { + user.Settings.settings.language = DEFAULT_LANGUAGE_CODE; + } + return user; } else { throw new HttpException( diff --git a/apps/api/src/app/endpoints/ai/ai.controller.ts b/apps/api/src/app/endpoints/ai/ai.controller.ts index 980d5607c..8050c675b 100644 --- a/apps/api/src/app/endpoints/ai/ai.controller.ts +++ b/apps/api/src/app/endpoints/ai/ai.controller.ts @@ -1,10 +1,6 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { ApiService } from '@ghostfolio/api/services/api/api.service'; -import { - DEFAULT_CURRENCY, - DEFAULT_LANGUAGE_CODE -} from '@ghostfolio/common/config'; import { AiPromptResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import type { AiPromptMode, RequestWithUser } from '@ghostfolio/common/types'; @@ -53,10 +49,8 @@ export class AiController { filters, mode, impersonationId: undefined, - languageCode: - this.request.user.Settings.settings.language ?? DEFAULT_LANGUAGE_CODE, - userCurrency: - this.request.user.Settings.settings.baseCurrency ?? DEFAULT_CURRENCY, + languageCode: this.request.user.Settings.settings.language, + userCurrency: this.request.user.Settings.settings.baseCurrency, userId: this.request.user.id }); diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 18f923112..313721a17 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1332,6 +1332,7 @@ export class PortfolioService { [ new FeeRatioInitialInvestment( this.exchangeRateDataService, + userSettings.language, summary.committedFunds, summary.fees ) diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts index 063c40608..ee34ef52d 100644 --- a/apps/api/src/app/subscription/subscription.service.ts +++ b/apps/api/src/app/subscription/subscription.service.ts @@ -61,7 +61,7 @@ export class SubscriptionService { const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = { cancel_url: `${this.configurationService.get('ROOT_URL')}/${ - user.Settings?.settings?.language ?? DEFAULT_LANGUAGE_CODE + user.Settings.settings.language }/account`, client_reference_id: user.id, line_items: [ diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 2a48b2583..774f9746b 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -302,6 +302,7 @@ export class UserService { undefined ).getSettings(user.Settings.settings), FeeRatioInitialInvestment: new FeeRatioInitialInvestment( + undefined, undefined, undefined, undefined diff --git a/apps/api/src/models/rule.ts b/apps/api/src/models/rule.ts index 187527fbb..916cc9231 100644 --- a/apps/api/src/models/rule.ts +++ b/apps/api/src/models/rule.ts @@ -1,5 +1,6 @@ import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { groupBy } from '@ghostfolio/common/helper'; import { PortfolioPosition, @@ -14,19 +15,23 @@ import { RuleInterface } from './interfaces/rule.interface'; export abstract class Rule implements RuleInterface { private key: string; + private languageCode: string; private name: string; public constructor( protected exchangeRateDataService: ExchangeRateDataService, { key, + languageCode = DEFAULT_LANGUAGE_CODE, name }: { key: string; + languageCode?: string; // TODO: Make mandatory name: string; } ) { this.key = key; + this.languageCode = languageCode; this.name = name; } @@ -34,6 +39,10 @@ export abstract class Rule implements RuleInterface { return this.key; } + public getLanguageCode() { + return this.languageCode; + } + public getName() { return this.name; } 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 8c546133e..5968be198 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 @@ -2,7 +2,6 @@ import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.in import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { UserSettings } from '@ghostfolio/common/interfaces'; export class FeeRatioInitialInvestment extends Rule { @@ -12,10 +11,12 @@ export class FeeRatioInitialInvestment extends Rule { public constructor( protected exchangeRateDataService: ExchangeRateDataService, + languageCode: string, totalInvestment: number, fees: number ) { super(exchangeRateDataService, { + languageCode, key: FeeRatioInitialInvestment.name, name: 'Fee Ratio' }); @@ -30,32 +31,28 @@ export class FeeRatioInitialInvestment extends Rule { : 0; if (feeRatio > ruleSettings.thresholdMax) { - const evaluation = this.i18nService.getTranslation({ - id: 'rule.fee-ratio-initial-investment.exceed', - languageCode: DEFAULT_LANGUAGE_CODE, - placeholders: { - feeRatio: (ruleSettings.thresholdMax * 100).toFixed(2), - thresholdMax: (feeRatio * 100).toPrecision(3) - } - }); - return { - evaluation, + evaluation: this.i18nService.getTranslation({ + id: 'rule.feeRatioInitialInvestment.exceed', + languageCode: this.getLanguageCode(), + placeholders: { + feeRatio: (ruleSettings.thresholdMax * 100).toFixed(2), + thresholdMax: (feeRatio * 100).toPrecision(3) + } + }), value: false }; } - const evaluation = this.i18nService.getTranslation({ - id: 'rule.fee-ratio-initial-investment.not-exceed', - languageCode: DEFAULT_LANGUAGE_CODE, - placeholders: { - feeRatio: (feeRatio * 100).toPrecision(3), - thresholdMax: (ruleSettings.thresholdMax * 100).toFixed(2) - } - }); - return { - evaluation, + evaluation: this.i18nService.getTranslation({ + id: 'rule.feeRatioInitialInvestment.notExceed', + languageCode: this.getLanguageCode(), + placeholders: { + feeRatio: (feeRatio * 100).toPrecision(3), + thresholdMax: (ruleSettings.thresholdMax * 100).toFixed(2) + } + }), value: true }; } diff --git a/apps/client/src/app/components/access-table/access-table.component.ts b/apps/client/src/app/components/access-table/access-table.component.ts index e70b6684a..bcd5839e0 100644 --- a/apps/client/src/app/components/access-table/access-table.component.ts +++ b/apps/client/src/app/components/access-table/access-table.component.ts @@ -1,6 +1,5 @@ import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; -import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { Access, User } from '@ghostfolio/common/interfaces'; import { paths } from '@ghostfolio/common/paths'; @@ -54,7 +53,7 @@ export class AccessTableComponent implements OnChanges { } public getPublicUrl(aId: string): string { - const languageCode = this.user?.settings?.language ?? DEFAULT_LANGUAGE_CODE; + const languageCode = this.user.settings.language; return `${this.baseUrl}/${languageCode}/${paths.public}/${aId}`; } diff --git a/apps/client/src/app/components/admin-settings/admin-settings.component.ts b/apps/client/src/app/components/admin-settings/admin-settings.component.ts index 3ff8370cd..68db9eaac 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.component.ts +++ b/apps/client/src/app/components/admin-settings/admin-settings.component.ts @@ -3,10 +3,7 @@ import { NotificationService } from '@ghostfolio/client/core/notification/notifi import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { - DEFAULT_LANGUAGE_CODE, - PROPERTY_API_KEY_GHOSTFOLIO -} from '@ghostfolio/common/config'; +import { PROPERTY_API_KEY_GHOSTFOLIO } from '@ghostfolio/common/config'; import { getDateFormatString } from '@ghostfolio/common/helper'; import { DataProviderGhostfolioStatusResponse, @@ -70,11 +67,10 @@ export class AdminSettingsComponent implements OnDestroy, OnInit { this.user = state.user; this.defaultDateFormat = getDateFormatString( - this.user?.settings?.locale + this.user.settings.locale ); - const languageCode = - this.user?.settings?.language ?? DEFAULT_LANGUAGE_CODE; + const languageCode = this.user.settings.language; this.pricingUrl = `https://ghostfol.io/${languageCode}/${paths.pricing}`; diff --git a/apps/client/src/app/pages/i18n/i18n-page.html b/apps/client/src/app/pages/i18n/i18n-page.html index e6ac9691e..72d07b620 100644 --- a/apps/client/src/app/pages/i18n/i18n-page.html +++ b/apps/client/src/app/pages/i18n/i18n-page.html @@ -11,11 +11,11 @@ performance, portfolio, software, stock, trading, wealth, web3
  • My Account
  • -
  • +
  • The fees do exceed {thresholdMax}% of your initial investment ({feeRatio}%)
  • -
  • +
  • The fees do not exceed {thresholdMax}% of your initial investment ({feeRatio}%)