Revise the messages to make them localizable. Note: The log messages are not marked for localization. Probably, we want to keep log files in English for easier global troubleshooting. Having a user run `go generate` requires a valid and up-to-date Go environment. Rather than instructing users how to setup the environment correctly, the `go generate` was integrated into build.bat. This reuses the Go building environment downloaded and prepared by build.bat to provide controllable and consistent result. Use `make generate` on Linux. As the zgotext.go output varies for GOARCH=386 and amd64, one had to be chosen to provide stable output. The former is the first one to build in build.bat. Signed-off-by: Simon Rozman <simon@rozman.si>master
@@ -6,6 +6,7 @@ | |||
/amd64 | |||
# Misc | |||
/locales/*/out.gotext.json | |||
/sign.bat | |||
*.swp | |||
*.bak | |||
@@ -46,6 +46,11 @@ fmt: export GOARCH := amd64 | |||
fmt: | |||
go fmt ./... | |||
generate: export CC := i686-w64-mingw32-gcc | |||
generate: export GOARCH := 386 | |||
generate: | |||
go generate | |||
deploy: amd64/wireguard.exe | |||
-ssh $(DEPLOYMENT_HOST) -- 'taskkill /im wireguard.exe /f' | |||
scp $< $(DEPLOYMENT_HOST):$(DEPLOYMENT_PATH) | |||
@@ -24,6 +24,28 @@ C:\Projects\wireguard-windows> amd64\wireguard.exe | |||
Since WireGuard requires the Wintun driver to be installed, and this generally requires a valid Microsoft signature, you may benefit from first installing a release of WireGuard for Windows from the official [wireguard.com](https://www.wireguard.com/install/) builds, which bundles a Microsoft-signed Wintun, and then subsequently run your own wireguard.exe. Alternatively, you can craft your own installer using the `quickinstall.bat` script. | |||
### Optional: Localizing | |||
To translate WireGuard UI to your language: | |||
1. Upgrade `resources.rc` accordingly. Follow the pattern. | |||
2. Add your language ID to the `//go:generate go run golang.org/x/text/cmd/gotext ... -lang=en,<langID>...` line in `main.go`. | |||
3. Configure and run `build` to prepare initial `locales\<langID>\messages.gotext.json` file: | |||
``` | |||
C:\Projects\wireguard-windows> set GenerateLocalizations=yes | |||
C:\Projects\wireguard-windows> build | |||
C:\Projects\wireguard-windows> copy locales\<langID>\out.gotext.json locales\<langID>\messages.gotext.json | |||
``` | |||
4. Translate `locales\<langID>\messages.gotext.json`. See other language message files how to translate messages and how to tackle plural. | |||
5. Run `build` from the step 3 again, and test. | |||
6. Repeat from step 4. | |||
### Optional: Creating the Installer | |||
The installer build script will take care of downloading, verifying, and extracting the right versions of the various dependencies: | |||
@@ -70,6 +70,10 @@ if exist .deps\prepared goto :render | |||
mkdir %1 >NUL 2>&1 | |||
echo [+] Assembling resources %1 | |||
windres -i resources.rc -o resources.syso -O coff || exit /b %errorlevel% | |||
if "%GenerateLocalizations%|%1"=="yes|x86" ( | |||
echo [+] Generating localizations %1 | |||
go generate || exit /b 1 | |||
) | |||
echo [+] Building program %1 | |||
go build -ldflags="-H windowsgui -s -w" -tags walk_use_cgo -trimpath -v -o "%~1\wireguard.exe" || exit /b 1 | |||
if not exist "%~1\wg.exe" ( | |||
@@ -16,6 +16,8 @@ import ( | |||
"time" | |||
"golang.org/x/crypto/curve25519" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
) | |||
const KeyLength = 32 | |||
@@ -131,18 +133,6 @@ func NewPrivateKeyFromString(b64 string) (*Key, error) { | |||
return parseKeyBase64(b64) | |||
} | |||
func formatInterval(i int64, n string, l int) string { | |||
r := "" | |||
if l > 0 { | |||
r += ", " | |||
} | |||
r += fmt.Sprintf("%d %s", i, n) | |||
if i != 1 { | |||
r += "s" | |||
} | |||
return r | |||
} | |||
func (t HandshakeTime) IsEmpty() bool { | |||
return t == HandshakeTime(0) | |||
} | |||
@@ -151,9 +141,9 @@ func (t HandshakeTime) String() string { | |||
u := time.Unix(0, 0).Add(time.Duration(t)).Unix() | |||
n := time.Now().Unix() | |||
if u == n { | |||
return "Now" | |||
return l18n.Sprintf("Now") | |||
} else if u > n { | |||
return "System clock wound backward!" | |||
return l18n.Sprintf("System clock wound backward!") | |||
} | |||
left := n - u | |||
years := left / (365 * 24 * 60 * 60) | |||
@@ -164,37 +154,37 @@ func (t HandshakeTime) String() string { | |||
left = left % (60 * 60) | |||
minutes := left / 60 | |||
seconds := left % 60 | |||
s := "" | |||
s := make([]string, 0, 5) | |||
if years > 0 { | |||
s += formatInterval(years, "year", len(s)) | |||
s = append(s, l18n.Sprintf("%d year(s)", years)) | |||
} | |||
if days > 0 { | |||
s += formatInterval(days, "day", len(s)) | |||
s = append(s, l18n.Sprintf("%d day(s)", days)) | |||
} | |||
if hours > 0 { | |||
s += formatInterval(hours, "hour", len(s)) | |||
s = append(s, l18n.Sprintf("%d hour(s)", hours)) | |||
} | |||
if minutes > 0 { | |||
s += formatInterval(minutes, "minute", len(s)) | |||
s = append(s, l18n.Sprintf("%d minute(s)", minutes)) | |||
} | |||
if seconds > 0 { | |||
s += formatInterval(seconds, "second", len(s)) | |||
s = append(s, l18n.Sprintf("%d second(s)", seconds)) | |||
} | |||
s += " ago" | |||
return s | |||
timestamp := strings.Join(s, l18n.EnumerationSeparator()) | |||
return l18n.Sprintf("%s ago", timestamp) | |||
} | |||
func (b Bytes) String() string { | |||
if b < 1024 { | |||
return fmt.Sprintf("%d B", b) | |||
return l18n.Sprintf("%d\u00a0B", b) | |||
} else if b < 1024*1024 { | |||
return fmt.Sprintf("%.2f KiB", float64(b)/1024) | |||
return l18n.Sprintf("%.2f\u00a0KiB", float64(b)/1024) | |||
} else if b < 1024*1024*1024 { | |||
return fmt.Sprintf("%.2f MiB", float64(b)/(1024*1024)) | |||
return l18n.Sprintf("%.2f\u00a0MiB", float64(b)/(1024*1024)) | |||
} else if b < 1024*1024*1024*1024 { | |||
return fmt.Sprintf("%.2f GiB", float64(b)/(1024*1024*1024)) | |||
return l18n.Sprintf("%.2f\u00a0GiB", float64(b)/(1024*1024*1024)) | |||
} | |||
return fmt.Sprintf("%.2f TiB", float64(b)/(1024*1024*1024)/1024) | |||
return l18n.Sprintf("%.2f\u00a0TiB", float64(b)/(1024*1024*1024)/1024) | |||
} | |||
func (conf *Config) DeduplicateNetworkEntries() { | |||
@@ -8,13 +8,14 @@ package conf | |||
import ( | |||
"encoding/base64" | |||
"encoding/hex" | |||
"fmt" | |||
"net" | |||
"strconv" | |||
"strings" | |||
"time" | |||
"golang.org/x/text/encoding/unicode" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
) | |||
type ParseError struct { | |||
@@ -23,7 +24,7 @@ type ParseError struct { | |||
} | |||
func (e *ParseError) Error() string { | |||
return fmt.Sprintf("%s: %q", e.why, e.offender) | |||
return l18n.Sprintf("%s: %q", e.why, e.offender) | |||
} | |||
func parseIPCidr(s string) (ipcidr *IPCidr, err error) { | |||
@@ -37,7 +38,7 @@ func parseIPCidr(s string) (ipcidr *IPCidr, err error) { | |||
addrStr, cidrStr = s[:i], s[i+1:] | |||
} | |||
err = &ParseError{"Invalid IP address", s} | |||
err = &ParseError{l18n.Sprintf("Invalid IP address"), s} | |||
addr := net.ParseIP(addrStr) | |||
if addr == nil { | |||
return | |||
@@ -47,7 +48,7 @@ func parseIPCidr(s string) (ipcidr *IPCidr, err error) { | |||
addr = maybeV4 | |||
} | |||
if len(cidrStr) > 0 { | |||
err = &ParseError{"Invalid network prefix length", s} | |||
err = &ParseError{l18n.Sprintf("Invalid network prefix length"), s} | |||
cidr, err = strconv.Atoi(cidrStr) | |||
if err != nil || cidr < 0 || cidr > 128 { | |||
return | |||
@@ -68,11 +69,11 @@ func parseIPCidr(s string) (ipcidr *IPCidr, err error) { | |||
func parseEndpoint(s string) (*Endpoint, error) { | |||
i := strings.LastIndexByte(s, ':') | |||
if i < 0 { | |||
return nil, &ParseError{"Missing port from endpoint", s} | |||
return nil, &ParseError{l18n.Sprintf("Missing port from endpoint"), s} | |||
} | |||
host, portStr := s[:i], s[i+1:] | |||
if len(host) < 1 { | |||
return nil, &ParseError{"Invalid endpoint host", host} | |||
return nil, &ParseError{l18n.Sprintf("Invalid endpoint host"), host} | |||
} | |||
port, err := parsePort(portStr) | |||
if err != nil { | |||
@@ -80,7 +81,7 @@ func parseEndpoint(s string) (*Endpoint, error) { | |||
} | |||
hostColon := strings.IndexByte(host, ':') | |||
if host[0] == '[' || host[len(host)-1] == ']' || hostColon > 0 { | |||
err := &ParseError{"Brackets must contain an IPv6 address", host} | |||
err := &ParseError{l18n.Sprintf("Brackets must contain an IPv6 address"), host} | |||
if len(host) > 3 && host[0] == '[' && host[len(host)-1] == ']' && hostColon > 0 { | |||
end := len(host) - 1 | |||
if i := strings.LastIndexByte(host, '%'); i > 1 { | |||
@@ -104,7 +105,7 @@ func parseMTU(s string) (uint16, error) { | |||
return 0, err | |||
} | |||
if m < 576 || m > 65535 { | |||
return 0, &ParseError{"Invalid MTU", s} | |||
return 0, &ParseError{l18n.Sprintf("Invalid MTU"), s} | |||
} | |||
return uint16(m), nil | |||
} | |||
@@ -115,7 +116,7 @@ func parsePort(s string) (uint16, error) { | |||
return 0, err | |||
} | |||
if m < 0 || m > 65535 { | |||
return 0, &ParseError{"Invalid port", s} | |||
return 0, &ParseError{l18n.Sprintf("Invalid port"), s} | |||
} | |||
return uint16(m), nil | |||
} | |||
@@ -129,7 +130,7 @@ func parsePersistentKeepalive(s string) (uint16, error) { | |||
return 0, err | |||
} | |||
if m < 0 || m > 65535 { | |||
return 0, &ParseError{"Invalid persistent keepalive", s} | |||
return 0, &ParseError{l18n.Sprintf("Invalid persistent keepalive"), s} | |||
} | |||
return uint16(m), nil | |||
} | |||
@@ -137,10 +138,10 @@ func parsePersistentKeepalive(s string) (uint16, error) { | |||
func parseKeyBase64(s string) (*Key, error) { | |||
k, err := base64.StdEncoding.DecodeString(s) | |||
if err != nil { | |||
return nil, &ParseError{"Invalid key: " + err.Error(), s} | |||
return nil, &ParseError{l18n.Sprintf("Invalid key: %v", err), s} | |||
} | |||
if len(k) != KeyLength { | |||
return nil, &ParseError{"Keys must decode to exactly 32 bytes", s} | |||
return nil, &ParseError{l18n.Sprintf("Keys must decode to exactly 32 bytes"), s} | |||
} | |||
var key Key | |||
copy(key[:], k) | |||
@@ -150,10 +151,10 @@ func parseKeyBase64(s string) (*Key, error) { | |||
func parseKeyHex(s string) (*Key, error) { | |||
k, err := hex.DecodeString(s) | |||
if err != nil { | |||
return nil, &ParseError{"Invalid key: " + err.Error(), s} | |||
return nil, &ParseError{l18n.Sprintf("Invalid key: %v", err), s} | |||
} | |||
if len(k) != KeyLength { | |||
return nil, &ParseError{"Keys must decode to exactly 32 bytes", s} | |||
return nil, &ParseError{l18n.Sprintf("Keys must decode to exactly 32 bytes"), s} | |||
} | |||
var key Key | |||
copy(key[:], k) | |||
@@ -163,7 +164,7 @@ func parseKeyHex(s string) (*Key, error) { | |||
func parseBytesOrStamp(s string) (uint64, error) { | |||
b, err := strconv.ParseUint(s, 10, 64) | |||
if err != nil { | |||
return 0, &ParseError{"Number must be a number between 0 and 2^64-1: " + err.Error(), s} | |||
return 0, &ParseError{l18n.Sprintf("Number must be a number between 0 and 2^64-1: %v", err), s} | |||
} | |||
return b, nil | |||
} | |||
@@ -173,7 +174,7 @@ func splitList(s string) ([]string, error) { | |||
for _, split := range strings.Split(s, ",") { | |||
trim := strings.TrimSpace(split) | |||
if len(trim) == 0 { | |||
return nil, &ParseError{"Two commas in a row", s} | |||
return nil, &ParseError{l18n.Sprintf("Two commas in a row"), s} | |||
} | |||
out = append(out, trim) | |||
} | |||
@@ -196,7 +197,7 @@ func (c *Config) maybeAddPeer(p *Peer) { | |||
func FromWgQuick(s string, name string) (*Config, error) { | |||
if !TunnelNameIsValid(name) { | |||
return nil, &ParseError{"Tunnel name is not valid", name} | |||
return nil, &ParseError{l18n.Sprintf("Tunnel name is not valid"), name} | |||
} | |||
lines := strings.Split(s, "\n") | |||
parserState := notInASection | |||
@@ -225,15 +226,15 @@ func FromWgQuick(s string, name string) (*Config, error) { | |||
continue | |||
} | |||
if parserState == notInASection { | |||
return nil, &ParseError{"Line must occur in a section", line} | |||
return nil, &ParseError{l18n.Sprintf("Line must occur in a section"), line} | |||
} | |||
equals := strings.IndexByte(line, '=') | |||
if equals < 0 { | |||
return nil, &ParseError{"Invalid config key is missing an equals separator", line} | |||
return nil, &ParseError{l18n.Sprintf("Invalid config key is missing an equals separator"), line} | |||
} | |||
key, val := strings.TrimSpace(lineLower[:equals]), strings.TrimSpace(line[equals+1:]) | |||
if len(val) == 0 { | |||
return nil, &ParseError{"Key must have a value", line} | |||
return nil, &ParseError{l18n.Sprintf("Key must have a value"), line} | |||
} | |||
if parserState == inInterfaceSection { | |||
switch key { | |||
@@ -276,12 +277,12 @@ func FromWgQuick(s string, name string) (*Config, error) { | |||
for _, address := range addresses { | |||
a := net.ParseIP(address) | |||
if a == nil { | |||
return nil, &ParseError{"Invalid IP address", address} | |||
return nil, &ParseError{l18n.Sprintf("Invalid IP address"), address} | |||
} | |||
conf.Interface.DNS = append(conf.Interface.DNS, a) | |||
} | |||
default: | |||
return nil, &ParseError{"Invalid key for [Interface] section", key} | |||
return nil, &ParseError{l18n.Sprintf("Invalid key for [Interface] section"), key} | |||
} | |||
} else if parserState == inPeerSection { | |||
switch key { | |||
@@ -322,18 +323,18 @@ func FromWgQuick(s string, name string) (*Config, error) { | |||
} | |||
peer.Endpoint = *e | |||
default: | |||
return nil, &ParseError{"Invalid key for [Peer] section", key} | |||
return nil, &ParseError{l18n.Sprintf("Invalid key for [Peer] section"), key} | |||
} | |||
} | |||
} | |||
conf.maybeAddPeer(peer) | |||
if !sawPrivateKey { | |||
return nil, &ParseError{"An interface must have a private key", "[none specified]"} | |||
return nil, &ParseError{l18n.Sprintf("An interface must have a private key"), l18n.Sprintf("[none specified]")} | |||
} | |||
for _, p := range conf.Peers { | |||
if p.PublicKey.IsZero() { | |||
return nil, &ParseError{"All peers must have public keys", "[none specified]"} | |||
return nil, &ParseError{l18n.Sprintf("All peers must have public keys"), l18n.Sprintf("[none specified]")} | |||
} | |||
} | |||
@@ -375,11 +376,11 @@ func FromUAPI(s string, existingConfig *Config) (*Config, error) { | |||
} | |||
equals := strings.IndexByte(line, '=') | |||
if equals < 0 { | |||
return nil, &ParseError{"Invalid config key is missing an equals separator", line} | |||
return nil, &ParseError{l18n.Sprintf("Invalid config key is missing an equals separator"), line} | |||
} | |||
key, val := line[:equals], line[equals+1:] | |||
if len(val) == 0 { | |||
return nil, &ParseError{"Key must have a value", line} | |||
return nil, &ParseError{l18n.Sprintf("Key must have a value"), line} | |||
} | |||
switch key { | |||
case "public_key": | |||
@@ -390,7 +391,7 @@ func FromUAPI(s string, existingConfig *Config) (*Config, error) { | |||
if val == "0" { | |||
continue | |||
} else { | |||
return nil, &ParseError{"Error in getting configuration", val} | |||
return nil, &ParseError{l18n.Sprintf("Error in getting configuration"), val} | |||
} | |||
} | |||
if parserState == inInterfaceSection { | |||
@@ -411,7 +412,7 @@ func FromUAPI(s string, existingConfig *Config) (*Config, error) { | |||
// Ignored for now. | |||
default: | |||
return nil, &ParseError{"Invalid key for interface section", key} | |||
return nil, &ParseError{l18n.Sprintf("Invalid key for interface section"), key} | |||
} | |||
} else if parserState == inPeerSection { | |||
switch key { | |||
@@ -429,7 +430,7 @@ func FromUAPI(s string, existingConfig *Config) (*Config, error) { | |||
peer.PresharedKey = *k | |||
case "protocol_version": | |||
if val != "1" { | |||
return nil, &ParseError{"Protocol version must be 1", val} | |||
return nil, &ParseError{l18n.Sprintf("Protocol version must be 1"), val} | |||
} | |||
case "allowed_ip": | |||
a, err := parseIPCidr(val) | |||
@@ -474,7 +475,7 @@ func FromUAPI(s string, existingConfig *Config) (*Config, error) { | |||
} | |||
peer.LastHandshakeTime += HandshakeTime(time.Duration(t) * time.Nanosecond) | |||
default: | |||
return nil, &ParseError{"Invalid key for peer section", key} | |||
return nil, &ParseError{l18n.Sprintf("Invalid key for peer section"), key} | |||
} | |||
} | |||
} | |||
@@ -16,6 +16,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | |||
golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de h1:aYKJLPSrddB2N7/6OKyFqJ337SXpo61bBuvO5p1+7iY= | |||
golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= | |||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
golang.zx2c4.com/wireguard v0.0.20200122-0.20200214175355-9cbcff10dd3e h1:cEeaW64u6+FcYcO4/M+rpqJhb9pFl/9CGs5oqlN6ps8= | |||
golang.zx2c4.com/wireguard v0.0.20200122-0.20200214175355-9cbcff10dd3e/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= | |||
@@ -0,0 +1,65 @@ | |||
/* SPDX-License-Identifier: MIT | |||
* | |||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved. | |||
*/ | |||
package l18n | |||
import ( | |||
"sync" | |||
"golang.org/x/sys/windows" | |||
"golang.org/x/text/language" | |||
"golang.org/x/text/message" | |||
) | |||
var printer *message.Printer | |||
var printerLock sync.Mutex | |||
// prn returns the printer for user preferred UI language. | |||
func prn() *message.Printer { | |||
if printer != nil { | |||
return printer | |||
} | |||
printerLock.Lock() | |||
if printer != nil { | |||
printerLock.Unlock() | |||
return printer | |||
} | |||
printer = message.NewPrinter(lang()) | |||
printerLock.Unlock() | |||
return printer | |||
} | |||
// lang returns the user preferred UI language we have most confident translation in the default catalog available. | |||
func lang() (tag language.Tag) { | |||
tag = language.English | |||
confidence := language.No | |||
languages, err := windows.GetUserPreferredUILanguages(windows.MUI_LANGUAGE_NAME) | |||
if err != nil { | |||
return | |||
} | |||
for i := range languages { | |||
t, _, c := message.DefaultCatalog.Matcher().Match(message.MatchLanguage(languages[i])) | |||
if c > confidence { | |||
tag = t | |||
confidence = c | |||
} | |||
} | |||
return | |||
} | |||
// Sprintf is like fmt.Sprintf, but using language-specific formatting. | |||
func Sprintf(key message.Reference, a ...interface{}) string { | |||
return prn().Sprintf(key, a...) | |||
} | |||
// EnumerationSeparator returns enumeration separator. For English and western languages, | |||
// enumeration separator is a comma followed by a space (i.e. ", "). For Chinese, it returns | |||
// "\u3001". | |||
func EnumerationSeparator() string { | |||
// BUG: We could just use `Sprintf(", " /* ...translator instructions... */)` and let the | |||
// individual locale catalog handle its translation. Unfortunately, the gotext utility tries to | |||
// be nice to translators and skips all strings without letters when updating catalogs. | |||
return Sprintf("[EnumerationSeparator]" /* Text to insert between items when listing - most western languages will translate ‘[EnumerationSeparator]’ into ‘, ’ to produce lists like ‘apple, orange, strawberry’. Eastern languages might translate into ‘、’ to produce lists like ‘リンゴ、オレンジ、イチゴ’. */) | |||
} |
@@ -5,6 +5,8 @@ | |||
package main | |||
//go:generate go run golang.org/x/text/cmd/gotext -srclang=en update -out=zgotext.go -lang=en | |||
import ( | |||
"fmt" | |||
"os" | |||
@@ -15,43 +17,43 @@ import ( | |||
"golang.org/x/sys/windows" | |||
"golang.zx2c4.com/wireguard/windows/elevate" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
"golang.zx2c4.com/wireguard/windows/ringlogger" | |||
"golang.zx2c4.com/wireguard/windows/tunnel" | |||
"golang.zx2c4.com/wireguard/windows/ui" | |||
) | |||
var flags = [...]string{ | |||
"(no argument): elevate and install manager service for current user", | |||
"/installmanagerservice", | |||
"/installtunnelservice CONFIG_PATH", | |||
"/uninstallmanagerservice", | |||
"/uninstalltunnelservice TUNNEL_NAME", | |||
"/managerservice", | |||
"/tunnelservice CONFIG_PATH", | |||
"/ui CMD_READ_HANDLE CMD_WRITE_HANDLE CMD_EVENT_HANDLE LOG_MAPPING_HANDLE", | |||
"/dumplog OUTPUT_PATH", | |||
} | |||
func fatal(v ...interface{}) { | |||
windows.MessageBox(0, windows.StringToUTF16Ptr(fmt.Sprint(v...)), windows.StringToUTF16Ptr("Error"), windows.MB_ICONERROR) | |||
windows.MessageBox(0, windows.StringToUTF16Ptr(fmt.Sprint(v...)), windows.StringToUTF16Ptr(l18n.Sprintf("Error")), windows.MB_ICONERROR) | |||
os.Exit(1) | |||
} | |||
func fatalf(format string, v ...interface{}) { | |||
fatal(fmt.Sprintf(format, v...)) | |||
fatal(l18n.Sprintf(format, v...)) | |||
} | |||
func info(title string, format string, v ...interface{}) { | |||
windows.MessageBox(0, windows.StringToUTF16Ptr(fmt.Sprintf(format, v...)), windows.StringToUTF16Ptr(title), windows.MB_ICONINFORMATION) | |||
windows.MessageBox(0, windows.StringToUTF16Ptr(l18n.Sprintf(format, v...)), windows.StringToUTF16Ptr(title), windows.MB_ICONINFORMATION) | |||
} | |||
func usage() { | |||
var flags = [...]string{ | |||
l18n.Sprintf("(no argument): elevate and install manager service"), | |||
"/installmanagerservice", | |||
"/installtunnelservice CONFIG_PATH", | |||
"/uninstallmanagerservice", | |||
"/uninstalltunnelservice TUNNEL_NAME", | |||
"/managerservice", | |||
"/tunnelservice CONFIG_PATH", | |||
"/ui CMD_READ_HANDLE CMD_WRITE_HANDLE CMD_EVENT_HANDLE LOG_MAPPING_HANDLE", | |||
"/dumplog OUTPUT_PATH", | |||
} | |||
builder := strings.Builder{} | |||
for _, flag := range flags { | |||
builder.WriteString(fmt.Sprintf(" %s\n", flag)) | |||
} | |||
info("Command Line Options", "Usage: %s [\n%s]", os.Args[0], builder.String()) | |||
info(l18n.Sprintf("Command Line Options"), "Usage: %s [\n%s]", os.Args[0], builder.String()) | |||
os.Exit(1) | |||
} | |||
@@ -62,7 +64,7 @@ func checkForWow64() { | |||
fatalf("Unable to determine whether the process is running under WOW64: %v", err) | |||
} | |||
if b { | |||
fatal("You must use the 64-bit version of WireGuard on this computer.") | |||
fatalf("You must use the 64-bit version of WireGuard on this computer.") | |||
} | |||
} | |||
@@ -136,7 +138,7 @@ func main() { | |||
} | |||
checkForAdminDesktop() | |||
time.Sleep(30 * time.Second) | |||
fatal("WireGuard system tray icon did not appear after 30 seconds.") | |||
fatalf("WireGuard system tray icon did not appear after 30 seconds.") | |||
return | |||
case "/uninstallmanagerservice": | |||
if len(os.Args) != 2 { | |||
@@ -6,35 +6,45 @@ | |||
#include <windows.h> | |||
#include "version/version.h" | |||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml | |||
#pragma code_page(65001) // UTF-8 | |||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL | |||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml | |||
$wireguard.ico ICON ui/icon/wireguard.ico | |||
dot-gray.ico ICON ui/icon/dot-gray.ico | |||
VS_VERSION_INFO VERSIONINFO | |||
FILEVERSION WIREGUARD_WINDOWS_VERSION_ARRAY | |||
PRODUCTVERSION WIREGUARD_WINDOWS_VERSION_ARRAY | |||
FILEOS VOS_NT_WINDOWS32 | |||
FILETYPE VFT_APP | |||
FILESUBTYPE VFT2_UNKNOWN | |||
BEGIN | |||
BLOCK "StringFileInfo" | |||
BEGIN | |||
BLOCK "040904b0" | |||
BEGIN | |||
VALUE "CompanyName", "WireGuard LLC" | |||
VALUE "FileDescription", "WireGuard: Fast, Modern, Secure VPN Tunnel" | |||
VALUE "FileVersion", WIREGUARD_WINDOWS_VERSION_STRING | |||
VALUE "InternalName", "wireguard" | |||
VALUE "LegalCopyright", "Copyright \xa9 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved." | |||
VALUE "OriginalFilename", "wireguard.exe" | |||
VALUE "ProductName", "WireGuard" | |||
VALUE "ProductVersion", WIREGUARD_WINDOWS_VERSION_STRING | |||
VALUE "Comments", "https://www.wireguard.com/" | |||
END | |||
END | |||
BLOCK "VarFileInfo" | |||
BEGIN | |||
VALUE "Translation", 0x409, 1200 | |||
END | |||
#define VERSIONINFO_TEMPLATE(block_id, lang_id, codepage_id, file_desc, comments) \ | |||
VS_VERSION_INFO VERSIONINFO \ | |||
FILEVERSION WIREGUARD_WINDOWS_VERSION_ARRAY \ | |||
PRODUCTVERSION WIREGUARD_WINDOWS_VERSION_ARRAY \ | |||
FILEOS VOS_NT_WINDOWS32 \ | |||
FILETYPE VFT_APP \ | |||
FILESUBTYPE VFT2_UNKNOWN \ | |||
BEGIN \ | |||
BLOCK "StringFileInfo" \ | |||
BEGIN \ | |||
BLOCK block_id \ | |||
BEGIN \ | |||
VALUE "CompanyName", "WireGuard LLC" \ | |||
VALUE "FileDescription", file_desc \ | |||
VALUE "FileVersion", WIREGUARD_WINDOWS_VERSION_STRING \ | |||
VALUE "InternalName", "wireguard-windows" \ | |||
VALUE "LegalCopyright", "Copyright © 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved." \ | |||
VALUE "OriginalFilename", "wireguard.exe" \ | |||
VALUE "ProductName", "WireGuard" \ | |||
VALUE "ProductVersion", WIREGUARD_WINDOWS_VERSION_STRING \ | |||
VALUE "Comments", comments \ | |||
END \ | |||
END \ | |||
BLOCK "VarFileInfo" \ | |||
BEGIN \ | |||
VALUE "Translation", lang_id, codepage_id \ | |||
END \ | |||
END | |||
LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT | |||
VERSIONINFO_TEMPLATE( | |||
"040904b0", 0x409, 0x4b0, | |||
"WireGuard: Fast, Modern, Secure VPN Tunnel", | |||
"https://www.wireguard.com/" | |||
) |
@@ -6,7 +6,6 @@ | |||
package ui | |||
import ( | |||
"fmt" | |||
"runtime" | |||
"strings" | |||
@@ -14,6 +13,7 @@ import ( | |||
"github.com/lxn/win" | |||
"golang.org/x/sys/windows" | |||
"golang.zx2c4.com/wireguard/device" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/version" | |||
) | |||
@@ -47,7 +47,7 @@ func runAboutDialog(owner walk.Form) error { | |||
showingAboutDialog = nil | |||
}() | |||
disposables.Add(showingAboutDialog) | |||
showingAboutDialog.SetTitle("About WireGuard") | |||
showingAboutDialog.SetTitle(l18n.Sprintf("About WireGuard")) | |||
showingAboutDialog.SetLayout(vbl) | |||
if icon, err := loadLogoIcon(32); err == nil { | |||
showingAboutDialog.SetIcon(icon) | |||
@@ -79,7 +79,7 @@ func runAboutDialog(owner walk.Form) error { | |||
if logo, err := loadLogoIcon(128); err == nil { | |||
iv.SetImage(logo) | |||
} | |||
iv.Accessibility().SetName("WireGuard logo image") | |||
iv.Accessibility().SetName(l18n.Sprintf("WireGuard logo image")) | |||
wgLbl, err := walk.NewTextLabel(showingAboutDialog) | |||
if err != nil { | |||
@@ -95,7 +95,7 @@ func runAboutDialog(owner walk.Form) error { | |||
return err | |||
} | |||
detailsLbl.SetTextAlignment(walk.AlignHCenterVNear) | |||
detailsLbl.SetText(fmt.Sprintf("App version: %s\nGo backend version: %s\nGo version: %s\nOperating system: %s\nArchitecture: %s", version.Number, device.WireGuardGoVersion, strings.TrimPrefix(runtime.Version(), "go"), version.OsName(), runtime.GOARCH)) | |||
detailsLbl.SetText(l18n.Sprintf("App version: %s\nGo backend version: %s\nGo version: %s\nOperating system: %s\nArchitecture: %s", version.Number, device.WireGuardGoVersion, strings.TrimPrefix(runtime.Version(), "go"), version.OsName(), runtime.GOARCH)) | |||
copyrightLbl, err := walk.NewTextLabel(showingAboutDialog) | |||
if err != nil { | |||
@@ -119,14 +119,14 @@ func runAboutDialog(owner walk.Form) error { | |||
return err | |||
} | |||
closePB.SetAlignment(walk.AlignHCenterVNear) | |||
closePB.SetText("Close") | |||
closePB.SetText(l18n.Sprintf("Close")) | |||
closePB.Clicked().Attach(showingAboutDialog.Accept) | |||
donatePB, err := walk.NewPushButton(buttonCP) | |||
if err != nil { | |||
return err | |||
} | |||
donatePB.SetAlignment(walk.AlignHCenterVNear) | |||
donatePB.SetText("♥ &Donate!") | |||
donatePB.SetText(l18n.Sprintf("♥ &Donate!")) | |||
donatePB.Clicked().Attach(func() { | |||
if easterEggIndex == -1 { | |||
easterEggIndex = 0 | |||
@@ -6,7 +6,6 @@ | |||
package ui | |||
import ( | |||
"fmt" | |||
"strconv" | |||
"strings" | |||
"time" | |||
@@ -15,6 +14,7 @@ import ( | |||
"github.com/lxn/win" | |||
"golang.zx2c4.com/wireguard/windows/conf" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
) | |||
@@ -108,7 +108,7 @@ func newLabelStatusLine(parent walk.Container) (*labelStatusLine, error) { | |||
return nil, err | |||
} | |||
disposables.Add(lsl.label) | |||
lsl.label.SetText("Status:") | |||
lsl.label.SetText(l18n.Sprintf("Status:")) | |||
lsl.label.SetTextAlignment(walk.AlignHFarVNear) | |||
if lsl.statusComposite, err = walk.NewComposite(parent); err != nil { | |||
@@ -180,7 +180,7 @@ func newLabelTextLine(fieldName string, parent walk.Container) (*labelTextLine, | |||
return nil, err | |||
} | |||
disposables.Add(lt.label) | |||
lt.label.SetText(fieldName + ":") | |||
lt.label.SetText(fieldName) | |||
lt.label.SetTextAlignment(walk.AlignHFarVNear) | |||
lt.label.SetVisible(false) | |||
@@ -216,9 +216,9 @@ func (tal *toggleActiveLine) update(state manager.TunnelState) { | |||
switch state { | |||
case manager.TunnelStarted: | |||
text = "&Deactivate" | |||
text = l18n.Sprintf("&Deactivate") | |||
case manager.TunnelStopped: | |||
text = "&Activate" | |||
text = l18n.Sprintf("&Activate") | |||
case manager.TunnelStarting, manager.TunnelStopping: | |||
text = textForState(state, true) | |||
default: | |||
@@ -300,11 +300,11 @@ func newInterfaceView(parent walk.Container) (*interfaceView, error) { | |||
disposables.Add(iv.status) | |||
items := []labelTextLineItem{ | |||
{"Public key", &iv.publicKey}, | |||
{"Listen port", &iv.listenPort}, | |||
{"MTU", &iv.mtu}, | |||
{"Addresses", &iv.addresses}, | |||
{"DNS servers", &iv.dns}, | |||
{l18n.Sprintf("Public key:"), &iv.publicKey}, | |||
{l18n.Sprintf("Listen port:"), &iv.listenPort}, | |||
{l18n.Sprintf("MTU:"), &iv.mtu}, | |||
{l18n.Sprintf("Addresses:"), &iv.addresses}, | |||
{l18n.Sprintf("DNS servers:"), &iv.dns}, | |||
} | |||
if iv.lines, err = createLabelTextLines(items, parent, &disposables); err != nil { | |||
return nil, err | |||
@@ -328,13 +328,13 @@ func newPeerView(parent walk.Container) (*peerView, error) { | |||
pv := new(peerView) | |||
items := []labelTextLineItem{ | |||
{"Public key", &pv.publicKey}, | |||
{"Preshared key", &pv.presharedKey}, | |||
{"Allowed IPs", &pv.allowedIPs}, | |||
{"Endpoint", &pv.endpoint}, | |||
{"Persistent keepalive", &pv.persistentKeepalive}, | |||
{"Latest handshake", &pv.latestHandshake}, | |||
{"Transfer", &pv.transfer}, | |||
{l18n.Sprintf("Public key:"), &pv.publicKey}, | |||
{l18n.Sprintf("Preshared key:"), &pv.presharedKey}, | |||
{l18n.Sprintf("Allowed IPs:"), &pv.allowedIPs}, | |||
{l18n.Sprintf("Endpoint:"), &pv.endpoint}, | |||
{l18n.Sprintf("Persistent keepalive:"), &pv.persistentKeepalive}, | |||
{l18n.Sprintf("Latest handshake:"), &pv.latestHandshake}, | |||
{l18n.Sprintf("Transfer:"), &pv.transfer}, | |||
} | |||
var err error | |||
if pv.lines, err = createLabelTextLines(items, parent, nil); err != nil { | |||
@@ -383,7 +383,7 @@ func (iv *interfaceView) apply(c *conf.Interface) { | |||
for i, address := range c.Addresses { | |||
addrStrings[i] = address.String() | |||
} | |||
iv.addresses.show(strings.Join(addrStrings[:], ", ")) | |||
iv.addresses.show(strings.Join(addrStrings[:], l18n.EnumerationSeparator())) | |||
} else { | |||
iv.addresses.hide() | |||
} | |||
@@ -393,7 +393,7 @@ func (iv *interfaceView) apply(c *conf.Interface) { | |||
for i, address := range c.DNS { | |||
addrStrings[i] = address.String() | |||
} | |||
iv.dns.show(strings.Join(addrStrings[:], ", ")) | |||
iv.dns.show(strings.Join(addrStrings[:], l18n.EnumerationSeparator())) | |||
} else { | |||
iv.dns.hide() | |||
} | |||
@@ -407,7 +407,7 @@ func (pv *peerView) apply(c *conf.Peer) { | |||
pv.publicKey.show(c.PublicKey.String()) | |||
if !c.PresharedKey.IsZero() { | |||
pv.presharedKey.show("enabled") | |||
pv.presharedKey.show(l18n.Sprintf("enabled")) | |||
} else { | |||
pv.presharedKey.hide() | |||
} | |||
@@ -417,7 +417,7 @@ func (pv *peerView) apply(c *conf.Peer) { | |||
for i, address := range c.AllowedIPs { | |||
addrStrings[i] = address.String() | |||
} | |||
pv.allowedIPs.show(strings.Join(addrStrings[:], ", ")) | |||
pv.allowedIPs.show(strings.Join(addrStrings[:], l18n.EnumerationSeparator())) | |||
} else { | |||
pv.allowedIPs.hide() | |||
} | |||
@@ -441,7 +441,7 @@ func (pv *peerView) apply(c *conf.Peer) { | |||
} | |||
if c.RxBytes > 0 || c.TxBytes > 0 { | |||
pv.transfer.show(fmt.Sprintf("%s received, %s sent", c.RxBytes.String(), c.TxBytes.String())) | |||
pv.transfer.show(l18n.Sprintf("%s received, %s sent", c.RxBytes.String(), c.TxBytes.String())) | |||
} else { | |||
pv.transfer.hide() | |||
} | |||
@@ -552,11 +552,11 @@ func (cv *ConfView) onToggleActiveClicked() { | |||
if err != nil { | |||
cv.Synchronize(func() { | |||
if oldState == manager.TunnelUnknown { | |||
showErrorCustom(cv.Form(), "Failed to determine tunnel state", err.Error()) | |||
showErrorCustom(cv.Form(), l18n.Sprintf("Failed to determine tunnel state"), err.Error()) | |||
} else if oldState == manager.TunnelStopped { | |||
showErrorCustom(cv.Form(), "Failed to activate tunnel", err.Error()) | |||
showErrorCustom(cv.Form(), l18n.Sprintf("Failed to activate tunnel"), err.Error()) | |||
} else if oldState == manager.TunnelStarted { | |||
showErrorCustom(cv.Form(), "Failed to deactivate tunnel", err.Error()) | |||
showErrorCustom(cv.Form(), l18n.Sprintf("Failed to deactivate tunnel"), err.Error()) | |||
} | |||
}) | |||
} | |||
@@ -612,7 +612,7 @@ func (cv *ConfView) setTunnel(tunnel *manager.Tunnel, config *conf.Config, state | |||
return | |||
} | |||
title := "Interface: " + config.Name | |||
title := l18n.Sprintf("Interface: %s", config.Name) | |||
if cv.name.Title() != title { | |||
cv.SetSuspended(true) | |||
defer cv.SetSuspended(false) | |||
@@ -656,7 +656,7 @@ func (cv *ConfView) setTunnel(tunnel *manager.Tunnel, config *conf.Config, state | |||
if err != nil { | |||
continue | |||
} | |||
group.SetTitle("Peer") | |||
group.SetTitle(l18n.Sprintf("Peer")) | |||
pv, err := newPeerView(group) | |||
if err != nil { | |||
group.Dispose() | |||
@@ -6,7 +6,6 @@ | |||
package ui | |||
import ( | |||
"fmt" | |||
"strings" | |||
"github.com/lxn/walk" | |||
@@ -14,6 +13,7 @@ import ( | |||
"golang.org/x/sys/windows" | |||
"golang.zx2c4.com/wireguard/windows/conf" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
"golang.zx2c4.com/wireguard/windows/ui/syntax" | |||
) | |||
@@ -52,9 +52,9 @@ func newEditDialog(owner walk.Form, tunnel *manager.Tunnel) (*EditDialog, error) | |||
var title string | |||
if tunnel == nil { | |||
title = "Create new tunnel" | |||
title = l18n.Sprintf("Create new tunnel") | |||
} else { | |||
title = "Edit tunnel" | |||
title = l18n.Sprintf("Edit tunnel") | |||
} | |||
if tunnel == nil { | |||
@@ -88,7 +88,7 @@ func newEditDialog(owner walk.Form, tunnel *manager.Tunnel) (*EditDialog, error) | |||
} | |||
layout.SetRange(nameLabel, walk.Rectangle{0, 0, 1, 1}) | |||
nameLabel.SetTextAlignment(walk.AlignHFarVCenter) | |||
nameLabel.SetText("&Name:") | |||
nameLabel.SetText(l18n.Sprintf("&Name:")) | |||
if dlg.nameEdit, err = walk.NewLineEdit(dlg); err != nil { | |||
return nil, err | |||
@@ -102,14 +102,14 @@ func newEditDialog(owner walk.Form, tunnel *manager.Tunnel) (*EditDialog, error) | |||
} | |||
layout.SetRange(pubkeyLabel, walk.Rectangle{0, 1, 1, 1}) | |||
pubkeyLabel.SetTextAlignment(walk.AlignHFarVCenter) | |||
pubkeyLabel.SetText("&Public key:") | |||
pubkeyLabel.SetText(l18n.Sprintf("&Public key:")) | |||
if dlg.pubkeyEdit, err = walk.NewLineEdit(dlg); err != nil { | |||
return nil, err | |||
} | |||
layout.SetRange(dlg.pubkeyEdit, walk.Rectangle{1, 1, 1, 1}) | |||
dlg.pubkeyEdit.SetReadOnly(true) | |||
dlg.pubkeyEdit.SetText("(unknown)") | |||
dlg.pubkeyEdit.SetText(l18n.Sprintf("(unknown)")) | |||
dlg.pubkeyEdit.Accessibility().SetRole(walk.AccRoleStatictext) | |||
if dlg.syntaxEdit, err = syntax.NewSyntaxEdit(dlg); err != nil { | |||
@@ -128,8 +128,8 @@ func newEditDialog(owner walk.Form, tunnel *manager.Tunnel) (*EditDialog, error) | |||
if dlg.blockUntunneledTrafficCB, err = walk.NewCheckBox(buttonsContainer); err != nil { | |||
return nil, err | |||
} | |||
dlg.blockUntunneledTrafficCB.SetText("&Block untunneled traffic (kill-switch)") | |||
dlg.blockUntunneledTrafficCB.SetToolTipText("When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.") | |||
dlg.blockUntunneledTrafficCB.SetText(l18n.Sprintf("&Block untunneled traffic (kill-switch)")) | |||
dlg.blockUntunneledTrafficCB.SetToolTipText(l18n.Sprintf("When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.")) | |||
dlg.blockUntunneledTrafficCB.SetVisible(false) | |||
dlg.blockUntunneledTrafficCB.CheckedChanged().Attach(dlg.onBlockUntunneledTrafficCBCheckedChanged) | |||
@@ -138,14 +138,14 @@ func newEditDialog(owner walk.Form, tunnel *manager.Tunnel) (*EditDialog, error) | |||
if dlg.saveButton, err = walk.NewPushButton(buttonsContainer); err != nil { | |||
return nil, err | |||
} | |||
dlg.saveButton.SetText("&Save") | |||
dlg.saveButton.SetText(l18n.Sprintf("&Save")) | |||
dlg.saveButton.Clicked().Attach(dlg.onSaveButtonClicked) | |||
cancelButton, err := walk.NewPushButton(buttonsContainer) | |||
if err != nil { | |||
return nil, err | |||
} | |||
cancelButton.SetText("Cancel") | |||
cancelButton.SetText(l18n.Sprintf("Cancel")) | |||
cancelButton.Clicked().Attach(dlg.Cancel) | |||
dlg.SetCancelButton(cancelButton) | |||
@@ -160,7 +160,7 @@ func newEditDialog(owner walk.Form, tunnel *manager.Tunnel) (*EditDialog, error) | |||
syntaxEditWnd := dlg.syntaxEdit.Handle() | |||
parentWnd := win.GetParent(syntaxEditWnd) | |||
labelWnd := win.CreateWindowEx(0, | |||
windows.StringToUTF16Ptr("STATIC"), windows.StringToUTF16Ptr("&Configuration:"), | |||
windows.StringToUTF16Ptr("STATIC"), windows.StringToUTF16Ptr(l18n.Sprintf("&Configuration:")), | |||
win.WS_CHILD|win.WS_GROUP|win.SS_LEFT, 0, 0, 0, 0, | |||
parentWnd, win.HMENU(^uintptr(0)), win.HINSTANCE(win.GetWindowLongPtr(parentWnd, win.GWLP_HINSTANCE)), nil) | |||
prevWnd := win.GetWindow(syntaxEditWnd, win.GW_HWNDPREV) | |||
@@ -301,18 +301,18 @@ func (dlg *EditDialog) onSyntaxEditPrivateKeyChanged(privateKey string) { | |||
if key != nil { | |||
dlg.pubkeyEdit.SetText(key.Public().String()) | |||
} else { | |||
dlg.pubkeyEdit.SetText("(unknown)") | |||
dlg.pubkeyEdit.SetText(l18n.Sprintf("(unknown)")) | |||
} | |||
} | |||
func (dlg *EditDialog) onSaveButtonClicked() { | |||
newName := dlg.nameEdit.Text() | |||
if newName == "" { | |||
showWarningCustom(dlg, "Invalid name", "A name is required.") | |||
showWarningCustom(dlg, l18n.Sprintf("Invalid name"), l18n.Sprintf("A name is required.")) | |||
return | |||
} | |||
if !conf.TunnelNameIsValid(newName) { | |||
showWarningCustom(dlg, "Invalid name", fmt.Sprintf("Tunnel name ‘%s’ is invalid.", newName)) | |||
showWarningCustom(dlg, l18n.Sprintf("Invalid name"), l18n.Sprintf("Tunnel name ‘%s’ is invalid.", newName)) | |||
return | |||
} | |||
newNameLower := strings.ToLower(newName) | |||
@@ -320,12 +320,12 @@ func (dlg *EditDialog) onSaveButtonClicked() { | |||
if newNameLower != strings.ToLower(dlg.config.Name) { | |||
existingTunnelList, err := manager.IPCClientTunnels() | |||
if err != nil { | |||
showWarningCustom(dlg, "Unable to list existing tunnels", err.Error()) | |||
showWarningCustom(dlg, l18n.Sprintf("Unable to list existing tunnels"), err.Error()) | |||
return | |||
} | |||
for _, tunnel := range existingTunnelList { | |||
if strings.ToLower(tunnel.Name) == newNameLower { | |||
showWarningCustom(dlg, "Tunnel already exists", fmt.Sprintf("Another tunnel already exists with the name ‘%s’.", newName)) | |||
showWarningCustom(dlg, l18n.Sprintf("Tunnel already exists"), l18n.Sprintf("Another tunnel already exists with the name ‘%s’.", newName)) | |||
return | |||
} | |||
} | |||
@@ -333,7 +333,7 @@ func (dlg *EditDialog) onSaveButtonClicked() { | |||
cfg, err := conf.FromWgQuick(dlg.syntaxEdit.Text(), newName) | |||
if err != nil { | |||
showErrorCustom(dlg, "Unable to create new configuration", err.Error()) | |||
showErrorCustom(dlg, l18n.Sprintf("Unable to create new configuration"), err.Error()) | |||
return | |||
} | |||
@@ -6,10 +6,11 @@ | |||
package ui | |||
import ( | |||
"fmt" | |||
"os" | |||
"github.com/lxn/walk" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
) | |||
func writeFileWithOverwriteHandling(owner walk.Form, filePath string, write func(file *os.File) error) bool { | |||
@@ -18,7 +19,7 @@ func writeFileWithOverwriteHandling(owner walk.Form, filePath string, write func | |||
return false | |||
} | |||
showErrorCustom(owner, "Writing file failed", err.Error()) | |||
showErrorCustom(owner, l18n.Sprintf("Writing file failed"), err.Error()) | |||
return true | |||
} | |||
@@ -26,7 +27,7 @@ func writeFileWithOverwriteHandling(owner walk.Form, filePath string, write func | |||
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600) | |||
if err != nil { | |||
if os.IsExist(err) { | |||
if walk.DlgCmdNo == walk.MsgBox(owner, "Writing file failed", fmt.Sprintf(`File ‘%s’ already exists. | |||
if walk.DlgCmdNo == walk.MsgBox(owner, l18n.Sprintf("Writing file failed"), l18n.Sprintf(`File ‘%s’ already exists. | |||
Do you want to overwrite it?`, filePath), walk.MsgBoxYesNo|walk.MsgBoxDefButton2|walk.MsgBoxIconWarning) { | |||
return false | |||
@@ -8,6 +8,7 @@ package ui | |||
import ( | |||
"github.com/lxn/walk" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
) | |||
@@ -87,15 +88,15 @@ func iconForState(state manager.TunnelState, size int) (icon *walk.Icon, err err | |||
func textForState(state manager.TunnelState, withEllipsis bool) (text string) { | |||
switch state { | |||
case manager.TunnelStarted: | |||
text = "Active" | |||
text = l18n.Sprintf("Active") | |||
case manager.TunnelStarting: | |||
text = "Activating" | |||
text = l18n.Sprintf("Activating") | |||
case manager.TunnelStopped: | |||
text = "Inactive" | |||
text = l18n.Sprintf("Inactive") | |||
case manager.TunnelStopping: | |||
text = "Deactivating" | |||
text = l18n.Sprintf("Deactivating") | |||
case manager.TunnelUnknown: | |||
text = "Unknown state" | |||
text = l18n.Sprintf("Unknown state") | |||
} | |||
if withEllipsis { | |||
switch state { | |||
@@ -12,6 +12,7 @@ import ( | |||
"time" | |||
"github.com/lxn/walk" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/ringlogger" | |||
) | |||
@@ -41,7 +42,7 @@ func NewLogPage() (*LogPage, error) { | |||
lp.model.quit <- true | |||
}) | |||
lp.SetTitle("Log") | |||
lp.SetTitle(l18n.Sprintf("Log")) | |||
lp.SetLayout(walk.NewVBoxLayout()) | |||
if lp.logView, err = walk.NewTableView(lp); err != nil { | |||
@@ -57,19 +58,19 @@ func NewLogPage() (*LogPage, error) { | |||
} | |||
lp.logView.AddDisposable(contextMenu) | |||
copyAction := walk.NewAction() | |||
copyAction.SetText("&Copy") | |||
copyAction.SetText(l18n.Sprintf("&Copy")) | |||
copyAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyC}) | |||
copyAction.Triggered().Attach(lp.onCopy) | |||
contextMenu.Actions().Add(copyAction) | |||
lp.ShortcutActions().Add(copyAction) | |||
selectAllAction := walk.NewAction() | |||
selectAllAction.SetText("Select &all") | |||
selectAllAction.SetText(l18n.Sprintf("Select &all")) | |||
selectAllAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyA}) | |||
selectAllAction.Triggered().Attach(lp.onSelectAll) | |||
contextMenu.Actions().Add(selectAllAction) | |||
lp.ShortcutActions().Add(selectAllAction) | |||
saveAction := walk.NewAction() | |||
saveAction.SetText("&Save to file…") | |||
saveAction.SetText(l18n.Sprintf("&Save to file…")) | |||
saveAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyS}) | |||
saveAction.Triggered().Attach(lp.onSave) | |||
contextMenu.Actions().Add(saveAction) | |||
@@ -83,14 +84,14 @@ func NewLogPage() (*LogPage, error) { | |||
stampCol := walk.NewTableViewColumn() | |||
stampCol.SetName("Stamp") | |||
stampCol.SetTitle("Time") | |||
stampCol.SetTitle(l18n.Sprintf("Time")) | |||
stampCol.SetFormat("2006-01-02 15:04:05.000") | |||
stampCol.SetWidth(140) | |||
lp.logView.Columns().Add(stampCol) | |||
msgCol := walk.NewTableViewColumn() | |||
msgCol.SetName("Line") | |||
msgCol.SetTitle("Log message") | |||
msgCol.SetTitle(l18n.Sprintf("Log message")) | |||
lp.logView.Columns().Add(msgCol) | |||
lp.model = newLogModel(lp) | |||
@@ -111,7 +112,7 @@ func NewLogPage() (*LogPage, error) { | |||
if err != nil { | |||
return nil, err | |||
} | |||
saveButton.SetText("&Save") | |||
saveButton.SetText(l18n.Sprintf("&Save")) | |||
saveButton.Clicked().Attach(lp.onSave) | |||
disposables.Spare() | |||
@@ -146,9 +147,9 @@ func (lp *LogPage) onSelectAll() { | |||
func (lp *LogPage) onSave() { | |||
fd := walk.FileDialog{ | |||
Filter: "Text Files (*.txt)|*.txt|All Files (*.*)|*.*", | |||
Filter: l18n.Sprintf("Text Files (*.txt)|*.txt|All Files (*.*)|*.*"), | |||
FilePath: fmt.Sprintf("wireguard-log-%s.txt", time.Now().Format("2006-01-02T150405")), | |||
Title: "Export log to file", | |||
Title: l18n.Sprintf("Export log to file"), | |||
} | |||
form := lp.Form() | |||
@@ -13,6 +13,7 @@ import ( | |||
"github.com/lxn/win" | |||
"golang.org/x/sys/windows" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
) | |||
@@ -118,7 +119,7 @@ func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) { | |||
CbSize: uint32(unsafe.Sizeof(win.MENUITEMINFO{})), | |||
FMask: win.MIIM_ID | win.MIIM_STRING | win.MIIM_FTYPE, | |||
FType: win.MIIM_STRING, | |||
DwTypeData: windows.StringToUTF16Ptr("&About WireGuard…"), | |||
DwTypeData: windows.StringToUTF16Ptr(l18n.Sprintf("&About WireGuard…")), | |||
WID: uint32(aboutWireGuardCmd), | |||
}) | |||
win.InsertMenuItem(systemMenu, 1, true, &win.MENUITEMINFO{ | |||
@@ -169,7 +170,7 @@ func (mtw *ManageTunnelsWindow) onTunnelChange(tunnel *manager.Tunnel, state man | |||
if len(errMsg) > 0 && errMsg[len(errMsg)-1] != '.' { | |||
errMsg += "." | |||
} | |||
showWarningCustom(mtw, "Tunnel Error", errMsg+"\n\nPlease consult the log for more information.") | |||
showWarningCustom(mtw, l18n.Sprintf("Tunnel Error"), l18n.Sprintf("%s\n\nPlease consult the log for more information.", errMsg)) | |||
} | |||
}) | |||
} | |||
@@ -178,7 +179,7 @@ func (mtw *ManageTunnelsWindow) UpdateFound() { | |||
if mtw.updatePage != nil { | |||
return | |||
} | |||
mtw.SetTitle(mtw.Title() + " (out of date)") | |||
mtw.SetTitle(l18n.Sprintf("%s (out of date)", mtw.Title())) | |||
updatePage, err := NewUpdatePage() | |||
if err == nil { | |||
mtw.updatePage = updatePage | |||
@@ -6,12 +6,13 @@ | |||
package ui | |||
import ( | |||
"fmt" | |||
"os" | |||
"runtime" | |||
"github.com/lxn/win" | |||
"golang.org/x/sys/windows" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
) | |||
func raise(hwnd win.HWND) { | |||
@@ -66,7 +67,7 @@ func WaitForRaiseUIThenQuit() { | |||
return 0 | |||
}, 0, 0, win.WINEVENT_SKIPOWNPROCESS|win.WINEVENT_OUTOFCONTEXT) | |||
if err != nil { | |||
showErrorCustom(nil, "WireGuard Detection Error", fmt.Sprintf("Unable to wait for WireGuard window to appear: %v", err)) | |||
showErrorCustom(nil, l18n.Sprintf("WireGuard Detection Error"), l18n.Sprintf("Unable to wait for WireGuard window to appear: %v", err)) | |||
} | |||
for { | |||
var msg win.MSG | |||
@@ -6,12 +6,12 @@ | |||
package ui | |||
import ( | |||
"fmt" | |||
"sort" | |||
"strings" | |||
"time" | |||
"golang.zx2c4.com/wireguard/windows/conf" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
"github.com/lxn/walk" | |||
@@ -53,7 +53,7 @@ func NewTray(mtw *ManageTunnelsWindow) (*Tray, error) { | |||
func (tray *Tray) setup() error { | |||
tray.clicked = tray.onManageTunnels | |||
tray.SetToolTip("WireGuard: Deactivated") | |||
tray.SetToolTip(l18n.Sprintf("WireGuard: Deactivated")) | |||
tray.SetVisible(true) | |||
if icon, err := loadLogoIcon(16); err == nil { | |||
tray.SetIcon(icon) | |||
@@ -76,15 +76,15 @@ func (tray *Tray) setup() error { | |||
separator bool | |||
defawlt bool | |||
}{ | |||
{label: "Status: Unknown"}, | |||
{label: "Addresses: None", hidden: true}, | |||
{label: l18n.Sprintf("Status: Unknown")}, | |||
{label: l18n.Sprintf("Addresses: None"), hidden: true}, | |||
{separator: true}, | |||
{separator: true}, | |||
{label: "&Manage tunnels…", handler: tray.onManageTunnels, enabled: true, defawlt: true}, | |||
{label: "&Import tunnel(s) from file…", handler: tray.onImport, enabled: true}, | |||
{label: l18n.Sprintf("&Manage tunnels…"), handler: tray.onManageTunnels, enabled: true, defawlt: true}, | |||
{label: l18n.Sprintf("&Import tunnel(s) from file…"), handler: tray.onImport, enabled: true}, | |||
{separator: true}, | |||
{label: "&About WireGuard…", handler: tray.onAbout, enabled: true}, | |||
{label: "E&xit", handler: onQuit, enabled: true}, | |||
{label: l18n.Sprintf("&About WireGuard…"), handler: tray.onAbout, enabled: true}, | |||
{label: l18n.Sprintf("E&xit"), handler: onQuit, enabled: true}, | |||
} { | |||
var action *walk.Action | |||
if item.separator { | |||
@@ -160,11 +160,11 @@ func (tray *Tray) addTunnelAction(tunnel *manager.Tunnel) { | |||
tray.mtw.tunnelsPage.listView.selectTunnel(tclosure.Name) | |||
tray.mtw.tabs.SetCurrentIndex(0) | |||
if oldState == manager.TunnelUnknown { | |||
showErrorCustom(tray.mtw, "Failed to determine tunnel state", err.Error()) | |||
showErrorCustom(tray.mtw, l18n.Sprintf("Failed to determine tunnel state"), err.Error()) | |||
} else if oldState == manager.TunnelStopped { | |||
showErrorCustom(tray.mtw, "Failed to activate tunnel", err.Error()) | |||
showErrorCustom(tray.mtw, l18n.Sprintf("Failed to activate tunnel"), err.Error()) | |||
} else if oldState == manager.TunnelStarted { | |||
showErrorCustom(tray.mtw, "Failed to deactivate tunnel", err.Error()) | |||
showErrorCustom(tray.mtw, l18n.Sprintf("Failed to deactivate tunnel"), err.Error()) | |||
} | |||
}) | |||
} | |||
@@ -213,7 +213,7 @@ func (tray *Tray) onTunnelChange(tunnel *manager.Tunnel, state manager.TunnelSta | |||
tray.updateGlobalState(globalState) | |||
tray.SetTunnelState(tunnel, state, err == nil) | |||
if !tray.mtw.Visible() && err != nil { | |||
tray.ShowError("WireGuard Tunnel Error", err.Error()) | |||
tray.ShowError(l18n.Sprintf("WireGuard Tunnel Error"), err.Error()) | |||
} | |||
}) | |||
} | |||
@@ -234,8 +234,9 @@ func (tray *Tray) updateGlobalState(globalState manager.TunnelState) { | |||
} | |||
} | |||
tray.SetToolTip(fmt.Sprintf("WireGuard: %s", textForState(globalState, true))) | |||
statusAction.SetText(fmt.Sprintf("Status: %s", textForState(globalState, false))) | |||
tray.SetToolTip(l18n.Sprintf("WireGuard: %s", textForState(globalState, true))) | |||
stateText := textForState(globalState, false) | |||
statusAction.SetText(l18n.Sprintf("Status: %s", stateText)) | |||
switch globalState { | |||
case manager.TunnelStarting: | |||
@@ -274,13 +275,13 @@ func (tray *Tray) SetTunnelState(tunnel *manager.Tunnel, state manager.TunnelSta | |||
var sb strings.Builder | |||
for i, addr := range config.Interface.Addresses { | |||
if i > 0 { | |||
sb.WriteString(", ") | |||
sb.WriteString(l18n.EnumerationSeparator()) | |||
} | |||
sb.WriteString(addr.String()) | |||
} | |||
tray.mtw.Synchronize(func() { | |||
activeCIDRsAction.SetText(fmt.Sprintf("Addresses: %s", sb.String())) | |||
activeCIDRsAction.SetText(l18n.Sprintf("Addresses: %s", sb.String())) | |||
}) | |||
} | |||
}() | |||
@@ -288,21 +289,21 @@ func (tray *Tray) SetTunnelState(tunnel *manager.Tunnel, state manager.TunnelSta | |||
tunnelAction.SetChecked(true) | |||
if !wasChecked && showNotifications { | |||
icon, _ := iconWithOverlayForState(state, 128) | |||
tray.ShowCustom("WireGuard Activated", fmt.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon) | |||
tray.ShowCustom(l18n.Sprintf("WireGuard Activated"), l18n.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon) | |||
} | |||
case manager.TunnelStopped: | |||
tunnelAction.SetChecked(false) | |||
if wasChecked && showNotifications { | |||
icon, _ := loadSystemIcon("imageres", 26, 128) // TODO: this icon isn't very good... | |||
tray.ShowCustom("WireGuard Deactivated", fmt.Sprintf("The %s tunnel has been deactivated.", tunnel.Name), icon) | |||
tray.ShowCustom(l18n.Sprintf("WireGuard Deactivated"), l18n.Sprintf("The %s tunnel has been deactivated.", tunnel.Name), icon) | |||
} | |||
} | |||
} | |||
func (tray *Tray) UpdateFound() { | |||
action := walk.NewAction() | |||
action.SetText("An Update is Available!") | |||
action.SetText(l18n.Sprintf("An Update is Available!")) | |||
menuIcon, _ := loadSystemIcon("imageres", 1, 16) | |||
action.SetImage(menuIcon) | |||
action.SetDefault(true) | |||
@@ -319,7 +320,7 @@ func (tray *Tray) UpdateFound() { | |||
showUpdateBalloon := func() { | |||
icon, _ := loadSystemIcon("imageres", 1, 128) | |||
tray.ShowCustom("WireGuard Update Available", "An update to WireGuard is now available. You are advised to update as soon as possible.", icon) | |||
tray.ShowCustom(l18n.Sprintf("WireGuard Update Available"), l18n.Sprintf("An update to WireGuard is now available. You are advised to update as soon as possible."), icon) | |||
} | |||
timeSinceStart := time.Now().Sub(startTime) | |||
@@ -7,6 +7,7 @@ package ui | |||
import ( | |||
"archive/zip" | |||
"errors" | |||
"fmt" | |||
"io/ioutil" | |||
"os" | |||
@@ -17,6 +18,7 @@ import ( | |||
"github.com/lxn/walk" | |||
"golang.zx2c4.com/wireguard/windows/conf" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
) | |||
@@ -45,7 +47,7 @@ func NewTunnelsPage() (*TunnelsPage, error) { | |||
} | |||
disposables.Add(tp) | |||
tp.SetTitle("Tunnels") | |||
tp.SetTitle(l18n.Sprintf("Tunnels")) | |||
tp.SetLayout(walk.NewHBoxLayout()) | |||
tp.listContainer, _ = walk.NewComposite(tp) | |||
@@ -101,7 +103,7 @@ func NewTunnelsPage() (*TunnelsPage, error) { | |||
tp.listView.CurrentIndexChanged().Attach(func() { | |||
editTunnel.SetEnabled(tp.listView.CurrentIndex() > -1) | |||
}) | |||
editTunnel.SetText("&Edit") | |||
editTunnel.SetText(l18n.Sprintf("&Edit")) | |||
editTunnel.Clicked().Attach(tp.onEditTunnel) | |||
disposables.Spare() | |||
@@ -142,7 +144,7 @@ func (tp *TunnelsPage) CreateToolbar() error { | |||
} | |||
tp.AddDisposable(addMenu) | |||
importAction := walk.NewAction() | |||
importAction.SetText("&Import tunnel(s) from file…") | |||
importAction.SetText(l18n.Sprintf("&Import tunnel(s) from file…")) | |||
importActionIcon, _ := loadSystemIcon("imageres", 3, 16) | |||
importAction.SetImage(importActionIcon) | |||
importAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyO}) | |||
@@ -150,7 +152,7 @@ func (tp *TunnelsPage) CreateToolbar() error { | |||
importAction.Triggered().Attach(tp.onImport) | |||
addMenu.Actions().Add(importAction) | |||
addAction := walk.NewAction() | |||
addAction.SetText("Add &empty tunnel…") | |||
addAction.SetText(l18n.Sprintf("Add &empty tunnel…")) | |||
addActionIcon, _ := loadSystemIcon("imageres", 2, 16) | |||
addAction.SetImage(addActionIcon) | |||
addAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyN}) | |||
@@ -159,7 +161,7 @@ func (tp *TunnelsPage) CreateToolbar() error { | |||
addMenuAction := walk.NewMenuAction(addMenu) | |||
addMenuActionIcon, _ := loadSystemIcon("shell32", 149, 16) | |||
addMenuAction.SetImage(addMenuActionIcon) | |||
addMenuAction.SetText("Add Tunnel") | |||
addMenuAction.SetText(l18n.Sprintf("Add Tunnel")) | |||
addMenuAction.SetToolTip(importAction.Text()) | |||
addMenuAction.Triggered().Attach(tp.onImport) | |||
tp.listToolbar.Actions().Add(addMenuAction) | |||
@@ -170,7 +172,7 @@ func (tp *TunnelsPage) CreateToolbar() error { | |||
deleteActionIcon, _ := loadSystemIcon("shell32", 131, 16) | |||
deleteAction.SetImage(deleteActionIcon) | |||
deleteAction.SetShortcut(walk.Shortcut{0, walk.KeyDelete}) | |||
deleteAction.SetToolTip("Remove selected tunnel(s)") | |||
deleteAction.SetToolTip(l18n.Sprintf("Remove selected tunnel(s)")) | |||
deleteAction.Triggered().Attach(tp.onDelete) | |||
tp.listToolbar.Actions().Add(deleteAction) | |||
tp.listToolbar.Actions().Add(walk.NewSeparatorAction()) | |||
@@ -178,7 +180,7 @@ func (tp *TunnelsPage) CreateToolbar() error { | |||
exportAction := walk.NewAction() | |||
exportActionIcon, _ := loadSystemIcon("imageres", 165, 16) // Or "shell32", 45? | |||
exportAction.SetImage(exportActionIcon) | |||
exportAction.SetToolTip("Export all tunnels to zip…") | |||
exportAction.SetToolTip(l18n.Sprintf("Export all tunnels to zip")) | |||
exportAction.Triggered().Attach(tp.onExportTunnels) | |||
tp.listToolbar.Actions().Add(exportAction) | |||
@@ -195,42 +197,42 @@ func (tp *TunnelsPage) CreateToolbar() error { | |||
} | |||
tp.listView.AddDisposable(contextMenu) | |||
toggleAction := walk.NewAction() | |||
toggleAction.SetText("&Toggle") | |||
toggleAction.SetText(l18n.Sprintf("&Toggle")) | |||
toggleAction.SetDefault(true) | |||
toggleAction.Triggered().Attach(tp.onTunnelsViewItemActivated) | |||
contextMenu.Actions().Add(toggleAction) | |||
contextMenu.Actions().Add(walk.NewSeparatorAction()) | |||
importAction2 := walk.NewAction() | |||
importAction2.SetText("&Import tunnel(s) from file…") | |||
importAction2.SetText(l18n.Sprintf("&Import tunnel(s) from file…")) | |||
importAction2.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyO}) | |||
importAction2.Triggered().Attach(tp.onImport) | |||
contextMenu.Actions().Add(importAction2) | |||
tp.ShortcutActions().Add(importAction2) | |||
addAction2 := walk.NewAction() | |||
addAction2.SetText("Add &empty tunnel…") | |||
addAction2.SetText(l18n.Sprintf("Add &empty tunnel…")) | |||
addAction2.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyN}) | |||
addAction2.Triggered().Attach(tp.onAddTunnel) | |||
contextMenu.Actions().Add(addAction2) | |||
tp.ShortcutActions().Add(addAction2) | |||
exportAction2 := walk.NewAction() | |||
exportAction2.SetText("Export all tunnels to &zip…") | |||
exportAction2.SetText(l18n.Sprintf("Export all tunnels to &zip…")) | |||
exportAction2.Triggered().Attach(tp.onExportTunnels) | |||
contextMenu.Actions().Add(exportAction2) | |||
contextMenu.Actions().Add(walk.NewSeparatorAction()) | |||
editAction := walk.NewAction() | |||
editAction.SetText("Edit &selected tunnel…") | |||
editAction.SetText(l18n.Sprintf("Edit &selected tunnel…")) | |||
editAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyE}) | |||
editAction.Triggered().Attach(tp.onEditTunnel) | |||
contextMenu.Actions().Add(editAction) | |||
tp.ShortcutActions().Add(editAction) | |||
deleteAction2 := walk.NewAction() | |||
deleteAction2.SetText("&Remove selected tunnel(s)") | |||
deleteAction2.SetText(l18n.Sprintf("&Remove selected tunnel(s)")) | |||
deleteAction2.SetShortcut(walk.Shortcut{0, walk.KeyDelete}) | |||
deleteAction2.Triggered().Attach(tp.onDelete) | |||
contextMenu.Actions().Add(deleteAction2) | |||
tp.listView.ShortcutActions().Add(deleteAction2) | |||
selectAllAction := walk.NewAction() | |||
selectAllAction.SetText("Select &all") | |||
selectAllAction.SetText(l18n.Sprintf("Select &all")) | |||
selectAllAction.SetShortcut(walk.Shortcut{walk.ModControl, walk.KeyA}) | |||
selectAllAction.Triggered().Attach(tp.onSelectAll) | |||
contextMenu.Actions().Add(selectAllAction) | |||
@@ -324,7 +326,7 @@ func (tp *TunnelsPage) importFiles(paths []string) { | |||
} | |||
if lastErr != nil || unparsedConfigs == nil { | |||
syncedMsgBox("Error", fmt.Sprintf("Could not import selected configuration: %v", lastErr), walk.MsgBoxIconWarning) | |||
syncedMsgBox(l18n.Sprintf("Error"), l18n.Sprintf("Could not import selected configuration: %v", lastErr), walk.MsgBoxIconWarning) | |||
return | |||
} | |||
@@ -335,7 +337,7 @@ func (tp *TunnelsPage) importFiles(paths []string) { | |||
existingTunnelList, err := manager.IPCClientTunnels() | |||
if err != nil { | |||
syncedMsgBox("Error", fmt.Sprintf("Could not enumerate existing tunnels: %v", lastErr), walk.MsgBoxIconWarning) | |||
syncedMsgBox(l18n.Sprintf("Error"), l18n.Sprintf("Could not enumerate existing tunnels: %v", lastErr), walk.MsgBoxIconWarning) | |||
return | |||
} | |||
existingLowerTunnels := make(map[string]bool, len(existingTunnelList)) | |||
@@ -347,7 +349,7 @@ func (tp *TunnelsPage) importFiles(paths []string) { | |||
tp.listView.SetSuspendTunnelsUpdate(true) | |||
for _, unparsedConfig := range unparsedConfigs { | |||
if existingLowerTunnels[strings.ToLower(unparsedConfig.Name)] { | |||
lastErr = fmt.Errorf("Another tunnel already exists with the name ‘%s’", unparsedConfig.Name) | |||
lastErr = errors.New(l18n.Sprintf("Another tunnel already exists with the name ‘%s’", unparsedConfig.Name)) | |||
continue | |||
} | |||
config, err := conf.FromWgQuickWithUnknownEncoding(unparsedConfig.Config, unparsedConfig.Name) | |||
@@ -367,13 +369,13 @@ func (tp *TunnelsPage) importFiles(paths []string) { | |||
m, n := configCount, len(unparsedConfigs) | |||
switch { | |||
case n == 1 && m != n: | |||
syncedMsgBox("Error", fmt.Sprintf("Unable to import configuration: %v", lastErr), walk.MsgBoxIconWarning) | |||
syncedMsgBox(l18n.Sprintf("Error"), l18n.Sprintf("Unable to import configuration: %v", lastErr), walk.MsgBoxIconWarning) | |||
case n == 1 && m == n: | |||
// nothing | |||
case m == n: | |||
syncedMsgBox("Imported tunnels", fmt.Sprintf("Imported %d tunnels", m), walk.MsgBoxIconInformation) | |||
syncedMsgBox(l18n.Sprintf("Imported tunnels"), l18n.Sprintf("Imported %d tunnels", m), walk.MsgBoxIconInformation) | |||
case m != n: | |||
syncedMsgBox("Imported tunnels", fmt.Sprintf("Imported %d of %d tunnels", m, n), walk.MsgBoxIconWarning) | |||
syncedMsgBox(l18n.Sprintf("Imported tunnels"), l18n.Sprintf("Imported %d of %d tunnels", m, n), walk.MsgBoxIconWarning) | |||
} | |||
}() | |||
} | |||
@@ -405,7 +407,7 @@ func (tp *TunnelsPage) exportTunnels(filePath string) { | |||
func (tp *TunnelsPage) addTunnel(config *conf.Config) { | |||
_, err := manager.IPCClientNewTunnel(config) | |||
if err != nil { | |||
showErrorCustom(tp.Form(), "Unable to create tunnel", err.Error()) | |||
showErrorCustom(tp.Form(), l18n.Sprintf("Unable to create tunnel"), err.Error()) | |||
} | |||
} | |||
@@ -422,11 +424,11 @@ func (tp *TunnelsPage) onTunnelsViewItemActivated() { | |||
if err != nil { | |||
tp.Synchronize(func() { | |||
if oldState == manager.TunnelUnknown { | |||
showErrorCustom(tp.Form(), "Failed to determine tunnel state", err.Error()) | |||
showErrorCustom(tp.Form(), l18n.Sprintf("Failed to determine tunnel state"), err.Error()) | |||
} else if oldState == manager.TunnelStopped { | |||
showErrorCustom(tp.Form(), "Failed to activate tunnel", err.Error()) | |||
showErrorCustom(tp.Form(), l18n.Sprintf("Failed to activate tunnel"), err.Error()) | |||
} else if oldState == manager.TunnelStarted { | |||
showErrorCustom(tp.Form(), "Failed to deactivate tunnel", err.Error()) | |||
showErrorCustom(tp.Form(), l18n.Sprintf("Failed to deactivate tunnel"), err.Error()) | |||
} | |||
}) | |||
return | |||
@@ -466,16 +468,20 @@ func (tp *TunnelsPage) onDelete() { | |||
return | |||
} | |||
var topic string | |||
var title, question string | |||
if len(indices) > 1 { | |||
topic = fmt.Sprintf("%d tunnels", len(indices)) | |||
tunnelCount := len(indices) | |||
title = l18n.Sprintf("Delete %d tunnels", tunnelCount) | |||
question = l18n.Sprintf("Are you sure you would like to delete %d tunnels?", tunnelCount) | |||
} else { | |||
topic = fmt.Sprintf("‘%s’", tp.listView.model.tunnels[indices[0]].Name) | |||
tunnelName := tp.listView.model.tunnels[indices[0]].Name | |||
title = l18n.Sprintf("Delete tunnel ‘%s’", tunnelName) | |||
question = l18n.Sprintf("Are you sure you would like to delete tunnel ‘%s’?", tunnelName) | |||
} | |||
if walk.DlgCmdNo == walk.MsgBox( | |||
tp.Form(), | |||
fmt.Sprintf("Delete %s", topic), | |||
fmt.Sprintf("Are you sure you would like to delete %s? You cannot undo this action.", topic), | |||
title, | |||
l18n.Sprintf("%s You cannot undo this action.", question), | |||
walk.MsgBoxYesNo|walk.MsgBoxIconWarning) { | |||
return | |||
} | |||
@@ -515,9 +521,9 @@ func (tp *TunnelsPage) onDelete() { | |||
if len(errors) > 0 { | |||
tp.listView.Synchronize(func() { | |||
if len(errors) == 1 { | |||
showErrorCustom(tp.Form(), "Unable to delete tunnel", fmt.Sprintf("A tunnel was unable to be removed: %s", errors[0].Error())) | |||
showErrorCustom(tp.Form(), l18n.Sprintf("Unable to delete tunnel"), l18n.Sprintf("A tunnel was unable to be removed: %s", errors[0].Error())) | |||
} else { | |||
showErrorCustom(tp.Form(), "Unable to delete tunnels", fmt.Sprintf("%d tunnels were unable to be removed.", len(errors))) | |||
showErrorCustom(tp.Form(), l18n.Sprintf("Unable to delete tunnels"), l18n.Sprintf("%d tunnels were unable to be removed.", len(errors))) | |||
} | |||
}) | |||
} | |||
@@ -530,8 +536,8 @@ func (tp *TunnelsPage) onSelectAll() { | |||
func (tp *TunnelsPage) onImport() { | |||
dlg := walk.FileDialog{ | |||
Filter: "Configuration Files (*.zip, *.conf)|*.zip;*.conf|All Files (*.*)|*.*", | |||
Title: "Import tunnel(s) from file", | |||
Filter: l18n.Sprintf("Configuration Files (*.zip, *.conf)|*.zip;*.conf|All Files (*.*)|*.*"), | |||
Title: l18n.Sprintf("Import tunnel(s) from file"), | |||
} | |||
if ok, _ := dlg.ShowOpenMultiple(tp.Form()); !ok { | |||
@@ -543,8 +549,8 @@ func (tp *TunnelsPage) onImport() { | |||
func (tp *TunnelsPage) onExportTunnels() { | |||
dlg := walk.FileDialog{ | |||
Filter: "Configuration ZIP Files (*.zip)|*.zip", | |||
Title: "Export tunnels to zip", | |||
Filter: l18n.Sprintf("Configuration ZIP Files (*.zip)|*.zip"), | |||
Title: l18n.Sprintf("Export tunnels to zip"), | |||
} | |||
if ok, _ := dlg.ShowSave(tp.Form()); !ok { | |||
@@ -571,7 +577,7 @@ func (tp *TunnelsPage) swapFiller(enabled bool) bool { | |||
func (tp *TunnelsPage) onTunnelsChanged() { | |||
if tp.swapFiller(tp.listView.model.RowCount() == 0) { | |||
tp.fillerButton.SetText("Import tunnel(s) from file") | |||
tp.fillerButton.SetText(l18n.Sprintf("Import tunnel(s) from file")) | |||
tp.fillerHandler = tp.onImport | |||
} | |||
} | |||
@@ -581,8 +587,9 @@ func (tp *TunnelsPage) onSelectedTunnelsChanged() { | |||
return | |||
} | |||
indices := tp.listView.SelectedIndexes() | |||
if tp.swapFiller(len(indices) > 1) { | |||
tp.fillerButton.SetText(fmt.Sprintf("Delete %d tunnels", len(indices))) | |||
tunnelCount := len(indices) | |||
if tp.swapFiller(tunnelCount > 1) { | |||
tp.fillerButton.SetText(l18n.Sprintf("Delete %d tunnels", tunnelCount)) | |||
tp.fillerHandler = tp.onDelete | |||
} | |||
} |
@@ -15,6 +15,7 @@ import ( | |||
"github.com/lxn/win" | |||
"golang.org/x/sys/windows" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
"golang.zx2c4.com/wireguard/windows/version" | |||
) | |||
@@ -75,7 +76,7 @@ func RunUI() { | |||
tray.UpdateFound() | |||
} | |||
case manager.UpdateStateUpdatesDisabledUnofficialBuild: | |||
mtw.SetTitle(mtw.Title() + " (unsigned build, no updates)") | |||
mtw.SetTitle(l18n.Sprintf("%s (unsigned build, no updates)", mtw.Title())) | |||
} | |||
}) | |||
} | |||
@@ -100,7 +101,7 @@ func RunUI() { | |||
if shouldQuitManagerWhenExiting { | |||
_, err := manager.IPCClientQuit(true) | |||
if err != nil { | |||
showErrorCustom(nil, "Error Exiting WireGuard", fmt.Sprintf("Unable to exit service due to: %v. You may want to stop WireGuard from the service manager.", err)) | |||
showErrorCustom(nil, l18n.Sprintf("Error Exiting WireGuard"), l18n.Sprintf("Unable to exit service due to: %v. You may want to stop WireGuard from the service manager.", err)) | |||
} | |||
} | |||
} | |||
@@ -115,7 +116,7 @@ func showError(err error, owner walk.Form) bool { | |||
return false | |||
} | |||
showErrorCustom(owner, "Error", err.Error()) | |||
showErrorCustom(owner, l18n.Sprintf("Error"), err.Error()) | |||
return true | |||
} | |||
@@ -6,10 +6,9 @@ | |||
package ui | |||
import ( | |||
"fmt" | |||
"github.com/lxn/walk" | |||
"golang.zx2c4.com/wireguard/windows/l18n" | |||
"golang.zx2c4.com/wireguard/windows/manager" | |||
"golang.zx2c4.com/wireguard/windows/updater" | |||
) | |||
@@ -30,7 +29,7 @@ func NewUpdatePage() (*UpdatePage, error) { | |||
} | |||
disposables.Add(up) | |||
up.SetTitle("An Update is Available!") | |||
up.SetTitle(l18n.Sprintf("An Update is Available!")) | |||
tabIcon, _ := loadSystemIcon("imageres", 1, 16) | |||
up.SetImage(tabIcon) | |||
@@ -41,14 +40,14 @@ func NewUpdatePage() (*UpdatePage, error) { | |||
if err != nil { | |||
return nil, err | |||
} | |||
instructions.SetText("An update to WireGuard is available. It is highly advisable to update without delay.") | |||
instructions.SetText(l18n.Sprintf("An update to WireGuard is available. It is highly advisable to update without delay.")) | |||
instructions.SetMinMaxSize(walk.Size{1, 0}, walk.Size{0, 0}) | |||
status, err := walk.NewTextLabel(up) | |||
if err != nil { | |||
return nil, err | |||
} | |||
status.SetText("Status: Waiting for user") | |||
status.SetText(l18n.Sprintf("Status: Waiting for user")) | |||
status.SetMinMaxSize(walk.Size{1, 0}, walk.Size{0, 0}) | |||
bar, err := walk.NewProgressBar(up) | |||
@@ -63,7 +62,7 @@ func NewUpdatePage() (*UpdatePage, error) { | |||
} | |||
updateIcon, _ := loadSystemIcon("shell32", 46, 32) | |||
button.SetImage(updateIcon) | |||
button.SetText("Update Now") | |||
button.SetText(l18n.Sprintf("Update Now")) | |||
walk.NewVSpacer(up) | |||
@@ -75,7 +74,7 @@ func NewUpdatePage() (*UpdatePage, error) { | |||
bar.SetVisible(true) | |||
bar.SetMarqueeMode(true) | |||
up.SetSuspended(false) | |||
status.SetText("Status: Waiting for updater service") | |||
status.SetText(l18n.Sprintf("Status: Waiting for updater service")) | |||
} | |||
} | |||
@@ -97,7 +96,7 @@ func NewUpdatePage() (*UpdatePage, error) { | |||
err := manager.IPCClientUpdate() | |||
if err != nil { | |||
switchToReadyState() | |||
status.SetText(fmt.Sprintf("Error: %v. Please try again.", err)) | |||
status.SetText(l18n.Sprintf("Error: %v. Please try again.", err)) | |||
} | |||
}) | |||
@@ -106,11 +105,13 @@ func NewUpdatePage() (*UpdatePage, error) { | |||
switchToUpdatingState() | |||
if dp.Error != nil { | |||
switchToReadyState() | |||
status.SetText(fmt.Sprintf("Error: %v. Please try again.", dp.Error)) | |||
err := dp.Error | |||
status.SetText(l18n.Sprintf("Error: %v. Please try again.", err)) | |||
return | |||
} | |||
if len(dp.Activity) > 0 { | |||
status.SetText(fmt.Sprintf("Status: %s", dp.Activity)) | |||
stateText := dp.Activity | |||
status.SetText(l18n.Sprintf("Status: %s", stateText)) | |||
} | |||
if dp.BytesTotal > 0 { | |||
bar.SetMarqueeMode(false) | |||
@@ -123,7 +124,7 @@ func NewUpdatePage() (*UpdatePage, error) { | |||
} | |||
if dp.Complete { | |||
switchToReadyState() | |||
status.SetText("Status: Complete!") | |||
status.SetText(l18n.Sprintf("Status: Complete!")) | |||
return | |||
} | |||
}) | |||
@@ -0,0 +1,357 @@ | |||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | |||
package main | |||
import ( | |||
"golang.org/x/text/language" | |||
"golang.org/x/text/message" | |||
"golang.org/x/text/message/catalog" | |||
) | |||
type dictionary struct { | |||
index []uint32 | |||
data string | |||
} | |||
func (d *dictionary) Lookup(key string) (data string, ok bool) { | |||
p, ok := messageKeyToIndex[key] | |||
if !ok { | |||
return "", false | |||
} | |||
start, end := d.index[p], d.index[p+1] | |||
if start == end { | |||
return "", false | |||
} | |||
return d.data[start:end], true | |||
} | |||
func init() { | |||
dict := map[string]catalog.Dictionary{ | |||
"en": &dictionary{index: enIndex, data: enData}, | |||
} | |||
fallback := language.MustParse("en") | |||
cat, err := catalog.NewFromMap(dict, catalog.Fallback(fallback)) | |||
if err != nil { | |||
panic(err) | |||
} | |||
message.DefaultCatalog = cat | |||
} | |||
var messageKeyToIndex = map[string]int{ | |||
"%.2f\u00a0GiB": 21, | |||
"%.2f\u00a0KiB": 19, | |||
"%.2f\u00a0MiB": 20, | |||
"%.2f\u00a0TiB": 22, | |||
"%d day(s)": 13, | |||
"%d hour(s)": 14, | |||
"%d minute(s)": 15, | |||
"%d second(s)": 16, | |||
"%d tunnels were unable to be removed.": 157, | |||
"%d year(s)": 12, | |||
"%d\u00a0B": 18, | |||
"%s\n\nPlease consult the log for more information.": 110, | |||
"%s (out of date)": 111, | |||
"%s (unsigned build, no updates)": 162, | |||
"%s You cannot undo this action.": 153, | |||
"%s ago": 17, | |||
"%s received, %s sent": 70, | |||
"%s: %q": 23, | |||
"&About WireGuard…": 108, | |||
"&Activate": 57, | |||
"&Block untunneled traffic (kill-switch)": 81, | |||
"&Configuration:": 85, | |||
"&Copy": 101, | |||
"&Deactivate": 56, | |||
"&Edit": 132, | |||
"&Import tunnel(s) from file…": 118, | |||
"&Manage tunnels…": 117, | |||
"&Name:": 78, | |||
"&Public key:": 79, | |||
"&Remove selected tunnel(s)": 140, | |||
"&Save": 83, | |||
"&Save to file…": 103, | |||
"&Toggle": 137, | |||
"(no argument): elevate and install manager service": 1, | |||
"(unknown)": 80, | |||
"A name is required.": 87, | |||
"A tunnel was unable to be removed: %s": 155, | |||
"About WireGuard": 50, | |||
"Activating": 96, | |||
"Active": 95, | |||
"Add &empty tunnel…": 133, | |||
"Add Tunnel": 134, | |||
"Addresses:": 61, | |||
"Addresses: %s": 123, | |||
"Addresses: None": 116, | |||
"All peers must have public keys": 44, | |||
"Allowed IPs:": 64, | |||
"An Update is Available!": 128, | |||
"An interface must have a private key": 42, | |||
"An update to WireGuard is available. It is highly advisable to update without delay.": 165, | |||
"An update to WireGuard is now available. You are advised to update as soon as possible.": 130, | |||
"Another tunnel already exists with the name ‘%s’": 143, | |||
"Another tunnel already exists with the name ‘%s’.": 91, | |||
"App version: %s\nGo backend version: %s\nGo version: %s\nOperating system: %s\nArchitecture: %s": 52, | |||
"Are you sure you would like to delete %d tunnels?": 150, | |||
"Are you sure you would like to delete tunnel ‘%s’?": 152, | |||
"Brackets must contain an IPv6 address": 28, | |||
"Cancel": 84, | |||
"Close": 53, | |||
"Command Line Options": 3, | |||
"Configuration Files (*.zip, *.conf)|*.zip;*.conf|All Files (*.*)|*.*": 158, | |||
"Configuration ZIP Files (*.zip)|*.zip": 160, | |||
"Could not enumerate existing tunnels: %v": 142, | |||
"Could not import selected configuration: %v": 141, | |||
"Create new tunnel": 76, | |||
"DNS servers:": 62, | |||
"Deactivating": 98, | |||
"Delete %d tunnels": 149, | |||
"Delete tunnel ‘%s’": 151, | |||
"E&xit": 119, | |||
"Edit &selected tunnel…": 139, | |||
"Edit tunnel": 77, | |||
"Endpoint:": 65, | |||
"Error": 0, | |||
"Error Exiting WireGuard": 163, | |||
"Error in getting configuration": 45, | |||
"Error: %v. Please try again.": 169, | |||
"Export all tunnels to &zip…": 138, | |||
"Export all tunnels to zip": 136, | |||
"Export log to file": 107, | |||
"Export tunnels to zip": 161, | |||
"Failed to activate tunnel": 72, | |||
"Failed to deactivate tunnel": 73, | |||
"Failed to determine tunnel state": 71, | |||
"File ‘%s’ already exists.\n\nDo you want to overwrite it?": 94, | |||
"Import tunnel(s) from file": 159, | |||
"Imported %d of %d tunnels": 147, | |||
"Imported %d tunnels": 146, | |||
"Imported tunnels": 145, | |||
"Inactive": 97, | |||
"Interface: %s": 74, | |||
"Invalid IP address": 24, | |||
"Invalid MTU": 29, | |||
"Invalid config key is missing an equals separator": 38, | |||
"Invalid endpoint host": 27, | |||
"Invalid key for [Interface] section": 40, | |||
"Invalid key for [Peer] section": 41, | |||
"Invalid key for interface section": 46, | |||
"Invalid key for peer section": 48, | |||
"Invalid key: %v": 32, | |||
"Invalid name": 86, | |||
"Invalid network prefix length": 25, | |||
"Invalid persistent keepalive": 31, | |||
"Invalid port": 30, | |||
"Key must have a value": 39, | |||
"Keys must decode to exactly 32 bytes": 33, | |||
"Latest handshake:": 67, | |||
"Line must occur in a section": 37, | |||
"Listen port:": 59, | |||
"Log": 100, | |||
"Log message": 105, | |||
"MTU:": 60, | |||
"Missing port from endpoint": 26, | |||
"Now": 10, | |||
"Number must be a number between 0 and 2^64-1: %v": 34, | |||
"Peer": 75, | |||
"Persistent keepalive:": 66, | |||
"Preshared key:": 63, | |||
"Protocol version must be 1": 47, | |||
"Public key:": 58, | |||
"Remove selected tunnel(s)": 135, | |||
"Select &all": 102, | |||
"Status:": 55, | |||
"Status: %s": 122, | |||
"Status: Complete!": 170, | |||
"Status: Unknown": 115, | |||
"Status: Waiting for updater service": 168, | |||
"Status: Waiting for user": 166, | |||
"System clock wound backward!": 11, | |||
"Text Files (*.txt)|*.txt|All Files (*.*)|*.*": 106, | |||
"The %s tunnel has been activated.": 125, | |||
"The %s tunnel has been deactivated.": 127, | |||
"Time": 104, | |||
"Transfer:": 68, | |||
"Tunnel Error": 109, | |||
"Tunnel already exists": 90, | |||
"Tunnel name is not valid": 36, | |||
"Tunnel name ‘%s’ is invalid.": 88, | |||
"Tunnels": 131, | |||
"Two commas in a row": 35, | |||
"Unable to create new configuration": 92, | |||
"Unable to create tunnel": 148, | |||
"Unable to delete tunnel": 154, | |||
"Unable to delete tunnels": 156, | |||
"Unable to determine whether the process is running under WOW64: %v": 4, | |||
"Unable to exit service due to: %v. You may want to stop WireGuard from the service manager.": 164, | |||
"Unable to import configuration: %v": 144, | |||
"Unable to list existing tunnels": 89, | |||
"Unable to open current process token: %v": 6, | |||
"Unable to wait for WireGuard window to appear: %v": 113, | |||
"Unknown state": 99, | |||
"Update Now": 167, | |||
"Usage: %s [\n%s]": 2, | |||
"When a configuration has exactly one peer, and that peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, then the tunnel service engages a firewall ruleset to block all traffic that is neither to nor from the tunnel interface, with special exceptions for DHCP and NDP.": 82, | |||
"WireGuard Activated": 124, | |||
"WireGuard Deactivated": 126, | |||
"WireGuard Detection Error": 112, | |||
"WireGuard Tunnel Error": 120, | |||
"WireGuard Update Available": 129, | |||
"WireGuard is running, but the UI is only accessible from desktops of the Builtin %s group.": 8, | |||
"WireGuard logo image": 51, | |||
"WireGuard may only be used by users who are a member of the Builtin %s group.": 7, | |||
"WireGuard system tray icon did not appear after 30 seconds.": 9, | |||
"WireGuard: %s": 121, | |||
"WireGuard: Deactivated": 114, | |||
"Writing file failed": 93, | |||
"You must use the 64-bit version of WireGuard on this computer.": 5, | |||
"[EnumerationSeparator]": 49, | |||
"[none specified]": 43, | |||
"enabled": 69, | |||
"http2: Framer %p: failed to decode just-written frame": 171, | |||
"http2: Framer %p: read %v": 173, | |||
"http2: Framer %p: wrote %v": 172, | |||
"http2: decoded hpack field %+v": 174, | |||
"♥ &Donate!": 54, | |||
} | |||
var enIndex = []uint32{ // 176 elements | |||
// Entry 0 - 1F | |||
0x00000000, 0x00000006, 0x00000039, 0x0000004f, | |||
0x00000064, 0x000000aa, 0x000000e9, 0x00000115, | |||
0x00000166, 0x000001c4, 0x00000200, 0x00000204, | |||
0x00000221, 0x00000241, 0x0000025f, 0x0000027f, | |||
0x000002a3, 0x000002c7, 0x000002d1, 0x000002da, | |||
0x000002e7, 0x000002f4, 0x00000301, 0x0000030e, | |||
0x0000031b, 0x0000032e, 0x0000034c, 0x00000367, | |||
0x0000037d, 0x000003a3, 0x000003af, 0x000003bc, | |||
// Entry 20 - 3F | |||
0x000003d9, 0x000003ec, 0x00000411, 0x00000445, | |||
0x00000459, 0x00000472, 0x0000048f, 0x000004c1, | |||
0x000004d7, 0x000004fb, 0x0000051a, 0x0000053f, | |||
0x00000550, 0x00000570, 0x0000058f, 0x000005b1, | |||
0x000005cc, 0x000005e9, 0x000005ec, 0x000005fc, | |||
0x00000611, 0x0000067c, 0x00000682, 0x0000068f, | |||
0x00000697, 0x000006a3, 0x000006ad, 0x000006b9, | |||
0x000006c6, 0x000006cb, 0x000006d6, 0x000006e3, | |||
// Entry 40 - 5F | |||
0x000006f2, 0x000006ff, 0x00000709, 0x0000071f, | |||
0x00000731, 0x0000073b, 0x00000743, 0x0000075e, | |||
0x0000077f, 0x00000799, 0x000007b5, 0x000007c6, | |||
0x000007cb, 0x000007dd, 0x000007e9, 0x000007f0, | |||
0x000007fd, 0x00000807, 0x0000082f, 0x0000094d, | |||
0x00000953, 0x0000095a, 0x0000096a, 0x00000977, | |||
0x0000098b, 0x000009af, 0x000009cf, 0x000009e5, | |||
0x00000a1e, 0x00000a41, 0x00000a55, 0x00000a94, | |||
// Entry 60 - 7F | |||
0x00000a9b, 0x00000aa6, 0x00000aaf, 0x00000abc, | |||
0x00000aca, 0x00000ace, 0x00000ad4, 0x00000ae0, | |||
0x00000af1, 0x00000af6, 0x00000b02, 0x00000b2f, | |||
0x00000b42, 0x00000b56, 0x00000b63, 0x00000b97, | |||
0x00000bab, 0x00000bc5, 0x00000bfa, 0x00000c11, | |||
0x00000c21, 0x00000c31, 0x00000c44, 0x00000c63, | |||
0x00000c69, 0x00000c80, 0x00000c91, 0x00000c9f, | |||
0x00000cb0, 0x00000cc4, 0x00000ce9, 0x00000cff, | |||
// Entry 80 - 9F | |||
0x00000d26, 0x00000d3e, 0x00000d59, 0x00000db1, | |||
0x00000db9, 0x00000dbf, 0x00000dd4, 0x00000ddf, | |||
0x00000df9, 0x00000e13, 0x00000e1b, 0x00000e39, | |||
0x00000e52, 0x00000e6d, 0x00000e9c, 0x00000ec8, | |||
0x00000f00, 0x00000f26, 0x00000f37, 0x00000f6d, | |||
0x00000fb4, 0x00000fcc, 0x00000ffe, 0x00001070, | |||
0x0000108a, 0x000010c4, 0x000010e7, 0x000010ff, | |||
0x00001128, 0x00001141, 0x0000119a, 0x000011df, | |||
// Entry A0 - BF | |||
0x000011fa, 0x00001220, 0x00001236, 0x00001259, | |||
0x00001271, 0x000012d0, 0x00001325, 0x0000133e, | |||
0x00001349, 0x0000136d, 0x0000138d, 0x0000139f, | |||
0x000013d8, 0x000013f9, 0x00001419, 0x0000143b, | |||
} // Size: 716 bytes | |||
const enData string = "" + // Size: 5179 bytes | |||
"\x02Error\x02(no argument): elevate and install manager service\x02Usage" + | |||
": %[1]s [\x0a%[2]s]\x02Command Line Options\x02Unable to determine wheth" + | |||
"er the process is running under WOW64: %[1]v\x02You must use the 64-bit " + | |||
"version of WireGuard on this computer.\x02Unable to open current process" + | |||
" token: %[1]v\x02WireGuard may only be used by users who are a member of" + | |||
" the Builtin %[1]s group.\x02WireGuard is running, but the UI is only ac" + | |||
"cessible from desktops of the Builtin %[1]s group.\x02WireGuard system t" + | |||
"ray icon did not appear after 30 seconds.\x02Now\x02System clock wound b" + | |||
"ackward!\x14\x01\x81\x01\x00\x02\x0b\x02%[1]d year\x00\x0c\x02%[1]d year" + | |||
"s\x14\x01\x81\x01\x00\x02\x0a\x02%[1]d day\x00\x0b\x02%[1]d days\x14\x01" + | |||
"\x81\x01\x00\x02\x0b\x02%[1]d hour\x00\x0c\x02%[1]d hours\x14\x01\x81" + | |||
"\x01\x00\x02\x0d\x02%[1]d minute\x00\x0e\x02%[1]d minutes\x14\x01\x81" + | |||
"\x01\x00\x02\x0d\x02%[1]d second\x00\x0e\x02%[1]d seconds\x02%[1]s ago" + | |||
"\x02%[1]d\u00a0B\x02%.2[1]f\u00a0KiB\x02%.2[1]f\u00a0MiB\x02%.2[1]f" + | |||
"\u00a0GiB\x02%.2[1]f\u00a0TiB\x02%[1]s: %[2]q\x02Invalid IP address\x02I" + | |||
"nvalid network prefix length\x02Missing port from endpoint\x02Invalid en" + | |||
"dpoint host\x02Brackets must contain an IPv6 address\x02Invalid MTU\x02I" + | |||
"nvalid port\x02Invalid persistent keepalive\x02Invalid key: %[1]v\x02Key" + | |||
"s must decode to exactly 32 bytes\x02Number must be a number between 0 a" + | |||
"nd 2^64-1: %[1]v\x02Two commas in a row\x02Tunnel name is not valid\x02L" + | |||
"ine must occur in a section\x02Invalid config key is missing an equals s" + | |||
"eparator\x02Key must have a value\x02Invalid key for [Interface] section" + | |||
"\x02Invalid key for [Peer] section\x02An interface must have a private k" + | |||
"ey\x02[none specified]\x02All peers must have public keys\x02Error in ge" + | |||
"tting configuration\x02Invalid key for interface section\x02Protocol ver" + | |||
"sion must be 1\x02Invalid key for peer section\x02, \x02About WireGuard" + | |||
"\x02WireGuard logo image\x02App version: %[1]s\x0aGo backend version: %[" + | |||
"2]s\x0aGo version: %[3]s\x0aOperating system: %[4]s\x0aArchitecture: %[5" + | |||
"]s\x02Close\x02♥ &Donate!\x02Status:\x02&Deactivate\x02&Activate\x02Publ" + | |||
"ic key:\x02Listen port:\x02MTU:\x02Addresses:\x02DNS servers:\x02Preshar" + | |||
"ed key:\x02Allowed IPs:\x02Endpoint:\x02Persistent keepalive:\x02Latest " + | |||
"handshake:\x02Transfer:\x02enabled\x02%[1]s received, %[2]s sent\x02Fail" + | |||
"ed to determine tunnel state\x02Failed to activate tunnel\x02Failed to d" + | |||
"eactivate tunnel\x02Interface: %[1]s\x02Peer\x02Create new tunnel\x02Edi" + | |||
"t tunnel\x02&Name:\x02&Public key:\x02(unknown)\x02&Block untunneled tra" + | |||
"ffic (kill-switch)\x02When a configuration has exactly one peer, and tha" + | |||
"t peer has an allowed IPs containing at least one of 0.0.0.0/0 or ::/0, " + | |||
"then the tunnel service engages a firewall ruleset to block all traffic " + | |||
"that is neither to nor from the tunnel interface, with special exception" + | |||
"s for DHCP and NDP.\x02&Save\x02Cancel\x02&Configuration:\x02Invalid nam" + | |||
"e\x02A name is required.\x02Tunnel name ‘%[1]s’ is invalid.\x02Unable to" + | |||
" list existing tunnels\x02Tunnel already exists\x02Another tunnel alread" + | |||
"y exists with the name ‘%[1]s’.\x02Unable to create new configuration" + | |||
"\x02Writing file failed\x02File ‘%[1]s’ already exists.\x0a\x0aDo you wa" + | |||
"nt to overwrite it?\x02Active\x02Activating\x02Inactive\x02Deactivating" + | |||
"\x02Unknown state\x02Log\x02&Copy\x02Select &all\x02&Save to file…\x02Ti" + | |||
"me\x02Log message\x02Text Files (*.txt)|*.txt|All Files (*.*)|*.*\x02Exp" + | |||
"ort log to file\x02&About WireGuard…\x02Tunnel Error\x02%[1]s\x0a\x0aPle" + | |||
"ase consult the log for more information.\x02%[1]s (out of date)\x02Wire" + | |||
"Guard Detection Error\x02Unable to wait for WireGuard window to appear: " + | |||
"%[1]v\x02WireGuard: Deactivated\x02Status: Unknown\x02Addresses: None" + | |||
"\x02&Manage tunnels…\x02&Import tunnel(s) from file…\x02E&xit\x02WireGua" + | |||
"rd Tunnel Error\x02WireGuard: %[1]s\x02Status: %[1]s\x02Addresses: %[1]s" + | |||
"\x02WireGuard Activated\x02The %[1]s tunnel has been activated.\x02WireG" + | |||
"uard Deactivated\x02The %[1]s tunnel has been deactivated.\x02An Update " + | |||
"is Available!\x02WireGuard Update Available\x02An update to WireGuard is" + | |||
" now available. You are advised to update as soon as possible.\x02Tunnel" + | |||
"s\x02&Edit\x02Add &empty tunnel…\x02Add Tunnel\x02Remove selected tunnel" + | |||
"(s)\x02Export all tunnels to zip\x02&Toggle\x02Export all tunnels to &zi" + | |||
"p…\x02Edit &selected tunnel…\x02&Remove selected tunnel(s)\x02Could not " + | |||
"import selected configuration: %[1]v\x02Could not enumerate existing tun" + | |||
"nels: %[1]v\x02Another tunnel already exists with the name ‘%[1]s’\x02Un" + | |||
"able to import configuration: %[1]v\x02Imported tunnels\x14\x01\x81\x01" + | |||
"\x00\x02\x16\x02Imported %[1]d tunnel\x00\x17\x02Imported %[1]d tunnels" + | |||
"\x14\x02\x80\x01\x02\x1f\x02Imported %[1]d of %[2]d tunnel\x00 \x02Impor" + | |||
"ted %[1]d of %[2]d tunnels\x02Unable to create tunnel\x14\x01\x81\x01" + | |||
"\x00\x02\x14\x02Delete %[1]d tunnel\x00\x15\x02Delete %[1]d tunnels\x14" + | |||
"\x01\x81\x01\x00\x024\x02Are you sure you would like to delete %[1]d tun" + | |||
"nel?\x005\x02Are you sure you would like to delete %[1]d tunnels?\x02Del" + | |||
"ete tunnel ‘%[1]s’\x02Are you sure you would like to delete tunnel ‘%[1]" + | |||
"s’?\x02%[1]s You cannot undo this action.\x02Unable to delete tunnel\x02" + | |||
"A tunnel was unable to be removed: %[1]s\x02Unable to delete tunnels\x14" + | |||
"\x01\x81\x01\x00\x02'\x02%[1]d tunnel was unable to be removed.\x00)\x02" + | |||
"%[1]d tunnels were unable to be removed.\x02Configuration Files (*.zip, " + | |||
"*.conf)|*.zip;*.conf|All Files (*.*)|*.*\x02Import tunnel(s) from file" + | |||
"\x02Configuration ZIP Files (*.zip)|*.zip\x02Export tunnels to zip\x02%[" + | |||
"1]s (unsigned build, no updates)\x02Error Exiting WireGuard\x02Unable to" + | |||
" exit service due to: %[1]v. You may want to stop WireGuard from the ser" + | |||
"vice manager.\x02An update to WireGuard is available. It is highly advis" + | |||
"able to update without delay.\x02Status: Waiting for user\x02Update Now" + | |||
"\x02Status: Waiting for updater service\x02Error: %[1]v. Please try agai" + | |||
"n.\x02Status: Complete!\x02http2: Framer %[1]p: failed to decode just-wr" + | |||
"itten frame\x02http2: Framer %[1]p: wrote %[2]v\x02http2: Framer %[1]p: " + | |||
"read %[2]v\x02http2: decoded hpack field %+[1]v" | |||
// Total table size 5895 bytes (5KiB); checksum: ED8BBF53 |