Files
jiang13-forum/service/geo_zh.go

308 lines
9.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"strconv"
"strings"
)
// countryZh ISO2 → 中文国名(展示用;存储仍用 ISO
var countryZh = map[string]string{
"CN": "中国", "HK": "中国香港", "MO": "中国澳门", "TW": "中国台湾",
"US": "美国", "JP": "日本", "KR": "韩国", "SG": "新加坡",
"GB": "英国", "DE": "德国", "FR": "法国", "RU": "俄罗斯",
"CA": "加拿大", "AU": "澳大利亚", "IN": "印度", "TH": "泰国",
"VN": "越南", "MY": "马来西亚", "ID": "印度尼西亚", "PH": "菲律宾",
"NL": "荷兰", "IT": "意大利", "ES": "西班牙", "BR": "巴西",
"MX": "墨西哥", "SE": "瑞典", "CH": "瑞士", "PL": "波兰",
"TR": "土耳其", "SA": "沙特阿拉伯", "AE": "阿联酋", "NZ": "新西兰",
"IE": "爱尔兰", "BE": "比利时", "AT": "奥地利", "NO": "挪威",
"DK": "丹麦", "FI": "芬兰", "PT": "葡萄牙", "CZ": "捷克",
"UA": "乌克兰", "IL": "以色列", "ZA": "南非", "AR": "阿根廷",
}
type cnRegionMeta struct {
Name string
ISO string
}
// cnRegionMap 中国省级英文名/别名 → 中文名 + 地图 ISO
var cnRegionMap = map[string]cnRegionMeta{
"beijing": {Name: "北京", ISO: "BJ"},
"peking": {Name: "北京", ISO: "BJ"},
"tianjin": {Name: "天津", ISO: "TJ"},
"shanghai": {Name: "上海", ISO: "SH"},
"chongqing": {Name: "重庆", ISO: "CQ"},
"chungking": {Name: "重庆", ISO: "CQ"},
"hebei": {Name: "河北", ISO: "HE"},
"shanxi": {Name: "山西", ISO: "SX"},
"liaoning": {Name: "辽宁", ISO: "LN"},
"jilin": {Name: "吉林", ISO: "JL"},
"heilongjiang": {Name: "黑龙江", ISO: "HL"},
"jiangsu": {Name: "江苏", ISO: "JS"},
"zhejiang": {Name: "浙江", ISO: "ZJ"},
"anhui": {Name: "安徽", ISO: "AH"},
"fujian": {Name: "福建", ISO: "FJ"},
"jiangxi": {Name: "江西", ISO: "JX"},
"shandong": {Name: "山东", ISO: "SD"},
"henan": {Name: "河南", ISO: "HA"},
"hubei": {Name: "湖北", ISO: "HB"},
"hunan": {Name: "湖南", ISO: "HN"},
"guangdong": {Name: "广东", ISO: "GD"},
"guangxi": {Name: "广西", ISO: "GX"},
"hainan": {Name: "海南", ISO: "HI"},
"sichuan": {Name: "四川", ISO: "SC"},
"guizhou": {Name: "贵州", ISO: "GZ"},
"yunnan": {Name: "云南", ISO: "YN"},
"xizang": {Name: "西藏", ISO: "XZ"},
"tibet": {Name: "西藏", ISO: "XZ"},
"shaanxi": {Name: "陕西", ISO: "SN"},
"shaanxi province": {Name: "陕西", ISO: "SN"},
"gansu": {Name: "甘肃", ISO: "GS"},
"qinghai": {Name: "青海", ISO: "QH"},
"ningxia": {Name: "宁夏", ISO: "NX"},
"xinjiang": {Name: "新疆", ISO: "XJ"},
"xinjiang uygur": {Name: "新疆", ISO: "XJ"},
"inner mongolia": {Name: "内蒙古", ISO: "NM"},
"nei mongol": {Name: "内蒙古", ISO: "NM"},
"guangxi zhuangzu": {Name: "广西", ISO: "GX"},
"ningxia huizu": {Name: "宁夏", ISO: "NX"},
"hong kong": {Name: "香港", ISO: "HK"},
"macau": {Name: "澳门", ISO: "MO"},
"macao": {Name: "澳门", ISO: "MO"},
"taiwan": {Name: "台湾", ISO: "TW"},
// 台湾各县市IP2Location 常直接给县市名作为 region
"changhua": {Name: "台湾", ISO: "TW"},
"chiayi": {Name: "台湾", ISO: "TW"},
"hsinchu": {Name: "台湾", ISO: "TW"},
"hualien": {Name: "台湾", ISO: "TW"},
"kaohsiung": {Name: "台湾", ISO: "TW"},
"keelung": {Name: "台湾", ISO: "TW"},
"kinmen": {Name: "台湾", ISO: "TW"},
"lienchiang": {Name: "台湾", ISO: "TW"},
"miaoli": {Name: "台湾", ISO: "TW"},
"nantou": {Name: "台湾", ISO: "TW"},
"new taipei": {Name: "台湾", ISO: "TW"},
"penghu": {Name: "台湾", ISO: "TW"},
"pingtung": {Name: "台湾", ISO: "TW"},
"taichung": {Name: "台湾", ISO: "TW"},
"tainan": {Name: "台湾", ISO: "TW"},
"taipei": {Name: "台湾", ISO: "TW"},
"taitung": {Name: "台湾", ISO: "TW"},
"taoyuan": {Name: "台湾", ISO: "TW"},
"yilan": {Name: "台湾", ISO: "TW"},
"yunlin": {Name: "台湾", ISO: "TW"},
}
// cityZh 定义见 geo_zh_cities.goLITE 中国城市全表 + 同音复合键)
// asnOrgZh 常见 ASN / AS 组织名 → 中文运营商
var asnOrgZh = map[uint]string{
4134: "中国电信",
4812: "中国电信",
4837: "中国联通",
4808: "中国联通",
9808: "中国移动",
56040: "中国移动",
56041: "中国移动",
56042: "中国移动",
4538: "教育网",
23910: "教育网",
58539: "教育网",
7497: "教育网",
}
var asnOrgKeywordZh = []struct {
kw string
zh string
}{
{"chinanet", "中国电信"},
{"china telecom", "中国电信"},
{"chinatelecom", "中国电信"},
{"unicom", "中国联通"},
{"china unicom", "中国联通"},
{"mobile", "中国移动"},
{"china mobile", "中国移动"},
{"cernet", "教育网"},
{"education", "教育网"},
}
// CountryLabelZh ISO2 → 中文国名(未知则返回原码)
func CountryLabelZh(iso string) string {
iso = strings.ToUpper(strings.TrimSpace(iso))
if iso == "" {
return ""
}
if v, ok := countryZh[iso]; ok {
return v
}
return iso
}
// ApplyGeoZh 就地补充中文省/市/运营商country 保持 ISO2
func ApplyGeoZh(g *GeoInfo) {
if g == nil {
return
}
g.Country = normalizeCountryCode(g.Country)
if g.Country == "CN" || g.Country == "HK" || g.Country == "MO" || g.Country == "TW" {
mapCNRegionCity(g)
}
if g.ASN > 0 {
if zh, ok := asnOrgZh[g.ASN]; ok {
g.ASOrg = zh
} else if g.ASOrg != "" {
g.ASOrg = mapASOrgZh(g.ASOrg)
}
} else if g.ASOrg != "" {
g.ASOrg = mapASOrgZh(g.ASOrg)
}
g.Region = truncateRunes(strings.TrimSpace(g.Region), 64)
g.City = truncateRunes(strings.TrimSpace(g.City), 64)
g.ASOrg = truncateRunes(strings.TrimSpace(g.ASOrg), 128)
}
func mapCNRegionCity(g *GeoInfo) {
if g.RegionISO == "" {
if meta, ok := lookupCNRegion(g.Region); ok {
g.Region = meta.Name
g.RegionISO = meta.ISO
}
} else if g.Region != "" {
if meta, ok := lookupCNRegion(g.Region); ok {
if g.RegionISO == "" {
g.RegionISO = meta.ISO
}
g.Region = meta.Name
}
} else if g.RegionISO != "" {
for _, meta := range cnRegionMap {
if meta.ISO == strings.ToUpper(g.RegionISO) {
g.Region = meta.Name
break
}
}
}
if g.City != "" {
if zh, ok := lookupCityZh(g.RegionISO, g.Region, g.City); ok {
g.City = zh
}
}
// 直辖市BIN 有时只给城市不给省
if g.Region == "" && g.City != "" {
switch g.City {
case "北京", "上海", "天津", "重庆":
g.Region = g.City
if meta, ok := cnRegionMap[strings.ToLower(g.City)]; ok {
g.RegionISO = meta.ISO
}
}
}
}
func lookupCNRegion(name string) (cnRegionMeta, bool) {
key := strings.ToLower(strings.TrimSpace(name))
if key == "" {
return cnRegionMeta{}, false
}
if meta, ok := cnRegionMap[key]; ok {
return meta, true
}
// 去掉常见后缀再试
for _, suffix := range []string{" province", " sheng", " autonomous region", " municipality", " zhuangzu", " huizu", " uygur"} {
if strings.HasSuffix(key, suffix) {
if meta, ok := cnRegionMap[strings.TrimSuffix(key, suffix)]; ok {
return meta, true
}
}
}
return cnRegionMeta{}, false
}
// lookupCityZh 将 IP2Location 英文城市名映射为中文。
// 同音城市优先用「省ISO|城市」复合键;并尝试去掉 city/shi 后缀与撇号变体。
func lookupCityZh(regionISO, region, name string) (string, bool) {
key := normalizeCityKey(name)
if key == "" {
return "", false
}
iso := strings.ToLower(strings.TrimSpace(regionISO))
if iso == "" {
if meta, ok := lookupCNRegion(region); ok {
iso = strings.ToLower(meta.ISO)
}
}
candidates := cityKeyCandidates(key)
if iso != "" {
for _, c := range candidates {
if zh, ok := cityZh[iso+"|"+c]; ok {
return zh, true
}
}
}
for _, c := range candidates {
if zh, ok := cityZh[c]; ok {
return zh, true
}
}
return "", false
}
func normalizeCityKey(name string) string {
key := strings.ToLower(strings.TrimSpace(name))
if key == "" || key == "-" {
return ""
}
return key
}
func cityKeyCandidates(key string) []string {
seen := map[string]struct{}{}
var out []string
add := func(s string) {
s = strings.TrimSpace(s)
if s == "" {
return
}
if _, ok := seen[s]; ok {
return
}
seen[s] = struct{}{}
out = append(out, s)
}
add(key)
add(strings.ReplaceAll(key, "'", ""))
for _, suffix := range []string{" city", " shi"} {
if strings.HasSuffix(key, suffix) {
base := strings.TrimSuffix(key, suffix)
add(base)
add(strings.ReplaceAll(base, "'", ""))
}
}
return out
}
func mapASOrgZh(org string) string {
l := strings.ToLower(strings.TrimSpace(org))
if l == "" {
return org
}
for _, rule := range asnOrgKeywordZh {
if strings.Contains(l, rule.kw) {
return rule.zh
}
}
// "AS4134 Chinanet" 等形式
if strings.HasPrefix(l, "as") {
num := strings.TrimPrefix(l, "as")
if i := strings.IndexByte(num, ' '); i >= 0 {
num = num[:i]
}
if n, err := strconv.ParseUint(num, 10, 32); err == nil {
if zh, ok := asnOrgZh[uint(n)]; ok {
return zh
}
}
}
return org
}