Automatically Switch Languages Based on IP — Devlog
A few days ago, I started working on an idea that came up in the Flarum community: automatically switching a visitor’s language based on their IP address and browser language.
#39699 @Charity @Karl @Silvestrus @devnull @Tutrix @Wlork
I’m finally at the point where I can show you what I’ve built.
And honestly, I think the result turned out much better than I initially expected.

What does it do?
The extension automatically determines a visitor’s preferred Flarum locale using two sources:
- Browser language preferences
- IP-based country detection
The IP detection is handled entirely by the extension itself. There is no third-party API request to an external IP geolocation service.
It supports both IPv4 and IPv6, with the IP-to-country resolution performed locally using optimized IP range data and binary search.
That means there is no external API latency on the critical request path, and the visitor's IP does not need to be sent to a third-party geolocation service just to determine their country.
Based on the detected country and browser language, the extension can automatically select the appropriate language for the visitor.

Native integration with Flarum's localization flow
This is probably the part I’m most excited about.
I didn't want language detection to feel like a post-boot hack, a client-side language switch, or an after-the-fact redirect. I wanted it to become part of Flarum's existing localization flow.
The extension does this by placing a lightweight HTTP middleware directly before Flarum's core SetLocale middleware using insertBefore(SetLocale::class, ...).
This allows the visitor's preferred locale to be resolved before Flarum's core SetLocale middleware finalizes the locale for the request.
So instead of:
Flarum selects the fallback locale → extension tries to change it afterwards
the flow becomes:
Visitor request → extension detects the preferred locale → Flarum's SetLocale receives the detected locale → Flarum continues with that locale
The important part is that the extension isn't trying to replace Flarum's localization system.
It works with it.
The detected locale becomes the actual request locale that Flarum natively serves, rather than something that has to be corrected later on the frontend.
And because the detection is performed locally by the extension, there is no additional third-party API request blocking the request.
That was one of the main architectural goals of the project.
Visitor and country statistics
The extension can also collect statistics about the countries and IP addresses of visitors.

This gives administrators a much clearer picture of where their community is actually coming from.
You can see which countries are visiting your forum and use that information to make better decisions about language support.
The tracking is configurable, so administrators can decide whether they want to collect these statistics.
Missing languages
One of the other things I wanted to solve was the problem of discovering languages that your community might need without having to guess.

The extension can keep track of visitors requesting languages that aren't currently installed on your Flarum forum.
This means you can discover potential language needs from actual visitor traffic rather than simply relying on assumptions.
Settings

The settings page brings the main configuration options together, including language detection behavior, country/language mappings, fallback handling, and statistics.
The goal was to make the extension flexible enough for more advanced installations while keeping the basic setup straightforward.
How I built it
Now for the slightly unusual part of this story.
I'll be completely honest: I don't think I could have written this extension without my existing Flarum knowledge.
This was actually the first Flarum extension I developed using Claude CLI as a major part of the development process.
Before starting, I designed the architecture and prepared roughly 1,500 lines of instructions and technical context for the initial prompt.
Then I let Claude Sonnet 5 High work through the implementation.
The development process took roughly five days, around 30 hours of work, and cost approximately $500 in API usage.
And no, this wasn't a case of:
“Give Claude a prompt and come back five days later.”
It made plenty of mistakes.
A lot of them.
I had to continuously inspect the implementation, identify incorrect assumptions, investigate failures, and manually guide it toward the correct architecture. That added roughly another 10 hours of hands-on debugging and intervention.
Interestingly, the hardest part wasn't always fixing the bugs.
Diagnosing why they existed was often much harder.
Understanding Flarum's middleware pipeline, figuring out exactly when the locale becomes part of the request, and finding the correct place to integrate with the native SetLocale flow was a particularly interesting challenge.
Working directly with Flarum's localization catalogue and middleware pipeline at this level was also a first for me.
And honestly, I had a lot of fun doing it.
The result
What started as an idea from the Flarum discussion turned into something considerably more ambitious than I originally expected.
The extension currently provides:
- Browser language detection
- Local IP-based country detection
- IPv4 support
- IPv6 support
- Automatic locale selection
- Country-to-language mapping
- Visitor country/IP statistics
- Missing language statistics
- Native integration with Flarum's
SetLocale middleware
- No third-party IP geolocation API required
- Configurable detection and fallback behavior
- A dedicated overview dashboard
- Language management and statistics
- Country statistics
- Missing language tracking
- A comprehensive settings page
I'm genuinely very happy with how this turned out.
I started this project because I wanted automatic language detection to feel like a native part of Flarum, rather than an extension bolted onto the side of it.
I think I got surprisingly close to that goal.
The extension isn't publicly released yet, but if you want to be one of the first people to try it, you can follow me here on Flarum or on GitHub. I'll share the release as soon as it's ready.
And, as always, I'd love to hear what you think.
What do you think about the architecture and the approach?
Especially if you have experience with Flarum's localization or middleware system, I'd be very interested in hearing your thoughts.