diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd17aead..f4c28f319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the language localization for German (`de`) + +## 1.181.2 - 21.08.2022 + +### Added + +- Added a language selector to the account page +- Added support for translated labels in the value component + +### Changed + +- Integrated the commands `database:setup` and `database:migrate` into the container start + +### Fixed + +- Fixed a division by zero error in the benchmarks calculation + +### Todo + +- Apply manual data migration (`yarn database:migrate`) is not needed anymore + +## 1.180.1 - 18.08.2022 + +### Added + +- Set up `ng-extract-i18n-merge` to improve the i18n extraction and merge workflow +- Set up language localization for German (`de`) +- Resolved the feature graphic of the blog post + +### Changed + +- Tagged template literal strings in components for localization with `$localize` + +### Fixed + +- Fixed the license component in the about page +- Fixed the links to the blog posts + +## 1.179.5 - 15.08.2022 + +### Added + +- Set up i18n support +- Added a blog post: _500 Stars on GitHub_ + +### Changed + +- Reduced the maximum width of the performance chart on the home page + +## 1.178.0 - 09.08.2022 + +### Added + +- Added `url` to the symbol profile overrides model for manual adjustments +- Added default values for `countries` and `sectors` of the symbol profile overrides model + +### Changed + - Simplified the initialization of the exchange rate service +- Improved the orders query for `assetClass` with symbol profile overrides +- Improved the styling of the benchmarks in the markets overview + +### Todo + +- Apply data migration (`yarn database:migrate`) ## 1.177.0 - 04.08.2022 diff --git a/Dockerfile b/Dockerfile index 9a965ddf0..a72236805 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,4 +58,4 @@ RUN apt update && apt install -y \ COPY --from=builder /ghostfolio/dist/apps /ghostfolio/apps WORKDIR /ghostfolio/apps/api EXPOSE 3333 -CMD [ "node", "main" ] +CMD [ "yarn", "start:prod" ] diff --git a/README.md b/README.md index 712776bb9..2ae2808cf 100644 --- a/README.md +++ b/README.md @@ -114,14 +114,6 @@ Run the following command to start the Docker images from [Docker Hub](https://h docker-compose --env-file ./.env -f docker/docker-compose.yml up -d ``` -##### Setup Database - -Run the following command to setup the database once Ghostfolio is running: - -```bash -docker-compose --env-file ./.env -f docker/docker-compose.yml exec ghostfolio yarn database:setup -``` - #### b. Build and run environment Run the following commands to build and start the Docker images: @@ -131,14 +123,6 @@ docker-compose --env-file ./.env -f docker/docker-compose.build.yml build docker-compose --env-file ./.env -f docker/docker-compose.build.yml up -d ``` -##### Setup Database - -Run the following command to setup the database once Ghostfolio is running: - -```bash -docker-compose --env-file ./.env -f docker/docker-compose.build.yml exec ghostfolio yarn database:setup -``` - #### Fetch Historical Data Open http://localhost:3333 in your browser and accomplish these steps: @@ -150,8 +134,8 @@ Open http://localhost:3333 in your browser and accomplish these steps: #### Upgrade Version 1. Increase the version of the `ghostfolio/ghostfolio` Docker image in `docker/docker-compose.yml` -1. Run the following command to start the new Docker image: `docker-compose --env-file ./.env -f docker/docker-compose.yml up -d` -1. Then, run the following command to keep your database schema in sync: `docker-compose --env-file ./.env -f docker/docker-compose.yml exec ghostfolio yarn database:migrate` +1. Run the following command to start the new Docker image: `docker-compose --env-file ./.env -f docker/docker-compose.yml up -d` + At each start, the container will automatically apply the database schema migrations if needed. ### Run with _Unraid_ (Community) diff --git a/angular.json b/angular.json index bd60b92ee..8f6f6e3de 100644 --- a/angular.json +++ b/angular.json @@ -77,41 +77,45 @@ "polyfills": "apps/client/src/polyfills.ts", "tsConfig": "apps/client/tsconfig.app.json", "assets": [ - "apps/client/src/assets", { "glob": "assetlinks.json", "input": "apps/client/src/assets", - "output": "./.well-known" + "output": "./../.well-known" }, { "glob": "CHANGELOG.md", "input": "", - "output": "./assets" + "output": "./../assets" }, { "glob": "LICENSE", "input": "", - "output": "./assets" + "output": "./../assets" }, { "glob": "robots.txt", "input": "apps/client/src/assets", - "output": "./" + "output": "./../" }, { "glob": "sitemap.xml", "input": "apps/client/src/assets", - "output": "./" + "output": "./../" }, { "glob": "**/*", "input": "node_modules/ionicons/dist/ionicons", - "output": "./ionicons" + "output": "./../ionicons" }, { "glob": "**/*.js", "input": "node_modules/ionicons/dist/", - "output": "./" + "output": "./../" + }, + { + "glob": "**/*", + "input": "apps/client/src/assets", + "output": "./../assets/" } ], "styles": ["apps/client/src/styles.scss"], @@ -124,6 +128,14 @@ "namedChunks": true }, "configurations": { + "development-de": { + "baseHref": "/de/", + "localize": ["de"] + }, + "development-en": { + "baseHref": "/en/", + "localize": ["en"] + }, "production": { "fileReplacements": [ { @@ -162,15 +174,24 @@ "proxyConfig": "apps/client/proxy.conf.json" }, "configurations": { + "development-de": { + "browserTarget": "client:build:development-de" + }, + "development-en": { + "browserTarget": "client:build:development-en" + }, "production": { "browserTarget": "client:build:production" } } }, "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", + "builder": "ng-extract-i18n-merge:ng-extract-i18n-merge", "options": { - "browserTarget": "client:build" + "browserTarget": "client:build", + "includeContext": true, + "outputPath": "src/locales", + "targetFiles": ["messages.de.xlf"] } }, "lint": { @@ -188,6 +209,15 @@ "outputs": ["coverage/apps/client"] } }, + "i18n": { + "locales": { + "de": { + "baseHref": "/de/", + "translation": "apps/client/src/locales/messages.de.xlf" + } + }, + "sourceLocale": "en" + }, "tags": [] }, "client-e2e": { diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index f1fc27976..e41b60b0e 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -10,7 +10,7 @@ import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-d import { PrismaModule } from '@ghostfolio/api/services/prisma.module'; import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module'; import { BullModule } from '@nestjs/bull'; -import { Module } from '@nestjs/common'; +import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; import { ServeStaticModule } from '@nestjs/serve-static'; @@ -23,6 +23,7 @@ import { AuthModule } from './auth/auth.module'; import { BenchmarkModule } from './benchmark/benchmark.module'; import { CacheModule } from './cache/cache.module'; import { ExportModule } from './export/export.module'; +import { FrontendMiddleware } from './frontend.middleware'; import { ImportModule } from './import/import.module'; import { InfoModule } from './info/info.module'; import { OrderModule } from './order/order.module'; @@ -82,4 +83,10 @@ import { UserModule } from './user/user.module'; controllers: [AppController], providers: [CronService] }) -export class AppModule {} +export class AppModule { + configure(consumer: MiddlewareConsumer) { + consumer + .apply(FrontendMiddleware) + .forRoutes({ path: '*', method: RequestMethod.ALL }); + } +} diff --git a/apps/api/src/app/auth/auth.controller.ts b/apps/api/src/app/auth/auth.controller.ts index 9cb6f8132..749f6f037 100644 --- a/apps/api/src/app/auth/auth.controller.ts +++ b/apps/api/src/app/auth/auth.controller.ts @@ -1,5 +1,6 @@ import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { OAuthResponse } from '@ghostfolio/common/interfaces'; import { Body, @@ -62,9 +63,17 @@ export class AuthController { const jwt: string = req.user.jwt; if (jwt) { - res.redirect(`${this.configurationService.get('ROOT_URL')}/auth/${jwt}`); + res.redirect( + `${this.configurationService.get( + 'ROOT_URL' + )}/${DEFAULT_LANGUAGE_CODE}/auth/${jwt}` + ); } else { - res.redirect(`${this.configurationService.get('ROOT_URL')}/auth`); + res.redirect( + `${this.configurationService.get( + 'ROOT_URL' + )}/${DEFAULT_LANGUAGE_CODE}/auth` + ); } } diff --git a/apps/api/src/app/benchmark/benchmark.service.ts b/apps/api/src/app/benchmark/benchmark.service.ts index f7a10d8e5..2e8ee61d4 100644 --- a/apps/api/src/app/benchmark/benchmark.service.ts +++ b/apps/api/src/app/benchmark/benchmark.service.ts @@ -48,9 +48,13 @@ export class BenchmarkService { benchmarks = allTimeHighs.map((allTimeHigh, index) => { const { marketPrice } = quotes[benchmarkAssets[index].symbol]; - const performancePercentFromAllTimeHigh = new Big(marketPrice) - .div(allTimeHigh) - .minus(1); + let performancePercentFromAllTimeHigh = new Big(0); + + if (allTimeHigh) { + performancePercentFromAllTimeHigh = new Big(marketPrice) + .div(allTimeHigh) + .minus(1); + } return { marketCondition: this.getMarketCondition( diff --git a/apps/api/src/app/frontend.middleware.ts b/apps/api/src/app/frontend.middleware.ts new file mode 100644 index 000000000..c45a35749 --- /dev/null +++ b/apps/api/src/app/frontend.middleware.ts @@ -0,0 +1,81 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; +import { Injectable, NestMiddleware } from '@nestjs/common'; +import { NextFunction, Request, Response } from 'express'; + +@Injectable() +export class FrontendMiddleware implements NestMiddleware { + public indexHtmlDe = fs.readFileSync( + this.getPathOfIndexHtmlFile('de'), + 'utf8' + ); + public indexHtmlEn = fs.readFileSync( + this.getPathOfIndexHtmlFile(DEFAULT_LANGUAGE_CODE), + 'utf8' + ); + + public constructor( + private readonly configurationService: ConfigurationService + ) {} + + public use(req: Request, res: Response, next: NextFunction) { + let featureGraphicPath = 'assets/cover.png'; + + if ( + req.path === '/en/blog/2022/08/500-stars-on-github' || + req.path === '/en/blog/2022/08/500-stars-on-github/' + ) { + featureGraphicPath = 'assets/images/blog/500-stars-on-github.jpg'; + } + + if (req.path.startsWith('/api/') || this.isFileRequest(req.url)) { + // Skip + next(); + } else if (req.path === '/de' || req.path.startsWith('/de/')) { + res.send( + this.interpolate(this.indexHtmlDe, { + featureGraphicPath, + languageCode: 'de', + path: req.path, + rootUrl: this.configurationService.get('ROOT_URL') + }) + ); + } else { + res.send( + this.interpolate(this.indexHtmlEn, { + featureGraphicPath, + languageCode: DEFAULT_LANGUAGE_CODE, + path: req.path, + rootUrl: this.configurationService.get('ROOT_URL') + }) + ); + } + } + + private getPathOfIndexHtmlFile(aLocale: string) { + return path.join(__dirname, '..', 'client', aLocale, 'index.html'); + } + + private interpolate(template: string, context: any) { + return template.replace(/[$]{([^}]+)}/g, (_, objectPath) => { + const properties = objectPath.split('.'); + return properties.reduce( + (previous, current) => previous?.[current], + context + ); + }); + } + + private isFileRequest(filename: string) { + if (filename === '/assets/LICENSE') { + return true; + } else if (filename.includes('auth/ey')) { + return false; + } + + return filename.split('.').pop() !== filename; + } +} diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 6b12bd723..bf549200e 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -230,9 +230,10 @@ export class OrderService { }) }, { - SymbolProfileOverrides: { - is: null - } + OR: [ + { SymbolProfileOverrides: { is: null } }, + { SymbolProfileOverrides: { assetClass: null } } + ] } ] }, diff --git a/apps/api/src/app/subscription/subscription.controller.ts b/apps/api/src/app/subscription/subscription.controller.ts index aabc46d24..70317fe76 100644 --- a/apps/api/src/app/subscription/subscription.controller.ts +++ b/apps/api/src/app/subscription/subscription.controller.ts @@ -1,6 +1,9 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; -import { PROPERTY_COUPONS } from '@ghostfolio/common/config'; +import { + DEFAULT_LANGUAGE_CODE, + PROPERTY_COUPONS +} from '@ghostfolio/common/config'; import { Coupon } from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { @@ -93,7 +96,11 @@ export class SubscriptionController { 'SubscriptionController' ); - res.redirect(`${this.configurationService.get('ROOT_URL')}/account`); + res.redirect( + `${this.configurationService.get( + 'ROOT_URL' + )}/${DEFAULT_LANGUAGE_CODE}/account` + ); } @Post('stripe/checkout-session') diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts index 5a4f75c20..fa061f369 100644 --- a/apps/api/src/app/subscription/subscription.service.ts +++ b/apps/api/src/app/subscription/subscription.service.ts @@ -1,5 +1,6 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { SubscriptionType } from '@ghostfolio/common/types/subscription.type'; import { Injectable, Logger } from '@nestjs/common'; import { Subscription } from '@prisma/client'; @@ -33,7 +34,9 @@ export class SubscriptionService { userId: string; }) { const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = { - cancel_url: `${this.configurationService.get('ROOT_URL')}/account`, + cancel_url: `${this.configurationService.get( + 'ROOT_URL' + )}/${DEFAULT_LANGUAGE_CODE}/account`, client_reference_id: userId, line_items: [ { diff --git a/apps/api/src/app/user/update-user-setting.dto.ts b/apps/api/src/app/user/update-user-setting.dto.ts index b97dd287e..f458294f8 100644 --- a/apps/api/src/app/user/update-user-setting.dto.ts +++ b/apps/api/src/app/user/update-user-setting.dto.ts @@ -9,6 +9,10 @@ export class UpdateUserSettingDto { @IsOptional() isRestrictedView?: boolean; + @IsString() + @IsOptional() + language?: string; + @IsString() @IsOptional() locale?: string; diff --git a/apps/api/src/services/symbol-profile.service.ts b/apps/api/src/services/symbol-profile.service.ts index c91da6d61..1c8da554c 100644 --- a/apps/api/src/services/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile.service.ts @@ -115,9 +115,16 @@ export class SymbolProfileService { } item.name = item.SymbolProfileOverrides?.name ?? item.name; - item.sectors = - (item.SymbolProfileOverrides.sectors as unknown as Sector[]) ?? - item.sectors; + + if ( + (item.SymbolProfileOverrides.sectors as unknown as Sector[])?.length > + 0 + ) { + item.sectors = item.SymbolProfileOverrides + .sectors as unknown as Sector[]; + } + + item.url = item.SymbolProfileOverrides?.url ?? item.url; delete item.SymbolProfileOverrides; } diff --git a/apps/client/src/app/app-routing.module.ts b/apps/client/src/app/app-routing.module.ts index 558a3360d..da506e673 100644 --- a/apps/client/src/app/app-routing.module.ts +++ b/apps/client/src/app/app-routing.module.ts @@ -54,45 +54,52 @@ const routes: Routes = [ import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule) }, { - path: 'de/blog/2021/07/hallo-ghostfolio', + path: 'blog/2021/07/hallo-ghostfolio', loadChildren: () => import( './pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.module' ).then((m) => m.HalloGhostfolioPageModule) }, { - path: 'demo', - loadChildren: () => - import('./pages/demo/demo-page.module').then((m) => m.DemoPageModule) - }, - { - path: 'en/blog/2021/07/hello-ghostfolio', + path: 'blog/2021/07/hello-ghostfolio', loadChildren: () => import( './pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.module' ).then((m) => m.HelloGhostfolioPageModule) }, { - path: 'en/blog/2022/01/ghostfolio-first-months-in-open-source', + path: 'blog/2022/01/ghostfolio-first-months-in-open-source', loadChildren: () => import( './pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.module' ).then((m) => m.FirstMonthsInOpenSourcePageModule) }, { - path: 'en/blog/2022/07/ghostfolio-meets-internet-identity', + path: 'blog/2022/07/ghostfolio-meets-internet-identity', loadChildren: () => import( './pages/blog/2022/07/ghostfolio-meets-internet-identity/ghostfolio-meets-internet-identity-page.module' ).then((m) => m.GhostfolioMeetsInternetIdentityPageModule) }, { - path: 'en/blog/2022/07/how-do-i-get-my-finances-in-order', + path: 'blog/2022/07/how-do-i-get-my-finances-in-order', loadChildren: () => import( './pages/blog/2022/07/how-do-i-get-my-finances-in-order/how-do-i-get-my-finances-in-order-page.module' ).then((m) => m.HowDoIGetMyFinancesInOrderPageModule) }, + { + path: 'blog/2022/08/500-stars-on-github', + loadChildren: () => + import( + './pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.module' + ).then((m) => m.FiveHundredStarsOnGitHubPageModule) + }, + { + path: 'demo', + loadChildren: () => + import('./pages/demo/demo-page.module').then((m) => m.DemoPageModule) + }, { path: 'faq', loadChildren: () => diff --git a/apps/client/src/app/app.component.html b/apps/client/src/app/app.component.html index 04d3d01ca..4525930cf 100644 --- a/apps/client/src/app/app.component.html +++ b/apps/client/src/app/app.component.html @@ -24,8 +24,8 @@ class="cursor-pointer d-inline-block info-message px-3 py-2" (click)="onCreateAccount()" > - You are using the Live Demo. - Create Account + You are using the Live Demo. + Create Account
- {{ baseUrl }}/p/{{ element.id }}{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }} @@ -41,8 +43,8 @@ - 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 210995c53..de26e5938 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 @@ -8,6 +8,7 @@ import { Output } from '@angular/core'; import { MatTableDataSource } from '@angular/material/table'; +import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { Access } from '@ghostfolio/common/interfaces'; @Component({ @@ -24,6 +25,7 @@ export class AccessTableComponent implements OnChanges, OnInit { public baseUrl = window.location.origin; public dataSource: MatTableDataSource; + public defaultLanguageCode = DEFAULT_LANGUAGE_CODE; public displayedColumns = []; public constructor() {} @@ -44,7 +46,7 @@ export class AccessTableComponent implements OnChanges, OnInit { public onDeleteAccess(aId: string) { const confirmation = confirm( - 'Do you really want to revoke this granted access?' + $localize`Do you really want to revoke this granted access?` ); if (confirmation) { diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html index 7bccfee87..edf111ffc 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html @@ -21,18 +21,10 @@
- + Account Type
- + Platform
diff --git a/apps/client/src/app/components/accounts-table/accounts-table.component.html b/apps/client/src/app/components/accounts-table/accounts-table.component.html index d576e5447..b38914d73 100644 --- a/apps/client/src/app/components/accounts-table/accounts-table.component.html +++ b/apps/client/src/app/components/accounts-table/accounts-table.component.html @@ -19,13 +19,8 @@ - - Currency + + Currency {{ element.currency }} @@ -36,13 +31,8 @@ - - Platform + + Platform
@@ -81,10 +71,9 @@ - Cash Balance + Cash Balance - Value + Value - Value + Value - # + # Type Symbol Data Source @@ -105,19 +105,18 @@ - - 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 3642a9e1d..84dae467b 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 @@ -43,8 +43,8 @@
-
diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.html b/apps/client/src/app/components/admin-market-data/admin-market-data.html index 725c75e22..654a692a6 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.html +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.html @@ -36,26 +36,23 @@ diff --git a/apps/client/src/app/components/admin-overview/admin-overview.component.ts b/apps/client/src/app/components/admin-overview/admin-overview.component.ts index 493802806..c351f1a68 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.component.ts +++ b/apps/client/src/app/components/admin-overview/admin-overview.component.ts @@ -103,7 +103,7 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { } public onAddCurrency() { - const currency = prompt('Please add a currency:'); + const currency = prompt($localize`Please add a currency:`); if (currency) { const currencies = uniq([...this.customCurrencies, currency]); @@ -116,7 +116,9 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { } public onDeleteCoupon(aCouponCode: string) { - const confirmation = confirm('Do you really want to delete this coupon?'); + const confirmation = confirm( + $localize`Do you really want to delete this coupon?` + ); if (confirmation === true) { const coupons = this.coupons.filter((coupon) => { @@ -127,7 +129,9 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { } public onDeleteCurrency(aCurrency: string) { - const confirmation = confirm('Do you really want to delete this currency?'); + const confirmation = confirm( + $localize`Do you really want to delete this currency?` + ); if (confirmation === true) { const currencies = this.customCurrencies.filter((currency) => { @@ -142,7 +146,9 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { } public onFlushCache() { - const confirmation = confirm('Do you really want to flush the cache?'); + const confirmation = confirm( + $localize`Do you really want to flush the cache?` + ); if (confirmation === true) { this.cacheService @@ -190,7 +196,7 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { } public onSetSystemMessage() { - const systemMessage = prompt('Please set your system message:'); + const systemMessage = prompt($localize`Please set your system message:`); if (systemMessage) { this.putSystemMessage(systemMessage); diff --git a/apps/client/src/app/components/admin-overview/admin-overview.html b/apps/client/src/app/components/admin-overview/admin-overview.html index d510582c3..76aab924b 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.html +++ b/apps/client/src/app/components/admin-overview/admin-overview.html @@ -8,7 +8,7 @@
{{ userCount }}
-
Transaction Count
+
Activity Count
{{ transactionCount }} ({{ transactionCount / userCount | number @@ -17,7 +17,7 @@
-
Data Gathering
+
Data Management
diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index fc5aaa83b..7f2a34d2b 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -55,7 +55,9 @@ export class AdminUsersComponent implements OnDestroy, OnInit { } public onDeleteUser(aId: string) { - const confirmation = confirm('Do you really want to delete this user?'); + const confirmation = confirm( + $localize`Do you really want to delete this user?` + ); if (confirmation) { this.dataService diff --git a/apps/client/src/app/components/admin-users/admin-users.html b/apps/client/src/app/components/admin-users/admin-users.html index 8eef62832..c047d8897 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -7,17 +7,17 @@ # User - - Registration + + Registration - - Accounts + + Accounts - - Activities + + Activities - - Engagement per Day + + Engagement per Day Last Request diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index 9846cd76b..336bc6d24 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -285,17 +285,16 @@ mat-flat-button > - Get Started + >Get started diff --git a/apps/client/src/app/components/header/header.component.ts b/apps/client/src/app/components/header/header.component.ts index 3e04f2206..9e17b59ad 100644 --- a/apps/client/src/app/components/header/header.component.ts +++ b/apps/client/src/app/components/header/header.component.ts @@ -109,7 +109,7 @@ export class HeaderComponent implements OnChanges { data: { accessToken: '', hasPermissionToUseSocialLogin: this.hasPermissionForSocialLogin, - title: 'Sign in' + title: $localize`Sign in` }, width: '30rem' }); @@ -123,7 +123,7 @@ export class HeaderComponent implements OnChanges { .loginAnonymous(data?.accessToken) .pipe( catchError(() => { - alert('Oops! Incorrect Security Token.'); + alert($localize`Oops! Incorrect Security Token.`); return EMPTY; }), diff --git a/apps/client/src/app/components/home-holdings/home-holdings.component.ts b/apps/client/src/app/components/home-holdings/home-holdings.component.ts index a19d1746c..2065da3f1 100644 --- a/apps/client/src/app/components/home-holdings/home-holdings.component.ts +++ b/apps/client/src/app/components/home-holdings/home-holdings.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; +import { ToggleComponent } from '@ghostfolio/client/components/toggle/toggle.component'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { @@ -9,7 +10,6 @@ import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { defaultDateRangeOptions } from '@ghostfolio/common/config'; import { Position, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { DateRange } from '@ghostfolio/common/types'; @@ -27,7 +27,7 @@ import { PositionDetailDialogParams } from '../position/position-detail-dialog/i }) export class HomeHoldingsComponent implements OnDestroy, OnInit { public dateRange: DateRange; - public dateRangeOptions = defaultDateRangeOptions; + public dateRangeOptions = ToggleComponent.DEFAULT_DATE_RANGE_OPTIONS; public deviceType: string; public hasImpersonationId: boolean; public hasPermissionToCreateOrder: boolean; diff --git a/apps/client/src/app/components/home-market/home-market.html b/apps/client/src/app/components/home-market/home-market.html index e8cf52bee..b0a01a43c 100644 --- a/apps/client/src/app/components/home-market/home-market.html +++ b/apps/client/src/app/components/home-market/home-market.html @@ -34,6 +34,7 @@ Sign in with Internet Identity - Sign in with Google @@ -49,12 +49,11 @@
diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts index d8cda52f4..7a69f0d58 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts @@ -45,7 +45,7 @@ export class PortfolioSummaryComponent implements OnChanges, OnInit { public onEditEmergencyFund() { const emergencyFundInput = prompt( - 'Please enter the amount of your emergency fund:', + $localize`Please enter the amount of your emergency fund:`, this.summary.emergencyFund.toString() ); const emergencyFund = parseFloat(emergencyFundInput?.trim()); diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html index 59a8e4e16..c910587ae 100644 --- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html +++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html @@ -35,112 +35,124 @@
+ >Change
+ >Performance
+ >Average Unit Price
+ >Market Price
+ >Minimum Price
+ >Maximum Price
+ >Quantity
+ >Investment
+ >First Buy Date
+ >Transactions
+ >Asset Class
+ >Asset Sub Class
+ >Sector
+ >Country
diff --git a/apps/client/src/app/components/positions-table/positions-table.component.html b/apps/client/src/app/components/positions-table/positions-table.component.html index f41638946..678ba13da 100644 --- a/apps/client/src/app/components/positions-table/positions-table.component.html +++ b/apps/client/src/app/components/positions-table/positions-table.component.html @@ -18,8 +18,8 @@ - - Symbol + + Symbol {{ element.symbol | gfSymbol }} @@ -30,11 +30,10 @@ - Name + Name {{ @@ -47,11 +46,10 @@ - Value + Value
@@ -68,11 +66,10 @@ - Allocation + Allocation
@@ -89,10 +86,9 @@ - Performance + Performance
@@ -137,8 +133,8 @@ *ngIf="dataSource.data.length > pageSize && !isLoading" class="my-3 text-center" > -
diff --git a/apps/client/src/app/components/toggle/toggle.component.ts b/apps/client/src/app/components/toggle/toggle.component.ts index dd5d6bbfc..a330e8af8 100644 --- a/apps/client/src/app/components/toggle/toggle.component.ts +++ b/apps/client/src/app/components/toggle/toggle.component.ts @@ -17,6 +17,14 @@ import { ToggleOption } from '@ghostfolio/common/types'; styleUrls: ['./toggle.component.scss'] }) export class ToggleComponent implements OnChanges, OnInit { + public static DEFAULT_DATE_RANGE_OPTIONS: ToggleOption[] = [ + { label: $localize`Today`, value: '1d' }, + { label: $localize`YTD`, value: 'ytd' }, + { label: $localize`1Y`, value: '1y' }, + { label: $localize`5Y`, value: '5y' }, + { label: $localize`Max`, value: 'max' } + ]; + @Input() defaultValue: string; @Input() isLoading: boolean; @Input() options: ToggleOption[]; diff --git a/apps/client/src/app/core/auth.guard.ts b/apps/client/src/app/core/auth.guard.ts index 6161df1f2..2daf2fab7 100644 --- a/apps/client/src/app/core/auth.guard.ts +++ b/apps/client/src/app/core/auth.guard.ts @@ -72,7 +72,13 @@ export class AuthGuard implements CanActivate { }) ) .subscribe((user) => { - if ( + const userLanguage = user?.settings?.language; + + if (userLanguage && document.documentElement.lang !== userLanguage) { + window.location.href = `../${userLanguage}`; + resolve(false); + return; + } else if ( state.url.startsWith('/home') && user.settings.viewMode === ViewMode.ZEN ) { diff --git a/apps/client/src/app/core/http-response.interceptor.ts b/apps/client/src/app/core/http-response.interceptor.ts index b76884ffa..84390b44c 100644 --- a/apps/client/src/app/core/http-response.interceptor.ts +++ b/apps/client/src/app/core/http-response.interceptor.ts @@ -56,14 +56,18 @@ export class HttpResponseInterceptor implements HttpInterceptor { if (!this.snackBarRef) { if (this.info.isReadOnlyMode) { this.snackBarRef = this.snackBar.open( - 'This feature is currently unavailable. Please try again later.', + $localize`This feature is currently unavailable.` + + ' ' + + $localize`Please try again later.`, undefined, { duration: 6000 } ); } else { this.snackBarRef = this.snackBar.open( - 'This feature requires a subscription.', - this.hasPermissionForSubscription ? 'Upgrade Plan' : undefined, + $localize`This feature requires a subscription.`, + this.hasPermissionForSubscription + ? $localize`Upgrade Plan` + : undefined, { duration: 6000 } ); } @@ -79,8 +83,10 @@ export class HttpResponseInterceptor implements HttpInterceptor { } else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) { if (!this.snackBarRef) { this.snackBarRef = this.snackBar.open( - 'Oops! Something went wrong. Please try again later.', - 'Okay', + $localize`Oops! Something went wrong.` + + ' ' + + $localize`Please try again later.`, + $localize`Okay`, { duration: 6000 } ); diff --git a/apps/client/src/app/pages/about/about-page-routing.module.ts b/apps/client/src/app/pages/about/about-page-routing.module.ts index 441d130da..33a303a5b 100644 --- a/apps/client/src/app/pages/about/about-page-routing.module.ts +++ b/apps/client/src/app/pages/about/about-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: AboutPageComponent, path: '', - title: 'About' + title: $localize`About` } ]; diff --git a/apps/client/src/app/pages/about/about-page.html b/apps/client/src/app/pages/about/about-page.html index 8d1b05a3a..0df161a5d 100644 --- a/apps/client/src/app/pages/about/about-page.html +++ b/apps/client/src/app/pages/about/about-page.html @@ -1,7 +1,7 @@
-

About Ghostfolio

+

About Ghostfolio

Ghostfolio is a lightweight wealth management application for @@ -21,7 +21,7 @@ This instance is running Ghostfolio {{ version }}. - Check the system status at status.ghostfol.io

-

Ghostfolio in Numbers

+

Ghostfolio in Numbers

+ >Active Users
+ >New Users
+ >Active Users
@@ -148,10 +152,11 @@ href="https://github.com/ghostfolio/ghostfolio/graphs/contributors" > + >Contributors on GitHub
@@ -160,10 +165,11 @@ href="https://github.com/ghostfolio/ghostfolio/stargazers" > + >Stars on GitHub
@@ -177,7 +183,6 @@ FAQChangelog & LicensePrivacy PolicyBlogChangelog - +
@@ -15,7 +15,7 @@

License

- +
diff --git a/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page-routing.module.ts b/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page-routing.module.ts index 7cbeeb247..8eaa96329 100644 --- a/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page-routing.module.ts +++ b/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: PrivacyPolicyPageComponent, path: '', - title: 'Privacy Policy' + title: $localize`Privacy Policy` } ]; diff --git a/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page.html b/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page.html index f3021edeb..44f76264e 100644 --- a/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page.html +++ b/apps/client/src/app/pages/about/privacy-policy/privacy-policy-page.html @@ -2,7 +2,7 @@

Privacy Policy

- +
diff --git a/apps/client/src/app/pages/account/account-page-routing.module.ts b/apps/client/src/app/pages/account/account-page-routing.module.ts index 1e5e6f00a..e9d07bd77 100644 --- a/apps/client/src/app/pages/account/account-page-routing.module.ts +++ b/apps/client/src/app/pages/account/account-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: AccountPageComponent, path: '', - title: 'My Ghostfolio' + title: $localize`My Ghostfolio` } ]; diff --git a/apps/client/src/app/pages/account/account-page.component.ts b/apps/client/src/app/pages/account/account-page.component.ts index 804a48565..7c97fef6f 100644 --- a/apps/client/src/app/pages/account/account-page.component.ts +++ b/apps/client/src/app/pages/account/account-page.component.ts @@ -53,6 +53,7 @@ export class AccountPageComponent implements OnDestroy, OnInit { public hasPermissionToDeleteAccess: boolean; public hasPermissionToUpdateViewMode: boolean; public hasPermissionToUpdateUserSettings: boolean; + public language = document.documentElement.lang; public locales = ['de', 'de-CH', 'en-GB', 'en-US']; public price: number; public priceId: string; @@ -162,6 +163,14 @@ export class AccountPageComponent implements OnDestroy, OnInit { this.user = user; this.changeDetectorRef.markForCheck(); + + if (aKey === 'language') { + if (aValue) { + window.location.href = `../${aValue}/account`; + } else { + window.location.href = `../`; + } + } }); }); } @@ -218,7 +227,7 @@ export class AccountPageComponent implements OnDestroy, OnInit { } public onRedeemCoupon() { - let couponCode = prompt('Please enter your coupon code:'); + let couponCode = prompt($localize`Please enter your coupon code:`); couponCode = couponCode?.trim(); if (couponCode) { @@ -227,17 +236,21 @@ export class AccountPageComponent implements OnDestroy, OnInit { .pipe( takeUntil(this.unsubscribeSubject), catchError(() => { - this.snackBar.open('😞 Could not redeem coupon code', undefined, { - duration: 3000 - }); + this.snackBar.open( + '😞 ' + $localize`Could not redeem coupon code`, + undefined, + { + duration: 3000 + } + ); return EMPTY; }) ) .subscribe(() => { this.snackBarRef = this.snackBar.open( - '✅ Coupon code has been redeemed', - 'Reload', + '✅' + $localize`Coupon code has been redeemed`, + $localize`Reload`, { duration: 3000 } @@ -283,7 +296,7 @@ export class AccountPageComponent implements OnDestroy, OnInit { this.registerDevice(); } else { const confirmation = confirm( - 'Do you really want to remove this sign in method?' + $localize`Do you really want to remove this sign in method?` ); if (confirmation) { diff --git a/apps/client/src/app/pages/account/account-page.html b/apps/client/src/app/pages/account/account-page.html index 88a1ee620..335ec69f6 100644 --- a/apps/client/src/app/pages/account/account-page.html +++ b/apps/client/src/app/pages/account/account-page.html @@ -31,11 +31,10 @@
-
- Base Currency +
+ Base Currency
@@ -111,11 +110,30 @@
+
+
+
Language
+
Beta
+
+
+ + + + Deutsch + English + + +
+
Locale
-
- Date and number format +
+ Date and number format
@@ -137,8 +155,8 @@
-
- View Mode +
+ View Mode
diff --git a/apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.html b/apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.html index 492736507..badb12f69 100644 --- a/apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.html +++ b/apps/client/src/app/pages/account/create-or-update-access-dialog/create-or-update-access-dialog.html @@ -14,12 +14,11 @@
diff --git a/apps/client/src/app/pages/accounts/accounts-page-routing.module.ts b/apps/client/src/app/pages/accounts/accounts-page-routing.module.ts index 74f5ea050..705c4449c 100644 --- a/apps/client/src/app/pages/accounts/accounts-page-routing.module.ts +++ b/apps/client/src/app/pages/accounts/accounts-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: AccountsPageComponent, path: '', - title: 'Accounts' + title: $localize`Accounts` } ]; diff --git a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html index eaef9d607..971487356 100644 --- a/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html +++ b/apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -66,12 +66,11 @@
diff --git a/apps/client/src/app/pages/admin/admin-page-routing.module.ts b/apps/client/src/app/pages/admin/admin-page-routing.module.ts index 48cf239ba..76fde5561 100644 --- a/apps/client/src/app/pages/admin/admin-page-routing.module.ts +++ b/apps/client/src/app/pages/admin/admin-page-routing.module.ts @@ -20,7 +20,7 @@ const routes: Routes = [ ], component: AdminPageComponent, path: '', - title: 'Admin Control' + title: $localize`Admin Control` } ]; diff --git a/apps/client/src/app/pages/auth/auth-page.component.ts b/apps/client/src/app/pages/auth/auth-page.component.ts index 7764c45ff..75702c096 100644 --- a/apps/client/src/app/pages/auth/auth-page.component.ts +++ b/apps/client/src/app/pages/auth/auth-page.component.ts @@ -28,6 +28,7 @@ export class AuthPageComponent implements OnDestroy, OnInit { .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((params) => { const jwt = params['jwt']; + this.tokenStorageService.saveToken( jwt, this.settingsStorageService.getSetting(STAY_SIGNED_IN) === 'true' diff --git a/apps/client/src/app/pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.html b/apps/client/src/app/pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.html index ae3aa358b..b3b2fde5f 100644 --- a/apps/client/src/app/pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.html +++ b/apps/client/src/app/pages/blog/2021/07/hallo-ghostfolio/hallo-ghostfolio-page.html @@ -68,7 +68,7 @@

Ghostfol.io Screenshot diff --git a/apps/client/src/app/pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.html b/apps/client/src/app/pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.html index 47c2819fb..43e3e0e35 100644 --- a/apps/client/src/app/pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.html +++ b/apps/client/src/app/pages/blog/2021/07/hello-ghostfolio/hello-ghostfolio-page.html @@ -66,7 +66,7 @@

Ghostfol.io Screenshot diff --git a/apps/client/src/app/pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.html b/apps/client/src/app/pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.html index 7e40176d3..c91c91638 100644 --- a/apps/client/src/app/pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.html +++ b/apps/client/src/app/pages/blog/2022/01/first-months-in-open-source/first-months-in-open-source-page.html @@ -20,9 +20,7 @@

From 1* to 100 stars on GitHub

When I decided to - publish + publish the project as open source software2022-07-23

Ghostfolio meets Internet Identity Teaser
diff --git a/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page-routing.module.ts b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page-routing.module.ts new file mode 100644 index 000000000..c64edfab2 --- /dev/null +++ b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page-routing.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; + +import { FiveHundredStarsOnGitHubPageComponent } from './500-stars-on-github-page.component'; + +const routes: Routes = [ + { + canActivate: [AuthGuard], + component: FiveHundredStarsOnGitHubPageComponent, + path: '', + title: '500 Stars on GitHub' + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class FiveHundredStarsOnGitHubRoutingModule {} diff --git a/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.component.ts b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.component.ts new file mode 100644 index 000000000..de177d9c2 --- /dev/null +++ b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + host: { class: 'page' }, + selector: 'gf-500-stars-on-github-page', + styleUrls: ['./500-stars-on-github-page.scss'], + templateUrl: './500-stars-on-github-page.html' +}) +export class FiveHundredStarsOnGitHubPageComponent {} diff --git a/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.html b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.html new file mode 100644 index 000000000..68aa4c8ba --- /dev/null +++ b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.html @@ -0,0 +1,195 @@ +
+
+
+
+
+

500 Stars

+
2022-08-18
+ 500 Stars on GitHub Teaser +
+
+

+ Ghostfolio, the web-based personal + finance management software, is celebrating 500 stars on + GitHub. This + is a major milestone for this open source project and a good time + for another + recap. +

+
+
+

Growing Community

+

+ The Ghostfolio community is growing on various platforms and has + recently passed 100 members on + Slack + as well as 100 followers on + Twitter. If you have + not joined yet, this is a good time to make sure you do not miss out + on any future updates. +

+
+
+

Message Queue: Asynchronous Processing

+

+ Overall + stability and robustness + has increased significantly since the introduction of a + message queue. The + workers of this robust queue system process jobs, namely gathering + historical market data, asynchronously in the background to not + bother the main service. +

+
+
+

Ready for Web 3.0

+

+ The + recent integration of Internet Identity, a blockchain authentication system, makes Ghostfolio ready for + Web3. This third iteration of the World Wide Web is the vision of a + new and better Internet based on decentralized blockchains to give + power back to the users. Internet Identity created by the + Dfinity Foundation enables you to + sign in securely and anonymously to Ghostfolio without an email + address, username, or a password. All you need is your device with + built-in biometric authentication. +

+
+
+

Break-even Point

+

+ Despite the complicated + economic situation at this time, + the goal set at the beginning of the year to build a sustainable + business and reach break-even with the SaaS offering (Ghostfolio Premium) has been achieved. We will continue to leverage the revenue to + further improve the fully managed cloud offering for our paying + customers. A new goal we have set for ourselves is to become + profitable. +

+
+
+

Outlook

+

+ Besides all the positive accomplishments during the last months, + there is still a lot of room for improvement. It would be great to + onboard more contributors who are actively involved in software + engineering to realize the full potential of open source software. + If you are a web developer and interested in personal finance, + please get in touch by email via + hi@ghostfol.io or on Twitter + @ghostfolio_. We are + happy to discuss ideas. +

+

+ We would like to say thank you for all your feedback and support + since the beginning of this project. +

+

+ Off to the next 500 stars!
+ Thomas from Ghostfolio +

+
+
+
    +
  • + Blockchain +
  • +
  • + BuildInPublic +
  • +
  • + Cloud +
  • +
  • + Community +
  • +
  • + Finance +
  • +
  • + Fintech +
  • +
  • + Future +
  • +
  • + Goal +
  • +
  • + Internet Identity +
  • +
  • + Investment +
  • +
  • + Message Queue +
  • +
  • + OpenSaaS +
  • +
  • + Open Source +
  • +
  • + OSS +
  • +
  • + Personal Finance +
  • +
  • + Planning +
  • +
  • + Portfolio +
  • +
  • + Portfolio Tracker +
  • +
  • + Progress +
  • +
  • + SaaS +
  • +
  • + Software +
  • +
  • + User Feedback +
  • +
  • + Wealth +
  • +
  • + Wealth Management +
  • +
  • + Web3 +
  • +
  • + Web 3.0 +
  • +
  • + Worker +
  • +
+
+
+
+
+
diff --git a/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.module.ts b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.module.ts new file mode 100644 index 000000000..ffc072ac9 --- /dev/null +++ b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.module.ts @@ -0,0 +1,13 @@ +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { FiveHundredStarsOnGitHubRoutingModule } from './500-stars-on-github-page-routing.module'; +import { FiveHundredStarsOnGitHubPageComponent } from './500-stars-on-github-page.component'; + +@NgModule({ + declarations: [FiveHundredStarsOnGitHubPageComponent], + imports: [CommonModule, FiveHundredStarsOnGitHubRoutingModule, RouterModule], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class FiveHundredStarsOnGitHubPageModule {} diff --git a/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.scss b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.scss new file mode 100644 index 000000000..5d4e87f30 --- /dev/null +++ b/apps/client/src/app/pages/blog/2022/08/500-stars-on-github/500-stars-on-github-page.scss @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/apps/client/src/app/pages/blog/blog-page-routing.module.ts b/apps/client/src/app/pages/blog/blog-page-routing.module.ts index 44f02178c..b65a7bd8c 100644 --- a/apps/client/src/app/pages/blog/blog-page-routing.module.ts +++ b/apps/client/src/app/pages/blog/blog-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: BlogPageComponent, path: '', - title: 'Blog' + title: $localize`Blog` } ]; diff --git a/apps/client/src/app/pages/blog/blog-page.html b/apps/client/src/app/pages/blog/blog-page.html index 12f16b654..871af06a5 100644 --- a/apps/client/src/app/pages/blog/blog-page.html +++ b/apps/client/src/app/pages/blog/blog-page.html @@ -8,7 +8,31 @@ +
+ + + + +
+
+
@@ -34,7 +58,7 @@
@@ -60,7 +84,7 @@
@@ -86,7 +110,7 @@
Hello Ghostfolio
@@ -110,7 +134,7 @@
Hallo Ghostfolio
diff --git a/apps/client/src/app/pages/demo/demo-page.component.ts b/apps/client/src/app/pages/demo/demo-page.component.ts index dce634a7d..c62ece50a 100644 --- a/apps/client/src/app/pages/demo/demo-page.component.ts +++ b/apps/client/src/app/pages/demo/demo-page.component.ts @@ -28,7 +28,7 @@ export class DemoPageComponent implements OnDestroy { if (hasToken) { alert( - 'As you are already logged in, you cannot access the demo account.' + $localize`As you are already logged in, you cannot access the demo account.` ); } else { this.tokenStorageService.saveToken(this.info.demoAuthToken, true); diff --git a/apps/client/src/app/pages/faq/faq-page-routing.module.ts b/apps/client/src/app/pages/faq/faq-page-routing.module.ts index 66b522e86..52deddb27 100644 --- a/apps/client/src/app/pages/faq/faq-page-routing.module.ts +++ b/apps/client/src/app/pages/faq/faq-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: FaqPageComponent, path: '', - title: 'FAQ' + title: $localize`FAQ` } ]; diff --git a/apps/client/src/app/pages/faq/faq-page.html b/apps/client/src/app/pages/faq/faq-page.html index 6f81579b1..459d8987d 100644 --- a/apps/client/src/app/pages/faq/faq-page.html +++ b/apps/client/src/app/pages/faq/faq-page.html @@ -1,61 +1,57 @@
-

Frequently Asked Questions (FAQ)

+

Frequently Asked Questions (FAQ)

- What is Ghostfolio? - + What is Ghostfolio? + Ghostfolio is a lightweight, open source wealth management application for individuals to keep track of their net worth. The software empowers you to make solid, data-driven investment decisions. - What assets can I track with Ghostfolio? - + With Ghostfolio, you can keep track of various assets like stocks, ETFs or cryptocurrencies. - What else is included in Ghostfolio? - + What else is included in Ghostfolio? + Please find a feature overview to manage your wealth
here. - How do I start? - + How do I start? + You can sign up via the “Get Started” button at the top of the page. You have multiple options to join Ghostfolio: Create an account with a security token, using - Internet Identity or Google Sign. We will guide you to set up your portfolio. - Can I use Ghostfolio anonymously? - + Can I use Ghostfolio anonymously? + Yes, the authentication systems (via security token or - Internet Identity) enable you to sign in securely and anonymously to Ghostfolio. There is no need for an email address, phone number, or a username. - How can Ghostfolio be free? - How can Ghostfolio be free? + This project is driven by the efforts of contributors from around the world. The source code is @@ -66,16 +62,16 @@ > - Is it really free? - Is it really free? + Yes, it is! Our pricing page details everything you get for free. - What is Ghostfolio Premium? - What is Ghostfolio Premium? + Ghostfolio Premium is a fully managed Ghostfolio cloud offering for ambitious investors. The revenue is used to cover the hosting infrastructure. It is the Open Source @@ -83,8 +79,8 @@ > - Can I start with a trial version? - Can I start with a trial version? + Yes, you can try Ghostfolio Premium by signing up for Ghostfolio and applying for a trial (see “My Ghostfolio”). It’s @@ -93,8 +89,8 @@ > - Which devices are supported? - Which devices are supported? + Ghostfolio works in every modern web browser on smartphones, tablets and desktop computers (where you have even more analysis options and statistics). For Android users, there is a dedicated Ghostfolio app @@ -106,10 +102,10 @@ > - Ghostfolio sounds cool, how can I get involved? - Any support for Ghostfolio is welcome. Be it with a Ghostfolio Premium subscription to finance the hosting, a positive rating in the @@ -126,8 +122,8 @@ > - Got any other questions? - Got any other questions? + Join the Ghostfolio
-

- Features -

+

Features

Check out the numerous features of Ghostfolio to @@ -14,7 +12,7 @@

-

Stocks

+

Stocks

Keep track of your stock purchases and sales.

@@ -22,7 +20,7 @@
-

ETFs

+

ETFs

Are you into ETFs (Exchange Traded Funds)? Track your ETF investments. @@ -33,7 +31,7 @@

-

Bonds

+

Bonds

Manage your investment in bonds and other assets with fixed income. @@ -44,7 +42,7 @@

-

Cryptocurrencies

+

Cryptocurrencies

Keep track of your Bitcoin and Altcoin holdings.

@@ -54,7 +52,7 @@
-

Dividend

+

Dividend

Are you building a dividend portfolio? Track your dividend in Ghostfolio. @@ -65,7 +63,7 @@

-

Wealth Items

+

Wealth Items

Track all your treasuries, be it your luxury watch or rare trading cards. @@ -76,7 +74,7 @@

-

Emergency Fund

+

Emergency Fund

Define your emergency fund you are comfortable with for difficult times. @@ -87,7 +85,7 @@

-

Import and Export

+

Import and Export

Import and export your investment activities.

@@ -95,7 +93,7 @@
-

Multi-Accounts

+

Multi-Accounts

Keep an eye on all your accounts across multiple platforms (multi-banking). @@ -107,7 +105,7 @@

- Portfolio Calculations + Portfolio Calculations

- Portfolio Allocations + Portfolio Allocations
-

Dark Mode

+

Dark Mode

Ghostfolio automatically switches to a dark color theme based on your operating system's preferences. @@ -152,7 +150,7 @@

-

Zen Mode

+

Zen Mode

Keep calm and activate Zen Mode if the markets are going crazy.

@@ -166,7 +164,7 @@

- Market Mood + Market Mood

@@ -181,7 +179,7 @@

- Static Analysis + Static Analysis
-

Community

+

Community

Join the Ghostfolio

-

Open Source Software

+

Open Source Software

The source code is fully available as

diff --git a/apps/client/src/app/pages/home/home-page-routing.module.ts b/apps/client/src/app/pages/home/home-page-routing.module.ts index 80c5a141b..c0e64715d 100644 --- a/apps/client/src/app/pages/home/home-page-routing.module.ts +++ b/apps/client/src/app/pages/home/home-page-routing.module.ts @@ -20,7 +20,7 @@ const routes: Routes = [ ], component: HomePageComponent, path: '', - title: 'Overview' + title: $localize`Overview` } ]; diff --git a/apps/client/src/app/pages/landing/landing-page.html b/apps/client/src/app/pages/landing/landing-page.html index 7aef4eeab..7527faa13 100644 --- a/apps/client/src/app/pages/landing/landing-page.html +++ b/apps/client/src/app/pages/landing/landing-page.html @@ -1,7 +1,7 @@
-

+

Manage your wealth like a boss

@@ -13,7 +13,7 @@ Ghostfol.io Trailer @@ -29,19 +29,13 @@ Get Started -
or
- +
or
+
Live Demo
@@ -107,7 +101,7 @@
@@ -162,16 +156,11 @@ Join now or check out the example account

@@ -183,7 +172,7 @@ href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa" title="Get Ghostfolio on Google Play" > - Google Play Badge + Google Play Badge
diff --git a/apps/client/src/app/pages/markets/markets-page-routing.module.ts b/apps/client/src/app/pages/markets/markets-page-routing.module.ts index 47a10820f..ba6b6bb82 100644 --- a/apps/client/src/app/pages/markets/markets-page-routing.module.ts +++ b/apps/client/src/app/pages/markets/markets-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: MarketsPageComponent, path: '', - title: 'Markets' + title: $localize`Markets` } ]; diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page-routing.module.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page-routing.module.ts index f402823cc..e5dfd6a2a 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: AllocationsPageComponent, path: '', - title: 'Allocations' + title: $localize`Allocations` } ]; diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index 9c60b4e13..d230331e9 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -54,8 +54,8 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { }; public period = 'current'; public periodOptions: ToggleOption[] = [ - { label: 'Initial', value: 'original' }, - { label: 'Current', value: 'current' } + { label: $localize`Initial`, value: 'original' }, + { label: $localize`Current`, value: 'current' } ]; public placeholder = ''; public portfolioDetails: PortfolioDetails; diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html index baa01aa32..51aa49ae2 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html @@ -94,8 +94,8 @@
- - By Holding + By Holding
+ >Developed Markets
+ >Emerging Markets
+ >Other Markets
diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page-routing.module.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page-routing.module.ts index c2f15863d..b9b8fd712 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: AnalysisPageComponent, path: '', - title: 'Analysis' + title: $localize`Analysis` } ]; diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 1f52c8c84..36bab3523 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -26,8 +26,8 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public investmentsByMonth: InvestmentItem[]; public mode: GroupBy; public modeOptions: ToggleOption[] = [ - { label: 'Monthly', value: 'month' }, - { label: 'Accumulating', value: undefined } + { label: $localize`Monthly`, value: 'month' }, + { label: $localize`Accumulating`, value: undefined } ]; public top3: Position[]; public user: User; diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index 159cd5678..cad0f1179 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -52,7 +52,7 @@ Top 3Top @@ -88,7 +88,7 @@ Bottom 3Bottom diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts b/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts index 50be75498..7ca0c8d20 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: FirePageComponent, path: '', - title: 'FIRE' + title: $localize`FIRE` } ]; diff --git a/apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts b/apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts index 208248bcf..76adcd35d 100644 --- a/apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/holdings/holdings-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: HoldingsPageComponent, path: '', - title: 'Holdings' + title: $localize`Holdings` } ]; diff --git a/apps/client/src/app/pages/portfolio/portfolio-page.html b/apps/client/src/app/pages/portfolio/portfolio-page.html index 73f1f2f52..d5aa3b693 100644 --- a/apps/client/src/app/pages/portfolio/portfolio-page.html +++ b/apps/client/src/app/pages/portfolio/portfolio-page.html @@ -91,7 +91,7 @@

- X-ray + X-ray
-

+

X-ray

-

+

Ghostfolio X-ray uses static analysis to identify potential issues and risks in your portfolio.

-

Currency Cluster Risks

+

Currency Cluster Risks

-

Account Cluster Risks

+

Account Cluster Risks

-

Fees

+

Fees

- Asset Sub-Class + Asset Sub Class Cancel
diff --git a/apps/client/src/app/pages/portfolio/transactions/transactions-page-routing.module.ts b/apps/client/src/app/pages/portfolio/transactions/transactions-page-routing.module.ts index 17feefa27..70dc226f5 100644 --- a/apps/client/src/app/pages/portfolio/transactions/transactions-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/transactions/transactions-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: TransactionsPageComponent, path: '', - title: 'Activities' + title: $localize`Activities` } ]; diff --git a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts index 80c0701b5..d0fc9ee63 100644 --- a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts +++ b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts @@ -188,7 +188,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { input.type = 'file'; input.onchange = (event) => { - this.snackBar.open('⏳ Importing data...'); + this.snackBar.open('⏳' + $localize`Importing data...`); // Getting the file reference const file = (event.target as HTMLInputElement).files[0]; @@ -334,7 +334,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { private handleImportSuccess() { this.fetchActivities(); - this.snackBar.open('✅ Import has been completed', undefined, { + this.snackBar.open('✅' + $localize`Import has been completed`, undefined, { duration: 3000 }); } diff --git a/apps/client/src/app/pages/pricing/pricing-page-routing.module.ts b/apps/client/src/app/pages/pricing/pricing-page-routing.module.ts index 3380ccdab..0e4af3df3 100644 --- a/apps/client/src/app/pages/pricing/pricing-page-routing.module.ts +++ b/apps/client/src/app/pages/pricing/pricing-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: PricingPageComponent, path: '', - title: 'Pricing' + title: $localize`Pricing` } ]; diff --git a/apps/client/src/app/pages/pricing/pricing-page.html b/apps/client/src/app/pages/pricing/pricing-page.html index 428700acf..322868e04 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.html +++ b/apps/client/src/app/pages/pricing/pricing-page.html @@ -1,7 +1,7 @@
-

+

Pricing Plans

@@ -20,7 +20,7 @@
-

Open Source

+

Open Source

For tech-savvy investors who prefer to run Ghostfolio on their own infrastructure. @@ -73,7 +73,7 @@ [ngClass]="{ 'active': user?.subscription?.type === 'Basic' }" >

-

Basic

+

Basic

For new investors who are just getting started with trading.

@@ -124,7 +124,7 @@ >

- Premium + Premium {{ baseCurrency }} {{ price }} per year per year

@@ -196,14 +196,14 @@

- + Get Started

It's free

diff --git a/apps/client/src/app/pages/public/public-page-routing.module.ts b/apps/client/src/app/pages/public/public-page-routing.module.ts index b303d499f..a648bed2a 100644 --- a/apps/client/src/app/pages/public/public-page-routing.module.ts +++ b/apps/client/src/app/pages/public/public-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: PublicPageComponent, path: ':id', - title: 'Portfolio' + title: $localize`Portfolio` } ]; diff --git a/apps/client/src/app/pages/public/public-page.html b/apps/client/src/app/pages/public/public-page.html index 5746d68a5..ee2e368cd 100644 --- a/apps/client/src/app/pages/public/public-page.html +++ b/apps/client/src/app/pages/public/public-page.html @@ -82,27 +82,30 @@
+ >Developed Markets
+ >Emerging Markets
+ >Other Markets
@@ -129,8 +132,8 @@ Ghostfolio empowers you to keep track of your wealth.

diff --git a/apps/client/src/app/pages/register/register-page-routing.module.ts b/apps/client/src/app/pages/register/register-page-routing.module.ts index b41e9365f..71401852d 100644 --- a/apps/client/src/app/pages/register/register-page-routing.module.ts +++ b/apps/client/src/app/pages/register/register-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: RegisterPageComponent, path: '', - title: 'Registration' + title: $localize`Registration` } ]; diff --git a/apps/client/src/app/pages/register/register-page.html b/apps/client/src/app/pages/register/register-page.html index 770beb345..0542af79c 100644 --- a/apps/client/src/app/pages/register/register-page.html +++ b/apps/client/src/app/pages/register/register-page.html @@ -20,12 +20,11 @@
or
@@ -36,15 +35,15 @@ > Continue with Internet Identity - Continue with Google diff --git a/apps/client/src/app/pages/resources/resources-page-routing.module.ts b/apps/client/src/app/pages/resources/resources-page-routing.module.ts index d6ff02496..d0f385e10 100644 --- a/apps/client/src/app/pages/resources/resources-page-routing.module.ts +++ b/apps/client/src/app/pages/resources/resources-page-routing.module.ts @@ -9,7 +9,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: ResourcesPageComponent, path: '', - title: 'Resources' + title: $localize`Resources` } ]; diff --git a/apps/client/src/app/pages/resources/resources-page.html b/apps/client/src/app/pages/resources/resources-page.html index 141956896..a32d21585 100644 --- a/apps/client/src/app/pages/resources/resources-page.html +++ b/apps/client/src/app/pages/resources/resources-page.html @@ -29,8 +29,7 @@ easier and faster in this guide.
diff --git a/apps/client/src/app/pages/webauthn/webauthn-page-routing.module.ts b/apps/client/src/app/pages/webauthn/webauthn-page-routing.module.ts index 0297c10d9..bf2118b92 100644 --- a/apps/client/src/app/pages/webauthn/webauthn-page-routing.module.ts +++ b/apps/client/src/app/pages/webauthn/webauthn-page-routing.module.ts @@ -3,7 +3,7 @@ import { RouterModule, Routes } from '@angular/router'; import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component'; const routes: Routes = [ - { component: WebauthnPageComponent, path: '', title: 'Login' } + { component: WebauthnPageComponent, path: '', title: $localize`Sign in` } ]; @NgModule({ diff --git a/apps/client/src/app/pages/webauthn/webauthn-page.html b/apps/client/src/app/pages/webauthn/webauthn-page.html index d4a76b54a..a81a9c6fc 100644 --- a/apps/client/src/app/pages/webauthn/webauthn-page.html +++ b/apps/client/src/app/pages/webauthn/webauthn-page.html @@ -14,21 +14,20 @@ *ngIf="hasError" class="align-items-center col d-flex flex-column justify-content-center" > -

- Oops, authentication has failed. +

+ Oops, authentication has failed.

-
or
-
diff --git a/apps/client/src/app/pages/zen/zen-page-routing.module.ts b/apps/client/src/app/pages/zen/zen-page-routing.module.ts index 77e8a2169..70cf4e84d 100644 --- a/apps/client/src/app/pages/zen/zen-page-routing.module.ts +++ b/apps/client/src/app/pages/zen/zen-page-routing.module.ts @@ -16,7 +16,7 @@ const routes: Routes = [ ], component: ZenPageComponent, path: '', - title: 'Overview' + title: $localize`Overview` } ]; diff --git a/apps/client/src/assets/images/blog/500-stars-on-github.jpg b/apps/client/src/assets/images/blog/500-stars-on-github.jpg new file mode 100644 index 000000000..8a55b497e Binary files /dev/null and b/apps/client/src/assets/images/blog/500-stars-on-github.jpg differ diff --git a/apps/client/src/assets/robots.txt b/apps/client/src/assets/robots.txt index f87a4c1b5..059aea258 100644 --- a/apps/client/src/assets/robots.txt +++ b/apps/client/src/assets/robots.txt @@ -1,6 +1,6 @@ User-agent: * Allow: / -Disallow: /about/privacy-policy -Disallow: /p/* +Disallow: /en/about/privacy-policy +Disallow: /en/p/* Sitemap: https://ghostfol.io/sitemap.xml diff --git a/apps/client/src/assets/site.webmanifest b/apps/client/src/assets/site.webmanifest index 91853e97b..5948d94fe 100644 --- a/apps/client/src/assets/site.webmanifest +++ b/apps/client/src/assets/site.webmanifest @@ -6,12 +6,12 @@ "icons": [ { "sizes": "192x192", - "src": "/assets/android-chrome-192x192.png", + "src": "/en/assets/android-chrome-192x192.png", "type": "image/png" }, { "sizes": "512x512", - "src": "/assets/android-chrome-512x512.png", + "src": "/en/assets/android-chrome-512x512.png", "type": "image/png" } ], diff --git a/apps/client/src/assets/sitemap.xml b/apps/client/src/assets/sitemap.xml index 0b2cf4ee3..1bc1b3bbb 100644 --- a/apps/client/src/assets/sitemap.xml +++ b/apps/client/src/assets/sitemap.xml @@ -6,66 +6,70 @@ http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> https://ghostfol.io - 2022-07-29T00:00:00+00:00 + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/about - 2022-07-29T00:00:00+00:00 - - - https://ghostfol.io/about/changelog - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/blog - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/about + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/about/changelog + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/demo - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/blog + 2022-08-18T00:00:00+00:00 https://ghostfol.io/en/blog/2021/07/hello-ghostfolio - 2022-07-29T00:00:00+00:00 + 2022-08-18T00:00:00+00:00 https://ghostfol.io/en/blog/2022/01/ghostfolio-first-months-in-open-source - 2022-07-29T00:00:00+00:00 + 2022-08-18T00:00:00+00:00 https://ghostfol.io/en/blog/2022/07/ghostfolio-meets-internet-identity - 2022-07-29T00:00:00+00:00 + 2022-08-18T00:00:00+00:00 https://ghostfol.io/en/blog/2022/07/how-do-i-get-my-finances-in-order - 2022-07-29T00:00:00+00:00 + 2022-08-18T00:00:00+00:00 + + + https://ghostfol.io/en/blog/2022/08/500-stars-on-github + 2022-08-18T00:00:00+00:00 + + + https://ghostfol.io/en/demo + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/faq - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/faq + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/features - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/features + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/markets - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/markets + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/pricing - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/pricing + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/register - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/register + 2022-08-18T00:00:00+00:00 - https://ghostfol.io/resources - 2022-07-29T00:00:00+00:00 + https://ghostfol.io/en/resources + 2022-08-18T00:00:00+00:00 diff --git a/apps/client/src/index.html b/apps/client/src/index.html index 2e20f9d25..27ec03287 100644 --- a/apps/client/src/index.html +++ b/apps/client/src/index.html @@ -1,5 +1,5 @@ - + Ghostfolio – Open Source Wealth Management Software @@ -19,10 +19,7 @@ name="twitter:description" content="Ghostfolio is a lightweight wealth management application for individuals to keep track of stocks, ETFs or cryptocurrencies" /> - + - - - + + + - + - +