Skip to main content

Localization

@necord/localization is a lightweight localization module for Necord. 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.

Installation​

npm i @necord/localization necord discord.js

Usage​

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

import { NecordModule } from 'necord';
import { Module } from '@nestjs/common';
import { NecordLocalizationModule, DefaultLocalizationAdapter, UserResolver } from '@necord/localization';
import { IntentsBitField } from 'discord.js';
import { AppService } from './app.service';

@Module({
imports: [
NecordModule.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!]
}),
NecordLocalizationModule.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 { NecordModule } from 'necord';
import { Module } from '@nestjs/common';
import { NecordLocalizationModule, NestedLocalizationAdapter, UserResolver } from '@necord/localization';
import { IntentsBitField } from 'discord.js';
import { AppService } from './app.service';

@Module({
imports: [
NecordModule.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!]
}),
NecordLocalizationModule.forRoot({
resolvers: UserResolver,
adapter: new NestedLocalizationAdapter({
fallbackLocale: 'en-US',
locales: {
'en-US': {
'commands': {
'ping': {
'name': 'ping',
'description': 'Pong!'
}
}
},
ru: {
'commands': {
'ping': {
'name': 'пинг',
'description': 'Понг!'
}
}
}
}
})
})
],
providers: [AppService]
})
export class AppModule {
}
info

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

Custom Adapters​

Also, you can create your own localization adapter by extending the abstract BaseLocalizationAdapter class:

import { BaseLocalizationAdapter } from '@necord/localization';

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

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

Resolvers​

Resolvers are used to get the locale for translation. By default, Necord 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 '@necord/localization';
import { ExecutionContext, Injectable } from '@nestjs/common';
import { NecordExecutionContext } from 'necord';

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

return interaction.guildLocale ?? undefined;
}
}

Localization​

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

import { Inject, Injectable } from '@nestjs/common';
import { DefaultLocalizationAdapter, localizationMapByKey, LOCALIZATION_ADAPTER } from '@necord/localization';
import { Context, SlashCommand, SlashCommandContext } from 'necord';

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

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

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

import { Injectable } from '@nestjs/common';
import { CurrentTranslate, localizationMapByKey, TranslationFn } from '@necord/localization';
import { Context, SlashCommand, SlashCommandContext } from 'necord';

@Injectable()
export class AppService {
@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: localizationMapByKey('commands.ping.name'),
descriptionLocalizations: localizationMapByKey('commands.ping.description')
})
public async ping(
@Context() [interaction]: SlashCommandContext,
@CurrentTranslate() t: TranslationFn
): Promise<void> {
const message = t('commands.ping.description');
await 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

import { localizationMapByKey } from '@necord/localization';
import { SlashCommand } from 'necord';

export class AppService {
@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: localizationMapByKey('commands.ping.name', ['en-US', 'ru']),
descriptionLocalizations: localizationMapByKey('commands.ping.description', ['en-US', 'ru'])
})
public ping(): void {}
}

Or just pass a localization object with the locale and translation key to the nameLocalizations and descriptionLocalizations properties

import { SlashCommand } from 'necord';

export class AppService {
@SlashCommand({
name: 'ping',
description: 'Pong!',
nameLocalizations: {
'en-US': 'commands.ping.name',
ru: 'commands.ping.name'
},
descriptionLocalizations: {
'en-US': 'commands.ping.description',
ru: 'commands.ping.description'
}
})
public ping(): void {}
}

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

You can view a working example here.