Add locale data categories: word, sentence, date, time, ssn, version, email, ip, company, color, url, uuid, username, price

This commit is contained in:
lilleman
2026-06-05 20:18:27 +02:00
parent 943a1d9f1b
commit 770dcd72f2
30 changed files with 320 additions and 0 deletions
+6
View File
@@ -84,6 +84,12 @@ The library ships a ready-to-use set under [`locales/`](locales) (`en_US`,
`sv_SE`). Pass `New` the path to one of them, a copy, or your own directory — `sv_SE`). Pass `New` the path to one of them, a copy, or your own directory —
anywhere on disk. anywhere on disk.
Each ships these categories, formatted per locale (e.g. `date` is `MM/DD/YYYY`
in `en_US`, `YYYY-MM-DD` in `sv_SE`; `ssn` is a US SSN vs a Swedish
personnummer): `address`, `color`, `company`, `date`, `email`, `ip`, `person`,
`phone`, `price`, `sentence`, `ssn`, `time`, `url`, `username`, `uuid`,
`version`, `word`.
The directory's name is the locale and must be a full tag (`sv_SE`, never The directory's name is the locale and must be a full tag (`sv_SE`, never
`sv`). Any casing or separator is accepted and canonicalised (`sv-se`, `sv`). Any casing or separator is accepted and canonicalised (`sv-se`,
`SV_SE``sv_SE`); a non-full name returns an error. `SV_SE``sv_SE`); a non-full name returns an error.
+65
View File
@@ -0,0 +1,65 @@
package fakes
import (
"regexp"
"testing"
)
// TestShippedDataCategories asserts every shipped locale emits only well-formed
// values for each data category. en and sv patterns differ where the format is
// locale-specific (date, time, ssn, company, price, ...); the rest are shared.
func TestShippedDataCategories(t *testing.T) {
letters := regexp.MustCompile(`^[\pL'-]+$`)
semver := regexp.MustCompile(`^v?\d+\.\d+\.\d+(-(alpha|beta|rc)\.\d+)?$`)
ip := regexp.MustCompile(`^((\d{1,3}\.){3}\d{1,3}|([0-9a-f]{4}:){7}[0-9a-f]{4})$`)
uuid := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
color := regexp.MustCompile(`^(#[0-9a-f]{6}|[\pL ]+)$`)
url := regexp.MustCompile(`^https?://([a-z0-9-]+\.)+[a-z]{2,}(/[a-z0-9./-]*)?$`)
email := regexp.MustCompile(`^[a-z0-9._-]+@([a-z0-9-]+\.)+[a-z]{2,}$`)
uname := regexp.MustCompile(`^[a-z][a-z0-9._]*$`)
cases := []struct {
path string
en, sv *regexp.Regexp
}{
{"word", letters, letters},
{"sentence",
regexp.MustCompile(`^[A-Z].* .*[.!?]$`),
regexp.MustCompile(`^[A-ZÅÄÖ].* .*[.!?]$`)},
{"date",
regexp.MustCompile(`^(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{4}$`),
regexp.MustCompile(`^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$`)},
{"time",
regexp.MustCompile(`^([1-9]|1[0-2]):[0-5]\d (AM|PM)$`),
regexp.MustCompile(`^([01]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$`)},
{"ssn",
regexp.MustCompile(`^[1-9]\d{2}-\d{2}-\d{4}$`),
regexp.MustCompile(`^\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])-\d{4}$`)},
{"version", semver, semver},
{"email", email, email},
{"ip", ip, ip},
{"company",
regexp.MustCompile(`^.+ (Inc\.|LLC|Corp\.|Co\.|Group|Ltd\.)$`),
regexp.MustCompile(`^.+ (AB|HB|KB)$`)},
{"color", color, color},
{"url", url, url},
{"uuid", uuid, uuid},
{"username", uname, uname},
{"price",
regexp.MustCompile(`^\$\d{1,3}(,\d{3})?\.\d{2}$`),
regexp.MustCompile(`^\d{1,3}( \d{3})?(,\d{2})? kr$`)},
}
en := newFakes(t, "locales/en_US", WithSeed(1))
sv := newFakes(t, "locales/sv_SE", WithSeed(1))
for _, c := range cases {
for i := 0; i < 200; i++ {
if v := fake(t, en, c.path); !c.en.MatchString(v) {
t.Fatalf("en_US %s = %q, want %s", c.path, v, c.en)
}
if v := fake(t, sv, c.path); !c.sv.MatchString(v) {
t.Fatalf("sv_SE %s = %q, want %s", c.path, v, c.sv)
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
[
{
"format": "##{x}{x}{x}{x}{x}{x}",
"x": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"],
"weight": 2
},
["Black", "Blue", "Brown", "Gray", "Green", "Orange", "Pink", "Purple", "Red", "Sky Blue", "White", "Yellow"]
]
+14
View File
@@ -0,0 +1,14 @@
[
{
"format": "{base} {suffix}",
"base": [
{
"format": "{a}{b}",
"a": ["Acme", "Apex", "Blue", "Bright", "Iron", "North", "Pioneer", "Silver", "Summit", "Vertex"],
"b": ["core", "field", "line", "logic", "point", "soft", "tech", "ware", "wave", "works"]
},
["Atlas Industries", "Hudson Partners", "Liberty Holdings", "Riverside", "Smith & Sons"]
],
"suffix": ["Co.", "Corp.", "Group", "Inc.", "LLC", "Ltd."]
}
]
+15
View File
@@ -0,0 +1,15 @@
[
{
"format": "{month}/{day}/{year}",
"month": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"],
"day": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"],
"year": [
{ "format": "#1970" },
{ "format": "#1980" },
{ "format": "#1990", "weight": 2 },
{ "format": "2#0#00", "weight": 3 },
{ "format": "2#0#10", "weight": 3 },
{ "format": "2#020", "weight": 2 }
]
}
]
+15
View File
@@ -0,0 +1,15 @@
[
{
"format": "{local}@{domain}",
"local": [
{
"format": "{first}{sep}{last}{n}",
"first": ["james", "mary", "john", "patricia", "robert", "linda", "michael", "emma", "david", "olivia"],
"last": ["smith", "johnson", "williams", "brown", "jones", "garcia", "miller", "davis", "wilson", "taylor"],
"sep": [".", "_", "."],
"n": ["", "", "1", "7", "42"]
}
],
"domain": ["example.com", "example.org", "example.net", "mail.example.com"]
}
]
+11
View File
@@ -0,0 +1,11 @@
[
{
"format": "{o}.{o}.{o}.{o}",
"weight": 8,
"o": [{ "format": "0", "weight": 3 }, { "format": "10", "weight": 4 }, { "format": "#100", "weight": 2 }]
},
{
"format": "{g}:{g}:{g}:{g}:{g}:{g}:{g}:{g}",
"g": [{ "format": "{x}{x}{x}{x}", "x": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"] }]
}
]
+7
View File
@@ -0,0 +1,7 @@
[
{
"format": "${amt}.{cents}",
"amt": [{ "format": "10", "weight": 3 }, { "format": "100", "weight": 2 }, { "format": "1#,000" }],
"cents": [{ "format": "00" }]
}
]
+10
View File
@@ -0,0 +1,10 @@
[
{
"format": "{lead} {adj} {noun} {verb} {prep} {adj} {noun}.",
"lead": ["A", "Each", "Every", "One", "The"],
"adj": ["ancient", "bright", "fierce", "golden", "lazy", "modern", "quick", "silent"],
"noun": ["engine", "forest", "fox", "garden", "harbor", "mountain", "river", "signal"],
"verb": ["builds", "carries", "crosses", "follows", "guards", "reveals", "shapes"],
"prep": ["across", "beneath", "beyond", "over", "through"]
}
]
+3
View File
@@ -0,0 +1,3 @@
[
{ "format": "100-00-0000" }
]
+8
View File
@@ -0,0 +1,8 @@
[
{
"format": "{hour}:{minute} {ampm}",
"hour": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
"minute": [{ "format": "{t}0", "t": ["0", "1", "2", "3", "4", "5"] }],
"ampm": ["AM", "PM"]
}
]
+7
View File
@@ -0,0 +1,7 @@
[
{
"format": "https://{host}{path}",
"host": ["example.com", "www.example.com", "example.org", "blog.example.net", "shop.example.com", "example.io"],
"path": ["", { "format": "/{seg}", "seg": ["about", "account", "blog", "contact", "help", "items", "login", "news", "products", "search"] }]
}
]
+9
View File
@@ -0,0 +1,9 @@
[
{
"format": "{first}{sep}{last}{n}",
"first": ["alex", "chris", "jamie", "jordan", "morgan", "sam", "taylor", "casey", "riley", "drew"],
"last": ["adams", "brooks", "carter", "evans", "fisher", "grant", "hayes", "lane", "morris", "reed"],
"sep": ["", "", ".", "_"],
"n": ["", "", "", "1", "7", "23", "99"]
}
]
+6
View File
@@ -0,0 +1,6 @@
[
{
"format": "{x}{x}{x}{x}{x}{x}{x}{x}-{x}{x}{x}{x}-{x}{x}{x}{x}-{x}{x}{x}{x}-{x}{x}{x}{x}{x}{x}{x}{x}{x}{x}{x}{x}",
"x": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]
}
]
+8
View File
@@ -0,0 +1,8 @@
[
{
"format": "{pre}{n}.{n}.{n}{suffix}",
"n": [{ "format": "0", "weight": 3 }, { "format": "10" }],
"pre": ["", { "format": "v", "weight": 0.4 }],
"suffix": ["", { "format": "-{tag}.{m}", "tag": ["alpha", "beta", "rc"], "m": ["1", "2", "3"], "weight": 0.3 }]
}
]
+1
View File
@@ -0,0 +1 @@
["able", "after", "again", "beach", "bridge", "candle", "clever", "dream", "early", "forest", "garden", "gentle", "harbor", "island", "journey", "kettle", "ladder", "meadow", "narrow", "ocean", "pencil", "quiet", "river", "silver", "timber", "under", "velvet", "window", "yellow", "zephyr"]
+8
View File
@@ -0,0 +1,8 @@
[
{
"format": "##{x}{x}{x}{x}{x}{x}",
"x": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"],
"weight": 2
},
["Blå", "Brun", "Grå", "Grön", "Gul", "Himmelsblå", "Lila", "Orange", "Rosa", "Röd", "Svart", "Vit"]
]
+14
View File
@@ -0,0 +1,14 @@
[
{
"format": "{base} {suffix}",
"base": [
{
"format": "{a}{b}",
"a": ["Berg", "Blå", "Järn", "Nord", "Ny", "Sjö", "Skog", "Sol", "Sten", "Stor"],
"b": ["bygg", "data", "gruppen", "handel", "konsult", "logistik", "produkter", "system", "teknik", "verkstad"]
},
["Aktiebolaget Vega", "Ekonomihuset", "Lindberg", "Nordström", "Svensson & Co"]
],
"suffix": ["AB", "AB", "AB", "HB", "KB"]
}
]
+15
View File
@@ -0,0 +1,15 @@
[
{
"format": "{year}-{month}-{day}",
"month": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"],
"day": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"],
"year": [
{ "format": "#1970" },
{ "format": "#1980" },
{ "format": "#1990", "weight": 2 },
{ "format": "2#0#00", "weight": 3 },
{ "format": "2#0#10", "weight": 3 },
{ "format": "2#020", "weight": 2 }
]
}
]
+15
View File
@@ -0,0 +1,15 @@
[
{
"format": "{local}@{domain}",
"local": [
{
"format": "{first}{sep}{last}{n}",
"first": ["erik", "anna", "lars", "karin", "johan", "sara", "anders", "emma", "per", "sofia"],
"last": ["andersson", "johansson", "karlsson", "nilsson", "eriksson", "larsson", "olsson", "persson", "svensson", "gustafsson"],
"sep": [".", "_", "."],
"n": ["", "", "1", "7", "42"]
}
],
"domain": ["example.com", "example.org", "example.net", "mail.example.com"]
}
]
+11
View File
@@ -0,0 +1,11 @@
[
{
"format": "{o}.{o}.{o}.{o}",
"weight": 8,
"o": [{ "format": "0", "weight": 3 }, { "format": "10", "weight": 4 }, { "format": "#100", "weight": 2 }]
},
{
"format": "{g}:{g}:{g}:{g}:{g}:{g}:{g}:{g}",
"g": [{ "format": "{x}{x}{x}{x}", "x": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"] }]
}
]
+7
View File
@@ -0,0 +1,7 @@
[
{
"format": "{amt}{ore} kr",
"amt": [{ "format": "10", "weight": 3 }, { "format": "100", "weight": 2 }, { "format": "1 000" }],
"ore": ["", { "format": ",{c}", "c": [{ "format": "00" }], "weight": 0.4 }]
}
]
+10
View File
@@ -0,0 +1,10 @@
[
{
"format": "{lead} {adj} {noun} {verb} {prep} {adj} {noun}.",
"lead": ["Alla", "Den", "En", "Ett", "Varje"],
"adj": ["gamla", "gyllene", "ljusa", "mörka", "snabba", "stora", "tysta", "vilda"],
"noun": ["berget", "floden", "hamnen", "motorn", "räven", "signalen", "skogen", "trädgården"],
"verb": ["bygger", "bär", "följer", "formar", "korsar", "når", "vaktar"],
"prep": ["bortom", "genom", "längs", "under", "över"]
}
]
+8
View File
@@ -0,0 +1,8 @@
[
{
"format": "{yy}{month}{day}-0000",
"yy": [{ "format": "00" }],
"month": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"],
"day": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"]
}
]
+8
View File
@@ -0,0 +1,8 @@
[
{
"format": "{hour}:{minute}{sec}",
"hour": [{ "format": "#00", "weight": 4 }, { "format": "#10", "weight": 4 }, { "format": "2{t}", "t": ["0", "1", "2", "3"], "weight": 2 }],
"minute": [{ "format": "{t}0", "t": ["0", "1", "2", "3", "4", "5"] }],
"sec": ["", { "format": ":{s}", "s": [{ "format": "{t}0", "t": ["0", "1", "2", "3", "4", "5"] }], "weight": 0.4 }]
}
]
+7
View File
@@ -0,0 +1,7 @@
[
{
"format": "https://{host}{path}",
"host": ["example.com", "www.example.com", "example.org", "blogg.example.net", "butik.example.com", "example.se"],
"path": ["", { "format": "/{seg}", "seg": ["om", "produkter", "blogg", "kontakt", "hjalp", "artiklar", "logga-in", "nyheter", "start", "sok"] }]
}
]
+9
View File
@@ -0,0 +1,9 @@
[
{
"format": "{first}{sep}{last}{n}",
"first": ["erik", "anna", "lars", "karin", "johan", "sara", "anders", "emma", "per", "sofia"],
"last": ["berg", "lind", "holm", "strom", "dahl", "ek", "sjo", "ahl", "falk", "ros"],
"sep": ["", "", ".", "_"],
"n": ["", "", "", "1", "7", "23", "99"]
}
]
+6
View File
@@ -0,0 +1,6 @@
[
{
"format": "{x}{x}{x}{x}{x}{x}{x}{x}-{x}{x}{x}{x}-{x}{x}{x}{x}-{x}{x}{x}{x}-{x}{x}{x}{x}{x}{x}{x}{x}{x}{x}{x}{x}",
"x": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]
}
]
+8
View File
@@ -0,0 +1,8 @@
[
{
"format": "{pre}{n}.{n}.{n}{suffix}",
"n": [{ "format": "0", "weight": 3 }, { "format": "10" }],
"pre": ["", { "format": "v", "weight": 0.4 }],
"suffix": ["", { "format": "-{tag}.{m}", "tag": ["alpha", "beta", "rc"], "m": ["1", "2", "3"], "weight": 0.3 }]
}
]
+1
View File
@@ -0,0 +1 @@
["dröm", "fjäll", "glädje", "hav", "höst", "insjö", "källa", "ljus", "morgon", "norr", "ödmjuk", "penna", "regn", "skog", "sommar", "stjärna", "träd", "under", "vinter", "väg", "åker", "äng", "öken", "blomma", "fågel", "gata", "höjd", "klocka", "ljud", "måne"]