I’m working on adding multi-language support to my Discord bot using C#. While I know about the .resx approach, I prefer using JSON files for localization. I found a JSON schema for Discord.NET localization but I’m struggling to implement it properly.
My goal is to localize both command elements (names, descriptions, parameters) and regular bot responses using the same system. Here’s a sample JSON structure I want to work with:
the file paths are the biggest pain w/ embedded resources. set your json files to “embedded resource” in visual studio properties - dont use “content”. your culture files need exact naming like CommandTexts.en-US.json or they wont load. watch out tho - if localization breaks, Discord.NET silently falls back to default, so you might miss broken translations.
I’ve used this exact setup in production, and a custom wrapper around JsonLocalizationManager works best. You need a service that handles command localization and general messages separately. Your Program.cs config looks right. Just make sure your JSON files are embedded resources with proper naming like CommandTexts.en-US.json. For general bot responses, I built a separate LocalizationService that loads the same JSON files but gives you methods like GetString(string key, CultureInfo culture, params object[] args). Same JSON structure works for both. Watch out though; Discord.NET’s localization manager expects specific key formats for slash commands. Your JSON keys must match exactly what the framework wants or command registration fails silently. I went with a hybrid approach: command keys use Discord’s format, message keys use my own convention.
Heads up - culture fallbacks can be tricky with this setup. Discord.NET’s JsonLocalizationManager doesn’t handle missing translations well, so you’ll need custom fallback logic. I built a LocalizationHelper class that wraps the manager and automatically falls back to your default culture when keys are missing. Don’t forget to handle guild locale in your interaction handlers. Discord sends the user’s locale in the interaction context, but you have to explicitly use it when grabbing localized strings. Your {0}ms parameter formatting works fine, but watch out for parameter order with different languages. Some translators need to rearrange parameters based on sentence structure. I’d suggest named placeholders instead of positional ones - gives you way more flexibility across languages.