Kamero

Content Localization with IP Geolocation: Auto-Detect Language & Region

Serving content in the right language and format for each visitor is one of the highest-impact things you can do for user experience. IP geolocation gives you a reliable starting point for locale detection โ€” before the user even interacts with your site.

Why IP-Based Locale Detection?

Browser Accept-Language headers are useful but not always reliable. A user in Japan might have their browser set to English. IP geolocation adds a second signal โ€” their physical location โ€” which helps you make smarter defaults:

Country-to-Locale Mapping

const countryLocaleMap: Record<string, string> = {
  US: "en-US",
  GB: "en-GB",
  DE: "de-DE",
  FR: "fr-FR",
  ES: "es-ES",
  JP: "ja-JP",
  KR: "ko-KR",
  BR: "pt-BR",
  CN: "zh-CN",
  IN: "hi-IN",
  SA: "ar-SA",
  IT: "it-IT",
  NL: "nl-NL",
  RU: "ru-RU",
  TR: "tr-TR",
};

function getLocale(countryCode: string): string {
  return countryLocaleMap[countryCode] || "en-US";
}

Detect Locale from IP

async function detectVisitorLocale(): Promise<string> {
  const res = await fetch("https://geo.kamero.ai/api/geo");
  const { country } = await res.json();
  return getLocale(country);
}

// Usage
const locale = await detectVisitorLocale();
// "ja-JP" for a visitor in Japan

Combine with Accept-Language Header

function getBestLocale(
  acceptLanguage: string | null,
  geoCountry: string
): string {
  // Priority: explicit browser preference > geo
  if (acceptLanguage) {
    const preferred = acceptLanguage.split(",")[0]
      .split(";")[0].trim();
    // Check if it's a full locale (e.g., "fr-FR")
    if (preferred.includes("-")) return preferred;
  }

  // Fall back to geo-based locale
  return getLocale(geoCountry);
}

This gives you the best of both worlds: respect explicit user preferences, but use geolocation as a smart fallback.

Next.js Middleware for Auto-Locale

// middleware.ts
import { NextRequest, NextResponse } from "next/server";

const SUPPORTED_LOCALES = ["en", "de", "fr", "es", "ja"];

export function middleware(request: NextRequest) {
  const country = request.geo?.country || "US";
  const countryToLang: Record<string, string> = {
    DE: "de", FR: "fr", ES: "es",
    MX: "es", JP: "ja", AT: "de", CH: "de",
  };

  const detectedLang = countryToLang[country] || "en";
  const pathname = request.nextUrl.pathname;

  // Skip if already has locale prefix
  const hasLocale = SUPPORTED_LOCALES.some(
    (l) => pathname.startsWith(`/${l}/`) || pathname === `/${l}`
  );
  if (hasLocale) return NextResponse.next();

  // Redirect to localized path
  const url = request.nextUrl.clone();
  url.pathname = `/${detectedLang}${pathname}`;
  return NextResponse.redirect(url);
}

Format Dates and Numbers by Region

function formatForLocale(locale: string) {
  const now = new Date();

  return {
    date: new Intl.DateTimeFormat(locale, {
      dateStyle: "long",
    }).format(now),

    number: new Intl.NumberFormat(locale).format(1234567.89),

    currency: new Intl.NumberFormat(locale, {
      style: "currency",
      currency: getCurrencyForLocale(locale),
    }).format(99.99),
  };
}

// "en-US" โ†’ { date: "February 9, 2026",
//             number: "1,234,567.89",
//             currency: "$99.99" }
// "de-DE" โ†’ { date: "9. Februar 2026",
//             number: "1.234.567,89",
//             currency: "99,99 โ‚ฌ" }

React Component with Geo-Locale

"use client";
import { useState, useEffect } from "react";

export function LocalizedContent() {
  const [locale, setLocale] = useState("en-US");

  useEffect(() => {
    fetch("https://geo.kamero.ai/api/geo")
      .then(r => r.json())
      .then(({ country }) => setLocale(getLocale(country)))
      .catch(() => {}); // Keep default
  }, []);

  const messages: Record<string, { welcome: string }> = {
    "en-US": { welcome: "Welcome" },
    "es-ES": { welcome: "Bienvenido" },
    "fr-FR": { welcome: "Bienvenue" },
    "de-DE": { welcome: "Willkommen" },
    "ja-JP": { welcome: "ใ‚ˆใ†ใ“ใ" },
  };

  const msg = messages[locale] || messages["en-US"];

  return (
    <div lang={locale}>
      <h1>{msg.welcome}</h1>
      <p>{new Date().toLocaleDateString(locale)}</p>
    </div>
  );
}

Best Practices

Get Started

Detect your visitors' country instantly with a free API โ€” no key required.

View Documentation โ†’