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
- Yarn
- pnpm
- Bun
npm i @necord/localization necord discord.js
yarn add @necord/localization necord discord.js
pnpm add @necord/localization necord discord.js
bun add @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 {
}
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.
| Resolver | Description |
|---|---|
| UserResolver | Gets the locale from the user's locale property (interaction.locale) |
| GuildResolver | Gets 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);
}
}
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.