Skip to content

Commit 57bdc58

Browse files
authored
Merge pull request #17 from Daniel-Ric/feature/2026-02-03/build-everything-in-english
Translate CLI to English and modernize terminal UI
2 parents a9e869c + d3ef56c commit 57bdc58

8 files changed

Lines changed: 63 additions & 58 deletions

File tree

internal/cli/cli.go

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func (a *App) collectLookupConfig() (LookupConfig, error) {
157157
}
158158

159159
func (a *App) askMode() (Mode, error) {
160-
index, err := selectOption("Startmodus", []string{"UWP/TCP Abfrage", "IP Lookup"})
160+
index, err := selectOption("Start mode", []string{"UWP/TCP query", "IP lookup"})
161161
if err != nil {
162162
return "", err
163163
}
@@ -181,12 +181,12 @@ func (a *App) askEdition() (ping.Edition, error) {
181181
func (a *App) askHost() (string, error) {
182182
var errMsg string
183183
for {
184-
value, err := promptInput("Server Host", "z.B. play.example.com", errMsg)
184+
value, err := promptInput("Server host", "e.g. play.example.com", errMsg)
185185
if err != nil {
186186
return "", err
187187
}
188188
if strings.TrimSpace(value) == "" {
189-
errMsg = "Host darf nicht leer sein"
189+
errMsg = "Host cannot be empty"
190190
continue
191191
}
192192
return value, nil
@@ -196,21 +196,21 @@ func (a *App) askHost() (string, error) {
196196
func (a *App) askBaseHost() (string, error) {
197197
var errMsg string
198198
for {
199-
value, err := promptInput("IP/Domain ohne Endung", "z.B. example", errMsg)
199+
value, err := promptInput("IP/domain without ending", "e.g. example", errMsg)
200200
if err != nil {
201201
return "", err
202202
}
203203
value = strings.TrimSpace(value)
204204
if value == "" {
205-
errMsg = "Wert darf nicht leer sein"
205+
errMsg = "Value cannot be empty"
206206
continue
207207
}
208208
return value, nil
209209
}
210210
}
211211

212212
func (a *App) askSubdomainChoice() ([]string, error) {
213-
index, err := selectOption("Subdomain", []string{"Eigene Subdomains", "Subdomain-Pool", "Eigene + Pool"})
213+
index, err := selectOption("Subdomains", []string{"Custom subdomains", "Subdomain pool", "Custom + pool"})
214214
if err != nil {
215215
return nil, err
216216
}
@@ -231,7 +231,7 @@ func (a *App) askSubdomainChoice() ([]string, error) {
231231
func (a *App) askCustomSubdomains() ([]string, error) {
232232
var errMsg string
233233
for {
234-
value, err := promptInput("Subdomains (optional, kommasepariert)", "z.B. play, mc (leer lassen für keine)", errMsg)
234+
value, err := promptInput("Subdomains (optional, comma-separated)", "e.g. play, mc (leave empty for none)", errMsg)
235235
if err != nil {
236236
return nil, err
237237
}
@@ -241,15 +241,15 @@ func (a *App) askCustomSubdomains() ([]string, error) {
241241
}
242242
list := splitList(value)
243243
if len(list) == 0 {
244-
errMsg = "Subdomain darf nicht leer sein"
244+
errMsg = "Subdomain cannot be empty"
245245
continue
246246
}
247247
return list, nil
248248
}
249249
}
250250

251251
func (a *App) askDomainEndings() ([]string, error) {
252-
index, err := selectOption("Domain-Endung", []string{"Eigene Endungen", "Endungs-Pool", "Eigene + Pool"})
252+
index, err := selectOption("Domain endings", []string{"Custom endings", "Ending pool", "Custom + pool"})
253253
if err != nil {
254254
return nil, err
255255
}
@@ -277,13 +277,13 @@ func (a *App) askDomainEndings() ([]string, error) {
277277
func (a *App) askCustomEndings() ([]string, error) {
278278
var errMsg string
279279
for {
280-
value, err := promptInput("Domain-Endungen (kommasepariert)", "z.B. com, de", errMsg)
280+
value, err := promptInput("Domain endings (comma-separated)", "e.g. com, net", errMsg)
281281
if err != nil {
282282
return nil, err
283283
}
284284
value = strings.TrimSpace(value)
285285
if value == "" {
286-
errMsg = "Endung darf nicht leer sein"
286+
errMsg = "Ending cannot be empty"
287287
continue
288288
}
289289
rawList := splitList(value)
@@ -296,7 +296,7 @@ func (a *App) askCustomEndings() ([]string, error) {
296296
list = append(list, normalized)
297297
}
298298
if len(list) == 0 {
299-
errMsg = "Endung darf nicht leer sein"
299+
errMsg = "Ending cannot be empty"
300300
continue
301301
}
302302
return list, nil
@@ -322,7 +322,7 @@ func (a *App) askPort(edition ping.Edition) (int, error) {
322322
defaultPort := ping.DefaultPort(edition)
323323
var errMsg string
324324
for {
325-
value, err := promptInput(fmt.Sprintf("Port (%d)", defaultPort), "Leer lassen für Standardport", errMsg)
325+
value, err := promptInput(fmt.Sprintf("Port (%d)", defaultPort), "Leave empty for the default port", errMsg)
326326
if err != nil {
327327
return 0, err
328328
}
@@ -335,7 +335,7 @@ func (a *App) askPort(edition ping.Edition) (int, error) {
335335
continue
336336
}
337337
if port == 0 {
338-
errMsg = "Port darf nicht leer sein"
338+
errMsg = "Port cannot be empty"
339339
continue
340340
}
341341
return port, nil
@@ -355,9 +355,9 @@ func (a *App) executeDirect(config DirectConfig) error {
355355
ctx, cancel := context.WithTimeout(context.Background(), a.inputTimeout)
356356
defer cancel()
357357

358-
resultText, err := withSpinner("Abfrage", func(frame int) string {
358+
resultText, err := withSpinner("Query", func(frame int) string {
359359
_ = frame
360-
return "Server wird abgefragt"
360+
return "Querying server"
361361
}, 120*time.Millisecond, func() (string, error) {
362362
result, err := ping.Execute(ctx, config.Edition, config.Host, config.Port)
363363
if err != nil {
@@ -369,7 +369,7 @@ func (a *App) executeDirect(config DirectConfig) error {
369369
return err
370370
}
371371

372-
renderTextPage("Ergebnis", resultText)
372+
renderTextPage("Result", resultText)
373373
return nil
374374
}
375375

@@ -379,7 +379,7 @@ func (a *App) executeLookup(config LookupConfig) error {
379379
var current atomic.Value
380380
current.Store(ping.LookupProgress{})
381381

382-
resultText, err := withSpinner("IP Lookup", func(frame int) string {
382+
resultText, err := withSpinner("IP lookup", func(frame int) string {
383383
progress := current.Load().(ping.LookupProgress)
384384
return formatLookupProgress(progress, frame)
385385
}, 120*time.Millisecond, func() (string, error) {
@@ -403,22 +403,22 @@ func (a *App) executeLookup(config LookupConfig) error {
403403
return err
404404
}
405405

406-
renderTextPage("Ergebnis", resultText)
406+
renderTextPage("Result", resultText)
407407
return nil
408408
}
409409

410410
func formatLookupProgress(progress ping.LookupProgress, frame int) string {
411411
if progress.Total == 0 {
412-
return "Domains werden überprüft"
412+
return "Checking domains"
413413
}
414414
subdomain := progress.Subdomain
415415
if subdomain == "" {
416-
subdomain = "(keine)"
416+
subdomain = "(none)"
417417
}
418418
bar := buildProgressBar(progress.Completed, progress.Total, frame, 20)
419419
percent := (float64(progress.Completed) / float64(progress.Total)) * 100
420420
return fmt.Sprintf(
421-
"%s %d/%d (%.0f%%)\nSubdomain: %-12s | Endung: %-10s | Host: %s",
421+
"%s %d/%d (%.0f%%)\nSubdomain: %s\nEnding: %s\nHost: %s",
422422
bar,
423423
progress.Completed,
424424
progress.Total,
@@ -455,7 +455,7 @@ func buildProgressBar(completed, total, frame, width int) string {
455455
}
456456

457457
func (a *App) askAgain() (bool, error) {
458-
index, err := selectOption("Nächster Schritt", []string{"Neue Abfrage", "Beenden"})
458+
index, err := selectOption("Next step", []string{"New query", "Exit"})
459459
if err != nil {
460460
return false, err
461461
}
@@ -464,12 +464,13 @@ func (a *App) askAgain() (bool, error) {
464464

465465
func formatLookupResult(result ping.LookupResult) string {
466466
var builder strings.Builder
467-
builder.WriteString(fmt.Sprintf("Kombinationen geprüft: %d/%d\n", result.Completed, result.Attempts))
468-
builder.WriteString(fmt.Sprintf("Treffer: %d\n", len(result.Matches)))
467+
builder.WriteString("Summary\n")
468+
builder.WriteString(fmt.Sprintf("• Checked combinations: %d/%d\n", result.Completed, result.Attempts))
469+
builder.WriteString(fmt.Sprintf("• Matches: %d\n", len(result.Matches)))
469470
builder.WriteString("\n")
470471

471472
if len(result.Matches) == 0 {
472-
builder.WriteString("Keine passenden Server gefunden.")
473+
builder.WriteString("No matching servers found.")
473474
return builder.String()
474475
}
475476

internal/cli/lookup_endings.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func loadDomainEndings() ([]string, error) {
2525

2626
iana, err := fetchIANATLDs(ctx)
2727
if err != nil {
28-
domainEndingsErr = fmt.Errorf("konnte endungs-pool nicht laden: %w", err)
28+
domainEndingsErr = fmt.Errorf("could not load ending pool: %w", err)
2929
domainEndingsCache = domainEndingPool
3030
return
3131
}
@@ -49,7 +49,7 @@ func fetchIANATLDs(ctx context.Context) ([]string, error) {
4949
}
5050
defer resp.Body.Close()
5151
if resp.StatusCode != http.StatusOK {
52-
return nil, fmt.Errorf("iana antwort: %s", resp.Status)
52+
return nil, fmt.Errorf("iana response: %s", resp.Status)
5353
}
5454

5555
scanner := bufio.NewScanner(resp.Body)
@@ -65,7 +65,7 @@ func fetchIANATLDs(ctx context.Context) ([]string, error) {
6565
return nil, err
6666
}
6767
if len(list) == 0 {
68-
return nil, fmt.Errorf("keine endungen gefunden")
68+
return nil, fmt.Errorf("no endings found")
6969
}
7070
return list, nil
7171
}

internal/cli/ui.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const (
1717
colorBold = "\033[1m"
1818
)
1919

20-
var errAborted = errors.New("abgebrochen")
20+
var errAborted = errors.New("aborted")
2121

2222
func supportsColor() bool {
2323
if os.Getenv("NO_COLOR") != "" {
@@ -36,12 +36,12 @@ func style(text, color string) string {
3636

3737
func promptInput(label, hint, errMsg string) (string, error) {
3838
clearScreen()
39-
fmt.Println(style(label, colorAccent))
39+
renderHeader(label)
4040
if hint != "" {
41-
fmt.Println(style(hint, colorDim))
41+
fmt.Println(style(fmt.Sprintf("Hint: %s", hint), colorDim))
4242
}
4343
if errMsg != "" {
44-
fmt.Println(style(errMsg, colorWarn))
44+
fmt.Println(style(fmt.Sprintf("⚠ %s", errMsg), colorWarn))
4545
}
4646
fmt.Println()
4747
fmt.Print(style("> ", colorAccent))
@@ -55,7 +55,7 @@ func promptInput(label, hint, errMsg string) (string, error) {
5555

5656
func selectOption(title string, options []string) (int, error) {
5757
if len(options) == 0 {
58-
return 0, errors.New("keine Auswahloptionen verfügbar")
58+
return 0, errors.New("no options available")
5959
}
6060
fd := int(os.Stdin.Fd())
6161
state, err := makeRaw(fd)
@@ -146,18 +146,18 @@ func renderMenuBlock(title string, options []string, selected int, hint string,
146146
clearScreen()
147147
}
148148
lines := 0
149-
lines += printLine(style(title, colorAccent))
150-
lines += printLine("")
149+
lines += printLine(style(title, colorAccent+colorBold))
150+
lines += printLine(style(strings.Repeat("─", 44), colorDim))
151151
for i, option := range options {
152152
if i == selected {
153-
lines += printLine(fmt.Sprintf("%s %s", style(">", colorAccent+colorBold), style(option, colorBold)))
153+
lines += printLine(fmt.Sprintf("%s %s", style("", colorAccent+colorBold), style(option, colorBold)))
154154
continue
155155
}
156-
lines += printLine(fmt.Sprintf(" %s", option))
156+
lines += printLine(fmt.Sprintf(" %s", option))
157157
}
158158
lines += printLine("")
159159
if hint == "" {
160-
hint = "Pfeiltasten (oder W/S), Enter zum Bestätigen"
160+
hint = "Use ↑/↓ (or W/S), Enter to confirm"
161161
}
162162
lines += printLine(style(hint, colorDim))
163163
return lines
@@ -198,8 +198,7 @@ func renderTextPage(title, content string) {
198198

199199
func renderPage(title string, lines []string) {
200200
clearScreen()
201-
fmt.Println(style(title, colorAccent))
202-
fmt.Println()
201+
renderHeader(title)
203202
for _, line := range lines {
204203
if strings.TrimSpace(line) == "" {
205204
fmt.Println()
@@ -213,6 +212,12 @@ func renderSpinnerPage(title, message, frame string) {
213212
renderPage(title, []string{fmt.Sprintf("%s %s", style(message, colorDim), style(frame, colorAccent))})
214213
}
215214

215+
func renderHeader(title string) {
216+
fmt.Println(style(title, colorAccent+colorBold))
217+
fmt.Println(style(strings.Repeat("─", 44), colorDim))
218+
fmt.Println()
219+
}
220+
216221
func withSpinner(title string, message func(frame int) string, tick time.Duration, action func() (string, error)) (string, error) {
217222
resultCh := make(chan struct {
218223
result string
@@ -227,8 +232,7 @@ func withSpinner(title string, message func(frame int) string, tick time.Duratio
227232
}()
228233

229234
clearScreen()
230-
fmt.Println(style(title, colorAccent))
231-
fmt.Println()
235+
renderHeader(title)
232236

233237
frames := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
234238
ticker := time.NewTicker(tick)

internal/ping/bedrock.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ func mustHex(s string) []byte {
2323

2424
func parsePong(buf []byte) (BedrockPong, error) {
2525
if len(buf) < 35 {
26-
return BedrockPong{}, fmt.Errorf("pong zu kurz: %d bytes", len(buf))
26+
return BedrockPong{}, fmt.Errorf("pong too short: %d bytes", len(buf))
2727
}
2828

2929
if buf[0] != 0x1c {
30-
return BedrockPong{}, fmt.Errorf("unerwartete packet id: 0x%02x", buf[0])
30+
return BedrockPong{}, fmt.Errorf("unexpected packet id: 0x%02x", buf[0])
3131
}
3232

3333
nameLen := int(binary.BigEndian.Uint16(buf[33:35]))
3434
if 35+nameLen > len(buf) {
35-
return BedrockPong{}, fmt.Errorf("ungültige advertise länge: %d (buf=%d)", nameLen, len(buf))
35+
return BedrockPong{}, fmt.Errorf("invalid advertise length: %d (buf=%d)", nameLen, len(buf))
3636
}
3737

3838
advertise := string(buf[35 : 35+nameLen])

internal/ping/execute.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func Execute(ctx context.Context, edition Edition, host string, port int) (Resul
2626
}
2727
return pong, nil
2828
default:
29-
return nil, fmt.Errorf("unbekannte edition: %s", edition)
29+
return nil, fmt.Errorf("unknown edition: %s", edition)
3030
}
3131
}
3232

@@ -44,10 +44,10 @@ func ParsePort(value string) (int, error) {
4444
}
4545
port, err := strconv.Atoi(trimmed)
4646
if err != nil {
47-
return 0, fmt.Errorf("ungültiger port")
47+
return 0, fmt.Errorf("invalid port")
4848
}
4949
if port < 1 || port > 65535 {
50-
return 0, fmt.Errorf("port außerhalb des bereichs 1-65535")
50+
return 0, fmt.Errorf("port out of range (1-65535)")
5151
}
5252
return port, nil
5353
}

internal/ping/java.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func PingJava(ctx context.Context, host string, port int) (JavaStatus, error) {
4646
return JavaStatus{}, err
4747
}
4848
if packetID != 0x00 {
49-
return JavaStatus{}, fmt.Errorf("unerwartete status packet id: %d", packetID)
49+
return JavaStatus{}, fmt.Errorf("unexpected status packet id: %d", packetID)
5050
}
5151

5252
statusJSON, err := readString(respReader)
@@ -109,7 +109,7 @@ func pingJavaLatency(conn net.Conn) (int64, error) {
109109
return 0, err
110110
}
111111
if packetID != 0x01 {
112-
return 0, fmt.Errorf("unerwartete pong packet id: %d", packetID)
112+
return 0, fmt.Errorf("unexpected pong packet id: %d", packetID)
113113
}
114114
var sent uint64
115115
if err := binary.Read(respReader, binary.BigEndian, &sent); err != nil {

0 commit comments

Comments
 (0)