首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau
百百 BLOG
首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau

Kurau Blog

「隨心而寫,真真假假,都是我」

一個記錄生活、輸出興趣的個人空間。
遊戲、影視、閱讀、學習……每一段體驗都值得留下文字。

頁面導覽

  • 學習紀錄
  • 遊戲心得
  • 影視Life
  • 書單
  • 委託作品與二創
  • Kurau
  • 合作邀請

找到我

歡迎來 Discord 找我聊天!

“曾經發生的事不可能忘記,只是暫時想不起來而已。”-《神隱少女》

© 2026 Kurau All rights reserved

部署與DevOps

如何找到你的ip與local

By Kurau·2024-01-11·Updated 2026-05-09·5 分鐘閱讀

判斷使用者 Locale / IP

TL;DR
三種方法:navigator.language(瀏覽器設定,最簡單)、Geolocation API(經緯度,要使用者授權)、IP 地理查詢(server-side / 第三方 API,最隱蔽)。2026 年 SSR / Edge runtime 推薦走 Accept-Language header + Cloudflare CF-IPCountry。

三種方法對比

方法精準度需要授權適合
navigator.language中(==語言偏好不一定 = 地點==)❌ 不需要預設語言判斷
Geolocation API超精準(GPS)✅ 使用者要同意彈窗地圖 / 在地服務
IP 地理查詢中-高(會被 VPN 騙)❌ 不需要區域內容 / 法規合規
Accept-Language header同 navigator.language❌ 不需要SSR / Edge
Cloudflare CF-IPCountry中-高❌ 不需要走 CF 的網站

方法 1:navigator.language(最簡單)

navigator.language          // "zh-TW"
navigator.languages         // ["zh-TW", "en-US", "ja"](優先順序)
javascript

格式:BCP 47 語言標籤:

language[-script][-region]

zh-TW       中文-臺灣
zh-Hans-CN  中文-簡體-中國
en-US       英文-美國
ja          日文(無地區)
function getInitialLocale(): string {
  if (typeof window === 'undefined') return 'zh-TW';   // SSR fallback

  const browserLang = navigator.language;

  // 對應到我們支援的 locale
  if (browserLang.startsWith('zh')) return 'zh-TW';
  if (browserLang.startsWith('ja')) return 'ja';
  return 'en';
}
typescript
不等於地理位置
在台灣的人 可能設成 en-US(工程師習慣英文 OS),用 navigator.language 判 = 顯示英文 — 不一定是他要的。

實務上 給使用者 手動切換的選項,navigator.language 只當 預設值。


方法 2:Geolocation API(精準經緯度)

function getLocation(): Promise<GeolocationPosition> {
  return new Promise((resolve, reject) => {
    if (!navigator.geolocation) {
      reject(new Error('Geolocation not supported'));
      return;
    }

    navigator.geolocation.getCurrentPosition(
      resolve,
      reject,
      {
        enableHighAccuracy: true,
        timeout: 5000,
        maximumAge: 0,
      },
    );
  });
}

// 使用
try {
  const pos = await getLocation();
  console.log(`Lat: ${pos.coords.latitude}, Lng: ${pos.coords.longitude}`);
  // 接下來呼叫 reverse geocoding API 把座標轉成國家
} catch (err) {
  console.warn('User denied or error:', err);
}
typescript

Permission 流程處理:

async function getLocationWithPermission() {
  const result = await navigator.permissions.query({ name: 'geolocation' });

  switch (result.state) {
    case 'granted':
      return await getLocation();        // 已授權
    case 'prompt':
      return await getLocation();        // 觸發授權彈窗
    case 'denied':
      throw new Error('Permission denied');
  }
}
typescript

經緯度 → 國家(Reverse Geocoding)

async function coordsToCountry(lat: number, lng: number): Promise<string> {
  // Nominatim(OpenStreetMap)免費
  const res = await fetch(
    `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`
  );
  const data = await res.json();
  return data.address?.country_code;     // "tw" / "us" / "jp"
}
typescript

其他選擇:Google Geocoding API(更準但要錢)、Mapbox Geocoding。


方法 3:IP 地理查詢

Client-side 第三方 API(免費):

