refactor: Remove config, display, and i18n packages (#26)
Removes the following unused packages: - pkg/config - pkg/display - pkg/i18n Also removes the dependencies from pkg/runtime and cleans up the go.mod and go.sum files. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
parent
623f05ffc1
commit
614aed51ba
26 changed files with 5 additions and 8125 deletions
5
go.mod
5
go.mod
|
|
@ -4,19 +4,17 @@ go 1.25
|
|||
|
||||
require (
|
||||
github.com/ProtonMail/go-crypto v1.3.0
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0
|
||||
github.com/pkg/sftp v1.13.10
|
||||
github.com/skeema/knownhosts v1.3.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.37
|
||||
golang.org/x/crypto v0.43.0
|
||||
golang.org/x/text v0.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/adrg/xdg v0.5.3 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.5.1 // indirect
|
||||
|
|
@ -52,6 +50,7 @@ require (
|
|||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -1,7 +1,5 @@
|
|||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
|
|
@ -78,8 +76,6 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
|||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/Snider/Core/pkg/config/internal"
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
)
|
||||
|
||||
// Options holds configuration for the config service.
|
||||
type Options = internal.Options
|
||||
|
||||
// Service provides access to the application's configuration.
|
||||
type Service = internal.Service
|
||||
|
||||
// New is the constructor for static dependency injection.
|
||||
func New() (*Service, error) {
|
||||
return internal.New()
|
||||
}
|
||||
|
||||
// Register is the constructor for dynamic dependency injection.
|
||||
func Register(c *core.Core) (any, error) {
|
||||
return internal.Register(c)
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const appName = "lethean"
|
||||
const configFileName = "config.json"
|
||||
|
||||
// setupTestEnv creates a temporary home directory for testing and ensures a clean environment.
|
||||
func setupTestEnv(t *testing.T) (string, func()) {
|
||||
tempHomeDir, err := os.MkdirTemp("", "test_home_*")
|
||||
require.NoError(t, err, "Failed to create temp home directory")
|
||||
|
||||
oldHome := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tempHomeDir)
|
||||
|
||||
// Unset XDG vars to ensure HOME is used for path resolution, creating a hermetic test.
|
||||
oldXdgData, hadXdgData := os.LookupEnv("XDG_DATA_HOME")
|
||||
oldXdgCache, hadXdgCache := os.LookupEnv("XDG_CACHE_HOME")
|
||||
require.NoError(t, os.Unsetenv("XDG_DATA_HOME"))
|
||||
require.NoError(t, os.Unsetenv("XDG_CACHE_HOME"))
|
||||
|
||||
cleanup := func() {
|
||||
os.Setenv("HOME", oldHome)
|
||||
if hadXdgData {
|
||||
os.Setenv("XDG_DATA_HOME", oldXdgData)
|
||||
} else {
|
||||
os.Unsetenv("XDG_DATA_HOME")
|
||||
}
|
||||
if hadXdgCache {
|
||||
os.Setenv("XDG_CACHE_HOME", oldXdgCache)
|
||||
} else {
|
||||
os.Unsetenv("XDG_CACHE_HOME")
|
||||
}
|
||||
os.RemoveAll(tempHomeDir)
|
||||
}
|
||||
|
||||
return tempHomeDir, cleanup
|
||||
}
|
||||
|
||||
func TestConfigService(t *testing.T) {
|
||||
t.Run("New service creates default config", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
serviceInstance, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// Check that the config file was created
|
||||
assert.FileExists(t, serviceInstance.ConfigPath, "config.json was not created")
|
||||
|
||||
// Check default values
|
||||
assert.Equal(t, "en", serviceInstance.Language, "Expected default language 'en'")
|
||||
})
|
||||
|
||||
t.Run("New service loads existing config", func(t *testing.T) {
|
||||
tempHomeDir, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
// Manually create a config file with non-default values
|
||||
configDir := filepath.Join(tempHomeDir, appName, "config")
|
||||
require.NoError(t, os.MkdirAll(configDir, os.ModePerm), "Failed to create test config dir")
|
||||
configPath := filepath.Join(configDir, configFileName)
|
||||
|
||||
customConfig := `{"language": "fr", "features": ["beta-testing"]}`
|
||||
require.NoError(t, os.WriteFile(configPath, []byte(customConfig), 0644), "Failed to write custom config file")
|
||||
|
||||
serviceInstance, err := New()
|
||||
require.NoError(t, err, "New() failed while loading existing config")
|
||||
|
||||
assert.Equal(t, "fr", serviceInstance.Language, "Expected language 'fr'")
|
||||
assert.True(t, serviceInstance.IsFeatureEnabled("beta-testing"), "Expected 'beta-testing' feature to be enabled")
|
||||
assert.False(t, serviceInstance.IsFeatureEnabled("alpha-testing"), "Did not expect 'alpha-testing' to be enabled")
|
||||
})
|
||||
|
||||
t.Run("Set and Get", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
key := "language"
|
||||
expectedValue := "de"
|
||||
require.NoError(t, s.Set(key, expectedValue), "Set() failed")
|
||||
|
||||
var actualValue string
|
||||
require.NoError(t, s.Get(key, &actualValue), "Get() failed")
|
||||
assert.Equal(t, expectedValue, actualValue, "Get() returned unexpected value")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsFeatureEnabled(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test with no features enabled
|
||||
assert.False(t, s.IsFeatureEnabled("beta-feature"))
|
||||
|
||||
// Enable a feature
|
||||
err = s.Set("features", []string{"beta-feature", "alpha-testing"})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test for an enabled feature
|
||||
assert.True(t, s.IsFeatureEnabled("beta-feature"))
|
||||
|
||||
// Test for another enabled feature
|
||||
assert.True(t, s.IsFeatureEnabled("alpha-testing"))
|
||||
|
||||
// Test for a disabled feature
|
||||
assert.False(t, s.IsFeatureEnabled("gamma-feature"))
|
||||
|
||||
// Test with an empty string
|
||||
assert.False(t, s.IsFeatureEnabled(""))
|
||||
}
|
||||
|
||||
func TestSet_Good(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// Test setting a string value
|
||||
err = s.Set("language", "de")
|
||||
assert.NoError(t, err)
|
||||
var lang string
|
||||
err = s.Get("language", &lang)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "de", lang)
|
||||
|
||||
// Test setting a slice value
|
||||
err = s.Set("features", []string{"new-feature"})
|
||||
assert.NoError(t, err)
|
||||
var features []string
|
||||
err = s.Get("features", &features)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"new-feature"}, features)
|
||||
}
|
||||
|
||||
func TestSet_Bad(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// Test setting a value with the wrong type
|
||||
err = s.Set("language", 123)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test setting a non-existent key
|
||||
err = s.Set("nonExistentKey", "value")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestSet_Ugly(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// This should not panic
|
||||
assert.NotPanics(t, func() {
|
||||
err = s.Set("features", nil)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify the slice is now nil
|
||||
var features []string
|
||||
err = s.Get("features", &features)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, features)
|
||||
|
||||
// Test with a nil slice
|
||||
err = s.Set("features", nil)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, s.IsFeatureEnabled("beta-feature"))
|
||||
}
|
||||
|
||||
func TestRegister_Good(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
c, err := core.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
svc, err := Register(c)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, svc)
|
||||
|
||||
configSvc, ok := svc.(*Service)
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, configSvc.Runtime)
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// setupTestEnv creates a temporary home directory for testing and ensures a clean environment.
|
||||
func setupTestEnv(t *testing.T) (string, func()) {
|
||||
tempHomeDir, err := os.MkdirTemp("", "test_home_*")
|
||||
require.NoError(t, err, "Failed to create temp home directory")
|
||||
|
||||
oldHome := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tempHomeDir)
|
||||
|
||||
// Unset XDG vars to ensure HOME is used for path resolution, creating a hermetic test.
|
||||
oldXdgData, hadXdgData := os.LookupEnv("XDG_DATA_HOME")
|
||||
oldXdgCache, hadXdgCache := os.LookupEnv("XDG_CACHE_HOME")
|
||||
require.NoError(t, os.Unsetenv("XDG_DATA_HOME"))
|
||||
require.NoError(t, os.Unsetenv("XDG_CACHE_HOME"))
|
||||
|
||||
cleanup := func() {
|
||||
os.Setenv("HOME", oldHome)
|
||||
if hadXdgData {
|
||||
os.Setenv("XDG_DATA_HOME", oldXdgData)
|
||||
} else {
|
||||
os.Unsetenv("XDG_DATA_HOME")
|
||||
}
|
||||
if hadXdgCache {
|
||||
os.Setenv("XDG_CACHE_HOME", oldXdgCache)
|
||||
} else {
|
||||
os.Unsetenv("XDG_CACHE_HOME")
|
||||
}
|
||||
os.RemoveAll(tempHomeDir)
|
||||
}
|
||||
|
||||
return tempHomeDir, cleanup
|
||||
}
|
||||
|
||||
func TestConfigService(t *testing.T) {
|
||||
t.Run("New service creates default config", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
serviceInstance, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// Check that the config file was created
|
||||
assert.FileExists(t, serviceInstance.ConfigPath, "config.json was not created")
|
||||
|
||||
// Check default values
|
||||
assert.Equal(t, "en", serviceInstance.Language, "Expected default language 'en'")
|
||||
})
|
||||
|
||||
t.Run("New service loads existing config", func(t *testing.T) {
|
||||
tempHomeDir, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
// Manually create a config file with non-default values
|
||||
configDir := filepath.Join(tempHomeDir, appName, "config")
|
||||
require.NoError(t, os.MkdirAll(configDir, os.ModePerm), "Failed to create test config dir")
|
||||
configPath := filepath.Join(configDir, configFileName)
|
||||
|
||||
customConfig := `{"language": "fr", "features": ["beta-testing"]}`
|
||||
require.NoError(t, os.WriteFile(configPath, []byte(customConfig), 0644), "Failed to write custom config file")
|
||||
|
||||
serviceInstance, err := New()
|
||||
require.NoError(t, err, "New() failed while loading existing config")
|
||||
|
||||
assert.Equal(t, "fr", serviceInstance.Language, "Expected language 'fr'")
|
||||
assert.True(t, serviceInstance.IsFeatureEnabled("beta-testing"), "Expected 'beta-testing' feature to be enabled")
|
||||
assert.False(t, serviceInstance.IsFeatureEnabled("alpha-testing"), "Did not expect 'alpha-testing' to be enabled")
|
||||
})
|
||||
|
||||
t.Run("Set and Get", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
key := "language"
|
||||
expectedValue := "de"
|
||||
require.NoError(t, s.Set(key, expectedValue), "Set() failed")
|
||||
|
||||
var actualValue string
|
||||
require.NoError(t, s.Get(key, &actualValue), "Get() failed")
|
||||
assert.Equal(t, expectedValue, actualValue, "Get() returned unexpected value")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsFeatureEnabled(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test with no features enabled
|
||||
assert.False(t, s.IsFeatureEnabled("beta-feature"))
|
||||
|
||||
// Enable a feature
|
||||
s.Features = []string{"beta-feature", "alpha-testing"}
|
||||
|
||||
// Test for an enabled feature
|
||||
assert.True(t, s.IsFeatureEnabled("beta-feature"))
|
||||
|
||||
// Test for another enabled feature
|
||||
assert.True(t, s.IsFeatureEnabled("alpha-testing"))
|
||||
|
||||
// Test for a disabled feature
|
||||
assert.False(t, s.IsFeatureEnabled("gamma-feature"))
|
||||
|
||||
// Test with an empty string
|
||||
assert.False(t, s.IsFeatureEnabled(""))
|
||||
|
||||
// Test with a nil slice
|
||||
s.Features = nil
|
||||
assert.False(t, s.IsFeatureEnabled("beta-feature"))
|
||||
}
|
||||
|
||||
func TestSet_Good(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// Test setting a string value
|
||||
err = s.Set("language", "de")
|
||||
assert.NoError(t, err)
|
||||
var lang string
|
||||
err = s.Get("language", &lang)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "de", lang)
|
||||
|
||||
// Test setting a slice value
|
||||
err = s.Set("features", []string{"new-feature"})
|
||||
assert.NoError(t, err)
|
||||
var features []string
|
||||
err = s.Get("features", &features)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"new-feature"}, features)
|
||||
}
|
||||
|
||||
func TestSet_Bad(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// Test setting a value with the wrong type
|
||||
err = s.Set("language", 123)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test setting a non-existent key
|
||||
err = s.Set("nonExistentKey", "value")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestSet_Ugly(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
require.NoError(t, err, "New() failed")
|
||||
|
||||
// This should not panic
|
||||
assert.NotPanics(t, func() {
|
||||
err = s.Set("features", nil)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify the slice is now nil
|
||||
var features []string
|
||||
err = s.Get("features", &features)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, features)
|
||||
}
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
const appName = "lethean"
|
||||
const configFileName = "config.json"
|
||||
|
||||
// Options holds configuration for the config service.
|
||||
type Options struct{}
|
||||
|
||||
// Service provides access to the application's configuration.
|
||||
// It handles loading, saving, and providing access to configuration values.
|
||||
type Service struct {
|
||||
*core.Runtime[Options] `json:"-"`
|
||||
|
||||
// Persistent fields, saved to config.json.
|
||||
ConfigPath string `json:"configPath,omitempty"`
|
||||
UserHomeDir string `json:"userHomeDir,omitempty"`
|
||||
RootDir string `json:"rootDir,omitempty"`
|
||||
CacheDir string `json:"cacheDir,omitempty"`
|
||||
ConfigDir string `json:"configDir,omitempty"`
|
||||
DataDir string `json:"dataDir,omitempty"`
|
||||
WorkspaceDir string `json:"workspaceDir,omitempty"`
|
||||
DefaultRoute string `json:"default_route"`
|
||||
Features []string `json:"features"`
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
// createServiceInstance contains the common logic for initializing a Service struct.
|
||||
func createServiceInstance() (*Service, error) {
|
||||
// --- Path and Directory Setup ---
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not resolve user home directory: %w", err)
|
||||
}
|
||||
userHomeDir := filepath.Join(homeDir, appName)
|
||||
|
||||
rootDir, err := xdg.DataFile(appName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not resolve data directory: %w", err)
|
||||
}
|
||||
|
||||
cacheDir, err := xdg.CacheFile(appName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not resolve cache directory: %w", err)
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
UserHomeDir: userHomeDir,
|
||||
RootDir: rootDir,
|
||||
CacheDir: cacheDir,
|
||||
ConfigDir: filepath.Join(userHomeDir, "config"),
|
||||
DataDir: filepath.Join(userHomeDir, "data"),
|
||||
WorkspaceDir: filepath.Join(userHomeDir, "workspace"),
|
||||
DefaultRoute: "/",
|
||||
Features: []string{},
|
||||
Language: "en",
|
||||
}
|
||||
s.ConfigPath = filepath.Join(s.ConfigDir, configFileName)
|
||||
|
||||
dirs := []string{s.RootDir, s.ConfigDir, s.DataDir, s.CacheDir, s.WorkspaceDir, s.UserHomeDir}
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("could not create directory %s: %w", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Load or Create Configuration ---
|
||||
if data, err := os.ReadFile(s.ConfigPath); err == nil {
|
||||
// Config file exists, load it.
|
||||
if err := json.Unmarshal(data, s); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
} else if os.IsNotExist(err) {
|
||||
// Config file does not exist, create it with default values.
|
||||
if err := s.Save(); err != nil {
|
||||
return nil, fmt.Errorf("failed to create default config file: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Another error occurred reading the file.
|
||||
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// New is the constructor for static dependency injection.
|
||||
// It creates a Service instance without initializing the core.Runtime field.
|
||||
func New() (*Service, error) {
|
||||
return createServiceInstance()
|
||||
}
|
||||
|
||||
// Register is the constructor for dynamic dependency injection (used with core.WithService).
|
||||
// It creates a Service instance and initializes its core.Runtime field.
|
||||
func Register(c *core.Core) (any, error) {
|
||||
s, err := createServiceInstance()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Defensive check: createServiceInstance should not return nil service with nil error
|
||||
if s == nil {
|
||||
return nil, errors.New("config: createServiceInstance returned a nil service instance with no error")
|
||||
}
|
||||
s.Runtime = core.NewRuntime(c, Options{})
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Save writes the current configuration to config.json.
|
||||
func (s *Service) Save() error {
|
||||
data, err := json.MarshalIndent(s, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(s.ConfigPath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves a configuration value by its key.
|
||||
func (s *Service) Get(key string, out any) error {
|
||||
val := reflect.ValueOf(s).Elem()
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
jsonTag := field.Tag.Get("json")
|
||||
if jsonTag != "" && jsonTag != "-" {
|
||||
jsonName := strings.Split(jsonTag, ",")[0]
|
||||
if strings.EqualFold(jsonName, key) {
|
||||
outVal := reflect.ValueOf(out)
|
||||
if outVal.Kind() != reflect.Ptr || outVal.IsNil() {
|
||||
return errors.New("output argument must be a non-nil pointer")
|
||||
}
|
||||
targetVal := outVal.Elem()
|
||||
srcVal := val.Field(i)
|
||||
|
||||
if !srcVal.Type().AssignableTo(targetVal.Type()) {
|
||||
return fmt.Errorf("cannot assign config value of type %s to output of type %s", srcVal.Type(), targetVal.Type())
|
||||
}
|
||||
targetVal.Set(srcVal)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("key '%s' not found in config", key)
|
||||
}
|
||||
|
||||
// IsFeatureEnabled checks if a specific feature is enabled in the config.
|
||||
func (s *Service) IsFeatureEnabled(feature string) bool {
|
||||
for _, f := range s.Features {
|
||||
if f == feature {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Set updates a configuration value and saves the config.
|
||||
func (s *Service) Set(key string, v any) error {
|
||||
val := reflect.ValueOf(s).Elem()
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
jsonTag := field.Tag.Get("json")
|
||||
if jsonTag != "" && jsonTag != "-" {
|
||||
jsonName := strings.Split(jsonTag, ",")[0]
|
||||
if strings.EqualFold(jsonName, key) {
|
||||
fieldVal := val.Field(i)
|
||||
if !fieldVal.CanSet() {
|
||||
return fmt.Errorf("cannot set config field for key '%s'", key)
|
||||
}
|
||||
if v == nil {
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.Pointer, reflect.Interface, reflect.Map, reflect.Slice, reflect.Func:
|
||||
fieldVal.Set(reflect.Zero(fieldVal.Type()))
|
||||
return s.Save()
|
||||
default:
|
||||
return fmt.Errorf("type mismatch for key '%s': expected %s, got nil", key, fieldVal.Type())
|
||||
}
|
||||
}
|
||||
newVal := reflect.ValueOf(v)
|
||||
if !newVal.Type().AssignableTo(fieldVal.Type()) {
|
||||
return fmt.Errorf("type mismatch for key '%s': expected %s, got %s", key, fieldVal.Type(), newVal.Type())
|
||||
}
|
||||
fieldVal.Set(newVal)
|
||||
return s.Save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("key '%s' not found in config", key)
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package display
|
||||
|
||||
import "github.com/wailsapp/wails/v3/pkg/application"
|
||||
|
||||
// ActionOpenWindow is an IPC message used to request a new window.
|
||||
type ActionOpenWindow struct {
|
||||
application.WebviewWindowOptions
|
||||
}
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// Options holds configuration for the display service.
|
||||
type Options struct{}
|
||||
|
||||
// Service manages windowing, dialogs, and other visual elements.
|
||||
type Service struct {
|
||||
*core.Runtime[Options]
|
||||
config core.Config
|
||||
}
|
||||
|
||||
// newDisplayService contains the common logic for initializing a Service struct.
|
||||
func newDisplayService() (*Service, error) {
|
||||
return &Service{}, nil
|
||||
}
|
||||
|
||||
// New is the constructor for static dependency injection.
|
||||
// It creates a Service instance without initializing the core.Runtime field.
|
||||
func New() (*Service, error) {
|
||||
s, err := newDisplayService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Register is the constructor for dynamic dependency injection (used with core.WithService).
|
||||
// It creates a Service instance and initializes its core.Runtime field.
|
||||
func Register(c *core.Core) (any, error) {
|
||||
s, err := newDisplayService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Runtime = core.NewRuntime(c, Options{})
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Service) ServiceName() string { return "github.com/Snider/Core/display" }
|
||||
|
||||
// HandleIPCEvents processes IPC messages and performs actions such as opening windows or initializing services based on message types.
|
||||
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
|
||||
switch m := msg.(type) {
|
||||
case map[string]any:
|
||||
if action, ok := m["action"].(string); ok && action == "display.open_window" {
|
||||
return s.handleOpenWindowAction(m)
|
||||
}
|
||||
case ActionOpenWindow:
|
||||
_, err := s.NewWithStruct(&m.WebviewWindowOptions)
|
||||
return err
|
||||
case core.ActionServiceStartup:
|
||||
return s.ServiceStartup(context.Background(), application.ServiceOptions{})
|
||||
default:
|
||||
c.App.Logger.Error("Display: Unknown message type", "type", fmt.Sprintf("%T", m))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleOpenWindowAction processes a message to configure and create a new window using specified name and options.
|
||||
func (s *Service) handleOpenWindowAction(msg map[string]any) error {
|
||||
opts := application.WebviewWindowOptions{}
|
||||
if name, ok := msg["name"].(string); ok {
|
||||
opts.Name = name
|
||||
}
|
||||
if optsMap, ok := msg["options"].(map[string]any); ok {
|
||||
if title, ok := optsMap["Title"].(string); ok {
|
||||
opts.Title = title
|
||||
}
|
||||
if width, ok := optsMap["Width"].(float64); ok {
|
||||
opts.Width = int(width)
|
||||
}
|
||||
if height, ok := optsMap["Height"].(float64); ok {
|
||||
opts.Height = int(height)
|
||||
}
|
||||
}
|
||||
s.Core().App.Window.NewWithOptions(opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShowEnvironmentDialog displays a dialog containing detailed information about the application's runtime environment.
|
||||
func (s *Service) ShowEnvironmentDialog() {
|
||||
envInfo := s.Core().App.Env.Info()
|
||||
|
||||
details := fmt.Sprintf(`Environment Information:\n\nOperating System: %s\nArchitecture: %s\nDebug Mode: %t\n\nDark Mode: %t\n\nPlatform Information:`,
|
||||
envInfo.OS,
|
||||
envInfo.Arch,
|
||||
envInfo.Debug,
|
||||
s.Core().App.Env.IsDarkMode()) // Use d.core.App
|
||||
|
||||
// Add platform-specific details
|
||||
for key, value := range envInfo.PlatformInfo {
|
||||
details += fmt.Sprintf("\n%s: %v", key, value)
|
||||
}
|
||||
|
||||
if envInfo.OSInfo != nil {
|
||||
details += fmt.Sprintf("\n\nOS Details:\nName: %s\nVersion: %s",
|
||||
envInfo.OSInfo.Name,
|
||||
envInfo.OSInfo.Version)
|
||||
}
|
||||
|
||||
dialog := s.Core().App.Dialog.Info()
|
||||
dialog.SetTitle("Environment Information")
|
||||
dialog.SetMessage(details)
|
||||
dialog.Show()
|
||||
}
|
||||
|
||||
// ServiceStartup initializes the display service and sets up the main application window and system tray.
|
||||
func (s *Service) ServiceStartup(context.Context, application.ServiceOptions) error {
|
||||
s.Core().App.Logger.Info("Display service started")
|
||||
s.buildMenu()
|
||||
s.systemTray()
|
||||
|
||||
// This will be updated to use the restored OpenWindow method
|
||||
return s.OpenWindow()
|
||||
}
|
||||
|
||||
// OpenWindow creates a new window with the default options.
|
||||
func (s *Service) OpenWindow(opts ...core.WindowOption) error {
|
||||
// Default options
|
||||
winOpts := &core.WindowConfig{
|
||||
Name: "main",
|
||||
Title: "Core",
|
||||
Width: 1280,
|
||||
Height: 800,
|
||||
URL: "/",
|
||||
}
|
||||
|
||||
// Apply options
|
||||
for _, opt := range opts {
|
||||
opt.Apply(winOpts)
|
||||
}
|
||||
|
||||
// Create Wails window options
|
||||
wailsOpts := application.WebviewWindowOptions{
|
||||
Name: winOpts.Name,
|
||||
Title: winOpts.Title,
|
||||
Width: winOpts.Width,
|
||||
Height: winOpts.Height,
|
||||
URL: winOpts.URL,
|
||||
}
|
||||
|
||||
s.Core().App.Window.NewWithOptions(wailsOpts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// monitorScreenChanges listens for theme change events and logs when screen configuration changes occur.
|
||||
func (s *Service) monitorScreenChanges() {
|
||||
s.Core().App.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) {
|
||||
s.Core().App.Logger.Info("Screen configuration changed")
|
||||
})
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// newTestCore creates a new core instance with essential services for testing.
|
||||
func newTestCore(t *testing.T) *core.Core {
|
||||
// We need a real wails app for the display service to function.
|
||||
// This setup will be more complex than for other services.
|
||||
// For now, we can use a simplified core instance.
|
||||
coreInstance, err := core.New()
|
||||
require.NoError(t, err)
|
||||
return coreInstance
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
service, err := New()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, service, "New() should return a non-nil service instance")
|
||||
}
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
coreInstance := newTestCore(t)
|
||||
service, err := Register(coreInstance)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, service, "Register() should return a non-nil service instance")
|
||||
}
|
||||
|
||||
func TestOpenWindow(t *testing.T) {
|
||||
// This test is complex to set up properly without a running Wails application.
|
||||
// A true functional test would require a more elaborate test harness that
|
||||
// can initialize the Wails runtime.
|
||||
|
||||
// For now, we can perform a basic smoke test.
|
||||
t.Run("basic window open smoke test", func(t *testing.T) {
|
||||
// Skipping this test for now as it requires a running app instance.
|
||||
t.Skip("Skipping OpenWindow test as it requires a running Wails application instance.")
|
||||
})
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// buildMenu creates and sets the main application menu.
|
||||
func (s *Service) buildMenu() {
|
||||
appMenu := s.Core().App.Menu.New()
|
||||
if runtime.GOOS == "darwin" {
|
||||
appMenu.AddRole(application.AppMenu)
|
||||
}
|
||||
appMenu.AddRole(application.FileMenu)
|
||||
appMenu.AddRole(application.ViewMenu)
|
||||
appMenu.AddRole(application.EditMenu)
|
||||
|
||||
workspace := appMenu.AddSubmenu("Workspace")
|
||||
workspace.Add("New").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
workspace.Add("List").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
|
||||
// Add brand-specific menu items
|
||||
//if s.brand == DeveloperHub {
|
||||
// appMenu.AddSubmenu("Developer")
|
||||
//}
|
||||
|
||||
appMenu.AddRole(application.WindowMenu)
|
||||
appMenu.AddRole(application.HelpMenu)
|
||||
|
||||
s.Core().App.Menu.Set(appMenu)
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// setupTray configures and creates the system tray icon and menu.
|
||||
func (s *Service) systemTray() {
|
||||
|
||||
systray := s.Core().App.SystemTray.New()
|
||||
systray.SetTooltip("Core")
|
||||
systray.SetLabel("Core")
|
||||
//appTrayIcon, _ := d.assets.ReadFile("assets/apptray.png")
|
||||
//
|
||||
//if runtime.GOOS == "darwin" {
|
||||
// systray.SetTemplateIcon(appTrayIcon)
|
||||
//} else {
|
||||
// // Support for light/dark mode icons
|
||||
// systray.SetDarkModeIcon(appTrayIcon)
|
||||
// systray.SetIcon(appTrayIcon)
|
||||
//}
|
||||
// Create a hidden window for the system tray menu to interact with
|
||||
trayWindow, _ := s.NewWithStruct(&Window{
|
||||
Name: "system-tray",
|
||||
Title: "System Tray Status",
|
||||
URL: "system-tray.html",
|
||||
Width: 400,
|
||||
Frameless: true,
|
||||
Hidden: true,
|
||||
})
|
||||
systray.AttachWindow(trayWindow).WindowOffset(5)
|
||||
|
||||
// --- Build Tray Menu ---
|
||||
trayMenu := s.Core().App.Menu.New()
|
||||
trayMenu.Add("Open Desktop").OnClick(func(ctx *application.Context) {
|
||||
for _, window := range s.Core().App.Window.GetAll() {
|
||||
window.Show()
|
||||
}
|
||||
})
|
||||
trayMenu.Add("Close Desktop").OnClick(func(ctx *application.Context) {
|
||||
for _, window := range s.Core().App.Window.GetAll() {
|
||||
window.Hide()
|
||||
}
|
||||
})
|
||||
|
||||
trayMenu.Add("Environment Info").OnClick(func(ctx *application.Context) {
|
||||
s.ShowEnvironmentDialog()
|
||||
})
|
||||
// Add brand-specific menu items
|
||||
//switch d.brand {
|
||||
//case AdminHub:
|
||||
// trayMenu.Add("Manage Workspace").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case ServerHub:
|
||||
// trayMenu.Add("Server Control").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case GatewayHub:
|
||||
// trayMenu.Add("Routing Table").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case DeveloperHub:
|
||||
// trayMenu.Add("Debug Console").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case ClientHub:
|
||||
// trayMenu.Add("Connect").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
// trayMenu.Add("Disconnect").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//}
|
||||
|
||||
trayMenu.AddSeparator()
|
||||
trayMenu.Add("Quit").OnClick(func(ctx *application.Context) {
|
||||
s.Core().App.Quit()
|
||||
})
|
||||
|
||||
systray.SetMenu(trayMenu)
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
type WindowOption func(*application.WebviewWindowOptions) error
|
||||
|
||||
type Window = application.WebviewWindowOptions
|
||||
|
||||
func WindowName(s string) WindowOption {
|
||||
return func(o *Window) error {
|
||||
o.Name = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
func WindowTitle(s string) WindowOption {
|
||||
return func(o *Window) error {
|
||||
o.Title = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WindowURL(s string) WindowOption {
|
||||
return func(o *Window) error {
|
||||
o.URL = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WindowWidth(i int) WindowOption {
|
||||
return func(o *Window) error {
|
||||
o.Width = i
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WindowHeight(i int) WindowOption {
|
||||
return func(o *Window) error {
|
||||
o.Height = i
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func applyOptions(opts ...WindowOption) *Window {
|
||||
w := &Window{}
|
||||
if opts == nil {
|
||||
return w
|
||||
}
|
||||
for _, o := range opts {
|
||||
if err := o(w); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// NewWithStruct creates a new window using the provided options and returns its handle.
|
||||
func (s *Service) NewWithStruct(options *Window) (*application.WebviewWindow, error) {
|
||||
return s.Core().App.Window.NewWithOptions(*options), nil
|
||||
}
|
||||
|
||||
// NewWithOptions creates a new window by applying a series of options.
|
||||
func (s *Service) NewWithOptions(opts ...WindowOption) (*application.WebviewWindow, error) {
|
||||
return s.NewWithStruct(applyOptions(opts...))
|
||||
}
|
||||
|
||||
// NewWithURL creates a new default window pointing to the specified URL.
|
||||
func (s *Service) NewWithURL(url string) (*application.WebviewWindow, error) {
|
||||
return s.NewWithOptions(
|
||||
WindowURL(url),
|
||||
WindowTitle("Core"),
|
||||
WindowHeight(900),
|
||||
WindowWidth(1280),
|
||||
)
|
||||
}
|
||||
|
||||
//// OpenWindow is a convenience method that creates and shows a window from a set of options.
|
||||
//func (s *Service) OpenWindow(opts ...WindowOption) error {
|
||||
// _, err := s.NewWithOptions(opts...)
|
||||
// return err
|
||||
//}
|
||||
|
||||
// SelectDirectory opens a directory selection dialog and returns the selected path.
|
||||
func (s *Service) SelectDirectory() (string, error) {
|
||||
dialog := application.OpenFileDialog()
|
||||
dialog.SetTitle("Select Project Directory")
|
||||
return dialog.PromptForSingleSelection()
|
||||
}
|
||||
|
||||
var instance *Window
|
||||
|
||||
func (s *Service) Window() *Window { return instance }
|
||||
File diff suppressed because it is too large
Load diff
187
pkg/i18n/i18n.go
187
pkg/i18n/i18n.go
|
|
@ -1,187 +0,0 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
//go:embed locales/*.json
|
||||
var localeFS embed.FS
|
||||
|
||||
// Options holds configuration for the i18n service.
|
||||
type Options struct{}
|
||||
|
||||
// Service provides internationalization and localization.
|
||||
type Service struct {
|
||||
*core.Runtime[Options]
|
||||
bundle *i18n.Bundle
|
||||
localizer *i18n.Localizer
|
||||
availableLangs []language.Tag
|
||||
}
|
||||
|
||||
// newI18nService contains the common logic for initializing a Service struct.
|
||||
func newI18nService() (*Service, error) {
|
||||
bundle := i18n.NewBundle(language.English)
|
||||
bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
|
||||
|
||||
availableLangs, err := getAvailableLanguages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, lang := range availableLangs {
|
||||
filePath := fmt.Sprintf("locales/%s.json", lang.String())
|
||||
if _, err := bundle.LoadMessageFileFS(localeFS, filePath); err != nil {
|
||||
return nil, fmt.Errorf("failed to load message file %s: %w", filePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
bundle: bundle,
|
||||
availableLangs: availableLangs,
|
||||
}
|
||||
// Language will be set during ServiceStartup after config is available.
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// New is the constructor for static dependency injection.
|
||||
// It creates a Service instance without initializing the core.Runtime field.
|
||||
// Dependencies are passed directly here.
|
||||
func New() (*Service, error) {
|
||||
s, err := newI18nService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Register is the constructor for dynamic dependency injection (used with core.WithService).
|
||||
// It creates a Service instance and initializes its core.Runtime field.
|
||||
// Dependencies are injected during ServiceStartup.
|
||||
func Register(c *core.Core) (any, error) {
|
||||
s, err := newI18nService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Runtime = core.NewRuntime(c, Options{})
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// HandleIPCEvents processes IPC messages, including injecting dependencies on startup.
|
||||
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
|
||||
switch m := msg.(type) {
|
||||
case core.ActionServiceStartup:
|
||||
return s.ServiceStartup(context.Background(), application.ServiceOptions{})
|
||||
default:
|
||||
c.App.Logger.Error("Display: Unknown message type", "type", fmt.Sprintf("%T", m))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServiceStartup is called when the app starts, after dependencies are injected.
|
||||
func (s *Service) ServiceStartup(context.Context, application.ServiceOptions) error {
|
||||
// Determine initial language after config is available.
|
||||
initialLang := "en"
|
||||
var lang string
|
||||
_ = s.Config().Get("language", &lang)
|
||||
if lang != "" {
|
||||
initialLang = lang
|
||||
}
|
||||
err := s.SetLanguage(initialLang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Core().App.Logger.Info("I18n service started")
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Language Management ---
|
||||
|
||||
func getAvailableLanguages() ([]language.Tag, error) {
|
||||
files, err := localeFS.ReadDir("locales")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read embedded locales directory: %w", err)
|
||||
}
|
||||
|
||||
var availableLangs []language.Tag
|
||||
for _, file := range files {
|
||||
lang := strings.TrimSuffix(file.Name(), ".json")
|
||||
tag := language.Make(lang)
|
||||
availableLangs = append(availableLangs, tag)
|
||||
}
|
||||
return availableLangs, nil
|
||||
}
|
||||
|
||||
func detectLanguage(supported []language.Tag) (string, error) {
|
||||
langEnv := os.Getenv("LANG")
|
||||
if langEnv == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
baseLang := strings.Split(langEnv, ".")[0]
|
||||
parsedLang, err := language.Parse(baseLang)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse language tag '%s': %w", baseLang, err)
|
||||
}
|
||||
|
||||
if len(supported) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
matcher := language.NewMatcher(supported)
|
||||
_, index, confidence := matcher.Match(parsedLang)
|
||||
|
||||
if confidence >= language.Low {
|
||||
return supported[index].String(), nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// --- Public Service Methods ---
|
||||
|
||||
func (s *Service) SetLanguage(lang string) error {
|
||||
requestedLang, err := language.Parse(lang)
|
||||
if err != nil {
|
||||
return fmt.Errorf("i18n: failed to parse language tag \"%s\": %w", lang, err)
|
||||
}
|
||||
|
||||
if len(s.availableLangs) == 0 {
|
||||
return fmt.Errorf("i18n: no available languages loaded in the bundle")
|
||||
}
|
||||
|
||||
matcher := language.NewMatcher(s.availableLangs)
|
||||
bestMatch, _, confidence := matcher.Match(requestedLang)
|
||||
|
||||
if confidence == language.No {
|
||||
return fmt.Errorf("i18n: unsupported language: %s", lang)
|
||||
}
|
||||
|
||||
s.localizer = i18n.NewLocalizer(s.bundle, bestMatch.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) Translate(messageID string) string {
|
||||
translation, err := s.localizer.Localize(&i18n.LocalizeConfig{MessageID: messageID})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "i18n: translation for key \"%s\" not found\n", messageID)
|
||||
return messageID
|
||||
}
|
||||
return translation
|
||||
}
|
||||
|
||||
// Ensure Service implements the core.I18n interface.
|
||||
var _ core.I18n = (*Service)(nil)
|
||||
|
||||
// SetBundle is a test helper to inject a bundle.
|
||||
func (s *Service) SetBundle(bundle *i18n.Bundle) {
|
||||
s.bundle = bundle
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func newTestBundle() *i18n.Bundle {
|
||||
bundle := i18n.NewBundle(language.English)
|
||||
bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
|
||||
bundle.MustParseMessageFileBytes([]byte(`{
|
||||
"hello": "Hello"
|
||||
}`), "en.json")
|
||||
bundle.MustParseMessageFileBytes([]byte(`{
|
||||
"hello": "Bonjour"
|
||||
}`), "fr.json")
|
||||
return bundle
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
s, err := New()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
}
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
c, err := core.New()
|
||||
require.NoError(t, err)
|
||||
s, err := Register(c)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, s)
|
||||
}
|
||||
|
||||
func TestSetLanguage(t *testing.T) {
|
||||
s, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
s.SetBundle(newTestBundle())
|
||||
|
||||
err = s.SetLanguage("en")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = s.SetLanguage("fr")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = s.SetLanguage("invalid")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestTranslate(t *testing.T) {
|
||||
s, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
s.SetBundle(newTestBundle())
|
||||
|
||||
err = s.SetLanguage("en")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Hello", s.Translate("hello"))
|
||||
|
||||
err = s.SetLanguage("fr")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Bonjour", s.Translate("hello"))
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
{
|
||||
"app.boot.download-check": "Nach Updates suchen",
|
||||
"app.boot.folder-check": "Setup-Check",
|
||||
"app.boot.loaded-runtime": "Anwendung geladen",
|
||||
"app.boot.server-check": "Überprüfung des Servers",
|
||||
"app.boot.start-runtime": "Desktop starten",
|
||||
"app.core.ui.search": "Suchen",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-export": "Blockchain-Export",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-import": "Blockchain-Import",
|
||||
"app.lthn.chain.daemons.lethean-wallet-cli": "Brieftasche CLI",
|
||||
"app.lthn.chain.daemons.lethean-wallet-rpc": "Wallet-RPC",
|
||||
"app.lthn.chain.daemons.lethean-wallet-vpn-rpc": "Exit-Knoten-Wallet",
|
||||
"app.lthn.chain.daemons.letheand": "Blockchain-Dienst",
|
||||
"app.lthn.chain.desc.no_transactions": "In diesem Block waren keine Transaktionen enthalten",
|
||||
"app.lthn.chain.description": "Lethean (LTHN) Blockchain-Statistiken",
|
||||
"app.lthn.chain.heading": "Lethean Blockchain-Statistiken",
|
||||
"app.lthn.chain.menu.blocks": "Blöcke",
|
||||
"app.lthn.chain.menu.configuration": "Aufbau",
|
||||
"app.lthn.chain.menu.raw_data": "Rohblockdaten",
|
||||
"app.lthn.chain.menu.stats": "Statistiken",
|
||||
"app.lthn.chain.table.age": "Alter",
|
||||
"app.lthn.chain.table.depth": "Tiefe",
|
||||
"app.lthn.chain.table.difficulty": "Schwierigkeit",
|
||||
"app.lthn.chain.table.height": "Höhe",
|
||||
"app.lthn.chain.table.reward": "BELOHNUNG",
|
||||
"app.lthn.chain.table.time": "Zeit",
|
||||
"app.lthn.chain.table.title.chain-status": "Blockchain-Status",
|
||||
"app.lthn.chain.table.title.recent-blocks": "Kürzlich erstellte Blöcke",
|
||||
"app.lthn.chain.title": "Blockchain Explorer",
|
||||
"app.lthn.chain.words.alt_blocks_count": "Alt-Blöcke",
|
||||
"app.lthn.chain.words.block_size": "Block Größe",
|
||||
"app.lthn.chain.words.block_size_limit": "Begrenzung der Blockgröße",
|
||||
"app.lthn.chain.words.chain_stat": "Kettenstatistik",
|
||||
"app.lthn.chain.words.chain_stat_value": "Knoten gemeldeter Wert",
|
||||
"app.lthn.chain.words.cumulative_difficulty": "Kumulative Schwierigkeit",
|
||||
"app.lthn.chain.words.depth": "Tiefe vom oberen Block",
|
||||
"app.lthn.chain.words.difficulty": "Schwierigkeit",
|
||||
"app.lthn.chain.words.grey_peerlist_size": "P2P graue Kollegen",
|
||||
"app.lthn.chain.words.hash": "Hash",
|
||||
"app.lthn.chain.words.height": "Höhe",
|
||||
"app.lthn.chain.words.incoming_connections_count": "P2P-Eingang",
|
||||
"app.lthn.chain.words.install-blockchain": "Blockchain installieren",
|
||||
"app.lthn.chain.words.last_block_time": "Synchronisiert mit Block:",
|
||||
"app.lthn.chain.words.loading-data": "Laden von Blockchain-Daten",
|
||||
"app.lthn.chain.words.major_version": "Hauptversion",
|
||||
"app.lthn.chain.words.miner_transaction": "Miner-Transaktion",
|
||||
"app.lthn.chain.words.miner_tx": "POW Miner-Transaktion",
|
||||
"app.lthn.chain.words.minor_version": "Nebenversion",
|
||||
"app.lthn.chain.words.nonce": "Lösung blockieren",
|
||||
"app.lthn.chain.words.orphan_status": "Gültiger Block",
|
||||
"app.lthn.chain.words.outgoing_connections_count": "P2P-Ausgang",
|
||||
"app.lthn.chain.words.reward": "BELOHNUNG",
|
||||
"app.lthn.chain.words.start_time": "Startzeit",
|
||||
"app.lthn.chain.words.status": "Status",
|
||||
"app.lthn.chain.words.target": "Ziel",
|
||||
"app.lthn.chain.words.target_height": "Zielhöhe",
|
||||
"app.lthn.chain.words.testnet": "Testnetz",
|
||||
"app.lthn.chain.words.timestamp": "Zeitstempel",
|
||||
"app.lthn.chain.words.top_height": "NEUESTER BLOCK",
|
||||
"app.lthn.chain.words.tx_count": "Transaktionen insgesamt",
|
||||
"app.lthn.chain.words.tx_pool_size": "ausstehende Transaktionen",
|
||||
"app.lthn.chain.words.unlock_time": "Block entsperren",
|
||||
"app.lthn.chain.words.valid": "Gültiger Block",
|
||||
"app.lthn.chain.words.version": "Version der Blockstruktur",
|
||||
"app.lthn.chain.words.white_peerlist_size": "P2P-Whitelist",
|
||||
"app.lthn.console.title": "Konsole",
|
||||
"app.lthn.wallet.button.create-wallet": "Brieftasche erstellen",
|
||||
"app.lthn.wallet.button.restore-wallet": "Brieftasche wiederherstellen",
|
||||
"app.lthn.wallet.button.unlock-wallet": "Freischalten",
|
||||
"app.lthn.wallet.label.address": "Adresse",
|
||||
"app.lthn.wallet.label.autosave": "Offene Brieftasche speichern",
|
||||
"app.lthn.wallet.label.filename": "Dateiname",
|
||||
"app.lthn.wallet.label.restore-height": "Höhe wiederherstellen",
|
||||
"app.lthn.wallet.label.spend-key": "Schlüssel ausgeben",
|
||||
"app.lthn.wallet.label.view-key": "Ansichtsschlüssel",
|
||||
"app.lthn.wallet.label.wallet-password": "Brieftaschen-Passwort",
|
||||
"app.lthn.wallet.label.wallet-password-confirm": "Passwort bestätigen",
|
||||
"app.lthn.wallet.titles.new-wallet": "Neue Brieftasche erstellen",
|
||||
"app.lthn.wallet.titles.restore-keys": "Von Schlüsseln wiederherstellen",
|
||||
"app.lthn.wallet.titles.restore-seed": "Wiederherstellung aus Samen",
|
||||
"app.lthn.wallet.titles.unlock-wallet": "Brieftasche entsperren",
|
||||
"app.lthn.wallet.titles.wallet-transactions": "Wallet-Transaktionen",
|
||||
"app.market.apps": "App-Marktplatz",
|
||||
"app.market.dashboard": "Instrumententafel",
|
||||
"app.market.installed": "Installierte Apps",
|
||||
"app.market.no-apps-installed": "Sie haben keine Apps installiert.",
|
||||
"app.market.view-installable-apps": "Installierbare Apps anzeigen",
|
||||
"app.title": "Lethean Desktop",
|
||||
"charts.network-hashrate.subtitle": "Daten bereitgestellt von",
|
||||
"charts.network-hashrate.title": "Netzwerk-Hash-Rate",
|
||||
"lang.de": "Deutsche",
|
||||
"lang.en": "Englisch",
|
||||
"lang.es": "Spanisch",
|
||||
"lang.fr": "Französisch",
|
||||
"lang.ru": "Russisch",
|
||||
"lang.uk": "Ukrainisch (Ukraine)",
|
||||
"lang.zh": "Chinesisch",
|
||||
"menu.about": "Über",
|
||||
"menu.activity": "Aktivität",
|
||||
"menu.api": "api",
|
||||
"menu.blockchain": "Blockchain",
|
||||
"menu.build": "Bauen",
|
||||
"menu.dashboard": "Instrumententafel",
|
||||
"menu.docs": "Dokumentation",
|
||||
"menu.documentation": "Dokumentation",
|
||||
"menu.explorer": "Forscher",
|
||||
"menu.help": "Hilfe",
|
||||
"menu.hub-admin": "Administrator",
|
||||
"menu.hub-client": "Klient",
|
||||
"menu.hub-developer": "Entwickler",
|
||||
"menu.hub-gateway": "TOR",
|
||||
"menu.hub-server": "Server-Hub",
|
||||
"menu.info": "info",
|
||||
"menu.logout": "Abmelden",
|
||||
"menu.mining": "Bergbau",
|
||||
"menu.settings": "die Einstellungen",
|
||||
"menu.vpn": "VPN",
|
||||
"menu.wallet": "Brieftasche",
|
||||
"menu.your-profile": "Dein Profil ",
|
||||
"view.dashboard.description": "Lethean (LTHN) Web-App",
|
||||
"view.dashboard.heading": "Lethean-Dashboard",
|
||||
"view.dashboard.title": "Lethean (LTHN)",
|
||||
"view.wallets.description": "Krypto-Wallet-Manager",
|
||||
"view.wallets.heading": "Wallet-Manager",
|
||||
"view.wallets.title": "Geldbörsen",
|
||||
"words.actions.add": "Hinzufügen",
|
||||
"words.actions.clone": "Klon",
|
||||
"words.actions.edit": "Bearbeiten",
|
||||
"words.actions.install": "Installieren",
|
||||
"words.actions.new": "Neu",
|
||||
"words.actions.remove": "Löschen",
|
||||
"words.actions.report": "Bericht",
|
||||
"words.actions.save": "sparen",
|
||||
"words.states.installing": "Installieren",
|
||||
"words.states.installing_desc": "Wir laden die ausführbaren Blockchain-Dateien von GitHub in Ihr Lethean-Benutzerverzeichnis herunter.",
|
||||
"words.states.loading": "Wird geladen",
|
||||
"words.states.not_installed": "Nicht installiert",
|
||||
"words.states.not_installed_desc": "Klicken Sie auf Blockchain installieren, um die neueste Lethean Blockchain CLI . herunterzuladen",
|
||||
"words.things.button": "Taste",
|
||||
"words.things.documentation": "Dokumentation",
|
||||
"words.things.menu": "Speisekarte",
|
||||
"words.things.mining-pool": "Bergbaupool",
|
||||
"words.things.page": "Seite",
|
||||
"words.things.problem": "Problem",
|
||||
"words.things.type": "Art",
|
||||
"words.time.past.day": "vor einem Tag",
|
||||
"words.time.past.days": "Vor Tagen",
|
||||
"words.time.past.hour": "vor einer Stunde",
|
||||
"words.time.past.hours": "Vor Stunden",
|
||||
"words.time.past.minute": "vor einer Minute",
|
||||
"words.time.past.minutes": "Vor ein paar Minuten",
|
||||
"words.time.past.month": "vor einem Monat",
|
||||
"words.time.past.months": " vor wenigen Monaten",
|
||||
"words.time.past.seconds": "vor ein paar Sekunden",
|
||||
"words.time.past.year": "vor einem Jahr",
|
||||
"words.time.past.years": "vor Jahren"
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
{
|
||||
"app.boot.download-check": "Checking for Updates",
|
||||
"app.boot.folder-check": "Setup Check",
|
||||
"app.boot.loaded-runtime": "Application Loaded",
|
||||
"app.boot.server-check": "Checking Server",
|
||||
"app.boot.start-runtime": "Starting Desktop",
|
||||
"app.core.ui.search": "Search",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-export": "Blockchain Export",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-import": "Blockchain Import",
|
||||
"app.lthn.chain.daemons.lethean-wallet-cli": "Wallet CLI",
|
||||
"app.lthn.chain.daemons.lethean-wallet-rpc": "Wallet RPC",
|
||||
"app.lthn.chain.daemons.lethean-wallet-vpn-rpc": "Exit Node Wallet",
|
||||
"app.lthn.chain.daemons.letheand": "Blockchain Service",
|
||||
"app.lthn.chain.desc.no_transactions": "There were no transactions included in this block",
|
||||
"app.lthn.chain.description": "Lethean (LTHN) Blockchain Stats",
|
||||
"app.lthn.chain.heading": "Lethean Blockchain Stats",
|
||||
"app.lthn.chain.menu.blocks": "Blocks",
|
||||
"app.lthn.chain.menu.configuration": "Configuration",
|
||||
"app.lthn.chain.menu.raw_data": "Raw Block Data",
|
||||
"app.lthn.chain.menu.stats": "Stats",
|
||||
"app.lthn.chain.table.age": "Age",
|
||||
"app.lthn.chain.table.depth": "Depth",
|
||||
"app.lthn.chain.table.difficulty": "Difficulty",
|
||||
"app.lthn.chain.table.height": "Height",
|
||||
"app.lthn.chain.table.reward": "Reward",
|
||||
"app.lthn.chain.table.time": "Time",
|
||||
"app.lthn.chain.table.title.chain-status": "Blockchain Status",
|
||||
"app.lthn.chain.table.title.recent-blocks": "Recently Created Blocks",
|
||||
"app.lthn.chain.title": "Blockchain Explorer",
|
||||
"app.lthn.chain.words.alt_blocks_count": "Alt Blocks",
|
||||
"app.lthn.chain.words.block_size": "Block Size",
|
||||
"app.lthn.chain.words.block_size_limit": "Block Size Limit",
|
||||
"app.lthn.chain.words.chain_stat": "Chain Stats",
|
||||
"app.lthn.chain.words.chain_stat_value": "Node Reported Value",
|
||||
"app.lthn.chain.words.cumulative_difficulty": "Cumulative Difficulty",
|
||||
"app.lthn.chain.words.depth": "Depth from Top Block",
|
||||
"app.lthn.chain.words.difficulty": "Difficulty",
|
||||
"app.lthn.chain.words.grey_peerlist_size": "P2P Grey Peers",
|
||||
"app.lthn.chain.words.hash": "Hash",
|
||||
"app.lthn.chain.words.height": "Height",
|
||||
"app.lthn.chain.words.incoming_connections_count": "P2P Incoming",
|
||||
"app.lthn.chain.words.install-blockchain": "Install Blockchain",
|
||||
"app.lthn.chain.words.last_block_time": "Synchronised to Block:",
|
||||
"app.lthn.chain.words.loading-data": "Loading Blockchain Data",
|
||||
"app.lthn.chain.words.major_version": "Major Version",
|
||||
"app.lthn.chain.words.miner_transaction": "Miner Transaction",
|
||||
"app.lthn.chain.words.miner_tx": "POW Miner Transaction",
|
||||
"app.lthn.chain.words.minor_version": "Minor Version",
|
||||
"app.lthn.chain.words.nonce": "Block Solution",
|
||||
"app.lthn.chain.words.orphan_status": "Valid Block",
|
||||
"app.lthn.chain.words.outgoing_connections_count": "P2P Out",
|
||||
"app.lthn.chain.words.reward": "Reward",
|
||||
"app.lthn.chain.words.start_time": "Start Time",
|
||||
"app.lthn.chain.words.status": "Status",
|
||||
"app.lthn.chain.words.target": "Target",
|
||||
"app.lthn.chain.words.target_height": "Target Height",
|
||||
"app.lthn.chain.words.testnet": "Testnet",
|
||||
"app.lthn.chain.words.timestamp": "Timestamp",
|
||||
"app.lthn.chain.words.top_height": "Newest Block",
|
||||
"app.lthn.chain.words.tx_count": "Total Transactions",
|
||||
"app.lthn.chain.words.tx_pool_size": "Pending Transactions",
|
||||
"app.lthn.chain.words.unlock_time": "Unlock Block",
|
||||
"app.lthn.chain.words.valid": "Valid Block",
|
||||
"app.lthn.chain.words.version": "Block Structure Version",
|
||||
"app.lthn.chain.words.white_peerlist_size": "P2P Whitelist",
|
||||
"app.lthn.console.title": "Console",
|
||||
"app.lthn.wallet.button.create-wallet": "Create Wallet",
|
||||
"app.lthn.wallet.button.restore-wallet": "Restore Wallet",
|
||||
"app.lthn.wallet.button.unlock-wallet": "Unlock",
|
||||
"app.lthn.wallet.label.address": "Address",
|
||||
"app.lthn.wallet.label.autosave": "Save Open Wallet",
|
||||
"app.lthn.wallet.label.filename": "Filename",
|
||||
"app.lthn.wallet.label.restore-height": "Restore Height",
|
||||
"app.lthn.wallet.label.spend-key": "Spend Key",
|
||||
"app.lthn.wallet.label.view-key": "View Key",
|
||||
"app.lthn.wallet.label.wallet-password": "Wallet Password",
|
||||
"app.lthn.wallet.label.wallet-password-confirm": "Confirm Password",
|
||||
"app.lthn.wallet.titles.new-wallet": "Make New Wallet",
|
||||
"app.lthn.wallet.titles.restore-keys": "Restore From Keys",
|
||||
"app.lthn.wallet.titles.restore-seed": "Restore From Seed",
|
||||
"app.lthn.wallet.titles.unlock-wallet": "Unlock Wallet",
|
||||
"app.lthn.wallet.titles.wallet-transactions": "Wallet Transactions",
|
||||
"app.market.apps": "App Marketplace",
|
||||
"app.market.dashboard": "Dashboard",
|
||||
"app.market.installed": "Installed Apps",
|
||||
"app.market.no-apps-installed": "You have no apps installed.",
|
||||
"app.market.view-installable-apps": "View Installable Apps",
|
||||
"app.title": "Lethean Desktop",
|
||||
"charts.network-hashrate.subtitle": "Data Provided by",
|
||||
"charts.network-hashrate.title": "Network Hash Rate",
|
||||
"lang.de": "German",
|
||||
"lang.en": "English",
|
||||
"lang.es": "Spanish",
|
||||
"lang.fr": "French",
|
||||
"lang.ru": "Russian",
|
||||
"lang.uk": "Ukrainian (Ukraine)",
|
||||
"lang.zh": "Chinese",
|
||||
"menu.about": "About",
|
||||
"menu.activity": "Activity",
|
||||
"menu.api": "api",
|
||||
"menu.blockchain": "Blockchain",
|
||||
"menu.build": "Build",
|
||||
"menu.dashboard": "Dashboard",
|
||||
"menu.docs": "Documentation",
|
||||
"menu.documentation": "Documentation",
|
||||
"menu.explorer": "Explorer",
|
||||
"menu.help": "Help",
|
||||
"menu.hub-admin": "Admin Hub",
|
||||
"menu.hub-client": "Client Hub",
|
||||
"menu.hub-developer": "Developer",
|
||||
"menu.hub-gateway": "Gateway",
|
||||
"menu.hub-server": "Server Hub",
|
||||
"menu.info": "info",
|
||||
"menu.logout": "Sign Out",
|
||||
"menu.mining": "Mining",
|
||||
"menu.settings": "Settings",
|
||||
"menu.vpn": "VPN",
|
||||
"menu.wallet": "Wallet",
|
||||
"menu.your-profile": "Your Profile",
|
||||
"view.dashboard.description": "Lethean (LTHN) Web app",
|
||||
"view.dashboard.heading": "Lethean Dashboard",
|
||||
"view.dashboard.title": "Lethean (LTHN)",
|
||||
"view.wallets.description": "Crypto Wallet Manager",
|
||||
"view.wallets.heading": "Wallet Manager",
|
||||
"view.wallets.title": "Wallets",
|
||||
"words.actions.add": "Add",
|
||||
"words.actions.clone": "Clone",
|
||||
"words.actions.edit": "Edit",
|
||||
"words.actions.install": "Install",
|
||||
"words.actions.new": "New",
|
||||
"words.actions.remove": "Remove",
|
||||
"words.actions.report": "Report",
|
||||
"words.actions.save": "Save",
|
||||
"words.states.installing": "Installing",
|
||||
"words.states.installing_desc": "We are downloading the blockchain executables from GitHub to your Lethean user directory.",
|
||||
"words.states.loading": "Loading",
|
||||
"words.states.not_installed": "Not Installed",
|
||||
"words.states.not_installed_desc": "Click Install Blockchain to download the latest Lethean Blockchain CLI",
|
||||
"words.things.button": "Button",
|
||||
"words.things.documentation": "Documentation",
|
||||
"words.things.menu": "Menu",
|
||||
"words.things.mining-pool": "Mining Pool",
|
||||
"words.things.page": "Page",
|
||||
"words.things.problem": "Problem",
|
||||
"words.things.type": "Type",
|
||||
"words.time.past.day": "a day ago",
|
||||
"words.time.past.days": "days ago",
|
||||
"words.time.past.hour": "an hour ago",
|
||||
"words.time.past.hours": "hours ago",
|
||||
"words.time.past.minute": "a minute ago",
|
||||
"words.time.past.minutes": "minutes ago",
|
||||
"words.time.past.month": "a month ago",
|
||||
"words.time.past.months": " months ago",
|
||||
"words.time.past.seconds": "a few seconds ago",
|
||||
"words.time.past.year": "a year ago",
|
||||
"words.time.past.years": "years ago"
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
{
|
||||
"app.boot.download-check": "Comprobando actualizaciones",
|
||||
"app.boot.folder-check": "Comprobación de configuración",
|
||||
"app.boot.loaded-runtime": "Aplicación cargada",
|
||||
"app.boot.server-check": "Servidor de comprobación",
|
||||
"app.boot.start-runtime": "Iniciar escritorio",
|
||||
"app.core.ui.search": "Buscar",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-export": "Exportación de Blockchain",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-import": "Importación de Blockchain",
|
||||
"app.lthn.chain.daemons.lethean-wallet-cli": "CLI de Wallet",
|
||||
"app.lthn.chain.daemons.lethean-wallet-rpc": "Monedero RPC",
|
||||
"app.lthn.chain.daemons.lethean-wallet-vpn-rpc": "Salir de la billetera del nodo",
|
||||
"app.lthn.chain.daemons.letheand": "Servicio Blockchain",
|
||||
"app.lthn.chain.desc.no_transactions": "No hubo transacciones incluidas en este bloque",
|
||||
"app.lthn.chain.description": "Estadísticas de Blockchain de Lethean (LTHN)",
|
||||
"app.lthn.chain.heading": "Estadísticas de Lethean Blockchain",
|
||||
"app.lthn.chain.menu.blocks": "bloques",
|
||||
"app.lthn.chain.menu.configuration": "Configuración",
|
||||
"app.lthn.chain.menu.raw_data": "Datos de bloque sin procesar",
|
||||
"app.lthn.chain.menu.stats": "Estadísticas",
|
||||
"app.lthn.chain.table.age": "años",
|
||||
"app.lthn.chain.table.depth": "Profundidad",
|
||||
"app.lthn.chain.table.difficulty": "Dificultad",
|
||||
"app.lthn.chain.table.height": "Altura",
|
||||
"app.lthn.chain.table.reward": "recompensa",
|
||||
"app.lthn.chain.table.time": "hora",
|
||||
"app.lthn.chain.table.title.chain-status": "Estado de blockchain",
|
||||
"app.lthn.chain.table.title.recent-blocks": "Bloques creados recientemente",
|
||||
"app.lthn.chain.title": "Explorador de blockchain",
|
||||
"app.lthn.chain.words.alt_blocks_count": "Bloques alternativos",
|
||||
"app.lthn.chain.words.block_size": "Tamaño de bloque",
|
||||
"app.lthn.chain.words.block_size_limit": "Límite de tamaño de bloque",
|
||||
"app.lthn.chain.words.chain_stat": "Estadísticas de la cadena",
|
||||
"app.lthn.chain.words.chain_stat_value": "Valor informado del nodo",
|
||||
"app.lthn.chain.words.cumulative_difficulty": "Dificultad Acumulativa",
|
||||
"app.lthn.chain.words.depth": "Profundidad desde el bloque superior",
|
||||
"app.lthn.chain.words.difficulty": "Dificultad",
|
||||
"app.lthn.chain.words.grey_peerlist_size": "Compañeros grises P2P",
|
||||
"app.lthn.chain.words.hash": "Picadillo",
|
||||
"app.lthn.chain.words.height": "Altura",
|
||||
"app.lthn.chain.words.incoming_connections_count": "P2P entrante",
|
||||
"app.lthn.chain.words.install-blockchain": "Instalar Blockchain",
|
||||
"app.lthn.chain.words.last_block_time": "Sincronizado para bloquear:",
|
||||
"app.lthn.chain.words.loading-data": "Cargando datos de Blockchain",
|
||||
"app.lthn.chain.words.major_version": "versión principal",
|
||||
"app.lthn.chain.words.miner_transaction": "Transacción minera",
|
||||
"app.lthn.chain.words.miner_tx": "Transacción de minero POW",
|
||||
"app.lthn.chain.words.minor_version": "versión menor",
|
||||
"app.lthn.chain.words.nonce": "Solución de bloque",
|
||||
"app.lthn.chain.words.orphan_status": "Bloque válido",
|
||||
"app.lthn.chain.words.outgoing_connections_count": "P2P hacia fuera",
|
||||
"app.lthn.chain.words.reward": "recompensa",
|
||||
"app.lthn.chain.words.start_time": "Hora de inicio",
|
||||
"app.lthn.chain.words.status": "Estado",
|
||||
"app.lthn.chain.words.target": "Objetivo",
|
||||
"app.lthn.chain.words.target_height": "Altura objetivo",
|
||||
"app.lthn.chain.words.testnet": "Testnet",
|
||||
"app.lthn.chain.words.timestamp": "marca de tiempo",
|
||||
"app.lthn.chain.words.top_height": "Bloque más nuevo",
|
||||
"app.lthn.chain.words.tx_count": "Transacciones totales",
|
||||
"app.lthn.chain.words.tx_pool_size": "Transacciones pendientes",
|
||||
"app.lthn.chain.words.unlock_time": "Desbloquear bloque",
|
||||
"app.lthn.chain.words.valid": "Bloque válido",
|
||||
"app.lthn.chain.words.version": "Versión de estructura de bloque",
|
||||
"app.lthn.chain.words.white_peerlist_size": "Lista blanca P2P",
|
||||
"app.lthn.console.title": "Consola",
|
||||
"app.lthn.wallet.button.create-wallet": "Crear billetera",
|
||||
"app.lthn.wallet.button.restore-wallet": "Restaurar billetera",
|
||||
"app.lthn.wallet.button.unlock-wallet": "Desbloquear",
|
||||
"app.lthn.wallet.label.address": "Dirección",
|
||||
"app.lthn.wallet.label.autosave": "Guardar billetera abierta",
|
||||
"app.lthn.wallet.label.filename": "Nombre del archivo",
|
||||
"app.lthn.wallet.label.restore-height": "Restaurar altura",
|
||||
"app.lthn.wallet.label.spend-key": "Gastar clave",
|
||||
"app.lthn.wallet.label.view-key": "Ver clave",
|
||||
"app.lthn.wallet.label.wallet-password": "Contraseña de billetera",
|
||||
"app.lthn.wallet.label.wallet-password-confirm": "Confirmar contraseña",
|
||||
"app.lthn.wallet.titles.new-wallet": "Crear nueva billetera",
|
||||
"app.lthn.wallet.titles.restore-keys": "Restaurar desde claves",
|
||||
"app.lthn.wallet.titles.restore-seed": "Restaurar de semilla",
|
||||
"app.lthn.wallet.titles.unlock-wallet": "Desbloquear billetera",
|
||||
"app.lthn.wallet.titles.wallet-transactions": "Transacciones de billetera",
|
||||
"app.market.apps": "Mercado de aplicaciones",
|
||||
"app.market.dashboard": "Tablero",
|
||||
"app.market.installed": "Aplicaciones instaladas",
|
||||
"app.market.no-apps-installed": "No tienes aplicaciones instaladas.",
|
||||
"app.market.view-installable-apps": "Ver aplicaciones instalables",
|
||||
"app.title": "Escritorio Lethean",
|
||||
"charts.network-hashrate.subtitle": "Datos proporcionados por",
|
||||
"charts.network-hashrate.title": "Tasa de hash de red",
|
||||
"lang.de": "alemán",
|
||||
"lang.en": "Inglés",
|
||||
"lang.es": "español",
|
||||
"lang.fr": "francés",
|
||||
"lang.ru": "ruso",
|
||||
"lang.uk": "Ucraniano (Ucrania)",
|
||||
"lang.zh": "chino",
|
||||
"menu.about": "Acerca De",
|
||||
"menu.activity": "Actividad",
|
||||
"menu.api": "api",
|
||||
"menu.blockchain": "Blockchain",
|
||||
"menu.build": "Construir",
|
||||
"menu.dashboard": "Tablero",
|
||||
"menu.docs": "Documentación",
|
||||
"menu.documentation": "Documentación",
|
||||
"menu.explorer": "explorador",
|
||||
"menu.help": "Ayuda",
|
||||
"menu.hub-admin": "Administración",
|
||||
"menu.hub-client": "Cliente",
|
||||
"menu.hub-developer": "Desarrollador",
|
||||
"menu.hub-gateway": "Puerta",
|
||||
"menu.hub-server": "Centro de servidores",
|
||||
"menu.info": "información",
|
||||
"menu.logout": "Desconectar",
|
||||
"menu.mining": "Minería",
|
||||
"menu.settings": "Ajustes",
|
||||
"menu.vpn": "VPN",
|
||||
"menu.wallet": "billetera",
|
||||
"menu.your-profile": "Tu perfil",
|
||||
"view.dashboard.description": "Aplicación web Lethean (LTHN)",
|
||||
"view.dashboard.heading": "Panel de Lethean",
|
||||
"view.dashboard.title": "Lethean (LTHN)",
|
||||
"view.wallets.description": "Administrador de criptomonedas",
|
||||
"view.wallets.heading": "Administrador de billetera",
|
||||
"view.wallets.title": "carteras",
|
||||
"words.actions.add": "Añadir",
|
||||
"words.actions.clone": "Clon",
|
||||
"words.actions.edit": "Editar",
|
||||
"words.actions.install": "instalar",
|
||||
"words.actions.new": "nuevo",
|
||||
"words.actions.remove": "retirar",
|
||||
"words.actions.report": "informe",
|
||||
"words.actions.save": "Salvar",
|
||||
"words.states.installing": "Instalando",
|
||||
"words.states.installing_desc": "Estamos descargando los ejecutables de blockchain de GitHub a su directorio de usuario de Lethean.",
|
||||
"words.states.loading": "Cargando",
|
||||
"words.states.not_installed": "No instalado",
|
||||
"words.states.not_installed_desc": "Haga clic en Instalar Blockchain para descargar la última CLI de Lethean Blockchain",
|
||||
"words.things.button": "Botón",
|
||||
"words.things.documentation": "Documentación",
|
||||
"words.things.menu": "menú",
|
||||
"words.things.mining-pool": "Pool de minería",
|
||||
"words.things.page": "Página",
|
||||
"words.things.problem": "Problema",
|
||||
"words.things.type": "Tipo",
|
||||
"words.time.past.day": "HACE UN DIA",
|
||||
"words.time.past.days": "hace días",
|
||||
"words.time.past.hour": "hace una hora",
|
||||
"words.time.past.hours": "horas atras",
|
||||
"words.time.past.minute": "hace un minuto",
|
||||
"words.time.past.minutes": "hace minutos",
|
||||
"words.time.past.month": "hace un mes",
|
||||
"words.time.past.months": " Hace meses",
|
||||
"words.time.past.seconds": "hace unos segundos",
|
||||
"words.time.past.year": "hace un año",
|
||||
"words.time.past.years": "Hace años que"
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
{
|
||||
"app.boot.download-check": "Vérification des mises à jour",
|
||||
"app.boot.folder-check": "Vérification de la configuration",
|
||||
"app.boot.loaded-runtime": "Application chargée",
|
||||
"app.boot.server-check": "Vérification du serveur",
|
||||
"app.boot.start-runtime": "Démarrage du bureau",
|
||||
"app.core.ui.search": "Recherche",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-export": "Exportation de la blockchain",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-import": "Importation de blockchain",
|
||||
"app.lthn.chain.daemons.lethean-wallet-cli": "Portefeuille CLI",
|
||||
"app.lthn.chain.daemons.lethean-wallet-rpc": "Portefeuille RPC",
|
||||
"app.lthn.chain.daemons.lethean-wallet-vpn-rpc": "Quitter le portefeuille de nœuds",
|
||||
"app.lthn.chain.daemons.letheand": "Service de blockchain",
|
||||
"app.lthn.chain.desc.no_transactions": "Il n'y avait aucune transaction incluse dans ce bloc",
|
||||
"app.lthn.chain.description": "Statistiques de la chaîne de blocs Lethean (LTHN)",
|
||||
"app.lthn.chain.heading": "Statistiques Lethean Blockchain",
|
||||
"app.lthn.chain.menu.blocks": "Des blocs",
|
||||
"app.lthn.chain.menu.configuration": "Configuration",
|
||||
"app.lthn.chain.menu.raw_data": "Données de bloc brutes",
|
||||
"app.lthn.chain.menu.stats": "statistiques",
|
||||
"app.lthn.chain.table.age": "âge",
|
||||
"app.lthn.chain.table.depth": "Profondeur",
|
||||
"app.lthn.chain.table.difficulty": "difficulté",
|
||||
"app.lthn.chain.table.height": "la taille",
|
||||
"app.lthn.chain.table.reward": "Récompensé",
|
||||
"app.lthn.chain.table.time": "Temps",
|
||||
"app.lthn.chain.table.title.chain-status": "Statut de la blockchain",
|
||||
"app.lthn.chain.table.title.recent-blocks": "Blocs récemment créés",
|
||||
"app.lthn.chain.title": "Explorateur de blockchain",
|
||||
"app.lthn.chain.words.alt_blocks_count": "Blocs alternatifs",
|
||||
"app.lthn.chain.words.block_size": "Taille de bloc",
|
||||
"app.lthn.chain.words.block_size_limit": "Limite de taille de bloc",
|
||||
"app.lthn.chain.words.chain_stat": "Statistiques de la chaîne",
|
||||
"app.lthn.chain.words.chain_stat_value": "Valeur signalée par le nœud",
|
||||
"app.lthn.chain.words.cumulative_difficulty": "Difficulté cumulée",
|
||||
"app.lthn.chain.words.depth": "Profondeur à partir du bloc supérieur",
|
||||
"app.lthn.chain.words.difficulty": "difficulté",
|
||||
"app.lthn.chain.words.grey_peerlist_size": "Pairs gris P2P",
|
||||
"app.lthn.chain.words.hash": "Hacher",
|
||||
"app.lthn.chain.words.height": "la taille",
|
||||
"app.lthn.chain.words.incoming_connections_count": "P2P entrant",
|
||||
"app.lthn.chain.words.install-blockchain": "Installer la blockchain",
|
||||
"app.lthn.chain.words.last_block_time": "Synchronisé pour bloquer :",
|
||||
"app.lthn.chain.words.loading-data": "Chargement des données de la blockchain",
|
||||
"app.lthn.chain.words.major_version": "Version majeure",
|
||||
"app.lthn.chain.words.miner_transaction": "Transaction de mineur",
|
||||
"app.lthn.chain.words.miner_tx": "Transaction de mineur POW",
|
||||
"app.lthn.chain.words.minor_version": "Version mineure",
|
||||
"app.lthn.chain.words.nonce": "Bloquer la solution",
|
||||
"app.lthn.chain.words.orphan_status": "Bloc valide",
|
||||
"app.lthn.chain.words.outgoing_connections_count": "Sortie P2P",
|
||||
"app.lthn.chain.words.reward": "Récompensé",
|
||||
"app.lthn.chain.words.start_time": "Heure de début",
|
||||
"app.lthn.chain.words.status": "Statut",
|
||||
"app.lthn.chain.words.target": "Cible",
|
||||
"app.lthn.chain.words.target_height": "Hauteur cible",
|
||||
"app.lthn.chain.words.testnet": "Réseau de test",
|
||||
"app.lthn.chain.words.timestamp": "horodatage",
|
||||
"app.lthn.chain.words.top_height": "Bloc le plus récent",
|
||||
"app.lthn.chain.words.tx_count": "Transactions totales",
|
||||
"app.lthn.chain.words.tx_pool_size": "Opérations en attente",
|
||||
"app.lthn.chain.words.unlock_time": "Débloquer le bloc",
|
||||
"app.lthn.chain.words.valid": "Bloc valide",
|
||||
"app.lthn.chain.words.version": "Version de structure de bloc",
|
||||
"app.lthn.chain.words.white_peerlist_size": "Liste blanche P2P",
|
||||
"app.lthn.console.title": "Console",
|
||||
"app.lthn.wallet.button.create-wallet": "Créer un portefeuille",
|
||||
"app.lthn.wallet.button.restore-wallet": "Restaurer le portefeuille",
|
||||
"app.lthn.wallet.button.unlock-wallet": "Ouvrir",
|
||||
"app.lthn.wallet.label.address": "Adresse",
|
||||
"app.lthn.wallet.label.autosave": "Enregistrer le portefeuille ouvert",
|
||||
"app.lthn.wallet.label.filename": "Nom de fichier",
|
||||
"app.lthn.wallet.label.restore-height": "Restaurer la hauteur",
|
||||
"app.lthn.wallet.label.spend-key": "Dépenser la clé",
|
||||
"app.lthn.wallet.label.view-key": "Afficher la clé",
|
||||
"app.lthn.wallet.label.wallet-password": "Mot de passe portefeuille",
|
||||
"app.lthn.wallet.label.wallet-password-confirm": "Confirmez le mot de passe",
|
||||
"app.lthn.wallet.titles.new-wallet": "Créer un nouveau portefeuille",
|
||||
"app.lthn.wallet.titles.restore-keys": "Restaurer à partir des clés",
|
||||
"app.lthn.wallet.titles.restore-seed": "Restaurer à partir de la graine",
|
||||
"app.lthn.wallet.titles.unlock-wallet": "Déverrouiller le portefeuille",
|
||||
"app.lthn.wallet.titles.wallet-transactions": "Transactions de portefeuille",
|
||||
"app.market.apps": "Marché d'applications",
|
||||
"app.market.dashboard": "Tableau de bord",
|
||||
"app.market.installed": "Applications installées",
|
||||
"app.market.no-apps-installed": "Aucune application n'est installée.",
|
||||
"app.market.view-installable-apps": "Afficher les applications installables",
|
||||
"app.title": "Bureau Lethean",
|
||||
"charts.network-hashrate.subtitle": "Données fournies par",
|
||||
"charts.network-hashrate.title": "Taux de hachage du réseau",
|
||||
"lang.de": "Allemand",
|
||||
"lang.en": "Anglais",
|
||||
"lang.es": "Espanol",
|
||||
"lang.fr": "Français",
|
||||
"lang.ru": "Russe",
|
||||
"lang.uk": "Ukrainien (Ukraine)",
|
||||
"lang.zh": "chinois",
|
||||
"menu.about": "A Propos",
|
||||
"menu.activity": "activité",
|
||||
"menu.api": "api",
|
||||
"menu.blockchain": "Blockchain",
|
||||
"menu.build": "Build",
|
||||
"menu.dashboard": "Tableau de bord",
|
||||
"menu.docs": "Documentation",
|
||||
"menu.documentation": "Documentation",
|
||||
"menu.explorer": "Explorateur",
|
||||
"menu.help": "Aide",
|
||||
"menu.hub-admin": "Administrateur",
|
||||
"menu.hub-client": "Client",
|
||||
"menu.hub-developer": "Développeur",
|
||||
"menu.hub-gateway": "PASSERELLE",
|
||||
"menu.hub-server": "Centre de serveurs",
|
||||
"menu.info": "info",
|
||||
"menu.logout": "Se déconnecter",
|
||||
"menu.mining": "Exploitation minière",
|
||||
"menu.settings": "Réglages",
|
||||
"menu.vpn": "VPN",
|
||||
"menu.wallet": "Portefeuille",
|
||||
"menu.your-profile": "Votre profil",
|
||||
"view.dashboard.description": "Application Web Lethean (LTHN)",
|
||||
"view.dashboard.heading": "Tableau de bord Léthéan",
|
||||
"view.dashboard.title": "Lethean (LTHN)",
|
||||
"view.wallets.description": "Gestionnaire de portefeuille crypto",
|
||||
"view.wallets.heading": "Gestionnaire de portefeuille",
|
||||
"view.wallets.title": "Portefeuilles",
|
||||
"words.actions.add": "Ajouter",
|
||||
"words.actions.clone": "Cloner",
|
||||
"words.actions.edit": "Modifier",
|
||||
"words.actions.install": "Installer",
|
||||
"words.actions.new": "Nouveau",
|
||||
"words.actions.remove": "Retirer",
|
||||
"words.actions.report": "rapport",
|
||||
"words.actions.save": "sauvegarder",
|
||||
"words.states.installing": "L'installation",
|
||||
"words.states.installing_desc": "Nous téléchargeons les exécutables blockchain de GitHub dans votre répertoire utilisateur Lethean.",
|
||||
"words.states.loading": "Chargement",
|
||||
"words.states.not_installed": "Pas installé",
|
||||
"words.states.not_installed_desc": "Cliquez sur Installer Blockchain pour télécharger la dernière CLI Lethean Blockchain",
|
||||
"words.things.button": "Bouton",
|
||||
"words.things.documentation": "Documentation",
|
||||
"words.things.menu": "menu",
|
||||
"words.things.mining-pool": "Piscine minière",
|
||||
"words.things.page": "Page",
|
||||
"words.things.problem": "Problème",
|
||||
"words.things.type": "Type",
|
||||
"words.time.past.day": "il y a un jour",
|
||||
"words.time.past.days": "il y a quelques jours",
|
||||
"words.time.past.hour": "il y a une heure",
|
||||
"words.time.past.hours": "il y a des heures",
|
||||
"words.time.past.minute": "Il y'a une minute",
|
||||
"words.time.past.minutes": "il y a quelques minutes",
|
||||
"words.time.past.month": "il y a un mois",
|
||||
"words.time.past.months": " il y a des mois",
|
||||
"words.time.past.seconds": "il ya quelques secondes",
|
||||
"words.time.past.year": "il y a un an",
|
||||
"words.time.past.years": "il y a des années"
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
{
|
||||
"app.boot.download-check": "Проверка обновлений",
|
||||
"app.boot.folder-check": "Проверка установки",
|
||||
"app.boot.loaded-runtime": "Приложение загружено",
|
||||
"app.boot.server-check": "Проверка сервера",
|
||||
"app.boot.start-runtime": "Запуск рабочего стола",
|
||||
"app.core.ui.search": "Поиск",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-export": "Блокчейн Экспорт",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-import": "Блокчейн Импорт",
|
||||
"app.lthn.chain.daemons.lethean-wallet-cli": "Кошелек CLI",
|
||||
"app.lthn.chain.daemons.lethean-wallet-rpc": "Кошелек RPC",
|
||||
"app.lthn.chain.daemons.lethean-wallet-vpn-rpc": "Выход из кошелька узла",
|
||||
"app.lthn.chain.daemons.letheand": "Блокчейн Сервис",
|
||||
"app.lthn.chain.desc.no_transactions": "В этом блоке не было включенных транзакций",
|
||||
"app.lthn.chain.description": "Статистика блокчейна Lethean (LTHN)",
|
||||
"app.lthn.chain.heading": "Статистика Lethean Blockchain",
|
||||
"app.lthn.chain.menu.blocks": "блоки",
|
||||
"app.lthn.chain.menu.configuration": "конфигурация",
|
||||
"app.lthn.chain.menu.raw_data": "Необработанные данные блока",
|
||||
"app.lthn.chain.menu.stats": "Статистика",
|
||||
"app.lthn.chain.table.age": "возраст",
|
||||
"app.lthn.chain.table.depth": "Глубина",
|
||||
"app.lthn.chain.table.difficulty": "Сложность",
|
||||
"app.lthn.chain.table.height": "Высота",
|
||||
"app.lthn.chain.table.reward": "Награда",
|
||||
"app.lthn.chain.table.time": "Время",
|
||||
"app.lthn.chain.table.title.chain-status": "Статус блокчейна",
|
||||
"app.lthn.chain.table.title.recent-blocks": "Недавно созданные блоки",
|
||||
"app.lthn.chain.title": "Исследователь блокчейн",
|
||||
"app.lthn.chain.words.alt_blocks_count": "Альтернативные блоки",
|
||||
"app.lthn.chain.words.block_size": "Размер блока",
|
||||
"app.lthn.chain.words.block_size_limit": "Ограничение размера блока",
|
||||
"app.lthn.chain.words.chain_stat": "Цепная статистика",
|
||||
"app.lthn.chain.words.chain_stat_value": "Сообщаемое значение узла",
|
||||
"app.lthn.chain.words.cumulative_difficulty": "Суммарная сложность",
|
||||
"app.lthn.chain.words.depth": "Глубина от верхнего блока",
|
||||
"app.lthn.chain.words.difficulty": "Сложность",
|
||||
"app.lthn.chain.words.grey_peerlist_size": "P2P Серые узлы",
|
||||
"app.lthn.chain.words.hash": "Хэш",
|
||||
"app.lthn.chain.words.height": "Высота",
|
||||
"app.lthn.chain.words.incoming_connections_count": "P2P In",
|
||||
"app.lthn.chain.words.install-blockchain": "Установить блокчейн",
|
||||
"app.lthn.chain.words.last_block_time": "Синхронизировано с блоком:",
|
||||
"app.lthn.chain.words.loading-data": "Загрузка данных блокчейна",
|
||||
"app.lthn.chain.words.major_version": "основная версия",
|
||||
"app.lthn.chain.words.miner_transaction": "Шахтерская транзакция",
|
||||
"app.lthn.chain.words.miner_tx": "Транзакция POW Miner",
|
||||
"app.lthn.chain.words.minor_version": "минорная версия",
|
||||
"app.lthn.chain.words.nonce": "Блочное решение",
|
||||
"app.lthn.chain.words.orphan_status": "Действительный блок",
|
||||
"app.lthn.chain.words.outgoing_connections_count": "P2P Out",
|
||||
"app.lthn.chain.words.reward": "Награда",
|
||||
"app.lthn.chain.words.start_time": "Начальное время",
|
||||
"app.lthn.chain.words.status": "Статус",
|
||||
"app.lthn.chain.words.target": "цель",
|
||||
"app.lthn.chain.words.target_height": "Высота",
|
||||
"app.lthn.chain.words.testnet": "Тестовая сеть",
|
||||
"app.lthn.chain.words.timestamp": "Метка времени",
|
||||
"app.lthn.chain.words.top_height": "Крайний блок",
|
||||
"app.lthn.chain.words.tx_count": "Всего транзакций",
|
||||
"app.lthn.chain.words.tx_pool_size": "Незавершенные транзакции",
|
||||
"app.lthn.chain.words.unlock_time": "Разблокировать Блок",
|
||||
"app.lthn.chain.words.valid": "Действительный блок",
|
||||
"app.lthn.chain.words.version": "Версия структуры блока",
|
||||
"app.lthn.chain.words.white_peerlist_size": "P2P Белый список",
|
||||
"app.lthn.console.title": "Приставка",
|
||||
"app.lthn.wallet.button.create-wallet": "Создать кошелек",
|
||||
"app.lthn.wallet.button.restore-wallet": "Восстановить кошелек",
|
||||
"app.lthn.wallet.button.unlock-wallet": "отпереть",
|
||||
"app.lthn.wallet.label.address": "Адрес",
|
||||
"app.lthn.wallet.label.autosave": "Сохранить открытый кошелек",
|
||||
"app.lthn.wallet.label.filename": "Имя файла",
|
||||
"app.lthn.wallet.label.restore-height": "С какого блока восстанавливаем?",
|
||||
"app.lthn.wallet.label.spend-key": "Spend key",
|
||||
"app.lthn.wallet.label.view-key": "View Key",
|
||||
"app.lthn.wallet.label.wallet-password": "Пароль кошелька",
|
||||
"app.lthn.wallet.label.wallet-password-confirm": "Подтвердите Пароль",
|
||||
"app.lthn.wallet.titles.new-wallet": "Сделать новый кошелек",
|
||||
"app.lthn.wallet.titles.restore-keys": "Восстановить из ключей",
|
||||
"app.lthn.wallet.titles.restore-seed": "Восстановить из seed",
|
||||
"app.lthn.wallet.titles.unlock-wallet": "Разблокировать кошелек",
|
||||
"app.lthn.wallet.titles.wallet-transactions": "Транзакции",
|
||||
"app.market.apps": "Магазин приложений",
|
||||
"app.market.dashboard": "Приборная доска",
|
||||
"app.market.installed": "Установленные приложения",
|
||||
"app.market.no-apps-installed": "У вас не установлены приложения.",
|
||||
"app.market.view-installable-apps": "Просмотр устанавливаемых приложений",
|
||||
"app.title": "Lethean Рабочий стол",
|
||||
"charts.network-hashrate.subtitle": "Данные предоставлены",
|
||||
"charts.network-hashrate.title": "Скорость хэширования в сети",
|
||||
"lang.de": "Немецкий",
|
||||
"lang.en": "Английский",
|
||||
"lang.es": "Испанский",
|
||||
"lang.fr": "Французский",
|
||||
"lang.ru": "Русский",
|
||||
"lang.uk": "Украинский (Украина)",
|
||||
"lang.zh": "Китайский язык",
|
||||
"menu.about": "Около",
|
||||
"menu.activity": "Активность",
|
||||
"menu.api": "api",
|
||||
"menu.blockchain": "Blockchain",
|
||||
"menu.build": "Сборка",
|
||||
"menu.dashboard": "Дашборд",
|
||||
"menu.docs": "Документация",
|
||||
"menu.documentation": "Документация",
|
||||
"menu.explorer": "Explorer",
|
||||
"menu.help": "Помощь",
|
||||
"menu.hub-admin": "Админ",
|
||||
"menu.hub-client": "Клиент",
|
||||
"menu.hub-developer": "Разработчик",
|
||||
"menu.hub-gateway": "Шлюз",
|
||||
"menu.hub-server": "Серверный концентратор",
|
||||
"menu.info": "Информация",
|
||||
"menu.logout": "Выход",
|
||||
"menu.mining": "Горнодобывающая промышленность",
|
||||
"menu.settings": "Настройки",
|
||||
"menu.vpn": "VPN",
|
||||
"menu.wallet": "Кошелек",
|
||||
"menu.your-profile": "Ваш профиль",
|
||||
"view.dashboard.description": "Веб-приложение Lethean (LTHN)",
|
||||
"view.dashboard.heading": "Панель управления Lethean",
|
||||
"view.dashboard.title": "Lethean (LTHN)",
|
||||
"view.wallets.description": "Менеджер крипто-кошелька",
|
||||
"view.wallets.heading": "Менеджер кошелька",
|
||||
"view.wallets.title": "Кошелёк",
|
||||
"words.actions.add": "Добавить",
|
||||
"words.actions.clone": "Клонировать",
|
||||
"words.actions.edit": "Редактировать",
|
||||
"words.actions.install": "Установка",
|
||||
"words.actions.new": "Новый",
|
||||
"words.actions.remove": "Удалить",
|
||||
"words.actions.report": "Отчет",
|
||||
"words.actions.save": "Сохранить",
|
||||
"words.states.installing": "Установка",
|
||||
"words.states.installing_desc": "Мы загружаем исполняемые файлы блокчейна с GitHub в ваш каталог пользователей Lethean.",
|
||||
"words.states.loading": "погрузка",
|
||||
"words.states.not_installed": "Не установлено",
|
||||
"words.states.not_installed_desc": "Нажмите «Установить блокчейн», чтобы загрузить последнюю версию интерфейса командной строки Lethean Blockchain.",
|
||||
"words.things.button": "Кнопка",
|
||||
"words.things.documentation": "Документация",
|
||||
"words.things.menu": "Меню",
|
||||
"words.things.mining-pool": "Майнинг пул",
|
||||
"words.things.page": "Страница",
|
||||
"words.things.problem": "Проблема",
|
||||
"words.things.type": "Тип",
|
||||
"words.time.past.day": "Предыдущий день",
|
||||
"words.time.past.days": "дней назад",
|
||||
"words.time.past.hour": "час назад",
|
||||
"words.time.past.hours": "несколько часов назад",
|
||||
"words.time.past.minute": "минуту назад",
|
||||
"words.time.past.minutes": "несколько минут назад",
|
||||
"words.time.past.month": "месяц назад",
|
||||
"words.time.past.months": " несколько месяцев назад",
|
||||
"words.time.past.seconds": "несколько секунд назад",
|
||||
"words.time.past.year": "год назад",
|
||||
"words.time.past.years": "много лет назад"
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
{
|
||||
"app.boot.download-check": "Перевірка наявності оновлень",
|
||||
"app.boot.folder-check": "Перевірка налаштування",
|
||||
"app.boot.loaded-runtime": "Завантажено програму",
|
||||
"app.boot.server-check": "Перевірка сервера",
|
||||
"app.boot.start-runtime": "Запуск робочого столу",
|
||||
"app.core.ui.search": "Пошук",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-export": "Експорт блокчейну",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-import": "Імпорт блокчейну",
|
||||
"app.lthn.chain.daemons.lethean-wallet-cli": "CLI гаманця",
|
||||
"app.lthn.chain.daemons.lethean-wallet-rpc": "RPC Wallet",
|
||||
"app.lthn.chain.daemons.lethean-wallet-vpn-rpc": "Вийдіть з Node Wallet",
|
||||
"app.lthn.chain.daemons.letheand": "Сервіс блокчейн",
|
||||
"app.lthn.chain.desc.no_transactions": "Не було жодних транзакцій, включених до цього блоку",
|
||||
"app.lthn.chain.description": "Статистика блокчейну Lethean (LTHN).",
|
||||
"app.lthn.chain.heading": "Статистика блокчейну Lethean",
|
||||
"app.lthn.chain.menu.blocks": "Блоки",
|
||||
"app.lthn.chain.menu.configuration": "Конфігурація",
|
||||
"app.lthn.chain.menu.raw_data": "Необроблені дані блоку",
|
||||
"app.lthn.chain.menu.stats": "Статистика",
|
||||
"app.lthn.chain.table.age": "Вік",
|
||||
"app.lthn.chain.table.depth": "Глибина",
|
||||
"app.lthn.chain.table.difficulty": "складність",
|
||||
"app.lthn.chain.table.height": "Висота",
|
||||
"app.lthn.chain.table.reward": "Нагорода",
|
||||
"app.lthn.chain.table.time": "Час",
|
||||
"app.lthn.chain.table.title.chain-status": "Стан блокчейну",
|
||||
"app.lthn.chain.table.title.recent-blocks": "Нещодавно створені блоки",
|
||||
"app.lthn.chain.title": "Blockchain Explorer",
|
||||
"app.lthn.chain.words.alt_blocks_count": "Альтернативні блоки",
|
||||
"app.lthn.chain.words.block_size": "Розмір блоку",
|
||||
"app.lthn.chain.words.block_size_limit": "Обмеження розміру блоку",
|
||||
"app.lthn.chain.words.chain_stat": "Статистика ланцюга",
|
||||
"app.lthn.chain.words.chain_stat_value": "Повідомлене значення вузла",
|
||||
"app.lthn.chain.words.cumulative_difficulty": "Сукупна складність",
|
||||
"app.lthn.chain.words.depth": "Глибина від верхнього блоку",
|
||||
"app.lthn.chain.words.difficulty": "складність",
|
||||
"app.lthn.chain.words.grey_peerlist_size": "P2P Grey Peers",
|
||||
"app.lthn.chain.words.hash": "Хеш",
|
||||
"app.lthn.chain.words.height": "Висота",
|
||||
"app.lthn.chain.words.incoming_connections_count": "P2P вхідні",
|
||||
"app.lthn.chain.words.install-blockchain": "Встановіть блокчейн",
|
||||
"app.lthn.chain.words.last_block_time": "Синхронізовано з блокуванням:",
|
||||
"app.lthn.chain.words.loading-data": "Завантаження даних Blockchain",
|
||||
"app.lthn.chain.words.major_version": "Основна версія",
|
||||
"app.lthn.chain.words.miner_transaction": "Транзакція майнера",
|
||||
"app.lthn.chain.words.miner_tx": "Транзакція майнера для військовополонених",
|
||||
"app.lthn.chain.words.minor_version": "Друга версія",
|
||||
"app.lthn.chain.words.nonce": "Блок рішення",
|
||||
"app.lthn.chain.words.orphan_status": "Дійсний блок",
|
||||
"app.lthn.chain.words.outgoing_connections_count": "Вихід P2P",
|
||||
"app.lthn.chain.words.reward": "Нагорода",
|
||||
"app.lthn.chain.words.start_time": "Час початку",
|
||||
"app.lthn.chain.words.status": "статус",
|
||||
"app.lthn.chain.words.target": "Ціль",
|
||||
"app.lthn.chain.words.target_height": "Цільова висота",
|
||||
"app.lthn.chain.words.testnet": "Testnet",
|
||||
"app.lthn.chain.words.timestamp": "Відмітка часу",
|
||||
"app.lthn.chain.words.top_height": "Найновіший блок",
|
||||
"app.lthn.chain.words.tx_count": "Загальна кількість транзакцій",
|
||||
"app.lthn.chain.words.tx_pool_size": "Транзакції в очікуванні",
|
||||
"app.lthn.chain.words.unlock_time": "Розблокувати блок",
|
||||
"app.lthn.chain.words.valid": "Дійсний блок",
|
||||
"app.lthn.chain.words.version": "Версія блокової структури",
|
||||
"app.lthn.chain.words.white_peerlist_size": "Білий список P2P",
|
||||
"app.lthn.console.title": "консоль",
|
||||
"app.lthn.wallet.button.create-wallet": "створити гаманець",
|
||||
"app.lthn.wallet.button.restore-wallet": "Відновити гаманець",
|
||||
"app.lthn.wallet.button.unlock-wallet": "розблокувати",
|
||||
"app.lthn.wallet.label.address": "Адреса",
|
||||
"app.lthn.wallet.label.autosave": "Зберегти Відкрийте гаманець",
|
||||
"app.lthn.wallet.label.filename": "Ім'я файлу",
|
||||
"app.lthn.wallet.label.restore-height": "Відновити висоту",
|
||||
"app.lthn.wallet.label.spend-key": "Ключ витрат",
|
||||
"app.lthn.wallet.label.view-key": "Ключ перегляду",
|
||||
"app.lthn.wallet.label.wallet-password": "Пароль гаманця",
|
||||
"app.lthn.wallet.label.wallet-password-confirm": "Підтвердьте пароль",
|
||||
"app.lthn.wallet.titles.new-wallet": "Створіть новий гаманець",
|
||||
"app.lthn.wallet.titles.restore-keys": "Відновити з ключів",
|
||||
"app.lthn.wallet.titles.restore-seed": "Відновити з насіння",
|
||||
"app.lthn.wallet.titles.unlock-wallet": "Розблокувати гаманець",
|
||||
"app.lthn.wallet.titles.wallet-transactions": "Трансакції гаманця",
|
||||
"app.market.apps": "App Marketplace",
|
||||
"app.market.dashboard": "Панель приладів",
|
||||
"app.market.installed": "Встановлені програми",
|
||||
"app.market.no-apps-installed": "У вас не встановлено жодної програми.",
|
||||
"app.market.view-installable-apps": "Переглянути додатки, які можна встановити",
|
||||
"app.title": "Lethean Робочий стіл",
|
||||
"charts.network-hashrate.subtitle": "Дані надані",
|
||||
"charts.network-hashrate.title": "Швидкість хеш-мережі",
|
||||
"lang.de": "Німецька",
|
||||
"lang.en": "Англійська",
|
||||
"lang.es": "Іспанська",
|
||||
"lang.fr": "Французька",
|
||||
"lang.ru": "російський",
|
||||
"lang.uk": "українська (Україна)",
|
||||
"lang.zh": "Китайська",
|
||||
"menu.about": "Про",
|
||||
"menu.activity": "Діяльність",
|
||||
"menu.api": "Api",
|
||||
"menu.blockchain": "Блокчейн",
|
||||
"menu.build": "Будувати",
|
||||
"menu.dashboard": "Панель приладів",
|
||||
"menu.docs": "Документація",
|
||||
"menu.documentation": "Документація",
|
||||
"menu.explorer": "Explorer",
|
||||
"menu.help": "Довідка",
|
||||
"menu.hub-admin": "Адмін",
|
||||
"menu.hub-client": "Клієнт",
|
||||
"menu.hub-developer": "Розробник",
|
||||
"menu.hub-gateway": "Шлюз",
|
||||
"menu.hub-server": "Центр серверів",
|
||||
"menu.info": "інформація",
|
||||
"menu.logout": "Вийти з аккаунта",
|
||||
"menu.mining": "Видобуток корисних копалин",
|
||||
"menu.settings": "налаштування",
|
||||
"menu.vpn": "VPN",
|
||||
"menu.wallet": "Гаманець",
|
||||
"menu.your-profile": "Ваш профіль",
|
||||
"view.dashboard.description": "Веб-додаток Lethean (LTHN).",
|
||||
"view.dashboard.heading": "Приладова панель Lethean",
|
||||
"view.dashboard.title": "Lethean (LTHN)",
|
||||
"view.wallets.description": "Менеджер криптовалютного гаманця",
|
||||
"view.wallets.heading": "Менеджер гаманця",
|
||||
"view.wallets.title": "Гаманці",
|
||||
"words.actions.add": "Додати",
|
||||
"words.actions.clone": "Клон",
|
||||
"words.actions.edit": "редагувати",
|
||||
"words.actions.install": "встановити",
|
||||
"words.actions.new": "новий",
|
||||
"words.actions.remove": "Видалити",
|
||||
"words.actions.report": "звіт",
|
||||
"words.actions.save": "зберегти",
|
||||
"words.states.installing": "Встановлення",
|
||||
"words.states.installing_desc": "Ми завантажуємо виконувані файли блокчейна з GitHub у ваш каталог користувачів Lethean.",
|
||||
"words.states.loading": "Завантаження",
|
||||
"words.states.not_installed": "Не встановлено",
|
||||
"words.states.not_installed_desc": "Натисніть Встановити Blockchain, щоб завантажити останню версію Lethean Blockchain CLI",
|
||||
"words.things.button": "Кнопка",
|
||||
"words.things.documentation": "Документація",
|
||||
"words.things.menu": "меню",
|
||||
"words.things.mining-pool": "Майнінг-пул",
|
||||
"words.things.page": "сторінка",
|
||||
"words.things.problem": "Проблема",
|
||||
"words.things.type": "Тип",
|
||||
"words.time.past.day": "день тому",
|
||||
"words.time.past.days": "днів тому",
|
||||
"words.time.past.hour": "годину тому",
|
||||
"words.time.past.hours": "годин тому",
|
||||
"words.time.past.minute": "хвилину тому",
|
||||
"words.time.past.minutes": "хвилин тому",
|
||||
"words.time.past.month": "місяць тому",
|
||||
"words.time.past.months": " місяців тому",
|
||||
"words.time.past.seconds": "кілька секунд тому",
|
||||
"words.time.past.year": "рік назад",
|
||||
"words.time.past.years": "багато років тому"
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
{
|
||||
"app.boot.download-check": "查询更新",
|
||||
"app.boot.folder-check": "设置检查",
|
||||
"app.boot.loaded-runtime": "应用程序已加载",
|
||||
"app.boot.server-check": "检查服务器",
|
||||
"app.boot.start-runtime": "启动桌面",
|
||||
"app.core.ui.search": "搜索",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-export": "区块链导出",
|
||||
"app.lthn.chain.daemons.lethean-blockchain-import": "区块链导入",
|
||||
"app.lthn.chain.daemons.lethean-wallet-cli": "钱包命令行界面(CLI)",
|
||||
"app.lthn.chain.daemons.lethean-wallet-rpc": "钱包远程过程调用(RPC)",
|
||||
"app.lthn.chain.daemons.lethean-wallet-vpn-rpc": "出口节点钱包",
|
||||
"app.lthn.chain.daemons.letheand": "区块链服务",
|
||||
"app.lthn.chain.desc.no_transactions": "此区块中不包含任何交易",
|
||||
"app.lthn.chain.description": "Lethean (LTHN) 区块链统计",
|
||||
"app.lthn.chain.heading": "Lethean 区块链统计数据",
|
||||
"app.lthn.chain.menu.blocks": "块",
|
||||
"app.lthn.chain.menu.configuration": "组态",
|
||||
"app.lthn.chain.menu.raw_data": "原始块数据",
|
||||
"app.lthn.chain.menu.stats": "统计资料",
|
||||
"app.lthn.chain.table.age": "年龄",
|
||||
"app.lthn.chain.table.depth": "深度",
|
||||
"app.lthn.chain.table.difficulty": "困难",
|
||||
"app.lthn.chain.table.height": "高度",
|
||||
"app.lthn.chain.table.reward": "奖励",
|
||||
"app.lthn.chain.table.time": "时间",
|
||||
"app.lthn.chain.table.title.chain-status": "区块链状态",
|
||||
"app.lthn.chain.table.title.recent-blocks": "最近创建的块",
|
||||
"app.lthn.chain.title": "区块链浏览器",
|
||||
"app.lthn.chain.words.alt_blocks_count": "替代块",
|
||||
"app.lthn.chain.words.block_size": "块大小",
|
||||
"app.lthn.chain.words.block_size_limit": "块大小限制",
|
||||
"app.lthn.chain.words.chain_stat": "连锁统计",
|
||||
"app.lthn.chain.words.chain_stat_value": "节点报告值",
|
||||
"app.lthn.chain.words.cumulative_difficulty": "累积难度",
|
||||
"app.lthn.chain.words.depth": "顶块深度",
|
||||
"app.lthn.chain.words.difficulty": "困难",
|
||||
"app.lthn.chain.words.grey_peerlist_size": "P2P 灰色同行",
|
||||
"app.lthn.chain.words.hash": "哈希",
|
||||
"app.lthn.chain.words.height": "高度",
|
||||
"app.lthn.chain.words.incoming_connections_count": "P2P传入",
|
||||
"app.lthn.chain.words.install-blockchain": "安装区块链",
|
||||
"app.lthn.chain.words.last_block_time": "同步到块:",
|
||||
"app.lthn.chain.words.loading-data": "加载区块链数据",
|
||||
"app.lthn.chain.words.major_version": "主要版本",
|
||||
"app.lthn.chain.words.miner_transaction": "",
|
||||
"app.lthn.chain.words.miner_tx": "POW 矿工交易",
|
||||
"app.lthn.chain.words.minor_version": "次要版本",
|
||||
"app.lthn.chain.words.nonce": "块解决方案",
|
||||
"app.lthn.chain.words.orphan_status": "有效区块",
|
||||
"app.lthn.chain.words.outgoing_connections_count": "点对点输出",
|
||||
"app.lthn.chain.words.reward": "奖励",
|
||||
"app.lthn.chain.words.start_time": "开始时间",
|
||||
"app.lthn.chain.words.status": "状态",
|
||||
"app.lthn.chain.words.target": "目标",
|
||||
"app.lthn.chain.words.target_height": "目标高度",
|
||||
"app.lthn.chain.words.testnet": "测试网",
|
||||
"app.lthn.chain.words.timestamp": "时间戳",
|
||||
"app.lthn.chain.words.top_height": "最新区块",
|
||||
"app.lthn.chain.words.tx_count": "交易总额",
|
||||
"app.lthn.chain.words.tx_pool_size": "待交易",
|
||||
"app.lthn.chain.words.unlock_time": "解锁方块",
|
||||
"app.lthn.chain.words.valid": "有效区块",
|
||||
"app.lthn.chain.words.version": "块结构版本",
|
||||
"app.lthn.chain.words.white_peerlist_size": "P2P白名单",
|
||||
"app.lthn.console.title": "安慰",
|
||||
"app.lthn.wallet.button.create-wallet": "创建钱包",
|
||||
"app.lthn.wallet.button.restore-wallet": "恢复钱包",
|
||||
"app.lthn.wallet.button.unlock-wallet": "开锁",
|
||||
"app.lthn.wallet.label.address": "地址",
|
||||
"app.lthn.wallet.label.autosave": "保存开设钱包",
|
||||
"app.lthn.wallet.label.filename": "文件名",
|
||||
"app.lthn.wallet.label.restore-height": "恢复高度",
|
||||
"app.lthn.wallet.label.spend-key": "花费密钥",
|
||||
"app.lthn.wallet.label.view-key": "查看密钥",
|
||||
"app.lthn.wallet.label.wallet-password": "钱包密码",
|
||||
"app.lthn.wallet.label.wallet-password-confirm": "确认密码",
|
||||
"app.lthn.wallet.titles.new-wallet": "创建新钱包",
|
||||
"app.lthn.wallet.titles.restore-keys": "恢复钱包(密钥)",
|
||||
"app.lthn.wallet.titles.restore-seed": "恢复钱包(种子)",
|
||||
"app.lthn.wallet.titles.unlock-wallet": "解锁钱包",
|
||||
"app.lthn.wallet.titles.wallet-transactions": "钱包交易",
|
||||
"app.market.apps": "应用市场",
|
||||
"app.market.dashboard": "仪表板",
|
||||
"app.market.installed": "已安装的应用程序",
|
||||
"app.market.no-apps-installed": "您没有安装任何应用程序。",
|
||||
"app.market.view-installable-apps": "查看可安装的应用程序",
|
||||
"app.title": "Lethean 桌面",
|
||||
"charts.network-hashrate.subtitle": "数据提供者",
|
||||
"charts.network-hashrate.title": "网络哈希率",
|
||||
"lang.de": "德语",
|
||||
"lang.en": "英语",
|
||||
"lang.es": "西班牙语",
|
||||
"lang.fr": "法国",
|
||||
"lang.ru": "俄语",
|
||||
"lang.uk": "乌克兰语(乌克兰)",
|
||||
"lang.zh": "中文",
|
||||
"menu.about": "关于",
|
||||
"menu.activity": "活动",
|
||||
"menu.api": "应用程序接口(API)",
|
||||
"menu.blockchain": "区块链",
|
||||
"menu.build": "建立",
|
||||
"menu.dashboard": "仪表盘",
|
||||
"menu.docs": "文档",
|
||||
"menu.documentation": "文档",
|
||||
"menu.explorer": "资源管理器",
|
||||
"menu.help": "帮助",
|
||||
"menu.hub-admin": "行政",
|
||||
"menu.hub-client": "客户",
|
||||
"menu.hub-developer": "开发人员",
|
||||
"menu.hub-gateway": "网关",
|
||||
"menu.hub-server": "服务器中心",
|
||||
"menu.info": "信息",
|
||||
"menu.logout": "登出",
|
||||
"menu.mining": "矿业",
|
||||
"menu.settings": "设置",
|
||||
"menu.vpn": "虚拟专用网",
|
||||
"menu.wallet": "钱包",
|
||||
"menu.your-profile": "您的个人资料",
|
||||
"view.dashboard.description": "Lethean (LTHN) 网络应用程序",
|
||||
"view.dashboard.heading": "Lethean仪表盘",
|
||||
"view.dashboard.title": "Lethean (LTHN)",
|
||||
"view.wallets.description": "加密钱包管理器",
|
||||
"view.wallets.heading": "钱包管理器",
|
||||
"view.wallets.title": "皮夹",
|
||||
"words.actions.add": "添加",
|
||||
"words.actions.clone": "复制",
|
||||
"words.actions.edit": "编辑",
|
||||
"words.actions.install": "安装",
|
||||
"words.actions.new": "新",
|
||||
"words.actions.remove": "去掉",
|
||||
"words.actions.report": "报告",
|
||||
"words.actions.save": "保存",
|
||||
"words.states.installing": "正在安装",
|
||||
"words.states.installing_desc": "我们正在将区块链可执行文件从 GitHub 下载到您的 Lethean 用户目录。",
|
||||
"words.states.loading": "装载",
|
||||
"words.states.not_installed": "未安装",
|
||||
"words.states.not_installed_desc": "单击安装区块链以下载最新的 Lethean 区块链命令行界面(CLI)",
|
||||
"words.things.button": "按键",
|
||||
"words.things.documentation": "文档",
|
||||
"words.things.menu": "菜单",
|
||||
"words.things.mining-pool": "矿池",
|
||||
"words.things.page": "页面",
|
||||
"words.things.problem": "问题",
|
||||
"words.things.type": "类型",
|
||||
"words.time.past.day": "一天前",
|
||||
"words.time.past.days": "几天前",
|
||||
"words.time.past.hour": "一小时前",
|
||||
"words.time.past.hours": "小时前",
|
||||
"words.time.past.minute": "一分钟前",
|
||||
"words.time.past.minutes": "几分钟前",
|
||||
"words.time.past.month": "一个月前",
|
||||
"words.time.past.months": " 几个月前",
|
||||
"words.time.past.seconds": "几秒钟前",
|
||||
"words.time.past.year": "一年前",
|
||||
"words.time.past.years": "几年前"
|
||||
}
|
||||
3
pkg/i18n/testdata/en.json
vendored
3
pkg/i18n/testdata/en.json
vendored
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"greeting": "Hello"
|
||||
}
|
||||
3
pkg/i18n/testdata/es.json
vendored
3
pkg/i18n/testdata/es.json
vendored
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"greeting": "Hola"
|
||||
}
|
||||
|
|
@ -4,11 +4,8 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Snider/Core/pkg/config"
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/Snider/Core/pkg/crypt"
|
||||
"github.com/Snider/Core/pkg/display"
|
||||
"github.com/Snider/Core/pkg/i18n"
|
||||
"github.com/Snider/Core/pkg/workspace"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
|
@ -18,10 +15,7 @@ import (
|
|||
type Runtime struct {
|
||||
app *application.App
|
||||
Core *core.Core
|
||||
Config *config.Service
|
||||
Display *display.Service
|
||||
Crypt *crypt.Service
|
||||
I18n *i18n.Service
|
||||
Workspace *workspace.Service
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +29,7 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory)
|
|||
core.WithWails(app),
|
||||
}
|
||||
|
||||
for _, name := range []string{"config", "display", "crypt", "i18n", "workspace"} {
|
||||
for _, name := range []string{"crypt", "workspace"} {
|
||||
factory, ok := factories[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("service %s factory not provided", name)
|
||||
|
|
@ -55,22 +49,10 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory)
|
|||
}
|
||||
|
||||
// --- Type Assertions ---
|
||||
configSvc, ok := services["config"].(*config.Service)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("config service has unexpected type")
|
||||
}
|
||||
displaySvc, ok := services["display"].(*display.Service)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("display service has unexpected type")
|
||||
}
|
||||
cryptSvc, ok := services["crypt"].(*crypt.Service)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("crypt service has unexpected type")
|
||||
}
|
||||
i18nSvc, ok := services["i18n"].(*i18n.Service)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("i18n service has unexpected type")
|
||||
}
|
||||
workspaceSvc, ok := services["workspace"].(*workspace.Service)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("workspace service has unexpected type")
|
||||
|
|
@ -79,10 +61,7 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory)
|
|||
rt := &Runtime{
|
||||
app: app,
|
||||
Core: coreInstance,
|
||||
Config: configSvc,
|
||||
Display: displaySvc,
|
||||
Crypt: cryptSvc,
|
||||
I18n: i18nSvc,
|
||||
Workspace: workspaceSvc,
|
||||
}
|
||||
|
||||
|
|
@ -92,10 +71,7 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory)
|
|||
// New creates and wires together all application services.
|
||||
func New(app *application.App) (*Runtime, error) {
|
||||
return NewWithFactories(app, map[string]ServiceFactory{
|
||||
"config": func() (any, error) { return config.New() },
|
||||
"display": func() (any, error) { return display.New() },
|
||||
"crypt": func() (any, error) { return crypt.New() },
|
||||
"i18n": func() (any, error) { return i18n.New() },
|
||||
"workspace": func() (any, error) { return workspace.New() },
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,7 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/Snider/Core/pkg/config"
|
||||
"github.com/Snider/Core/pkg/crypt"
|
||||
"github.com/Snider/Core/pkg/display"
|
||||
"github.com/Snider/Core/pkg/i18n"
|
||||
"github.com/Snider/Core/pkg/runtime"
|
||||
"github.com/Snider/Core/pkg/workspace"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -27,20 +24,14 @@ func TestNew(t *testing.T) {
|
|||
name: "Good path",
|
||||
app: nil,
|
||||
factories: map[string]runtime.ServiceFactory{
|
||||
"config": func() (any, error) { return &config.Service{}, nil },
|
||||
"display": func() (any, error) { return &display.Service{}, nil },
|
||||
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||
},
|
||||
expectErr: false,
|
||||
checkRuntime: func(t *testing.T, rt *runtime.Runtime) {
|
||||
assert.NotNil(t, rt)
|
||||
assert.NotNil(t, rt.Core)
|
||||
assert.NotNil(t, rt.Config)
|
||||
assert.NotNil(t, rt.Display)
|
||||
assert.NotNil(t, rt.Crypt)
|
||||
assert.NotNil(t, rt.I18n)
|
||||
assert.NotNil(t, rt.Workspace)
|
||||
},
|
||||
},
|
||||
|
|
@ -48,10 +39,7 @@ func TestNew(t *testing.T) {
|
|||
name: "Factory returns an error",
|
||||
app: nil,
|
||||
factories: map[string]runtime.ServiceFactory{
|
||||
"config": func() (any, error) { return &config.Service{}, nil },
|
||||
"display": func() (any, error) { return &display.Service{}, nil },
|
||||
"crypt": func() (any, error) { return nil, errors.New("crypt service failed") },
|
||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||
},
|
||||
expectErr: true,
|
||||
|
|
@ -61,23 +49,17 @@ func TestNew(t *testing.T) {
|
|||
name: "Factory returns wrong type",
|
||||
app: nil,
|
||||
factories: map[string]runtime.ServiceFactory{
|
||||
"config": func() (any, error) { return &config.Service{}, nil },
|
||||
"display": func() (any, error) { return "not a display service", nil },
|
||||
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||
"crypt": func() (any, error) { return "not a crypt service", nil },
|
||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||
},
|
||||
expectErr: true,
|
||||
expectErrStr: "display service has unexpected type",
|
||||
expectErrStr: "crypt service has unexpected type",
|
||||
},
|
||||
{
|
||||
name: "With non-nil app",
|
||||
app: &application.App{},
|
||||
factories: map[string]runtime.ServiceFactory{
|
||||
"config": func() (any, error) { return &config.Service{}, nil },
|
||||
"display": func() (any, error) { return &display.Service{}, nil },
|
||||
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||
},
|
||||
expectErr: false,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue