
Discord Game Integration Bot: Through Translations and Timezones
I’ve been building Discord bots for years. Custom ones, weird ones, some that handle thousands of users daily. But I had never built a real Discord game integration before. When a game studio needed exactly that, I jumped at the opportunity. What could go wrong?
A Simple Request
Strinova is an anime-style tactical shooter where your character can flatten into a 2D paper form mid-combat. It’s as weird and cool as it sounds. When they decided to set up a Discord server, they reached out to Paragon, my client, the company behind the official VALORANT Discord servers.
As Paragon was just getting started helping with configuration and moderation, a bot came up in the conversation. My ears perked up. Then they said they’d build it themselves. Cool. Fine. Totally fine. And then, they realized they wouldn’t have the time.
So they asked me. I had never been so happy to be someone’s plan B ✨!
They wanted a bot that could pull data from the game and bring it into Discord. Player stats, achievements, account linking. A bridge between the two. I had never built anything that talked to a game’s API before, and the idea alone made my brain light up. We started talking with the Strinova team, ready to hash out the details, and that’s when reality introduced itself.
Lost in Translation (Literally)
The first thing I learned is that the Strinova team didn’t speak English. I asked if they spoke French by any chance. No luck. They were based in Hong Kong and mostly spoke Chinese. Some of them could manage some English, but it was still hard to understand each other.
At first we communicated by text, with an interpreter relaying between us. We thought that would solve it. But after a few technical exchanges that were left completely misunderstood by the other party, we figured out that the interpreter had no technical background whatsoever. They were translating words, not meaning. It wasn’t helping.
So we moved to Lark. Lark is basically the eastern-asian equivalent of Teams, except for two things. First, it can automatically translate what people say into your preferred language, whether it’s text or voice. I don’t know if Teams can do that, but I used Teams once, briefly, a long time ago, which obviously makes me an expert, so I can confidently declare that Teams does NOT do that. Second, Lark is actually good. Like, genuinely pleasant to use. Could you say the same about Teams without feeling like you’re defending an unpopular opinion? Exactly.
With voice calls and automatic translation, we could finally get to the real conversations. Which came with their own surprises.
The Bot That Didn’t Exist Yet
With the language barrier mostly solved, we started discussing the actual project. I was expecting them to hand me technical requirements and limitations. Which they did, but only to a point. More often than not, they were asking me how they should architecture their side of the project so it would work with my bot.
I don’t think they understood that the bot didn’t exist yet. It was just an idea at that point.
I’m still not entirely sure that’s what was happening, honestly. It’s just how their answers made me feel. But we kept talking, going back and forth on the details of each side’s implementation, and eventually I had a clear enough picture to start building something.
The plan was a bot that would interface with the game through OAuth. Well, a private OAuth, so really more Auth than O. The bot would provide a button in Discord. When clicked, it would open your browser and prompt you to log into the game’s portal. After logging in, it would redirect you to a page managed by the bot, which would gather your in-game data and signal back to Discord that everything worked.
Getting there was hell. The person working on the auth portal was not the same person working on the data interface, and they had to coordinate. They were not great at that, but to be fair, coordinating across teams can be hard even when you speak the same language. But eventually, data started flowing, and I was excited all over again.

…well. “Flowing” is generous. It was more like a puddle. The same puddle. Every time.
Schrödinger’s Data
The test data I was receiving was very fixed. Same values, same structure, same everything. I was technically testing my bot, but in practice I was only ever testing one very specific case. So I asked if I could get access to more varied data, or maybe an anonymized copy of production data that would reflect the real world. They couldn’t do that, but they could connect my bot to an environment linked to a dev build of the game. That was a pretty exciting win!
Pretty quickly though, I realized how different this new data was. That’s when it hit me. I could see that the second environment’s data was very different from the first one’s, but I still had no idea what production data actually looked like. Maybe it was a third flavour of very different. Maybe it was similar to the first environment. Hopefully it was similar to the second one.
I started coding with a bit more modularity in mind. I wanted my reliance on the game’s data format to be as light as possible, so that if production surprised me again, it wouldn’t be a nightmare to adapt. Since I was building all of this in TypeScript, that meant a whole lot of abstract interfaces. But honestly? It was fun.
But what came next was about as fun as watching a corporate training video while filing your taxes.
The Translation Trauma Arc
I kept pushing, building features, handling error cases. And then I realized I had no translations whatsoever. There were strings coming from the game in English or Chinese depending on the configuration, but no translation keys, nothing structured. I was going to have to dive into internationalization.
And I was terrified. Let me explain why.
A Trip Down Memory Pain
This is a bit of a tangent, but it matters. On Discord, I own a server about learning French. I didn’t create it, someone gave it to me, but I’ve been running it for years. discord.gg/french, even the URL shows it’s a big one. It has a custom bot called Nostradamus that’s been on the server since day one. When someone asked me to come fix it because it had crashed, I did. That was Nostradamus v1. Then I rewrote it because the code was simplistic. v2. Then I reworked the architecture to support more features. v3. The v3 is still alive today.
But a few years ago, I wanted to upgrade it again. Make it more robust, more strict. It was plain JavaScript, so I started a v4 in TypeScript. It was one of my first TypeScript projects. I tried installing it myself, it wasn’t great. I tried a framework we were using at work for a totally different purpose, and it felt structured at first but quickly turned into red tape. I was fighting the framework to make a Discord bot. Then I tried a translation library and understood absolutely nothing about it. I was mentally exhausted and burnt out, so I stopped.
Later, I heard about a TypeScript framework made specifically for Discord bots. It felt like a sign. I installed it, and they even had their own translation library. I pushed through, tried to configure it, and hit a wall again. I looked into it and realized it was just a wrapper around the same translation library I had already failed to understand. I stopped Nostradamus v4 and never went back to it.
Back to the Present
So when translations came up for the Strinova bot, you can probably understand why I was terrified.
But I also felt like I had grown since then. Not just as a developer, but in how I approached things. The first time around, I had a very specific idea of how a translation library should work in a Discord bot, and when the reality didn’t match, I got confused. Without realizing it, that confusion turned into a bias. “This library is a disaster.” And once you believe that, everything you read confirms it.
This time, I went in with no expectations. Well, almost. Old habits die hard. But every time I caught myself thinking “this doesn’t make sense”, I took a breath and kept reading. The library was still confusing, but it wasn’t the enemy anymore. I installed it, configured it, and it worked. I had conquered i18next, and I was so proud.