async function getCountryByIP(): Promise<string> {
  const res = await fetch('https://ipapi.co/json/');
  const data = await res.json();
  return data.country_code;       // "TW" / "US"
}
javascript
API免費額度備註
ipapi.co1000 req/天簡潔好用
ip-api.com45 req/分鐘免費,但 production 要 https 付費
ipinfo.io50K req/月商業級
geolocation-db.com免費老牌
CORS / Rate Limit
Client-side 直接 call 第三方 API 容易被 rate limit(訪客一多就爆)。建議走 server-side proxy:
// app/api/geo/route.ts(Next.js)
export async function GET(req: Request) {
  const ip = req.headers.get('x-forwarded-for') ?? '';
  const res = await fetch(`https://ipapi.co/${ip}/json/`);
  const data = await res.json();
  return Response.json({ country: data.country_code });
}
typescript

方法 4:Server-side(SSR / Edge)— 推薦

Accept-Language Header

// Next.js middleware.ts
export function middleware(request: NextRequest) {
  const accept = request.headers.get('accept-language') ?? '';
  const lang = accept.split(',')[0].trim();      // "zh-TW"
  const country = lang.split('-')[1] ?? 'TW';
  // ... 用這個資料 redirect / 設 cookie
}
typescript

Cloudflare CF-IPCountry(免費,自動)

如果你的網站走 Cloudflare CDN,Cloudflare 自動加 header:

export function middleware(request: NextRequest) {
  const country = request.headers.get('cf-ipcountry');   // "TW"
  // 完全免費,不用打第三方 API
}
typescript

這是 2026 年最划算的做法:精準、免費、快、無 rate limit。

Vercel @vercel/functions(Vercel 平台)

import { geolocation } from '@vercel/functions';

export function GET(req: Request) {
  const { country, city } = geolocation(req);
  return Response.json({ country, city });
}
typescript

Vercel Edge Function 內建,跟 Cloudflare 差不多概念。


各方法的隱私 / 法規

方法GDPR 友善?用戶感知
navigator.language✅ 完全 OK無感
Accept-Language✅ 完全 OK無感
IP 地理⚠️ 算個資,需告知無感
Geolocation API✅ 因為要授權有彈窗,清楚
Cloudflare CF-IPCountry⚠️ 同 IP 地理無感
在台灣 / 美國
IP 地理查詢通常不算高敏感個資,但 GDPR 區域(EU)要在 隱私政策列出。Termly(Termly 第三方cookie教學)可以幫你管。

實戰範例:多語站自動跳轉

// middleware.ts(Next.js Edge)
import { NextRequest, NextResponse } from 'next/server';

const SUPPORTED_LOCALES = ['zh-TW', 'en', 'ja'];

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;

  // 已經有 locale 前綴就不動
  if (SUPPORTED_LOCALES.some((l) => pathname.startsWith(`/${l}`))) {
    return NextResponse.next();
  }

  // 用 Cloudflare(若有)或 Accept-Language 偵測
  const country = request.headers.get('cf-ipcountry');
  const accept = request.headers.get('accept-language') ?? '';

  let locale = 'en';   // 預設
  if (country === 'TW' || country === 'HK') locale = 'zh-TW';
  else if (country === 'JP') locale = 'ja';
  else if (accept.includes('zh')) locale = 'zh-TW';
  else if (accept.includes('ja')) locale = 'ja';

  // 重導
  const url = request.nextUrl.clone();
  url.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(url);
}

export const config = {
  matcher: ['/((?!api|_next|.*\\..*).*)'],
};
typescript

最後 給使用者一個 手動切換 UI,別假設機器永遠對。

目錄

    ◆ 相關文章

    • React專案無法正常Github Action

      2026-05-09
    • 如何布置靜態網站 (免費資源) ⇒ Next.js

      2026-05-09
    • 免費伺服器 介紹

      2026-05-09
    • 網頁縮圖 - SEO

      2026-05-09
    ← 上一篇Termly 第三方cookie教學下一篇 →方便你的SideProject

    ◆ 關於作者

    Kurau

    個人寫作 / 創作的 SoT,記錄遊戲、影視、學習與生活。

    更多 Kurau 的文章