Browse Source

Localize default account name created during user registration

pull/7888/head
Thomas Kaul 1 month ago
parent
commit
60e1a42109
  1. 8
      apps/api/src/app/user/user.controller.ts
  2. 10
      apps/api/src/app/user/user.service.ts
  3. 31
      apps/api/src/helper/language.helper.spec.ts
  4. 16
      apps/api/src/helper/language.helper.ts
  5. 17
      apps/api/src/middlewares/language-redirect.middleware.ts
  6. 2
      apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.component.ts
  7. 7
      libs/common/src/lib/dtos/create-user.dto.ts
  8. 2
      libs/common/src/lib/dtos/index.ts
  9. 5
      libs/ui/src/lib/services/data.service.ts

8
apps/api/src/app/user/user.controller.ts

@ -15,6 +15,7 @@ import {
THROTTLE_SIGNUP_TTL THROTTLE_SIGNUP_TTL
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { import {
CreateUserDto,
DeleteOwnUserDto, DeleteOwnUserDto,
UpdateOwnAccessTokenDto, UpdateOwnAccessTokenDto,
UpdateUserSettingDto UpdateUserSettingDto
@ -147,7 +148,7 @@ export class UserController {
} }
}) })
@UseGuards(CustomThrottlerGuard) @UseGuards(CustomThrottlerGuard)
public async signupUser(): Promise<UserItem> { public async signupUser(@Body() data: CreateUserDto): Promise<UserItem> {
const isUserSignupEnabled = const isUserSignupEnabled =
await this.propertyService.isUserSignupEnabled(); await this.propertyService.isUserSignupEnabled();
@ -158,7 +159,10 @@ export class UserController {
); );
} }
const { accessToken, id, role } = await this.userService.createUser(); const { accessToken, id, role } = await this.userService.createUser({
data: {},
languageCode: data?.languageCode
});
return { return {
accessToken, accessToken,

10
apps/api/src/app/user/user.service.ts

@ -2,6 +2,7 @@ import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.ser
import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service';
import { environment } from '@ghostfolio/api/environments/environment'; import { environment } from '@ghostfolio/api/environments/environment';
import { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed.event'; import { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed.event';
import { getSupportedLanguageCode } from '@ghostfolio/api/helper/language.helper';
import { getRandomString } from '@ghostfolio/api/helper/string.helper'; import { getRandomString } from '@ghostfolio/api/helper/string.helper';
import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment';
import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account';
@ -28,7 +29,6 @@ import { TagService } from '@ghostfolio/api/services/tag/tag.service';
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
DEFAULT_DATE_RANGE, DEFAULT_DATE_RANGE,
DEFAULT_LANGUAGE_CODE,
DEFAULT_LOCALE, DEFAULT_LOCALE,
PROPERTY_API_KEY_GHOSTFOLIO, PROPERTY_API_KEY_GHOSTFOLIO,
PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_READ_ONLY_MODE,
@ -692,9 +692,11 @@ export class UserService {
public async createUser( public async createUser(
{ {
data data,
languageCode
}: { }: {
data: Prisma.UserCreateInput; data: Prisma.UserCreateInput;
languageCode?: string;
} = { data: {} } } = { data: {} }
): Promise<User> { ): Promise<User> {
if (!data.provider) { if (!data.provider) {
@ -714,8 +716,8 @@ export class UserService {
create: { create: {
currency: DEFAULT_CURRENCY, currency: DEFAULT_CURRENCY,
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'myAccount', languageCode: getSupportedLanguageCode(languageCode),
languageCode: DEFAULT_LANGUAGE_CODE // TODO id: 'myAccount'
}) })
} }
}, },

31
apps/api/src/helper/language.helper.spec.ts

@ -0,0 +1,31 @@
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
import {
getLanguageCodeFromHeader,
getSupportedLanguageCode
} from './language.helper';
describe('getLanguageCodeFromHeader', () => {
it('should get the primary language code', () => {
expect(getLanguageCodeFromHeader('de-CH,de;q=0.9,en;q=0.8')).toEqual('de');
});
it('should use the default language code if the header is missing', () => {
expect(getLanguageCodeFromHeader()).toEqual(DEFAULT_LANGUAGE_CODE);
});
it('should use the default language code if the language is not supported', () => {
expect(getLanguageCodeFromHeader('xx')).toEqual(DEFAULT_LANGUAGE_CODE);
});
});
describe('getSupportedLanguageCode', () => {
it('should return a supported language code', () => {
expect(getSupportedLanguageCode('de')).toEqual('de');
});
it('should use the default language code if the language is not supported', () => {
expect(getSupportedLanguageCode()).toEqual(DEFAULT_LANGUAGE_CODE);
expect(getSupportedLanguageCode('xx')).toEqual(DEFAULT_LANGUAGE_CODE);
});
});

16
apps/api/src/helper/language.helper.ts

@ -0,0 +1,16 @@
import {
DEFAULT_LANGUAGE_CODE,
SUPPORTED_LANGUAGE_CODES
} from '@ghostfolio/common/config';
export function getLanguageCodeFromHeader(acceptLanguage?: string): string {
const languageCode = acceptLanguage?.split(',')[0].split('-')[0];
return getSupportedLanguageCode(languageCode);
}
export function getSupportedLanguageCode(languageCode?: string): string {
return (SUPPORTED_LANGUAGE_CODES as readonly string[]).includes(languageCode)
? languageCode
: DEFAULT_LANGUAGE_CODE;
}

17
apps/api/src/middlewares/language-redirect.middleware.ts

@ -1,8 +1,5 @@
import { environment } from '@ghostfolio/api/environments/environment'; import { environment } from '@ghostfolio/api/environments/environment';
import { import { getLanguageCodeFromHeader } from '@ghostfolio/api/helper/language.helper';
DEFAULT_LANGUAGE_CODE,
SUPPORTED_LANGUAGE_CODES
} from '@ghostfolio/common/config';
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { StatusCodes } from 'http-status-codes'; import { StatusCodes } from 'http-status-codes';
@ -20,15 +17,9 @@ export function languageRedirectMiddleware(
return next(); return next();
} }
let languageCode = DEFAULT_LANGUAGE_CODE; const languageCode = getLanguageCodeFromHeader(
request.headers['accept-language']
try { );
const code = request.headers['accept-language'].split(',')[0].split('-')[0];
if ((SUPPORTED_LANGUAGE_CODES as readonly string[]).includes(code)) {
languageCode = code;
}
} catch {}
return response.redirect( return response.redirect(
StatusCodes.MOVED_PERMANENTLY, StatusCodes.MOVED_PERMANENTLY,

2
apps/client/src/app/pages/register/user-account-registration-dialog/user-account-registration-dialog.component.ts

@ -76,7 +76,7 @@ export class GfUserAccountRegistrationDialogComponent {
protected createAccount() { protected createAccount() {
this.dataService this.dataService
.postUser() .postUser({ languageCode: document.documentElement.lang })
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(({ accessToken, authToken, role }) => { .subscribe(({ accessToken, authToken, role }) => {
this.accessToken = accessToken; this.accessToken = accessToken;

7
libs/common/src/lib/dtos/create-user.dto.ts

@ -0,0 +1,7 @@
import { IsOptional, IsString } from 'class-validator';
export class CreateUserDto {
@IsOptional()
@IsString()
languageCode?: string;
}

2
libs/common/src/lib/dtos/index.ts

@ -10,6 +10,7 @@ import { CreateAssetProfileDto } from './create-asset-profile.dto';
import { CreateOrderDto } from './create-order.dto'; import { CreateOrderDto } from './create-order.dto';
import { CreatePlatformDto } from './create-platform.dto'; import { CreatePlatformDto } from './create-platform.dto';
import { CreateTagDto } from './create-tag.dto'; import { CreateTagDto } from './create-tag.dto';
import { CreateUserDto } from './create-user.dto';
import { CreateWatchlistItemDto } from './create-watchlist-item.dto'; import { CreateWatchlistItemDto } from './create-watchlist-item.dto';
import { DeleteOwnUserDto } from './delete-own-user.dto'; import { DeleteOwnUserDto } from './delete-own-user.dto';
import { HoldingDto } from './holding.dto'; import { HoldingDto } from './holding.dto';
@ -43,6 +44,7 @@ export {
CreateOrderDto, CreateOrderDto,
CreatePlatformDto, CreatePlatformDto,
CreateTagDto, CreateTagDto,
CreateUserDto,
CreateWatchlistItemDto, CreateWatchlistItemDto,
DeleteOwnUserDto, DeleteOwnUserDto,
HoldingDto, HoldingDto,

5
libs/ui/src/lib/services/data.service.ts

@ -4,6 +4,7 @@ import {
CreateAccountDto, CreateAccountDto,
CreateOrderDto, CreateOrderDto,
CreateTagDto, CreateTagDto,
CreateUserDto,
CreateWatchlistItemDto, CreateWatchlistItemDto,
DeleteOwnUserDto, DeleteOwnUserDto,
TransferBalanceDto, TransferBalanceDto,
@ -896,8 +897,8 @@ export class DataService {
return this.http.post<Tag>(`/api/v1/tags`, aTag); return this.http.post<Tag>(`/api/v1/tags`, aTag);
} }
public postUser() { public postUser(aData: CreateUserDto) {
return this.http.post<UserItem>('/api/v1/user', {}); return this.http.post<UserItem>('/api/v1/user', aData);
} }
public postWatchlistItem(watchlistItem: CreateWatchlistItemDto) { public postWatchlistItem(watchlistItem: CreateWatchlistItemDto) {

Loading…
Cancel
Save