v0.308.0: controller batch 2026-10-12 — clear-address warning (R-922), secret sweep over every page (R-255), read_failed (R-266), no NVMe heat band (R-333), share globe (R-577), Hungarian dates and numbers (R-562), lost-drive option pinned (R-521)
Operator rulings 2026-10-12, 09 §3 decisions 196, 204-207, 223, 227. MinAgent 0.131.0 unchanged. R-255's sweep found one leak, fixed here: an optional-config secret_input was rendered into its settings page; it is now revealed on demand. Hungarian parity fixtures re-captured once for R-562. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ── Dates and numbers per language (R-562, v0.308.0, `09` §3 decision 223) ────────────────────────
|
||||
//
|
||||
// The ONE place the dashboard decides how a date, a time or a decimal number looks:
|
||||
//
|
||||
// Hungarian 2026. 10. 10. 15:04 1,5 GB (the Hungarian date order with its dots; decimal COMMA)
|
||||
// English 2026-10-10 15:04 1.5 GB (ISO-like; decimal point)
|
||||
//
|
||||
// Every function takes the time ALREADY in the zone it is shown in (the dashboard shows Budapest
|
||||
// local time; the callers convert). An unknown language formats as Hungarian, like every other
|
||||
// fallback in this package. Pinned by TestFormat_* in format_test.go.
|
||||
|
||||
// FormatDateTime renders a date with hour and minute.
|
||||
func FormatDateTime(lang string, t time.Time) string {
|
||||
if Normalize(lang) == "en" {
|
||||
return t.Format("2006-01-02 15:04")
|
||||
}
|
||||
return t.Format("2006. 01. 02. 15:04")
|
||||
}
|
||||
|
||||
// FormatDate renders a date alone.
|
||||
func FormatDate(lang string, t time.Time) string {
|
||||
if Normalize(lang) == "en" {
|
||||
return t.Format("2006-01-02")
|
||||
}
|
||||
return t.Format("2006. 01. 02.")
|
||||
}
|
||||
|
||||
// FormatMonthDayTime renders a date in the current year: month, day, hour and minute.
|
||||
func FormatMonthDayTime(lang string, t time.Time) string {
|
||||
if Normalize(lang) == "en" {
|
||||
return t.Format("01-02 15:04")
|
||||
}
|
||||
return t.Format("01. 02. 15:04")
|
||||
}
|
||||
|
||||
// FormatDecimal renders v with prec digits after the decimal separator of lang.
|
||||
func FormatDecimal(lang string, v float64, prec int) string {
|
||||
s := strconv.FormatFloat(v, 'f', prec, 64)
|
||||
if Normalize(lang) == "en" {
|
||||
return s
|
||||
}
|
||||
return strings.Replace(s, ".", ",", 1)
|
||||
}
|
||||
|
||||
// LocalizeDecimals rewrites the decimal POINT of every number in an already formatted short string
|
||||
// ("1.5 GB", "Immich (95.0 GB)") into lang's separator. Only a point with a digit on BOTH sides is
|
||||
// touched, so a sentence's full stop, a version ("0.307.0" — see below) or an address is not a target
|
||||
// by accident: a dot preceded by a digit and followed by a digit is rewritten only when the number it
|
||||
// belongs to has exactly ONE dot. English returns s unchanged.
|
||||
//
|
||||
// For strings another package formatted with %.1f (appbackup.HumanizeBytes and its siblings) and
|
||||
// handed to a page as data. A new formatter should call FormatDecimal instead.
|
||||
func LocalizeDecimals(lang, s string) string {
|
||||
if Normalize(lang) == "en" || !strings.Contains(s, ".") {
|
||||
return s
|
||||
}
|
||||
b := []byte(s)
|
||||
isDigit := func(c byte) bool { return c >= '0' && c <= '9' }
|
||||
for i := 1; i < len(b)-1; i++ {
|
||||
if b[i] != '.' || !isDigit(b[i-1]) || !isDigit(b[i+1]) {
|
||||
continue
|
||||
}
|
||||
// The whole digit-and-dot run around this dot: more than one dot is a version or an address.
|
||||
start, end := i, i
|
||||
for start > 0 && (isDigit(b[start-1]) || b[start-1] == '.') {
|
||||
start--
|
||||
}
|
||||
for end < len(b)-1 && (isDigit(b[end+1]) || b[end+1] == '.') {
|
||||
end++
|
||||
}
|
||||
if strings.Count(string(b[start:end+1]), ".") != 1 {
|
||||
i = end
|
||||
continue
|
||||
}
|
||||
b[i] = ','
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
Reference in New Issue
Block a user