diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | client/client.go | 173 | ||||
| -rw-r--r-- | client/client_test.go | 2 | ||||
| -rw-r--r-- | client/locales/da/messages.gotext.json | 392 | ||||
| -rw-r--r-- | client/locales/de/messages.gotext.json | 392 | ||||
| -rw-r--r-- | client/locales/en/messages.gotext.json | 466 | ||||
| -rw-r--r-- | client/locales/es/messages.gotext.json | 392 | ||||
| -rw-r--r-- | client/locales/fr/messages.gotext.json | 392 | ||||
| -rw-r--r-- | client/locales/it/messages.gotext.json | 392 | ||||
| -rw-r--r-- | client/locales/nl/messages.gotext.json | 392 | ||||
| -rw-r--r-- | client/locales/uk/messages.gotext.json | 392 | ||||
| -rw-r--r-- | client/zgotext.go | 272 | ||||
| -rw-r--r-- | exports/exports.go | 33 | ||||
| -rw-r--r-- | go.mod | 15 | ||||
| -rw-r--r-- | go.sum | 7 | ||||
| -rw-r--r-- | i18nerr/i18nerr.go | 145 | ||||
| -rw-r--r-- | internal/server/server.go | 10 | ||||
| -rw-r--r-- | types/error/error.go | 24 | ||||
| -rw-r--r-- | wrappers/python/eduvpn_common/main.py | 18 |
19 files changed, 3796 insertions, 116 deletions
@@ -2,3 +2,6 @@ *.iml .idea/ .DS_Store + +# locales +/client/locales/*/out.gotext.json diff --git a/client/client.go b/client/client.go index 124d9b5..3c18027 100644 --- a/client/client.go +++ b/client/client.go @@ -1,13 +1,15 @@ +//go:generate go run golang.org/x/text/cmd/gotext -srclang=en update -out=zgotext.go -lang=da,de,en,es,fr,it,nl,ukr + // Package client implements the public interface for creating eduVPN/Let's Connect! clients package client import ( "context" - "fmt" "strings" "sync" "time" + "github.com/eduvpn/eduvpn-common/i18nerr" "github.com/eduvpn/eduvpn-common/internal/config" "github.com/eduvpn/eduvpn-common/internal/discovery" "github.com/eduvpn/eduvpn-common/internal/failover" @@ -73,15 +75,6 @@ func userAgentName(clientID string) string { } } -func (c *Client) logError(err error) { - // Logs the error with the same level/verbosity as the error - if c.Debug { - log.Logger.Inherit(err, fmt.Sprintf("\nwith stacktrace: %s\n", err.(*errors.Error).ErrorStack())) - } else { - log.Logger.Inherit(err, "") - } -} - func (c *Client) isLetsConnect() bool { // see https://git.sr.ht/~fkooman/vpn-user-portal/tree/v3/item/src/OAuth/VpnClientDb.php return strings.HasPrefix(c.Name, "org.letsconnect-vpn.app") @@ -121,7 +114,7 @@ type Client struct { func (c *Client) updateTokens(srv server.Server) error { if c.TokenGetter == nil { - return errors.New("no tokken getter defined") + return errors.New("no token getter defined") } pSrv, err := c.pubCurrentServer(srv) if err != nil { @@ -168,10 +161,10 @@ func (c *Client) forwardTokens(srv server.Server) error { func (c *Client) goTransition(id fsm.StateID) error { handled, err := c.FSM.GoTransition(id) if err != nil { - return err + return i18nerr.Wrapf(err, "Internal state transition error") } if !handled { - log.Logger.Debugf("transition not handled by the client: %s", GetStateName(id)) + log.Logger.Debugf("transition not handled by the client to internal state: '%s'", GetStateName(id)) } return nil } @@ -188,11 +181,11 @@ func New(name string, version string, directory string, stateCallback func(FSMSt c = &Client{} if !isAllowedClientID(name) { - return nil, errors.Errorf("client ID is not allowed: '%v', see https://git.sr.ht/~fkooman/vpn-user-portal/tree/v3/item/src/OAuth/VpnClientDb.php for a list of allowed IDs", name) + return nil, i18nerr.Newf("The client registered with an invalid client ID: '%v'", name) } if len([]rune(version)) > 20 { - return nil, errors.Errorf("version is not allowed: '%s', must be max 20 characters", version) + return nil, i18nerr.Newf("The client registered with an invalid version: '%v'", version) } // Initialize the logger @@ -202,7 +195,7 @@ func New(name string, version string, directory string, stateCallback func(FSMSt } if err = log.Logger.Init(lvl, directory); err != nil { - return nil, err + return nil, i18nerr.Wrapf(err, "The log file with directory: '%s' failed to initialize", directory) } // set client name @@ -235,11 +228,11 @@ func New(name string, version string, directory string, stateCallback func(FSMSt // Registering means updating the FSM to get to the initial state correctly func (c *Client) Register() error { if !c.FSM.InState(StateDeregistered) { - return errors.Errorf("fsm attempt to register while in '%v'", c.FSM.Current) + return i18nerr.Wrapf(errors.New("The client tried to re-initialize without deregistering first"), "Client has an invalid state") } err := c.goTransition(StateNoServer) if err != nil { - return errors.WrapPrefix(err, "failed to register", 0) + return err } return nil } @@ -253,7 +246,7 @@ func (c *Client) Deregister() { // Save the config if err := c.Config.Save(&c); err != nil { - log.Logger.Infof("c.Config.Save failed: %s\nstacktrace:\n%s", err.Error(), err.(*errors.Error).ErrorStack()) + log.Logger.Infof("failed saving state configuration: '%v'", err) } // Empty out the state @@ -265,15 +258,9 @@ func (c *Client) Deregister() { // If this is the case then a previous version of the list is returned if there is any. // This takes into account the frequency of updates, see: https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md#organization-list. func (c *Client) DiscoOrganizations(ck *cookie.Cookie) (orgs *discotypes.Organizations, err error) { - defer func() { - if err != nil { - c.logError(err) - } - }() - // Not supported with Let's Connect! if c.isLetsConnect() { - return nil, errors.Errorf("discovery with Let's Connect is not supported") + return nil, i18nerr.Newf("Server/organization discovery with Let's Connect is not supported") } // Mark organizations as expired if we have not set an organization yet @@ -281,8 +268,11 @@ func (c *Client) DiscoOrganizations(ck *cookie.Cookie) (orgs *discotypes.Organiz c.Discovery.MarkOrganizationsExpired() } - // TODO: pass a context - return c.Discovery.Organizations(ck.Context()) + orgs, err = c.Discovery.Organizations(ck.Context()) + if err != nil { + err = i18nerr.Wrap(err, "An error occurred after getting the discovery files for the list of organizations") + } + return } // DiscoServers gets the servers list from the discovery server @@ -290,19 +280,16 @@ func (c *Client) DiscoOrganizations(ck *cookie.Cookie) (orgs *discotypes.Organiz // If this is the case then a previous version of the list is returned if there is any. // This takes into account the frequency of updates, see: https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md#server-list. func (c *Client) DiscoServers(ck *cookie.Cookie) (dss *discotypes.Servers, err error) { - defer func() { - if err != nil { - c.logError(err) - } - }() - // Not supported with Let's Connect! if c.isLetsConnect() { - return nil, errors.Errorf("discovery with Let's Connect is not supported") + return nil, i18nerr.Newf("Server/organization discovery with Let's Connect is not supported") } - // TODO: pass a context - return c.Discovery.Servers(ck.Context()) + dss, err = c.Discovery.Servers(ck.Context()) + if err != nil { + err = i18nerr.Wrap(err, "An error occurred after getting the discovery files for the list of servers") + } + return } // ExpiryTimes returns the different Unix timestamps regarding expiry @@ -314,17 +301,15 @@ func (c *Client) ExpiryTimes() (*srvtypes.Expiry, error) { // Get current expiry time srv, err := c.Servers.Current() if err != nil { - c.logError(err) - return nil, err + return nil, i18nerr.Wrap(err, "The current server could not be found when getting it for expiry") } b, err := srv.Base() if err != nil { - c.logError(err) return nil, err } if b.StartTime.IsZero() { - return nil, errors.New("start time is zero, did you get a configuration?") + return nil, i18nerr.New("No start time is defined for this server") } bT := b.RenewButtonTime() @@ -387,7 +372,7 @@ func (c *Client) callbacks(ck *cookie.Cookie, srv server.Server, forceauth bool) if srv.NeedsLocation() { err := c.locationCallback(ck) if err != nil { - return err + return i18nerr.Wrap(err, "The secure internet location could not be set") } } @@ -412,7 +397,7 @@ func (c *Client) callbacks(ck *cookie.Cookie, srv server.Server, forceauth bool) } err := c.loginCallback(ck, srv) if err != nil { - return err + return i18nerr.Wrap(err, "The authorization procedure failed to complete") } } err = c.goTransition(StateAuthorized) @@ -426,19 +411,18 @@ func (c *Client) callbacks(ck *cookie.Cookie, srv server.Server, forceauth bool) func (c *Client) profileCallback(ck *cookie.Cookie, srv server.Server) error { vp, err := server.HasValidProfile(ck.Context(), srv, c.SupportsWireguard) if err != nil { - return err + log.Logger.Warningf("failed to determine whether the current protocol is valid with error: %v", err) } if !vp { - b, err := srv.Base() + vps, err := server.ValidProfiles(srv, c.SupportsWireguard) if err != nil { - return err + return i18nerr.Wrapf(err, "No suitable profiles could be found") } - ps := b.Profiles.Public() errChan := make(chan error) go func() { err := c.FSM.GoTransitionRequired(StateAskProfile, &srvtypes.RequiredAskTransition{ C: ck, - Data: ps, + Data: vps.Public(), }) if err != nil { errChan <- err @@ -446,11 +430,11 @@ func (c *Client) profileCallback(ck *cookie.Cookie, srv server.Server) error { }() pID, err := ck.Receive(errChan) if err != nil { - return err + return i18nerr.Wrapf(err, "Profile with ID: '%s' could not be set", pID) } err = server.Profile(srv, pID) if err != nil { - return err + return i18nerr.Wrapf(err, "Profile with ID: '%s' could not be obtained from the server", pID) } } err = c.goTransition(StateChosenProfile) @@ -473,13 +457,13 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes. } // If we must run callbacks, go to the previous state if we're not in it if !ni && !c.FSM.InState(previousState) { - c.FSM.GoTransition(previousState) + c.FSM.GoTransition(previousState) //nolint:errcheck } }() if !ni { - // This only returns a boolean if it was handled err = c.goTransition(StateLoadingServer) + // this is already wrapped in an UI error if err != nil { return err } @@ -488,7 +472,7 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes. if _type != srvtypes.TypeSecureInternet { identifier, err = http.EnsureValidURL(identifier, true) if err != nil { - return err + return i18nerr.Wrapf(err, "The identifier that was passed to the library is incorrect") } } @@ -498,11 +482,11 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes. case srvtypes.TypeInstituteAccess: dSrv, err := c.Discovery.ServerByURL(identifier, "institute_access") if err != nil { - return err + return i18nerr.Wrapf(err, "Could not retrieve institute access server with URL: '%s' from discovery", identifier) } srv, err = c.Servers.AddInstituteAccess(ck.Context(), c.Name ,dSrv) if err != nil { - return err + return i18nerr.Wrapf(err, "The institute access server with URL: '%s' could not be added", identifier) } case srvtypes.TypeSecureInternet: dOrg, dSrv, err := c.Discovery.SecureHomeArgs(identifier) @@ -511,19 +495,19 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes. // Note that in the docs it states that it only should happen when the Org ID doesn't exist // However, this is nice as well because it also catches the error where the SecureInternetHome server is not found c.Discovery.MarkOrganizationsExpired() - return err + return i18nerr.Wrapf(err, "The secure internet server with organisation ID: '%s' could not be retrieved from discovery", identifier) } srv, err = c.Servers.AddSecureInternet(ck.Context(), c.Name, dOrg, dSrv) if err != nil { - return err + return i18nerr.Wrapf(err, "The secure internet server with organisation ID: '%s' could not be added", identifier) } case srvtypes.TypeCustom: srv, err = c.Servers.AddCustom(ck.Context(), c.Name, identifier) if err != nil { - return err + return i18nerr.Wrapf(err, "The custom server with URL: '%s' could not be added", identifier) } default: - return errors.Errorf("not a valid server type: %v", _type) + return i18nerr.Newf("Server type: '%v' is not valid to be added", _type) } // if we are non interactive, we run no callbacks @@ -533,6 +517,7 @@ func (c *Client) AddServer(ck *cookie.Cookie, identifier string, _type srvtypes. // callbacks err = c.callbacks(ck, srv, false) + // error is already UI wrapped if err != nil { return err } @@ -562,16 +547,13 @@ func (c *Client) config(ck *cookie.Cookie, srv server.Server, pTCP bool, forceAu cfgS, err := server.Config(ck.Context(), srv, c.SupportsWireguard, pTCP) if err != nil { - return nil, err + return nil, i18nerr.Wrap(err, "The VPN configuration could not be obtained") } p, err := server.CurrentProfile(srv) if err != nil { - return nil, err + return nil, i18nerr.Wrap(err, "The current profile could not be found") } pcfg := cfgS.Public(p.DefaultGateway) - if err != nil { - return nil, err - } return &pcfg, nil } @@ -587,7 +569,7 @@ func (c *Client) server(identifier string, _type srvtypes.Type) (srv server.Serv srv, err = c.Servers.CustomServer(identifier) setter = c.Servers.SetCustom default: - return nil, nil, errors.Errorf("not a valid server type: %v", _type) + return nil, nil, i18nerr.Newf("Not a valid server type: %v", _type) } return srv, setter, err } @@ -599,18 +581,16 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes. previousState := c.FSM.Current defer func() { if err == nil { - c.FSM.GoTransition(StateGotConfig) - } else { - if !c.FSM.InState(previousState) { - // go back to the previous state if an error occurred - c.FSM.GoTransition(previousState) - } + c.FSM.GoTransition(StateGotConfig) //nolint:errcheck + } else if !c.FSM.InState(previousState) { + // go back to the previous state if an error occurred + c.FSM.GoTransition(previousState) //nolint:errcheck } }() if _type != srvtypes.TypeSecureInternet { identifier, err = http.EnsureValidURL(identifier, true) if err != nil { - return nil, err + return nil, i18nerr.Wrapf(err, "Identifier: '%s' for server with type: '%d' is not valid", identifier, _type) } } err = c.goTransition(StateLoadingServer) @@ -625,10 +605,9 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes. err = server.RefreshEndpoints(ck.Context(), srv) // If we get a canceled error, return that, otherwise just log the error - cErr := context.Canceled if err != nil { - if errors.As(err, &cErr) { - return nil, err + if errors.Is(err, context.Canceled) { + return nil, i18nerr.Wrap(err, "The operation for getting a VPN configuration was canceled") } log.Logger.Warningf("failed to refresh server endpoints: %v", err) @@ -638,6 +617,7 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes. cfg, err = c.config(ck, srv, pTCP, false) tErr := &oauth.TokensInvalidError{} if err != nil && errors.As(err, &tErr) { + log.Logger.Debugf("the tokens were invalid, trying again...") cfg, err = c.config(ck, srv, pTCP, true) } @@ -656,7 +636,7 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes. // set the current server if err = set(srv); err != nil { - return nil, err + return nil, i18nerr.Wrapf(err, "Failed to set the server with identifier: '%s' as the current", identifier) } return cfg, nil @@ -666,19 +646,25 @@ func (c *Client) RemoveServer(identifier string, _type srvtypes.Type) (err error if _type != srvtypes.TypeSecureInternet { identifier, err = http.EnsureValidURL(identifier, true) if err != nil { - return err + return i18nerr.Wrapf(err, "Identifier: '%s' for server with type: '%d' is not valid for removal", identifier, _type) } } + // miscellaneous error + var mErr error switch _type { case srvtypes.TypeInstituteAccess: - return c.Servers.RemoveInstituteAccess(identifier) + mErr = c.Servers.RemoveInstituteAccess(identifier) case srvtypes.TypeSecureInternet: - return c.Servers.RemoveSecureInternet(identifier) + mErr = c.Servers.RemoveSecureInternet(identifier) case srvtypes.TypeCustom: - return c.Servers.RemoveCustom(identifier) + mErr = c.Servers.RemoveCustom(identifier) default: - return errors.Errorf("not a valid server type: %v", _type) + return i18nerr.Newf("Not a valid server type: %v", _type) + } + if mErr != nil { + log.Logger.Debugf("failed to remove server with identifier: '%s' and type: '%d', error: %v", identifier, _type, mErr) } + return nil } func (c *Client) CurrentServer() (*srvtypes.Current, error) { @@ -759,7 +745,7 @@ func (c *Client) pubServer(srv server.Server) (interface{}, error) { func (c *Client) ServerList() (*srvtypes.List, error) { if c.FSM.InState(StateDeregistered) { - return nil, errors.New("client is not registered") + return nil, i18nerr.New("Client is not registered") } var customServers []srvtypes.Server for _, v := range c.Servers.CustomServers.Map { @@ -821,7 +807,7 @@ func (c *Client) Cleanup(ck *cookie.Cookie) (err error) { // get the current server srv, err := c.Servers.Current() if err != nil { - return err + return i18nerr.Wrap(err, "Failed to get the current server to cleanup the connection") } err = c.updateTokens(srv) if err != nil { @@ -829,7 +815,7 @@ func (c *Client) Cleanup(ck *cookie.Cookie) (err error) { } err = server.Disconnect(ck.Context(), srv) if err != nil { - return err + return i18nerr.Wrap(err, "Failed to cleanup the VPN connection for the current server") } err = c.forwardTokens(srv) if err != nil { @@ -840,11 +826,11 @@ func (c *Client) Cleanup(ck *cookie.Cookie) (err error) { func (c *Client) SetSecureLocation(ck *cookie.Cookie, countryCode string) (err error) { if c.isLetsConnect() { - return errors.Errorf("setting a secure internet location with Let's Connect! is not supported") + return i18nerr.Newf("Setting a secure internet location with Let's Connect! is not supported") } if !c.Servers.HasSecureInternet() { - return errors.Errorf("no secure internet server available to set a location for") + return i18nerr.Newf("No secure internet server available to set a location for") } dSrv, err := c.Discovery.ServerByCountryCode(countryCode) @@ -860,13 +846,13 @@ func (c *Client) RenewSession(ck *cookie.Cookie) (err error) { defer c.mu.Unlock() srv, err := c.Servers.Current() if err != nil { - return err + return i18nerr.Wrap(err, "Failed to get current server for renewing the session") } // The server has not been chosen yet, this means that we want to manually renew // TODO: is this needed? if !c.FSM.InState(StateChosenServer) { - c.FSM.GoTransition(StateLoadingServer) - c.FSM.GoTransition(StateChosenServer) + c.FSM.GoTransition(StateLoadingServer) //nolint:errcheck + c.FSM.GoTransition(StateChosenServer) //nolint:errcheck } // update tokens in the end defer func() { @@ -884,12 +870,17 @@ func (c *Client) RenewSession(ck *cookie.Cookie) (err error) { func (c *Client) StartFailover(ck *cookie.Cookie, gateway string, mtu int, readRxBytes func() (int64, error)) (bool, error) { f := failover.New(readRxBytes) - return f.Start(ck.Context(), gateway, mtu) + d, err := f.Start(ck.Context(), gateway, mtu) + if err != nil { + return d, i18nerr.Wrapf(err, "Failover failed to complete with gateway: '%s' and mtu: '%d'", gateway, mtu) + } + return d, nil } func (c *Client) SetState(state FSMStateID) error { c.mu.Lock() defer c.mu.Unlock() + curr := c.FSM.Current _, err := c.FSM.GoTransition(state) if err != nil { // self-transitions are only debug errors @@ -897,7 +888,7 @@ func (c *Client) SetState(state FSMStateID) error { log.Logger.Debugf("attempt an invalid self-transition: %s", c.FSM.GetStateName(state)) return nil } - return err + return i18nerr.Wrapf(err, "Failed internal state transition requested by the client from: '%s' to '%s'", GetStateName(curr), GetStateName(state)) } return nil } diff --git a/client/client_test.go b/client/client_test.go index 7077ce4..e60fd46 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -462,7 +462,7 @@ func TestInvalidClientID(t *testing.T) { if err == nil { t.Fatalf("expected invalid register with clientID: %v, but got no error", k) } - if !strings.HasPrefix(err.Error(), "client ID is not allowed") { + if !strings.HasPrefix(err.Error(), "The client registered with an invalid client ID") { t.Fatalf("register error has invalid prefix: %v", err.Error()) } } diff --git a/client/locales/da/messages.gotext.json b/client/locales/da/messages.gotext.json new file mode 100644 index 0000000..1e788ae --- /dev/null +++ b/client/locales/da/messages.gotext.json @@ -0,0 +1,392 @@ +{ + "language": "da", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "" + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ] + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ] + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ] + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "" + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "" + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "" + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "" + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "" + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "" + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "" + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "" + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "" + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "" + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "" + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "" + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "" + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "" + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "" + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ] + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ] + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "" + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "" + } + ] +}
\ No newline at end of file diff --git a/client/locales/de/messages.gotext.json b/client/locales/de/messages.gotext.json new file mode 100644 index 0000000..68119d3 --- /dev/null +++ b/client/locales/de/messages.gotext.json @@ -0,0 +1,392 @@ +{ + "language": "de", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "" + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ] + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ] + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ] + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "" + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "" + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "" + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "" + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "" + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "" + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "" + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "" + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "" + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "" + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "" + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "" + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "" + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "" + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "" + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ] + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ] + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "" + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "" + } + ] +}
\ No newline at end of file diff --git a/client/locales/en/messages.gotext.json b/client/locales/en/messages.gotext.json new file mode 100644 index 0000000..15502ba --- /dev/null +++ b/client/locales/en/messages.gotext.json @@ -0,0 +1,466 @@ +{ + "language": "en", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "Internal state transition error", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "The client registered with an invalid client ID: '{Name}'", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ], + "fuzzy": true + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "The client registered with an invalid version: '{Version}'", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ], + "fuzzy": true + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "The log file with directory: '{Directory}' failed to initialize", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ], + "fuzzy": true + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "Client has an invalid state", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "Server/organization discovery with Let's Connect is not supported", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "An error occurred after getting the discovery files for the list of organizations", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "An error occurred after getting the discovery files for the list of servers", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "The current server could not be found when getting it for expiry", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "The secure internet location could not be set", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "The authorization procedure failed to complete", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "No suitable profiles could be found", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "Profile with ID: '{PID}' could not be set", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ], + "fuzzy": true + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "Profile with ID: '{PID}' could not be obtained from the server", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ], + "fuzzy": true + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "The identifier that was passed to the library is incorrect", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ], + "fuzzy": true + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "The institute access server with URL: '{Identifier}' could not be added", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ], + "fuzzy": true + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ], + "fuzzy": true + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ], + "fuzzy": true + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "The custom server with URL: '{Identifier}' could not be added", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ], + "fuzzy": true + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "Server type: '{Type}' is not valid to be added", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ], + "fuzzy": true + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "The VPN configuration could not be obtained", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "The current profile could not be found", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "Not a valid server type: {Type}", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ], + "fuzzy": true + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ], + "fuzzy": true + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "The operation for getting a VPN configuration was canceled", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "Failed to set the server with identifier: '{Identifier}' as the current", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ], + "fuzzy": true + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ], + "fuzzy": true + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "Failed to get the current server to cleanup the connection", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "Failed to cleanup the VPN connection for the current server", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "Setting a secure internet location with Let's Connect! is not supported", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "No secure internet server available to set a location for", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "Failed to get current server for renewing the session", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ], + "fuzzy": true + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ], + "fuzzy": true + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "timeout reached", + "translatorComment": "Copied from source.", + "fuzzy": true + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "with cause:", + "translatorComment": "Copied from source.", + "fuzzy": true + } + ] +}
\ No newline at end of file diff --git a/client/locales/es/messages.gotext.json b/client/locales/es/messages.gotext.json new file mode 100644 index 0000000..6c93919 --- /dev/null +++ b/client/locales/es/messages.gotext.json @@ -0,0 +1,392 @@ +{ + "language": "es", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "" + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ] + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ] + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ] + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "" + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "" + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "" + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "" + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "" + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "" + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "" + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "" + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "" + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "" + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "" + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "" + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "" + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "" + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "" + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ] + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ] + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "" + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "" + } + ] +}
\ No newline at end of file diff --git a/client/locales/fr/messages.gotext.json b/client/locales/fr/messages.gotext.json new file mode 100644 index 0000000..0e793f1 --- /dev/null +++ b/client/locales/fr/messages.gotext.json @@ -0,0 +1,392 @@ +{ + "language": "fr", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "" + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ] + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ] + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ] + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "" + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "" + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "" + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "" + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "" + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "" + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "" + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "" + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "" + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "" + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "" + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "" + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "" + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "" + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "" + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ] + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ] + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "" + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "" + } + ] +}
\ No newline at end of file diff --git a/client/locales/it/messages.gotext.json b/client/locales/it/messages.gotext.json new file mode 100644 index 0000000..ca5bf05 --- /dev/null +++ b/client/locales/it/messages.gotext.json @@ -0,0 +1,392 @@ +{ + "language": "it", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "" + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ] + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ] + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ] + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "" + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "" + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "" + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "" + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "" + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "" + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "" + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "" + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "" + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "" + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "" + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "" + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "" + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "" + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "" + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ] + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ] + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "" + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "" + } + ] +}
\ No newline at end of file diff --git a/client/locales/nl/messages.gotext.json b/client/locales/nl/messages.gotext.json new file mode 100644 index 0000000..58411b9 --- /dev/null +++ b/client/locales/nl/messages.gotext.json @@ -0,0 +1,392 @@ +{ + "language": "nl", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "" + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ] + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ] + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ] + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "" + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "" + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "" + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "" + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "" + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "" + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "" + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "" + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "" + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "" + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "" + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "" + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "" + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "" + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "" + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ] + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ] + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "" + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "" + } + ] +}
\ No newline at end of file diff --git a/client/locales/uk/messages.gotext.json b/client/locales/uk/messages.gotext.json new file mode 100644 index 0000000..55cd77f --- /dev/null +++ b/client/locales/uk/messages.gotext.json @@ -0,0 +1,392 @@ +{ + "language": "uk", + "messages": [ + { + "id": "Internal state transition error", + "message": "Internal state transition error", + "translation": "" + }, + { + "id": "The client registered with an invalid client ID: '{Name}'", + "message": "The client registered with an invalid client ID: '{Name}'", + "translation": "", + "placeholders": [ + { + "id": "Name", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "name" + } + ] + }, + { + "id": "The client registered with an invalid version: '{Version}'", + "message": "The client registered with an invalid version: '{Version}'", + "translation": "", + "placeholders": [ + { + "id": "Version", + "string": "%[1]v", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "version" + } + ] + }, + { + "id": "The log file with directory: '{Directory}' failed to initialize", + "message": "The log file with directory: '{Directory}' failed to initialize", + "translation": "", + "placeholders": [ + { + "id": "Directory", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "directory" + } + ] + }, + { + "id": "Client has an invalid state", + "message": "Client has an invalid state", + "translation": "" + }, + { + "id": "Server/organization discovery with Let's Connect is not supported", + "message": "Server/organization discovery with Let's Connect is not supported", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of organizations", + "message": "An error occurred after getting the discovery files for the list of organizations", + "translation": "" + }, + { + "id": "An error occurred after getting the discovery files for the list of servers", + "message": "An error occurred after getting the discovery files for the list of servers", + "translation": "" + }, + { + "id": "The current server could not be found when getting it for expiry", + "message": "The current server could not be found when getting it for expiry", + "translation": "" + }, + { + "id": "The secure internet location could not be set", + "message": "The secure internet location could not be set", + "translation": "" + }, + { + "id": "The authorization procedure failed to complete", + "message": "The authorization procedure failed to complete", + "translation": "" + }, + { + "id": "No suitable profiles could be found", + "message": "No suitable profiles could be found", + "translation": "" + }, + { + "id": "Profile with ID: '{PID}' could not be set", + "message": "Profile with ID: '{PID}' could not be set", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "Profile with ID: '{PID}' could not be obtained from the server", + "message": "Profile with ID: '{PID}' could not be obtained from the server", + "translation": "", + "placeholders": [ + { + "id": "PID", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "pID" + } + ] + }, + { + "id": "The identifier that was passed to the library is incorrect", + "message": "The identifier that was passed to the library is incorrect", + "translation": "" + }, + { + "id": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "message": "Could not retrieve institute access server with URL: '{Identifier}' from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The institute access server with URL: '{Identifier}' could not be added", + "message": "The institute access server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be retrieved from discovery", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "message": "The secure internet server with organisation ID: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "The custom server with URL: '{Identifier}' could not be added", + "message": "The custom server with URL: '{Identifier}' could not be added", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Server type: '{Type}' is not valid to be added", + "message": "Server type: '{Type}' is not valid to be added", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "The VPN configuration could not be obtained", + "message": "The VPN configuration could not be obtained", + "translation": "" + }, + { + "id": "The current profile could not be found", + "message": "The current profile could not be found", + "translation": "" + }, + { + "id": "Not a valid server type: {Type}", + "message": "Not a valid server type: {Type}", + "translation": "", + "placeholders": [ + { + "id": "Type", + "string": "%[1]v", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 1, + "expr": "_type" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "The operation for getting a VPN configuration was canceled", + "message": "The operation for getting a VPN configuration was canceled", + "translation": "" + }, + { + "id": "Failed to set the server with identifier: '{Identifier}' as the current", + "message": "Failed to set the server with identifier: '{Identifier}' as the current", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + } + ] + }, + { + "id": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "message": "Identifier: '{Identifier}' for server with type: '{Type}' is not valid for removal", + "translation": "", + "placeholders": [ + { + "id": "Identifier", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "identifier" + }, + { + "id": "Type", + "string": "%[2]d", + "type": "github.com/eduvpn/eduvpn-common/types/server.Type", + "underlyingType": "int8", + "argNum": 2, + "expr": "_type" + } + ] + }, + { + "id": "Failed to get the current server to cleanup the connection", + "message": "Failed to get the current server to cleanup the connection", + "translation": "" + }, + { + "id": "Failed to cleanup the VPN connection for the current server", + "message": "Failed to cleanup the VPN connection for the current server", + "translation": "" + }, + { + "id": "Setting a secure internet location with Let's Connect! is not supported", + "message": "Setting a secure internet location with Let's Connect! is not supported", + "translation": "" + }, + { + "id": "No secure internet server available to set a location for", + "message": "No secure internet server available to set a location for", + "translation": "" + }, + { + "id": "Failed to get current server for renewing the session", + "message": "Failed to get current server for renewing the session", + "translation": "" + }, + { + "id": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "message": "Failover failed to complete with gateway: '{Gateway}' and mtu: '{Mtu}'", + "translation": "", + "placeholders": [ + { + "id": "Gateway", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "gateway" + }, + { + "id": "Mtu", + "string": "%[2]d", + "type": "int", + "underlyingType": "int", + "argNum": 2, + "expr": "mtu" + } + ] + }, + { + "id": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "message": "Failed internal state transition requested by the client from: '{StateNamecurr}' to '{StateNamestate}'", + "translation": "", + "placeholders": [ + { + "id": "StateNamecurr", + "string": "%[1]s", + "type": "string", + "underlyingType": "string", + "argNum": 1, + "expr": "GetStateName(curr)" + }, + { + "id": "StateNamestate", + "string": "%[2]s", + "type": "string", + "underlyingType": "string", + "argNum": 2, + "expr": "GetStateName(state)" + } + ] + }, + { + "id": "timeout reached", + "message": "timeout reached", + "translation": "" + }, + { + "id": "with cause:", + "message": "with cause:", + "translation": "" + } + ] +}
\ No newline at end of file diff --git a/client/zgotext.go b/client/zgotext.go new file mode 100644 index 0000000..b1720a4 --- /dev/null +++ b/client/zgotext.go @@ -0,0 +1,272 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +package client + +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{ + "da": &dictionary{index: daIndex, data: daData}, + "de": &dictionary{index: deIndex, data: deData}, + "dnk": &dictionary{index: dnkIndex, data: dnkData}, + "en": &dictionary{index: enIndex, data: enData}, + "es": &dictionary{index: esIndex, data: esData}, + "fr": &dictionary{index: frIndex, data: frData}, + "it": &dictionary{index: itIndex, data: itData}, + "nl": &dictionary{index: nlIndex, data: nlData}, + "uk": &dictionary{index: ukIndex, data: ukData}, + } + 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{ + "An error occurred after getting the discovery files for the list of organizations": 6, + "An error occurred after getting the discovery files for the list of servers": 7, + "Client has an invalid state": 4, + "Could not retrieve institute access server with URL: '%s' from discovery": 15, + "Failed internal state transition requested by the client from: '%s' to '%s'": 34, + "Failed to cleanup the VPN connection for the current server": 29, + "Failed to get current server for renewing the session": 32, + "Failed to get the current server to cleanup the connection": 28, + "Failed to set the server with identifier: '%s' as the current": 26, + "Failover failed to complete with gateway: '%s' and mtu: '%d'": 33, + "Identifier: '%s' for server with type: '%d' is not valid": 24, + "Identifier: '%s' for server with type: '%d' is not valid for removal": 27, + "Internal state transition error": 0, + "No secure internet server available to set a location for": 31, + "No suitable profiles could be found": 11, + "Not a valid server type: %v": 23, + "Profile with ID: '%s' could not be obtained from the server": 13, + "Profile with ID: '%s' could not be set": 12, + "Server type: '%v' is not valid to be added": 20, + "Server/organization discovery with Let's Connect is not supported": 5, + "Setting a secure internet location with Let's Connect! is not supported": 30, + "The VPN configuration could not be obtained": 21, + "The authorization procedure failed to complete": 10, + "The client registered with an invalid client ID: '%v'": 1, + "The client registered with an invalid version: '%v'": 2, + "The current profile could not be found": 22, + "The current server could not be found when getting it for expiry": 8, + "The custom server with URL: '%s' could not be added": 19, + "The identifier that was passed to the library is incorrect": 14, + "The institute access server with URL: '%s' could not be added": 16, + "The log file with directory: '%s' failed to initialize": 3, + "The operation for getting a VPN configuration was canceled": 25, + "The secure internet location could not be set": 9, + "The secure internet server with organisation ID: '%s' could not be added": 18, + "The secure internet server with organisation ID: '%s' could not be retrieved from discovery": 17, + "timeout reached": 35, + "with cause:": 36, +} + +var daIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const daData string = "" + +var deIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const deData string = "" + +var dnkIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const dnkData string = "" + +var enIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000020, 0x00000059, 0x00000090, + 0x000000ca, 0x000000e6, 0x00000128, 0x0000017a, + 0x000001c6, 0x00000207, 0x00000235, 0x00000264, + 0x00000288, 0x000002b2, 0x000002f1, 0x0000032c, + 0x00000378, 0x000003b9, 0x00000418, 0x00000464, + 0x0000049b, 0x000004c9, 0x000004f5, 0x0000051c, + 0x0000053b, 0x0000057a, 0x000005b5, 0x000005f6, + 0x00000641, 0x0000067c, 0x000006b8, 0x00000700, + // Entry 20 - 3F + 0x0000073a, 0x00000770, 0x000007b3, 0x00000805, + 0x00000815, 0x00000821, +} // Size: 176 bytes + +const enData string = "" + // Size: 2081 bytes + "\x02Internal state transition error\x02The client registered with an inv" + + "alid client ID: '%[1]v'\x02The client registered with an invalid version" + + ": '%[1]v'\x02The log file with directory: '%[1]s' failed to initialize" + + "\x02Client has an invalid state\x02Server/organization discovery with Le" + + "t's Connect is not supported\x02An error occurred after getting the disc" + + "overy files for the list of organizations\x02An error occurred after get" + + "ting the discovery files for the list of servers\x02The current server c" + + "ould not be found when getting it for expiry\x02The secure internet loca" + + "tion could not be set\x02The authorization procedure failed to complete" + + "\x02No suitable profiles could be found\x02Profile with ID: '%[1]s' coul" + + "d not be set\x02Profile with ID: '%[1]s' could not be obtained from the " + + "server\x02The identifier that was passed to the library is incorrect\x02" + + "Could not retrieve institute access server with URL: '%[1]s' from discov" + + "ery\x02The institute access server with URL: '%[1]s' could not be added" + + "\x02The secure internet server with organisation ID: '%[1]s' could not b" + + "e retrieved from discovery\x02The secure internet server with organisati" + + "on ID: '%[1]s' could not be added\x02The custom server with URL: '%[1]s'" + + " could not be added\x02Server type: '%[1]v' is not valid to be added\x02" + + "The VPN configuration could not be obtained\x02The current profile could" + + " not be found\x02Not a valid server type: %[1]v\x02Identifier: '%[1]s' f" + + "or server with type: '%[2]d' is not valid\x02The operation for getting a" + + " VPN configuration was canceled\x02Failed to set the server with identif" + + "ier: '%[1]s' as the current\x02Identifier: '%[1]s' for server with type:" + + " '%[2]d' is not valid for removal\x02Failed to get the current server to" + + " cleanup the connection\x02Failed to cleanup the VPN connection for the " + + "current server\x02Setting a secure internet location with Let's Connect!" + + " is not supported\x02No secure internet server available to set a locati" + + "on for\x02Failed to get current server for renewing the session\x02Failo" + + "ver failed to complete with gateway: '%[1]s' and mtu: '%[2]d'\x02Failed " + + "internal state transition requested by the client from: '%[1]s' to '%[2]" + + "s'\x02timeout reached\x02with cause:" + +var esIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const esData string = "" + +var frIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const frData string = "" + +var itIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const itData string = "" + +var nlIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const nlData string = "" + +var ukIndex = []uint32{ // 38 elements + // Entry 0 - 1F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + // Entry 20 - 3F + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, +} // Size: 176 bytes + +const ukData string = "" + +// Total table size 3665 bytes (3KiB); checksum: 21098CA7 diff --git a/exports/exports.go b/exports/exports.go index 0e557fa..4a92f1a 100644 --- a/exports/exports.go +++ b/exports/exports.go @@ -1,6 +1,6 @@ // package main implements the main exported API to be used by other languages // Some notes: -// - Errors are returned as c strings, free them using FreeString. Same is the case for other string types, you should also free them +// - Errors are returned as JSON c strings. The JSON type is defined in types/error/error.go Error. Free them using FreeString. Same is the case for other string types, you should also free them // - Types are converted from the Go representation to C using JSON strings // - Cookies are used for cancellation, just fancy contexts. Create a cookie using `CookieNew`, pass it to the function that needs one as the first argument. To cancel the function, call `CookieCancel`, passing in the same cookie as argument // - Cookies must also be freed, by using the CookieDelete function if the cookie is no longer needed @@ -47,9 +47,11 @@ import ( "github.com/go-errors/errors" "github.com/eduvpn/eduvpn-common/client" + "github.com/eduvpn/eduvpn-common/i18nerr" "github.com/eduvpn/eduvpn-common/internal/log" "github.com/eduvpn/eduvpn-common/types/cookie" srvtypes "github.com/eduvpn/eduvpn-common/types/server" + errtypes "github.com/eduvpn/eduvpn-common/types/error" ) var VPNState *client.Client @@ -58,7 +60,22 @@ func getCError(err error) *C.char { if err == nil { return nil } - return C.CString(err.Error()) + retErr := errtypes.Error{ + Message: errtypes.Translated{ + "en": err.Error(), + }, + Misc: false, + } + v, ok := err.(*i18nerr.Error) + if ok { + retErr.Message = v.Translations() + retErr.Misc = v.Misc + } + retData, err := getReturnData(retErr) + if err != nil { + return C.CString("failed to get error return") + } + return C.CString(retData) } func getReturnData(data interface{}) (string, error) { @@ -112,7 +129,7 @@ func getVPNState() (*client.Client, error) { // - Write the state graph to a file in the configDirectory. This can be used to create a FSM png file with mermaid https://mermaid.js.org/ // // After registering, the FSM is initialized and the state transition NO_SERVER should have been completed -// If some error occurs during registering, it is returned as a C string +// If some error occurs during registering, it is returned as a types/error/error.go Error // //export Register func Register( @@ -157,7 +174,7 @@ func Register( // Expiry times are just fields that represent unix timestamps at which to do certain events regarding expiry, // e.g. when to show the renew button, when to show expiry notifications // The expiry times structure is defined in types/server/server.go `Expiry` -// If some error occurs during registering, it is returned as a C string +// If some error occurs, it is returned as types/error/error.go Error // //export ExpiryTimes func ExpiryTimes() (*C.char, *C.char) { @@ -203,7 +220,7 @@ func Deregister() *C.char { // // ni stands for non-interactive. If non-zero, any state transitions will not be run. // This ni flag is useful for preprovisioned servers. For normal usage, you want to set this to zero (meaning: False) -// If the server cannot be added it returns the error as a string +// If the server cannot be added it returns the error as types/error/error.go Error // Note that the server is removed when an error has occured // The following state callbacks are mandatory to handle: // - OAUTH_STARTED: This indicates that the OAuth procedure has been started, it returns the URL as the data. @@ -230,7 +247,7 @@ func AddServer(c C.uintptr_t, _type C.int, id *C.char, ni C.int) *C.char { // - In case of secure internet: The organization ID // - In case of custom server: The base URL // - In case of institute access: The base URL -// If the server cannot be removed it returns the error as a string +// If the server cannot be removed it returns the error types/error/error.go Error // Note that the server is not removed when an error has occured // //export RemoveServer @@ -295,7 +312,7 @@ func ServerList() (*C.char, *C.char) { // - In case of custom server: The base URL // - In case of institute access: The base URL // pTCP is if we prefer TCP or not to get the configuration, non-zero means yes -// If the server cannot be added it returns the error as a string +// If the server cannot be added it returns the error as types/error/error.go Error // Note that the server is removed when an error has occured // The current state callbacks MUST be handled // - ASK_PROFILE: This asks the client for profile. @@ -329,7 +346,7 @@ func ServerList() (*C.char, *C.char) { // // After getting a configuration, the FSM moves to the GOT_CONFIG state // The return data is the configuration, marshalled as JSON and defined in types/server/server.go Configuration -// This is nil if an error is returned as a string +// This is nil if an error is returned as types/error/error.go Error // //export GetConfig func GetConfig(c C.uintptr_t, _type C.int, id *C.char, pTCP C.int) (*C.char, *C.char) { @@ -3,13 +3,20 @@ module github.com/eduvpn/eduvpn-common go 1.18 require ( - github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 + github.com/jedisct1/go-minisign v0.0.0-20230513092556-d96eb068239a + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 + golang.org/x/text v0.9.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 ) require ( + golang.org/x/mod v0.8.0 // indirect + golang.org/x/tools v0.6.0 // indirect +) + +require ( github.com/go-errors/errors v1.4.2 - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/net v0.14.0 - golang.org/x/sys v0.11.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 + golang.org/x/sys v0.8.0 // indirect ) @@ -3,16 +3,15 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/jedisct1/go-minisign v0.0.0-20230211184525-1f273d8dc776 h1:WXhZ7psl6HhDDW58rDWIJE6oB0ETjaQA4U6d8U7lMyg= github.com/jedisct1/go-minisign v0.0.0-20230211184525-1f273d8dc776/go.mod h1:09CTTv5TZgz94QHts03Xnuzy5LmxCE8BNqQRFigO5gA= -github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY= -github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= diff --git a/i18nerr/i18nerr.go b/i18nerr/i18nerr.go new file mode 100644 index 0000000..813d287 --- /dev/null +++ b/i18nerr/i18nerr.go @@ -0,0 +1,145 @@ +// package i18nerr implements errors with internationalization using gotext +package i18nerr + +import ( + "context" + "errors" + "sync" + + "github.com/eduvpn/eduvpn-common/internal/log" + + "golang.org/x/text/language" + "golang.org/x/text/message" +) + +var printers sync.Map +var once sync.Once + + +// TranslatedInner defines errors that are used as inner causes but are still translated because they can happen frequently +func TranslatedInner(inner error) (string, bool) { + unwrapped := inner + for errors.Unwrap(unwrapped) != nil { + unwrapped = errors.Unwrap(unwrapped) + } + + switch { + case errors.Is(inner, context.DeadlineExceeded): + return printerOrNew(language.English).Sprintf("timeout reached"), false + case errors.Is(inner, context.Canceled): + return unwrapped.Error(), true + } + return unwrapped.Error(), false +} + +// Error wraps an actual error with the translation key +// This translation key is later used to lookup translation +// The inner error always consists of the translation key and some formatting +type Error struct { + key message.Reference + args []interface{} + wrapped *Error + Misc bool +} + +func (e *Error) translated(t language.Tag) string { + once.Do(func() { + inititializeLangs() + }) + msg := printerOrNew(t).Sprintf(e.key, e.args) + if e.wrapped != nil { + return msg + " " + printerOrNew(t).Sprintf("with cause:") + " " + e.wrapped.Error() + } + return msg +} + +// Error gets the error string +// it does this by simply forwarding the error method from the actual inner error +func (e *Error) Error() string { + return e.translated(language.English) +} + +// Translations returns all the translations for the error including the source translation (english) +func (e *Error) Translations() map[string]string { + translations := make(map[string]string) + // add the source transltaion first + source := e.Error() + translations[language.English.String()] = source + for _, t := range message.DefaultCatalog.Languages() { + // already added + if t == language.English { + continue + } + // get the final translation string for the tag + // and add it if it's not equal to the english version + f := e.translated(t) + if f != source { + translations[t.String()] = f + } + } + return translations +} + + +// Unwrap returns the unwrapped error +// it does this by unwrapping the inner error +func (e *Error) Unwrap() error { + if e.wrapped == nil { + return nil + } + return e.wrapped.Unwrap() +} + +// printerOrNew gets a message printer from the global printers map using the tag 'tag' +// If the printer cannot be found in the sync map, we return a new printer +func printerOrNew(tag language.Tag) *message.Printer { + v, ok := printers.Load(tag) + if !ok { + log.Logger.Debugf("i18n: could not load printer with tag: '%v' from map", tag) + return message.NewPrinter(tag) + } + p, ok := v.(*message.Printer) + if !ok { + log.Logger.Debugf("i18n: could not load printer with tag: '%v' as the type is not correct: '%T'", tag, p) + return message.NewPrinter(tag) + } + return p +} + +// New creates a new i18n error using a message reference +func New(key message.Reference) *Error { + _ = printerOrNew(language.English).Sprint(key) + return &Error{key: key} +} + +// Newf creates a new i18n error using a message reference and arguments. +// It formats this with fmt.Errorf +func Newf(key message.Reference, args ...interface{}) *Error { + _ = printerOrNew(language.English).Sprintf(key, args...) + return &Error{key: key, args: args} +} + +// Wrap creates a new i18n error using an error to be wrapped 'err' and a prefix message reference 'key'. +// It formats this with fmt.Errorf +func Wrap(err error, key message.Reference) *Error { + _ = printerOrNew(language.English).Sprintf(key) + t, misc := TranslatedInner(err) + return &Error{key: key, wrapped: &Error{key: t, Misc: misc}, Misc: misc} +} + +// Wrapf creates a new i18n error using an error to be wrapped 'err' and a prefix message reference 'key' with format arguments 'args'. +// It formats this with fmt.Errorf +func Wrapf(err error, key message.Reference, args ...interface{}) *Error { + _ = printerOrNew(language.English).Sprintf(key, args...) + t, misc := TranslatedInner(err) + return &Error{key: key, args: args, wrapped: &Error{key: t, Misc: misc}, Misc: misc} +} + +// initializeLangs initializes the printers from the default catalog into the sync map +// we cannot do this in init() because this is too early +func inititializeLangs() { + log.Logger.Debugf("i18n: initializing languages...") + for _, t := range message.DefaultCatalog.Languages() { + printers.Store(t, message.NewPrinter(t)) + } +} diff --git a/internal/server/server.go b/internal/server/server.go index c34158a..45d6ccd 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -77,7 +77,7 @@ func CurrentProfile(srv Server) (*profile.Profile, error) { return nil, errors.Errorf("profile not found: " + pID) } -func ValidProfiles(srv Server, wireguardSupport bool) (*[]profile.Profile, error) { +func ValidProfiles(srv Server, wireguardSupport bool) (*profile.Info, error) { // No error wrapping here otherwise we wrap it too much b, err := srv.Base() if err != nil { @@ -87,7 +87,12 @@ func ValidProfiles(srv Server, wireguardSupport bool) (*[]profile.Profile, error if len(ps) == 0 { return nil, errors.Errorf("no profiles found with supported protocols") } - return &ps, nil + return &profile.Info{ + Current: b.Profiles.Current, + Info: profile.ListInfo{ + ProfileList: ps, + }, + }, nil } func Profile(srv Server, id string) error { @@ -190,6 +195,7 @@ func HasValidProfile(ctx context.Context, srv Server, wireguardSupport bool) (bo } } + // there are multiple profiles and no selection has been made if len(b.Profiles.Info.ProfileList) != 1 && b.Profiles.Current == "" { return false, nil } diff --git a/types/error/error.go b/types/error/error.go new file mode 100644 index 0000000..9697e9d --- /dev/null +++ b/types/error/error.go @@ -0,0 +1,24 @@ +package err + +// Translated defines the type for translated strings +// It is a map from language tags to error messages +type Translated map[string]string + +// Error is the struct that defines the public error types +// This contains the error message with translations +// And other info +type Error struct { + // Message defines the error message + // It is a map from language tags to messages + // If a language is not translated, the whole language tag key is missing + // E.g. compare (english and french translations) + // {"en": "hello", "fr": "bonjour"} + // and + // {"en": "hello"} + // English is always present and should be used as a fallback + Message Translated `json:"message"` + + // Misc indicates whether or not this error is only there for miscellaneous purposes + // If this is set to True, the client UI SHOULD NOT show this error + Misc bool `json:"misc"` +} diff --git a/wrappers/python/eduvpn_common/main.py b/wrappers/python/eduvpn_common/main.py index f3639e0..335b78c 100644 --- a/wrappers/python/eduvpn_common/main.py +++ b/wrappers/python/eduvpn_common/main.py @@ -1,4 +1,5 @@ import ctypes +import json from enum import IntEnum from typing import Any, Callable, Iterator, Optional @@ -17,14 +18,19 @@ from eduvpn_common.state import State class WrappedError(Exception): - pass + def __init__(self, translations, language, misc): + self.translations = translations + self.language = language + self.misc = misc + def __str__(self) -> str: + print(self.translations) + return self.translations[self.language] -def forwardError(error: bytes | str): - # TODO: HACK, remove this - if isinstance(error, str): - raise WrappedError(error) - raise WrappedError(error.decode("utf-8")) + +def forwardError(error: str): + d = json.loads(error) + raise WrappedError(d["message"], "en", d["misc"]) class ServerType(IntEnum): |