The hardest part was behind me. All I had to do now was populate the translation files. How bad could that be?
40,000 Shades of Bad
I started adding translations and quickly ran into two categories of problems. For bot-related strings, we simply had nothing. No translations existed at all, so we translated everything with DeepL. For game-related strings, the issue was different: I was receiving the strings directly, and they told me to just map them to their translated equivalents. I refused. If the game ever changed a string, even by a single character, my mapping would break. I needed IDs or slugs to build proper translation keys.
They updated the API to include them. Win! 🎉
Then I asked for the actual translations for everything the API was sending me. They gave me an Excel file. I started downloading it and noticed it was taking a while. Figured the servers were just slow. Then I opened it and my computer froze. I was starting to think something was wrong with this file. I checked its size and understood.
Once it finally loaded, I looked at the number of rows. Around 40,000 lines of translations. Each language in its own column. Well… almost. Japanese and Korean were missing. I asked about them and received a second file.

I wrote a quick script to extract the lines I needed from those spreadsheets. It crashed. Turns out Node.js has RAM limitations and a 30,000-line Excel file will happily remind you of that. Once I worked around that, it was time for a lot of copying and pasting! Between the translation files and the spreadsheets, I was starting to feel more like a secretary than a developer. Surely the document work was over and I could get back to writing code.
The (Boring) Elder Scrolls
Around that time, they asked us, well, me, to fill out a ton of paperwork so they could get the authorizations to spin up a production server.
At first, I filled them out like they were sacred, like making a single mistake would tear the very fabric of reality apart. Then they received my first version and I noticed they were almost as confused as me about what to do with them. That’s when I understood: this paperwork was required by upper management, not by the team I was working with. It was just about filling them out to the best of my abilities, and if something was wrong, we could fix it later.
So I filled out a second version and it was apparently perfect. Then they asked for more. I asked how much more. They pulled out an entire folder full of documents.
I went to my client with a big smile on my face, which in corporate language roughly translates to a cry for help. Totally unrelated, of course, the boss of the team that had been asking me to fill out the paperwork joined the conversation, said we didn’t need all of that and to just proceed, and left.
My client is amazing. He knows how to talk to corporations. At least I was done with documents. Right?
Ship and Pray
Not quite. There were still documents to write, just not the corporate kind. When we finally got the production servers delivered, I asked for access and got declined. No SSH, no logs, no way to restart the bot. If something broke, I wouldn’t even know.
I asked them: you’ll deal with all of that? They said yes. Do you have people who know how to run it? They said no, but that’s why I needed to write full documentation. Not just how to deploy, but everything. Which OS, which distribution, which version. What software to install. How to configure it. How to restart the bot, how to maintain it. Basically a Dockerfile, but for humans.
Understanding that I would never see the terminal of this production server, I wrote the documentation.
Keep in mind that since their team was based in Hong Kong and I was in France, the timezones meant I was working on features during the day, and during the night I was getting pinged about bugs, paperwork, architecture questions. It was almost a 24/7 gig.
My partner was noticing how much I was putting in. Between the features, the bugs, the timezone gap and the paperwork, the project had grown well beyond what any of us had anticipated. But I was building something I’d never built before, and I was excited to see it through.
Eventually, I finished the bot enough for it to be stable and ready to ship. We deployed to production, tested, a few things didn’t work, I fixed them, we tested some more. It was another round of sleepless but very pingful nights. And then, one morning, I woke up to a very different kind of ping.
The Launch I Slept Through
They had picked a date and, thanks to my documentation being thorough enough for them to feel confident, announced the bot while I was asleep. Hong Kong daytime, middle of the night for me. I woke up, checked my phone, and braced for disaster.
Nothing. No crash, no panicked pings, no catastrophic failure. It just… worked. Flawlessly. I’m not saying there was nothing to fix down the line, but launch day? Smooth.

After months of language barriers, data mysteries, translation spreadsheets, corporate paperwork, and timezone whiplash, the bot held. And I wasn’t even awake to see it.
Oh, one last thing. I’ve always been bad at naming bots. This one I named GIB. I thought it was cute, and it stood for Game Integration Bot. They renamed it Dorm on the server, but since the bot lives on my Discord account, they couldn’t change the username. So if you look closely, right under the fancy Dorm nickname? Gib#1858. Still there ✨





