Compare commits

...

49 Commits

Author SHA1 Message Date
Syncthing Release Automation
a162e8d9f9 gui, man, authors: Update docs, translations, and contributors 2022-05-09 03:58:03 +00:00
André Colomb
235422c26d gui, authors: Remove additional bot names (#8333)
Skip Syncthing*Automation in authors list as well.
2022-05-08 22:42:05 +02:00
André Colomb
2bcaa17fc3 gui: Add default ignores to the advanced configuration editor modal (fixes #8264) (#8265) 2022-05-08 21:34:03 +02:00
greatroar
97291c9184 lib/api: Fix and optimize csrfManager (#8329)
An off-by-one error could cause tokens to be forgotten. Suppose

	tokens := []string{"foo", "bar", "baz", "quux"}
	i := 2
	token := tokens[i] // token == "baz"

Then, after

	copy(tokens[1:], tokens[:i+1])
	tokens[0] = token

we have

	tokens == []string{"baz", "foo", "bar", "baz"}

The short test actually relied on this bug.
2022-05-07 12:30:13 +02:00
Jakob Borg
520ca4bcb0 script, gui: Exclude bots from the in-GUI authors list 2022-05-06 08:00:22 +02:00
Simon Frei
f35fb974d0 build: Add concise build instructions to readme (#8327) 2022-05-06 07:56:26 +02:00
Jakob Borg
f8c51d801a lib/discover: Filter locally announced addresses (fixes #7289) (#8302) 2022-05-04 18:43:00 +02:00
Jakob Borg
16c0c2f7a7 Merge branch 'release'
* release:
  lib/connections: Correct race on loop variable (fixes #8320) (#8321)
2022-05-04 18:25:58 +02:00
Jakob Borg
2145b3701d lib/connections: Correct race on loop variable (fixes #8320) (#8321) 2022-05-04 18:17:03 +02:00
Jakob Borg
ce0ded7c78 lib/connections: Correct race on loop variable (fixes #8320) (#8321) 2022-05-04 18:16:36 +02:00
André Colomb
31a78592e8 gui: Mark folders paused on remote device. (#8286)
Similar to the "remote has not accepted sharing" message, add a
footnote number 2 to indicate a folder which will not sync with a
certain device because the remote has paused it.  Applies to the edit
folder / device modals' sharing tab, as well as the "shared with"
listing and tooltips under device and folder details.
2022-05-03 21:51:09 +02:00
Jakob Borg
334a78f185 cmd/strelaysrv, cmd/strelaypoolsrv: Sanitize query strings (fixes #8314) (#8315)
Use the proper encoding function in the relay server when constructing
the URL. In the pool server, parse and re-encode the query values to
sanitize whatever the client sent.
2022-05-02 10:38:49 +02:00
greatroar
233d3e7f7b lib/events: Remove unused method noopLogger.Stop (#8312)
This was needed for the old suture API, abandoned in
9524b51708.
2022-05-02 08:00:55 +02:00
Syncthing Release Automation
41a429b52c gui, man, authors: Update docs, translations, and contributors 2022-05-02 04:07:12 +00:00
greatroar
d00a30069a lib/db: Constant/unused args and return values, double it.Release (#8259) 2022-04-27 20:32:44 +02:00
greatroar
49488c0e71 all: Clean up fmt.Errorf usage (#8309) 2022-04-27 20:30:13 +02:00
Simon Frei
4031568cdf gui: Bandaid for null http errors (fixes #8261) (#8305) 2022-04-25 19:15:14 +02:00
Syncthing Release Automation
fff9bf98eb gui, man, authors: Update docs, translations, and contributors 2022-04-25 03:54:44 +00:00
Simon Frei
6a7fc49c6b lib/discover: Increase global discovery timeout (#8303) 2022-04-23 16:12:25 +02:00
red_led
89f5d0d400 gui: Always show vertical scroll bar (#8301)
This stops interface from jumping left and right when page content no longer fits into screen.
2022-04-22 21:39:30 +02:00
André Colomb
1eda82b95f lib/model: Improve remoteFolderState reporting (fixes #8266) (#8283) 2022-04-22 08:42:20 +02:00
Jakob Borg
623ec03dad lib/model: Correct type of event data (fixes #8294) (#8295)
These things are fragile, every event should use an ${eventType}Data struct or something instead.
2022-04-21 15:45:31 +02:00
Jakob Borg
c0de42e3df gui: Use neutral color for zero out of zero listeners (#8281) 2022-04-20 14:43:11 +02:00
tomasz1986
4893513800 gui: Improve Latest Change translation string for better multilanguage support (#8290)
Currently, the "Latest Change" translation string is hard-coded to use
the English-like word order of V ("deleted") + N ("file"). As such, it
is incompatible with languages that require to use a different word
order, e.g. Korean or Japanese. In other words, a proper translation of
the string to those languages is currently impossible.

This commit changes the translation string, so that it includes the file
variable, and thanks to this, it can be easily adopted to languages with
different word order than English.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2022-04-18 06:41:26 +02:00
Syncthing Release Automation
100142067d gui, man, authors: Update docs, translations, and contributors 2022-04-18 03:54:08 +00:00
Simon Frei
3907cb0693 lib/model: Subscribe to correct event for fs watching (ref #8249) (#8287) 2022-04-17 12:41:25 +04:00
Jakob Borg
61dffabf97 cmd/syncthing, lib/logger: Add date to default log format (fixes #8272) (#8273)
This changes the default log format to include the date.
2022-04-15 07:46:14 +04:00
Eng Zer Jun
bc27aa12cd all: use T.TempDir to create temporary test directory (#8280)
This commit replaces `os.MkdirTemp` with `t.TempDir` in tests. The
directory created by `t.TempDir` is automatically removed when the test
and all its subtests complete.

Prior to this commit, temporary directory created using `os.MkdirTemp`
needs to be removed manually by calling `os.RemoveAll`, which is omitted
in some tests. The error handling boilerplate e.g.
	defer func() {
		if err := os.RemoveAll(dir); err != nil {
			t.Fatal(err)
		}
	}
is also tedious, but `t.TempDir` handles this for us nicely.

Reference: https://pkg.go.dev/testing#T.TempDir
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2022-04-15 07:44:06 +04:00
André Colomb
0537b9546f lib/model: Switch the remoteFolderState default value to valid (#8275)
Showing all folders from disconnected or paused remote devices as
unaccepted would be a lot of false positives.  As we cannot know
whether the remote has accepted while it doesn't have an active
connection, let's better report false negatives, as in assuming the
folders are accepted.
2022-04-13 18:15:58 +02:00
Simon Frei
0525c755f4 build: Bump quic-go to 0.26.0 for go1.18 update (#8231)
Merging because I want this in the RC, when we do the RC...
2022-04-12 16:27:29 +04:00
Simon Frei
bcd91f536e lib/connections: Create the forgotten channel (ref #8263) (#8267) 2022-04-11 17:32:22 +04:00
Syncthing Release Automation
f9c6c69fa8 gui, man, authors: Update docs, translations, and contributors 2022-04-11 03:52:11 +00:00
André Colomb
0c46e0a9cc gui, lib/model: Mark folders unaccepted by remote device (fixes #8202) (#8201) 2022-04-10 22:47:57 +02:00
Greg
bca6d31b95 Correct comment typo in build.go (#8234) 2022-04-10 22:24:57 +02:00
Simon Frei
db72579f0e lib: Get rid of buggy filesystem wrapping (#8257) 2022-04-10 20:55:05 +02:00
Jakob Borg
9b09bcc5f1 lib/connections: Always run a simple connection test (#7866) 2022-04-10 20:54:42 +02:00
Simon Frei
22e12904c9 lib/connections: Make request tests sequential (#8263) 2022-04-10 20:54:16 +02:00
Simon Frei
b947056e62 lib: Removal global connection registry (#8254) 2022-04-09 16:04:56 +02:00
Jeffery To
e30898ddb3 build: Update go directive to 1.17 (fixes #8258) (#8260)
This should fix compiling from the source tarball with Go 1.18.
2022-04-09 16:01:24 +02:00
Simon Frei
072fa46bfd lib/connections, lib/model: Improve new conn handling (#8253) 2022-04-07 17:35:33 +02:00
Simon Frei
edc3a77b98 lib/fs, lib/model: Add warning about kqueue resource usage (fixes #7855) (#8249) 2022-04-05 21:32:06 +02:00
Syncthing Release Automation
2b80848341 gui, man, authors: Update docs, translations, and contributors 2022-04-04 03:50:52 +00:00
André Colomb
30fa462e33 all: Comments and cosmetics (#8218)
* lib/api: Note ItemStarted and ItemFinished for default filtering.

The reasoning why LocalChangeDetected and RemoteChangeDetected events
are not included in the event stream by default (without explicit
filter mask requested) also holds for the ItemStarted and ItemFinished
events.  They should be excluded as well when we start to break the
API compatibility for some reason.

* gui: Enumerate unused event types in the eventService.

Define constants for the unused event types as well, for completeness'
sake.  They are intentionally not handled in the GUI currently.

* cmd/syncthing: Harmonize uppercase CLI argument placeholders.

Use ALL-UPPERCASE and connecting dashes to distinguish argument
placeholders from literal argument options (e.g. "cpu" or "heap" for
profiling).  The dash makes it clear which words form a single
argument and where a new argument starts.

This style is already used for the "syncthing cli debug file" command.

* lib/model: Simplify event data structure.

Using map[string]interface{} is not necessary when all values are
known to be strings.
2022-04-02 20:36:19 +02:00
Syncthing Release Automation
11ac945b87 gui, man, authors: Update docs, translations, and contributors 2022-03-28 03:50:34 +00:00
Simon Frei
55c513b827 lib/model: Clear folder error after loading ignores (fixes #8232) (#8238) 2022-03-27 21:29:40 +02:00
greatroar
0eca0ac45a lib/model: Chmod to mode|0700, not 755 (#8235) 2022-03-26 12:07:07 +02:00
greatroar
4be867c560 all: Replace errors.Cause with errors.Is (#8236) 2022-03-26 12:05:57 +02:00
Naveen
44b11ec257 Pin actions to a full length commit SHA (#8224) 2022-03-24 16:46:06 +01:00
Greg
53926a1ae6 lib/fs: Do not follow symlinks in watcher on solaris (fixes #8020) (#8223) 2022-03-24 08:36:43 +01:00
156 changed files with 1654 additions and 1473 deletions

View File

@@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
name: Update translations and documentation
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
with:
fetch-depth: 0
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
- uses: actions/setup-go@v2
- uses: actions/setup-go@bfdd3570ce990073878bf10f6b2d79082de49492 # v2
with:
go-version: ^1.17.6
- run: |

View File

@@ -96,6 +96,7 @@ Dominik Heidler (asdil12) <dominik@heidler.eu>
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
Elliot Huffman <thelich2@gmail.com>
Emil Hessman (ceh) <emil@hessman.se>
Eng Zer Jun <engzerjun@gmail.com>
Eric Lesiuta <elesiuta@gmail.com>
Erik Meitner (WSGCSysadmin) <e.meitner@willystreet.coop>
Evgeny Kuznetsov <evgeny@kuznetsov.md>
@@ -112,6 +113,7 @@ Gilli Sigurdsson (gillisig) <gilli@vx.is>
Gleb Sinyavskiy <zhulik.gleb@gmail.com>
Graham Miln (grahammiln) <graham.miln@dssw.co.uk> <graham.miln@miln.eu>
greatroar <61184462+greatroar@users.noreply.github.com>
Greg <gco@jazzhaiku.com>
Han Boetes <han@boetes.org>
HansK-p <42314815+HansK-p@users.noreply.github.com>
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
@@ -135,6 +137,7 @@ Jaroslav Lichtblau <svetlemodry@users.noreply.github.com>
Jaroslav Malec (dzarda) <dzardacz@gmail.com>
jaseg <githubaccount@jaseg.net>
Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
Jeffery To <jeffery.to@gmail.com>
jelle van der Waa <jelle@vdwaa.nl>
Jens Diemer (jedie) <github.com@jensdiemer.de> <git@jensdiemer.de>
Jerry Jacobs (xor-gate) <jerry.jacobs@xor-gate.org> <xor-gate@users.noreply.github.com>
@@ -202,6 +205,7 @@ MikolajTwarog <43782609+MikolajTwarog@users.noreply.github.com>
Mingxuan Lin <gdlmx@users.noreply.github.com>
mv1005 <49659413+mv1005@users.noreply.github.com>
Nate Morrison (nrm21) <natemorrison@gmail.com>
Naveen <172697+naveensrinivasan@users.noreply.github.com>
Nicholas Rishel (PrototypeNM1) <rishel.nick@gmail.com> <PrototypeNM1@users.noreply.github.com>
Nico Stapelbroek <3368018+nstapelbroek@users.noreply.github.com>
Nicolas Braud-Santoni <nicolas@braud-santoni.eu>
@@ -235,6 +239,7 @@ Piotr Bejda (piobpl) <piotrb10@gmail.com>
Pramodh KP (pramodhkp) <pramodh.p@directi.com> <1507241+pramodhkp@users.noreply.github.com>
Quentin Hibon <qh.public@yahoo.com>
Rahmi Pruitt <rjpruitt16@gmail.com>
red_led <red-led@users.noreply.github.com>
Richard Hartmann <RichiH@users.noreply.github.com>
Robert Carosi (nov1n) <robert@carosi.nl>
Roberto Santalla <roobre@users.noreply.github.com>

View File

@@ -79,8 +79,10 @@ bug, feel free to report it in the [GitHub issue tracker][10].
## Building
Building Syncthing from source is easy, and there's [a guide][5]
that describes it for both Unix and Windows systems.
Building Syncthing from source is easy. After extracting the source bundle from
a release or checking out git, you just need to run `go run build.go` and the
binaries are created in `./bin`. There's [a guide][5] with more details on the
build process.
## Signed Releases
@@ -112,4 +114,4 @@ All code is licensed under the [MPLv2 License][7].
[14]: assets/logo-text-128.png
[15]: https://syncthing.net/
[16]: https://github.com/syncthing/syncthing/blob/main/README-Docker.md
[17]: https://github.com/syncthing/docs
[17]: https://github.com/syncthing/docs

View File

@@ -883,7 +883,7 @@ func shouldRebuildAssets(target, srcdir string) bool {
func updateDependencies() {
runPrint(goCmd, "get", "-u", "./cmd/...")
runPrint(goCmd, "mod", "tidy", "-go=1.16", "-compat=1.16")
runPrint(goCmd, "mod", "tidy", "-go=1.17", "-compat=1.17")
// We might have updated the protobuf package and should regenerate to match.
proto()
@@ -985,7 +985,7 @@ func getGitVersion() (string, error) {
v0 := string(bs)
// To be more semantic-versionish and ensure proper ordering in our
// upgrade process, we make sure there's only one hypen in the version.
// upgrade process, we make sure there's only one hyphen in the version.
versionRe := regexp.MustCompile(`-([0-9]{1,3}-g[0-9a-f]{5,10}(-dirty)?)`)
if m := versionRe.FindStringSubmatch(vcur); len(m) > 0 {

View File

@@ -84,7 +84,7 @@ func checkServers(deviceID protocol.DeviceID, servers ...string) {
}
func checkServer(deviceID protocol.DeviceID, server string) checkResult {
disco, err := discover.NewGlobal(server, tls.Certificate{}, nil, events.NoopLogger)
disco, err := discover.NewGlobal(server, tls.Certificate{}, nil, events.NoopLogger, nil)
if err != nil {
return checkResult{error: err}
}

View File

@@ -368,6 +368,11 @@ func handlePostRequest(w http.ResponseWriter, r *http.Request) {
return
}
// Canonicalize the URL. In particular, parse and re-encode the query
// string so that it's guaranteed to be valid.
uri.RawQuery = uri.Query().Encode()
newRelay.URL = uri.String()
if relayCert != nil {
advertisedId := uri.Query().Get("id")
idFromCert := protocol.NewDeviceID(relayCert.Raw).String()

View File

@@ -11,6 +11,7 @@ import (
"encoding/json"
"fmt"
"net/http/httptest"
"net/url"
"strings"
"sync"
"testing"
@@ -65,3 +66,29 @@ func TestHandleGetRequest(t *testing.T) {
}
}
}
func TestCanonicalizeQueryValues(t *testing.T) {
// This just demonstrates and validates the uri.Parse/String stuff in
// regards to query strings.
in := "http://example.com/?some weird= query^value"
exp := "http://example.com/?some+weird=+query%5Evalue"
uri, err := url.Parse(in)
if err != nil {
t.Fatal(err)
}
str := uri.String()
if str != in {
// Just re-encoding the URL doesn't sanitize the query string.
t.Errorf("expected %q, got %q", in, str)
}
uri.RawQuery = uri.Query().Encode()
str = uri.String()
if str != exp {
// The query string is now in correct format.
t.Errorf("expected %q, got %q", exp, str)
}
}

View File

@@ -230,12 +230,31 @@ func main() {
go statusService(statusAddr)
}
uri, err := url.Parse(fmt.Sprintf("relay://%s/?id=%s&pingInterval=%s&networkTimeout=%s&sessionLimitBps=%d&globalLimitBps=%d&statusAddr=%s&providedBy=%s", mapping.Address(), id, pingInterval, networkTimeout, sessionLimitBps, globalLimitBps, statusAddr, providedBy))
uri, err := url.Parse(fmt.Sprintf("relay://%s/", mapping.Address()))
if err != nil {
log.Fatalln("Failed to construct URI", err)
return
}
// Add properly encoded query string parameters to URL.
query := make(url.Values)
query.Set("id", id.String())
query.Set("pingInterval", pingInterval.String())
query.Set("networkTimeout", networkTimeout.String())
if sessionLimitBps > 0 {
query.Set("sessionLimitBps", fmt.Sprint(sessionLimitBps))
}
if globalLimitBps > 0 {
query.Set("globalLimitBps", fmt.Sprint(globalLimitBps))
}
if statusAddr != "" {
query.Set("statusAddr", statusAddr)
}
if providedBy != "" {
query.Set("providedBy", providedBy)
}
uri.RawQuery = query.Encode()
log.Println("URI:", uri.String())
if poolAddrs == defaultPoolAddrs {

View File

@@ -27,7 +27,7 @@ var errorsCommand = cli.Command{
{
Name: "push",
Usage: "Push an error to active clients",
ArgsUsage: "[error message]",
ArgsUsage: "ERROR-MESSAGE",
Action: expects(1, errorsPush),
},
{

View File

@@ -8,6 +8,7 @@ package cli
import (
"bufio"
"errors"
"fmt"
"path/filepath"
@@ -39,13 +40,13 @@ var operationCommand = cli.Command{
{
Name: "folder-override",
Usage: "Override changes on folder (remote for sendonly, local for receiveonly). WARNING: Destructive - deletes/changes your data.",
ArgsUsage: "[folder id]",
ArgsUsage: "FOLDER-ID",
Action: expects(1, foldersOverride),
},
{
Name: "default-ignores",
Usage: "Set the default ignores (config) from a file",
ArgsUsage: "path",
ArgsUsage: "PATH",
Action: expects(1, setDefaultIgnores),
},
},
@@ -77,12 +78,12 @@ func foldersOverride(c *cli.Context) error {
if body != "" {
errStr += "\nBody: " + body
}
return fmt.Errorf(errStr)
return errors.New(errStr)
}
return nil
}
}
return fmt.Errorf("Folder " + rid + " not found")
return fmt.Errorf("Folder %q not found", rid)
}
func setDefaultIgnores(c *cli.Context) error {

View File

@@ -10,6 +10,7 @@ package decrypt
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"log"
@@ -45,7 +46,7 @@ func (c *CLI) Run() error {
log.SetFlags(0)
if c.To == "" && !c.VerifyOnly {
return fmt.Errorf("must set --to or --verify-only")
return errors.New("must set --to or --verify-only")
}
if c.TokenPath == "" {
@@ -138,7 +139,7 @@ func (c *CLI) process(srcFs fs.Filesystem, dstFs fs.Filesystem, path string) err
}
defer encFd.Close()
encFi, err := c.loadEncryptedFileInfo(encFd)
encFi, err := loadEncryptedFileInfo(encFd)
if err != nil {
return fmt.Errorf("%s: loading metadata trailer: %w", path, err)
}
@@ -246,7 +247,7 @@ func (c *CLI) decryptFile(encFi *protocol.FileInfo, plainFi *protocol.FileInfo,
// loadEncryptedFileInfo loads the encrypted FileInfo trailer from a file on
// disk.
func (c *CLI) loadEncryptedFileInfo(fd fs.File) (*protocol.FileInfo, error) {
func loadEncryptedFileInfo(fd fs.File) (*protocol.FileInfo, error) {
// Seek to the size of the trailer block
if _, err := fd.Seek(-4, io.SeekEnd); err != nil {
return nil, err

View File

@@ -11,6 +11,7 @@ import (
"bufio"
"context"
"crypto/tls"
"errors"
"fmt"
"log"
"os"
@@ -40,7 +41,7 @@ func (c *CLI) Run() error {
if c.HomeDir != "" {
if c.ConfDir != "" {
return fmt.Errorf("--home must not be used together with --config")
return errors.New("--home must not be used together with --config")
}
c.ConfDir = c.HomeDir
}

View File

@@ -68,9 +68,9 @@ The --logflags value is a sum of the following:
8 Long filename
16 Short filename
I.e. to prefix each log line with date and time, set --logflags=3 (1 + 2 from
above). The value 0 is used to disable all of the above. The default is to
show time only (2).
I.e. to prefix each log line with time and filename, set --logflags=18 (2 + 16
from above). The value 0 is used to disable all of the above. The default is
to show date and time (3).
Logging always happens to the command line (stdout) and optionally to the
file at the path specified by --logfile=path. In addition to an path, the special
@@ -187,7 +187,7 @@ type serveOptions struct {
func defaultVars() kong.Vars {
vars := kong.Vars{}
vars["logFlags"] = strconv.Itoa(log.Ltime)
vars["logFlags"] = strconv.Itoa(logger.DefaultFlags)
vars["logMaxSize"] = strconv.Itoa(10 << 20) // 10 MiB
vars["logMaxFiles"] = "3" // plus the current one

View File

@@ -17,14 +17,17 @@ import (
func TestRotatedFile(t *testing.T) {
// Verify that log rotation happens.
dir, err := os.MkdirTemp("", "syncthing")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
dir := t.TempDir()
open := func(name string) (io.WriteCloser, error) {
return os.Create(name)
f, err := os.Create(name)
t.Cleanup(func() {
if f != nil {
_ = f.Close()
}
})
return f, err
}
logName := filepath.Join(dir, "log.txt")

29
go.mod
View File

@@ -28,7 +28,7 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/lib/pq v1.10.3
github.com/lucas-clemente/quic-go v0.25.0
github.com/lucas-clemente/quic-go v0.27.0
github.com/maruel/panicparse v1.6.1
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0
github.com/minio/sha256-simd v1.0.0
@@ -53,8 +53,31 @@ require (
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c
golang.org/x/text v0.3.7
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
golang.org/x/tools v0.1.6
golang.org/x/tools v0.1.7
google.golang.org/protobuf v1.27.1
)
go 1.16
require (
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/oschwald/maxminddb-golang v1.8.0 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
go 1.17

25
go.sum
View File

@@ -240,21 +240,24 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucas-clemente/quic-go v0.22.0/go.mod h1:vF5M1XqhBAHgbjKcJOXY3JZz3GP0T3FQhz/uyOUS38Q=
github.com/lucas-clemente/quic-go v0.25.0 h1:K+X9Gvd7JXsOHtU0N2icZ2Nw3rx82uBej3mP4CLgibc=
github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
github.com/lucas-clemente/quic-go v0.26.0 h1:ALBQXr9UJ8A1LyzvceX4jd9QFsHvlI0RR6BkV16o00A=
github.com/lucas-clemente/quic-go v0.26.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
github.com/lucas-clemente/quic-go v0.27.0 h1:v6WY87q9zD4dKASbG8hy/LpzAVNzEQzw8sEIeloJsc4=
github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-15 v0.1.5/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc=
github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y=
github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/maruel/panicparse v1.6.1 h1:803MjBzGcUgE1vYgg3UMNq3G1oyYeKkMu3t6hBS97x0=
github.com/maruel/panicparse v1.6.1/go.mod h1:uoxI4w9gJL6XahaYPMq/z9uadrdr1SyHuQwV2q80Mm0=
github.com/maruel/panicparse/v2 v2.1.1/go.mod h1:AeTWdCE4lcq8OKsLb6cHSj1RWHVSnV9HBCk7sKLF4Jg=
@@ -337,7 +340,6 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -407,7 +409,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
@@ -507,7 +508,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167 h1:eDd+TJqbgfXruGQ5sJRU7tEtp/58OAx4+Ayjxg4SM+4=
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -585,7 +585,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -652,8 +651,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6 h1:SIasE1FVIQOWz2GEAHFOmoW7xchJcqlucjSULTL0Ag4=
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -10,6 +10,7 @@
body {
padding-bottom: 70px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
overflow-y: scroll;
}
h1, h2, h3, h4, h5 {

View File

@@ -75,13 +75,14 @@
"Default Device": "Устройство по подразбиране",
"Default Folder": "Папка по подразбиране",
"Default Folder Path": "Път до папка по подразбиране",
"Default Ignore Patterns": "Подразбирани шаблони за пренебрегване",
"Defaults": "Подразбирани",
"Delete": "Изтрий",
"Delete Unexpected Items": "Премахване на неочакваните",
"Deleted": "Изтрито",
"Deleted {%file%}": "{{file}} премахнат",
"Deselect All": "Изчистване",
"Deselect devices to stop sharing this folder with.": "Махнете отметката от устройство, за да спрете споделяне с него.",
"Deselect folders to stop sharing with this device.": "Махнете отметката от папка, за да спрете споделяне устройствто.",
"Deselect folders to stop sharing with this device.": "Махнете отметката пред папката, за да спрете споделянето ѝ с устройството.",
"Device": "Устройство",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство \"{{name}}\" ({{device}}) с адрес {{address}} желае да се свърже. Да бъде ли добавено?",
"Device ID": "Идентификатор на устройство",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Броят версии трябва да бъде число и не може да бъде празно.",
"The path cannot be blank.": "Пътят не може да бъде празен.",
"The rate limit must be a non-negative number (0: no limit)": "Ограничението на скоростта трябва да бъде положително число (0: неограничено)",
"The remote device has not accepted sharing this folder.": "Отдалеченото устройство не е приело да споделя папката.",
"The rescan interval must be a non-negative number of seconds.": "Интервалът на обхождане трябва да е положителен брой секунди.",
"There are no devices to share this folder with.": "Няма устройства, с които да споделите папката.",
"There are no file versions to restore.": "Файлът няма версии, които да бъдат възстановени.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Несподелени папки",
"Untrusted": "Недоверено",
"Up to Date": "Синхронизирано",
"Updated": "Обновено",
"Updated {%file%}": "{{file}} обновен",
"Upgrade": "Обновяване",
"Upgrade To {%version%}": "Обновяване до {{version}}",
"Upgrading": "Обновяване",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Carpeta de la Ruta per Defecte",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Esborrar",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Esborrat",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Anul·lar tota la selecció",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "El nombre de versions deu ser un nombre i no pot estar buit.",
"The path cannot be blank.": "La ruta no pot estar buida.",
"The rate limit must be a non-negative number (0: no limit)": "El llímit del ritme deu ser un nombre no negatiu (0: sense llímit)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "L'interval de reescaneig deu ser un nombre positiu de segons.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Actualitzat",
"Updated": "Actualitzat",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Actualitzar",
"Upgrade To {%version%}": "Actualitzar a {{version}}",
"Upgrading": "Actualitzant",

View File

@@ -75,10 +75,11 @@
"Default Device": "Výchozí zařízení",
"Default Folder": "Výchozí složka",
"Default Folder Path": "Popis umístění výchozí složky",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Výchozí hodnoty",
"Delete": "Smazat",
"Delete Unexpected Items": "Smazat neočekávané položky",
"Deleted": "Smazáno",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Zrušit výběr všeho",
"Deselect devices to stop sharing this folder with.": "Zrušte výběr zařízení, se kterými již nemá být tato složka sdílena.",
"Deselect folders to stop sharing with this device.": "Zrušte výběr složek, které se mají přestat sdílet s tímto zařízením.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Je třeba, aby počet verzí bylo číslo a nemůže zůstat nevyplněné.",
"The path cannot be blank.": "Popis umístění nemůže zůstat nevyplněný.",
"The rate limit must be a non-negative number (0: no limit)": "Je třeba, aby limit rychlosti bylo kladné číslo (0: bez limitu)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Je třeba, aby interval opakování skenování bylo kladné číslo.",
"There are no devices to share this folder with.": "Nejsou žádná zařízení, se kterými lze sdílet tuto složku.",
"There are no file versions to restore.": "Žádné verze souboru k obnovení.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Nesdílené složky",
"Untrusted": "Bez důvěry",
"Up to Date": "Aktuální",
"Updated": "Aktualizováno",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Přechod na novější verzi",
"Upgrade To {%version%}": "Aktualizovat na {{version}}",
"Upgrading": "Aktualizuje se",

View File

@@ -28,7 +28,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "En ekstern kommando styrer versioneringen. Den skal fjerne filen fra den delte mappe. Hvis stien til programmet indeholder mellemrum, bør den sættes i anførselstegn.",
"Anonymous Usage Reporting": "Anonym brugerstatistik",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formatet for anonym brugerstatistik er ændret. Vil du flytte til det nye format?",
"Apply": "Apply",
"Apply": "Anvend",
"Are you sure you want to continue?": "Fortsætte?",
"Are you sure you want to override all remote changes?": "Tilsidesæt alle eksterne ændringer?",
"Are you sure you want to permanently delete all these files?": "Slette valgte filer permanent?",
@@ -75,10 +75,11 @@
"Default Device": "Standard enhed",
"Default Folder": "Standard mappe",
"Default Folder Path": "Standardmappesti",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Standarder",
"Delete": "Slet",
"Delete Unexpected Items": "Slet ikke forventede elementer ",
"Deleted": "Slettet",
"Deleted {%file%}": "Slettet {{file}}",
"Deselect All": "Fravælg alle",
"Deselect devices to stop sharing this folder with.": "Fravælg enheder for at stoppe mappe deling.",
"Deselect folders to stop sharing with this device.": "Fravælg mapper for at stoppe deling med denne enhed.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Antallet af versioner skal være et tal og feltet må ikke være tomt.",
"The path cannot be blank.": "Stien må ikke være tom.",
"The rate limit must be a non-negative number (0: no limit)": "Hastighedsbegrænsningen skal være et ikke-negativt tal (0: ingen begrænsning)",
"The remote device has not accepted sharing this folder.": "Fjern enhed har ikke accepteret deling af denne mappe. ",
"The rescan interval must be a non-negative number of seconds.": "Genskanningsintervallet skal være et ikke-negativt antal sekunder.",
"There are no devices to share this folder with.": "Der er ingen enheder at dele denne mappe med.",
"There are no file versions to restore.": "Der er ingen fil versioner at gendanne.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Ikke delte mapper",
"Untrusted": "Ikke troværdig",
"Up to Date": "Fuldt opdateret",
"Updated": "Opdateret",
"Updated {%file%}": "Opdateret {{file}}",
"Upgrade": "Opgradér",
"Upgrade To {%version%}": "Opgradér til {{version}}",
"Upgrading": "Opgraderer",

View File

@@ -75,10 +75,11 @@
"Default Device": "Vorgabegerät",
"Default Folder": "Vorgabeordner",
"Default Folder Path": "Standardmäßiger Ordnerpfad",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Vorgaben",
"Delete": "Löschen",
"Delete Unexpected Items": "Unerwartete Elemente löschen",
"Deleted": "Gelöscht",
"Deleted {%file%}": "{{file}} gelöscht",
"Deselect All": "Alle abwählen",
"Deselect devices to stop sharing this folder with.": "Geräte abwählen, um diesen Ordner nicht mehr damit zu teilen.",
"Deselect folders to stop sharing with this device.": "Ordner abwählen, um sie nicht mehr für mit diesem Gerät zu teilen.",
@@ -332,7 +333,7 @@
"Start Browser": "Browser starten",
"Statistics": "Statistiken",
"Stopped": "Gestoppt",
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Speichert und synchronisiert nur verschlüsselte Daten. Ordner auf allen verbundenen Geräten müssen mit dem selben Passwort eingerichtet werden oder vom Typ \"{{receiveEncrypted}}\" sein.",
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Speichert und synchronisiert nur verschlüsselte Daten. Ordner auf allen verbundenen Geräten müssen mit dem selben Passwort eingerichtet werden oder vom Typ \"{{receiveEncrypted}}\" sein.",
"Support": "Support",
"Support Bundle": "Supportpaket",
"Sync Protocol Listen Addresses": "Adresse(n) für das Synchronisierungsprotokoll",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Die Anzahl von Versionen muss eine Ganzzahl und darf nicht leer sein.",
"The path cannot be blank.": "Der Pfad darf nicht leer sein.",
"The rate limit must be a non-negative number (0: no limit)": "Das Datenratelimit muss eine nicht negative Zahl sein (0 = kein Limit).",
"The remote device has not accepted sharing this folder.": "Dieser geteilte Ordner wurde vom Gerät nicht angenommen.",
"The rescan interval must be a non-negative number of seconds.": "Das Scanintervall muss eine nicht negative Anzahl (in Sekunden) sein.",
"There are no devices to share this folder with.": "Es gibt keine Geräte, mit denen dieser Ordner geteilt werden kann.",
"There are no file versions to restore.": "Es gibt keine Dateiversionen zum Wiederherstellen.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Nicht geteilte Ordner",
"Untrusted": "Nicht vertraut",
"Up to Date": "Aktuell",
"Updated": "Aktualisiert",
"Updated {%file%}": "{{file}} aktualisiert",
"Upgrade": "Aktualisierung",
"Upgrade To {%version%}": "Aktualisierung auf {{version}}",
"Upgrading": "Wird aktualisiert",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Προκαθορισμένη διαδρομή φακέλων",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Διαγραφή",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Διαγραμμένα",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Αποεπιλογή όλων",
"Deselect devices to stop sharing this folder with.": "Αποεπιλέξτε συσκευές για να σταματήσει ο διαμοιρασμός του φακέλου με αυτές.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Ο αριθμός εκδόσεων πρέπει να είναι αριθμός και σίγουρα όχι κενό.",
"The path cannot be blank.": "Το μονοπάτι δεν μπορεί να είναι κενό.",
"The rate limit must be a non-negative number (0: no limit)": "Το όριο ταχύτητας πρέπει να είναι ένας μη-αρνητικός αριθμός (0: χωρίς όριο)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Ο χρόνος επανελέγχου για αλλαγές είναι σε δευτερόλεπτα (δηλ. θετικός αριθμός).",
"There are no devices to share this folder with.": "Δεν υπάρχουν συσκευές με τις οποίες διαμοιράζεται αυτός ο φάκελος.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Ενημερωμένη",
"Updated": "Ενημερωμένο",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Αναβάθμιση",
"Upgrade To {%version%}": "Αναβάθμιση στην έκδοση {{version}}",
"Upgrading": "Αναβάθμιση",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Default Folder Path",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Delete",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Deleted",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
"The path cannot be blank.": "The path cannot be blank.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Up to Date",
"Updated": "Updated",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Upgrade",
"Upgrade To {%version%}": "Upgrade To {{version}}",
"Upgrading": "Upgrading",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Default Folder Path",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Delete",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Deleted",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
"The path cannot be blank.": "The path cannot be blank.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Up to Date",
"Updated": "Updated",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Upgrade",
"Upgrade To {%version%}": "Upgrade to {{version}}",
"Upgrading": "Upgrading",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Default Folder Path",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Delete",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Deleted",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,8 @@
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
"The path cannot be blank.": "The path cannot be blank.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The remote device has paused this folder.": "The remote device has paused this folder.",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +411,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Up to Date",
"Updated": "Updated",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Upgrade",
"Upgrade To {%version%}": "Upgrade To {{version}}",
"Upgrading": "Upgrading",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Defaŭlta Dosierujo",
"Default Folder Path": "Defaŭlta Dosieruja Vojo",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Forigu",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Forigita",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Malelekti Ĉiujn",
"Deselect devices to stop sharing this folder with.": "Malelekti aparatojn por ĉesi komunigi tiun ĉi dosierujon kun ili.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "La nombro da versioj devas esti nombro kaj ne povas esti malplena.",
"The path cannot be blank.": "La vojo ne povas esti malplena.",
"The rate limit must be a non-negative number (0: no limit)": "La rapideca limo devas esti pozitiva nombro (0: senlimo)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "La intervalo de reskano devas esti pozitiva nombro da sekundoj.",
"There are no devices to share this folder with.": "Estas neniu aparato kun kiu komunigi tiun ĉi dosierujon.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Ĝisdata",
"Updated": "Ĝisdatigita",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Altgradigo",
"Upgrade To {%version%}": "Altgradigi Al {{version}}",
"Upgrading": "Altgradigata",

View File

@@ -75,10 +75,11 @@
"Default Device": "Equipo por defecto",
"Default Folder": "Carpeta por defecto",
"Default Folder Path": "Ruta de la carpeta por defecto",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Valores por defecto",
"Delete": "Eliminar",
"Delete Unexpected Items": "Borrar elementos inesperados",
"Deleted": "Eliminado",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Deseleccionar Todo",
"Deselect devices to stop sharing this folder with.": "Deseleccione los equipos con los cuales dejar de compartir esta carpeta.",
"Deselect folders to stop sharing with this device.": "Deseleccione las carpetas para dejar de compartir con este equipo.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "El número de versiones debe ser un número y no puede estar vacío.",
"The path cannot be blank.": "La ruta no puede estar vacía.",
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
"There are no devices to share this folder with.": "No hay equipos con los cuales compartir esta carpeta.",
"There are no file versions to restore.": "No hay versiones de archivo que restaurar.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Carpetas no compartidas",
"Untrusted": "No confiable",
"Up to Date": "Actualizado",
"Updated": "Actualizado",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Actualizar",
"Upgrade To {%version%}": "Actualizar a {{version}}",
"Upgrading": "Actualizando",

View File

@@ -75,10 +75,11 @@
"Default Device": "Dispositivo Predeterminado",
"Default Folder": "Carpeta Predeterminada",
"Default Folder Path": "Ruta de la carpeta por defecto",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Valores Predeterminados",
"Delete": "Eliminar",
"Delete Unexpected Items": "Borrar Elementos Inesperados",
"Deleted": "Eliminado",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Deseleccionar Todo",
"Deselect devices to stop sharing this folder with.": "Deseleccionar dispositivos con los cuales dejar de compartir esta carpeta.",
"Deselect folders to stop sharing with this device.": "Deseleccionar carpetas para dejar de compartir con este dispositivo.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "El número de versiones debe ser un número y no puede estar vacío.",
"The path cannot be blank.": "La ruta no puede estar vacía.",
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
"There are no devices to share this folder with.": "No hay dispositivos con los cuales compartir esta carpeta.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Carpetas no Compartidas",
"Untrusted": "No Confiable",
"Up to Date": "Actualizado",
"Updated": "Actualizado",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Actualizar",
"Upgrade To {%version%}": "Actualizar a {{version}}",
"Upgrading": "Actualizando",

View File

@@ -75,10 +75,11 @@
"Default Device": "Gailu lehenetsia",
"Default Folder": "Karpeta lehenetsia",
"Default Folder Path": "Partekatzearen sustrai bide lehenetsia",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Lehenetsiak",
"Delete": "Kendu",
"Delete Unexpected Items": "Ezabatu ustekabeko elementuak",
"Deleted": "Kendua",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Hautaketa guztia kendu",
"Deselect devices to stop sharing this folder with.": "Desautatu karpeta honekin partekatu nahi ez dituzun gailuak.",
"Deselect folders to stop sharing with this device.": "Desautatu karpetak gailu honekin partekatzeari uzteko.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Bertsioen kopuruak numerikoa behar du izan eta ez da hutsa izaiten ahal",
"The path cannot be blank.": "Bidea ez da hutsa izaiten ahal",
"The rate limit must be a non-negative number (0: no limit)": "Ixuriaren emaria ez da negatiboa izaiten ahal (0 = mugarik gabekoa)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Ikerketaren tartea ez da segundo kopuru negatiboa izaiten ahal",
"There are no devices to share this folder with.": "Ez dago partekatutako erabilera horri gehitzeko gailurik.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Partekatu gabeko karpetak",
"Untrusted": "Ez da fidagarria",
"Up to Date": "Eguneratua",
"Updated": "Berritua",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Aktualizatu",
"Upgrade To {%version%}": "Egunetaratu {{version}}-ari buruz",
"Upgrading": "Syncthing-en egunetaratzea",

View File

@@ -28,8 +28,8 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ulkoinen komento hallitsee versionnin. Sen täytyy poistaa tiedosto synkronoidusta kansiosta. Mikäli ohjelman polussa on välilyöntejä se on laitettava lainausmerkkeihin.",
"Anonymous Usage Reporting": "Anonyymi käyttöraportointi",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonyymi käyttöraportti on muuttunut. Haluatko vaihtaa uuteen muotoon?",
"Apply": "Apply",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"Apply": "Käytä",
"Are you sure you want to continue?": "Haluatko varmasti jatkaa?",
"Are you sure you want to override all remote changes?": "Are you sure you want to override all remote changes?",
"Are you sure you want to permanently delete all these files?": "Are you sure you want to permanently delete all these files?",
"Are you sure you want to remove device {%name%}?": "Oletko varma, että haluat postaa laitteen {{name}}?",
@@ -46,7 +46,7 @@
"Available debug logging facilities:": "Saatavilla olevat debug-luokat:",
"Be careful!": "Ole varovainen!",
"Bugs": "Bugit",
"Cancel": "Cancel",
"Cancel": "Peruuta",
"Changelog": "Muutoshistoria",
"Clean out after": "Puhdista seuraavan ajan kuluttua",
"Cleaning Versions": "Cleaning Versions",
@@ -71,17 +71,18 @@
"Custom Range": "Custom Range",
"Danger!": "Vaara!",
"Debugging Facilities": "Debug -luokat",
"Default Configuration": "Default Configuration",
"Default Configuration": "Oletusasetukset",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Oletuspolku kansioille",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Poista",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Poistettu",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Poista valinnat",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
"Deselect devices to stop sharing this folder with.": "Poista laitteiden valinnat, joiden kanssa haluat lopettaa tämän kansion jakamisen.",
"Deselect folders to stop sharing with this device.": "Poista kansioiden valinta lopettaaksesi jakamisen tämän laitteen kanssa.",
"Device": "Laite",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Laite \"{{name}}\" {{device}} osoitteessa ({{address}}) haluaa yhdistää. Lisää uusi laite?",
"Device ID": "Laitteen ID",
@@ -104,7 +105,7 @@
"Discovery": "Etsintä",
"Discovery Failures": "Etsinnässä tapahtuneet virheet",
"Discovery Status": "Discovery Status",
"Dismiss": "Dismiss",
"Dismiss": "Ohita",
"Do not add it to the ignore list, so this notification may recur.": "Do not add it to the ignore list, so this notification may recur.",
"Do not restore": "Älä palauta",
"Do not restore all": "Älä palauta kaikkia",
@@ -168,7 +169,7 @@
"Help": "Apua",
"Home page": "Kotisivu",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.",
"Identification": "Identification",
"Identification": "Laitteen tunniste",
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.",
"Ignore": "Ohita",
@@ -224,7 +225,7 @@
"No File Versioning": "Ei tiedostoversiointia",
"No files will be deleted as a result of this operation.": "Yhtään tiedostoa ei poisteta tämän toimenpiteen jälkeen.",
"No upgrades": "Ei päivityksiä",
"Not shared": "Not shared",
"Not shared": "Ei jaettu",
"Notice": "Huomautus",
"OK": "OK",
"Off": "Pois",
@@ -255,7 +256,7 @@
"Please wait": "Ole hyvä ja odota",
"Prefix indicating that the file can be deleted if preventing directory removal": "Etuliite, joka määrittää että tiedosto voidaan poistaa, mikäli se estää kansion poistamisen.",
"Prefix indicating that the pattern should be matched without case sensitivity": "Etuliite, joka määrittää että isot ja pienet kirjaimet eivät merkitse",
"Preparing to Sync": "Preparing to Sync",
"Preparing to Sync": "Valmistellaan synkronointia",
"Preview": "Esikatselu",
"Preview Usage Report": "Esikatsele käyttöraportti",
"Quick guide to supported patterns": "Tuettujen lausekkeiden pikaohje",
@@ -292,8 +293,8 @@
"See external versioning help for supported templated command line parameters.": "Katso ulkopuolisen versiohallinnan tukisivu komentoriviparametreistä.",
"Select All": "Valitse kaikki",
"Select a version": "Valitse versio",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
"Select additional devices to share this folder with.": "Valitse muita laitteita, joiden kanssa haluat jakaa tämän kansion.",
"Select additional folders to share with this device.": "Valitse lisää kansioita jaettavaksi tämän laitteen kanssa.",
"Select latest version": "Valitse viimeisin versio",
"Select oldest version": "Valitse vanhin versio",
"Select the folders to share with this device.": "Valitse kansiot jaettavaksi tämän laitteen kanssa.",
@@ -305,7 +306,7 @@
"Share Folder": "Jaa kansio",
"Share Folders With Device": "Jaa kansioita laitteen kanssa",
"Share this folder?": "Jaa tämä kansio?",
"Shared Folders": "Shared Folders",
"Shared Folders": "Jaetut kansiot",
"Shared With": "Jaettu seuraavien kanssa",
"Sharing": "Jakaminen",
"Show ID": "Näytä ID",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Versioiden määrän rulee olla numero, eikä se voi olla tyhjä.",
"The path cannot be blank.": "Polku ei voi olla tyhjä.",
"The rate limit must be a non-negative number (0: no limit)": "Nopeusrajan tulee olla positiivinen luku tai nolla. (0: ei rajaa)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Uudelleenskannauksen aikavälin tulee olla ei-negatiivinen numero sekunteja.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -404,11 +406,11 @@
"Unignore": "Poista ohitus",
"Unknown": "Tuntematon",
"Unshared": "Jakamaton",
"Unshared Devices": "Unshared Devices",
"Unshared Folders": "Unshared Folders",
"Unshared Devices": "Jakamattomat laitteet",
"Unshared Folders": "Jakamattomat kansiot",
"Untrusted": "Untrusted",
"Up to Date": "Ajan tasalla",
"Updated": "Päivitetty",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Päivitys",
"Upgrade To {%version%}": "Päivitä versioon {{version}}",
"Upgrading": "Päivitetään",
@@ -425,7 +427,7 @@
"Waiting to Clean": "Waiting to Clean",
"Waiting to Scan": "Waiting to Scan",
"Waiting to Sync": "Waiting to Sync",
"Warning": "Warning",
"Warning": "Varoitus",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Varoitus: tämä polku on olemassa olevan kansion \"{{otherFolder}}\" yläkansio.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Varoitus: Tämä kansio on jo olemassa olevan kansion yläkansio \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Varoitus: tämä polku on olemassa olevan kansion \"{{otherFolder}}\" alikansio.",
@@ -433,11 +435,11 @@
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Varoitus: jos käytät ulkopuolista tiedostojärjestelmän muutosten valvojaa, kuten {{syncthingInotify}} varmista, että se ei ole aktiivinen.",
"Watch for Changes": "Seuraa muutoksia",
"Watching for Changes": "Seuraa muutoksia",
"Watching for changes discovers most changes without periodic scanning.": "Watching for changes discovers most changes without periodic scanning.",
"Watching for changes discovers most changes without periodic scanning.": "Muutosten tarkkailu löytää useimmat muutokset ilman säännöllistä tarkistusta.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lisättäessä laitetta, muista että tämä laite tulee myös lisätä toiseen laitteeseen.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Lisättäessä uutta kansiota, muista että kansion ID:tä käytetään solmimaan kansiot yhteen laitteiden välillä. Ne ovat riippuvaisia kirjankoosta ja niiden tulee täsmätä kaikkien laitteiden välillä.",
"Yes": "Kyllä",
"Yesterday": "Yesterday",
"Yesterday": "Eilen",
"You can also select one of these nearby devices:": "Voit myös valita jonkin näistä lähellä olevista laitteista:",
"You can change your choice at any time in the Settings dialog.": "Voit muuttaa valintaasi koska tahansa \"Asetukset\" -valikossa.",
"You can read more about the two release channels at the link below.": "Voit lukea lisää kahdesta julkaisukanavasta alla olevasta linkistä.",
@@ -452,10 +454,10 @@
"full documentation": "täysi dokumentaatio",
"items": "kohteet",
"seconds": "seconds",
"theme-name-black": "Black",
"theme-name-dark": "Dark",
"theme-name-default": "Default",
"theme-name-light": "Light",
"theme-name-black": "Musta",
"theme-name-dark": "Tumma",
"theme-name-default": "Oletus",
"theme-name-light": "Vaalea",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} haluaa jakaa kansion \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} haluaa jakaa kansion \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."

View File

@@ -28,7 +28,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers du répertoire partagé. Si le chemin contient des espaces, il doit être spécifié entre guillemets.",
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Le format du rapport anonyme d'utilisation a changé. Voulez-vous passer au nouveau format ?",
"Apply": "Apply",
"Apply": "Appliquer",
"Are you sure you want to continue?": "Confirmez-vous ?",
"Are you sure you want to override all remote changes?": "Voulez-vous vraiment écraser tous les changements distants ?",
"Are you sure you want to permanently delete all these files?": "Êtes-vous sûrs de vouloir définitivement supprimer tous ces fichiers ?",
@@ -75,10 +75,11 @@
"Default Device": "Nouveaux appareils",
"Default Folder": "Nouveaux partages",
"Default Folder Path": "Chemin parent par défaut pour les nouveaux partages",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Personnalisation",
"Delete": "Supprimer",
"Delete Unexpected Items": "Supprimer les éléments inattendus",
"Deleted": "Supprimé",
"Deleted {%file%}": "{{file}} supprimé",
"Deselect All": "Tout déselectionner",
"Deselect devices to stop sharing this folder with.": "Désélectionnez les appareils avec lesquels vous ne souhaitez plus partager ces données.",
"Deselect folders to stop sharing with this device.": "Désélectionnez les partages auxquels cet appareil doit plus participer.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Le nombre de versions doit être numérique, et ne peut pas être vide.",
"The path cannot be blank.": "Le chemin ne peut pas être vide.",
"The rate limit must be a non-negative number (0: no limit)": "La limite de débit ne doit pas être négative (0 = pas de limite)",
"The remote device has not accepted sharing this folder.": "L'appareil distant n'a pas (encore ?) accepté de partager ce répertoire.",
"The rescan interval must be a non-negative number of seconds.": "L'intervalle d'analyse ne doit pas être un nombre négatif de secondes.",
"There are no devices to share this folder with.": "Il n'y a aucun appareil à ajouter à ce partage.",
"There are no file versions to restore.": "Aucune version de fichier à restaurer.",
@@ -393,7 +395,7 @@
"Time the item was last modified": "Dernière modification de l'élément",
"Today": "Aujourd'hui",
"Trash Can File Versioning": "Style poubelle",
"Twitter": "Twitter",
"Twitter": "Piaf",
"Type": "Type",
"UNIX Permissions": "Permissions UNIX",
"Unavailable": "Indisponible",
@@ -408,7 +410,7 @@
"Unshared Folders": "Non partagés",
"Untrusted": "Retirer la confiance (chiffrer tous les partages)",
"Up to Date": "À jour",
"Updated": "Mis à jour",
"Updated {%file%}": "{{file}} modifié",
"Upgrade": "Mettre à jour",
"Upgrade To {%version%}": "Mettre à jour vers {{version}}",
"Upgrading": "Mise à jour de Syncthing",

View File

@@ -75,10 +75,11 @@
"Default Device": "Standertapparaat",
"Default Folder": "Standertmap",
"Default Folder Path": "Standert Map-paad",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Standertwearden",
"Delete": "Fuortsmite",
"Delete Unexpected Items": " Unferwachte items wiskje",
"Deleted": "Fuortsmiten",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Alles Deselektearje",
"Deselect devices to stop sharing this folder with.": "Kies de apparaten om dizze map net langer mei te dielen.",
"Deselect folders to stop sharing with this device.": "Deselektearje mappen om it dielen mei dit apparaat te stopjen.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "It tal fan ferzjes moat in nûmer wêze en mei net leech wêze.",
"The path cannot be blank.": "It paad mei net leech wêze.",
"The rate limit must be a non-negative number (0: no limit)": "It fluggenslimyt moat in posityf nûmer wêze (0: gjin limyt)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "It wersken-ynterfal moat in posityf tal fan sekonden wêze.",
"There are no devices to share this folder with.": "Der binne gjin apparaten om dizze map mei te dielen.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Net-dielde mappen",
"Untrusted": "Net betroud",
"Up to Date": "By de tiid",
"Updated": "Fernijt",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Fernije",
"Upgrade To {%version%}": "Fernije nei {{version}}",
"Upgrading": "Oan it fernijen",

View File

@@ -75,10 +75,11 @@
"Default Device": "Alapértelmezett eszköz",
"Default Folder": "Alapértelmezett mappa",
"Default Folder Path": "Alapértelmezett mappa útvonala",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Alapértelmezések",
"Delete": "Törlés",
"Delete Unexpected Items": "Váratlan elemek törlése",
"Deleted": "Törölve",
"Deleted {%file%}": "Törölt {{file}}",
"Deselect All": "Kijelölés megszüntetése",
"Deselect devices to stop sharing this folder with.": "Azon eszközök kijelölésének törlése, amelyekkel e mappa megosztása leállítandó.",
"Deselect folders to stop sharing with this device.": "Szüntesse meg a mappák kijelölését a mappák megosztásának leállításához ezzel az eszközzel.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "A megtartott verziók száma nem lehet üres.",
"The path cannot be blank.": "Az elérési útvonal nem lehet üres.",
"The rate limit must be a non-negative number (0: no limit)": "A sebességlimitnek pozitív számnak kell lennie (0: nincs limit)",
"The remote device has not accepted sharing this folder.": "A távoli eszköz nem fogadta el ennek a mappának a megosztását.",
"The rescan interval must be a non-negative number of seconds.": "Az átnézési intervallum nullánál nagyobb másodperc érték kell legyen.",
"There are no devices to share this folder with.": "Nincsenek eszközök, amelyekkel megosztható lenne a mappa.",
"There are no file versions to restore.": "Nincsenek visszaállítható fájlverziók.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Nem megosztott mappák",
"Untrusted": "Nem megbízható",
"Up to Date": "Friss",
"Updated": "Frissítve",
"Updated {%file%}": "Frissített {{file}}",
"Upgrade": "Frissítés",
"Upgrade To {%version%}": "Frissítés a verzióra: {{version}}",
"Upgrading": "Frissítés",

View File

@@ -75,10 +75,11 @@
"Default Device": "Perangkat Bawaan",
"Default Folder": "Folder Bawaan",
"Default Folder Path": "Lokasi Folder Bawaan",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Bawaan",
"Delete": "Hapus",
"Delete Unexpected Items": "Hapus Barang Tidak Terduga",
"Deleted": "Terhapus",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Batal Pilih Semua",
"Deselect devices to stop sharing this folder with.": "Batal pilih perangkat untuk memberhentikan pembagian folder ini.",
"Deselect folders to stop sharing with this device.": "Batal pilih folder untuk memberhentikan pembagian dengan perangkat ini.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Jumlah versi harus berupa angka dan tidak dapat kosong.",
"The path cannot be blank.": "Lokasi tidak dapat kosong.",
"The rate limit must be a non-negative number (0: no limit)": "Pembatasan kecepatan harus berupa angka positif (0: tidak terbatas)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Interval pemindaian ulang harus berupa angka positif.",
"There are no devices to share this folder with.": "Tidak ada perangkat untuk membagikan folder ini.",
"There are no file versions to restore.": "Tidak ada versi berkas untuk dipulihkan.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Folder Tidak Terbagi",
"Untrusted": "Tidak Terpercaya",
"Up to Date": "Tersinkron",
"Updated": "Diperbarui",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Tingkatkan",
"Upgrade To {%version%}": "Tingkatkan Ke {{version}}",
"Upgrading": "Meningkatkan",

View File

@@ -11,15 +11,15 @@
"Add Folder": "Aggiungi Cartella",
"Add Remote Device": "Aggiungi Dispositivo Remoto",
"Add devices from the introducer to our device list, for mutually shared folders.": "Aggiungi dispositivi dall'introduttore all'elenco dei dispositivi, per cartelle condivise reciprocamente.",
"Add ignore patterns": "Add ignore patterns",
"Add ignore patterns": "Aggiungi schemi di esclusione",
"Add new folder?": "Aggiungere una nuova cartella?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Inoltre, verrà incrementato l'intervallo di scansione completo (60 volte, vale a dire un nuovo default di 1h). Puoi anche configurarlo manualmente per ogni cartella dopo aver scelto No.",
"Address": "Indirizzo",
"Addresses": "Indirizzi",
"Advanced": "Avanzato",
"Advanced": "Avanzate",
"Advanced Configuration": "Configurazione Avanzata",
"All Data": "Tutti i Dati",
"All Time": "All Time",
"All Time": "Sempre",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Tutte le cartelle condivise con questo dispositivo devono essere protette da una password, in modo tale che tutti i dati inviati siano illeggibili senza la password fornita.",
"Allow Anonymous Usage Reporting?": "Abilitare Statistiche Anonime di Utilizzo?",
"Allowed Networks": "Reti Consentite.",
@@ -28,7 +28,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Il controllo versione è gestito da un comando esterno. Quest'ultimo deve rimuovere il file dalla cartella condivisa. Se il percorso dell'applicazione contiene spazi, deve essere indicato tra virgolette.",
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Il formato delle statistiche anonime di utilizzo è cambiato. Vuoi passare al nuovo formato?",
"Apply": "Apply",
"Apply": "Applica",
"Are you sure you want to continue?": "Sei sicuro di voler continuare?",
"Are you sure you want to override all remote changes?": "Sei sicuro di voler sovrascrivere tutte le modifiche remote?",
"Are you sure you want to permanently delete all these files?": "Sei sicuro di voler eliminare definitivamente tutti questi file?",
@@ -68,17 +68,18 @@
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 i seguenti Collaboratori:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creazione di schemi di esclusione, sovrascrivendo un file esistente in {{path}}.",
"Currently Shared With Devices": "Attualmente Condiviso Con Dispositivi",
"Custom Range": "Custom Range",
"Custom Range": "Intervallo personalizzato",
"Danger!": "Pericolo!",
"Debugging Facilities": "Servizi di Debug",
"Default Configuration": "Configurazione predefinita",
"Default Device": "Dispositivo predefinito",
"Default Folder": "Cartella predefinita",
"Default Folder Path": "Percorso cartella predefinita",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Impostazioni predefinite",
"Delete": "Elimina",
"Delete Unexpected Items": "Elimina elementi imprevisti",
"Deleted": "Cancellato",
"Deleted {%file%}": "Cancellato {{file}}",
"Deselect All": "Deseleziona tutto",
"Deselect devices to stop sharing this folder with.": "Deseleziona i dispositivi con cui interrompere la condivisione di questa cartella.",
"Deselect folders to stop sharing with this device.": "Deseleziona le cartelle per interromperne la condivisione con questo dispositivo.",
@@ -131,7 +132,7 @@
"Error": "Errore",
"External File Versioning": "Controllo Versione Esterno",
"Failed Items": "Elementi Errati",
"Failed to load file versions.": "Failed to load file versions.",
"Failed to load file versions.": "Impossibile caricare le versioni dei file.",
"Failed to load ignore patterns.": "Impossibile caricare gli schemi di esclusione.",
"Failed to setup, retrying": "Configurazione fallita, riprovo",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connessione a server IPv6 fallisce se non c'è connettività IPv6.",
@@ -174,7 +175,7 @@
"Ignore": "Ignora",
"Ignore Patterns": "Schemi Esclusione File",
"Ignore Permissions": "Ignora Permessi",
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.",
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "Gli schemi di esclusione possono essere aggiunti solo dopo la creazione della cartella. Se selezionato, dopo il salvataggio verrà presentato un campo di input per inserire i modelli di esclusione.",
"Ignored Devices": "Dispositivi ignorati",
"Ignored Folders": "Cartelle ignorate",
"Ignored at": "Ignorato a",
@@ -186,9 +187,9 @@
"Keep Versions": "Versioni Mantenute",
"LDAP": "LDAP",
"Largest First": "Prima il più grande",
"Last 30 Days": "Last 30 Days",
"Last 7 Days": "Last 7 Days",
"Last Month": "Last Month",
"Last 30 Days": "Ultimi 30 giorni",
"Last 7 Days": "Ultimi 7 giorni",
"Last Month": "Ultimo Mese",
"Last Scan": "Ultima Scansione",
"Last seen": "Ultima connessione",
"Latest Change": "Ultima Modifica",
@@ -299,7 +300,7 @@
"Select the folders to share with this device.": "Seleziona le cartelle da condividere con questo dispositivo.",
"Send & Receive": "Invia & Ricevi",
"Send Only": "Invia Soltanto",
"Set Ignores on Added Folder": "Set Ignores on Added Folder",
"Set Ignores on Added Folder": "Imposta Esclusioni sulla Cartella Aggiunta",
"Settings": "Impostazioni",
"Share": "Condividi",
"Share Folder": "Condividi la Cartella",
@@ -378,20 +379,21 @@
"The number of versions must be a number and cannot be blank.": "Il numero di versioni dev'essere un numero e non può essere vuoto.",
"The path cannot be blank.": "Il percorso non può essere vuoto.",
"The rate limit must be a non-negative number (0: no limit)": "Il limite di banda deve essere un numero non negativo (0: nessun limite)",
"The remote device has not accepted sharing this folder.": "Il dispositivo remoto non ha accettato di condividere questa cartella.",
"The rescan interval must be a non-negative number of seconds.": "L'intervallo di scansione deve essere un numero non negativo secondi.",
"There are no devices to share this folder with.": "Non ci sono dispositivi con cui condividere questa cartella.",
"There are no file versions to restore.": "There are no file versions to restore.",
"There are no file versions to restore.": "Non ci sono versioni di file da ripristinare.",
"There are no folders to share with this device.": "Non ci sono cartelle da condividere con questo dispositivo.",
"They are retried automatically and will be synced when the error is resolved.": "Verranno effettuati tentativi in automatico e verranno sincronizzati quando l'errore sarà risolto.",
"This Device": "Questo Dispositivo",
"This Month": "This Month",
"This Month": "Questo Mese",
"This can easily give hackers access to read and change any files on your computer.": "Ciò potrebbe facilmente permettere agli hackers accesso alla lettura e modifica di qualunque file del tuo computer.",
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "Questo dispositivo non può rilevare automaticamente altri dispositivi o annunciare il proprio indirizzo per essere trovato da altri. Possono connettersi solo i dispositivi con indirizzi configurati staticamente.",
"This is a major version upgrade.": "Questo è un aggiornamento di versione principale",
"This setting controls the free space required on the home (i.e., index database) disk.": "Questa impostazione controlla lo spazio libero richiesto sul disco home (cioè, database di indice).",
"Time": "Tempo",
"Time the item was last modified": "Ora dell'ultima modifica degli elementi",
"Today": "Today",
"Today": "Oggi",
"Trash Can File Versioning": "Controllo Versione con Cestino",
"Twitter": "Twitter",
"Type": "Tipo",
@@ -408,7 +410,7 @@
"Unshared Folders": "Cartelle non condivise",
"Untrusted": "Non attendibile",
"Up to Date": "Sincronizzato",
"Updated": "Aggiornato",
"Updated {%file%}": "Aggiornato {{file}}",
"Upgrade": "Aggiornamento",
"Upgrade To {%version%}": "Aggiorna alla {{version}}",
"Upgrading": "Aggiornamento",
@@ -437,7 +439,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quando si aggiunge un nuovo dispositivo, tenere presente che il dispositivo deve essere aggiunto anche dall'altra parte.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Quando aggiungi una nuova cartella, ricordati che gli ID vengono utilizzati per collegare le cartelle nei dispositivi. Distinguono maiuscole e minuscole e devono corrispondere esattamente su tutti i dispositivi.",
"Yes": "Sì",
"Yesterday": "Yesterday",
"Yesterday": "Ieri",
"You can also select one of these nearby devices:": "È anche possibile selezionare uno di questi dispositivi nelle vicinanze:",
"You can change your choice at any time in the Settings dialog.": "Puoi sempre cambiare la tua scelta nel dialogo Impostazioni.",
"You can read more about the two release channels at the link below.": "Puoi ottenere piu informazioni riguarda i due canali di rilascio nel collegamento sottostante.",

View File

@@ -75,10 +75,11 @@
"Default Device": "デフォルトのデバイス",
"Default Folder": "デフォルトのフォルダー",
"Default Folder Path": "デフォルトのフォルダーパス",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "デフォルト",
"Delete": "削除",
"Delete Unexpected Items": "予期しないアイテムを削除",
"Deleted": "削除",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "すべて選択解除",
"Deselect devices to stop sharing this folder with.": "このフォルダの共有を停止したいデバイスがある場合は、当該デバイスの選択を解除してください。",
"Deselect folders to stop sharing with this device.": "このデバイスとの共有を停止するフォルダーを選択解除します。",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "保持するバージョン数は数値を指定してください。空欄にはできません。",
"The path cannot be blank.": "パスを入力してください。",
"The rate limit must be a non-negative number (0: no limit)": "帯域制限値は0以上で指定して下さい。 (0で無制限)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "再スキャン間隔は0秒以上で指定してください。",
"There are no devices to share this folder with.": "どのデバイスともフォルダーを共有していません。",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "非共有のフォルダー",
"Untrusted": "信頼しない",
"Up to Date": "最新",
"Updated": "更新",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "アップグレード",
"Upgrade To {%version%}": "{{version}} にアップグレードする",
"Upgrading": "アップグレード中",

View File

@@ -1,7 +1,7 @@
{
"A device with that ID is already added.": "이 ID 번호를 가진 기기가 이미 추가되어 있습니다.",
"A device with that ID is already added.": "이 식별자를 가진 기기가 이미 추가되어 있습니다.",
"A negative number of days doesn't make sense.": "일수를 음수로 입력하는 것은 말이 되지 않습니다.",
"A new major version may not be compatible with previous versions.": "새로운 주요 버전이 이전 버전과 호환되지 않을 수 있습니다.",
"A new major version may not be compatible with previous versions.": "새로운 주요 버전이 이전 버전과 호환되지 않을 수 있습니다.",
"API Key": "API 키",
"About": "정보",
"Action": "동작",
@@ -20,7 +20,7 @@
"Advanced Configuration": "고급 설정",
"All Data": "전체 데이터",
"All Time": "전체 기간",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "송신하는 데이터 모두를 비밀번호 없이 읽을 수 없도록 이 기기와 공유한 모든 폴더를 비밀번호로 보호해야 합니다.",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "송신하는 데이터 전체를 비밀번호 없이 읽을 수 없도록 이 기기와 공유한 모든 폴더를 비밀번호로 보호해야 합니다.",
"Allow Anonymous Usage Reporting?": "익명 사용 보고를 허용하시겠습니까?",
"Allowed Networks": "허가된 망",
"Alphabetic": "가나다순",
@@ -29,14 +29,14 @@
"Anonymous Usage Reporting": "익명 사용 보고",
"Anonymous usage report format has changed. Would you like to move to the new format?": "익명 사용 보고의 형식이 변경되었습니다. 새 형식으로 설정을 변경하시겠습니까?",
"Apply": "적용",
"Are you sure you want to continue?": "계속하시겠습니까?",
"Are you sure you want to override all remote changes?": "다른 기기의 변경 항목 모두를 덮어쓰시겠습니까?",
"Are you sure you want to permanently delete all these files?": "이 파일 모두를 영구 삭제하시겠습니까?",
"Are you sure you want to remove device {%name%}?": "{{name}} 기기를 제거하시겠습니까?",
"Are you sure you want to remove folder {%label%}?": "{{label}} 폴더를 제거하시겠습니까?",
"Are you sure you want to restore {%count%} files?": "{{count}}개의 파일을 복구하시겠습니까?",
"Are you sure you want to revert all local changes?": "현재 기기의 변경 항목 모두를 되돌리시겠습니까?",
"Are you sure you want to upgrade?": "업데이트하시겠습니까?",
"Are you sure you want to continue?": "정말로 계속하시겠습니까?",
"Are you sure you want to override all remote changes?": "다른 기기의 변경 항목 모두를 정말로 덮어쓰시겠습니까?",
"Are you sure you want to permanently delete all these files?": "이 파일 모두를 정말로 영구 삭제하시겠습니까?",
"Are you sure you want to remove device {%name%}?": "{{name}} 기기를 정말로 제거하시겠습니까?",
"Are you sure you want to remove folder {%label%}?": "{{label}} 폴더를 정말로 제거하시겠습니까?",
"Are you sure you want to restore {%count%} files?": "{{count}}개의 파일을 정말로 복구하시겠습니까?",
"Are you sure you want to revert all local changes?": "현재 기기의 변경 항목 모두를 정말로 되돌리시겠습니까?",
"Are you sure you want to upgrade?": "정말로 업데이트하시겠습니까?",
"Auto Accept": "자동 수락",
"Automatic Crash Reporting": "자동 충돌 보고",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "자동 업데이트가 안정 버전과 출시 후보 중 선택할 수 있게 변경되었습니다.",
@@ -64,7 +64,7 @@
"Connections": "연결",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "지속적 변경 항목 감시 기능이 Syncthing에 추가되었습니다. 저장장치에서 변경 항목이 감시되면 변경된 경로에서만 탐색이 실시됩니다. 변경 항목이 더 빠르게 전파되며 완전 탐색 횟수가 줄어드는 이점이 있습니다.",
"Copied from elsewhere": "다른 곳에서 복사됨",
"Copied from original": "원에서 복사됨",
"Copied from original": "원 파일에서 복사됨",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 하위 기여자들:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "무시 양식 생성 중; {{path}} 경로의 기존 파일을 덮어씁니다.",
"Currently Shared With Devices": "공유된 기기",
@@ -75,21 +75,22 @@
"Default Device": "기본 기기",
"Default Folder": "기본 폴더",
"Default Folder Path": "기본 폴더 경로",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "기본 설정",
"Delete": "삭제",
"Delete Unexpected Items": "예기치 못한 항목 삭제",
"Deleted": "삭제됨",
"Deleted {%file%}": "{{file}} 삭제됨",
"Deselect All": "모두 선택 해제",
"Deselect devices to stop sharing this folder with.": "현재 폴더를 공유하지 않을 기기를 선택 해제하십시오.",
"Deselect folders to stop sharing with this device.": "현재 기기와 공유하지 않을 폴더를 선택 해제하십시오.",
"Device": "기기",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" ({{device}} 기기가 {{address}}) 주소에서 접속을 요청했습니다. 새 기기를 추가하시겠습니까?",
"Device ID": "기기 ID 번호",
"Device ID": "기기 식별자",
"Device Identification": "기기 식별자",
"Device Name": "기기명",
"Device is untrusted, enter encryption password": "신뢰하지 않는 기기입니다; 암호화 비밀번호를 입력하십시오",
"Device rate limits": "기기 속도 제한",
"Device that last modified the item": "항목 최근 수정 기기",
"Device that last modified the item": "항목을 가장 최근 수정 기기",
"Devices": "기기",
"Disable Crash Reporting": "충돌 보고 비활성화",
"Disabled": "비활성화됨",
@@ -97,7 +98,7 @@
"Disabled periodic scanning and enabled watching for changes": "주기적 탐색 비활성화됨 및 변경 항목 감시 활성화됨",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "주기적 탐색 비활성화됨 및 변경 항목 감시 설정에 실패함; 1분마다 재시도 중:",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "파일 권한의 비교 및 동기화가 비활성화됩니다. FAT, exFAT, Synology, Android 등 파일 권한이 존재하지 않거나 비표준 파일 권한을 사용하는 체제에서 유용합니다.",
"Discard": "일시적 무시",
"Discard": "무시",
"Disconnected": "연결 끊김",
"Disconnected (Unused)": "연결 끊김(미사용)",
"Discovered": "탐지됨",
@@ -111,8 +112,8 @@
"Do you want to enable watching for changes for all your folders?": "변경 항목 감시를 모든 폴더에서 활성화하시겠습니까?",
"Documentation": "사용 설명서",
"Download Rate": "수신 속도",
"Downloaded": "수신됨",
"Downloading": "수신",
"Downloaded": "내려받음",
"Downloading": "내려받는 중",
"Edit": "편집",
"Edit Device": "기기 편집",
"Edit Device Defaults": "기기 기본 설정 편집",
@@ -134,7 +135,7 @@
"Failed to load file versions.": "파일 버전을 불러오기에 실패했습니다.",
"Failed to load ignore patterns.": "무시 양식을 불러오기에 실패했습니다.",
"Failed to setup, retrying": "설정 적용 실패; 재시도 중",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 연결이 없을 때는 IPv6 서버에 접속하지 못하는 것이 정상입니다.",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 연결되어 있지 않을 때는 IPv6 서버에 접속하지 못하는 것이 정상입니다.",
"File Pull Order": "파일 수신 순서",
"File Versioning": "파일 버전 관리",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Syncthing에 의해 교체 또는 삭제된 파일은 .stversions 폴더로 이동됩니다.",
@@ -145,7 +146,7 @@
"Filter by date": "날짜별 검색",
"Filter by name": "이름별 검색",
"Folder": "폴더",
"Folder ID": "폴더 ID 번호",
"Folder ID": "폴더 식별자",
"Folder Label": "폴더명",
"Folder Path": "폴더 경로",
"Folder Type": "폴더 유형",
@@ -162,8 +163,8 @@
"GUI Theme": "GUI 테마",
"General": "일반",
"Generate": "생성",
"Global Discovery": "글로벌 탐지",
"Global Discovery Servers": "글로벌 탐지 서버",
"Global Discovery": "외부 탐지",
"Global Discovery Servers": "외부 탐지 서버",
"Global State": "전체 기기 상태",
"Help": "도움말",
"Home page": "홈페이지",
@@ -190,7 +191,7 @@
"Last 7 Days": "지난 7일",
"Last Month": "지난 달",
"Last Scan": "최근 탐색",
"Last seen": "최근 접속",
"Last seen": "최근 연결",
"Latest Change": "최신 변경 항목",
"Learn more": "더 알아보기",
"Limit": "제한",
@@ -200,7 +201,7 @@
"Loading data...": "데이터를 불러오는 중...",
"Loading...": "불러오는 중...",
"Local Additions": "현재 기기 추가 항목",
"Local Discovery": "로컬 탐지",
"Local Discovery": "내부 탐지",
"Local State": "현재 기기 상태",
"Local State (Total)": "현재 기기 상태(합계)",
"Locally Changed Items": "현재 기기 변경 항목",
@@ -216,12 +217,12 @@
"Mod. Time": "수정 시간",
"Move to top of queue": "대기열 상단으로 이동",
"Multi level wildcard (matches multiple directory levels)": "다중 수준 와일드카드(여러 단계의 디렉토리에서 적용됨)",
"Never": "사용하지 않음",
"Never": "기록 없음",
"New Device": "새 기기",
"New Folder": "새 폴더",
"Newest First": "최신 파일 순",
"No": "아니요",
"No File Versioning": "파일 버전 관리하지 않음",
"No File Versioning": "파일 버전 관리하지 않음",
"No files will be deleted as a result of this operation.": "이 작업의 결과로는 아무 파일도 삭제되지 않습니다.",
"No upgrades": "업데이트하지 않음",
"Not shared": "공유되지 않음",
@@ -308,13 +309,13 @@
"Shared Folders": "공유된 폴더",
"Shared With": "공유된 기기",
"Sharing": "공유",
"Show ID": "기기 ID 번호 보기",
"Show ID": "기기 식별자 보기",
"Show QR": "QR 코드 보기",
"Show detailed discovery status": "탐지 현황 상세 보기",
"Show detailed listener status": "대기자 현황 상세 보기",
"Show diff with previous version": "이전 버전과의 diff 보기",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "기기 ID 번호를 대신해 기기 목록에서 나타납니다. 다른 기기에 선택적 기본값 이름으로 통보됩니다.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "기기 ID 번호를 대신해 기기 목록에서 나타납니다. 비워 둘 경우 다른 기기에서 통보받은 이름으로 갱신됩니다.",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "기기 식별자를 대신해 기기 목록에서 나타납니다. 다른 기기에 선택적 기본값 이름으로 통보됩니다.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "기기 식별자를 대신해 기기 목록에서 나타납니다. 비워 둘 경우 다른 기기에서 통보받은 이름으로 갱신됩니다.",
"Shutdown": "종료",
"Shutdown Complete": "종료 완료",
"Simple File Versioning": "간단한 파일 버전 관리",
@@ -351,22 +352,22 @@
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "시작 옵션이 GUI 주소를 덮어쓰고 있습니다. 덮어쓰기가 지속되는 동안에는 설정을 변경할 수 없습니다.",
"The Syncthing Authors": "The Syncthing Authors",
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthing의 관리자 인터페이스가 비밀번호 없이 원격 접속할 수 있도록 설정되어 있습니다.",
"The aggregated statistics are publicly available at the URL below.": "수집된 통계는 아래의 주소에서 공람되어 있습니다.",
"The aggregated statistics are publicly available at the URL below.": "수집된 통계는 아래의 주소에서 공람할 수 있습니다.",
"The cleanup interval cannot be blank.": "정리 간격은 비워 둘 수 없습니다.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "설정이 저장되었으나 아직 활성화되지 않았습니다. 새 설정을 활성화하려면 Syncthing을 재시작하십시오.",
"The device ID cannot be blank.": "기기 ID 번호는 비워 둘 수 없습니다.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "이 자리에 입력할 기기 ID 번호는 다른 기기의 \"동작 > 기기 ID 번호 보기\"에서 찾을 수 있습니다. 공백과 하이픈은 선택적입니다(무시됩니다).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "암호화된 사용 보고서는 매일 전송됩니다. 사용 중인 운영체제, 폴더 크기와 응용 프로그램 버전을 추적하기 위해서입니다. 만일 보고되는 정보가 변경되면 이 알림창이 다시 표시니다.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "입력한 기기 ID 번호가 올바르지 않습니다. 52 또는 56자의 알파벳과 숫자로 구성되어야 하며, 공백과 하이픈은 선택적입니다.",
"The folder ID cannot be blank.": "폴더 ID 번호는 비워 둘 수 없습니다.",
"The folder ID must be unique.": "폴더 ID 번호는 유일무이해야 합니다.",
"The device ID cannot be blank.": "기기 식별자는 비워 둘 수 없습니다.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "이 자리에 입력할 기기 식별자는 다른 기기의 \"동작 > 기기 식별자 보기\"에서 찾을 수 있습니다. 공백과 하이픈은 선택적입니다(무시됩니다).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "암호화된 사용 보고서는 매일 전송됩니다. 사용 중인 운영체제, 폴더 크기와 응용 프로그램 버전을 추적하기 위해서입니다. 만일 보고되는 정보가 변경되면 이 알림창이 다시 표시될 예정입니다.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "입력한 기기 식별자가 올바르지 않습니다. 52 또는 56자의 알파벳과 숫자로 구성되어야 하며, 공백과 하이픈은 선택적입니다.",
"The folder ID cannot be blank.": "폴더 식별자는 비워 둘 수 없습니다.",
"The folder ID must be unique.": "폴더 식별자는 유일무이해야 합니다.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "다른 기기의 폴더 내용을 현재 기기와 동일하도록 덮어씁니다. 현재 기기의 폴더에 존재하지 않는 파일은 다른 기기에서 삭제됩니다.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "현재 기기의 폴더 내용을 다른 기기와 동일하도록 덮어쓰입니다. 현재 기기의 폴더에 새로 추가된 파일은 삭제됩니다.",
"The folder path cannot be blank.": "폴더 경로는 비워 둘 수 없습니다.",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "다음과 같은 간격이 사용됩니다: 첫 한 시간 동안은 30초마다, 첫 하루 동안은 1시간마다, 첫 30일 동안은 1일마다, 그리고 최대 보관 기간까지는 1주일마다 버전이 보관됩니다.",
"The following items could not be synchronized.": "다음 항목이 동기화되지 못했습니다.",
"The following items were changed locally.": "다음 항목이 현재 기기에서 변경되었습니다.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "망 내의 다른 기기 탐지 및 현재 기기 통보를 위한 다음 방식이 실행 중입니다:",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "망 내의 다른 기기 탐지 및 현재 기기 통보를 위한 다음 방식이 사용 중입니다:",
"The following unexpected items were found.": "다음 예기치 못한 항목이 발견되었습니다.",
"The interval must be a positive number of seconds.": "간격은 초 단위의 양수여야 합니다.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "버전 폴더를 정리하는 초 단위의 간격입니다. 주기적 정리를 비활성화하려면 0을 입력하십시오.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "버전 개수는 숫자여야 하며 비워 둘 수 없습니다.",
"The path cannot be blank.": "경로는 비워 둘 수 없습니다.",
"The rate limit must be a non-negative number (0: no limit)": "속도 제한은 양수여야 합니다(0: 무제한)",
"The remote device has not accepted sharing this folder.": "다른 기기가 이 폴더의 공유를 승인하지 않았습니다.",
"The rescan interval must be a non-negative number of seconds.": "재탐색 간격은 초 단위의 양수여야 합니다.",
"There are no devices to share this folder with.": "이 폴더를 공유할 기기가 없습니다.",
"There are no file versions to restore.": "복구할 파일 버전이 없습니다.",
@@ -385,12 +387,12 @@
"They are retried automatically and will be synced when the error is resolved.": "자동 재시도 중이며 문제가 해결되는 즉시 동기화될 예정입니다.",
"This Device": "현재 기기",
"This Month": "이번 달",
"This can easily give hackers access to read and change any files on your computer.": "이로 인해서는 해커가 손쉽게 컴퓨터의 모든 파일을 읽고 편집할 수 있게 됩니다.",
"This can easily give hackers access to read and change any files on your computer.": "이로 인해서는 해커들이 현재 컴퓨터의 모든 파일을 손쉽게 읽고 변경할 수 있게 됩니다.",
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "이 기기는 다른 기기를 자동으로 탐지하거나 다른 기기로부터 발견되도록 자신의 주소를 통보할 수 없습니다. 고정 주소로 설정한 기기만이 현재 기기에 접속할 수 있습니다.",
"This is a major version upgrade.": "주요 버전 업데이트입니다.",
"This setting controls the free space required on the home (i.e., index database) disk.": "이 설정은 홈(즉, 인덕스 데이터베이스) 저장 장치의 여유 공간을 관리합니다.",
"Time": "시간",
"Time the item was last modified": "항목 최근 변경 시간",
"Time the item was last modified": "항목이 가장 최근에 수정된 시간",
"Today": "오늘",
"Trash Can File Versioning": "휴지통을 통한 파일 버전 관리",
"Twitter": "트위터",
@@ -408,7 +410,7 @@
"Unshared Folders": "공유되지 않은 폴더",
"Untrusted": "신뢰하지 않음",
"Up to Date": "최신 상태",
"Updated": "업데이트됨",
"Updated {%file%}": "{{file}} 업데이트됨",
"Upgrade": "업데이트",
"Upgrade To {%version%}": "{{version}}으로 업데이트",
"Upgrading": "업데이트 중",
@@ -435,7 +437,7 @@
"Watching for Changes": "변경 항목 감시",
"Watching for changes discovers most changes without periodic scanning.": "변경 항목 감시는 주기적으로 탐색하지 않아도 대부분의 변경 항목을 탐지합니다.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "새 기기를 추가할 때는 추가한 기기에서도 현재 기기를 추가해야 합니다.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "새 폴더를 추가할 때 폴더 ID 번호는 기기 간에 폴더를 묶어줍니다. 대소문자가 구분되며 모든 기기에서 동일해야 합니다.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "새 폴더를 추가할 때 폴더 식별자는 기기 간에 폴더를 묶어줍니다. 대소문자가 구분되며 모든 기기에서 동일해야 합니다.",
"Yes": "예",
"Yesterday": "어제",
"You can also select one of these nearby devices:": "주변의 기기 중 하나를 선택할 수도 있습니다.",

View File

@@ -75,10 +75,11 @@
"Default Device": "Numatytasis įrenginys",
"Default Folder": "Numatytasis aplankas",
"Default Folder Path": "Numatytojo aplanko kelias",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Numatytosios reikšmės",
"Delete": "Ištrinti",
"Delete Unexpected Items": "Ištrinti netikėtus elementus",
"Deleted": "Ištrinta",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Nuimti žymėjimą nuo visų",
"Deselect devices to stop sharing this folder with.": "Panaikinti įrenginių pasirinkimą, kad su jais būtų nustota bendrinti šį aplanką. ",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Versijų skaičius turi būti skaitmuo ir negali būti tuščias laukelis.",
"The path cannot be blank.": "Kelias negali būti tuščias.",
"The rate limit must be a non-negative number (0: no limit)": "Srauto maksimalus greitis privalo būti ne neigiamas skaičius (0: nėra apribojimo)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Nuskaitymo dažnis negali būti neigiamas skaičius.",
"There are no devices to share this folder with.": "Nėra įrenginių su kuriais bendrinti šį aplanką.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Atnaujinta",
"Updated": "Atnaujinta",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Atnaujinimas",
"Upgrade To {%version%}": "Atnaujinti į {{version}}",
"Upgrading": "Atnaujinama",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Forvalgt mappeplassering",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Slett",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Slettet",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Fjern alle markeringer",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Antall versjoner må være et tall og kan ikke være tomt.",
"The path cannot be blank.": "Plasseringen kan ikke være tom.",
"The rate limit must be a non-negative number (0: no limit)": "Hastighetsbegrensningen kan ikke være et negativt tall (0: ingen begrensing)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Antall sekund for intervallet kan ikke være negativt.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Oppdatert",
"Updated": "Oppdatert",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Oppgradere",
"Upgrade To {%version%}": "Oppgrader til {{version}}",
"Upgrading": "Oppgraderer",

View File

@@ -28,7 +28,7 @@
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Een externe opdracht regelt het versiebeheer. Hij moet het bestand verwijderen uit de gedeelde map. Als het pad naar de toepassing spaties bevat, moet dit tussen aanhalingstekens geplaatst worden.",
"Anonymous Usage Reporting": "Anonieme gebruikersstatistieken",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Het formaat voor anonieme gebruikersrapporten is gewijzigd. Wilt u naar het nieuwe formaat overschakelen?",
"Apply": "Apply",
"Apply": "Toepassen",
"Are you sure you want to continue?": "Weet u zeker dat u wilt doorgaan?",
"Are you sure you want to override all remote changes?": "Weet u zeker dat u alle externe wijzigingen wilt overschrijven?",
"Are you sure you want to permanently delete all these files?": "Weet u zeker dat u al deze bestanden permanent wilt verwijderen?",
@@ -75,10 +75,11 @@
"Default Device": "Standaardapparaat",
"Default Folder": "Standaardmap",
"Default Folder Path": "Standaardmaplocatie",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Standaardwaarden",
"Delete": "Verwijderen",
"Delete Unexpected Items": "Onverwachte items verwijderen",
"Deleted": "Verwijderd",
"Deleted {%file%}": "{{file}} verwijderd",
"Deselect All": "Alles deselecteren",
"Deselect devices to stop sharing this folder with.": "Deselecteer apparaten om er deze map niet meer mee te delen.",
"Deselect folders to stop sharing with this device.": "Deselecteer mappen om te stoppen met delen met dit apparaat.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Het aantal versies moet een getal zijn en mag niet leeg ziijn.",
"The path cannot be blank.": "Het pad mag niet leeg zijn.",
"The rate limit must be a non-negative number (0: no limit)": "De snelheidsbegrenzing moet een positief getal zijn (0: geen begrenzing)",
"The remote device has not accepted sharing this folder.": "Het externe apparaat heeft het delen van deze map niet geaccepteerd.",
"The rescan interval must be a non-negative number of seconds.": "Het interval voor opnieuw scannen moet een positief aantal seconden zijn.",
"There are no devices to share this folder with.": "Er zijn geen apparaten om deze map mee te delen.",
"There are no file versions to restore.": "Er zijn geen bestandsversies om te herstellen.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Niet-gedeelde mappen",
"Untrusted": "Niet vertrouwd",
"Up to Date": "Bijgewerkt",
"Updated": "Bijgewerkt",
"Updated {%file%}": "{{file}} bijgewerkt",
"Upgrade": "Bijwerken",
"Upgrade To {%version%}": "Bijwerken naar {{version}}",
"Upgrading": "Bijwerken",

View File

@@ -1,5 +1,5 @@
{
"A device with that ID is already added.": "Urządzenie o tym numerze ID jest już dodane.",
"A device with that ID is already added.": "Urządzenie o tym identyfikatorze jest już dodane.",
"A negative number of days doesn't make sense.": "Ujemna wartość liczbowa dni nie ma sensu.",
"A new major version may not be compatible with previous versions.": "Nowa duża wersja może nie być kompatybilna z poprzednimi wersjami.",
"API Key": "Klucz API",
@@ -12,7 +12,7 @@
"Add Remote Device": "Dodaj urządzenie zdalne",
"Add devices from the introducer to our device list, for mutually shared folders.": "Dodaj urządzenia od wprowadzającego do własnej listy urządzeń dla folderów współdzielonych obustronnie.",
"Add ignore patterns": "Dodaj wzorce ignorowania",
"Add new folder?": "Czy chcesz dodać nowy folder?",
"Add new folder?": "Czy dodać nowy folder?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Ponadto przedział czasowy pełnego ponownego skanowania zostanie zwiększony (o 60 razy, tj. do nowej domyślnej wartości 1h). Możesz również ustawić go później dla każdego folderu ręcznie po wybraniu Nie.",
"Address": "Adres",
"Addresses": "Adresy",
@@ -64,7 +64,7 @@
"Connections": "Połączenia",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Ciągłe obserwowanie zmian jest już dostępne w programie Syncthing. Będzie ono wykrywać zmiany na dysku i uruchamiać skanowanie tylko w zmodyfikowanych ścieżkach. Zalety tego rozwiązania są takie, że zmiany rozsyłane są szybciej oraz że wymagane jest mniej pełnych skanowań.",
"Copied from elsewhere": "Skopiowane z innego miejsca ",
"Copied from original": "Skopiowane z oryginału",
"Copied from original": "Skopiowane z pierwotnego pliku",
"Copyright © 2014-2019 the following Contributors:": "Wszelkie prawa zastrzeżone © 2014-2019 dla następujących współtwórców:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Tworzenie wzorców ignorowania; nadpisuje istniejący plik w ścieżce {{path}}.",
"Currently Shared With Devices": "Obecnie współdzielony z urządzeniami",
@@ -75,16 +75,17 @@
"Default Device": "Domyślne urządzenie",
"Default Folder": "Domyślny folder",
"Default Folder Path": "Domyślna ścieżka folderu",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Domyślne",
"Delete": "Usuń",
"Delete Unexpected Items": "Usuń elementy nieoczekiwane",
"Deleted": "Usunięto",
"Deleted {%file%}": "Usunięto {{file}}",
"Deselect All": "Odznacz wszystkie",
"Deselect devices to stop sharing this folder with.": "Odznacz urządzenia, z którymi chcesz przestać współdzielić ten folder.",
"Deselect folders to stop sharing with this device.": "Odznacz foldery, które chcesz przestać współdzielić z tym urządzeniem.",
"Device": "Urządzenie",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Urządzenie o nazwie \"{{name}}\" {{device}} pod adresem ({{address}}) chce się połączyć. Czy dodać nowe urządzenie?",
"Device ID": "Numer ID urządzenia",
"Device ID": "Identyfikator urządzenia",
"Device Identification": "Identyfikator urządzenia",
"Device Name": "Nazwa urządzenia",
"Device is untrusted, enter encryption password": "Urządzenie jest niezaufane; wprowadź szyfrujące hasło",
@@ -112,7 +113,7 @@
"Documentation": "Dokumentacja",
"Download Rate": "Prędkość pobierania",
"Downloaded": "Pobrane",
"Downloading": "Pobieranie",
"Downloading": "W trakcie pobierania",
"Edit": "Edytuj",
"Edit Device": "Edytuj urządzenie",
"Edit Device Defaults": "Edytuj domyślne ustawienia urządzeń",
@@ -145,7 +146,7 @@
"Filter by date": "Filtruj według daty",
"Filter by name": "Filtruj według nazwy",
"Folder": "Folder",
"Folder ID": "Numer ID folderu",
"Folder ID": "Identyfikator folderu",
"Folder Label": "Etykieta folderu",
"Folder Path": "Ścieżka folderu",
"Folder Type": "Rodzaj folderu",
@@ -308,13 +309,13 @@
"Shared Folders": "Współdzielone foldery",
"Shared With": "Współdzielony z",
"Sharing": "Współdzielenie",
"Show ID": "Pokaż numer ID",
"Show ID": "Pokaż identyfikator",
"Show QR": "Pokaż kod QR",
"Show detailed discovery status": "Pokaż szczegółowy stan odnajdywania",
"Show detailed listener status": "Pokaż szczegółowy stan nasłuchujących",
"Show diff with previous version": "Pokaż diff z poprzednią wersją",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Widoczna na liście urządzeń zamiast numeru ID urządzenia. Będzie anonsowana do innych urządzeń jako opcjonalna nazwa domyślna.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Widoczna na liście urządzeń zamiast numeru ID urządzenia. Będzie zaktualizowana do nazwy anonsowanej przez urządzenie, jeżeli pozostanie pusta.",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Widoczna na liście urządzeń zamiast identyfikatora urządzenia. Będzie anonsowana do innych urządzeń jako opcjonalna nazwa domyślna.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Widoczna na liście urządzeń zamiast identyfikatora urządzenia. Zostanie zaktualizowana do nazwy anonsowanej przez urządzenie, jeżeli pozostanie pusta.",
"Shutdown": "Wyłącz",
"Shutdown Complete": "Wyłączanie ukończone",
"Simple File Versioning": "Proste wersjonowanie plików",
@@ -354,12 +355,12 @@
"The aggregated statistics are publicly available at the URL below.": "Zebrane statystyki są publicznie dostępne pod poniższym adresem.",
"The cleanup interval cannot be blank.": "Przedział czasowy czyszczenia nie może być pusty.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Ustawienia zostały zapisane, ale nie są jeszcze aktywne. Syncthing musi zostać uruchomiony ponownie, aby aktywować nowe ustawienia.",
"The device ID cannot be blank.": "Numer ID urządzenia nie może być puste.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Numer ID urządzenia do wpisania tutaj można znaleźć w oknie \"Działania > Pokaż numer ID\" na innym urządzeniu. Spacje i myślniki są opcjonalne (ignorowane).",
"The device ID cannot be blank.": "Identyfikator urządzenia nie może być pusty.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Identyfikator urządzenia do wpisania tutaj można znaleźć w oknie \"Działania > Pokaż identyfikator\" na innym urządzeniu. Spacje i myślniki są opcjonalne (ignorowane).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Zaszyfrowane statystyki użycia są wysyłane codziennie. Używane są one do śledzenia popularności systemów, rozmiarów folderów oraz wersji programu. Jeżeli wysyłane statystyki ulegną zmianie, zostaniesz poproszony o ponowne udzielenie zgody w tym oknie.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Wprowadzony numer ID urządzenia wygląda na niepoprawny. Musi ono zawierać 52 lub 56 znaków składających się z liter i cyfr. Spacje i myślniki są opcjonalne.",
"The folder ID cannot be blank.": "Numer ID folderu nie może być pusty.",
"The folder ID must be unique.": "Numer ID folderu musi być niepowtarzalny.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Wprowadzony identyfikator urządzenia wygląda na niepoprawny. Musi on zawierać 52 lub 56 znaków składających się z liter i cyfr. Spacje i myślniki są opcjonalne.",
"The folder ID cannot be blank.": "Identyfikator folderu nie może być pusty.",
"The folder ID must be unique.": "Identyfikator folderu musi być niepowtarzalny.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Zawartość folderu na innych urządzeniach zostanie nadpisana tak, aby upodobnić się do tego urządzenia. Pliki nieobecne tutaj zostaną usunięte na innych urządzeniach.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Zawartość folderu na tym urządzeniu zostanie nadpisana tak, aby upodobnić się do innych urządzeń. Pliki nowo dodane tutaj zostaną usunięte.",
"The folder path cannot be blank.": "Ścieżka folderu nie może być pusta.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Liczba wersji musi być wartością liczbową oraz nie może być pusta.",
"The path cannot be blank.": "Ścieżka nie może być pusta.",
"The rate limit must be a non-negative number (0: no limit)": "Ograniczenie prędkości musi być nieujemną wartością liczbową (0: brak ograniczeń)",
"The remote device has not accepted sharing this folder.": "Urządzenie zdalne nie wyraziło zgody na udostępnienie tego folderu.",
"The rescan interval must be a non-negative number of seconds.": "Przedział czasowy ponownego skanowania musi być nieujemną liczbą sekund.",
"There are no devices to share this folder with.": "Brak urządzeń, z którymi można współdzielić ten folder.",
"There are no file versions to restore.": "Brak wersji plików, które można przywrócić.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Niewspółdzielone foldery",
"Untrusted": "Niezaufany",
"Up to Date": "Aktualny",
"Updated": "Zaktualizowano",
"Updated {%file%}": "Zaktualizowano {{file}}",
"Upgrade": "Aktualizacja",
"Upgrade To {%version%}": "Aktualizuj do {{version}}",
"Upgrading": "Aktualizowanie",
@@ -435,7 +437,7 @@
"Watching for Changes": "Obserwowanie zmian",
"Watching for changes discovers most changes without periodic scanning.": "Obserwowanie wykrywa większość zmian bez potrzeby okresowego skanowania.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Dodając nowe urządzenie pamiętaj, że musi ono zostać dodane także po drugiej stronie.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Dodając nowy folder pamiętaj, że numer ID jest używany do parowania folderów pomiędzy urządzeniami. Wielkość liter ma znaczenie i musi on być identyczny na wszystkich urządzeniach.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Dodając nowy folder pamiętaj, że identyfikator używany jest do parowania folderów pomiędzy urządzeniami. Wielkość liter ma znaczenie i musi być on identyczny na wszystkich urządzeniach.",
"Yes": "Tak",
"Yesterday": "Wczoraj",
"You can also select one of these nearby devices:": "Możesz również wybrać jedno z pobliskich urządzeń:",

View File

@@ -75,10 +75,11 @@
"Default Device": "Dispositivo Padrão",
"Default Folder": "Pasta Padrão",
"Default Folder Path": "Caminho padrão da pasta",
"Default Ignore Patterns": "Padrões de exclusão padrão",
"Defaults": "Padrões",
"Delete": "Apagar",
"Delete Unexpected Items": "Excluir Itens Inesperados",
"Deleted": "Apagado",
"Deleted {%file%}": "{{file}} excluído",
"Deselect All": "Desmarcar Todos",
"Deselect devices to stop sharing this folder with.": "Desmarque os dispositivos com os quais parar de compartilhar esta pasta.",
"Deselect folders to stop sharing with this device.": "Desmarque as pastas para interromper o compartilhamento com este dispositivo.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "O número de versões deve ser um valor numérico. O campo não pode ficar vazio.",
"The path cannot be blank.": "O caminho não pode ficar vazio.",
"The rate limit must be a non-negative number (0: no limit)": "O limite de velocidade deve ser um número positivo (0: sem limite)",
"The remote device has not accepted sharing this folder.": "O dispositivo remoto não aceitou compartilhar esta pasta.",
"The rescan interval must be a non-negative number of seconds.": "O intervalo entre verificações deve ser um número positivo de segundos.",
"There are no devices to share this folder with.": "Não há dispositivos com os quais compartilhar esta pasta.",
"There are no file versions to restore.": "Não há versões do arquivo para restaurar.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Pastas Não Compartilhadas",
"Untrusted": "Não confiável",
"Up to Date": "Em sincronia",
"Updated": "Atualizado",
"Updated {%file%}": "{{file}} atualizado",
"Upgrade": "Atualização",
"Upgrade To {%version%}": "Atualizar para {{version}}",
"Upgrading": "Atualizando",

View File

@@ -75,10 +75,11 @@
"Default Device": "Dispositivo predefinido",
"Default Folder": "Pasta predefinida",
"Default Folder Path": "Caminho da pasta predefinida",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Predefinições",
"Delete": "Eliminar",
"Delete Unexpected Items": "Eliminar itens inesperados",
"Deleted": "Eliminado",
"Deleted {%file%}": "{{file}} eliminado",
"Deselect All": "Retirar todas as selecções",
"Deselect devices to stop sharing this folder with.": "Retire a selecção para deixar de partilhar a pasta com esses dispositivos.",
"Deselect folders to stop sharing with this device.": "Retire a selecção das pastas para deixar de as partilhar com este dispositivo.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "O número de versões tem que ser um número e não pode estar vazio.",
"The path cannot be blank.": "O caminho não pode estar vazio.",
"The rate limit must be a non-negative number (0: no limit)": "O limite de velocidade tem que ser um número que não seja negativo (0: sem limite)",
"The remote device has not accepted sharing this folder.": "O dispositivo remoto não aceitou a partilha desta pasta.",
"The rescan interval must be a non-negative number of seconds.": "O intervalo entre verificações tem que ser um valor não negativo de segundos.",
"There are no devices to share this folder with.": "Não existem quaisquer dispositivos com os quais se possa partilhar esta pasta.",
"There are no file versions to restore.": "Não existem versões do ficheiro para restaurar.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Pastas não partilhadas",
"Untrusted": "Não fiável",
"Up to Date": "Em sincronia",
"Updated": "Actualizado",
"Updated {%file%}": "{{file}} modificado",
"Upgrade": "Actualizar",
"Upgrade To {%version%}": "Actualizar para {{version}}",
"Upgrading": "Actualizando",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Default Folder Path",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Şterge",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Șters",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Deselect All",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Numărul de versiuni trebuie să fie un număr şi nu poate fi gol.",
"The path cannot be blank.": "Locația nu poate fi goală.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Intervalul de rescanare trebuie să nu fie un număr negativ de secunde. ",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "La Zi",
"Updated": "Updated",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Upgrade",
"Upgrade To {%version%}": "Actualizează La Versiunea {{version}}",
"Upgrading": "Se Actualizează",

View File

@@ -75,10 +75,11 @@
"Default Device": "Стандартное Устройство",
"Default Folder": "Стандартная папка",
"Default Folder Path": "Путь для папок",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Стандартные параметры",
"Delete": "Удалить",
"Delete Unexpected Items": "Удалить неожиданные элементы",
"Deleted": "Удалено",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Снять выделение",
"Deselect devices to stop sharing this folder with.": "Отмените выбор устройств, чтобы прекратить совместное использование этой папки.",
"Deselect folders to stop sharing with this device.": "Отмените выбор папок, чтобы прекратить совместное использование с этим устройством.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Количество версий должно быть числом и не может быть пустым.",
"The path cannot be blank.": "Путь не может быть пустым.",
"The rate limit must be a non-negative number (0: no limit)": "Скорость должна быть неотрицательным числом (0: нет ограничения)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Интервал пересканирования должен быть неотрицательным количеством секунд.",
"There are no devices to share this folder with.": "Нет устройств, для которых будет доступна эта папка.",
"There are no file versions to restore.": "Нет версий файла для восстановления.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Не общедоступные папки",
"Untrusted": "Ненадёжный",
"Up to Date": "В актуальном состоянии",
"Updated": "Обновлено",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Обновить",
"Upgrade To {%version%}": "Обновить до {{version}}",
"Upgrading": "Обновление",

View File

@@ -1,462 +0,0 @@
{
"A device with that ID is already added.": "Prístroj s týmto ID je už pridaný.",
"A negative number of days doesn't make sense.": "Záporný počet dní nedáva zmysel.",
"A new major version may not be compatible with previous versions.": "Nová hlavná verzia nemusí byť kompatibilná s predchádzajúcimi verziami.",
"API Key": "API kľúč",
"About": "O aplikácii",
"Action": "Akcia",
"Actions": "Akcie",
"Add": "Pridať",
"Add Device": "Pridať zariadenie",
"Add Folder": "Pridať adresár",
"Add Remote Device": "Pridať vzdialené zariadenie",
"Add devices from the introducer to our device list, for mutually shared folders.": "Pre vzájomne zdieľané adresáre pridaj zariadenie od zavádzača do svojho zoznamu zariadení.",
"Add ignore patterns": "Add ignore patterns",
"Add new folder?": "Pridať nový adresár?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Navyše, interval plného skenovania bude navýšený (60krát, t.j. nová výchozia hodnota 1h). Môžete to nastaviť aj manuálne pre každý adresár ak zvolíte Nie.",
"Address": "Adresa",
"Addresses": "Adresy",
"Advanced": "Pokročilé",
"Advanced Configuration": "Pokročilá konfigurácia",
"All Data": "Všetky dáta",
"All Time": "All Time",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.",
"Allow Anonymous Usage Reporting?": "Povoliť anoynmné hlásenia o použivaní?",
"Allowed Networks": "Povolené siete",
"Alphabetic": "Abecedne",
"Altered by ignoring deletes.": "Altered by ignoring deletes.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.",
"Anonymous Usage Reporting": "Anonymné hlásenie o používaní",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formát anonymného hlásenia o používaní sa zmenil. Chcete prejsť na nový formát?",
"Apply": "Apply",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"Are you sure you want to override all remote changes?": "Are you sure you want to override all remote changes?",
"Are you sure you want to permanently delete all these files?": "Určite chcete vymazať všetky tieto súbory?",
"Are you sure you want to remove device {%name%}?": "Určite chcete odobrať zariadenie {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Určite chcete odobrať adresár {{label}}?",
"Are you sure you want to restore {%count%} files?": "Určite chcete obnoviť {{count}} súborov?",
"Are you sure you want to revert all local changes?": "Are you sure you want to revert all local changes?",
"Are you sure you want to upgrade?": "Určite chcete aktualizovať?",
"Auto Accept": "Automatické prijatie",
"Automatic Crash Reporting": "Automatické hlásenie chýb",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatická aktualizácia teraz ponúka voľbu medzi stabilnými vydaniami a kandidátmi na vydanie.",
"Automatic upgrades": "Automatické aktualizácie",
"Automatic upgrades are always enabled for candidate releases.": "Automatic upgrades are always enabled for candidate releases.",
"Automatically create or share folders that this device advertises at the default path.": "Automaticky vytvoriť alebo zdieľať adresáre, ktoré toto zariadenie ohlasuje, v predvolenom adresári.",
"Available debug logging facilities:": "Available debug logging facilities:",
"Be careful!": "Buď opatrný!",
"Bugs": "Chyby",
"Cancel": "Cancel",
"Changelog": "Záznam zmien",
"Clean out after": "Vyčistenie po",
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Interval čistenia",
"Click to see discovery failures": "Kliknite pre zobrazenie zlyhaní zisťovania.",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Zatvoriť",
"Command": "Príkaz",
"Comment, when used at the start of a line": "Komentár, keď použité na začiatku riadku",
"Compression": "Kompresia",
"Configured": "Nakonfigurované",
"Connected (Unused)": "Pripojené (Nepoužité)",
"Connection Error": "Chyba pripojenia",
"Connection Type": "Typ pripojenia",
"Connections": "Spojenia",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Nepretržité sledovanie zmien je už dostupné. Tým sa skenovanie spustí iba pre zmenené súbory. Výhoda je, že týmto spôsobom sa rýchlejšie šíria zmeny a nie je potrebných toľko veľa úplných skenov.",
"Copied from elsewhere": "Skoprírované odinakiaľ",
"Copied from original": "Skopírované z originálu",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 nasledujúci prispievatelia:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Vytváranie vzorov ignorovania, prepísanie existujúceho súboru v {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Custom Range": "Custom Range",
"Danger!": "Pozor!",
"Debugging Facilities": "Debugging Facilities",
"Default Configuration": "Default Configuration",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Predvolená adresárová cesta",
"Defaults": "Defaults",
"Delete": "Delete",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Zmazané",
"Deselect All": "Odznačiť všetko",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
"Device": "Zariadenie",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Zariadenie \"{{name}}\" ({{device}} na {{address}}) sa chce pripojiť. Pridať nové zariadenie?",
"Device ID": "ID zariadenia",
"Device Identification": "Identifikácia zariadenia",
"Device Name": "Názov zariadenia",
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
"Device rate limits": "Device rate limits",
"Device that last modified the item": "Zariadenie, ktoré naposledy pozmenilo položku",
"Devices": "Zariadenia",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Odpojené",
"Disabled periodic scanning and disabled watching for changes": "Disabled periodic scanning and disabled watching for changes",
"Disabled periodic scanning and enabled watching for changes": "Disabled periodic scanning and enabled watching for changes",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).",
"Discard": "Zahodiť",
"Disconnected": "Odpojené",
"Disconnected (Unused)": "Odpojené (Nepoužité)",
"Discovered": "Zistené",
"Discovery": "Zisťovanie",
"Discovery Failures": "Zlyhania zisťovania",
"Discovery Status": "Discovery Status",
"Dismiss": "Dismiss",
"Do not add it to the ignore list, so this notification may recur.": "Do not add it to the ignore list, so this notification may recur.",
"Do not restore": "Neobnovovať",
"Do not restore all": "Neobnovovať všetko",
"Do you want to enable watching for changes for all your folders?": "Chcete zapnúť sledovanie zmien vo všetkých priečinkoch?",
"Documentation": "Dokumentácia",
"Download Rate": "Rýchlosť sťahovania",
"Downloaded": "Stiahnuté",
"Downloading": "Sťahovanie",
"Edit": "Upraviť",
"Edit Device": "Upraviť zariadenie",
"Edit Device Defaults": "Edit Device Defaults",
"Edit Folder": "Upraviť priečinok",
"Edit Folder Defaults": "Edit Folder Defaults",
"Editing {%path%}.": "Úprava {{path}}.",
"Enable Crash Reporting": "Zapnúť hlásenie chýb",
"Enable NAT traversal": "Povoliť priechod NAT",
"Enable Relaying": "Povoliť prenosové uzly",
"Enabled": "Povolené",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Vložte kladné číslo (napr. \"2,35\") a zvoľte jednotku. Percentá sa zobrazujú ako časť celkovej veľkosti disku.",
"Enter a non-privileged port number (1024 - 65535).": "Vložte číslo neprivilegovaného portu (1024-65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.",
"Enter ignore patterns, one per line.": "Zadaj ignorované vzory, jeden na riadok.",
"Enter up to three octal digits.": "Zadajte max 3. číslice v osmičkovej sústave.",
"Error": "Chyba",
"External File Versioning": "Externé spracovanie verzií súborov",
"Failed Items": "Zlyhané položky",
"Failed to load file versions.": "Failed to load file versions.",
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
"Failed to setup, retrying": "Nepodarilo sa nastaviť, opakujem.",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Zlyhanie pripojenia k IPv6 serverom je očakávané ak neexistujú žiadne IPv6 pripojenia.",
"File Pull Order": "Poradie sťahovania súborov",
"File Versioning": "Verzie súborov",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Súbory zmenené alebo zmazané aplikáciou Syncthing sú presunuté do adresára .stversions .",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Súbory premiestnené alebo zmazané aplikáciou Sycthing sú presunuté do verzií označených dátumov v adresári .stversions.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Soubory sú chránené pred zmenami na ostatních zariadeniach, ale zmeny provedené z tohto zariadenia budú rozoslané na zvyšok klastra.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.",
"Filesystem Watcher Errors": "Chyby zo subsystému \"Filesystem watcher\"",
"Filter by date": "Filtrovanie podľa dátumu",
"Filter by name": "Filtrovanie podľa mena",
"Folder": "Adresár",
"Folder ID": "ID adresára",
"Folder Label": "Označenie adresára",
"Folder Path": "Cesta k adresáru",
"Folder Type": "Typ adresára",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Folder type \"{{receiveEncrypted}}\" can only be set when adding a new folder.",
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",
"Folders": "Adresáre",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.",
"Full Rescan Interval (s)": "Interval úplného skenu (s)",
"GUI": "GUI",
"GUI Authentication Password": "Prihlasovacie heslo do GUI",
"GUI Authentication User": "Prihlasovacie meno do GUI",
"GUI Authentication: Set User and Password": "Autentifikácia do webového rozhrania: Nastavenie mena a hesla",
"GUI Listen Address": "Adresa pre prístup do GUI",
"GUI Theme": "Grafická téma GUI",
"General": "Všeobecné",
"Generate": "Generovať",
"Global Discovery": "Globálne vyhľadávanie",
"Global Discovery Servers": "Globálne zisťovacie servery",
"Global State": "Globálny status",
"Help": "Pomoc",
"Home page": "Domovská stránka",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.",
"Identification": "Identification",
"If untrusted, enter encryption password": "Ak nie je dôveryhodný, uveďte heslo na dešifrovanie",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.",
"Ignore": "Ignorovať",
"Ignore Patterns": "Ignorované vzory",
"Ignore Permissions": "Ignorované práva",
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.",
"Ignored Devices": "Ignorované zariadenia",
"Ignored Folders": "Ingorované priečinky",
"Ignored at": "Ignorované na",
"Incoming Rate Limit (KiB/s)": "Limit pre sťahovanie (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Nesprávna konfigurácia môže poškodiť váš adresár a spôsobiť nefunkčnosť aplikácie Súbory.",
"Introduced By": "Uvedené",
"Introducer": "Uvádzač",
"Inversion of the given condition (i.e. do not exclude)": "Inverzia danej podmienky (napr. nevynechať)",
"Keep Versions": "Ponechanie verzií",
"LDAP": "LDAP",
"Largest First": "Najprv najväčšie",
"Last 30 Days": "Last 30 Days",
"Last 7 Days": "Last 7 Days",
"Last Month": "Last Month",
"Last Scan": "Posledný sken",
"Last seen": "Naposledy videný",
"Latest Change": "Posledná zmena",
"Learn more": "Zisti viac",
"Limit": "Limit",
"Listener Failures": "Listener Failures",
"Listener Status": "Listener Status",
"Listeners": "Načúvajúci",
"Loading data...": "Načítavanie údajov...",
"Loading...": "Načítavanie...",
"Local Additions": "Local Additions",
"Local Discovery": "Lokálne vyhľadávanie",
"Local State": "Lokálny status",
"Local State (Total)": "Lokálny status (celkový)",
"Locally Changed Items": "Lokálne zmenené položky",
"Log": "Záznam",
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
"Logs": "Záznamy",
"Major Upgrade": "Hlavná aktualizácia",
"Mass actions": "Hromadná akcia",
"Maximum Age": "Maximálny časový limit",
"Metadata Only": "Iba metadáta",
"Minimum Free Disk Space": "Minimálna veľkosť voľného miesta na disku",
"Mod. Device": "Mod. zariadenie",
"Mod. Time": "Mod. čas",
"Move to top of queue": "Presun na začiatok poradia",
"Multi level wildcard (matches multiple directory levels)": "Viacúrovňový zástupný znak (zhoda naprieč viacerými úrovňami adresára)",
"Never": "Nikdy",
"New Device": "Nové zariadenie",
"New Folder": "Nový adresár",
"Newest First": "Najnovší najprv",
"No": "Nie",
"No File Versioning": "Bez verzií súbor",
"No files will be deleted as a result of this operation.": "Ako výsledok tejto operáciu nebudú zmazané žiadne súbory.",
"No upgrades": "Bez aktualizácií",
"Not shared": "Not shared",
"Notice": "Oznámenie",
"OK": "OK",
"Off": "Vypnúť",
"Oldest First": "Najstarší najprv",
"Optional descriptive label for the folder. Can be different on each device.": "Voliteľné popisné označenie adresára. Môže byť rôzne na každom zariadení.",
"Options": "Nastavenia",
"Out of Sync": "Nesynchronizované",
"Out of Sync Items": "Nezosynchronizované položky",
"Outgoing Rate Limit (KiB/s)": "Obmedzenie odchádzajúcej rýchlosti (KiB/s)",
"Override": "Override",
"Override Changes": "Prepísať zmeny",
"Path": "Cesta",
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Cesta k adresáru na lokálnom počítači. Ak neexistuje, bude vytvorená. Znak vlnovky (~) môže byť použitý ako skratka pre",
"Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.": "Cesta, kde budú vytvorené nové, automaticky prijaté adresáre, rovnako aj predvolená cesta, kam budú pridané nové adresáre cez UI. Vlnovka (~) smeruje do {{tilde}}.",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Cesta, kde budú uložené verzie (ponechajte prázdne pre predvolený adresár .stversions v zdieľanom adresari)",
"Pause": "Pozastaviť",
"Pause All": "Pozastaviť všetky",
"Paused": "Pozastavené",
"Paused (Unused)": "Paused (Unused)",
"Pending changes": "Čakajúce zmeny",
"Periodic scanning at given interval and disabled watching for changes": "Periodické skenovanie v zvolenom rozsahu a vypnuté sledovanie zmien.",
"Periodic scanning at given interval and enabled watching for changes": "Periodické skenovanie v zvolenom rozsahu a zapnuté sledovanie zmien.",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Prístupové oprávnenia",
"Please consult the release notes before performing a major upgrade.": "Pred spustením hlavnej aktualizácie si prosím prečítajte poznámky k vydaniu.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Zadajte prosím prihlasovanie meno a heslo v dialógovom okne nastavení.",
"Please wait": "Prosím čakajte",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix označujúci, že súbor môže byť odstránený, ak bráni odstráneniu adresára.",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix označujúci, že vzory by mali ignorovať veľkosť písma.",
"Preparing to Sync": "Príprava na Sync",
"Preview": "Náhľad",
"Preview Usage Report": "Náhľad záznamu o používaní",
"Quick guide to supported patterns": "Rýchly sprievodca podporovanými vzormi",
"Random": "Náhodne",
"Receive Encrypted": "Prijať šifrované",
"Receive Only": "Iba prijímanie",
"Received data is already encrypted": "Prijaté údaje sú už šifrované",
"Recent Changes": "Nedávne zmeny",
"Reduced by ignore patterns": "Znížené o ignorované vzory",
"Release Notes": "Poznámky k vydaniu",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Kandidáti na vydanie obsahujú najnovšie vlastnosti a opravy. Sú podobné tradičným dvojtýždenným vydaniam programu Syncthing.",
"Remote Devices": "Vzdialené zariadenia",
"Remote GUI": "Remote GUI",
"Remove": "Odstrániť",
"Remove Device": "Odstrániť zariadenie",
"Remove Folder": "Odstrániť adresár",
"Required identifier for the folder. Must be the same on all cluster devices.": "Potrebný identifikátor pre adresár. Musí byť rovnaký na všetkých zariadeniach v skupine.",
"Rescan": "Opakovať skenovanie",
"Rescan All": "Opakovať skenovanie všetkých",
"Rescans": "Opätovné skeny",
"Restart": "Reštart",
"Restart Needed": "Potrebný reštart",
"Restarting": "Reštartovanie",
"Restore": "Obnoviť",
"Restore Versions": "Obnoviť verzie",
"Resume": "Pokračovať",
"Resume All": "Pokračuj so všetkými",
"Reused": "Opakovane použité",
"Revert": "Revert",
"Revert Local Changes": "Vrátiť lokálne zmeny",
"Save": "Uložiť",
"Scan Time Remaining": "Zostávajúci čas skenovania",
"Scanning": "Skenovanie",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Vybrať všetko",
"Select a version": "Zvoliť verziu",
"Select additional devices to share this folder with.": "Select additional devices to share this folder with.",
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
"Select latest version": "Zvoliť najnovšiu verziu",
"Select oldest version": "Zvoliť najstaršiu verziu",
"Select the folders to share with this device.": "Vyberte adresáre ktoré chcete zdieľať s týmto zariadením.",
"Send & Receive": "Prijímať a odosielať",
"Send Only": "Iba odosielať",
"Set Ignores on Added Folder": "Set Ignores on Added Folder",
"Settings": "Nastavenia",
"Share": "Zdieľať",
"Share Folder": "Zdieľať adresár",
"Share Folders With Device": "Zdieľať adresáre so zariadením",
"Share this folder?": "Zdieľať tento adresár?",
"Shared Folders": "Zdieľané priečinky",
"Shared With": "Zdieľané s",
"Sharing": "Zdieľať",
"Show ID": "Zobraziť ID",
"Show QR": "Zobraziť QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Ukázať rozdiely s predchádzajúcou verziou",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazované namiesto ID zariadenia v štatúte klastra. Toto pomenovanie bude oznamovať ostatným zariadeniam ako voliteľné predvolené pomenovanie.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazované namiesto ID zariadenia v klastri. Ak je ponechané prázdne, bude nahradené pomenovaním, ktoré oznamuje zariadenie.",
"Shutdown": "Vypnutie",
"Shutdown Complete": "Vypnutie ukončené",
"Simple File Versioning": "Jednoduché verzie súborov",
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
"Size": "Veľkosť",
"Smallest First": "Najmenší najprv",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Some discovery methods could not be established for finding other devices or announcing this device:",
"Some items could not be restored:": "Niektoré položky nemôžu byť obnovené:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Zdrojový kód",
"Stable releases and release candidates": "Stabilné verzie a kandidáti na vydanie",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabilné vydania sú odložené asi o dva týždne. Počas tejto doby prechádzajú testovaním ako kandidáti na vydanie.",
"Stable releases only": "Iba stabilné verzie",
"Staggered File Versioning": "Rozložené verzie súborov",
"Start Browser": "Spusti prehliadač",
"Statistics": "Štatistika",
"Stopped": "Zastavené",
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{{receiveEncrypted}}\" too.",
"Support": "Podpora",
"Support Bundle": "Support Bundle",
"Sync Protocol Listen Addresses": "Adresa načúvania synchronizačného protokolu",
"Syncing": "Synchronizácia",
"Syncthing has been shut down.": "Syncthing bol vypnutý.",
"Syncthing includes the following software or portions thereof:": "Syncthing obsahuje nasledujúci software nebo jeho časti:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing je otvorený softvér s licenciou MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing is listening on the following network addresses for connection attempts from other devices:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.",
"Syncthing is restarting.": "Syncthing sa reštartuje.",
"Syncthing is upgrading.": "Syncthing sa aktualizuje.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing se zdá byť nefunkčný, alebo je problém s internetovým pripojením. Opakujem...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.",
"Take me back": "Späť",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.",
"The Syncthing Authors": "The Syncthing Authors",
"The Syncthing admin interface is configured to allow remote access without a password.": "The Syncthing admin interface is configured to allow remote access without a password.",
"The aggregated statistics are publicly available at the URL below.": "Súhrnné štatistiky sú verejne dostupné na uvedenej URL.",
"The cleanup interval cannot be blank.": "The cleanup interval cannot be blank.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.",
"The device ID cannot be blank.": "ID zariadenia nemôže byť prázdne.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ID zariadenie pre vloženie môžete nájsť na druhom zariadení v dialógu \"Akcia > Zobraziť ID\". Medzery a pomlčky sú voliteľné (ignorované).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The folder ID cannot be blank.": "The folder ID cannot be blank.",
"The folder ID must be unique.": "ID adresára musí byť jedinečné.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.",
"The folder path cannot be blank.": "Cesta k adresáru nemôže byť prázdna.",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.",
"The following items could not be synchronized.": "The following items could not be synchronized.",
"The following items were changed locally.": "Tieto položky boli zmenené lokálne",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "The following methods are used to discover other devices on the network and announce this device to be found by others:",
"The following unexpected items were found.": "The following unexpected items were found.",
"The interval must be a positive number of seconds.": "The interval must be a positive number of seconds.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.",
"The maximum age must be a number and cannot be blank.": "Maximálny vek musí byť číslo a nemôže byť prázdne.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "The maximum time to keep a version (in days, set to 0 to keep versions forever).",
"The number of days must be a number and cannot be blank.": "Počet dní musí byť číslo a nemôže byť prázdny.",
"The number of days to keep files in the trash can. Zero means forever.": "Počet dní pre uchovanie súborov v koši. Nula znamená navždy.",
"The number of old versions to keep, per file.": "Počet uchovávaných starších verzií pre každý súbor.",
"The number of versions must be a number and cannot be blank.": "Počet verzií musí byť číslo a nemôže byť prázdny.",
"The path cannot be blank.": "Cesta nemôže byť prázdna.",
"The rate limit must be a non-negative number (0: no limit)": "Limit rýchlosti musí byť kladné číslo (0: bez limitu)",
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
"There are no file versions to restore.": "There are no file versions to restore.",
"There are no folders to share with this device.": "There are no folders to share with this device.",
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
"This Device": "Toto zariadenie",
"This Month": "This Month",
"This can easily give hackers access to read and change any files on your computer.": "This can easily give hackers access to read and change any files on your computer.",
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.",
"This is a major version upgrade.": "Toto je hlavná aktualizácia.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Toto nastavenie kontroluje voľné miesto požadované na domovskom disku (napr. indexová databáza).",
"Time": "Čas",
"Time the item was last modified": "Čas poslednej zmeny položky",
"Today": "Today",
"Trash Can File Versioning": "Verzie súborov v koši",
"Twitter": "Twitter",
"Type": "Typ",
"UNIX Permissions": "UNIX Permissions",
"Unavailable": "Nedostupné",
"Unavailable/Disabled by administrator or maintainer": "Nedostupné/Zakázané administrátorom alebo správcom",
"Undecided (will prompt)": "Undecided (will prompt)",
"Unexpected Items": "Unexpected Items",
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
"Unignore": "Unignore",
"Unknown": "Neznáme",
"Unshared": "Nezdieľané",
"Unshared Devices": "Unshared Devices",
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Aktuálne",
"Updated": "Aktualizované",
"Upgrade": "Aktualizácia",
"Upgrade To {%version%}": "Aktualizovať na {{version}}",
"Upgrading": "Aktualizácia",
"Upload Rate": "Rýchlosť odosielania",
"Uptime": "Doba prevádzky",
"Usage reporting is always enabled for candidate releases.": "Hlásenia o používaní sú pri kandidátoch na vydanie vždy povolené.",
"Use HTTPS for GUI": "Použiť HTTPS pre grafické rozhranie",
"Use notifications from the filesystem to detect changed items.": "Use notifications from the filesystem to detect changed items.",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Username/Password has not been set for the GUI authentication. Please consider setting it up.",
"Version": "Verzia",
"Versions": "Verzie",
"Versions Path": "Cesta k verziám",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Verzie sú automaticky zmazané ak sú staršie ako je maximálny časový limit alebo presiahnu počet súborov povolených v danom intervale.",
"Waiting to Clean": "Čakám na čistenie",
"Waiting to Scan": "Čakám na sken",
"Waiting to Sync": "Čakám na Sync",
"Warning": "Warning",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a parent directory of an existing folder \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a parent directory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a subdirectory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Warning: If you are using an external watcher like {{syncthingInotify}}, you should make sure it is deactivated.",
"Watch for Changes": "Sleduj zmeny",
"Watching for Changes": "Sledujú sa zmeny",
"Watching for changes discovers most changes without periodic scanning.": "Watching for changes discovers most changes without periodic scanning.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"Yes": "Áno",
"Yesterday": "Yesterday",
"You can also select one of these nearby devices:": "Môžete tiež vybrať jedno z týchto blízkych zariadení:",
"You can change your choice at any time in the Settings dialog.": "Voľbu môžete kedykoľvek zmeniť v dialógu Nastavenia.",
"You can read more about the two release channels at the link below.": "O dvoch vydávacích kanáloch si môžete viacej prečítať v odkaze nižšie.",
"You have no ignored devices.": "Nemáte žiadne ignorované zariadenia.",
"You have no ignored folders.": "Nemáte žiadne ignorované priečinky.",
"You have unsaved changes. Do you really want to discard them?": "Niektoré zmeny ste neuložili. Chcete ich skutočne zahodiť?",
"You must keep at least one version.": "Musíte ponechať aspoň jednu verziu",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
"days": "dní",
"directories": "priečinky",
"files": "súbory",
"full documentation": "úplná dokumntácia",
"items": "položiek",
"seconds": "sekúnd",
"theme-name-black": "Black",
"theme-name-dark": "Dark",
"theme-name-default": "Default",
"theme-name-light": "Light",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce zdieľať adresár \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce zdieľať adresár \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
}

View File

@@ -75,10 +75,11 @@
"Default Device": "Privzeta naprava",
"Default Folder": "Privzeta mapa",
"Default Folder Path": "Privzeta pot do mape",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Privzeti",
"Delete": "Izbriši",
"Delete Unexpected Items": "Izbrišite nepričakovane predmete",
"Deleted": "Izbrisana",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Prekliči vse",
"Deselect devices to stop sharing this folder with.": "Prekliči izbiro naprav, z katerimi ne želiš več deliti mape.",
"Deselect folders to stop sharing with this device.": "Prekliči mape, da se ne delijo več z to napravo.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Število različic mora biti število in ne more biti prazno.",
"The path cannot be blank.": "Pot ne more biti prazna.",
"The rate limit must be a non-negative number (0: no limit)": "Omejitev stopnje odzivnosti mora biti nenegativno število (0: brez omejitve)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Interval skeniranja mora biti pozitivna številka.",
"There are no devices to share this folder with.": "Ni naprav za skupno rabo te mape.",
"There are no file versions to restore.": "Ni različic od datotek za obnoviti.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Mape, ki niso v skupni rabi",
"Untrusted": "Nezaupno",
"Up to Date": "Posodobljeno",
"Updated": "Posodobljeno",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Posodobi",
"Upgrade To {%version%}": "Posodobi na različico {{version}}",
"Upgrading": "Posodabljanje",

View File

@@ -75,12 +75,13 @@
"Default Device": "Standardenhet",
"Default Folder": "Standardmapp",
"Default Folder Path": "Standardmappsökväg",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Standard",
"Delete": "Ta bort",
"Delete Unexpected Items": "Ta bort oväntade objekt",
"Deleted": "Borttagna",
"Deleted {%file%}": "Tog bort {{file}}",
"Deselect All": "Avmarkera alla",
"Deselect devices to stop sharing this folder with.": "Avmarkera enheter för att sluta dela denna mapp med.",
"Deselect devices to stop sharing this folder with.": "Avmarkera enheter som du vill sluta dela denna mapp med.",
"Deselect folders to stop sharing with this device.": "Avmarkera mappar för att sluta dela med denna enhet.",
"Device": "Enhet",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enhet \"{{name}}\" ({{device}} på {{address}}) vill ansluta. Lägg till ny enhet?",
@@ -250,9 +251,9 @@
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning vid givet intervall och misslyckades med att ställa in bevakning av ändringar, försöker igen var 1:e minut:",
"Permanently add it to the ignore list, suppressing further notifications.": "Lägg till det permanent i ignoreringslistan och undertryck ytterligare aviseringar.",
"Permissions": "Behörigheter",
"Please consult the release notes before performing a major upgrade.": "Vänligen läs igenom versionsnyheterna innan du utför en större uppgradering.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Vänligen ställ in en autentiseringsanvändare och ett lösenord för det grafiska användargränssnittet i dialogrutan Inställningar.",
"Please wait": "Vänligen vänta",
"Please consult the release notes before performing a major upgrade.": "Läs igenom versionsnyheterna innan du utför en större uppgradering.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ställ in en autentiseringsanvändare och ett lösenord för det grafiska användargränssnittet i dialogrutan Inställningar.",
"Please wait": "Vänta",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix som indikerar att filen kan tas bort om den förhindrar mappborttagning",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix som indikerar att mönstret ska matchas utan skiftlägeskänslighet",
"Preparing to Sync": "Förberedelser för synkronisering",
@@ -346,7 +347,7 @@
"Syncthing is upgrading.": "Syncthing uppgraderas.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing stöder nu automatiskt kraschrapportering till utvecklarna. Denna funktion är aktiverad som standard.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar vara avstängd eller så är det problem med din internetanslutning. Försöker igen...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing verkar ha drabbats av ett problem med behandlingen av din förfrågan. Vänligen uppdatera sidan eller starta om Syncthing om problemet kvarstår.",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing verkar ha drabbats av ett problem med behandlingen av din förfrågan. Uppdatera sidan eller starta om Syncthing om problemet kvarstår.",
"Take me back": "Ta mig tillbaka",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Den grafiska gränssnittsadressen åsidosätts av startalternativ. Ändringar här träder inte i kraft så länge åsidosättningen är på plats.",
"The Syncthing Authors": "Syncthing-upphovsmän",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Antalet versioner måste vara ett nummer och kan inte lämnas tomt.",
"The path cannot be blank.": "Sökvägen kan inte vara tom.",
"The rate limit must be a non-negative number (0: no limit)": "Frekvensgränsen måste vara ett icke-negativt tal (0: ingen gräns)",
"The remote device has not accepted sharing this folder.": "Fjärrenheten har inte accepterat delning av den här mappen.",
"The rescan interval must be a non-negative number of seconds.": "Förnyelseintervallet måste vara ett positivt antal sekunder",
"There are no devices to share this folder with.": "Det finns inga enheter att dela denna mapp med.",
"There are no file versions to restore.": "Det finns inga filversioner att återställa.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Odelade mappar",
"Untrusted": "Opålitlig",
"Up to Date": "Uppdaterad",
"Updated": "Uppdaterade",
"Updated {%file%}": "Uppdaterade {{file}}",
"Upgrade": "Uppgradering",
"Upgrade To {%version%}": "Uppgradera till {{version}}",
"Upgrading": "Uppgraderar",
@@ -417,7 +419,7 @@
"Usage reporting is always enabled for candidate releases.": "Användningsrapportering är alltid aktiverad för kandidatutgåvor.",
"Use HTTPS for GUI": "Använd HTTPS för gränssnitt",
"Use notifications from the filesystem to detect changed items.": "Använd aviseringar från filsystemet för att upptäcka ändrade objekt.",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Användarnamn/lösenord har inte ställts in för autentisering av det grafiska gränssnittet. Vänligen överväg att ställa in det.",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Användarnamn/lösenord har inte ställts in för autentisering av det grafiska gränssnittet. Överväg att ställa in det.",
"Version": "Version",
"Versions": "Versioner",
"Versions Path": "Sökväg för versioner",

View File

@@ -75,10 +75,11 @@
"Default Device": "Varsayılan Cihaz",
"Default Folder": "Varsayılan Klasör",
"Default Folder Path": "Varsayılan Klasör Yolu",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Varsayılanlar",
"Delete": "Sil",
"Delete Unexpected Items": "Beklenmeyen Öğeleri Sil",
"Deleted": "Silinen",
"Deleted {%file%}": "{{file}} silindi",
"Deselect All": "Tüm Seçimi Kaldır",
"Deselect devices to stop sharing this folder with.": "Bu klasörün paylaşımının durdurulacağı cihazların seçimini kaldırın.",
"Deselect folders to stop sharing with this device.": "Bu cihazla paylaşımı durdurulacak klasörlerin seçimini kaldırın.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Sürüm sayısı bir sayı olmak zorundadır ve boş bırakılamaz.",
"The path cannot be blank.": "Yol boş olamaz.",
"The rate limit must be a non-negative number (0: no limit)": "Hız sınırı negatif olmayan bir sayı olmak zorundadır (0: sınır yok)",
"The remote device has not accepted sharing this folder.": "Uzak cihaz bu klasörü paylaşmayı kabul etmedi.",
"The rescan interval must be a non-negative number of seconds.": "Yeniden tarama aralığı negatif olmayan bir saniye sayısı olmak zorundadır.",
"There are no devices to share this folder with.": "Bu klasörün paylaşılacağı cihazlar yok.",
"There are no file versions to restore.": "Geri yüklenecek dosya sürümleri yok.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Paylaşılmayan Klasörler",
"Untrusted": "Güvenilmez",
"Up to Date": "Güncel",
"Updated": "Güncellenen",
"Updated {%file%}": "{{file}} güncellendi",
"Upgrade": "Yükselt",
"Upgrade To {%version%}": "{{version}} Sürümüne Yükselt",
"Upgrading": "Yükseltiliyor",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Шлях до директорії по замовчанню",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Видалити",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Видалене",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "Зняти вибір з усіх",
"Deselect devices to stop sharing this folder with.": "Зніміть вибір з пристроїв, які не матимуть доступу до цієї директорії.",
"Deselect folders to stop sharing with this device.": "Deselect folders to stop sharing with this device.",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "Кількість версій повинна бути цифрою та не може бути порожньою.",
"The path cannot be blank.": "Шлях не може бути порожнім.",
"The rate limit must be a non-negative number (0: no limit)": "Швидкість має бути додатнім числом.",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "Інтервал повторного сканування повинен бути неід’ємною величиною.",
"There are no devices to share this folder with.": "Відсутні пристрої, які мають доступ до цієї директорії.",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "Актуальна версія",
"Updated": "Оновлено",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "Оновлення",
"Upgrade To {%version%}": "Оновити до {{version}}",
"Upgrading": "Оновлення",

View File

@@ -11,7 +11,7 @@
"Add Folder": "添加文件夹",
"Add Remote Device": "添加远程设备",
"Add devices from the introducer to our device list, for mutually shared folders.": "将这个设备上那些,跟本机有着共同文件夹的“远程设备”,都添加到本机的“远程设备”列表。",
"Add ignore patterns": "Add ignore patterns",
"Add ignore patterns": "增加忽略模式",
"Add new folder?": "添加新文件夹?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "另外,完整重新扫描的间隔将增大(时间 60以新的默认 1 小时为例)。你也可以在选择“否”后手动配置每个文件夹的时间。",
"Address": "地址",
@@ -19,16 +19,16 @@
"Advanced": "高级",
"Advanced Configuration": "高级配置",
"All Data": "所有数据",
"All Time": "All Time",
"All Time": "所有时间",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "与此设备共享的所有文件夹都必须有密码保护,这样所有发送的数据在没有密码的情况下是不可读的。",
"Allow Anonymous Usage Reporting?": "允许匿名使用报告?",
"Allowed Networks": "允许的网络",
"Alphabetic": "字母顺序",
"Altered by ignoring deletes.": "Altered by ignoring deletes.",
"Altered by ignoring deletes.": "被“忽略删除”修改。",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "外部命令接管了版本控制。该外部命令必须自行从共享文件夹中删除该文件。如果此应用程序的路径包含空格,应该用半角引号括起来。",
"Anonymous Usage Reporting": "匿名使用报告",
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用情况的报告格式已经变更。是否要迁移到新的格式?",
"Apply": "Apply",
"Apply": "应用",
"Are you sure you want to continue?": "您确定要继续吗?",
"Are you sure you want to override all remote changes?": "您确定要覆盖所有远程更改吗?  ",
"Are you sure you want to permanently delete all these files?": "确认要永久删除这些文件吗?",
@@ -68,17 +68,18 @@
"Copyright © 2014-2019 the following Contributors:": "版权所有 © 2014-2019 以下贡献者:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "正在创建忽略模式,覆盖位于 {{path}} 的已有文件。",
"Currently Shared With Devices": "当前设备已共享",
"Custom Range": "Custom Range",
"Custom Range": "自定义范围",
"Danger!": "危险!",
"Debugging Facilities": "调试功能",
"Default Configuration": "默认配置",
"Default Device": "默认设备",
"Default Folder": "默认文件夹",
"Default Folder Path": "默认文件夹路径",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "默认值",
"Delete": "删除",
"Delete Unexpected Items": "删除特殊项目",
"Deleted": "已删除",
"Deleted {%file%}": "{{file}} 已删除",
"Deselect All": "取消全选",
"Deselect devices to stop sharing this folder with.": "反选设备以停止共享此文件夹",
"Deselect folders to stop sharing with this device.": "取消选择文件夹以停止与此设备共享。",
@@ -131,7 +132,7 @@
"Error": "错误",
"External File Versioning": "外部版本控制",
"Failed Items": "失败的项目",
"Failed to load file versions.": "Failed to load file versions.",
"Failed to load file versions.": "加载文件版本失败。",
"Failed to load ignore patterns.": "加载忽略模式失败。",
"Failed to setup, retrying": "设置失败,正在重试。",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "如果本机没有配置IPv6则无法连接IPv6服务器是正常的。",
@@ -174,7 +175,7 @@
"Ignore": "忽略",
"Ignore Patterns": "忽略模式",
"Ignore Permissions": "忽略文件权限",
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.",
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "只有在文件夹创建后,才能对其增加忽略模式。勾选后,会在保存后提供一个设置忽略模式的输入框。",
"Ignored Devices": "已忽略的设备",
"Ignored Folders": "已忽略的文件夹",
"Ignored at": "已忽略于",
@@ -186,9 +187,9 @@
"Keep Versions": "保留版本数量",
"LDAP": "LDAP",
"Largest First": "大文件优先",
"Last 30 Days": "Last 30 Days",
"Last 7 Days": "Last 7 Days",
"Last Month": "Last Month",
"Last 30 Days": "最近30天",
"Last 7 Days": "最近7天",
"Last Month": "上个月",
"Last Scan": "最后扫描",
"Last seen": "最后可见",
"Latest Change": "最后更改",
@@ -299,7 +300,7 @@
"Select the folders to share with this device.": "选择与该设备共享的文件夹。",
"Send & Receive": "发送与接收",
"Send Only": "仅发送",
"Set Ignores on Added Folder": "Set Ignores on Added Folder",
"Set Ignores on Added Folder": "在增加的文件夹中设置忽略项",
"Settings": "设置",
"Share": "共享",
"Share Folder": "共享文件夹",
@@ -378,22 +379,23 @@
"The number of versions must be a number and cannot be blank.": "保留版本数量必须为数字,且不能为空。",
"The path cannot be blank.": "路径不能为空。",
"The rate limit must be a non-negative number (0: no limit)": "传输速度限制为非负整数0 表示不限制)",
"The remote device has not accepted sharing this folder.": "远程设备尚未允许分享此文件夹。",
"The rescan interval must be a non-negative number of seconds.": "扫描间隔单位为秒,且不能为负数。",
"There are no devices to share this folder with.": "没有设备共享此文件夹",
"There are no file versions to restore.": "There are no file versions to restore.",
"There are no file versions to restore.": "没有可供恢复的文件版本。",
"There are no folders to share with this device.": "没有文件夹与此设备共享。",
"They are retried automatically and will be synced when the error is resolved.": "系统将会自动重试,当错误被解决时,它们将会被同步。",
"This Device": "当前设备",
"This Month": "This Month",
"This Month": "本月",
"This can easily give hackers access to read and change any files on your computer.": "这会让骇客能够轻而易举地访问及修改您的文件。",
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "此设备无法自动发现其它设备或广播自己的地址被其他人发现。只有具有静态配置地址的设备才能连接。",
"This is a major version upgrade.": "这是一个重大版本更新。",
"This setting controls the free space required on the home (i.e., index database) disk.": "此设置控制主(例如索引数据库)磁盘上需要的可用空间。",
"Time": "时间",
"Time the item was last modified": "该项最近修改的时间",
"Today": "Today",
"Today": "今天",
"Trash Can File Versioning": "回收站式版本控制",
"Twitter": "Twitter",
"Twitter": "推特",
"Type": "类型",
"UNIX Permissions": "UNIX权限",
"Unavailable": "无效",
@@ -408,7 +410,7 @@
"Unshared Folders": "非共享文件夹",
"Untrusted": "不可信的",
"Up to Date": "同步完成",
"Updated": "已更新",
"Updated {%file%}": "{{file}} 已更新",
"Upgrade": "更新",
"Upgrade To {%version%}": "升级至版本 {{version}}",
"Upgrading": "升级中",
@@ -437,7 +439,7 @@
"When adding a new device, keep in mind that this device must be added on the other side too.": "若您在本机添加新设备,记住您也必须在这个新设备上添加本机。",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "若你添加了新文件夹,记住文件夹 ID 是用以在不同设备间建立联系的。在不同设备间拥有相同 ID 的文件夹将会被同步。且文件夹 ID 区分大小写。",
"Yes": "是",
"Yesterday": "Yesterday",
"Yesterday": "昨天",
"You can also select one of these nearby devices:": "您也可以从这些附近的设备中选择:",
"You can change your choice at any time in the Settings dialog.": "您可以在任何时候在设置对话框中更改选择。",
"You can read more about the two release channels at the link below.": "您可以从以下链接读取更多关于两个发行渠道的信息。",

View File

@@ -75,10 +75,11 @@
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "默認文件夾路徑",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "Defaults",
"Delete": "Delete",
"Delete Unexpected Items": "刪除不需要項目",
"Deleted": "已刪除",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "取消全選",
"Deselect devices to stop sharing this folder with.": "反選設備以停止共享此文件夾",
"Deselect folders to stop sharing with this device.": "停止選擇文件夾以停止與此設備共享。",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "保留版本數量必須為數字,且不能為空。",
"The path cannot be blank.": "路徑不能為空。",
"The rate limit must be a non-negative number (0: no limit)": "傳輸速度限制為非負整數0 表示不限制)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "掃瞄間隔單位為秒,且不能為負數。",
"There are no devices to share this folder with.": "沒有設備共享此文件夾",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "未共享的文件夾",
"Untrusted": "不信任",
"Up to Date": "同步完成",
"Updated": "已更新",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "更新",
"Upgrade To {%version%}": "升級至版本 {{version}}",
"Upgrading": "升級中",

View File

@@ -75,10 +75,11 @@
"Default Device": "預設裝置",
"Default Folder": "預設資料夾",
"Default Folder Path": "預設資料夾路徑",
"Default Ignore Patterns": "Default Ignore Patterns",
"Defaults": "預設",
"Delete": "刪除",
"Delete Unexpected Items": "刪除不預期的項目",
"Deleted": "已刪除",
"Deleted {%file%}": "Deleted {{file}}",
"Deselect All": "取消選取全部",
"Deselect devices to stop sharing this folder with.": "取消選擇裝置以停用與其共享資料夾。",
"Deselect folders to stop sharing with this device.": "取消選擇資料夾以停用與此裝置共享。",
@@ -378,6 +379,7 @@
"The number of versions must be a number and cannot be blank.": "每個檔案要保留的舊版本數量必須是數字且不能為空白。",
"The path cannot be blank.": "路徑不能空白。",
"The rate limit must be a non-negative number (0: no limit)": "限制速率必須為非負的數字 (0: 不設限制)",
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
"The rescan interval must be a non-negative number of seconds.": "重新掃描間隔必須為一個非負數的秒數。",
"There are no devices to share this folder with.": "沒有裝置可以共享此資料夾。",
"There are no file versions to restore.": "There are no file versions to restore.",
@@ -408,7 +410,7 @@
"Unshared Folders": "未共享的資料夾",
"Untrusted": "不信任",
"Up to Date": "最新",
"Updated": "已更新",
"Updated {%file%}": "Updated {{file}}",
"Upgrade": "更新",
"Upgrade To {%version%}": "更新至 {{version}}",
"Upgrading": "正在升級",

View File

@@ -1 +1 @@
var langPrettyprint = {"bg":"Bulgarian","ca@valencia":"Catalan (Valencian)","cs":"Czech","da":"Danish","de":"German","el":"Greek","en":"English","en-AU":"English (Australia)","en-GB":"English (United Kingdom)","eo":"Esperanto","es":"Spanish","es-ES":"Spanish (Spain)","eu":"Basque","fi":"Finnish","fr":"French","fy":"Western Frisian","hu":"Hungarian","id":"Indonesian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ro-RO":"Romanian (Romania)","ru":"Russian","sk":"Slovak","sl":"Slovenian","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-HK":"Chinese (Hong Kong)","zh-TW":"Chinese (Taiwan)"}
var langPrettyprint = {"bg":"Bulgarian","ca@valencia":"Catalan (Valencian)","cs":"Czech","da":"Danish","de":"German","el":"Greek","en":"English","en-AU":"English (Australia)","en-GB":"English (United Kingdom)","eo":"Esperanto","es":"Spanish","es-ES":"Spanish (Spain)","eu":"Basque","fi":"Finnish","fr":"French","fy":"Western Frisian","hu":"Hungarian","id":"Indonesian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ro-RO":"Romanian (Romania)","ru":"Russian","sl":"Slovenian","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-HK":"Chinese (Hong Kong)","zh-TW":"Chinese (Taiwan)"}

View File

@@ -1 +1 @@
var validLangs = ["bg","ca@valencia","cs","da","de","el","en","en-AU","en-GB","eo","es","es-ES","eu","fi","fr","fy","hu","id","it","ja","ko-KR","lt","nb","nl","pl","pt-BR","pt-PT","ro-RO","ru","sk","sl","sv","tr","uk","zh-CN","zh-HK","zh-TW"]
var validLangs = ["bg","ca@valencia","cs","da","de","el","en","en-AU","en-GB","eo","es","es-ES","eu","fi","fr","fy","hu","id","it","ja","ko-KR","lt","nb","nl","pl","pt-BR","pt-PT","ro-RO","ru","sl","sv","tr","uk","zh-CN","zh-HK","zh-TW"]

View File

@@ -532,7 +532,9 @@
</tr>
<tr>
<th><span class="fas fa-fw fa-share-alt"></span>&nbsp;<span translate>Shared With</span></th>
<td class="text-right" ng-attr-title="{{sharesFolder(folder)}}">{{sharesFolder(folder)}}</td>
<td class="text-right">
<span tooltip data-original-title="{{sharesFolder(folder)}} {{folderHasUnacceptedDevices(folder) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : ''}} {{folderHasPausedDevices(folder) ? '<br/>(<sup>2</sup>' + ('The remote device has paused this folder.' | translate) + ')' : ''}}" ng-bind-html="sharesFolder(folder)"></span>
</td>
</tr>
<tr ng-if="folderStats[folder.id].lastScan">
<th><span class="far fa-fw fa-clock"></span>&nbsp;<span translate>Last Scan</span></th>
@@ -545,9 +547,8 @@
<th><span class="fas fa-fw fa-exchange-alt"></span>&nbsp;<span translate>Latest Change</span></th>
<td class="text-right">
<span tooltip data-original-title="{{folderStats[folder.id].lastFile.filename}} @ {{folderStats[folder.id].lastFile.at | date:'yyyy-MM-dd HH:mm:ss'}}">
<span translate ng-if="!folderStats[folder.id].lastFile.deleted">Updated</span>
<span translate ng-if="folderStats[folder.id].lastFile.deleted">Deleted</span>
{{folderStats[folder.id].lastFile.filename | basename}}
<span translate translate-value-file="{{folderStats[folder.id].lastFile.filename | basename}}" ng-if="!folderStats[folder.id].lastFile.deleted">Updated {%file%}</span>
<span translate translate-value-file="{{folderStats[folder.id].lastFile.filename | basename}}" ng-if="folderStats[folder.id].lastFile.deleted">Deleted {%file%}</span>
</span>
</td>
</tr>
@@ -667,9 +668,9 @@
<th><span class="fas fa-fw fa-sitemap"></span>&nbsp;<span translate>Listeners</span></th>
<td class="text-right">
<span class="data" tooltip data-original-title="{{'Show detailed listener status' | translate}}.">
<a href="" ng-class="{'text-success': listenersFailed.length == 0, 'text-danger': listenersFailed.length == listenersTotal}" ng-click="showListenerStatus()">
{{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
</a>
<a href="" ng-class="{'text-success': listenersTotal > 0 && listenersFailed.length == 0, 'text-danger': listenersTotal > 0 && listenersFailed.length == listenersTotal}" ng-click="showListenerStatus()">
{{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
</a>
</span>
</td>
</tr>
@@ -678,8 +679,8 @@
<td class="text-right">
<span class="data" tooltip data-original-title="{{'Show detailed discovery status' | translate}}.">
<a href="" ng-class="{'text-success': discoveryFailed.length == 0, 'text-danger': discoveryFailed.length == discoveryTotal}" ng-click="showDiscoveryStatus()">
{{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}
</a>
{{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}
</a>
</span>
</td>
</tr>
@@ -833,7 +834,9 @@
</tr>
<tr ng-if="deviceFolders(deviceCfg).length > 0">
<th><span class="fas fa-fw fa-folder"></span>&nbsp;<span translate>Folders</span></th>
<td class="text-right" ng-attr-title="{{deviceFolders(deviceCfg).map(folderLabel).join(', ')}}">{{deviceFolders(deviceCfg).map(folderLabel).join(", ")}}</td>
<td class="text-right">
<span tooltip data-original-title="{{sharedFolders(deviceCfg)}} {{deviceHasUnacceptedFolders(deviceCfg) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : '' }} {{deviceHasPausedFolders(deviceCfg) ? '<br/>(<sup>2</sup>' + ('The remote device has paused this folder.' | translate) + ')' : '' }}" ng-bind-html="sharedFolders(deviceCfg)"></span>
</td>
</tr>
<tr ng-if="deviceCfg.remoteGUIPort > 0">
<th><span class="fas fa-fw fa-desktop"></span>&nbsp;<span translate>Remote GUI</span></th>

View File

@@ -19,7 +19,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Syncthing Release Automation, Tomasz Wilczyński, Wulf Weich, dependabot-preview[bot], dependabot[bot], greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eric Lesiuta, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, Kalle Laine, Karol Różycki, Kebin Liu, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Syncthing Automation, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, deepsource-autofix[bot], derekriemer, desbma, georgespatton, ghjklw, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, marco-m, mclang, mv1005, otbutz, overkill, perewa, rubenbe, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaya Chithra, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, Kalle Laine, Karol Różycki, Kebin Liu, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, derekriemer, desbma, georgespatton, ghjklw, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, marco-m, mclang, mv1005, otbutz, overkill, perewa, red_led, rubenbe, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙
</div>
</div>
<hr />

View File

@@ -1,14 +1,14 @@
<div class="col-md-6 checkbox">
<label for="sharedwith-{{id}}">
<input id="sharedwith-{{id}}" ng-model="selected[id]" type="checkbox" />
<span tooltip data-original-title="{{id}}">{{label}}</span>
<span tooltip data-original-title="{{id}}" ng-bind-html="label"></span>
</label>
</div>
<div class="col-md-6">
<div class="input-group">
<span class="input-group-addon" ng-switch="folderType !== 'receiveencrypted' && !encryptionPasswords[id]">
<span ng-switch-when='true' class="fas fa-fw fa-unlock" />
<span ng-switch-default class="fas fa-fw fa-lock" />
<span ng-switch-when='true' class="fas fa-fw fa-unlock"></span>
<span ng-switch-default class="fas fa-fw fa-lock"></span>
</span>
<span ng-switch="folderType === 'receiveencrypted'">
<span ng-switch-when='true'>
@@ -30,10 +30,10 @@
</span>
<span ng-switch="selected[id] && folderType !== 'receiveencrypted'" class="input-group-addon">
<span ng-switch-when='true'>
<span class="button fas fa-fw fa-eye" ng-click="togglePasswordVisibility()" />
<span class="button fas fa-fw fa-eye" ng-click="togglePasswordVisibility()"></span>
</span>
<span ng-switch-default>
<span class="button fas fa-fw fa-eye" disabled />
<span class="button fas fa-fw fa-eye" disabled></span>
</span>
</span>
</div>

View File

@@ -64,19 +64,27 @@ angular.module('syncthing.core')
PENDING_DEVICES_CHANGED: 'PendingDevicesChanged', // Emitted when pending devices were added / updated (connection from unknown ID) or removed (device is ignored or added)
DEVICE_PAUSED: 'DevicePaused', // Emitted when a device has been paused
DEVICE_RESUMED: 'DeviceResumed', // Emitted when a device has been resumed
CLUSTER_CONFIG_RECEIVED: 'ClusterConfigReceived', // Emitted when receiving a remote device's cluster config
DOWNLOAD_PROGRESS: 'DownloadProgress', // Emitted during file downloads for each folder for each file
FAILURE: 'Failure', // Specific errors sent to the usage reporting server for diagnosis
FOLDER_COMPLETION: 'FolderCompletion', //Emitted when the local or remote contents for a folder changes
FOLDER_REJECTED: 'FolderRejected', // DEPRECATED: Emitted when a device sends index information for a folder we do not have, or have but do not share with the device in question
PENDING_FOLDERS_CHANGED: 'PendingFoldersChanged', // Emitted when pending folders were added / updated (offered by some device, but not shared to them) or removed (folder ignored or added or no longer offered from the remote device)
FOLDER_SUMMARY: 'FolderSummary', // Emitted when folder contents have changed locally
ITEM_FINISHED: 'ItemFinished', // Generated when Syncthing ends synchronizing a file to a newer version
ITEM_STARTED: 'ItemStarted', // Generated when Syncthing begins synchronizing a file to a newer version
LISTEN_ADDRESSES_CHANGED: 'ListenAddressesChanged', // Listen address resolution has changed.
LOCAL_CHANGE_DETECTED: 'LocalChangeDetected', // Generated upon scan whenever the local disk has discovered an updated file from the previous scan.
LOCAL_INDEX_UPDATED: 'LocalIndexUpdated', // Generated when the local index information has changed, due to synchronizing one or more items from the cluster or discovering local changes during a scan
LOGIN_ATTEMPT: 'LoginAttempt', // Emitted on every login attempt when authentication is enabled for the GUI.
REMOTE_CHANGE_DETECTED: 'RemoteChangeDetected', // Generated upon scan whenever a file is locally updated due to a remote change.
REMOTE_DOWNLOAD_PROGRESS: 'RemoteDownloadProgress', // DownloadProgress message received from a connected remote device.
REMOTE_INDEX_UPDATED: 'RemoteIndexUpdated', // Generated each time new index information is received from a device
STARTING: 'Starting', // Emitted exactly once, when Syncthing starts, before parsing configuration etc
STARTUP_COMPLETED: 'StartupCompleted', // Emitted exactly once, when initialization is complete and Syncthing is ready to start exchanging data with other devices
STATE_CHANGED: 'StateChanged', // Emitted when a folder changes state
FOLDER_ERRORS: 'FolderErrors', // Emitted when a folder has errors preventing a full sync
FOLDER_WATCH_STATE_CHANGED: 'FolderWatchStateChanged', // Watcher routine encountered a new error, or a previous error disappeared after retrying.
FOLDER_SCAN_PROGRESS: 'FolderScanProgress', // Emitted every ScanProgressIntervalS seconds, indicating how far into the scan it is at.
FOLDER_PAUSED: 'FolderPaused', // Emitted when a folder is paused
FOLDER_RESUMED: 'FolderResumed', // Emitted when a folder is resumed

View File

@@ -178,7 +178,8 @@ angular.module('syncthing.core')
console.log('HTTPError', arg);
online = false;
if (!restarting) {
// We sometimes get arg == null from angularjs - no idea why
if (!restarting && arg) {
if (arg.status === 0) {
// A network error, not an HTTP error
$scope.$emit(Events.OFFLINE);
@@ -2365,17 +2366,52 @@ angular.module('syncthing.core')
+ '&device=' + encodeURIComponent(deviceID));
};
$scope.deviceNameMarkRemoteState = function (deviceID, folderID) {
var name = $scope.deviceName($scope.devices[deviceID]);
// Add footnote if sharing was not accepted on the remote device
if (deviceID in $scope.completion && folderID in $scope.completion[deviceID]) {
if ($scope.completion[deviceID][folderID].remoteState == 'notSharing') {
name += '<sup>1</sup>';
} else if ($scope.completion[deviceID][folderID].remoteState == 'paused') {
name += '<sup>2</sup>';
}
}
return name;
};
$scope.sharesFolder = function (folderCfg) {
var names = [];
folderCfg.devices.forEach(function (device) {
if (device.deviceID !== $scope.myID) {
names.push($scope.deviceName($scope.devices[device.deviceID]));
names.push($scope.deviceNameMarkRemoteState(device.deviceID, folderCfg.id));
}
});
names.sort();
return names.join(", ");
};
$scope.folderHasUnacceptedDevices = function (folderCfg) {
for (var deviceID in $scope.completion) {
if (deviceID in $scope.devices
&& folderCfg.id in $scope.completion[deviceID]
&& $scope.completion[deviceID][folderCfg.id].remoteState == 'notSharing') {
return true;
}
}
return false;
};
$scope.folderHasPausedDevices = function (folderCfg) {
for (var deviceID in $scope.completion) {
if (deviceID in $scope.devices
&& folderCfg.id in $scope.completion[deviceID]
&& $scope.completion[deviceID][folderCfg.id].remoteState == 'paused') {
return true;
}
}
return false;
};
$scope.deviceFolders = function (deviceCfg) {
var folders = [];
$scope.folderList().forEach(function (folder) {
@@ -2397,6 +2433,53 @@ angular.module('syncthing.core')
return label && label.length > 0 ? label : folderID;
};
$scope.folderLabelMarkRemoteState = function (folderID, deviceID) {
var label = $scope.folderLabel(folderID);
// Add footnote if sharing was not accepted on the remote device
if (deviceID in $scope.completion && folderID in $scope.completion[deviceID]) {
if ($scope.completion[deviceID][folderID].remoteState == 'notSharing') {
label += '<sup>1</sup>';
} else if ($scope.completion[deviceID][folderID].remoteState == 'paused') {
label += '<sup>2</sup>';
}
}
return label;
};
$scope.sharedFolders = function (deviceCfg) {
var labels = [];
$scope.deviceFolders(deviceCfg).forEach(function (folderID) {
labels.push($scope.folderLabelMarkRemoteState(folderID, deviceCfg.deviceID));
});
return labels.join(', ');
};
$scope.deviceHasUnacceptedFolders = function (deviceCfg) {
if (!(deviceCfg.deviceID in $scope.completion)) {
return false;
}
for (var folderID in $scope.completion[deviceCfg.deviceID]) {
if (folderID in $scope.folders
&& $scope.completion[deviceCfg.deviceID][folderID].remoteState == 'notSharing') {
return true;
}
}
return false;
};
$scope.deviceHasPausedFolders = function (deviceCfg) {
if (!(deviceCfg.deviceID in $scope.completion)) {
return false;
}
for (var folderID in $scope.completion[deviceCfg.deviceID]) {
if (folderID in $scope.folders
&& $scope.completion[deviceCfg.deviceID][folderID].remoteState == 'paused') {
return true;
}
}
return false;
};
$scope.deleteFolder = function (id) {
hideFolderModal();
if ($scope.currentFolder._editing != "existing") {
@@ -2777,6 +2860,12 @@ angular.module('syncthing.core')
$scope.advancedConfig = angular.copy($scope.config);
$scope.advancedConfig.devices.sort(deviceCompare);
$scope.advancedConfig.folders.sort(folderCompare);
$scope.advancedConfig.defaults.ignores._lines = function (newValue) {
if (arguments.length) {
$scope.advancedConfig.defaults.ignores.lines = newValue.split('\n');
}
return $scope.advancedConfig.defaults.ignores.lines.join('\n');
};
$('#advanced').modal('show');
};

View File

@@ -83,8 +83,14 @@
<a href="#" ng-click="selectAllSharedFolders(false)" translate>Deselect All</a></small>
</p>
<div class="form-group" ng-repeat="folder in currentSharing.shared">
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{folder.id}}" label="{{folderLabel(folder.id)}}" folder-type="{{folder.type}}" untrusted="currentDevice.untrusted" />
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{folder.id}}" label="{{folderLabelMarkUnaccepted(folder.id, currentDevice.deviceID)}}" folder-type="{{folder.type}}" untrusted="currentDevice.untrusted" />
</div>
<p class="help-block" ng-if="deviceHasUnacceptedFolders(currentDevice)">
<sup>1</sup> <span translate>The remote device has not accepted sharing this folder.</span>
</p>
<p class="help-block" ng-if="deviceHasPausedFolders(currentDevice)">
<sup>2</sup> <span translate>The remote device has paused this folder.</span>
</p>
</div>
<div class="form-horizontal" ng-if="currentSharing.unrelated.length">
<label translate for="folders">Unshared Folders</label>

View File

@@ -56,8 +56,14 @@
<a href="#" ng-click="selectAllSharedDevices(false)" translate>Deselect All</a></small>
</p>
<div class="form-group" ng-repeat="device in currentSharing.shared">
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{device.deviceID}}" label="{{deviceName(device)}}" folder-type="{{currentFolder.type}}" untrusted="device.untrusted || pendingIsRemoteEncrypted(currentFolder.id, device.deviceID)" />
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{device.deviceID}}" label="{{deviceNameMarkUnaccepted(device.deviceID, currentFolder.id)}}" folder-type="{{currentFolder.type}}" untrusted="device.untrusted || pendingIsRemoteEncrypted(currentFolder.id, device.deviceID)" />
</div>
<p class="help-block" ng-if="folderHasUnacceptedDevices(currentFolder)">
<sup>1</sup> <span translate>The remote device has not accepted sharing this folder.</span>
</p>
<p class="help-block" ng-if="folderHasPausedDevices(currentFolder)">
<sup>2</sup> <span translate>The remote device has paused this folder.</span>
</p>
</div>
<div class="form-horizontal" ng-if="currentSharing.unrelated.length || otherDevices().length <= 0">
<label translate>Unshared Devices</label>

View File

@@ -167,6 +167,26 @@
</form>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="advancedDefaultIgnoresHeading" data-toggle="collapse" data-parent="#advancedDefaults" href="#advancedDefaultIgnores" aria-expanded="false" aria-controls="advancedDefaultIgnores" style="cursor: pointer;">
<h4 class="panel-title" tabindex="0" translate>Default Ignore Patterns</h4>
</div>
<div id="advancedDefaultIgnores" class="panel-collapse collapse" role="tabpanel" aria-labelledby="advancedDefaultIgnoresHeading">
<form class="form-horizontal" role="form">
<div ng-repeat="(key, value) in advancedConfig.defaults.ignores" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
<label for="advancedDefaultIgnoresInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}&nbsp;<a href="{{docsURL('users/config#config-option-defaults.ignores.')}}{{key | lowercase}}" target="_blank"><span class="fas fa-question-circle"></span></a></label>
<div class="col-sm-8">
<div ng-switch="key">
<!-- Special case to preserve empty lines in multi-line input -->
<textarea ng-switch-when="lines" id="advancedDefaultIgnoresInput{{$index}}" class="form-control" rows="5" ng-model="advancedConfig.defaults.ignores._lines" ng-model-options="{ getterSetter: true }"></textarea>
<input ng-switch-default ng-if="inputTypeFor(key, value) == 'list'" id="advancedDefaultIgnoresInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.defaults.ignores[key]" ng-list />
<input ng-switch-default ng-if="inputTypeFor(key, value) != 'list'" id="advancedDefaultIgnoresInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.defaults.ignores[key]" />
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

View File

@@ -59,6 +59,8 @@ import (
)
const (
// Default mask excludes these very noisy event types to avoid filling the pipe.
// FIXME: ItemStarted and ItemFinished should be excluded for the same reason.
DefaultEventMask = events.AllEvents &^ events.LocalChangeDetected &^ events.RemoteChangeDetected
DiskEventMask = events.LocalChangeDetected | events.RemoteChangeDetected
EventSubBufferSize = 1000

View File

@@ -46,6 +46,7 @@ type apiKeyValidator interface {
func newCsrfManager(unique string, prefix string, apiKeyValidator apiKeyValidator, next http.Handler, saveLocation string) *csrfManager {
m := &csrfManager{
tokensMut: sync.NewMutex(),
tokens: make([]string, 0, maxCsrfTokens),
unique: unique,
prefix: prefix,
apiKeyValidator: apiKeyValidator,
@@ -108,7 +109,7 @@ func (m *csrfManager) validToken(token string) bool {
// Move this token to the head of the list. Copy the tokens at
// the front one step to the right and then replace the token
// at the head.
copy(m.tokens[1:], m.tokens[:i+1])
copy(m.tokens[1:], m.tokens[:i])
m.tokens[0] = token
}
return true
@@ -121,12 +122,14 @@ func (m *csrfManager) newToken() string {
token := rand.String(32)
m.tokensMut.Lock()
m.tokens = append([]string{token}, m.tokens...)
if len(m.tokens) > maxCsrfTokens {
m.tokens = m.tokens[:maxCsrfTokens]
}
defer m.tokensMut.Unlock()
if len(m.tokens) < maxCsrfTokens {
m.tokens = append(m.tokens, "")
}
copy(m.tokens[1:], m.tokens)
m.tokens[0] = token
m.save()
return token

View File

@@ -18,6 +18,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
@@ -35,6 +36,7 @@ import (
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/logger"
loggermocks "github.com/syncthing/syncthing/lib/logger/mocks"
"github.com/syncthing/syncthing/lib/model"
modelmocks "github.com/syncthing/syncthing/lib/model/mocks"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/svcutil"
@@ -72,10 +74,10 @@ func TestMain(m *testing.M) {
func TestCSRFToken(t *testing.T) {
t.Parallel()
max := 250
max := 10 * maxCsrfTokens
int := 5
if testing.Short() {
max = 20
max = 1 + maxCsrfTokens
int = 2
}
@@ -89,6 +91,11 @@ func TestCSRFToken(t *testing.T) {
t.Fatal("t3 should be valid")
}
valid := make(map[string]struct{}, maxCsrfTokens)
for _, token := range m.tokens {
valid[token] = struct{}{}
}
for i := 0; i < max; i++ {
if i%int == 0 {
// t1 and t2 should remain valid by virtue of us checking them now
@@ -101,11 +108,27 @@ func TestCSRFToken(t *testing.T) {
}
}
if len(m.tokens) == maxCsrfTokens {
// We're about to add a token, which will remove the last token
// from m.tokens.
delete(valid, m.tokens[len(m.tokens)-1])
}
// The newly generated token is always valid
t4 := m.newToken()
if !m.validToken(t4) {
t.Fatal("t4 should be valid at iteration", i)
}
valid[t4] = struct{}{}
v := make(map[string]struct{}, maxCsrfTokens)
for _, token := range m.tokens {
v[token] = struct{}{}
}
if !reflect.DeepEqual(v, valid) {
t.Fatalf("want valid tokens %v, got %v", valid, v)
}
}
if m.validToken(t3) {
@@ -260,7 +283,7 @@ func TestAPIServiceRequests(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer cancel()
t.Cleanup(cancel)
cases := []httpTestCase{
// /rest/db
@@ -298,6 +321,12 @@ func TestAPIServiceRequests(t *testing.T) {
Type: "application/json",
Prefix: "null",
},
{
URL: "/rest/db/status?folder=default",
Code: 200,
Type: "application/json",
Prefix: "",
},
// /rest/stats
{
@@ -466,14 +495,17 @@ func TestAPIServiceRequests(t *testing.T) {
}
for _, tc := range cases {
t.Log("Testing", tc.URL, "...")
testHTTPRequest(t, baseURL, tc, testAPIKey)
t.Run(cases[0].URL, func(t *testing.T) {
testHTTPRequest(t, baseURL, tc, testAPIKey)
})
}
}
// testHTTPRequest tries the given test case, comparing the result code,
// content type, and result prefix.
func testHTTPRequest(t *testing.T, baseURL string, tc httpTestCase, apikey string) {
// Should not be parallelized, as that just causes timeouts eventually with more test-cases
timeout := time.Second
if tc.Timeout > 0 {
timeout = tc.Timeout
@@ -608,7 +640,7 @@ func startHTTP(cfg config.Wrapper) (string, context.CancelFunc, error) {
}
addrChan := make(chan string)
mockedSummary := &modelmocks.FolderSummaryService{}
mockedSummary.SummaryReturns(map[string]interface{}{"mocked": true}, nil)
mockedSummary.SummaryReturns(new(model.FolderSummary), nil)
// Instantiate the API service
urService := ur.New(cfg, m, connections, false)
@@ -1136,11 +1168,7 @@ func TestBrowse(t *testing.T) {
pathSep := string(os.PathSeparator)
tmpDir, err := os.MkdirTemp("", "syncthing")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
tmpDir := t.TempDir()
if err := os.Mkdir(filepath.Join(tmpDir, "dir"), 0755); err != nil {
t.Fatal(err)

View File

@@ -457,7 +457,7 @@ func TestIssue1262(t *testing.T) {
t.Fatal(err)
}
actual := cfg.Folders()["test"].Filesystem().URI()
actual := cfg.Folders()["test"].Filesystem(nil).URI()
expected := `e:\`
if actual != expected {
@@ -494,7 +494,7 @@ func TestFolderPath(t *testing.T) {
Path: "~/tmp",
}
realPath := folder.Filesystem().URI()
realPath := folder.Filesystem(nil).URI()
if !filepath.IsAbs(realPath) {
t.Error(realPath, "should be absolute")
}
@@ -504,13 +504,10 @@ func TestFolderPath(t *testing.T) {
}
func TestFolderCheckPath(t *testing.T) {
n, err := os.MkdirTemp("", "")
if err != nil {
t.Fatal(err)
}
n := t.TempDir()
testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, n)
err = os.MkdirAll(filepath.Join(n, "dir", ".stfolder"), os.FileMode(0777))
err := os.MkdirAll(filepath.Join(n, "dir", ".stfolder"), os.FileMode(0777))
if err != nil {
t.Fatal(err)
}
@@ -602,11 +599,7 @@ func TestWindowsLineEndings(t *testing.T) {
t.Skip("Windows specific")
}
dir, err := os.MkdirTemp("", "syncthing-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
dir := t.TempDir()
path := filepath.Join(dir, "config.xml")
os.Remove(path)

View File

@@ -16,6 +16,7 @@ import (
"github.com/shirou/gopsutil/v3/disk"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
@@ -42,24 +43,29 @@ func (f FolderConfiguration) Copy() FolderConfiguration {
return c
}
func (f FolderConfiguration) Filesystem() fs.Filesystem {
// Filesystem creates a filesystem for the path and options of this folder.
// The fset parameter may be nil, in which case no mtime handling on top of
// the fileystem is provided.
func (f FolderConfiguration) Filesystem(fset *db.FileSet) fs.Filesystem {
// This is intentionally not a pointer method, because things like
// cfg.Folders["default"].Filesystem() should be valid.
var opts []fs.Option
// cfg.Folders["default"].Filesystem(nil) should be valid.
opts := make([]fs.Option, 0, 3)
if f.FilesystemType == fs.FilesystemTypeBasic && f.JunctionsAsDirs {
opts = append(opts, new(fs.OptionJunctionsAsDirs))
}
filesystem := fs.NewFilesystem(f.FilesystemType, f.Path, opts...)
if !f.CaseSensitiveFS {
filesystem = fs.NewCaseFilesystem(filesystem)
opts = append(opts, new(fs.OptionDetectCaseConflicts))
}
return filesystem
if fset != nil {
opts = append(opts, fset.MtimeOption())
}
return fs.NewFilesystem(f.FilesystemType, f.Path, opts...)
}
func (f FolderConfiguration) ModTimeWindow() time.Duration {
dur := time.Duration(f.RawModTimeWindowS) * time.Second
if f.RawModTimeWindowS < 1 && runtime.GOOS == "android" {
if usage, err := disk.Usage(f.Filesystem().URI()); err != nil {
if usage, err := disk.Usage(f.Filesystem(nil).URI()); err != nil {
dur = 2 * time.Second
l.Debugf(`Detecting FS at "%v" on android: Setting mtime window to 2s: err == "%v"`, f.Path, err)
} else if strings.HasPrefix(strings.ToLower(usage.Fstype), "ext2") || strings.HasPrefix(strings.ToLower(usage.Fstype), "ext3") || strings.HasPrefix(strings.ToLower(usage.Fstype), "ext4") {
@@ -89,7 +95,7 @@ func (f *FolderConfiguration) CreateMarker() error {
// begin with.
permBits = 0700
}
fs := f.Filesystem()
fs := f.Filesystem(nil)
err := fs.Mkdir(DefaultMarkerName, permBits)
if err != nil {
return err
@@ -106,7 +112,7 @@ func (f *FolderConfiguration) CreateMarker() error {
// CheckPath returns nil if the folder root exists and contains the marker file
func (f *FolderConfiguration) CheckPath() error {
fi, err := f.Filesystem().Stat(".")
fi, err := f.Filesystem(nil).Stat(".")
if err != nil {
if !fs.IsNotExist(err) {
return err
@@ -124,7 +130,7 @@ func (f *FolderConfiguration) CheckPath() error {
return ErrPathNotDirectory
}
_, err = f.Filesystem().Stat(f.MarkerName)
_, err = f.Filesystem(nil).Stat(f.MarkerName)
if err != nil {
if !fs.IsNotExist(err) {
return err
@@ -145,7 +151,7 @@ func (f *FolderConfiguration) CreateRoot() (err error) {
permBits = 0700
}
filesystem := f.Filesystem()
filesystem := f.Filesystem(nil)
if _, err = filesystem.Stat("."); fs.IsNotExist(err) {
err = filesystem.MkdirAll(".", permBits)
@@ -256,7 +262,7 @@ func (f *FolderConfiguration) CheckAvailableSpace(req uint64) error {
if val <= 0 {
return nil
}
fs := f.Filesystem()
fs := f.Filesystem(nil)
usage, err := fs.Usage(".")
if err != nil {
return nil

View File

@@ -199,7 +199,7 @@ func migrateToConfigV23(cfg *Configuration) {
// marker name in later versions.
for i := range cfg.Folders {
fs := cfg.Folders[i].Filesystem()
fs := cfg.Folders[i].Filesystem(nil)
// Invalid config posted, or tests.
if fs == nil {
continue
@@ -235,18 +235,18 @@ func migrateToConfigV21(cfg *Configuration) {
switch folder.Versioning.Type {
case "simple", "trashcan":
// Clean out symlinks in the known place
cleanSymlinks(folder.Filesystem(), ".stversions")
cleanSymlinks(folder.Filesystem(nil), ".stversions")
case "staggered":
versionDir := folder.Versioning.Params["versionsPath"]
if versionDir == "" {
// default place
cleanSymlinks(folder.Filesystem(), ".stversions")
cleanSymlinks(folder.Filesystem(nil), ".stversions")
} else if filepath.IsAbs(versionDir) {
// absolute
cleanSymlinks(fs.NewFilesystem(fs.FilesystemTypeBasic, versionDir), ".")
} else {
// relative to folder
cleanSymlinks(folder.Filesystem(), versionDir)
cleanSymlinks(folder.Filesystem(nil), versionDir)
}
}
}

View File

@@ -7,10 +7,12 @@
package connections
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/url"
@@ -21,6 +23,7 @@ import (
"github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/nat"
"github.com/syncthing/syncthing/lib/protocol"
@@ -294,7 +297,7 @@ func TestNextDialRegistryCleanup(t *testing.T) {
}
}
func BenchmarkConnections(pb *testing.B) {
func BenchmarkConnections(b *testing.B) {
addrs := []string{
"tcp://127.0.0.1:0",
"quic://127.0.0.1:0",
@@ -315,9 +318,13 @@ func BenchmarkConnections(pb *testing.B) {
}
for _, addr := range addrs {
for _, sz := range sizes {
data := make([]byte, sz)
if _, err := rand.Read(data); err != nil {
b.Fatal(err)
}
for _, direction := range []string{"cs", "sc"} {
proto := strings.SplitN(addr, ":", 2)[0]
pb.Run(fmt.Sprintf("%s_%d_%s", proto, sz, direction), func(b *testing.B) {
b.Run(fmt.Sprintf("%s_%d_%s", proto, sz, direction), func(b *testing.B) {
if proto == "relay" && !haveRelay {
b.Skip("could not connect to relay")
}
@@ -325,61 +332,79 @@ func BenchmarkConnections(pb *testing.B) {
if direction == "sc" {
server, client = client, server
}
data := make([]byte, sz)
if _, err := rand.Read(data); err != nil {
b.Fatal(err)
}
total := 0
wg := sync.NewWaitGroup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
wg := sync.NewWaitGroup()
wg.Add(2)
errC := make(chan error, 2)
go func() {
if err := sendMsg(client, data); err != nil {
b.Fatal(err)
if _, err := client.Write(data); err != nil {
errC <- err
return
}
wg.Done()
}()
go func() {
if err := recvMsg(server, data); err != nil {
b.Fatal(err)
if _, err := io.ReadFull(server, data); err != nil {
errC <- err
return
}
total += sz
wg.Done()
}()
wg.Wait()
close(errC)
err := <-errC
if err != nil {
b.Fatal(err)
}
}
b.ReportAllocs()
b.SetBytes(int64(total / b.N))
})
})
}
}
}
}
func sendMsg(c internalConn, buf []byte) error {
n, err := c.Write(buf)
if n != len(buf) || err != nil {
return err
func TestConnectionEstablishment(t *testing.T) {
addrs := []string{
"tcp://127.0.0.1:0",
"quic://127.0.0.1:0",
}
send := make([]byte, 128<<10)
if _, err := rand.Read(send); err != nil {
t.Fatal(err)
}
for _, addr := range addrs {
proto := strings.SplitN(addr, ":", 2)[0]
t.Run(proto, func(t *testing.T) {
withConnectionPair(t, addr, func(client, server internalConn) {
if _, err := client.Write(send); err != nil {
t.Fatal(err)
}
recv := make([]byte, len(send))
if _, err := io.ReadFull(server, recv); err != nil {
t.Fatal(err)
}
if !bytes.Equal(recv, send) {
t.Fatal("data mismatch")
}
})
})
}
return nil
}
func recvMsg(c internalConn, buf []byte) error {
for read := 0; read != len(buf); {
n, err := c.Read(buf)
read += n
if err != nil {
return err
}
}
return nil
}
func withConnectionPair(b *testing.B, connUri string, h func(client, server internalConn)) {
func withConnectionPair(b interface{ Fatal(...interface{}) }, connUri string, h func(client, server internalConn)) {
// Root of the service tree.
supervisor := suture.New("main", suture.Spec{
PassThroughPanics: true,
@@ -414,7 +439,7 @@ func withConnectionPair(b *testing.B, connUri string, h func(client, server inte
}
natSvc := nat.NewService(deviceId, wcfg)
conns := make(chan internalConn, 1)
listenSvc := lf.New(uri, wcfg, tlsCfg, conns, natSvc)
listenSvc := lf.New(uri, wcfg, tlsCfg, conns, natSvc, registry.New())
supervisor.Add(listenSvc)
var addr *url.URL
@@ -433,7 +458,8 @@ func withConnectionPair(b *testing.B, connUri string, h func(client, server inte
if err != nil {
b.Fatal(err)
}
dialer := df.New(cfg.Options, tlsCfg)
// Purposely using a different registry: Don't want to reuse port between dialer and listener on the same device
dialer := df.New(cfg.Options, tlsCfg, registry.New())
// Relays might take some time to register the device, so dial multiple times
clientConn, err := dialer.Dial(ctx, deviceId, addr)
@@ -447,19 +473,22 @@ func withConnectionPair(b *testing.B, connUri string, h func(client, server inte
}
}
data := []byte("hello")
// Quic does not start a stream until some data is sent through, so send something for the AcceptStream
// to fire on the other side.
if err := sendMsg(clientConn, data); err != nil {
send := []byte("hello")
if _, err := clientConn.Write(send); err != nil {
b.Fatal(err)
}
serverConn := <-conns
if err := recvMsg(serverConn, data); err != nil {
recv := make([]byte, len(send))
if _, err := io.ReadFull(serverConn, recv); err != nil {
b.Fatal(err)
}
if !bytes.Equal(recv, send) {
b.Fatal("data mismatch")
}
h(clientConn, serverConn)
@@ -467,7 +496,7 @@ func withConnectionPair(b *testing.B, connUri string, h func(client, server inte
_ = serverConn.Close()
}
func mustGetCert(b *testing.B) tls.Certificate {
func mustGetCert(b interface{ Fatal(...interface{}) }) tls.Certificate {
cert, err := tlsutil.NewCertificateInMemory("bench", 10)
if err != nil {
b.Fatal(err)

View File

@@ -41,6 +41,7 @@ func init() {
type quicDialer struct {
commonDialer
registry *registry.Registry
}
func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL) (internalConn, error) {
@@ -58,7 +59,7 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
// Given we always pass the connection to quic, it assumes it's a remote connection it never closes it,
// So our wrapper around it needs to close it, but it only needs to close it if it's not the listening connection.
var createdConn net.PacketConn
listenConn := registry.Get(uri.Scheme, packetConnUnspecified)
listenConn := d.registry.Get(uri.Scheme, packetConnUnspecified)
if listenConn != nil {
conn = listenConn.(net.PacketConn)
} else {
@@ -96,7 +97,7 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
type quicDialerFactory struct{}
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, registry *registry.Registry) genericDialer {
// So the idea is that we should probably try dialing every 20 seconds.
// However it would still be nice if this was adjustable/proportional to ReconnectIntervalS
// But prevent something silly like 1/3 = 0 etc.
@@ -104,10 +105,13 @@ func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Confi
if quicInterval < 10 {
quicInterval = 10
}
return &quicDialer{commonDialer{
reconnectInterval: time.Duration(quicInterval) * time.Second,
tlsCfg: tlsCfg,
}}
return &quicDialer{
commonDialer: commonDialer{
reconnectInterval: time.Duration(quicInterval) * time.Second,
tlsCfg: tlsCfg,
},
registry: registry,
}
}
func (quicDialerFactory) Priority() int {

View File

@@ -40,11 +40,12 @@ type quicListener struct {
onAddressesChangedNotifier
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
registry *registry.Registry
address *url.URL
laddr net.Addr
@@ -100,8 +101,8 @@ func (t *quicListener) serve(ctx context.Context) error {
go svc.Serve(ctx)
registry.Register(t.uri.Scheme, conn)
defer registry.Unregister(t.uri.Scheme, conn)
t.registry.Register(t.uri.Scheme, conn)
defer t.registry.Unregister(t.uri.Scheme, conn)
listener, err := quic.Listen(conn, t.tlsCfg, quicConfig)
if err != nil {
@@ -217,13 +218,14 @@ func (f *quicListenerFactory) Valid(config.Configuration) error {
return nil
}
func (f *quicListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener {
func (f *quicListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service, registry *registry.Registry) genericListener {
l := &quicListener{
uri: fixupPort(uri, config.DefaultQUICPort),
cfg: cfg,
tlsCfg: tlsCfg,
conns: conns,
factory: f,
uri: fixupPort(uri, config.DefaultQUICPort),
cfg: cfg,
tlsCfg: tlsCfg,
conns: conns,
factory: f,
registry: registry,
}
l.ServiceWithError = svcutil.AsService(l.serve, l.String())
l.nat.Store(stun.NATUnknown)

View File

@@ -36,7 +36,7 @@ func quicNetwork(uri *url.URL) string {
}
type quicTlsConn struct {
quic.Session
quic.Connection
quic.Stream
// If we created this connection, we should be the ones closing it.
createdConn net.PacketConn
@@ -44,7 +44,7 @@ type quicTlsConn struct {
func (q *quicTlsConn) Close() error {
sterr := q.Stream.Close()
seerr := q.Session.CloseWithError(0, "closing")
seerr := q.Connection.CloseWithError(0, "closing")
var pcerr error
if q.createdConn != nil {
pcerr = q.createdConn.Close()
@@ -59,7 +59,7 @@ func (q *quicTlsConn) Close() error {
}
func (q *quicTlsConn) ConnectionState() tls.ConnectionState {
return q.Session.ConnectionState().TLS.ConnectionState
return q.Connection.ConnectionState().TLS.ConnectionState
}
func packetConnUnspecified(conn interface{}) bool {

View File

@@ -15,10 +15,6 @@ import (
"github.com/syncthing/syncthing/lib/sync"
)
var (
Default = New()
)
type Registry struct {
mut sync.Mutex
available map[string][]interface{}
@@ -85,15 +81,3 @@ func (r *Registry) Get(scheme string, preferred func(interface{}) bool) interfac
}
return best
}
func Register(scheme string, item interface{}) {
Default.Register(scheme, item)
}
func Unregister(scheme string, item interface{}) {
Default.Unregister(scheme, item)
}
func Get(scheme string, preferred func(interface{}) bool) interface{} {
return Default.Get(scheme, preferred)
}

View File

@@ -13,6 +13,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/relay/client"
@@ -68,7 +69,7 @@ func (d *relayDialer) Dial(ctx context.Context, id protocol.DeviceID, uri *url.U
type relayDialerFactory struct{}
func (relayDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
func (relayDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, _ *registry.Registry) genericDialer {
return &relayDialer{commonDialer{
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.RelayReconnectIntervalM) * time.Minute,

View File

@@ -16,6 +16,7 @@ import (
"github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/nat"
"github.com/syncthing/syncthing/lib/relay/client"
@@ -177,7 +178,7 @@ func (t *relayListener) NATType() string {
type relayListenerFactory struct{}
func (f *relayListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener {
func (f *relayListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service, _ *registry.Registry) genericListener {
t := &relayListener{
uri: uri,
cfg: cfg,

View File

@@ -12,6 +12,7 @@ package connections
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"math"
"net"
@@ -22,6 +23,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/discover"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/nat"
@@ -55,6 +57,13 @@ var (
errDisabled = fmt.Errorf("%w: disabled by configuration", errUnsupported)
errDeprecated = fmt.Errorf("%w: deprecated", errUnsupported)
errNotInBuild = fmt.Errorf("%w: disabled at build time", errUnsupported)
// Various reasons to reject a connection
errNetworkNotAllowed = errors.New("network not allowed")
errDeviceAlreadyConnected = errors.New("already connected to this device")
errDeviceIgnored = errors.New("device is ignored")
errConnLimitReached = errors.New("connection limit reached")
errDevicePaused = errors.New("device is paused")
)
const (
@@ -128,6 +137,14 @@ type ConnectionStatusEntry struct {
Error *string `json:"error"`
}
type connWithHello struct {
c internalConn
hello protocol.Hello
err error
remoteID protocol.DeviceID
remoteCert *x509.Certificate
}
type service struct {
*suture.Supervisor
connectionStatusHandler
@@ -138,11 +155,13 @@ type service struct {
tlsCfg *tls.Config
discoverer discover.Finder
conns chan internalConn
hellos chan *connWithHello
bepProtocolName string
tlsDefaultCommonName string
limiter *limiter
natService *nat.Service
evLogger events.Logger
registry *registry.Registry
dialNow chan struct{}
dialNowDevices map[protocol.DeviceID]struct{}
@@ -153,7 +172,7 @@ type service struct {
listenerTokens map[string]suture.ServiceToken
}
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger) Service {
func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger, registry *registry.Registry) Service {
spec := svcutil.SpecWithInfoLogger(l)
service := &service{
Supervisor: suture.New("connections.Service", spec),
@@ -165,11 +184,13 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
tlsCfg: tlsCfg,
discoverer: discoverer,
conns: make(chan internalConn),
hellos: make(chan *connWithHello),
bepProtocolName: bepProtocolName,
tlsDefaultCommonName: tlsDefaultCommonName,
limiter: newLimiter(myID, cfg),
natService: nat.NewService(myID, cfg),
evLogger: evLogger,
registry: registry,
dialNowDevicesMut: sync.NewMutex(),
dialNow: make(chan struct{}, 1),
@@ -194,7 +215,8 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
// incoming or outgoing.
service.Add(svcutil.AsService(service.connect, fmt.Sprintf("%s/connect", service)))
service.Add(svcutil.AsService(service.handle, fmt.Sprintf("%s/handle", service)))
service.Add(svcutil.AsService(service.handleConns, fmt.Sprintf("%s/handleConns", service)))
service.Add(svcutil.AsService(service.handleHellos, fmt.Sprintf("%s/handleHellos", service)))
service.Add(service.natService)
svcutil.OnSupervisorDone(service.Supervisor, func() {
@@ -205,9 +227,9 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
return service
}
func (s *service) handle(ctx context.Context) error {
var c internalConn
func (s *service) handleConns(ctx context.Context) error {
for {
var c internalConn
select {
case <-ctx.Done():
return ctx.Err()
@@ -245,8 +267,84 @@ func (s *service) handle(ctx context.Context) error {
continue
}
if err := s.connectionCheckEarly(remoteID, c); err != nil {
l.Infof("Connection from %s at %s (%s) rejected: %v", remoteID, c.RemoteAddr(), c.Type(), err)
c.Close()
continue
}
_ = c.SetDeadline(time.Now().Add(20 * time.Second))
hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID))
go func() {
hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID))
select {
case s.hellos <- &connWithHello{c, hello, err, remoteID, remoteCert}:
case <-ctx.Done():
}
}()
}
}
func (s *service) connectionCheckEarly(remoteID protocol.DeviceID, c internalConn) error {
if s.cfg.IgnoredDevice(remoteID) {
return errDeviceIgnored
}
if max := s.cfg.Options().ConnectionLimitMax; max > 0 && s.model.NumConnections() >= max {
// We're not allowed to accept any more connections.
return errConnLimitReached
}
cfg, ok := s.cfg.Device(remoteID)
if !ok {
// We do go ahead exchanging hello messages to get information about the device.
return nil
}
if cfg.Paused {
return errDevicePaused
}
if len(cfg.AllowedNetworks) > 0 && !IsAllowedNetwork(c.RemoteAddr().String(), cfg.AllowedNetworks) {
// The connection is not from an allowed network.
return errNetworkNotAllowed
}
// Lower priority is better, just like nice etc.
if ct, ok := s.model.Connection(remoteID); ok {
if ct.Priority() > c.priority || time.Since(ct.Statistics().StartedAt) > minConnectionReplaceAge {
l.Debugf("Switching connections %s (existing: %s new: %s)", remoteID, ct, c)
} else {
// We should not already be connected to the other party. TODO: This
// could use some better handling. If the old connection is dead but
// hasn't timed out yet we may want to drop *that* connection and keep
// this one. But in case we are two devices connecting to each other
// in parallel we don't want to do that or we end up with no
// connections still established...
return errDeviceAlreadyConnected
}
}
return nil
}
func (s *service) handleHellos(ctx context.Context) error {
var c internalConn
var hello protocol.Hello
var err error
var remoteID protocol.DeviceID
var remoteCert *x509.Certificate
for {
select {
case <-ctx.Done():
return ctx.Err()
case withHello := <-s.hellos:
c = withHello.c
hello = withHello.hello
err = withHello.err
remoteID = withHello.remoteID
remoteCert = withHello.remoteCert
}
if err != nil {
if protocol.IsVersionMismatch(err) {
// The error will be a relatively user friendly description
@@ -279,25 +377,6 @@ func (s *service) handle(ctx context.Context) error {
continue
}
// If we have a relay connection, and the new incoming connection is
// not a relay connection, we should drop that, and prefer this one.
ct, connected := s.model.Connection(remoteID)
// Lower priority is better, just like nice etc.
if connected && (ct.Priority() > c.priority || time.Since(ct.Statistics().StartedAt) > minConnectionReplaceAge) {
l.Debugf("Switching connections %s (existing: %s new: %s)", remoteID, ct, c)
} else if connected {
// We should not already be connected to the other party. TODO: This
// could use some better handling. If the old connection is dead but
// hasn't timed out yet we may want to drop *that* connection and keep
// this one. But in case we are two devices connecting to each other
// in parallel we don't want to do that or we end up with no
// connections still established...
l.Infof("Connected to already connected device %s (existing: %s new: %s)", remoteID, ct, c)
c.Close()
continue
}
deviceCfg, ok := s.cfg.Device(remoteID)
if !ok {
l.Infof("Device %s removed from config during connection attempt at %s", remoteID, c)
@@ -346,7 +425,6 @@ func (s *service) handle(ctx context.Context) error {
continue
}
}
func (s *service) connect(ctx context.Context) error {
// Map of when to earliest dial each given device + address again
nextDialAt := make(nextDialRegistry)
@@ -581,7 +659,7 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
continue
}
dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg)
dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg, s.registry)
nextDialAt.set(deviceID, addr, now.Add(dialer.RedialFrequency()))
// For LAN addresses, increase the priority so that we
@@ -681,7 +759,7 @@ func (s *service) createListener(factory listenerFactory, uri *url.URL) bool {
l.Debugln("Starting listener", uri)
listener := factory.New(uri, s.cfg, s.tlsCfg, s.conns, s.natService)
listener := factory.New(uri, s.cfg, s.tlsCfg, s.conns, s.natService, s.registry)
listener.OnAddressesChanged(s.logListenAddressesChangedEvent)
// Retrying a listener many times in rapid succession is unlikely to help,
@@ -882,7 +960,7 @@ func (s *connectionStatusHandler) ConnectionStatus() map[string]ConnectionStatus
}
func (s *connectionStatusHandler) setConnectionStatus(address string, err error) {
if errors.Cause(err) == context.Canceled {
if errors.Is(err, context.Canceled) {
return
}

View File

@@ -16,6 +16,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/nat"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/stats"
@@ -139,7 +140,7 @@ func (c internalConn) String() string {
}
type dialerFactory interface {
New(config.OptionsConfiguration, *tls.Config) genericDialer
New(config.OptionsConfiguration, *tls.Config, *registry.Registry) genericDialer
Priority() int
AlwaysWAN() bool
Valid(config.Configuration) error
@@ -162,7 +163,7 @@ type genericDialer interface {
}
type listenerFactory interface {
New(*url.URL, config.Wrapper, *tls.Config, chan internalConn, *nat.Service) genericListener
New(*url.URL, config.Wrapper, *tls.Config, chan internalConn, *nat.Service, *registry.Registry) genericListener
Valid(config.Configuration) error
}

View File

@@ -13,6 +13,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -28,6 +29,7 @@ func init() {
type tcpDialer struct {
commonDialer
registry *registry.Registry
}
func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL) (internalConn, error) {
@@ -35,7 +37,7 @@ func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL)
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
conn, err := dialer.DialContextReusePort(timeoutCtx, uri.Scheme, uri.Host)
conn, err := dialer.DialContextReusePortFunc(d.registry)(timeoutCtx, uri.Scheme, uri.Host)
if err != nil {
return internalConn{}, err
}
@@ -62,12 +64,15 @@ func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL)
type tcpDialerFactory struct{}
func (tcpDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config) genericDialer {
return &tcpDialer{commonDialer{
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
tlsCfg: tlsCfg,
}}
func (tcpDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, registry *registry.Registry) genericDialer {
return &tcpDialer{
commonDialer: commonDialer{
trafficClass: opts.TrafficClass,
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
tlsCfg: tlsCfg,
},
registry: registry,
}
}
func (tcpDialerFactory) Priority() int {

View File

@@ -32,11 +32,12 @@ type tcpListener struct {
svcutil.ServiceWithError
onAddressesChangedNotifier
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
uri *url.URL
cfg config.Wrapper
tlsCfg *tls.Config
conns chan internalConn
factory listenerFactory
registry *registry.Registry
natService *nat.Service
mapping *nat.Mapping
@@ -69,8 +70,8 @@ func (t *tcpListener) serve(ctx context.Context) error {
t.notifyAddressesChanged(t)
defer t.clearAddresses(t)
registry.Register(t.uri.Scheme, tcaddr)
defer registry.Unregister(t.uri.Scheme, tcaddr)
t.registry.Register(t.uri.Scheme, tcaddr)
defer t.registry.Unregister(t.uri.Scheme, tcaddr)
l.Infof("TCP listener (%v) starting", tcaddr)
defer l.Infof("TCP listener (%v) shutting down", tcaddr)
@@ -213,7 +214,7 @@ func (t *tcpListener) NATType() string {
type tcpListenerFactory struct{}
func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener {
func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service, registry *registry.Registry) genericListener {
l := &tcpListener{
uri: fixupPort(uri, config.DefaultTCPPort),
cfg: cfg,
@@ -221,6 +222,7 @@ func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.C
conns: conns,
natService: natService,
factory: f,
registry: registry,
}
l.ServiceWithError = svcutil.AsService(l.serve, l.String())
return l

View File

@@ -841,6 +841,7 @@ func rewriteGlobals(t readWriteTransaction) error {
return err
}
defer it.Release()
for it.Next() {
var vl VersionListDeprecated
if err := vl.Unmarshal(it.Value()); err != nil {
@@ -858,10 +859,7 @@ func rewriteGlobals(t readWriteTransaction) error {
}
}
newVl, err := convertVersionList(vl)
if err != nil {
return err
}
newVl := convertVersionList(vl)
if err := t.Put(it.Key(), mustMarshal(&newVl)); err != nil {
return err
}
@@ -869,11 +867,10 @@ func rewriteGlobals(t readWriteTransaction) error {
return err
}
}
it.Release()
return it.Error()
}
func convertVersionList(vl VersionListDeprecated) (VersionList, error) {
func convertVersionList(vl VersionListDeprecated) VersionList {
var newVl VersionList
var newPos, oldPos int
var lastVersion protocol.Vector
@@ -893,7 +890,7 @@ func convertVersionList(vl VersionListDeprecated) (VersionList, error) {
}
if oldPos == len(vl.Versions) {
return newVl, nil
return newVl
}
if len(newVl.RawVersions) == 0 {
@@ -923,7 +920,7 @@ outer:
newPos++
}
return newVl, nil
return newVl
}
func getGlobalVersionsByKeyBefore11(key []byte, t readOnlyTransaction) (VersionListDeprecated, error) {

View File

@@ -403,8 +403,8 @@ func (s *FileSet) SetIndexID(device protocol.DeviceID, id protocol.IndexID) {
}
}
func (s *FileSet) MtimeFS(filesystem fs.Filesystem) fs.Filesystem {
opStr := fmt.Sprintf("%s MtimeFS()", s.folder)
func (s *FileSet) MtimeOption() fs.Option {
opStr := fmt.Sprintf("%s MtimeOption()", s.folder)
l.Debugf(opStr)
prefix, err := s.db.keyer.GenerateMtimesKey(nil, []byte(s.folder))
if backend.IsClosed(err) {
@@ -413,7 +413,7 @@ func (s *FileSet) MtimeFS(filesystem fs.Filesystem) fs.Filesystem {
fatalError(err, opStr, s.db)
}
kv := NewNamespacedKV(s.db, string(prefix))
return fs.NewMtimeFS(filesystem, kv)
return fs.NewMtimeOption(kv)
}
func (s *FileSet) ListDevices() []protocol.DeviceID {

View File

@@ -262,11 +262,9 @@ func (vl *VersionList) update(folder, device []byte, file protocol.FileIntf, t r
oldFV = oldFV.copy()
// Remove ourselves first
removedFV, haveRemoved, _, err := vl.pop(device, []byte(file.FileName()))
if err == nil {
// Find position and insert the file
err = vl.insert(folder, device, file, t)
}
removedFV, haveRemoved, _ := vl.pop(device)
// Find position and insert the file
err := vl.insert(folder, device, file, t)
if err != nil {
return FileVersion{}, FileVersion{}, FileVersion{}, false, false, false, err
}
@@ -315,28 +313,28 @@ func (vl *VersionList) insertAt(i int, v FileVersion) {
// pop removes the given device from the VersionList and returns the FileVersion
// before removing the device, whether it was found/removed at all and whether
// the global changed in the process.
func (vl *VersionList) pop(device, name []byte) (FileVersion, bool, bool, error) {
func (vl *VersionList) pop(device []byte) (FileVersion, bool, bool) {
invDevice, i, j, ok := vl.findDevice(device)
if !ok {
return FileVersion{}, false, false, nil
return FileVersion{}, false, false
}
globalPos := vl.findGlobal()
if vl.RawVersions[i].deviceCount() == 1 {
fv := vl.RawVersions[i]
vl.popVersionAt(i)
return fv, true, globalPos == i, nil
return fv, true, globalPos == i
}
oldFV := vl.RawVersions[i].copy()
if invDevice {
vl.RawVersions[i].InvalidDevices = popDeviceAt(vl.RawVersions[i].InvalidDevices, j)
return oldFV, true, false, nil
return oldFV, true, false
}
vl.RawVersions[i].Devices = popDeviceAt(vl.RawVersions[i].Devices, j)
// If the last valid device of the previous global was removed above,
// the global changed.
return oldFV, true, len(vl.RawVersions[i].Devices) == 0 && globalPos == i, nil
return oldFV, true, len(vl.RawVersions[i].Devices) == 0 && globalPos == i
}
// Get returns a FileVersion that contains the given device and whether it has

View File

@@ -825,10 +825,7 @@ func (t readWriteTransaction) removeFromGlobal(gk, keyBuf, folder, device, file
return keyBuf, t.Delete(gk)
}
removedFV, haveRemoved, globalChanged, err := fl.pop(device, file)
if err != nil {
return nil, err
}
removedFV, haveRemoved, globalChanged := fl.pop(device)
if !haveRemoved {
// There is no version for the given device
return keyBuf, nil

View File

@@ -104,32 +104,34 @@ func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
// DialContextReusePort tries dialing via proxy if a proxy is configured, and falls back to
// a direct connection reusing the port from the connections registry, if no proxy is defined, or connecting via proxy
// fails. It also in parallel dials without reusing the port, just in case reusing the port affects routing decisions badly.
func DialContextReusePort(ctx context.Context, network, addr string) (net.Conn, error) {
// If proxy is configured, there is no point trying to reuse listen addresses.
if proxy.FromEnvironment() != proxy.Direct {
return DialContext(ctx, network, addr)
}
func DialContextReusePortFunc(registry *registry.Registry) func(ctx context.Context, network, addr string) (net.Conn, error) {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
// If proxy is configured, there is no point trying to reuse listen addresses.
if proxy.FromEnvironment() != proxy.Direct {
return DialContext(ctx, network, addr)
}
localAddrInterface := registry.Get(network, func(addr interface{}) bool {
return addr.(*net.TCPAddr).IP.IsUnspecified()
})
if localAddrInterface == nil {
// Nothing listening, nothing to reuse.
return DialContext(ctx, network, addr)
}
localAddrInterface := registry.Get(network, func(addr interface{}) bool {
return addr.(*net.TCPAddr).IP.IsUnspecified()
})
if localAddrInterface == nil {
// Nothing listening, nothing to reuse.
return DialContext(ctx, network, addr)
}
laddr, ok := localAddrInterface.(*net.TCPAddr)
if !ok {
return nil, errUnexpectedInterfaceType
}
laddr, ok := localAddrInterface.(*net.TCPAddr)
if !ok {
return nil, errUnexpectedInterfaceType
}
// Dial twice, once reusing the listen address, another time not reusing it, just in case reusing the address
// influences routing and we fail to reach our destination.
dialer := net.Dialer{
Control: ReusePortControl,
LocalAddr: laddr,
// Dial twice, once reusing the listen address, another time not reusing it, just in case reusing the address
// influences routing and we fail to reach our destination.
dialer := net.Dialer{
Control: ReusePortControl,
LocalAddr: laddr,
}
return dialTwicePreferFirst(ctx, dialer.DialContext, (&net.Dialer{}).DialContext, "reuse", "non-reuse", network, addr)
}
return dialTwicePreferFirst(ctx, dialer.DialContext, (&net.Dialer{}).DialContext, "reuse", "non-reuse", network, addr)
}
type dialFunc func(ctx context.Context, network, address string) (net.Conn, error)

View File

@@ -14,6 +14,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -23,7 +24,7 @@ func setupCache() *manager {
cfg.Options.LocalAnnEnabled = false
cfg.Options.GlobalAnnEnabled = false
return NewManager(protocol.LocalDeviceID, config.Wrap("", cfg, protocol.LocalDeviceID, events.NoopLogger), tls.Certificate{}, events.NoopLogger, nil).(*manager)
return NewManager(protocol.LocalDeviceID, config.Wrap("", cfg, protocol.LocalDeviceID, events.NoopLogger), tls.Certificate{}, events.NoopLogger, nil, registry.New()).(*manager)
}
func TestCacheUnique(t *testing.T) {

View File

@@ -14,12 +14,14 @@ import (
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strconv"
stdsync "sync"
"time"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
@@ -44,7 +46,7 @@ type httpClient interface {
const (
defaultReannounceInterval = 30 * time.Minute
announceErrorRetryInterval = 5 * time.Minute
requestTimeout = 5 * time.Second
requestTimeout = 30 * time.Second
maxAddressChangesBetweenAnnouncements = 10
)
@@ -71,7 +73,7 @@ func (e *lookupError) CacheFor() time.Duration {
return e.cacheFor
}
func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, evLogger events.Logger) (FinderService, error) {
func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, evLogger events.Logger, registry *registry.Registry) (FinderService, error) {
server, opts, err := parseOptions(server)
if err != nil {
return nil, err
@@ -88,10 +90,16 @@ func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, evLo
// The http.Client used for announcements. It needs to have our
// certificate to prove our identity, and may or may not verify the server
// certificate depending on the insecure setting.
var dialContext func(ctx context.Context, network, addr string) (net.Conn, error)
if registry != nil {
dialContext = dialer.DialContextReusePortFunc(registry)
} else {
dialContext = dialer.DialContext
}
var announceClient httpClient = &contextClient{&http.Client{
Timeout: requestTimeout,
Transport: &http.Transport{
DialContext: dialer.DialContextReusePort,
DialContext: dialContext,
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: opts.insecure,

View File

@@ -16,6 +16,7 @@ import (
"testing"
"time"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/tlsutil"
@@ -56,15 +57,17 @@ func TestGlobalOverHTTP(t *testing.T) {
// is only allowed in combination with the "insecure" and "noannounce"
// parameters.
if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil, events.NoopLogger); err == nil {
registry := registry.New()
if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil, events.NoopLogger, registry); err == nil {
t.Fatal("http is not allowed without insecure and noannounce")
}
if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil, events.NoopLogger); err == nil {
if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil, events.NoopLogger, registry); err == nil {
t.Fatal("http is not allowed without noannounce")
}
if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil, events.NoopLogger); err == nil {
if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil, events.NoopLogger, registry); err == nil {
t.Fatal("http is not allowed without insecure")
}
@@ -185,7 +188,7 @@ func TestGlobalAnnounce(t *testing.T) {
go func() { _ = http.Serve(list, mux) }()
url := "https://" + list.Addr().String() + "?insecure"
disco, err := NewGlobal(url, cert, new(fakeAddressLister), events.NoopLogger)
disco, err := NewGlobal(url, cert, new(fakeAddressLister), events.NoopLogger, registry.New())
if err != nil {
t.Fatal(err)
}
@@ -210,7 +213,7 @@ func TestGlobalAnnounce(t *testing.T) {
}
func testLookup(url string) ([]string, error) {
disco, err := NewGlobal(url, tls.Certificate{}, nil, events.NoopLogger)
disco, err := NewGlobal(url, tls.Certificate{}, nil, events.NoopLogger, registry.New())
if err != nil {
return nil, err
}

View File

@@ -109,6 +109,13 @@ func (c *localClient) Error() error {
// send.
func (c *localClient) announcementPkt(instanceID int64, msg []byte) ([]byte, bool) {
addrs := c.addrList.AllAddresses()
// The list of all addresses can include unspecified addresses intended
// for a discovery server to complete, based on the packet source. We
// don't do that for local discovery, so filter out addresses that are
// usable as-is.
addrs = filterUnspecifiedLocal(addrs)
if len(addrs) == 0 {
// Nothing to announce
return msg, false
@@ -281,3 +288,30 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
return isNewDevice
}
// filterUnspecifiedLocal returns the list of addresses after removing any
// unspecified, localhost, multicast, broadcast or port-zero addresses.
func filterUnspecifiedLocal(addrs []string) []string {
filtered := addrs[:0]
for _, addr := range addrs {
u, err := url.Parse(addr)
if err != nil {
continue
}
tcpAddr, err := net.ResolveTCPAddr("tcp", u.Host)
if err != nil {
continue
}
switch {
case len(tcpAddr.IP) == 0:
case tcpAddr.Port == 0:
case tcpAddr.IP.IsUnspecified():
case !tcpAddr.IP.IsGlobalUnicast() && !tcpAddr.IP.IsLinkLocalUnicast():
default:
filtered = append(filtered, addr)
}
}
return filtered
}

View File

@@ -9,6 +9,7 @@ package discover
import (
"bytes"
"context"
"fmt"
"net"
"testing"
@@ -89,3 +90,33 @@ func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
t.Fatal("new instance ID should be new")
}
}
func TestFilterUnspecified(t *testing.T) {
addrs := []string{
"quic://[2001:db8::1]:22000", // OK
"tcp://192.0.2.42:22000", // OK
"quic://[2001:db8::1]:0", // remove, port zero
"tcp://192.0.2.42:0", // remove, port zero
"quic://[::]:22000", // remove, unspecified
"tcp://0.0.0.0:22000", // remove, unspecified
"tcp://[2001:db8::1]", // remove, no port
"tcp://192.0.2.42", // remove, no port
"tcp://foo:bar", // remove, host/port does not resolve
"tcp://127.0.0.1:22000", // remove, not usable from outside
"tcp://[::1]:22000", // remove, not usable from outside
"tcp://224.1.2.3:22000", // remove, not usable from outside (multicast)
"tcp://[fe80::9ef:dff1:b332:5e56]:55681", // OK
"pure garbage", // remove, garbage
"", // remove, garbage
}
exp := []string{
"quic://[2001:db8::1]:22000",
"tcp://192.0.2.42:22000",
"tcp://[fe80::9ef:dff1:b332:5e56]:55681",
}
res := filterUnspecifiedLocal(addrs)
if fmt.Sprint(res) != fmt.Sprint(exp) {
t.Log(res)
t.Error("filterUnspecified returned invalid addresses")
}
}

View File

@@ -19,6 +19,7 @@ import (
"github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/connections/registry"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/svcutil"
@@ -44,12 +45,13 @@ type manager struct {
cert tls.Certificate
evLogger events.Logger
addressLister AddressLister
registry *registry.Registry
finders map[string]cachedFinder
mut sync.RWMutex
}
func NewManager(myID protocol.DeviceID, cfg config.Wrapper, cert tls.Certificate, evLogger events.Logger, lister AddressLister) Manager {
func NewManager(myID protocol.DeviceID, cfg config.Wrapper, cert tls.Certificate, evLogger events.Logger, lister AddressLister, registry *registry.Registry) Manager {
m := &manager{
Supervisor: suture.New("discover.Manager", svcutil.SpecWithDebugLogger(l)),
myID: myID,
@@ -57,6 +59,7 @@ func NewManager(myID protocol.DeviceID, cfg config.Wrapper, cert tls.Certificate
cert: cert,
evLogger: evLogger,
addressLister: lister,
registry: registry,
finders: make(map[string]cachedFinder),
mut: sync.NewRWMutex(),
@@ -257,7 +260,7 @@ func (m *manager) CommitConfiguration(_, to config.Configuration) (handled bool)
if _, ok := m.finders[identity]; ok {
continue
}
gd, err := NewGlobal(srv, m.cert, m.addressLister, m.evLogger)
gd, err := NewGlobal(srv, m.cert, m.addressLister, m.evLogger, m.registry)
if err != nil {
l.Warnln("Global discovery:", err)
continue

View File

@@ -35,6 +35,7 @@ const (
PendingDevicesChanged
DevicePaused
DeviceResumed
ClusterConfigReceived
LocalChangeDetected
RemoteChangeDetected
LocalIndexUpdated
@@ -118,6 +119,8 @@ func (t EventType) String() string {
return "DevicePaused"
case DeviceResumed:
return "DeviceResumed"
case ClusterConfigReceived:
return "ClusterConfigReceived"
case FolderScanProgress:
return "FolderScanProgress"
case FolderPaused:
@@ -203,6 +206,8 @@ func UnmarshalEventType(s string) EventType {
return DevicePaused
case "DeviceResumed":
return DeviceResumed
case "ClusterConfigReceived":
return ClusterConfigReceived
case "FolderScanProgress":
return FolderScanProgress
case "FolderPaused":
@@ -548,8 +553,6 @@ var NoopLogger Logger = &noopLogger{}
func (*noopLogger) Serve(ctx context.Context) error { return nil }
func (*noopLogger) Stop() {}
func (*noopLogger) Log(t EventType, data interface{}) {}
func (*noopLogger) Subscribe(mask EventType) Subscription {

View File

@@ -27,12 +27,13 @@ var (
type OptionJunctionsAsDirs struct{}
func (o *OptionJunctionsAsDirs) apply(fs Filesystem) {
func (o *OptionJunctionsAsDirs) apply(fs Filesystem) Filesystem {
if basic, ok := fs.(*BasicFilesystem); !ok {
l.Warnln("WithJunctionsAsDirs must only be used with FilesystemTypeBasic")
} else {
basic.junctionsAsDirs = true
}
return fs
}
func (o *OptionJunctionsAsDirs) String() string {

View File

@@ -20,17 +20,13 @@ import (
func setup(t *testing.T) (*BasicFilesystem, string) {
t.Helper()
dir, err := os.MkdirTemp("", "")
if err != nil {
t.Fatal(err)
}
dir := t.TempDir()
return newBasicFilesystem(dir), dir
}
func TestChmodFile(t *testing.T) {
fs, dir := setup(t)
path := filepath.Join(dir, "file")
defer os.RemoveAll(dir)
defer os.Chmod(path, 0666)
@@ -71,7 +67,6 @@ func TestChownFile(t *testing.T) {
fs, dir := setup(t)
path := filepath.Join(dir, "file")
defer os.RemoveAll(dir)
defer os.Chmod(path, 0666)
@@ -108,7 +103,6 @@ func TestChownFile(t *testing.T) {
func TestChmodDir(t *testing.T) {
fs, dir := setup(t)
path := filepath.Join(dir, "dir")
defer os.RemoveAll(dir)
mode := os.FileMode(0755)
if runtime.GOOS == "windows" {
@@ -141,7 +135,6 @@ func TestChmodDir(t *testing.T) {
func TestChtimes(t *testing.T) {
fs, dir := setup(t)
path := filepath.Join(dir, "file")
defer os.RemoveAll(dir)
fd, err := os.Create(path)
if err != nil {
t.Error(err)
@@ -166,7 +159,6 @@ func TestChtimes(t *testing.T) {
func TestCreate(t *testing.T) {
fs, dir := setup(t)
path := filepath.Join(dir, "file")
defer os.RemoveAll(dir)
if _, err := os.Stat(path); err == nil {
t.Errorf("exists?")
@@ -190,7 +182,6 @@ func TestCreateSymlink(t *testing.T) {
fs, dir := setup(t)
path := filepath.Join(dir, "file")
defer os.RemoveAll(dir)
if err := fs.CreateSymlink("blah", "file"); err != nil {
t.Error(err)
@@ -215,7 +206,6 @@ func TestCreateSymlink(t *testing.T) {
func TestDirNames(t *testing.T) {
fs, dir := setup(t)
defer os.RemoveAll(dir)
// Case differences
testCases := []string{
@@ -244,8 +234,7 @@ func TestDirNames(t *testing.T) {
func TestNames(t *testing.T) {
// Tests that all names are without the root directory.
fs, dir := setup(t)
defer os.RemoveAll(dir)
fs, _ := setup(t)
expected := "file"
fd, err := fs.Create(expected)
@@ -284,8 +273,7 @@ func TestNames(t *testing.T) {
func TestGlob(t *testing.T) {
// Tests that all names are without the root directory.
fs, dir := setup(t)
defer os.RemoveAll(dir)
fs, _ := setup(t)
for _, dirToCreate := range []string{
filepath.Join("a", "test", "b"),
@@ -344,8 +332,7 @@ func TestGlob(t *testing.T) {
}
func TestUsage(t *testing.T) {
fs, dir := setup(t)
defer os.RemoveAll(dir)
fs, _ := setup(t)
usage, err := fs.Usage(".")
if err != nil {
if runtime.GOOS == "netbsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "solaris" {
@@ -577,18 +564,15 @@ func TestRel(t *testing.T) {
func TestBasicWalkSkipSymlink(t *testing.T) {
_, dir := setup(t)
defer os.RemoveAll(dir)
testWalkSkipSymlink(t, FilesystemTypeBasic, dir)
}
func TestWalkTraverseDirJunct(t *testing.T) {
_, dir := setup(t)
defer os.RemoveAll(dir)
testWalkTraverseDirJunct(t, FilesystemTypeBasic, dir)
}
func TestWalkInfiniteRecursion(t *testing.T) {
_, dir := setup(t)
defer os.RemoveAll(dir)
testWalkInfiniteRecursion(t, FilesystemTypeBasic, dir)
}

View File

@@ -12,7 +12,7 @@ package fs
import "github.com/syncthing/notify"
const (
subEventMask = notify.Create | notify.FileModified | notify.FileRenameFrom | notify.FileDelete | notify.FileRenameTo
subEventMask = notify.Create | notify.FileModified | notify.FileRenameFrom | notify.FileDelete | notify.FileRenameTo | notify.FileNoFollow
permEventMask = notify.FileAttrib
rmEventMask = notify.FileDelete | notify.FileRenameFrom
)

View File

@@ -18,4 +18,7 @@ const (
subEventMask = notify.NoteDelete | notify.NoteWrite | notify.NoteRename | notify.Create | notify.NoteAttrib | notify.NoteExtend
permEventMask = 0
rmEventMask = notify.NoteDelete | notify.NoteRename
// WatchKqueue indicates if kqueue is used for filesystem watching
WatchKqueue = true
)

View File

@@ -0,0 +1,13 @@
// Copyright (C) 2022 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build !dragonfly && !freebsd && !netbsd && !openbsd
// +build !dragonfly,!freebsd,!netbsd,!openbsd
package fs
// WatchKqueue indicates if kqueue is used for filesystem watching
const WatchKqueue = false

View File

@@ -49,7 +49,6 @@ func TestResolveWindows83(t *testing.T) {
dir = fs.resolveWin83(dir)
fs = newBasicFilesystem(dir)
}
defer os.RemoveAll(dir)
shortAbs, _ := fs.rooted("LFDATA~1")
long := "LFDataTool"
@@ -80,7 +79,6 @@ func TestIsWindows83(t *testing.T) {
dir = fs.resolveWin83(dir)
fs = newBasicFilesystem(dir)
}
defer os.RemoveAll(dir)
tempTop, _ := fs.rooted(TempName("baz"))
tempBelow, _ := fs.rooted(filepath.Join("foo", "bar", TempName("baz")))

View File

@@ -123,21 +123,27 @@ func (r *caseFilesystemRegistry) cleaner() {
var globalCaseFilesystemRegistry = caseFilesystemRegistry{fss: make(map[fskey]*caseFilesystem)}
// caseFilesystem is a BasicFilesystem with additional checks to make a
// potentially case insensitive underlying FS behave like it's case-sensitive.
type caseFilesystem struct {
Filesystem
realCaser
}
// NewCaseFilesystem ensures that the given, potentially case-insensitive filesystem
// OptionDetectCaseConflicts ensures that the potentially case-insensitive filesystem
// behaves like a case-sensitive filesystem. Meaning that it takes into account
// the real casing of a path and returns ErrCaseConflict if the given path differs
// from the real path. It is safe to use with any filesystem, i.e. also a
// case-sensitive one. However it will add some overhead and thus shouldn't be
// used if the filesystem is known to already behave case-sensitively.
func NewCaseFilesystem(fs Filesystem) Filesystem {
return wrapFilesystem(fs, globalCaseFilesystemRegistry.get)
type OptionDetectCaseConflicts struct{}
func (o *OptionDetectCaseConflicts) apply(fs Filesystem) Filesystem {
return globalCaseFilesystemRegistry.get(fs)
}
func (o *OptionDetectCaseConflicts) String() string {
return "detectCaseConflicts"
}
// caseFilesystem is a BasicFilesystem with additional checks to make a
// potentially case insensitive underlying FS behave like it's case-sensitive.
type caseFilesystem struct {
Filesystem
realCaser
}
func (f *caseFilesystem) Chmod(name string, mode FileMode) error {

Some files were not shown because too many files have changed in this diff Show More