Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,16 @@ import { I18nContext, I18nService } from 'nestjs-i18n';
export class AppService {
constructor(private readonly i18n: I18nService) {}
getHello(): string {
return this.i18n.t('test.HELLO',{ lang: I18nContext.current().lang });
return this.i18n.t('test.HELLO');
}

getHelloInSpecificLanguage(): string {
return this.i18n.t('test.HELLO',{ lang: "en" });
}
}
```


## Translate options

```typescript
Expand Down
9 changes: 7 additions & 2 deletions src/services/i18n.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import {
take,
takeUntil,
} from 'rxjs';
import { I18nOptions, I18nTranslation, I18nValidationError } from '..';
import {
I18nContext,
I18nOptions,
I18nTranslation,
I18nValidationError,
} from '..';
import {
I18N_LANGUAGES,
I18N_LANGUAGES_SUBJECT,
Expand Down Expand Up @@ -75,7 +80,7 @@ export class I18nService<K = Record<string, unknown>>
options?: TranslateOptions,
): IfAnyOrNever<R, string, R> {
options = {
lang: this.i18nOptions.fallbackLanguage,
lang: I18nContext.current()?.lang || this.i18nOptions.fallbackLanguage,
...options,
};

Expand Down
8 changes: 8 additions & 0 deletions tests/app/cats/cat.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ export class CatResolver {
return cat;
}

@Query('catUsingService')
async getCatUsingService(@Args('id') id: number) {
const cat = await this.catService.findById(id);
// we manually overwrite this property to indicate a value that is translated!
cat.description = this.i18nService.translate('test.cat');
return cat;
}

@Mutation('createCat')
async create(@Args('createCatInput') args: CreateCatInput): Promise<any> {
await this.pubSub.publish('catAdded', { catAdded: args.name });
Expand Down
3 changes: 2 additions & 1 deletion tests/app/cats/cat.types.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Query {
cats: [Cat]
cat(id: Int!): Cat
catUsingContext(id: Int!): Cat
catUsingService(id: Int!): Cat
}

type Mutation {
Expand All @@ -23,4 +24,4 @@ type Subscription {
input CreateCatInput {
name: String
age: Int
}
}
29 changes: 28 additions & 1 deletion tests/app/controllers/hello.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import { CreateUserDto } from '../dto/create-user.dto';
import { TestException, TestExceptionFilter } from '../filter/test.filter';
import { TestGuard } from '../guards/test.guard';
import { Hero, HeroById } from '../interfaces/hero.interface';
import { exampleErrorFormatter, exampleResponseBodyFormatter } from '../examples/example.functions';
import {
exampleErrorFormatter,
exampleResponseBodyFormatter,
} from '../examples/example.functions';
import { TestInterceptor } from '../interceptors/test.interceptor';

@Controller('hello')
Expand All @@ -35,6 +38,11 @@ export class HelloController {
return this.i18n.translate('test.HELLO', { lang });
}

@Get('/no-lang-for-service')
helloNoLangForService(): any {
return this.i18n.translate('test.HELLO');
}

@Get('/typed')
helloTyped(@I18nLang() lang: string): string {
return this.i18n.translate('test.HELLO', { lang });
Expand All @@ -59,6 +67,11 @@ export class HelloController {
return this.i18n.t('test.HELLO', { lang });
}

@Get('/short/no-lang-for-service')
helloShortNoLangForService(): any {
return this.i18n.t('test.HELLO');
}

@Get('/short/typed')
helloShortTyped(@I18nLang() lang: string): string {
return this.i18n.t('test.HELLO', { lang });
Expand Down Expand Up @@ -228,4 +241,18 @@ export class HelloController {
];
return items.find(({ id }) => id === data.id);
}

@GrpcMethod('HeroesService', 'FindOneTranslatedWithService')
findOneTranslatedWithService(@Payload() data: HeroById): Hero {
const items = [
{
id: 1,
name: this.i18n.t('test.set-up-password.heading', {
args: { username: 'John' },
}),
},
{ id: 2, name: 'Doe' },
];
return items.find(({ id }) => id === data.id);
}
}
1 change: 1 addition & 0 deletions tests/app/hero.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package hero;

service HeroesService {
rpc FindOne (HeroById) returns (Hero) {}
rpc FindOneTranslatedWithService (HeroById) returns (Hero) {}
}

message HeroById {
Expand Down
4 changes: 4 additions & 0 deletions tests/app/interfaces/hero.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ export interface HeroById {

export interface HeroService {
findOne(data: HeroById, metadata: Metadata): Observable<Hero>;
findOneTranslatedWithService(
data: HeroById,
metadata: Metadata,
): Observable<Hero>;
}
76 changes: 76 additions & 0 deletions tests/i18n-express.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,82 @@ describe('i18n module e2e express', () => {
.expect({ lang: 'nl' });
});

it(`/GET hello/no-lang-for-service should return fallback language`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.expect(200)
.expect('Hello');
});

it(`/GET hello/no-lang-for-service should return translation when providing query resolver`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service?lang=nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/no-lang-for-service should return translation when providing x-custom-lang`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.set('x-custom-lang', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/no-lang-for-service should return translation when providing accept-language`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.set('accept-language', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/no-lang-for-service should return translation when providing cookie`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.set('Cookie', ['lang=nl'])
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return fallback language`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.expect(200)
.expect('Hello');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing query resolver`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service?lang=nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing x-custom-lang`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.set('x-custom-lang', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing accept-language`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.set('accept-language', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing cookie`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.set('Cookie', ['lang=nl'])
.expect(200)
.expect('Hallo');
});

it('/POST cats with age 2 should error', async () => {
await request(app.getHttpServer())
.post('/cats')
Expand Down
76 changes: 76 additions & 0 deletions tests/i18n-fastify.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,82 @@ describe('i18n module e2e fastify', () => {
.expect({ lang: 'nl' });
});

it(`/GET hello/no-lang-for-service should return fallback language`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.expect(200)
.expect('Hello');
});

it(`/GET hello/no-lang-for-service should return translation when providing query resolver`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service?lang=nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/no-lang-for-service should return translation when providing x-custom-lang`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.set('x-custom-lang', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/no-lang-for-service should return translation when providing accept-language`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.set('accept-language', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/no-lang-for-service should return translation when providing cookie`, () => {
return request(app.getHttpServer())
.get('/hello/no-lang-for-service')
.set('Cookie', ['lang=nl'])
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return fallback language`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.expect(200)
.expect('Hello');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing query resolver`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service?lang=nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing x-custom-lang`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.set('x-custom-lang', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing accept-language`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.set('accept-language', 'nl')
.expect(200)
.expect('Hallo');
});

it(`/GET hello/short/no-lang-for-service should return translation when providing cookie`, () => {
return request(app.getHttpServer())
.get('/hello/short/no-lang-for-service')
.set('Cookie', ['lang=nl'])
.expect(200)
.expect('Hallo');
});

it('/POST cats with age 2 should error', async () => {
await request(app.getHttpServer())
.post('/cats')
Expand Down
83 changes: 83 additions & 0 deletions tests/i18n-gql.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,89 @@ describe('i18n module e2e graphql', () => {
});
});

it(`should query a particular cat (using injected I18nService) in fallback language`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: '{catUsingService(id:2){id,name,age,description}}',
})
.expect(200, {
data: {
catUsingService: {
id: 2,
name: 'bar',
age: 6,
description: 'Cat',
},
},
});
});

it(`should query a particular cat (using injected I18nService) in NL with x-custom-lang header`, () => {
return request(app.getHttpServer())
.post('/graphql')
.set('x-custom-lang', 'nl')
.send({
operationName: null,
variables: {},
query: '{catUsingService(id:2){id,name,age,description}}',
})
.expect(200, {
data: {
catUsingService: {
id: 2,
name: 'bar',
age: 6,
description: 'Kat',
},
},
});
});

it(`should query a particular cat (using injected I18nService) in NL with cookie`, () => {
return request(app.getHttpServer())
.post('/graphql')
.set('Cookie', ['lang=nl'])
.send({
operationName: null,
variables: {},
query: '{catUsingService(id:2){id,name,age,description}}',
})
.expect(200, {
data: {
catUsingService: {
id: 2,
name: 'bar',
age: 6,
description: 'Kat',
},
},
});
});

it(`should query a particular cat (using injected I18nService) in NL with accept-language header`, () => {
return request(app.getHttpServer())
.post('/graphql')
.set('accept-language', 'nl')
.send({
operationName: null,
variables: {},
query: '{catUsingService(id:2){id,name,age,description}}',
})
.expect(200, {
data: {
catUsingService: {
id: 2,
name: 'bar',
age: 6,
description: 'Kat',
},
},
});
});

afterAll(async () => {
apollo.stop();
await subscriptionClient.dispose();
Expand Down
Loading