Feature/setup i18n (#1139)

* Setup i18n

* Update changelog
pull/1138/head
Thomas Kaul 2 years ago committed by GitHub
parent 55182ac1af
commit 81245635af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Set up i18n support
### Changed
- Reduced the maximum width of the performance chart on the home page

@ -77,41 +77,50 @@
"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": "index.html",
"input": "apps/client/src/assets",
"output": "./../"
},
{
"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 +133,10 @@
"namedChunks": true
},
"configurations": {
"development-en": {
"baseHref": "/en/",
"localize": ["en"]
},
"production": {
"fileReplacements": [
{
@ -162,6 +175,9 @@
"proxyConfig": "apps/client/proxy.conf.json"
},
"configurations": {
"development-en": {
"browserTarget": "client:build:development-en"
},
"production": {
"browserTarget": "client:build:production"
}
@ -188,6 +204,15 @@
"outputs": ["coverage/apps/client"]
}
},
"i18n": {
"locales": {
"de": {
"baseHref": "/de/",
"translation": "apps/client/src/locales/messages.de.xlf"
}
},
"sourceLocale": "en"
},
"tags": []
},
"client-e2e": {

@ -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`
);
}
}

@ -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')

@ -54,45 +54,45 @@ 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: 'demo',
loadChildren: () =>
import('./pages/demo/demo-page.module').then((m) => m.DemoPageModule)
},
{
path: 'faq',
loadChildren: () =>

@ -21,8 +21,10 @@
<td *matCellDef="let element" class="px-1 text-nowrap" mat-cell>
<ng-container *ngIf="element.type === 'PUBLIC'">
<ion-icon class="mr-1" name="link-outline"></ion-icon>
<a href="{{ baseUrl }}/p/{{ element.id }}" target="_blank"
>{{ baseUrl }}/p/{{ element.id }}</a
<a
href="{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}"
target="_blank"
>{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}</a
>
</ng-container>
</td>

@ -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<Access>;
public defaultLanguageCode = DEFAULT_LANGUAGE_CODE;
public displayedColumns = [];
public constructor() {}

@ -25,14 +25,14 @@
>
<img
class="mr-2"
src="./assets/icons/internet-computer.svg"
src="../assets/icons/internet-computer.svg"
style="height: 0.75rem"
/><span i18n>Sign in with Internet Identity</span>
</button>
<a href="/api/v1/auth/google" mat-stroked-button
<a href="../api/v1/auth/google" mat-stroked-button
><img
class="mr-2"
src="./assets/icons/google.svg"
src="../assets/icons/google.svg"
style="height: 1rem"
/><span i18n>Sign in with Google</span></a
>

@ -4,7 +4,7 @@
<h3 class="mb-3 text-center" i18n>Changelog</h3>
<mat-card class="changelog">
<mat-card-content>
<markdown [src]="'assets/CHANGELOG.md'"></markdown>
<markdown [src]="'../assets/CHANGELOG.md'"></markdown>
</mat-card-content>
</mat-card>
</div>
@ -15,7 +15,7 @@
<h3 class="mb-3 text-center" i18n>License</h3>
<mat-card>
<mat-card-content>
<markdown [src]="'assets/LICENSE'"></markdown>
<markdown [src]="'../assets/LICENSE'"></markdown>
</mat-card-content>
</mat-card>
</div>

@ -2,7 +2,7 @@
<div class="mb-5 row">
<div class="col">
<h3 class="mb-3 text-center" i18n>Privacy Policy</h3>
<markdown [src]="'assets/privacy-policy.md'"></markdown>
<markdown [src]="'../assets/privacy-policy.md'"></markdown>
</div>
</div>
</div>

@ -68,7 +68,7 @@
<p class="my-5 text-center">
<img
alt="Ghostfol.io Screenshot"
src="./assets/images/screenshot.png"
src="../assets/images/screenshot.png"
style="max-width: 100%; width: 20rem"
title="Ghostfol.io Screenshot"
/>

@ -66,7 +66,7 @@
<p class="my-5 text-center">
<img
alt="Ghostfol.io Screenshot"
src="./assets/images/screenshot.png"
src="../assets/images/screenshot.png"
style="max-width: 100%; width: 20rem"
title="Ghostfol.io Screenshot"
/>

@ -8,7 +8,7 @@
<img
alt="Ghostfolio meets Internet Identity Teaser"
class="w-100"
src="./assets/images/blog/ghostfolio-meets-internet-identity.png"
src="../assets/images/blog/ghostfolio-meets-internet-identity.png"
title="Ghostfolio meets Internet Identity"
/>
</div>

@ -8,7 +8,7 @@
<div class="flex-nowrap no-gutters row">
<a
class="d-flex w-100"
[routerLink]="['/en', 'blog', '2022', '07', 'ghostfolio-meets-internet-identity']"
[routerLink]="['/blog', '2022', '07', 'ghostfolio-meets-internet-identity']"
>
<div class="flex-grow-1">
<div class="h6 m-0 text-truncate">
@ -34,7 +34,7 @@
<div class="flex-nowrap no-gutters row">
<a
class="d-flex w-100"
[routerLink]="['/en', 'blog', '2022', '07', 'how-do-i-get-my-finances-in-order']"
[routerLink]="['/blog', '2022', '07', 'how-do-i-get-my-finances-in-order']"
>
<div class="flex-grow-1">
<div class="h6 m-0 text-truncate">
@ -60,7 +60,7 @@
<div class="flex-nowrap no-gutters row">
<a
class="d-flex w-100"
[routerLink]="['/en', 'blog', '2022', '01', 'ghostfolio-first-months-in-open-source']"
[routerLink]="['/blog', '2022', '01', 'ghostfolio-first-months-in-open-source']"
>
<div class="flex-grow-1">
<div class="h6 m-0 text-truncate">
@ -86,7 +86,7 @@
<div class="flex-nowrap no-gutters row">
<a
class="d-flex w-100"
[routerLink]="['/en', 'blog', '2021', '07', 'hello-ghostfolio']"
[routerLink]="['/blog', '2021', '07', 'hello-ghostfolio']"
>
<div class="flex-grow-1">
<div class="h6 m-0 text-truncate">Hello Ghostfolio</div>
@ -110,7 +110,7 @@
<div class="flex-nowrap no-gutters row">
<a
class="d-flex w-100"
[routerLink]="['/de', 'blog', '2021', '07', 'hallo-ghostfolio']"
[routerLink]="['/blog', '2021', '07', 'hallo-ghostfolio']"
>
<div class="flex-grow-1">
<div class="h6 m-0 text-truncate">Hallo Ghostfolio</div>

@ -13,7 +13,7 @@
<img
alt="Ghostfol.io Trailer"
class="rounded video"
src="./assets/images/video-preview.jpg"
src="../assets/images/video-preview.jpg"
style="max-width: 100%; width: 40rem"
/>
</a>
@ -183,7 +183,7 @@
href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa"
title="Get Ghostfolio on Google Play"
>
<img alt="Google Play Badge" src="assets/badge-en-google-play.png" />
<img alt="Google Play Badge" src="../assets/badge-en-google-play.png" />
</a>
</div>
</div>

@ -36,15 +36,15 @@
>
<img
class="mr-2"
src="./assets/icons/internet-computer.svg"
src="../assets/icons/internet-computer.svg"
style="height: 0.75rem"
/>
<span i18n>Continue with Internet Identity</span>
</button>
<a class="d-block" href="/api/v1/auth/google" mat-stroked-button
<a class="d-block" href="../api/v1/auth/google" mat-stroked-button
><img
class="mr-2"
src="./assets/icons/google.svg"
src="../assets/icons/google.svg"
style="height: 1rem"
/><span i18n>Continue with Google</span></a
>

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Ghostfol.io</title>
<link rel="canonical" href="https://ghostfol.io/en/" />
<meta name="robots" content="noindex" />
<meta charset="utf-8" />
<meta http-equiv="refresh" content="0; url=https://ghostfol.io/en/" />
</head>
</html>

@ -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

@ -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"
}
],

@ -9,23 +9,19 @@
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/about</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/about/changelog</loc>
<loc>https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/blog</loc>
<loc>https://ghostfol.io/en/about</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio</loc>
<loc>https://ghostfol.io/en/about/changelog</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/demo</loc>
<loc>https://ghostfol.io/en/blog</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
@ -45,27 +41,31 @@
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/faq</loc>
<loc>https://ghostfol.io/en/demo</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/en/faq</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/features</loc>
<loc>https://ghostfol.io/en/features</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/markets</loc>
<loc>https://ghostfol.io/en/markets</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/pricing</loc>
<loc>https://ghostfol.io/en/pricing</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/register</loc>
<loc>https://ghostfol.io/en/register</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
<url>
<loc>https://ghostfol.io/resources</loc>
<loc>https://ghostfol.io/en/resources</loc>
<lastmod>2022-07-29T00:00:00+00:00</lastmod>
</url>
</urlset>

@ -21,7 +21,7 @@
/>
<meta
name="twitter:image"
content="https://www.ghostfol.io/assets/cover.png"
content="https://ghostfol.io/en/assets/cover.png"
/>
<meta
name="twitter:title"
@ -37,10 +37,10 @@
content="Ghostfolio Open Source Wealth Management Software"
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://www.ghostfol.io" />
<meta property="og:url" content="https://ghostfol.io" />
<meta
property="og:image"
content="https://www.ghostfol.io/assets/cover.png"
content="https://ghostfol.io/en/assets/cover.png"
/>
<meta property="og:updated_time" content="2022-05-28T00:00:00+00:00" />
<meta
@ -51,26 +51,26 @@
<link
rel="apple-touch-icon"
sizes="180x180"
href="/assets/apple-touch-icon.png"
href="../assets/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/assets/favicon-32x32.png"
href="../assets/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/assets/favicon-16x16.png"
href="../assets/favicon-16x16.png"
/>
<link rel="manifest" href="/assets/site.webmanifest" />
<link rel="manifest" href="../assets/site.webmanifest" />
</head>
<body>
<gf-root></gf-root>
<script type="module" src="ionicons/ionicons.esm.js"></script>
<script type="module" src="../ionicons/ionicons.esm.js"></script>
<script nomodule="" src="ionicons.js"></script>
<noscript

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -49,6 +49,7 @@ export const DATA_GATHERING_QUEUE_PRIORITY_LOW = Number.MAX_SAFE_INTEGER;
export const DATA_GATHERING_QUEUE_PRIORITY_HIGH = 1;
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
export const DEFAULT_LANGUAGE_CODE = 'en';
export const GATHER_ASSET_PROFILE_PROCESS = 'GATHER_ASSET_PROFILE';
export const GATHER_ASSET_PROFILE_PROCESS_OPTIONS: JobOptions = {

@ -13,7 +13,7 @@
"affected:lint": "nx affected:lint",
"affected:test": "nx affected:test",
"angular": "node --max_old_space_size=32768 ./node_modules/@angular/cli/bin/ng",
"build:all": "nx run api:build:production && nx run client:build:production && yarn replace-placeholders-in-build",
"build:all": "nx run api:build:production && nx run client:build:production --localize && yarn replace-placeholders-in-build",
"build:dev": "nx run api:build && nx run client:build && yarn replace-placeholders-in-build",
"build:storybook": "nx run ui:build-storybook",
"clean": "rimraf dist",
@ -29,6 +29,7 @@
"database:validate": "prisma validate",
"dep-graph": "nx dep-graph",
"e2e": "ng e2e",
"extract-locales": "ng extract-i18n --output-path ./apps/client/src/locales",
"format": "nx format:write",
"format:check": "nx format:check",
"format:write": "nx format:write",
@ -40,7 +41,7 @@
"postinstall": "prisma generate && ngcc --properties es2020 browser module main",
"replace-placeholders-in-build": "node ./replace.build.js",
"start": "node dist/apps/api/main",
"start:client": "ng serve client --hmr -o",
"start:client": "ng serve client --configuration=development-en --hmr -o",
"start:prod": "node apps/api/main",
"start:server": "nx serve api --watch",
"start:storybook": "nx run ui:storybook",

Loading…
Cancel
Save