diff --git a/CHANGELOG.md b/CHANGELOG.md index d6377b5ed..f6e694dca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for the dividend timeline grouped by year - Added support for the investment timeline grouped by year +- Set up the language localization for Français (`fr`) +- Set up the language localization for Português (`pt`) + +### Changed + +- Improved the language localization for Dutch (`nl`) + +## 1.223.0 - 2023-01-01 + +### Added + +- Added a student discount to the pricing page +- Added a prefix to the codes of the coupon system + +### Changed + +- Optimized the page titles in the header for mobile +- Extended the asset profile details dialog in the admin control panel ## 1.222.0 - 2022-12-29 diff --git a/apps/api/src/app/frontend.middleware.ts b/apps/api/src/app/frontend.middleware.ts index 6cb8293de..eb9e5561c 100644 --- a/apps/api/src/app/frontend.middleware.ts +++ b/apps/api/src/app/frontend.middleware.ts @@ -14,8 +14,10 @@ export class FrontendMiddleware implements NestMiddleware { public indexHtmlDe = ''; public indexHtmlEn = ''; public indexHtmlEs = ''; + public indexHtmlFr = ''; public indexHtmlIt = ''; public indexHtmlNl = ''; + public indexHtmlPt = ''; public isProduction: boolean; public constructor( @@ -41,6 +43,10 @@ export class FrontendMiddleware implements NestMiddleware { this.getPathOfIndexHtmlFile('es'), 'utf8' ); + this.indexHtmlFr = fs.readFileSync( + this.getPathOfIndexHtmlFile('fr'), + 'utf8' + ); this.indexHtmlIt = fs.readFileSync( this.getPathOfIndexHtmlFile('it'), 'utf8' @@ -49,6 +55,10 @@ export class FrontendMiddleware implements NestMiddleware { this.getPathOfIndexHtmlFile('nl'), 'utf8' ); + this.indexHtmlPt = fs.readFileSync( + this.getPathOfIndexHtmlFile('pt'), + 'utf8' + ); } catch {} } @@ -104,6 +114,15 @@ export class FrontendMiddleware implements NestMiddleware { rootUrl: this.configurationService.get('ROOT_URL') }) ); + } else if (request.path === '/fr' || request.path.startsWith('/fr/')) { + response.send( + this.interpolate(this.indexHtmlFr, { + featureGraphicPath, + languageCode: 'fr', + path: request.path, + rootUrl: this.configurationService.get('ROOT_URL') + }) + ); } else if (request.path === '/it' || request.path.startsWith('/it/')) { response.send( this.interpolate(this.indexHtmlIt, { @@ -126,6 +145,15 @@ export class FrontendMiddleware implements NestMiddleware { rootUrl: this.configurationService.get('ROOT_URL') }) ); + } else if (request.path === '/pt' || request.path.startsWith('/pt/')) { + response.send( + this.interpolate(this.indexHtmlPt, { + featureGraphicPath, + languageCode: 'pt', + path: request.path, + rootUrl: this.configurationService.get('ROOT_URL') + }) + ); } else { response.send( this.interpolate(this.indexHtmlEn, { diff --git a/apps/client/project.json b/apps/client/project.json index 0fd3d577c..f58bb3ae5 100644 --- a/apps/client/project.json +++ b/apps/client/project.json @@ -89,6 +89,10 @@ "baseHref": "/es/", "localize": ["es"] }, + "development-fr": { + "baseHref": "/fr/", + "localize": ["fr"] + }, "development-it": { "baseHref": "/it/", "localize": ["it"] @@ -97,6 +101,10 @@ "baseHref": "/nl/", "localize": ["nl"] }, + "development-pt": { + "baseHref": "/pt/", + "localize": ["pt"] + }, "production": { "fileReplacements": [ { @@ -144,12 +152,18 @@ "development-es": { "browserTarget": "client:build:development-es" }, + "development-fr": { + "browserTarget": "client:build:development-fr" + }, "development-it": { "browserTarget": "client:build:development-it" }, "development-nl": { "browserTarget": "client:build:development-nl" }, + "development-pt": { + "browserTarget": "client:build:development-pt" + }, "production": { "browserTarget": "client:build:production" } @@ -164,8 +178,10 @@ "targetFiles": [ "messages.de.xlf", "messages.es.xlf", + "messages.fr.xlf", "messages.it.xlf", - "messages.nl.xlf" + "messages.nl.xlf", + "messages.pt.xlf" ] } }, @@ -194,6 +210,10 @@ "baseHref": "/es/", "translation": "apps/client/src/locales/messages.es.xlf" }, + "fr": { + "baseHref": "/fr/", + "translation": "apps/client/src/locales/messages.fr.xlf" + }, "it": { "baseHref": "/it/", "translation": "apps/client/src/locales/messages.it.xlf" @@ -201,6 +221,10 @@ "nl": { "baseHref": "/nl/", "translation": "apps/client/src/locales/messages.nl.xlf" + }, + "pt": { + "baseHref": "/pt/", + "translation": "apps/client/src/locales/messages.pt.xlf" } }, "sourceLocale": "en" diff --git a/apps/client/src/app/app.component.html b/apps/client/src/app/app.component.html index 4525930cf..cdf0ba6b5 100644 --- a/apps/client/src/app/app.component.html +++ b/apps/client/src/app/app.component.html @@ -3,6 +3,7 @@ class="position-fixed w-100" [currentRoute]="currentRoute" [info]="info" + [pageTitle]="pageTitle" [user]="user" (signOut)="onSignOut()" > diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index 957dc8f5a..a412a8054 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -5,7 +5,13 @@ import { OnDestroy, OnInit } from '@angular/core'; -import { NavigationEnd, PRIMARY_OUTLET, Router } from '@angular/router'; +import { Title } from '@angular/platform-browser'; +import { + ActivatedRoute, + NavigationEnd, + PRIMARY_OUTLET, + Router +} from '@angular/router'; import { primaryColorHex, secondaryColorHex, @@ -36,6 +42,7 @@ export class AppComponent implements OnDestroy, OnInit { public currentYear = new Date().getFullYear(); public deviceType: string; public info: InfoItem; + public pageTitle: string; public user: User; public version = environment.version; @@ -47,6 +54,7 @@ export class AppComponent implements OnDestroy, OnInit { private deviceService: DeviceDetectorService, private materialCssVarsService: MaterialCssVarsService, private router: Router, + private title: Title, private tokenStorageService: TokenStorageService, private userService: UserService ) { @@ -66,6 +74,19 @@ export class AppComponent implements OnDestroy, OnInit { this.currentRoute = urlSegments[0].path; this.info = this.dataService.fetchInfo(); + + if (this.deviceType === 'mobile') { + setTimeout(() => { + const index = this.title.getTitle().indexOf('–'); + const title = + index === -1 + ? '' + : this.title.getTitle().substring(0, index).trim(); + this.pageTitle = title.length <= 15 ? title : 'Ghostfolio'; + + this.changeDetectorRef.markForCheck(); + }); + } }); this.userService.stateChanged diff --git a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts b/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts index b448e4b5a..767891a2a 100644 --- a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts +++ b/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts @@ -20,7 +20,6 @@ import { addDays, format, isBefore, - isDate, isSameDay, isToday, isValid, @@ -31,6 +30,7 @@ import { last } from 'lodash'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject, takeUntil } from 'rxjs'; +import { MarketDataDetailDialogParams } from './market-data-detail-dialog/interfaces/interfaces'; import { MarketDataDetailDialog } from './market-data-detail-dialog/market-data-detail-dialog.component'; @Component({ @@ -40,6 +40,7 @@ import { MarketDataDetailDialog } from './market-data-detail-dialog/market-data- templateUrl: './admin-market-data-detail.component.html' }) export class AdminMarketDataDetailComponent implements OnChanges, OnInit { + @Input() currency: string; @Input() dataSource: DataSource; @Input() dateOfFirstActivity: string; @Input() locale = getLocale(); @@ -161,9 +162,10 @@ export class AdminMarketDataDetailComponent implements OnChanges, OnInit { } const dialogRef = this.dialog.open(MarketDataDetailDialog, { - data: { + data: { date, marketPrice, + currency: this.currency, dataSource: this.dataSource, symbol: this.symbol, user: this.user diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts index 81360878b..23203a39e 100644 --- a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts @@ -2,6 +2,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { DataSource } from '@prisma/client'; export interface MarketDataDetailDialogParams { + currency: string; dataSource: DataSource; date: Date; marketPrice: number; diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html index 84dae467b..230133de7 100644 --- a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html +++ b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html @@ -1,5 +1,5 @@
-

Details for {{ data.symbol }}

+

Details for {{ data.symbol }}

@@ -30,6 +30,7 @@ type="number" [(ngModel)]="data.marketPrice" /> + {{ data.currency }}