Skip to content

Commit a7a5972

Browse files
committed
add custom language support (closes #14)
- new formatting for vue/js files - add lang customization to api docs Signed-off-by: Liam Stanley <[email protected]>
1 parent e20f63e commit a7a5972

File tree

12 files changed

+857
-673
lines changed

12 files changed

+857
-673
lines changed

api.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"net"
1111
"net/http"
12+
"regexp"
1213
"strconv"
1314
"strings"
1415

@@ -22,10 +23,30 @@ func registerAPI(r chi.Router) {
2223
r.Get("/api/{addr}/{filters}", apiLookup)
2324
}
2425

26+
var reLanguage = regexp.MustCompile(`^[^a-zA-Z, ]+.*?$`)
27+
2528
func apiLookup(w http.ResponseWriter, r *http.Request) {
2629
addr := strings.TrimSpace(chi.URLParam(r, "addr"))
2730
filters := strings.Split(chi.URLParam(r, "filters"), ",")
2831

32+
// Prioritize "lang" query param.
33+
lang := matchLanguage(strings.ReplaceAll(r.FormValue("lang"), " ", ""))
34+
35+
if lang == "" {
36+
// Try to get the language from the Accept-Language header.
37+
for _, l := range strings.Split(reLanguage.ReplaceAllString(r.Header.Get("Accept-Language"), ""), ",") {
38+
lang = matchLanguage(l)
39+
if lang != "" {
40+
break
41+
}
42+
}
43+
}
44+
45+
// If we still don't have a language, default to global language default.
46+
if lang == "" {
47+
lang = flags.DefaultLanguage
48+
}
49+
2950
// If they're trying to send us way too many filters (which could cause
3051
// unwanted extra memory usage/be considered a resource usage attack),
3152
// we shouldn't handle their request.
@@ -59,6 +80,8 @@ func apiLookup(w http.ResponseWriter, r *http.Request) {
5980
key = addr + ":" + strings.Join(filters, ",")
6081
}
6182

83+
key += "," + lang
84+
6285
var result *AddrResult
6386

6487
query, err := arc.GetIFPresent(key)
@@ -98,7 +121,7 @@ func apiLookup(w http.ResponseWriter, r *http.Request) {
98121
return
99122
}
100123

101-
result, err = addrLookup(r.Context(), ip, filters)
124+
result, err = addrLookup(r.Context(), ip, filters, lang)
102125
if err != nil {
103126
logger.Printf("error looking up address %q (%q): %s", addr, ip, err)
104127
w.WriteHeader(http.StatusServiceUnavailable)

db.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,42 @@ import (
2222
maxminddb "github.com/oschwald/maxminddb-golang"
2323
)
2424

25+
var supportedLanguages = []string{
26+
"de", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN",
27+
}
28+
29+
func matchLanguage(lang string) string {
30+
if lang == "" {
31+
return ""
32+
}
33+
34+
for i := 0; i < len(supportedLanguages); i++ {
35+
if strings.EqualFold(lang, supportedLanguages[i]) {
36+
return supportedLanguages[i]
37+
}
38+
39+
if j := strings.Index(supportedLanguages[i], "-"); j > 0 {
40+
if strings.EqualFold(lang, supportedLanguages[i][:j]) {
41+
return supportedLanguages[i]
42+
}
43+
44+
if k := strings.Index(lang, "-"); k > 0 {
45+
if strings.EqualFold(lang[:k], supportedLanguages[i][:j]) {
46+
return supportedLanguages[i]
47+
}
48+
}
49+
}
50+
51+
if j := strings.Index(lang, "-"); j > 0 {
52+
if strings.EqualFold(lang[:j], supportedLanguages[i]) {
53+
return supportedLanguages[i]
54+
}
55+
}
56+
}
57+
58+
return ""
59+
}
60+
2561
type DB struct {
2662
path string
2763
}
@@ -234,7 +270,7 @@ type AddrResult struct {
234270
// addrLookup does a geoip lookup of an IP address. filters is passed into
235271
// this function, in case there are any long running tasks which the user
236272
// may not even want (e.g. reverse dns lookups).
237-
func addrLookup(ctx context.Context, addr net.IP, filters []string) (*AddrResult, error) {
273+
func addrLookup(ctx context.Context, addr net.IP, filters []string, lang string) (*AddrResult, error) {
238274
var result *AddrResult
239275
var err error
240276

@@ -254,10 +290,10 @@ func addrLookup(ctx context.Context, addr net.IP, filters []string) (*AddrResult
254290

255291
result = &AddrResult{
256292
IP: addr,
257-
City: query.City.Names["en"],
258-
Country: query.Country.Names["en"],
293+
City: query.City.Names[lang],
294+
Country: query.Country.Names[lang],
259295
CountryCode: query.Country.Code,
260-
Continent: query.Continent.Names["en"],
296+
Continent: query.Continent.Names[lang],
261297
ContinentCode: query.Continent.Code,
262298
Lat: query.Location.Lat,
263299
Long: query.Location.Long,
@@ -268,7 +304,7 @@ func addrLookup(ctx context.Context, addr net.IP, filters []string) (*AddrResult
268304

269305
var subdiv []string
270306
for i := 0; i < len(query.Subdivisions); i++ {
271-
subdiv = append(subdiv, query.Subdivisions[i].Names["en"])
307+
subdiv = append(subdiv, query.Subdivisions[i].Names[lang])
272308
}
273309
result.Subdivision = strings.Join(subdiv, ", ")
274310

main.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@ var (
2929
)
3030

3131
type Flags struct {
32-
Debug bool `env:"DEBUG" short:"d" long:"debug" description:"enable exception display and pprof endpoints (warn: dangerous)"`
33-
Quiet bool `env:"QUIET" short:"q" long:"quiet" description:"disable verbose output"`
34-
DBPath string `env:"DB_PATH" long:"db" description:"path to read/store Maxmind DB" default:"geoip.db"`
35-
UpdateInterval time.Duration `env:"UPDATE_INTERVAL" long:"interval" description:"interval of time between database update checks" default:"12h"`
36-
UpdateURL string `env:"MAXMIND_UPDATE_URL" long:"update-url" description:"maxmind database file download location (must be gzipped)" default:"https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=%s&suffix=tar.gz"`
37-
LicenseKey string `env:"MAXMIND_LICENSE_KEY" long:"license-key" description:"maxmind license key (must register for a maxmind account)" required:"true"`
38-
Cache struct {
32+
Debug bool `env:"DEBUG" short:"d" long:"debug" description:"enable exception display and pprof endpoints (warn: dangerous)"`
33+
Quiet bool `env:"QUIET" short:"q" long:"quiet" description:"disable verbose output"`
34+
DBPath string `env:"DB_PATH" long:"db" description:"path to read/store Maxmind DB" default:"geoip.db"`
35+
UpdateInterval time.Duration `env:"UPDATE_INTERVAL" long:"interval" description:"interval of time between database update checks" default:"12h"`
36+
UpdateURL string `env:"MAXMIND_UPDATE_URL" long:"update-url" description:"maxmind database file download location (must be gzipped)" default:"https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=%s&suffix=tar.gz"`
37+
LicenseKey string `env:"MAXMIND_LICENSE_KEY" long:"license-key" description:"maxmind license key (must register for a maxmind account)" required:"true"`
38+
DefaultLanguage string `env:"DEFAULT_LANGUAGE" long:"lang" description:"default language to use for geolocation" default:"en"`
39+
Cache struct {
3940
Size int `env:"CACHE_SIZE" long:"size" description:"total number of lookups to keep in ARC cache (50% most recent, 50% most requested)" default:"500"`
4041
Expire time.Duration `env:"CACHE_EXPIRE" long:"expire" description:"expiration time of cache" default:"20m"`
4142
} `group:"Cache Options" namespace:"cache"`

public/index.html

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
<!DOCTYPE html>
22
<html lang="en">
3-
<head>
4-
<meta charset="utf-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6-
<link rel="icon" href="/static/img/favicon.ico">
7-
<title>GeoIP</title>
8-
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css" crossorigin="anonymous">
9-
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
10-
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css" crossorigin="anonymous">
11-
</head>
12-
<body>
13-
<div id="vue"><app></app></div>
14-
<script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
15-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.slim.min.js" crossorigin="anonymous"></script>
16-
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.js" crossorigin="anonymous"></script>
17-
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js" crossorigin="anonymous"></script>
18-
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js" crossorigin="anonymous"></script>
19-
<script type="module" src="/src/main.js"></script>
20-
</body>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
6+
<link rel="icon" href="/static/img/favicon.ico" />
7+
<title>GeoIP</title>
8+
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css" crossorigin="anonymous" />
9+
<link
10+
rel="stylesheet"
11+
href="https://unpkg.com/[email protected]/dist/leaflet.css"
12+
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
13+
crossorigin=""
14+
/>
15+
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css" crossorigin="anonymous" />
16+
</head>
17+
<body>
18+
<div id="vue"><app></app></div>
19+
<script
20+
src="https://unpkg.com/[email protected]/dist/leaflet.js"
21+
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
22+
crossorigin=""
23+
></script>
24+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.slim.min.js" crossorigin="anonymous"></script>
25+
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.js" crossorigin="anonymous"></script>
26+
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js" crossorigin="anonymous"></script>
27+
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js" crossorigin="anonymous"></script>
28+
<script type="module" src="/src/main.js"></script>
29+
</body>
2130
</html>

0 commit comments

Comments
 (0)