Skip to main content

Localization

Is a lightweight localization module for NestCord. It allows you to easily localize your bot's commands and messages. The module provides a simple API for managing locales and translations, as well as a powerful localization adapter system.

Usage​

Once the installation process is complete, we can import the NestCordLocalizationModule with your NestCordModule into the root AppModule:

import { NestCordModule } from '@globalart/nestcord';
import { Module } from '@nestjs/common';
import { NestCordLocalizationModule, DefaultLocalizationAdapter, UserResolver } from '@globalart/nestcord';
import { AppService } from './app.service';

@Module({
imports: [
NestCordModule.forRoot({
token: process.env.DISCORD_TOKEN,
intents: [
IntentsBitField.Flags.Guilds,
IntentsBitField.Flags.DirectMessages,
IntentsBitField.Flags.GuildMembers,
IntentsBitField.Flags.GuildMessages,
IntentsBitField.Flags.MessageContent
],
prefix: '!',
development: [process.env.DISCORD_TEST_GUILD]
}),
NestCordLocalizationModule.forRoot({
resolvers: UserResolver,
// Also you can provide class for support injection by @Inject
adapter: new DefaultLocalizationAdapter({
fallbackLocale: 'en-US',
locales: {
'en-US': {
'commands.ping.name': 'ping',
'commands.ping.description': 'Pong!'
},
ru: {
'commands.ping.name': 'пинг',
'commands.ping.description': 'Понг!'
}
}
})
})
],
providers: [AppService]
})
export class AppModule {
}

Adapters​

The DefaultLocalizationAdapter is a simple adapter that allows you to provide a map of locales and translations.

Also you can use the NestedLocalizationAdapter that allows you to organize translation keys into objects

import { NestCordLocalizationModule, NestedLocalizationAdapter, UserResolver } from '@globalart/nestcord/localization';

NestCordLocalizationModule.forRoot({
resolvers: UserResolver,
adapter: new NestedLocalizationAdapter({
fallbackLocale: 'en-US',
locales: {
'en-US': {
'commands': {
'ping': {
'name': 'ping',
'description': 'Pong!'
}
}
},
ru: {
'commands': {
'ping': {
'name': 'пинг',
'description': 'Понг!'
}
}
}
}
})
})

info

DefaultLocalizationAdapter and NestedLocalizationAdapter can translate your localization strings and placeholders (e.g {{username}})

Custom Adapters​

Also, you can create your own localization adapter. Just implement the BaseLocalizationAdapter interface:

import { BaseLocalizationAdapter } from '@globalart/nestcord/localization';

interface CustomLocalizationOptions {
fallbackLocale: string;
locales: Record<string, Record<string, string>>;
}

export class CustomLocalizationAdapter extends BaseLocalizationAdapter<CustomLocalizationOptions> {
public getTranslation(key: string, locale: string, ...args: unknown[]): string {
return `${key} by ${locale}`;
}
}

Resolvers​

Resolvers are used to get the locale for translation. By default, NestCord provides two resolvers: UserResolver and GuildResolver.

ResolverDescription
UserResolverGets the locale from the user's locale property (interaction.locale)
GuildResolverGets the locale from the guild's locale property (interaction.guildLocale)

Custom Resolvers​

Also, you can create your own Resolver. Just implement the LocaleResolver interface:

import { CommandContext, LocaleResolver } from '@globalart/nestcord';
import { ExecutionContext, Injectable } from '@nestjs/common';
import { NestCordExecutionContext } from '@globalart/nestcord';

@Injectable()
export class GuildResolver implements LocaleResolver {
resolve(context: ExecutionContext): string | string[] | undefined {
const nestcordContext = NestCordExecutionContext.create(context);
const [interaction] = nestcordContext.getContext<CommandContext>();

return interaction.guildLocale;
}
}

Localization​

We can inject the LOCALIZATION_ADAPTER into our service and use it to localize our commands and messages:

src/app.gateway.ts
import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
import { DefaultLocalizationAdapter, localizationMapByKey, LOCALIZATION_ADAPTER } from '@globalart/nestcord';
import { Context, SlashCommand, SlashCommandContext } from '@globalart/nestcord';

@Injectable()
export class AppService implements OnModuleInit {
public constructor(
@Inject(LOCALIZATION_ADAPTER)
private readonly localizationAdapter: DefaultLocalizationAdapter
) {
}

@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: localizationMapByKey('commands.ping.name'),
descriptionLocalizations: localizationMapByKey('commands.ping.name')
})
public ping(@Context() [interaction]: SlashCommandContext) {
const message = this.localizationAdapter.getTranslation(
'commands.ping.description',
interaction.locale
);
return interaction.reply(message);
}
}

Or you can use translate function from the localization adapter:

src/app.gateway.ts
import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
import { DefaultLocalizationAdapter, localizationMapByKey, LOCALIZATION_ADAPTER } from '@globalart/nestcord';
import { Context, SlashCommand, SlashCommandContext } from '@globalart/nestcord';

@Injectable()
export class AppService implements OnModuleInit {
public constructor(
@Inject(LOCALIZATION_ADAPTER)
private readonly localizationAdapter: DefaultLocalizationAdapter
) {
}

@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: localizationMapByKey('commands.ping.name'),
descriptionLocalizations: localizationMapByKey('commands.ping.name')
})
public ping(@Context() [interaction]: SlashCommandContext) {
const message = this.localizationAdapter.translate(
'commands.ping.description',
);
return interaction.reply(message);
}
}

Or you can use @CurrentTranslate decorator to get the current translation from context:

import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
import { DefaultLocalizationAdapter, CurrentTranslate, localizationMapByKey, TranslationFn } from '@globalart/nestcord/localization';
import { Context, SlashCommand, SlashCommandContext } from '@globalart/nestcord';

@Injectable()
export class AppService implements OnModuleInit {
@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: localizationMapByKey('commands.ping.name'),
descriptionLocalizations: localizationMapByKey('commands.ping.name')
})
public ping(
@Context() [interaction]: SlashCommandContext,
@CurrentTranslate() t: TranslationFn
) {
const message = t('commands.ping.description');
return interaction.reply(message);
}
}
info

Function localizationMapByKey are used to localize the command name and description. You pass the translation key or localization map as an argument to the function.

Setting up localized commands​

You can set what locales the command will be localized

@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: localizationMapByKey('commands.ping.name', ['en', 'ru']),
descriptionLocalizations: localizationMapByKey('commands.ping.name', ['en', 'ru'])
})

Or just pass a localization object with the location id and translation key to the nameLocalization and descriptionLocalizations properties

@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: {
en: 'command.ping.name',
ru: 'command.ping.name'
},
descriptionLocalizations: {
en: 'command.ping.description',
ru: 'command.ping.description'
}
})

Update localization without restarting the app​

If you get translations from the external API, for example, crowdin, weblate, or another translation services. You can update it just call the localizationService

import {
Context,
NestCordLocalizationService,
SlashCommand,
SlashCommandContext,
} from '@globalart/nestcord';

export class AppService {
constructor(
private readonly nestCordLocalizationService: NestCordLocalizationService,
) {}

@SlashCommand({
name: 'update_translations',
description: 'Update translations',
})
updateTranslations(@Context() [interaction]: SlashCommandContext) {
// Get data from API

this.nestCordLocalizationService.updateLocales({
// static translates for test or you can put it from API
'en-US': {
'commands.ping.description': 'My new translation',
},
});

return interaction.reply({ content: "Updated successful" });
}
}

Congratulations! You have successfully created your first localized command with NestCord!