locale.go RawHistoryFolder
1package environment
2
3import (
4 "fmt"
5 "os"
6 "os/exec"
7 "strings"
8
9 "github.com/bnema/dumber/internal/logging"
10)
11
12// DetectAndSetKeyboardLocale attempts to determine the current keyboard layout
13// and hints it to the webkit input layer for accelerator compatibility.
14func DetectAndSetKeyboardLocale() {
15 const colonSplitParts = 2 // Number of parts when splitting on colon
16
17 var locale string
18
19 // 0) Explicit override
20 locale = os.Getenv("DUMBER_KEYBOARD_LOCALE")
21 if locale == "" {
22 locale = os.Getenv("DUMB_BROWSER_KEYBOARD_LOCALE") // legacy prefix
23 }
24
25 // 1) XKB env override (actual keyboard layout)
26 if locale == "" {
27 locale = os.Getenv("XKB_DEFAULT_LAYOUT")
28 }
29
30 // 2) Best-effort probe of localectl/setxkbmap (actual keyboard layout)
31 // Check these BEFORE LANG since LANG is system language, not keyboard layout
32 if locale == "" {
33 if out, err := exec.Command("localectl", "status").Output(); err == nil {
34 s := string(out)
35 for _, line := range strings.Split(s, "\n") {
36 line = strings.TrimSpace(line)
37 if strings.HasPrefix(strings.ToLower(line), "x11 layout:") || strings.HasPrefix(strings.ToLower(line), "keyboard layout:") {
38 parts := strings.SplitN(line, ":", colonSplitParts)
39 if len(parts) == colonSplitParts {
40 cand := strings.TrimSpace(parts[1])
41 if cand != "" {
42 locale = cand
43 logging.Debug(fmt.Sprintf("[locale] keyboard layout from localectl: %s", locale))
44 }
45 }
46 break
47 }
48 }
49 }
50 }
51 if locale == "" {
52 if out, err := exec.Command("setxkbmap", "-query").Output(); err == nil {
53 s := string(out)
54 for _, line := range strings.Split(s, "\n") {
55 line = strings.TrimSpace(line)
56 if strings.HasPrefix(strings.ToLower(line), "layout:") {
57 parts := strings.SplitN(line, ":", colonSplitParts)
58 if len(parts) == colonSplitParts {
59 cand := strings.TrimSpace(parts[1])
60 if cand != "" {
61 locale = cand
62 logging.Debug(fmt.Sprintf("[locale] keyboard layout from setxkbmap: %s", locale))
63 }
64 }
65 break
66 }
67 }
68 }
69 }
70
71 // 3) Fallback to system locale (language, not keyboard layout)
72 if locale == "" {
73 locale = os.Getenv("LC_ALL")
74 }
75 if locale == "" {
76 locale = os.Getenv("LANG")
77 }
78 if locale == "" {
79 locale = os.Getenv("LC_CTYPE")
80 }
81 // Trim variants from locale
82 if locale != "" {
83 if i := strings.IndexByte(locale, '.'); i > 0 {
84 locale = locale[:i]
85 }
86 if i := strings.IndexByte(locale, '@'); i > 0 {
87 locale = locale[:i]
88 }
89 if i := strings.IndexByte(locale, '_'); i > 0 {
90 locale = locale[:i]
91 }
92 logging.Debug(fmt.Sprintf("[locale] keyboard layout from system locale: %s", locale))
93 }
94
95 if locale == "" {
96 locale = "en"
97 logging.Debug(fmt.Sprintf("[locale] keyboard layout defaulted to: %s", locale))
98 }
99
100 logging.Info(fmt.Sprintf("[locale] keyboard locale detected: %s", locale))
101}