HASH cd074d8f00f4
DATE 2025-09-16
SUBJECT ookstats: fix non ascii player names
FILES 1 CHANGED
HASH cd074d8f00f4
DATE 2025-09-16
SUBJECT ookstats: fix non ascii player names
FILES 1 CHANGED
┌─ nix/pkgs/ookstats/src/cmd/generate.go ────────────────────────────────────┐
│ diff --git a/nix/pkgs/ookstats/src/cmd/generate.go b/nix/pkgs/ookstats/src/cmd/gen │
│ erate.go │
│ index 6df34ed..3fd926f 100644 │
│ --- a/nix/pkgs/ookstats/src/cmd/generate.go │
│ +++ b/nix/pkgs/ookstats/src/cmd/generate.go │
│ @@ -10,6 +10,7 @@ import ( │
│ "strings" │
│ "sync" │
│ "time" │
│ + "unicode" │
│ │
│ "github.com/spf13/cobra" │
│ _ "github.com/tursodatabase/go-libsql" │
│ @@ -789,29 +790,50 @@ func generateSinglePlayerJSON(player PlayerData, bestRunsMap │
│ map[int64][]BestRun │
│ } │
│ │
│ // safeSlugName converts an arbitrary player name to a safe lowercase filename wi │
│ thout path separators. │
│ +// It preserves Unicode letters/numbers (including diacritics), matching frontend │
│ expectations │
│ +// that URLs may contain non-ASCII characters. Only path separators are removed a │
│ nd spaces -> '-'. │
│ func safeSlugName(s string) string { │
│ s = strings.ToLower(strings.TrimSpace(s)) │
│ // replace path separators explicitly │
│ s = strings.ReplaceAll(s, "/", "-") │
│ s = strings.ReplaceAll(s, "\\", "-") │
│ - // allow a-z, 0-9, '-', '_' only; replace spaces with '-' │
│ + // allow unicode letters/digits and '-', '_' ; replace spaces with '-' │
│ out := make([]rune, 0, len(s)) │
│ for _, r := range s { │
│ - switch { │
│ - case r >= 'a' && r <= 'z': │
│ - out = append(out, r) │
│ - case r >= '0' && r <= '9': │
│ + if r == ' ' { │
│ + out = append(out, '-') │
│ + continue │
│ + } │
│ + if r == '-' || r == '_' { │
│ out = append(out, r) │
│ - case r == '-' || r == '_': │
│ + continue │
│ + } │
│ + // Keep unicode letters and digits; drop other punctuation/symbols │
│ + if unicode.IsLetter(r) || unicode.IsDigit(r) { │
│ out = append(out, r) │
│ - case r == ' ': │
│ - out = append(out, '-') │
│ - default: │
│ - // drop other runes │
│ + continue │
│ + } │
│ + // else: drop │
│ + } │
│ + // collapse multiple dashes │
│ + cleaned := make([]rune, 0, len(out)) │
│ + prevDash := false │
│ + for _, r := range out { │
│ + if r == '-' { │
│ + if !prevDash { cleaned = append(cleaned, r) } │
│ + prevDash = true │
│ + } else { │
│ + cleaned = append(cleaned, r) │
│ + prevDash = false │
│ } │
│ } │
│ - if len(out) == 0 { return "player" } │
│ - return string(out) │
│ + if len(cleaned) == 0 { return "player" } │
│ + // trim leading/trailing '-' │
│ + i := 0 │
│ + j := len(cleaned) │
│ + for i < j && cleaned[i] == '-' { i++ } │
│ + for j > i && cleaned[j-1] == '-' { j-- } │
│ + return string(cleaned[i:j]) │
│ } │
│ │
│ func writeJSONFile(path string, v any) error { │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ .../ookstats/src/cmd/generate.go ───┐
│ diff --git a/nix/pkgs/ookstats/src/cmd/gener │
│ ate.go b/nix/pkgs/ookstats/src/cmd/generate. │
│ go │
│ index 6df34ed..3fd926f 100644 │
│ --- a/nix/pkgs/ookstats/src/cmd/generate.go │
│ +++ b/nix/pkgs/ookstats/src/cmd/generate.go │
│ @@ -10,6 +10,7 @@ import ( │
│ "strings" │
│ "sync" │
│ "time" │
│ + "unicode" │
│ │
│ "github.com/spf13/cobra" │
│ _ "github.com/tursodatabase/go-libsql" │
│ @@ -789,29 +790,50 @@ func generateSinglePla │
│ yerJSON(player PlayerData, bestRunsMap map[i │
│ nt64][]BestRun │
│ } │
│ │
│ // safeSlugName converts an arbitrary playe │
│ r name to a safe lowercase filename without │
│ path separators. │
│ +// It preserves Unicode letters/numbers (in │
│ cluding diacritics), matching frontend expec │
│ tations │
│ +// that URLs may contain non-ASCII characte │
│ rs. Only path separators are removed and spa │
│ ces -> '-'. │
│ func safeSlugName(s string) string { │
│ s = strings.ToLower(strings.TrimSpace(s │
│ )) │
│ // replace path separators explicitly │
│ s = strings.ReplaceAll(s, "/", "-") │
│ s = strings.ReplaceAll(s, "\\", "-") │
│ - // allow a-z, 0-9, '-', '_' only; repla │
│ ce spaces with '-' │
│ + // allow unicode letters/digits and '-' │
│ , '_' ; replace spaces with '-' │
│ out := make([]rune, 0, len(s)) │
│ for _, r := range s { │
│ - switch { │
│ - case r >= 'a' && r <= 'z': │
│ - out = append(out, r) │
│ - case r >= '0' && r <= '9': │
│ + if r == ' ' { │
│ + out = append(out, '-') │
│ + continue │
│ + } │
│ + if r == '-' || r == '_' { │
│ out = append(out, r) │
│ - case r == '-' || r == '_': │
│ + continue │
│ + } │
│ + // Keep unicode letters and digits; │
│ drop other punctuation/symbols │
│ + if unicode.IsLetter(r) || unicode.I │
│ sDigit(r) { │
│ out = append(out, r) │
│ - case r == ' ': │
│ - out = append(out, '-') │
│ - default: │
│ - // drop other runes │
│ + continue │
│ + } │
│ + // else: drop │
│ + } │
│ + // collapse multiple dashes │
│ + cleaned := make([]rune, 0, len(out)) │
│ + prevDash := false │
│ + for _, r := range out { │
│ + if r == '-' { │
│ + if !prevDash { cleaned = append │
│ (cleaned, r) } │
│ + prevDash = true │
│ + } else { │
│ + cleaned = append(cleaned, r) │
│ + prevDash = false │
│ } │
│ } │
│ - if len(out) == 0 { return "player" } │
│ - return string(out) │
│ + if len(cleaned) == 0 { return "player" │
│ } │
│ + // trim leading/trailing '-' │
│ + i := 0 │
│ + j := len(cleaned) │
│ + for i < j && cleaned[i] == '-' { i++ } │
│ + for j > i && cleaned[j-1] == '-' { j-- │
│ } │
│ + return string(cleaned[i:j]) │
│ } │
│ │
│ func writeJSONFile(path string, v any) erro │
│ r { │
└──────────────────────────────────────────────┘
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET