Compare commits

...

64 Commits

Author SHA1 Message Date
Jonathan
490ec4350c lib/model: Fix config deadlock when deleting a paused folder (#7854) 2021-07-30 14:41:57 +02:00
Simon Frei
dc0dd09e93 lib/model: Don't try to delete deleted item on revert (#7843) 2021-07-23 14:26:20 +02:00
Simon Frei
7ec76095e6 gui, script: Parse JS files for translation values (fixes #7845) (#7846) 2021-07-23 14:24:08 +02:00
Simon Frei
cb26552440 gui, script: Fix various gui string/translation issues (fixes #7839) (#7842) 2021-07-22 11:47:03 +02:00
Simon Frei
1ae5ac7d0b cmd/stcrashreceiver: Sanitize failure report fingerprints (#7840) 2021-07-22 11:16:24 +02:00
Simon Frei
eeb7091180 lib/model: Missing fmut-lock on encryption failures (#7841) 2021-07-22 11:15:25 +02:00
greatroar
dc38e6ae88 lib/relay/client: Stricter typing and remove unused code (#7819) 2021-07-21 09:49:09 +02:00
Jakob Borg
eb6cad7f93 gui, man, authors: Update docs, translations, and contributors 2021-07-21 07:45:25 +02:00
jtagcat
ae30b46bfe gui: advanced: On open collapse all, inc. GUI section. (#7820) 2021-07-16 06:40:30 +02:00
Jakob Borg
eab268f5f8 gui, man, authors: Update docs, translations, and contributors 2021-07-14 07:45:27 +02:00
greatroar
1e21042138 lib/connections: switch statement to get the QUIC network (#7816) 2021-07-10 13:53:51 +02:00
Jakob Borg
a56f70ab94 gui, man, authors: Update docs, translations, and contributors 2021-07-07 07:45:25 +02:00
Chih-Hsuan Yen
11c57b9097 lib/connections: Resolve IPv6 for quic6:// peers (fixes #7809) (#7810)
Before this patch, IPv4-compatible addresses (::ffff:aaa.bbb.ccc.ddd)
may be used if a quic6://some.domain:port is specified and both IPv4 and
IPv6 addresses exist for that domain name.
2021-07-05 13:19:56 +02:00
Simon Frei
1921533c4c lib/connections: Fully dial resumed devices (#7798) 2021-07-03 18:26:55 +02:00
Simon Frei
89e762fd6e lib/model: Prevent folder-type change from/to encrypted (fixes #7704) (#7796) 2021-07-03 13:47:04 +02:00
Simon Frei
a63d3ee625 lib/model: Scan removed dirs when reverting recv-enc (fixes #7706) (#7797) 2021-07-03 13:46:24 +02:00
Jakob Borg
82741ad207 gui, man, authors: Update docs, translations, and contributors 2021-06-30 07:45:30 +02:00
greatroar
bd363fe0b7 lib/protocol: Write uncompressible messages uncompressed (#7790) 2021-06-27 17:59:30 +02:00
Vladimir Rusinov
7a4c6d262f build: Remove no longer used temporaryBuildDir (#7795) 2021-06-27 17:59:02 +02:00
Simon Frei
445a82f120 lib/model: Compare all items with global on scan (fixes #7740) (#7791) 2021-06-27 08:48:54 +02:00
Simon Frei
69ce121267 lib/db: Missing event-logger in write-transaction (#7793) 2021-06-27 08:43:49 +02:00
Simon Frei
08e3cd1cce lib/fs: Set expiry after DirNames in case-fs (#7794) 2021-06-27 08:30:02 +02:00
André Colomb
da0e5edbec gui: Fix typo "recurr", again and for real (#7788) 2021-06-25 12:08:18 +02:00
Simon Frei
c78fa42f31 lib/connections: Dial devices in parallel (#7783) 2021-06-25 11:38:04 +02:00
Jakob Borg
993a3ebe73 lib/api: Always include usage reporting data in support bundle (#7786) 2021-06-24 22:00:14 +04:00
Jakob Borg
8040502599 gui, man, authors: Update docs, translations, and contributors 2021-06-23 07:45:27 +02:00
Simon Frei
c84e8d1e09 gui: Consider size 0 items in remote completion (fixes #7741) (#7781) 2021-06-21 22:53:50 +02:00
Simon Frei
5fb72eed85 gui: Make listener/discovery modal more discoverable (#7780) 2021-06-21 21:44:28 +02:00
Simon Frei
400d62c1e6 lib/connections: Missed map init in nextDialAt (ref #7753) (#7778) 2021-06-17 21:13:57 +04:00
Simon Frei
857caf3637 lib/connections: Trigger dialer when connection gets closed (#7753) 2021-06-17 13:57:44 +02:00
Simon Frei
aeca1fb575 lib/db: Check if sequences change when repairing metadata (#7770) 2021-06-17 13:53:39 +02:00
Simon Frei
ac2988a485 gui, lib: Handle pw adding remote encrypted folder (fixes #7705) (#7772) 2021-06-17 13:53:02 +02:00
Simon Frei
cb5ef250f4 build: Add codecov upload script to repo (#7776) 2021-06-17 13:52:10 +02:00
Simon Frei
23a0e18292 lib/db: Fix accounting bug when dropping indexes (#7774) 2021-06-17 10:15:11 +02:00
Simon Frei
aa6c55dec1 lib/model: Remove bogus failureevent when restarting folder (#7773) 2021-06-17 08:57:24 +02:00
André Colomb
7e0c24ec89 gui: Move the QR code button next to device ID in editDevice modal (#7653) 2021-06-16 21:10:16 +02:00
Simon Frei
2c7d9b59c6 gui: Fix race in online event callback (fixes #7733) (#7771) 2021-06-16 19:32:30 +02:00
Jakob Borg
adb7763f87 gui, man, authors: Update docs, translations, and contributors 2021-06-16 07:45:27 +02:00
André Colomb
89740490ac gui: Fix typo in new tooltip. (#7769) 2021-06-14 23:06:24 +02:00
greatroar
0b290f7206 all: go mod tidy (#7758) 2021-06-10 13:16:44 +02:00
Simon Frei
1e7a3997e3 lib/db, lib/model: Improve error handling on pending items (#7754) 2021-06-09 13:35:17 +02:00
Anur
e7f8538e4d lib/fs: Add bitmasks for Darwin to handle change to empty files (fixes #7731) (#7756) 2021-06-09 12:57:06 +02:00
Jakob Borg
18a608a6ff gui, man, authors: Update docs, translations, and contributors 2021-06-09 07:45:34 +02:00
Simon Frei
1a22689328 lib/db: Add failure reports to failures iterating over hashes (#7755) 2021-06-07 23:10:35 +02:00
Jakob Borg
ce65aea0ab lib/db: Use a more concurrent GC (fixes #7722) (#7750)
This changes the GC mechanism so that the first pass (which reads all
FileInfos to populate bloom filters with block & version hashes) can
happen concurrently with normal database operations.

The big gcMut still exists, and we grab it temporarily to block all
other modifications while we set up the bloom filters. We then release
the lock and let other things happen, with those other things also
updating the bloom filters as required. Once the first phase is done we
again grab the gcMut, knowing that we are the sole modifier of the
database, and do the cleanup.

I also removed the final compaction step.
2021-06-07 10:52:06 +02:00
André Colomb
45edad867c all: Allow dismissing pending devices / folders without ignoring (fixes #7700) (#7712) 2021-06-07 10:29:24 +02:00
André Colomb
ea0a408849 gui: Modal dialog for listeners and discovery status (#7539) 2021-06-07 09:08:44 +02:00
Simon Frei
18592af993 lib/model: Fix wrongly hardcoded arguments in test helper (#7749) 2021-06-05 17:01:23 +02:00
Simon Frei
b1e0e7b923 lib/model: Fix indexhandling for new folders paused on remote (#7747) 2021-06-05 16:27:15 +02:00
Jakob Borg
1e212a8dc6 cmd/syncthing: Wording on error return in "cli debug file" 2021-06-05 11:45:29 +02:00
Jakob Borg
0e9d2a13af cmd/syncthing: Improve "cli debug file" handling
Proper URL encoding, and return a sensible error when it's not found.
2021-06-05 11:19:02 +02:00
Simon Frei
6494a9332d lib/model: Fix test introduced in #7714 failing due to #7689 (#7745) 2021-06-04 15:32:47 +02:00
Simon Frei
41baccb85d lib/model: Fix passwords on receive-enc needing token (ref #7518) (#7739) 2021-06-03 15:39:49 +02:00
Simon Frei
52eb7392c4 lib/api, lib/config: Apply defaults before deserializing json (#7690) 2021-06-03 15:09:35 +02:00
Simon Frei
855c53ad02 lib/model: Fix reverting when version has only our own ID (fixes #7708) (#7714) 2021-06-03 15:08:56 +02:00
Simon Frei
004eded398 lib/model: Don't share with introduced device if encrypted (fixes #7724) (#7734) 2021-06-03 15:02:57 +02:00
Simon Frei
df48276300 lib/model: Ensure indexes are only received after checking IDs (ref #7649) (#7689) 2021-06-03 14:58:50 +02:00
Jakob Borg
accaf23aa3 gui, man, authors: Update docs, translations, and contributors 2021-06-02 07:45:38 +02:00
André Colomb
de6fe4dc6f gui: Show ID under each device section (local / remote), clickable for QR code (#7728) 2021-06-01 18:04:11 +02:00
Ikko Ashimine
08f6a91441 contributing: Fix GitHub format (#7713) 2021-05-31 10:27:35 +02:00
greatroar
95c9561e97 lib/db: Clean up Timer and wait for logging before return in GC (#7720) 2021-05-31 09:50:21 +02:00
Simon Frei
fcb19518c7 build, lib/model: Add flag to run tests without -short and fix failure (#7716) 2021-05-28 22:20:18 +02:00
Jakob Borg
fbaf696821 readme: Remove drama (#7711) 2021-05-26 14:59:49 +02:00
Jakob Borg
abe3483a8f gui, man, authors: Update docs, translations, and contributors 2021-05-26 07:45:29 +02:00
121 changed files with 6057 additions and 1980 deletions

View File

@@ -34,6 +34,7 @@ andyleap <andyleap@gmail.com>
Anjan Momi <anjan@momi.ca>
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
Antony Male (canton7) <antony.male@gmail.com>
Anur <anurnomeru@163.com>
Aranjedeath <Aranjedeath@users.noreply.github.com>
Arkadiusz Tymiński <gevleeog@gmail.com>
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
@@ -62,6 +63,7 @@ Carsten Hagemann (carstenhag) <moter8@gmail.com> <carsten@chagemann.de>
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
Cedric Staniewski (xduugu) <cedric@gmx.ca>
chenrui <rui@meetup.com>
Chih-Hsuan Yen <yan12125@gmail.com>
Choongkyu <choongkyu.kim+gh@gmail.com> <vapidlyrapid+gh@gmail.com>
Chris Howie (cdhowie) <me@chrishowie.com>
Chris Joel (cdata) <chris@scriptolo.gy>
@@ -114,6 +116,7 @@ Heiko Zuerker (Smiley73) <heiko@zuerker.org>
Hugo Locurcio <hugo.locurcio@hugo.pro>
Iain Barnett <iainspeed@gmail.com>
Ian Johnson (anonymouse64) <ian.johnson@canonical.com> <person.uwsome@gmail.com>
Ikko Ashimine <eltociear@gmail.com>
Ilya Brin <464157+ilyabrin@users.noreply.github.com>
Iskander Sharipov (Alex) <quasilyte@gmail.com>
Jaakko Hannikainen (jgke) <jgke@jgke.fi>
@@ -141,6 +144,7 @@ Jonathan <artback@protonmail.com>
Jonathan Cross <jcross@gmail.com>
Jonta <359397+Jonta@users.noreply.github.com>
Jose Manuel Delicado (jmdaweb) <jmdaweb@hotmail.com> <jmdaweb@users.noreply.github.com>
jtagcat <git-514635f7@jtag.cat>
Jörg Thalheim <Mic92@users.noreply.github.com>
Jędrzej Kula <kula.jedrek@gmail.com>
Kalle Laine <pahakalle@protonmail.com>
@@ -262,7 +266,7 @@ Unrud (Unrud) <unrud@openaliasbox.org> <Unrud@users.noreply.github.com>
Veeti Paananen (veeti) <veeti.paananen@rojekti.fi>
Victor Buinsky (buinsky) <vix_booja@tut.by>
Vil Brekin (Vilbrekin) <vilbrekin@gmail.com>
Vladimir Rusinov <vrusinov@google.com>
Vladimir Rusinov <vrusinov@google.com> <vladimir.rusinov@gmail.com>
wangguoliang <liangcszzu@163.com>
William A. Kennington III (wkennington) <william@wkennington.com>
wouter bolsterlee <wouter@bolsterl.ee>

View File

@@ -1,6 +1,6 @@
## Reporting Bugs
Please file bugs in the [Github Issue
Please file bugs in the [GitHub Issue
Tracker](https://github.com/syncthing/syncthing/issues). Include at
least the following:

View File

@@ -73,9 +73,8 @@ This helps the team understand what are the biggest pain points for our users, a
## Getting in Touch
The first and best point of contact is the [Forum][8]. There is also an IRC
channel, `#syncthing` on [freenode][4] (with a [web client][9]), for talking
directly to developers and users. If you've found something that is clearly a
The first and best point of contact is the [Forum][8].
If you've found something that is clearly a
bug, feel free to report it in the [GitHub issue tracker][10].
## Building
@@ -102,12 +101,10 @@ All code is licensed under the [MPLv2 License][7].
[1]: https://docs.syncthing.net/specs/bep-v1.html
[2]: https://docs.syncthing.net/intro/getting-started.html
[3]: https://github.com/syncthing/syncthing/blob/main/etc
[4]: https://www.freenode.net/
[5]: https://docs.syncthing.net/dev/building.html
[6]: https://docs.syncthing.net/
[7]: https://github.com/syncthing/syncthing/blob/main/LICENSE
[8]: https://forum.syncthing.net/
[9]: https://kiwiirc.com/client/irc.freenode.net/#syncthing
[10]: https://github.com/syncthing/syncthing/issues
[11]: https://docs.syncthing.net/users/contrib.html#gui-wrappers
[12]: https://www.bountysource.com/teams/syncthing/issues

View File

@@ -14,7 +14,6 @@ import (
"bytes"
"compress/flate"
"compress/gzip"
"crypto/sha256"
"encoding/json"
"errors"
"flag"
@@ -50,7 +49,9 @@ var (
benchRun string
debugBinary bool
coverage bool
long bool
timeout = "120s"
longTimeout = "600s"
numVersions = 5
withNextGenGUI = os.Getenv("BUILD_NEXT_GEN_GUI") != ""
)
@@ -363,6 +364,7 @@ func parseFlags() {
flag.StringVar(&cc, "cc", os.Getenv("CC"), "Set CC environment variable for `go build`")
flag.BoolVar(&debugBinary, "debug-binary", debugBinary, "Create unoptimized binary to use with delve, set -gcflags='-N -l' and omit -ldflags")
flag.BoolVar(&coverage, "coverage", coverage, "Write coverage profile of tests to coverage.txt")
flag.BoolVar(&long, "long", long, "Run tests without the -short flag")
flag.IntVar(&numVersions, "num-versions", numVersions, "Number of versions for changelog command")
flag.StringVar(&run, "run", "", "Specify which tests to run")
flag.StringVar(&benchRun, "bench", "", "Specify which benchmarks to run")
@@ -374,7 +376,13 @@ func test(tags []string, pkgs ...string) {
lazyRebuildAssets()
tags = append(tags, "purego")
args := []string{"test", "-short", "-timeout", timeout, "-tags", strings.Join(tags, " ")}
args := []string{"test", "-tags", strings.Join(tags, " ")}
if long {
timeout = longTimeout
} else {
args = append(args, "-short")
}
args = append(args, "-timeout", timeout)
if runtime.GOARCH == "amd64" {
switch runtime.GOOS {
@@ -1373,32 +1381,6 @@ func metalintShort() {
runPrint(goCmd, "test", "-short", "-run", "Metalint", "./meta")
}
func temporaryBuildDir() (string, error) {
// The base of our temp dir is "syncthing-xxxxxxxx" where the x:es
// are eight bytes from the sha256 of our working directory. We do
// this because we want a name in the global temp dir that doesn't
// conflict with someone else building syncthing on the same
// machine, yet is persistent between runs from the same source
// directory.
wd, err := os.Getwd()
if err != nil {
return "", err
}
hash := sha256.Sum256([]byte(wd))
base := fmt.Sprintf("syncthing-%x", hash[:4])
// The temp dir is taken from $STTMPDIR if set, otherwise the system
// default (potentially infrluenced by $TMPDIR on unixes).
var tmpDir string
if t := os.Getenv("STTMPDIR"); t != "" {
tmpDir = t
} else {
tmpDir = os.TempDir()
}
return filepath.Join(tmpDir, base), nil
}
func (t target) BinaryName() string {
if goos == "windows" {
return t.binaryName + ".exe"

View File

@@ -89,7 +89,8 @@ func handleFailureFn(dsn string) func(w http.ResponseWriter, req *http.Request)
pkt.Extra = raven.Extra{
"count": r.Count,
}
pkt.Fingerprint = []string{r.Description}
message := sanitizeMessageLDB(r.Description)
pkt.Fingerprint = []string{message}
if err := sendReport(dsn, pkt, userIDFor(req)); err != nil {
log.Println("Failed to send failure report:", err)

View File

@@ -143,15 +143,20 @@ var (
ldbPathRe = regexp.MustCompile(`(open|write|read) .+[\\/].+[\\/]index[^\\/]+[\\/][^\\/]+: `)
)
func crashReportFingerprint(message string) []string {
// Do not fingerprint on the stack in case of db corruption or fatal
// db io error - where it occurs doesn't matter.
orig := message
func sanitizeMessageLDB(message string) string {
message = ldbPosRe.ReplaceAllString(message, "${1}x)")
message = ldbFileRe.ReplaceAllString(message, "${1}x${3}")
message = ldbChecksumRe.ReplaceAllString(message, "${1}X${3}X")
message = ldbInternalKeyRe.ReplaceAllString(message, "${1}x${2}x")
message = ldbPathRe.ReplaceAllString(message, "$1 x: ")
return message
}
func crashReportFingerprint(message string) []string {
// Do not fingerprint on the stack in case of db corruption or fatal
// db io error - where it occurs doesn't matter.
orig := message
message = sanitizeMessageLDB(message)
if message != orig {
return []string{message}
}

View File

@@ -134,9 +134,11 @@ func (c *apiClient) Post(url, body string) (*http.Response, error) {
return c.Do(request)
}
var errNotFound = errors.New("invalid endpoint or API call")
func checkResponse(response *http.Response) error {
if response.StatusCode == http.StatusNotFound {
return errors.New("invalid endpoint or API call")
return errNotFound
} else if response.StatusCode == http.StatusUnauthorized {
return errors.New("invalid API key")
} else if response.StatusCode != http.StatusOK {

View File

@@ -8,6 +8,7 @@ package cli
import (
"fmt"
"net/url"
"github.com/urfave/cli"
)
@@ -35,7 +36,10 @@ var debugCommand = cli.Command{
func debugFile() cli.ActionFunc {
return func(c *cli.Context) error {
return indexDumpOutput(fmt.Sprintf("debug/file?folder=%v&file=%v", c.Args()[0], normalizePath(c.Args()[1])))(c)
query := make(url.Values)
query.Set("folder", c.Args()[0])
query.Set("file", normalizePath(c.Args()[1]))
return indexDumpOutput("debug/file?" + query.Encode())(c)
}
}

View File

@@ -48,6 +48,9 @@ func indexDumpOutput(url string) cli.ActionFunc {
return err
}
response, err := client.Get(url)
if errors.Is(err, errNotFound) {
return errors.New("not found (folder/file not in database)")
}
if err != nil {
return err
}

3
go.mod
View File

@@ -19,7 +19,7 @@ require (
github.com/gobwas/glob v0.2.3
github.com/gogo/protobuf v1.3.2
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.2
github.com/greatroar/blobloom v0.7.0
github.com/hashicorp/golang-lru v0.5.4
github.com/jackpal/gateway v1.0.7
@@ -52,7 +52,6 @@ require (
golang.org/x/text v0.3.6
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
golang.org/x/tools v0.1.0
google.golang.org/protobuf v1.26.0
)
go 1.14

7
go.sum
View File

@@ -129,7 +129,6 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -140,7 +139,6 @@ github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200j
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
@@ -190,7 +188,6 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -263,7 +260,6 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ=
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
@@ -274,7 +270,6 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@@ -652,7 +647,6 @@ google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
@@ -664,7 +658,6 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=

View File

@@ -443,3 +443,9 @@ ul.three-columns li, ul.two-columns li {
background-color: #eeeeee;
opacity: 1;
}
/* Make a "well" look more like a readonly text input when grouped with a button */
.input-group .well-sm {
padding-top: 6px;
padding-bottom: 6px;
}

View File

@@ -2,29 +2,29 @@
"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.": "Нова основна версия, която може да не е съвместима с предишни версии.",
"API Key": "API Ключ",
"API Key": "Ключ за ППИ",
"About": "За програмата",
"Action": "Действие",
"Actions": "Меню",
"Add": "Добави",
"Add Device": "Добави устройство",
"Add Folder": "Добави папка",
"Add Remote Device": "Добави ново устройство",
"Add devices from the introducer to our device list, for mutually shared folders.": "Добавяне на нови устройства през устройства предлагащи други устройства, за взаимно споделени папки.",
"Add new folder?": "Добави нова папка?",
"Add Remote Device": "Ново отдалечено устройство",
"Add devices from the introducer to our device list, for mutually shared folders.": "Добавяйте устройства през поръчителите за взаимно споделени папки.",
"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": "Адрес",
"Addresses": "Адреси",
"Advanced": "Допълнителни",
"Advanced Configuration": "Допълнителни настройки",
"Advanced Configuration": "Разширени настройки",
"All Data": "Всички данни",
"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?": "Разрешаване изпращането на анонимни статистически данни?",
"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": "Азбучен ред",
"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?": "Форматът на анонимния доклад е променен. Желаете ли да преминете към новия формат?",
"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?": "Форматът на данните за анонимно отчитане на употреба е променен. Желаете ли да използвате него вместо стария?",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"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%}?": "Сигурни ли сте, че искате да премахнете устройството {{name}}?",
@@ -32,20 +32,21 @@
"Are you sure you want to restore {%count%} files?": "Сигурни ли сте, че искате да възстановите файла {{count}}?",
"Are you sure you want to upgrade?": "Are you sure you want to upgrade?",
"Auto Accept": "Автоматично приемане",
"Automatic Crash Reporting": "Automatic Crash Reporting",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Автоматичното обновяване вече предлага избор между стабилни версии и кандидат версии.",
"Automatic Crash Reporting": "Автоматично изпращане на доклад за срив",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Автоматичното обновяване вече предлага избор между стабилни и предварителни издания.",
"Automatic upgrades": "Автоматично обновяване",
"Automatic upgrades are always enabled for candidate releases.": "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.": "Автоматично създаване или споделяне на папки, които това устройство предлага в пътя по подразбиране.",
"Available debug logging facilities:": "Дебъгинг функционалност на разположение:",
"Available debug logging facilities:": "Available debug logging facilities:",
"Be careful!": "Внимание!",
"Bugs": "Бъгове",
"Bugs": "Дефекти",
"Changelog": "Списък с промени",
"Clean out after": "Изчисти след",
"Clean out after": "Clean out after",
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Натиснете, за да видите грешки при откриването",
"Close": "Затвори",
"Click to see full identification string and QR code.": "Щракнете, за да видите целия идентификатор и код за QR.",
"Close": "Затваряне",
"Command": "Команда",
"Comment, when used at the start of a line": "Коментар, използван в началото на реда",
"Compression": "Компресиране",
@@ -54,14 +55,14 @@
"Connection Error": "Грешка при свързването",
"Connection Type": "Вид връзка",
"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. При промени по файловете се стартира сканиране само за променените папки. Ползите са, че промените биват синхронизирани по-бързо, без да се изисква цялостно сканирания на папките.",
"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": "Копиран от оригинала",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Създаване на шаблони за игнориране, презаписване на съществуващ файл в {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "При създаване на шаблони за отхвърляне, съществуващият файл {{path}}“ ще бъде презаписан.",
"Currently Shared With Devices": "Устройства, с които е споделена",
"Danger!": "Опасност!",
"Debugging Facilities": "Дебъг функционалност",
"Debugging Facilities": "Debugging Facilities",
"Default Configuration": "Default Configuration",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
@@ -70,15 +71,15 @@
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Изтрито",
"Deselect All": "Никое",
"Deselect devices to stop sharing this folder with.": "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": "Устройство",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство \"{{name}}\" ({{device}}) с адрес {{address}} желае да се свърже. Да бъде ли добавено?",
"Device ID": "Идентификатор на устройство",
"Device Identification": "Идентификатор на устройството",
"Device Identification": "Идентификатор на устройство",
"Device Name": "Име на устройството",
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
"Device rate limits": "Device rate limits",
"Device rate limits": "Ограничаване на скоростта",
"Device that last modified the item": "Устройство, което последно промени обекта",
"Devices": "Устройства",
"Disable Crash Reporting": "Disable Crash Reporting",
@@ -86,25 +87,28 @@
"Disabled periodic scanning and disabled watching for changes": "Периодичните сканирания и наблюденията за промяна са деактивирани.",
"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).": "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).": "Изключва сравняването и синхронизацията не правата на файловете. Полезно за системи с липсващи или специфични права (като FAT, exFAT, Synology, Android).",
"Discard": "Discard",
"Disconnected": "Не е свързано",
"Disconnected (Unused)": "Disconnected (Unused)",
"Discovered": "Открит",
"Discovery": "Откриване",
"Discovery Failures": "Грешка в откриването",
"Do not restore": "Не възстановявай",
"Do not restore all": "Не възстановявай всички",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Без възстановяване",
"Do not restore all": "Без възстановяване всички",
"Do you want to enable watching for changes for all your folders?": "Желаете ли да активирате наблюдението за промени на всички папки?",
"Documentation": "Документация",
"Download Rate": "Скорост на сваляне",
"Downloaded": "Изтеглен",
"Downloading": "Изтегляне",
"Edit": "Промени",
"Edit": "Редактиране",
"Edit Device": "Промяна на устройството",
"Edit Device Defaults": "Edit Device Defaults",
"Edit Device Defaults": "Подразбирани настройки на устройство",
"Edit Folder": "Промяна на папката",
"Edit Folder Defaults": "Edit Folder Defaults",
"Edit Folder Defaults": "Подразбирани настройки на папка",
"Editing {%path%}.": "Променяне на {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Разреши NAT traversal",
@@ -116,9 +120,9 @@
"Enter ignore patterns, one per line.": "Добавете шаблони за игнориране, по един на ред.",
"Enter up to three octal digits.": "Enter up to three octal digits.",
"Error": "Грешка",
"External File Versioning": "Външно управление на версиите",
"External File Versioning": "Външно управление на версии",
"Failed Items": "Неуспешни",
"Failed to setup, retrying": "Неуспешно конфигуриране, правенe на повторен опит",
"Failed to setup, retrying": "Грешка при настройване, извършва се повторен опит",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Неуспешна връзка към IPv6 сървъри може да се очаква ако няма IPv6 свързаност.",
"File Pull Order": "Ред на сваляне",
"File Versioning": "Версии на файловете",
@@ -133,17 +137,17 @@
"Folder ID": "Идентификатор на папката",
"Folder Label": "Име на папката",
"Folder Path": "Път до папката",
"Folder Type": "Вид папка",
"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": "Вид на папката",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Вида „{{receiveEncrypted}}“ може да бъде избран само при добавяне на папка.",
"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": "Папки",
"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)": "Интервал(и) за периодичното сканиране",
"Full Rescan Interval (s)": "Интервал на пълно обхождане (секунди)",
"GUI": "Потребителски интерфейс",
"GUI Authentication Password": "Парола за интерфейса",
"GUI Authentication User": "Потребител за интерфейса",
"GUI Authentication: Set User and Password": "GUI Authentication: Set User and Password",
"GUI Listen Address": "Адрес на слушане на GUI-то",
"GUI Listen Address": "Адрес на слушане",
"GUI Theme": "Тема за потребителския интерфейс",
"General": "Общи",
"Generate": "Генерирай",
@@ -151,20 +155,21 @@
"Global Discovery Servers": "Сървъри за глобално откриване",
"Global State": "Глобално състояние",
"Help": "Помощ",
"Home page": "Начална страница",
"Home page": "Страница",
"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.",
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
"Identification": "Идентификация",
"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": "Игнорирай",
"Ignore Patterns": "Шаблони за игнориране",
"Ignore Permissions": "Игнорирай правата за достъп",
"Ignore Permissions": "Незачитане на права",
"Ignored Devices": "Игнорирани устройства",
"Ignored Folders": "Игнорирани папки",
"Ignored at": "Ignored at",
"Incoming Rate Limit (KiB/s)": "Лимит на скоростта за сваляне (KiB/s)",
"Incoming Rate Limit (KiB/s)": "При изтегляне (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Неправилни настройки могат да повредят файлове и да попречат на синхронизирането.",
"Introduced By": "Предложено от",
"Introducer": "Може да предлага други устройства",
"Introducer": "Поръчител",
"Inversion of the given condition (i.e. do not exclude)": "Обратното на даденото условие (пр. не изключвай)",
"Keep Versions": "Пази версии",
"LDAP": "LDAP",
@@ -173,18 +178,18 @@
"Last seen": "Последно видяно",
"Latest Change": "Последна промяна",
"Learn more": "Научете повече",
"Limit": "Limit",
"Listeners": "Синхронизиращи устройства",
"Limit": "Ограничение",
"Listeners": "Listeners",
"Loading data...": "Зареждане на информация...",
"Loading...": "Зареждане...",
"Local Additions": "Local Additions",
"Local Discovery": "Локално откриване",
"Local State": "Локално състояние",
"Local State (Total)": "Локално състояние (общо)",
"Local Discovery": "Local Discovery",
"Local State": "Local State",
"Local State (Total)": "Local State (Total)",
"Locally Changed Items": "Locally Changed Items",
"Log": оклад",
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
"Logs": оклади",
"Log": невник",
"Log tailing paused. Scroll to the bottom to continue.": "Добавяне на редове към дневника е спряно. Плъзнете най-долу за да продължи.",
"Logs": невници",
"Major Upgrade": "Основно Обновяване",
"Mass actions": "Действия за всички",
"Maximum Age": "Максимална възраст",
@@ -211,48 +216,49 @@
"Options": "Настройки",
"Out of Sync": "Несинхронизирано",
"Out of Sync Items": "Несинхронизирани елементи",
"Outgoing Rate Limit (KiB/s)": "Лимит на скорост за качване (KiB/s)",
"Outgoing Rate Limit (KiB/s)": "При качване (KiB/s)",
"Override Changes": "Наложи локалните промени",
"Path": "Път",
"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": "Път до папката на това устройство. Ако не съществува ще бъде създадена. Символът тилда (~) може да бъде използван като заместител на",
"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%}.": "Къде да бъдат създавани, автоматично приети папки, както и предложението за път, при добавяне на нови папки от потребителският интерфейс. Символът тилда (~) ще бъде заменян с {{tilde}}.",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": ътят, където версиите да бъдат складирани (оставете празно за папката .stversions).",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": апка, в която да бъдат съхранявани версиите (оставете празно за подразбираната директория .stversions в споделената папка).",
"Pause": "Пауза",
"Pause All": "Пауза на всички",
"Paused": "На пауза",
"Paused (Unused)": "Paused (Unused)",
"Pending changes": "Pending changes",
"Periodic scanning at given interval and disabled watching for changes": ериодично сканиране, през определен интервал, без мониторинг за промени",
"Periodic scanning at given interval and enabled watching for changes": ериодично сканиране, през определен интервал, и мониторинг за промени",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Периодично сканиране, през определен интервал, мониторинга за промени не може да стартира. Всяка минута минута се прави опит за стартиране:",
"Permissions": "Права за достъп",
"Please consult the release notes before performing a major upgrade.": "Моля прочети бележките по обновяването преди да започнеш.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Моля задайте потребителско име и парола за потребителския интерфейс в секцията Настройки.",
"Periodic scanning at given interval and disabled watching for changes": ълно бхождане през определен интервал, без наблюдение за промени",
"Periodic scanning at given interval and enabled watching for changes": ълно бхождане през определен интервал и наблюдение за промени",
"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": "Права",
"Please consult the release notes before performing a major upgrade.": "Please consult the release notes before performing a major upgrade.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Задайте потребителско име и парола за потребителския интерфейс в настройките.",
"Please wait": "Моля изчакайте",
"Prefix indicating that the file can be deleted if preventing directory removal": "Представка, която индикира, че файлът може да бъде изтрит ако пречи на премахването на папка",
"Prefix indicating that the pattern should be matched without case sensitivity": "Представка, която индикира, че шаблона няма да прави разлика между главни/малки букви",
"Prefix indicating that the file can be deleted if preventing directory removal": "Представка, указваща че файлът може да бъде изтрит ако пречи на премахването на папка",
"Prefix indicating that the pattern should be matched without case sensitivity": "Представка, указваща че шаблонът не прави разлика в регистъра на буквите",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Преглед",
"Preview Usage Report": "Преглед на статистиката",
"Quick guide to supported patterns": "Бърз наръчник към поддържаните шаблони",
"Preview Usage Report": "Преглед на отчета за употребата",
"Quick guide to supported patterns": "Кратък наръчник на поддържаните шаблони",
"Random": "Произволен",
"Receive Encrypted": "Receive Encrypted",
"Receive Only": "Само получаване",
"Received data is already encrypted": "Received data is already encrypted",
"Recent Changes": "Последни промени",
"Reduced by ignore patterns": "Намалено посредством шаблон за игнориране",
"Release Notes": "Бележки по обновяването",
"Reduced by ignore patterns": "Намалено посредством шаблон за отхвърляне",
"Release Notes": "Бележки по изданието",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Кандидат версиите съдържат най-новата функционалност и поправки. Те са близки до традиционните дву-седмични Synchthing обновления.",
"Remote Devices": "Устройства",
"Remote GUI": "Remote GUI",
"Remove": "Премахни",
"Remote Devices": "Отдалачени устройства",
"Remote GUI": "Отдалечен ГПИ",
"Remove": "Премахване",
"Remove Device": "Премахване на устройство",
"Remove Folder": "Премахване на папка",
"Required identifier for the folder. Must be the same on all cluster devices.": "Задължителен идентификатор за папката. Трябва да бъде един и същ на всяко устройство.",
"Required identifier for the folder. Must be the same on all cluster devices.": "Задължителен идентификатор на папката. Трябва е еднакъв на всички устройства.",
"Rescan": "Сканирай",
"Rescan All": "Сканирай всички",
"Rescans": "Повторни сканирания",
"Restart": "Рестартирай",
"Restart": "Рестартиране",
"Restart Needed": "Изисква се рестартиране",
"Restarting": "Рестартиране",
"Restore": "Възстановяване",
@@ -261,48 +267,52 @@
"Resume All": "Пусни всички",
"Reused": "Повторно използван",
"Revert Local Changes": "Revert Local Changes",
"Save": "Запази",
"Scan Time Remaining": "Оставащо време за сканиране",
"Scanning": "Сканиране",
"See external versioning help for supported templated command line parameters.": "Прегледайте външната документацията за поддържаните командни параметри. ",
"Save": "Запазване",
"Scan Time Remaining": "Оставащо време до обхождане",
"Scanning": "Обхождане",
"See external versioning help for supported templated command line parameters.": "Прочетете ръководството за външно управление на версии, за да се запознаете с шаблонните параметри.",
"Select All": "Всички",
"Select a version": "Изберете версия",
"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.": "Изберете други устройства, с които да споделите с папката.",
"Select additional folders to share with this device.": "Изберете други папки, които да споделите с устройството.",
"Select latest version": "Избор на най-новата версия",
"Select oldest version": "Избор на най-старата версия",
"Select the folders to share with this device.": "Изберете папките за споделяне с това устройство.",
"Select the folders to share with this device.": "Изберете папките, които да споделите с устройството.",
"Send & Receive": "Изпращане и получаване",
"Send Only": "Само изпращане",
"Settings": "Настройки",
"Share": "Сподели",
"Share Folder": "Сподели папка",
"Share": "Споделяне",
"Share Folder": "Споделяне на папка",
"Share Folders With Device": "Споделяне на папки с устройството",
"Share this folder?": "Сподели тази папка?",
"Shared Folders": "Shared Folders",
"Shared Folders": "Споделени папки",
"Shared With": "Споделена с",
"Sharing": "Споделяне",
"Show ID": "Покажи идентификатора",
"Show QR": окажи QR",
"Show diff with previous version": "Показване на разликите спрямо предната версия",
"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": "Спри програмата",
"Show QR": реглед на QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Показване на разликите с предходната версия",
"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 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.": "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": "Опростени версии",
"Single level wildcard (matches within a directory only)": "Маска на едно ниво (покрива само в папка)",
"Size": "Размер",
"Smallest First": "Първо най-малките",
"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:": "Някои елементи не могат да бъдат възстановени:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Сорс код",
"Stable releases and release candidates": "Стабилни версии и кандидати за стабилни версии",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Стабилните версии са забавени с две седмици. През това време те преминават през тестване като бъдат кандидат версии.",
"Stable releases and release candidates": "Стабилни и предварителни издания",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Стабилните издания биват забавяни с две седмици. През този период те преминават през изпитване като предварителни издания.",
"Stable releases only": "Само стабилни версии",
"Staggered File Versioning": "Наслагващи се версии",
"Start Browser": "Стартирай браузъра",
"Statistics": "Статистика",
"Stopped": "Не се синхронизира",
"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.",
"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.": "Съхранява и синхронизира само шифровани данни. Папките на всички свързани устройства трябва да бъдат настроени със същата парола или също да са от вида „{{receiveEncrypted}}.",
"Support": "Помощ",
"Support Bundle": "Support Bundle",
"Sync Protocol Listen Addresses": "Адрес за слушане на синхронизиращия протокол",
@@ -310,21 +320,23 @@
"Syncthing has been shut down.": "Syncthing е спрян.",
"Syncthing includes the following software or portions thereof:": "Syncthing уползотворява частично или изцяло следните софтуерни продукти:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as 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 се рестартира",
"Syncthing is upgrading.": "Syncthing се обновява.",
"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 не е включен, или има проблем с връзката с Интернет. Повторен опит...",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Изглежда, че Syncthing не работи или няма достъп до интернет. Извършва се повторен опит",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing има проблем при обработването на заявката. Моля, презаредете браузъра или рестартирайте Syncthing ако проблемът продължи.",
"Take me back": "Take me back",
"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.": "Администраторският панел на Syncthing разрешава дистанционен достъп без да изисква парола.",
"The aggregated statistics are publicly available at the URL below.": "Обобщение на събраните статистически ще намерите на долния URL адрес.",
"The aggregated statistics are publicly available at the URL below.": "Обобщение на събраните статистически данни ще намерите на адреса по-долу.",
"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.": "Конфигурацията е запазена, но не е активирана. Syncthing трябва да рестартира, за да се активира новата конфигурация.",
"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 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 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.": "Идентификаторът на папката трябва да бъде уникален.",
@@ -334,6 +346,7 @@
"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 секунди, за първия ден се пази версия на всеки час, за първите 30 дена се пази версия всеки ден, до максимума се пази една версия всяка седмица.",
"The following items could not be synchronized.": "Следните елементи не могат да бъдат синхронизирани.",
"The following items were changed locally.": "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 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.",
@@ -345,12 +358,13 @@
"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 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 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.": "Ще бъдат спрени и автоматично синхронизирани, когато грешката бъде оправена.",
"This Device": "Вашето устройство",
"This Device": "Това устройство",
"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.": "Това е нова основна версия.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Тази настройка контролира нужното свободното място на основния (пр. този с базата данни) диск.",
"Time": "Време",
@@ -366,18 +380,18 @@
"Unignore": "Unignore",
"Unknown": "Неясно",
"Unshared": "Несподелена",
"Unshared Devices": "Unshared Devices",
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Unshared Devices": "Устройства, с които не е споделено",
"Unshared Folders": "Несподелени папки",
"Untrusted": "Недоверено",
"Up to Date": "Синхронизирано",
"Updated": "Обновено",
"Upgrade": "Обнови",
"Upgrade To {%version%}": "Обновен до {{version}}",
"Upgrade": "Обновяване",
"Upgrade To {%version%}": "Обновено до {{version}}",
"Upgrading": "Обновяване",
"Upload Rate": "Скорост на качване",
"Uptime": "Работи от",
"Usage reporting is always enabled for candidate releases.": "Докладът за ползването е винаги включен за кандидат нови версии.",
"Use HTTPS for GUI": "Използвай HTTPS за потребителския интерфейс",
"Usage reporting is always enabled for candidate releases.": "Отчитането на употребата винаги е включено за предварителните издания.",
"Use HTTPS for GUI": "Потребителският интерфейс работи под HTTPS",
"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": "Версия",
"Versions": "Версии",
@@ -391,12 +405,12 @@
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Предупреждение, този път е по-горна директория на съществуващата папка \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Внимание, това е вътрешна папка на вече съществуваща папка \"{{otherFolder}}\".",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Предупреждение, този път е под-директория на съществуващата папка \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Предупреждение: Ако използвате външна програма за наблюдение като {{syncthingInotify}}, трябва да я деактивирате.",
"Watch for Changes": "Мониторинг за промени",
"Watching for Changes": "Мониторинг за промени",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Предупреждение: Ако използвате външно приложение за наблюдение като {{syncthingInotify}}, трябва да го спрете.",
"Watch for Changes": "Watch for Changes",
"Watching for Changes": "Watching for Changes",
"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 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": "Да",
"You can also select one of these nearby devices:": "Също така може да изберете едно от устройствата, които намират се наблизо:",
"You can change your choice at any time in the Settings dialog.": "Може да промените решението си по всяко време в прозореца Настройки.",
@@ -404,15 +418,15 @@
"You have no ignored devices.": "Няма игнорирани устройства.",
"You have no ignored folders.": "Няма игнорирани папки.",
"You have unsaved changes. Do you really want to discard them?": "Има незапазени промени. Наистина ли желаете да ги отмените?",
"You must keep at least one version.": "Трябва да пазиш поне една версия.",
"You must keep at least one version.": "You must keep at least one version.",
"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": "дни",
"directories": "директории",
"files": "файла",
"full documentation": "пълна документация",
"items": "елемента",
"seconds": "seconds",
"seconds": "секунди",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} желае да сподели папката \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} желае да сподели папката \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
"{%reintroducer%} might reintroduce this device.": "Поръчителят {{reintroducer}} може отново да предложи това устройство."
}

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Clica per a vore els fallos en el descobriment",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Tancar",
"Command": "Comando",
"Comment, when used at the start of a line": "Comentar, quant s'utilitza al principi d'una línia",
@@ -93,6 +94,9 @@
"Discovered": "Descobert",
"Discovery": "Descobriment",
"Discovery Failures": "Fallades al Descobriment",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "No restaurar",
"Do not restore all": "No restaurar en absolut",
"Do you want to enable watching for changes for all your folders?": "Vols activar el rastreig continu de canvis per a totes les carpetes?",
@@ -153,6 +157,7 @@
"Help": "Ajuda",
"Home page": "Pàgina inicial",
"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": "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": "Ignorar",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Escaneig periòdic a l'interval determinat i desactivat el rastreig continu de canvis",
"Periodic scanning at given interval and enabled watching for changes": "Escaneig periòdic a l'interval determinat i activat el rastreig continu de canvis",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Escaneig periòdic a l'interval determinat i errada al activar el rastreig continu de canvis, reintentant cada 1 minut:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Permisos",
"Please consult the release notes before performing a major upgrade.": "Per favor, consultar les notes de la versió abans de fer una actualització important.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Per favor, estableix un usuari i password per a l'Interfície Gràfica d'Usuari en el menú d'Adjustos.",
@@ -284,6 +290,8 @@
"Sharing": "Compartint",
"Show ID": "Mostrar ID",
"Show QR": "Mostrar QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Mostrar les diferències amb la versió prèvia",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrat en lloc de l'ID del dispositiu en l'estat del grup (cluster). S'anunciarà als altres dispositius com el nom opcional per defecte.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrat en lloc de l'ID del dispositiu en l'estat del grup (cluster). S'actualitzarà al nom que el dispositiu anuncia si es deixa buit.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Comodí de nivell únic (coincideix sols dins d'un directori)",
"Size": "Tamany",
"Smallest First": "El més xicotet primer",
"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:": "Alguns ítems no s'han pogut restaurar:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Codi font",
"Stable releases and release candidates": "Versions estables i versions candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Les versions estables es retrasen sobre dos setmanes. Durant aquest temps es fiquen a prova com versions candidates.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing s'ha apagat",
"Syncthing includes the following software or portions thereof:": "Syncthing inclou el següent software o parts d'ell:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing és Software Gratuït i Open Source llicenciat com 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 està reiniciant.",
"Syncthing is upgrading.": "Syncthing està actualitzant-se.",
"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.",
@@ -334,6 +346,7 @@
"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.": "S'utilitzen els següents intervals: per a la primera hora es guarda una versió cada 30 segons, per al primer dia es guarda una versió cada hora, per als primers 30 dies es guarda una versió diaria, fins l'edat màxima es guarda una versió cada setmana.",
"The following items could not be synchronized.": "Els següents objectes no s'han pogut sincronitzar.",
"The following items were changed locally.": "Els següents ítems es canviaren localment.",
"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.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Es reintenta automàticament i es sincronitzaràn quant el resolga l'error.",
"This Device": "Aquest Dispositiu",
"This can easily give hackers access to read and change any files on your computer.": "Açò pot donar accés fàcilment als hackers per a llegir i canviar qualsevol fitxer al teu ordinador.",
"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.": "Aquesta és una actualització important de la versió.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Aquest ajust controla l'espai lliure requerit en el disc inicial (per exemple, la base de dades de l'index).",
"Time": "Temps",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Mazání verzí",
"Cleanup Interval": "Interval mazání",
"Click to see discovery failures": "Kliknutím zobrazíte nezdary při objevování",
"Click to see full identification string and QR code.": "Kliknutím zobrazíte úplnou identifikaci a QR kód.",
"Close": "Zavřít",
"Command": "Příkaz",
"Comment, when used at the start of a line": "Pokud použito na jeho začátku, je řádek považován za komentář",
@@ -91,8 +92,11 @@
"Disconnected": "Odpojeno",
"Disconnected (Unused)": "Odpojeno (nepoužité)",
"Discovered": "Objeveno",
"Discovery": "Oznamování",
"Discovery Failures": "Nezdary při oznamování",
"Discovery": "Objevování",
"Discovery Failures": "Nezdary při objevování",
"Dismiss": "OK",
"Do not add it to the ignore list, so this notification may recur.": "Nepřidávat k ignorování, takže oznámení se může opakovat.",
"Do not add it to the ignore list, so this notification may recurr.": "Nepřidávat k ignorování, takže oznámení se může opakovat.",
"Do not restore": "Neobnovit",
"Do not restore all": "Neobnovit nic",
"Do you want to enable watching for changes for all your folders?": "Chcete zapnout sledování změn pro všechny složky?",
@@ -112,7 +116,7 @@
"Enabled": "Zapnuto",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Zadejte kladné číslo (např. „2.35“) a zvolte jednotku. Procenta znamenají část celkové velikosti úložiště.",
"Enter a non-privileged port number (1024 - 65535).": "Zadejte čí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.": "Zadejte adresy oddělené čárkami („tcp://ip:port“, „tcp://host:port“) nebo „dynamic“ pro automatické zjištění adresy.",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Zadejte adresy oddělené čárkami („tcp://ip:port“, „tcp://host:port“) nebo „dynamic“ pro automatické objevení adresy.",
"Enter ignore patterns, one per line.": "Zadejte vzory toho, co ignorovat každý na zvlášť řádek.",
"Enter up to three octal digits.": "Zadejte nanejvýš tři osmičkové číslice.",
"Error": "Chyba",
@@ -147,12 +151,13 @@
"GUI Theme": "Motiv vzhledu pro GUI",
"General": "Obecné",
"Generate": "Vytvořit",
"Global Discovery": "Globální oznamování",
"Global Discovery Servers": "Servery globálního oznamování",
"Global Discovery": "Globální objevování",
"Global Discovery Servers": "Servery globálního objevování",
"Global State": "Globální status",
"Help": "Nápověda",
"Home page": "Domovská stránka",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Nicméně Vaše současná nastavení značí, že si nepřejete funkci povolit. Automatické hlášení pádů tedy bylo vypnuto.",
"Identification": "Identifikace",
"If untrusted, enter encryption password": "V případě nedůvěryhodnosti zadat šifrovací heslo",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Pokud chcete ostatním uživatelům tohoto počítače zabránit v přístupu k Syncthing (a skrze něj tedy ke svým souborům), zvažte nastavení ověřování se.",
"Ignore": "Ignorovat",
@@ -178,7 +183,7 @@
"Loading data...": "Načítání dat…",
"Loading...": "Načítání…",
"Local Additions": "Místní příbytky",
"Local Discovery": "Místní oznamování",
"Local Discovery": "Místní objevování",
"Local State": "Místní status",
"Local State (Total)": "Místní status (Celkem)",
"Locally Changed Items": "Lokálně změněné položky",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodické skenování podle zadaného intervalu; sledování změn vypnuto",
"Periodic scanning at given interval and enabled watching for changes": "Periodické skenování podle zadaného intervalu; sledování změn zapnuto",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodické skenování podle zadaného intervalu; nastavení sledování změn se nezdařilo, opětovný pokus každou 1 min: ",
"Permanently add it to the ignore list, suppressing further notifications.": "Natrvalo ignorovat, takže oznámení již nebudou přicházet.",
"Permissions": "Oprávnění",
"Please consult the release notes before performing a major upgrade.": "Před přechodem na novější hlavní verzi si nejdříve přečtěte poznámky k vydání nové verze.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "V dialogu Nastavení zadejte uživatelské jméno a heslo pro ověření se v GUI.",
@@ -284,6 +290,8 @@
"Sharing": "Sdílení",
"Show ID": "Zobrazit identifikátor",
"Show QR": "Zobrazit QR kód",
"Show detailed discovery status": "Zobrazit detailní stav objevování",
"Show detailed listener status": "Zobrazit detailní stav naslouchajících",
"Show diff with previous version": "Ukázat rozdíl oproti předchozí verzi",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Bude odesíláno ostatním zařízením jako výchozí název zařízení.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Pokud nebude vyplněno, bude nastaveno na název, který zařízení odesílá.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Jednoúrovňový zástupný znak (shody pouze uvnitř složky)",
"Size": "Velikost",
"Smallest First": "Od nejmenších",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Nebyly zajištěny některé metody pro objevování ostatních zařízení nebo oznamování tohoto zařízení:",
"Some items could not be restored:": "Některé položky nemohly být obnoveny:",
"Some listening addresses could not be enabled to accept connections:": "Některé adresy k naslouchání nebylo možné povolit pro příchozí spojení:",
"Source Code": "Zdrojové kódy",
"Stable releases and release candidates": "Stabilní vydání a kandidáti na vydání",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabilní vydání jsou opožděna zhruba o dva týdny. Po tuto dobu se testují jako kandidáti na vydání.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing bylo vypnuto.",
"Syncthing includes the following software or portions thereof:": "Syncthing obsahuje následující software nebo jejich část:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing je svobodný a open source software licencovaný jako MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing naslouchá pro příchozí spojení na následujících síťových adresách:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing nenaslouchá pro příchozí spojení na žádné adrese. Mohou fungovat jen odchozí spojení z tohoto zařízení.",
"Syncthing is restarting.": "Syncthing se restartuje.",
"Syncthing is upgrading.": "Syncthing se aktualizuje.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing nyní umožňuje automaticky hlásit vývojářům pády aplikace. Tato funkce je ve výchozím stavu povolena.",
@@ -334,6 +346,7 @@
"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.": "Jsou použity následující intervaly: za první hodinu jsou ponechány verze pro každých 30 sekund, za první den jsou ponechány verze pro každou hodinu, za prvních 30 dní jsou ponechány verze pro každý den a do nejvyššího nastaveného stáří jsou ponechány verze pro každý týden.",
"The following items could not be synchronized.": "Následující položky nemohly být synchronizovány.",
"The following items were changed locally.": "Tyto položky byly změněny lokálně",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "K objevování ostatních zařízení a oznamování tohoto zařízení se používají následující metody:",
"The following unexpected items were found.": "Byly nalezeny tyto neočekávané položky.",
"The interval must be a positive number of seconds.": "Interval musí být kladný počet sekund.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Interval (v sekundách) pro spouštění čištění ve složce s verzemi. Nula pravidelné čištění vypíná.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Nové pokusy o synchronizaci budou probíhat automaticky a položky budou synchronizovány jakmile bude chyba odstraněna.",
"This Device": "Toto zařízení",
"This can easily give hackers access to read and change any files on your computer.": "Toto může útočníkům jednoduše umožnit čtení a úpravy souborů na vašem počítači. ",
"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.": "Toto zařízení nemůže automaticky objevovat ostatní zařízení ani oznamovat ostatním vlastní adresu. Připojit se mohou jen zařízení se staticky nastavenou adresou.",
"This is a major version upgrade.": "Toto je velká aktualizace.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Toto nastavení ovládá velikost volného prostoru na hlavním datovém úložišti (to, na kterém je databáze rejstříku).",
"Time": "Čas",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Rydder op i versioner",
"Cleanup Interval": "Ryd op interval",
"Click to see discovery failures": "Klik for at se opdagelsesfejl",
"Click to see full identification string and QR code.": "Klik for at se fuld identifikation streng og QR kode.",
"Close": "Luk",
"Command": "Kommando",
"Comment, when used at the start of a line": "Kommentar, når den bruges i starten af en linje",
@@ -93,6 +94,9 @@
"Discovered": "Opdaget",
"Discovery": "Opslag",
"Discovery Failures": "Fejl ved opdagelse",
"Dismiss": "Forlade",
"Do not add it to the ignore list, so this notification may recur.": "Tilføj ikke til ignore liste, så påmindelsen kan gentage.",
"Do not add it to the ignore list, so this notification may recurr.": "Tilføj ikke til ignore liste, så påmindelse kan gentage.",
"Do not restore": "Genskab ikke",
"Do not restore all": "Genskab ikke alle",
"Do you want to enable watching for changes for all your folders?": "Vil du aktivere løbende overvågning af ændringer for alle dine mapper?",
@@ -153,6 +157,7 @@
"Help": "Hjælp",
"Home page": "Hjem",
"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": "Identifikation",
"If untrusted, enter encryption password": "Hvis ikke troværdig, indtast krypteringsadgangskode",
"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": "Ignorér",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodisk skanning med et givent interval og deaktiveret overvågning af ændringer",
"Periodic scanning at given interval and enabled watching for changes": "Periodisk skanning med et givent interval og aktiveret overvågning af ændringer",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning med et givent interval og lykkedes ikke med at opsætte overvågning af ændringer; prøver igen hvert minut:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanent tilføj til ignore liste, undertrykker yderligere påmindelse.",
"Permissions": "Tilladelser",
"Please consult the release notes before performing a major upgrade.": "Tjek venligst udgivelsesnoterne før opgradering til en ny hovedversion.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Opret venligst en GUI-bruger og -adgangskode i opsætningen.",
@@ -284,6 +290,8 @@
"Sharing": "Deler",
"Show ID": "Vis ID",
"Show QR": "Vis QR",
"Show detailed discovery status": "Vis detaljeret opdagelse status",
"Show detailed listener status": "Vis detaljeret lytter status",
"Show diff with previous version": "Vis forskelle fra tidligere version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Vises i stedet for enheds-ID i klyngestatus. Vil blive sendt til andre enheder som valgfrit standardnavn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vises i stedet for enheds-ID i klyngestatus. Vil blive opdateret til det navn, som enheden sender, hvis det ikke er udfyldt.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Enkeltniveau-wildcard (matcher kun inden for en mappe)",
"Size": "Størrelse",
"Smallest First": "Mindste først",
"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:": "Enkelte filer kunne ikke genskabes:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Kildekode",
"Stable releases and release candidates": "Stabile udgivelser og udgivelseskandidater ",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabile udgivelser er forsinket med omkring to uger. I denne periode gennemgår de afprøvninger som udgivelseskandidater.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing er lukket ned.",
"Syncthing includes the following software or portions thereof:": "Syncthing indeholder følgende software eller dele heraf:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing er fri og åben kildekode software licenseret som 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 genstarter.",
"Syncthing is upgrading.": "Syncthing opgraderer.",
"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.",
@@ -334,6 +346,7 @@
"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.": "De følgende intervaller er brugt: Inden for den første time bliver en version gemt hvert 30. sekund, inden for den første dag bliver en version gemt hver time, inden for de første 30 dage bliver en version gemt hver dag, og indtil den maksimale alder bliver en version gemt hver uge.",
"The following items could not be synchronized.": "Følgende filer kunne ikke synkroniseres.",
"The following items were changed locally.": "De følgende filer er ændret lokalt.",
"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.": "Følgende ikke-forventet emner blev fundet.",
"The interval must be a positive number of seconds.": "Intervallet skal være et positivt antal sekunder.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Interval i sekunder for kørsel af oprydning i versionskatalog. Nul vil deaktivere periodisk rengøring.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "De prøves igen automatisk og vil blive synkroniseret, når fejlen er løst.",
"This Device": "Denne enhed",
"This can easily give hackers access to read and change any files on your computer.": "Dette gør det nemt for hackere at få adgang til at læse og ændre filer på din 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.": "Dette er en ny hovedversion.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Denne indstilling styrer den krævede ledige plads på hjemmedrevet (dvs. drevet med indeksdatabasen).",
"Time": "Tid",

View File

@@ -18,7 +18,7 @@
"Advanced": "Erweitert",
"Advanced Configuration": "Erweiterte Konfiguration",
"All Data": "Alle Daten",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alle Ordner, welche mit diesem Gerät geteilt werden müssen von einem Passwort geschützt werden, sodass die gesendeten Daten ohne Kenntnis des Passworts nicht gelesen werden können. ",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alle Ordner, welche mit diesem Gerät geteilt werden, müssen von einem Passwort geschützt werden, so dass die gesendeten Daten ohne Kenntnis des Passworts nicht gelesen werden können. ",
"Allow Anonymous Usage Reporting?": "Übertragung von anonymen Nutzungsberichten erlauben?",
"Allowed Networks": "Erlaubte Netzwerke",
"Alphabetic": "Alphabetisch",
@@ -45,6 +45,7 @@
"Cleaning Versions": "Versionen bereinigen",
"Cleanup Interval": "Bereinigungsintervall",
"Click to see discovery failures": "Klick um Gerätesuchfehler anzuzeigen",
"Click to see full identification string and QR code.": "Klicken um die volle Kennung und den QR-Code anzuzeigen.",
"Close": "Schließen",
"Command": "Befehl",
"Comment, when used at the start of a line": "Kommentar, wenn am Anfang der Zeile benutzt.",
@@ -62,16 +63,16 @@
"Currently Shared With Devices": "Derzeit mit Geräten geteilt",
"Danger!": "Achtung!",
"Debugging Facilities": "Debugging-Möglichkeiten",
"Default Configuration": "Standardkonfiguration",
"Default Device": "Standardgerät",
"Default Folder": "Standardordner",
"Default Configuration": "Vorgabekonfiguration",
"Default Device": "Vorgabegerät",
"Default Folder": "Vorgabeordner",
"Default Folder Path": "Standardmäßiger Ordnerpfad",
"Defaults": "Standards",
"Defaults": "Vorgaben",
"Delete Unexpected Items": "Unerwartete Elemente löschen",
"Deleted": "Gelöscht",
"Deselect All": "Alle abwählen",
"Deselect devices to stop sharing this folder with.": "Die Geräte abwählen, für die dieser Ordner nicht mehr freigegeben werden soll.",
"Deselect folders to stop sharing with this device.": "Ordner abwählen um das Teilen mit dem Gerät zu stoppen.",
"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.",
"Device": "Gerät",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Gerät \"{{name}}\" ({{device}} {{address}}) möchte sich verbinden. Gerät hinzufügen?",
"Device ID": "Gerätekennung",
@@ -93,6 +94,9 @@
"Discovered": "Ermittelt",
"Discovery": "Gerätesuche",
"Discovery Failures": "Gerätesuchfehler",
"Dismiss": "Ausblenden",
"Do not add it to the ignore list, so this notification may recur.": "Nicht zur Ignorierliste hinzufügen, diese Benachrichtigung kann erneut auftauchen.",
"Do not add it to the ignore list, so this notification may recurr.": "Nicht zur Ignorierliste hinzufügen, diese Benachrichtigung kann erneut auftauchen.",
"Do not restore": "Nicht wiederherstellen",
"Do not restore all": "Nicht alle wiederherstellen",
"Do you want to enable watching for changes for all your folders?": "Möchten Sie das nach Änderungen für alle Ihre Ordner gesucht wird aktivieren?",
@@ -102,9 +106,9 @@
"Downloading": "Lädt herunter",
"Edit": "Bearbeiten",
"Edit Device": "Gerät bearbeiten",
"Edit Device Defaults": "Gerätestandards bearbeiten",
"Edit Device Defaults": "Gerätevorgaben bearbeiten",
"Edit Folder": "Ordner bearbeiten",
"Edit Folder Defaults": "Ordnerstandards bearbeiten",
"Edit Folder Defaults": "Ordnervorgaben bearbeiten",
"Editing {%path%}.": "Bearbeite {{path}}.",
"Enable Crash Reporting": "Absturzmeldung aktivieren",
"Enable NAT traversal": "NAT-Durchdringung aktivieren",
@@ -114,7 +118,7 @@
"Enter a non-privileged port number (1024 - 65535).": "Geben Sie eine nichtprivilegierte Portnummer ein (1024 - 65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Kommagetrennte Adressen (\"tcp://ip:port\", \"tcp://host:port\") oder \"dynamic\" eingeben, um die Adresse automatisch zu ermitteln.",
"Enter ignore patterns, one per line.": "Geben Sie Ignoriermuster ein, eines pro Zeile.",
"Enter up to three octal digits.": "Tragen Sie bis zu drei oktale (0-7) Zahlen ein.",
"Enter up to three octal digits.": "Tragen Sie bis zu drei oktale Ziffern (0-7) ein.",
"Error": "Fehler",
"External File Versioning": "Externe Dateiversionierung",
"Failed Items": "Fehlgeschlagene Elemente",
@@ -135,14 +139,14 @@
"Folder Path": "Ordnerpfad",
"Folder Type": "Ordnertyp",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Ordnertyp „{{receiveEncrypted}}“ kann nur beim Hinzufügen eines neuen Ordners festgelegt werden.",
"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.": "Der Ordnertyp \"{{receiveEncrypted}}\" kann nach dem Hinzufügen nicht geändert werden. Sie müssen den Ordner entweder entfernen, löschen oder die Daten auf dem Speichermedium entschlüsseln und anschließend den Order wieder neu hinzufügen.",
"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.": "Der Ordnertyp \"{{receiveEncrypted}}\" kann nach dem Hinzufügen nicht geändert werden. Sie müssen den Ordner entfernen, die Daten auf dem Speichermedium löschen oder entschlüsseln und anschließend den Order wieder neu hinzufügen.",
"Folders": "Ordner",
"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.": "Bei den folgenden Ordnern ist ein Fehler aufgetreten, während Sie nach Änderungen suchten. Es wird jede Minute erneut gesucht, damit die Fehler bald verschwinden. Falls die Fehler bestehen bleiben, versuchen Sie, das zugrunde liegende Problem zu beheben, und fragen Sie evtl. nach Hilfe.",
"Full Rescan Interval (s)": "Vollständiges Scanintervall (s)",
"GUI": "GUI",
"GUI Authentication Password": "Passwort für Zugang zur Benutzeroberfläche",
"GUI Authentication User": "Benutzername für Zugang zur Benutzeroberfläche",
"GUI Authentication: Set User and Password": "Benutzeroberflächenauthentisierung: Geben Sie Benutzer und Passwort ein. ",
"GUI Authentication: Set User and Password": "Authentifizierung für die Benutzeroberfläche: Geben Sie Benutzer und Passwort ein. ",
"GUI Listen Address": "Addresse der Benutzeroberfläche",
"GUI Theme": "GUI Design",
"General": "Allgemein",
@@ -153,8 +157,9 @@
"Help": "Hilfe",
"Home page": "Homepage",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Ihre aktuellen Einstellungen weisen jedoch daraufhin, dass Sie die Aktivierung möglicherweise nicht wünschen. Wir haben die automatischen Absturzberichte für Sie deaktiviert.",
"Identification": "Kennung",
"If untrusted, enter encryption password": "Wenn nicht vertraut, geben Sie das Verschlüsselungspasswort ein",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Falls Sie andere Benutzer dieses Computers daran hindern möchten auf Syncthing und somit ihren Dateien Zugriff zu haben, setzen Sie bitte einen Benutzernamen und Passwort.",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Falls Sie andere Benutzer dieses Computers am Zugriff auf Syncthing und somit Ihren Dateien hindern möchten, richten Sie bitte eine Authentifizierung ein.",
"Ignore": "Ignorieren",
"Ignore Patterns": "Ignoriermuster",
"Ignore Permissions": "Berechtigungen ignorieren",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodisches Scannen bei angegebenen Intervall und deaktivierter Überwachung von Änderungen",
"Periodic scanning at given interval and enabled watching for changes": "Periodisches Scannen bei angegebenen Intervall und aktivierter Überwachung von Änderungen",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisches Scannen bei angegebenen Intervall, fehlgeschlagene überprüfen auf Änderungen und erneuter versuch in 1 Min:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanent zur Ignorierliste hinzufügen, um weitere Benachrichtigungen zu unterdrücken.",
"Permissions": "Berechtigungen",
"Please consult the release notes before performing a major upgrade.": "Bitte lesen Sie die Veröffentlichungshinweise bevor Sie eine Hauptversionsaktualisierung installieren.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Bitte lege einen Benutzer und ein Passwort für die Benutzeroberfläche in den Einstellungen fest.",
@@ -267,8 +273,8 @@
"See external versioning help for supported templated command line parameters.": "Siehe Hilfe zur externen Versionierung für unterstützte Befehlszeilenparameter.",
"Select All": "Alle auswählen",
"Select a version": "Wählen Sie eine Version",
"Select additional devices to share this folder with.": "Weitere Geräte auswählen, für die dieser Ordner geteilt werden soll.",
"Select additional folders to share with this device.": "Wählen Sie weitere Ordner, um sie mit diesem Gerät zu teilen.",
"Select additional devices to share this folder with.": "Weitere Geräte auswählen, um diesen Ordner damit zu teilen.",
"Select additional folders to share with this device.": "Weitere Ordner auswählen, um mit diesem Gerät zu teilen.",
"Select latest version": "Letzte Version auswählen",
"Select oldest version": "Älteste Version auswählen",
"Select the folders to share with this device.": "Wähle Sie die Ordner aus, die Sie mit diesem Gerät teilen möchten.",
@@ -284,6 +290,8 @@
"Sharing": "Teilen",
"Show ID": "Eigene Kennung",
"Show QR": "Zeige QR Code",
"Show detailed discovery status": "Status der Gerätesuche anzeigen",
"Show detailed listener status": "Detaillierten Listener-Status anzeigen",
"Show diff with previous version": "Unterschied zur vorherigen Version anzeigen",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Wird anstatt der Gerätekennung im Verbund-Status angezeigt. Wird als optionaler Standardname an andere Geräte bekannt gegeben.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Wird auf diesem Gerät als Gerätename angezeigt und an die anderen Geräte im Geräte-Verbund weitergegeben. Wenn kein Gerätename anegegeben wird, wird der Name des entfernten Gerätes genommen.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Einzelnes Maskenzeichen (wird für einen einzelnen Ordner verwendet)",
"Size": "Größe",
"Smallest First": "Kleinstes zuerst",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Zum Auffinden anderer Geräte, oder um dieses Gerät anzukündigen, konnten manche Methoden nicht eingerichtet werden:",
"Some items could not be restored:": "Einige Elemente konnten nicht wiederhergestellt werden:",
"Some listening addresses could not be enabled to accept connections:": "An manchen Netzwerkadressen kann nicht gelauscht werden, um Verbindungen anzunehmen:",
"Source Code": "Quellcode",
"Stable releases and release candidates": "Stabile Veröffentlichungen und Veröffentlichungskandidaten",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabile Veröffentlichungen werden ca. 2 Wochen verzögert. Während dieser Zeit durchlaufen sie eine Testphase als Veröffentlichungskandidaten.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing wurde heruntergefahren.",
"Syncthing includes the following software or portions thereof:": "Syncthing enthält die folgende Software oder Teile von:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing ist freie und quelloffene Software, lizenziert als MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing lauscht an den folgenden Netzwerkadressen auf Verbindungsversuche von anderen Geräten:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing lauscht nicht auf Verbindungsversuche von anderen Geräten auf irgendeiner Adresse. Nur von diesem Gerät ausgehende Verbindungen können funktionieren.",
"Syncthing is restarting.": "Syncthing wird neu gestartet",
"Syncthing is upgrading.": "Syncthing wird aktualisiert",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing unterstützt jetzt automatische Absturzberichte an die Entwickler. Diese Funktion ist standardmäßig aktiviert.",
@@ -329,11 +341,12 @@
"The folder ID cannot be blank.": "Die Ordnerkennung darf nicht leer sein.",
"The folder ID must be unique.": "Die Ordnerkennung muss eindeutig sein.",
"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.": "Der Ordnerinhalt auf anderen Geräten wird überschrieben, um mit diesem Gerät identisch zu werden. Dateien, die hier nicht vorhanden sind, werden auf anderen Geräten gelöscht.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Der Ordnerinhalt auf diesem Gerät wird überschrieben, um mit anderen Geräten identisch zu werden. Dateien, die hier neu hinzugefügt werden, werden gelöscht.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Der Ordnerinhalt auf diesem Gerät wird überschrieben, um mit anderen Geräten identisch zu werden. Dateien, die hier neu hinzugefügt wurden, werden gelöscht.",
"The folder path cannot be blank.": "Der Ordnerpfad darf nicht leer sein.",
"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.": "Es wird in folgenden Abständen versioniert: In der ersten Stunde wird alle 30 Sekunden eine Version behalten, am ersten Tag eine jede Stunde, in den ersten 30 Tagen eine jeden Tag. Danach wird bis zum angegebenen Höchstalter eine Version pro Woche behalten.",
"The following items could not be synchronized.": "Die folgenden Elemente konnten nicht synchronisiert werden.",
"The following items were changed locally.": "Die folgenden Elemente wurden lokal geändert.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Die folgenden Methoden werden verwendet, um andere Geräte im Netzwerk aufzufinden und dieses Gerät anzukündigen, damit es von anderen gefunden wird:",
"The following unexpected items were found.": "Die folgenden unerwarteten Elemente wurden gefunden.",
"The interval must be a positive number of seconds.": "Das Intervall muss eine positive Zahl von Sekunden sein.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Das Intervall, in Sekunden, zwischen den Bereinigungen im Versionsverzeichnis. 0 um das regelmäßige Bereinigen zu deaktivieren.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Sie werden automatisch heruntergeladen und werden synchronisiert, wenn der Fehler behoben wurde.",
"This Device": "Dieses Gerät",
"This can easily give hackers access to read and change any files on your computer.": "Dies kann dazu führen, dass Unberechtigte relativ einfach auf Ihre Dateien zugreifen und diese ändern können.",
"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.": "Dieses Gerät kann nicht automatisch andere Geräte auffinden, oder seine eigene Adresse bekannt geben, um von anderen gefunden zu werden. Nur Geräte mit statisch konfigurierten Adressen können sich verbinden.",
"This is a major version upgrade.": "Dies ist eine Hauptversionsaktualisierung.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Diese Einstellung regelt den freien Speicherplatz, der für den Systemordner (d.h. Indexdatenbank) erforderlich ist.",
"Time": "Zeit",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Πατήστε για να δείτε τις αποτυχίες ανεύρεσης συσκευών",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Τέλος",
"Command": "Εντολή",
"Comment, when used at the start of a line": "Σχόλιο, όταν χρησιμοποιείται στην αρχή μιας γραμμής",
@@ -93,6 +94,9 @@
"Discovered": "Βάσει ανεύρεσης",
"Discovery": "Ανεύρεση συσκευών",
"Discovery Failures": "Αποτυχίες ανεύρεσης συσκευών",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Να μη γίνει επαναφορά",
"Do not restore all": "Να μη γίνει επαναφορά όλων",
"Do you want to enable watching for changes for all your folders?": "Επιθυμείτε να ενεργοποιήσετε την επιτήρηση για όλους τους φακέλους σας;",
@@ -153,6 +157,7 @@
"Help": "Βοήθεια",
"Home page": "Αρχική σελίδα",
"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": "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": "Αγνόησε",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Τακτική σάρωση ανά καθορισμένο διάστημα και απενεργοποίηση επιτήρησης αλλαγών",
"Periodic scanning at given interval and enabled watching for changes": "Τακτική σάρωση ανά καθορισμένο διάστημα και ενεργοποίηση επιτήρησης αλλαγών",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Τακτική σάρωση ανά καθορισμένο διάστημα και αποτυχία ενεργοποίησης επιτήρησης αλλαγών. Γίνεται νέα προσπάθεια κάθε 1m:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Δικαιώματα",
"Please consult the release notes before performing a major upgrade.": "Παρακαλούμε, πριν από την εκτέλεση μιας σημαντικής αναβάθμισης, να συμβουλευτείς το σημείωμα που τη συνοδεύει. ",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Παρακαλώ όρισε στις ρυθμίσεις έναν χρήστη και έναν κωδικό πρόσβασης για τη διεπαφή.",
@@ -284,6 +290,8 @@
"Sharing": "Διαμοιρασμός",
"Show ID": "Εμφάνιση ταυτότητας",
"Show QR": "Δείξε τον κωδικό QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Εμφάνιση διαφορών με προηγούμενη έκδοση",
"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.": "Θα φαίνεται αντί για την ταυτότητα της συσκευής στην προβολή της κατάστασης ολόκληρης της συστάδας. Θα ενημερώνεται αυτόματα αν αλλάξει το όνομα της συσκευής.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Τελεστής μπαλαντέρ (*) για ένα επίπεδο (χρησιμοποιείται για έναν φάκελο μόνο)",
"Size": "Μέγεθος",
"Smallest First": "Το μικρότερο πρώτα",
"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:": "Κάποια στοιχεία δεν μπόρεσαν να επαναφερθούν:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Πηγαίος κώδικας",
"Stable releases and release candidates": "Σταθερές εκδόσεις και υποψήφιες εκδόσεις.",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Οι σταθερές εκδόσεις βγαίνουν με καθυστέρηση περίπου δύο εβδομάδων. Σε αυτό το διάστημα δοκιμάζονται ως υποψήφιες εκδόσεις.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Το Syncthing έχει απενεργοποιηθεί.",
"Syncthing includes the following software or portions thereof:": "Το Syncthing περιλαμβάνει τα παρακάτω λογισμικά ή μέρη αυτών:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Το Syncthing είναι ελεύθερο λογισμικό και ανοικτού κώδικα, με άδεια 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 επανεκκινείται.",
"Syncthing is upgrading.": "Το Syncthing αναβαθμίζεται.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Το Syncthing επιτρέπει την αυτόματη υποβολή αναφορών σφαλμάτων στους προγραμματιστές. Η προεπιλεγμένη ρύθμιση είναι να αποστέλλονται οι αναφορές.",
@@ -334,6 +346,7 @@
"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 δευτερόλεπτα. Την πρώτη ημέρα, μια έκδοση κάθε μια ώρα. Τις πρώτες 30 ημέρες, μία έκδοση κάθε ημέρα. Από εκεί και έπειτα μέχρι τη μέγιστη ηλικία, θα τηρείται μια έκδοση κάθε εβδομάδα.",
"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 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.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Όταν επιλυθεί το σφάλμα θα κατεβούν και θα συχρονιστούν αυτόματα.",
"This Device": "Αυτή η συσκευή",
"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.": "Αυτή είναι μια σημαντική αναβάθμιση.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Αυτή η επιλογή καθορίζει τον ελεύθερο χώρο που θα παραμένει ελεύθερος στον δίσκο όπου βρίσκεται ο κατάλογος της εφαρμογής (και συνεπώς η βάση δεδομένων ευρετηρίων).",
"Time": "Χρόνος",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Click to see discovery failures",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Close",
"Command": "Command",
"Comment, when used at the start of a line": "Comment, when used at the start of a line",
@@ -93,6 +94,9 @@
"Discovered": "Discovered",
"Discovery": "Discovery",
"Discovery Failures": "Discovery Failures",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Do not restore",
"Do not restore all": "Do not restore all",
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
@@ -153,6 +157,7 @@
"Help": "Help",
"Home page": "Home page",
"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": "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": "Ignore",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
"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": "Permissions",
"Please consult the release notes before performing a major upgrade.": "Please consult the release notes before performing a major upgrade.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Please set a GUI Authentication User and Password in the Settings dialog.",
@@ -284,6 +290,8 @@
"Sharing": "Sharing",
"Show ID": "Show ID",
"Show QR": "Show QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Show diff with previous version",
"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 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.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
"Size": "Size",
"Smallest First": "Smallest First",
"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:": "Some items could not be restored:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Source Code",
"Stable releases and release candidates": "Stable releases and release candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing has been shut down.",
"Syncthing includes the following software or portions thereof:": "Syncthing includes the following software or portions thereof:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as 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 is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"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.",
@@ -334,6 +346,7 @@
"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 synchronised.",
"The following items were changed locally.": "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 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.",
@@ -351,6 +364,7 @@
"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": "This Device",
"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.": "This is a major version upgrade.",
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
"Time": "Time",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Click to see discovery failures",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Close",
"Command": "Command",
"Comment, when used at the start of a line": "Comment, when used at the start of a line",
@@ -93,6 +94,9 @@
"Discovered": "Discovered",
"Discovery": "Discovery",
"Discovery Failures": "Discovery Failures",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Do not restore",
"Do not restore all": "Do not restore all",
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
@@ -153,6 +157,7 @@
"Help": "Help",
"Home page": "Home page",
"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": "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": "Ignore",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
"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": "Permissions",
"Please consult the release notes before performing a major upgrade.": "Please consult the release notes before performing a major upgrade.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Please set a GUI Authentication User and Password in the Settings dialogue.",
@@ -284,6 +290,8 @@
"Sharing": "Sharing",
"Show ID": "Show ID",
"Show QR": "Show QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Show diff with previous version",
"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 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.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
"Size": "Size",
"Smallest First": "Smallest First",
"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:": "Some items could not be restored:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Source Code",
"Stable releases and release candidates": "Stable releases and release candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing has been shut down.",
"Syncthing includes the following software or portions thereof:": "Syncthing includes the following software or portions thereof:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as 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 is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"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.",
@@ -334,6 +346,7 @@
"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 synchronised.",
"The following items were changed locally.": "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 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.",
@@ -351,6 +364,7 @@
"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": "This Device",
"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.": "This is a major version upgrade.",
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
"Time": "Time",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Click to see discovery failures",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Close",
"Command": "Command",
"Comment, when used at the start of a line": "Comment, when used at the start of a line",
@@ -93,6 +94,9 @@
"Discovered": "Discovered",
"Discovery": "Discovery",
"Discovery Failures": "Discovery Failures",
"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": "Do not restore",
"Do not restore all": "Do not restore all",
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
@@ -118,6 +122,7 @@
"Error": "Error",
"External File Versioning": "External File Versioning",
"Failed Items": "Failed Items",
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
"Failed to setup, retrying": "Failed to setup, retrying",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"File Pull Order": "File Pull Order",
@@ -153,6 +158,7 @@
"Help": "Help",
"Home page": "Home page",
"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": "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": "Ignore",
@@ -174,6 +180,8 @@
"Latest Change": "Latest Change",
"Learn more": "Learn more",
"Limit": "Limit",
"Listener Failures": "Listener Failures",
"Listener Status": "Listener Status",
"Listeners": "Listeners",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
@@ -212,6 +220,7 @@
"Out of Sync": "Out of Sync",
"Out of Sync Items": "Out of Sync Items",
"Outgoing Rate Limit (KiB/s)": "Outgoing Rate Limit (KiB/s)",
"Override": "Override",
"Override Changes": "Override Changes",
"Path": "Path",
"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": "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",
@@ -225,6 +234,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
"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": "Permissions",
"Please consult the release notes before performing a major upgrade.": "Please consult the release notes before performing a major upgrade.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Please set a GUI Authentication User and Password in the Settings dialog.",
@@ -260,6 +270,7 @@
"Resume": "Resume",
"Resume All": "Resume All",
"Reused": "Reused",
"Revert": "Revert",
"Revert Local Changes": "Revert Local Changes",
"Save": "Save",
"Scan Time Remaining": "Scan Time Remaining",
@@ -284,6 +295,8 @@
"Sharing": "Sharing",
"Show ID": "Show ID",
"Show QR": "Show QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Show diff with previous version",
"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 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.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
@@ -293,7 +306,9 @@
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
"Size": "Size",
"Smallest First": "Smallest First",
"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:": "Some items could not be restored:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Source Code",
"Stable releases and release candidates": "Stable releases and release candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.",
@@ -310,6 +325,8 @@
"Syncthing has been shut down.": "Syncthing has been shut down.",
"Syncthing includes the following software or portions thereof:": "Syncthing includes the following software or portions thereof:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as 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 is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"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.",
@@ -334,6 +351,7 @@
"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.": "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 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.",
@@ -351,6 +369,7 @@
"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": "This Device",
"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.": "This is a major version upgrade.",
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
"Time": "Time",
@@ -378,6 +397,7 @@
"Uptime": "Uptime",
"Usage reporting is always enabled for candidate releases.": "Usage reporting is always enabled for candidate releases.",
"Use HTTPS for GUI": "Use HTTPS for GUI",
"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": "Version",
"Versions": "Versions",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Alklaku por vidi malsukcesajn malkovrojn",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Fermi",
"Command": "Komando",
"Comment, when used at the start of a line": "Komento, kiam uzita ĉe la komenco de lineo",
@@ -93,6 +94,9 @@
"Discovered": "Malkovrita",
"Discovery": "Malkovro",
"Discovery Failures": "Malsukcesoj de Malkovro",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Ne restarigu",
"Do not restore all": "Ne restarigu ĉion",
"Do you want to enable watching for changes for all your folders?": "Ĉu vi volas ebligi rigardado je ŝanĝoj por ĉiuj viaj dosierujoj?",
@@ -153,6 +157,7 @@
"Help": "Helpo",
"Home page": "Hejma paĝo",
"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": "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": "Ignoru",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Perioda skanado ĉe donita intervalo kaj malebligita rigardado je ŝanĝoj",
"Periodic scanning at given interval and enabled watching for changes": "Perioda skanado ĉe donita intervalo kaj ebligita rigardado je ŝanĝoj",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Perioda skanado ĉe donita intervalo kaj malsukcesis agordi rigardadon je ŝanĝoj. Provante denove ĉiuminute:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Permesoj",
"Please consult the release notes before performing a major upgrade.": "Bonvolu konsulti la notojn de eldono antaŭ elfari ĉefan ĝisdatigon.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Bonvolu agordi GUI Authentication Uzanto kaj Pasvorto en la agordoj dialogo.",
@@ -284,6 +290,8 @@
"Sharing": "Komunigo",
"Show ID": "Montru ID",
"Show QR": "Montru QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Montri diferenco kun antaŭa versio",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Montrita anstataŭ ID de Aparato en la statuso de la grupo. Estos anoncita al aliaj aparatoj kiel laŭvola defaŭlta nomo.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Montri anstataŭ ID de Aparato en la statuso de la grupo. Estos ĝisdatigita al la nomo de la aparato sciigante se ĝi estas lasita malplena.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Ununivela ĵokero (egalas nur ene de dosierujo)",
"Size": "Grandeco",
"Smallest First": "Plej Malgranda Unue",
"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:": "Iuj eroj ne povis esti restarigitaj:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Fontkodo",
"Stable releases and release candidates": "Stabilaj eldonoj kaj kandidataj eldonoj",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabilaj eldonoj prokrastas je ĉirkaŭ du semjanoj. Dum tiu tempo ili estos testataj kiel kandidataj eldonoj.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing estis malŝaltita.",
"Syncthing includes the following software or portions thereof:": "Syncthing inkluzivas la jenajn programarojn aŭ porciojn ĝiajn:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing estas libera kaj malferma fonta programaro licencita kiel 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 estas restartanta.",
"Syncthing is upgrading.": "Syncthing estas ĝisdatigita.",
"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.",
@@ -334,6 +346,7 @@
"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.": "La jenaj intervaloj estas uzataj: dum la unua horo version restas dum ĉiuj 30 sekundoj, dum la unua tago versio restas konservita dum ĉiu horo, dum la unuaj 30 tagoj versio estas konservita dum ĉiu tago, ĝis la maksimume aĝa versio restas konservita dum ĉiu semajno.",
"The following items could not be synchronized.": "La sekvantaj eroj ne povas esti sinkronigitaj.",
"The following items were changed locally.": "La sekvantaj eroj estis ŝanĝitaj loke.",
"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.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Ili estas reprovitaj aŭtomate kaj estos sinkronigitaj kiam la eraro estas solvita.",
"This Device": "Ĉi Tiu Aparato",
"This can easily give hackers access to read and change any files on your computer.": "Ĉi tio povas facile doni al kodumuloj atingon por legi kaj ŝanĝi ajnajn dosierojn en via komputilo.",
"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.": "Ĉi tio estas ĉefversio ĝisdatigita.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Ĉi tiu agordo regas la libera spaco postulita sur la hejma (t.e. indeksa datumbaza) disko.",
"Time": "Tempo",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Limpiando Versiones",
"Cleanup Interval": "Intervalo de Limpieza",
"Click to see discovery failures": "Clica para ver fallos de descubrimiento.",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Cerrar",
"Command": "Acción",
"Comment, when used at the start of a line": "Comentar, cuando se usa al comienzo de una línea",
@@ -93,6 +94,9 @@
"Discovered": "Descubierto",
"Discovery": "Descubrimiento",
"Discovery Failures": "Fallos de Descubrimiento",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "No restaurar",
"Do not restore all": "No restaurar todo",
"Do you want to enable watching for changes for all your folders?": "Quieres activar la vigilancia de cambios para todas tus carpetas?",
@@ -153,6 +157,7 @@
"Help": "Ayuda",
"Home page": "Página de inicio",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo, su configuración actual indica que puede no querer habilitarlo. Hemos deshabilitado el informe automático de errores por usted.",
"Identification": "Identification",
"If untrusted, enter encryption password": "Si no es de confianza, ingrese la contraseña de cifrado.",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si quiere impedirle a otros usuarios de esta computadora acceder a Syncthing y, a través de este, a sus archivos, considere establecer la autenticación.",
"Ignore": "Ignorar",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Escaneo periódico en un intervalo determinado y desactivada la vigilancia de cambios",
"Periodic scanning at given interval and enabled watching for changes": "Escaneo periódico en un intervalo determinado y activada la vigilancia de cambios",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Escaneo periódico en un intervalo determinado y falló la configuración de la vigilancia de cambios, se reintentará cada 1 minuto:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Permisos",
"Please consult the release notes before performing a major upgrade.": "Por favor, consultar las notas de la versión antes de realizar una actualización importante.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Por favor, introduzca un Usuario y Contraseña para la Autenticación de la Interfaz de Usuario en el panel de Ajustes.",
@@ -284,6 +290,8 @@
"Sharing": "Compartiendo",
"Show ID": "Mostrar ID",
"Show QR": "Mostrar QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Muestra las diferencias con la versión previa",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se notificará a los otros dispositivos como nombre opcional por defecto.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se actualizará al nombre que el dispositivo anuncia si se deja vacío.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Comodín de nivel único (coincide solamente dentro de un directorio)",
"Size": "Tamaño",
"Smallest First": "El más pequeño primero",
"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:": "Algunos ítems no se pudieron restaurar:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Código fuente",
"Stable releases and release candidates": "Versiones estables y versiones candidatas",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Las versiones estables son publicadas cada dos semanas. Durante este tiempo son probadas como versiones candidatas.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing se ha detenido.",
"Syncthing includes the following software or portions thereof:": "Syncthing incluye el siguiente software o partes de él:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing es Software Gratuito y Open Source Software licenciado como 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 se está reiniciando.",
"Syncthing is upgrading.": "Syncthing se está actualizando.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ahora permite el informe automático de errores a los desarrolladores. Esta característica está activada por defecto.",
@@ -334,6 +346,7 @@
"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.": "Se utilizan los siguientes intervalos: para la primera hora se mantiene una versión cada 30 segundos, para el primer día se mantiene una versión cada hora, para los primeros 30 días se mantiene una versión diaria hasta la edad máxima de una semana.",
"The following items could not be synchronized.": "Los siguientes elementos no pueden ser sincronizados.",
"The following items were changed locally.": "Los siguientes ítems fueron cambiados localmente.",
"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.": "Los siguientes elementos inesperados fueron encontrados.",
"The interval must be a positive number of seconds.": "El intervalo debe ser un número positivo de segundos.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "El intervalo, en segundos, para realizar la limpieza del directorio de versiones. Cero para desactivar la limpieza periódica.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
"This Device": "Este Dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier fichero de tu equipo.",
"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.": "Hay una actualización importante.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Este ajuste controla el espacio libre necesario en el disco principal (por ejemplo, el índice de la base de datos).",
"Time": "Hora",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Limpiando Versiones",
"Cleanup Interval": "Intervalo de Limpieza",
"Click to see discovery failures": "Clica para ver fallos de descubrimiento.",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Cerrar",
"Command": "Acción",
"Comment, when used at the start of a line": "Comentar, cuando se usa al comienzo de una línea",
@@ -93,6 +94,9 @@
"Discovered": "Descubierto",
"Discovery": "Descubrimiento",
"Discovery Failures": "Fallos de Descubrimiento",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "No restaurar",
"Do not restore all": "No restaurar todos",
"Do you want to enable watching for changes for all your folders?": "¿Deseas activar el control de cambios en todas tus carpetas?",
@@ -153,6 +157,7 @@
"Help": "Ayuda",
"Home page": "Página de inicio",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo, su configuración actual indica que puede no quererla activa. Hemos desactivado los informes automáticos de fallos por usted.",
"Identification": "Identification",
"If untrusted, enter encryption password": "Si no es de confianza, introduzca la contraseña de cifrado",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si desea evitar que otros usuarios de esta computadora accedan a Syncthing y, a través de él, a sus archivos, considere establecer la autenticación.",
"Ignore": "Ignorar",
@@ -190,8 +195,8 @@
"Maximum Age": "Edad máxima",
"Metadata Only": "Sólo metadatos",
"Minimum Free Disk Space": "Espacio mínimo libre en disco",
"Mod. Device": "Editar dispositivo",
"Mod. Time": "Editar Hora",
"Mod. Device": "Dispositivo mod.",
"Mod. Time": "Hora mod.",
"Move to top of queue": "Mover al principio de la cola",
"Multi level wildcard (matches multiple directory levels)": "Comodín multinivel (coincide con múltiples niveles de directorio)",
"Never": "Nunca",
@@ -213,7 +218,7 @@
"Out of Sync Items": "Elementos no sincronizados",
"Outgoing Rate Limit (KiB/s)": "Límite de subida (KiB/s)",
"Override Changes": "Anular cambios",
"Path": "Parche",
"Path": "Ruta",
"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": "Ruta a la carpeta en la máquina local. Se creará si no existe. El carácter de la tilde (~) puede usarse como atajo.",
"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%}.": "La ruta donde las nuevas carpetas automáticamente aceptadas será creada, así como la ruta por defecto sugerida al agregar nuevas carpetas mediante la UI. La letra (~) se expande a {{tilde}}.",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "La ruta donde las versiones deben ser almacenadas (dejar vacío para el directorio .stversions por defecto en la carpeta compartida).",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Escaneando periódicamente a un intervalo dado y detección de cambios desactivada",
"Periodic scanning at given interval and enabled watching for changes": "Escaneando periódicamente a un intervalo dado y detección de cambios activada",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Escaneando periódicamente a un intervalo dado y falló la configuración para detectar cambios, volviendo a intentarlo cada 1 m:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Permisos",
"Please consult the release notes before performing a major upgrade.": "Por favor, consultar las notas de la versión antes de realizar una actualización importante.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Por favor, introduzca un Usuario y Contraseña para la Autenticación de la Interfaz de Usuario en el panel de Ajustes.",
@@ -284,6 +290,8 @@
"Sharing": "Compartiendo",
"Show ID": "Mostrar ID",
"Show QR": "Mostrar QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Mostrar la diferencia con la versión anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se notificará a los otros dispositivos como nombre opcional por defecto.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se actualizará al nombre que el dispositivo anuncia si se deja vacío.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Comodín de nivel único (coincide solamente dentro de un directorio)",
"Size": "Tamaño",
"Smallest First": "El más pequeño primero",
"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:": "Algunos ítemes no pudieron ser restaurados:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Código fuente",
"Stable releases and release candidates": "Versiones estables y versiones candidatas",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Las versiones estables son publicadas cada dos semanas. Durante este tiempo son probadas como versiones candidatas.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing se ha detenido.",
"Syncthing includes the following software or portions thereof:": "Syncthing incluye el siguiente software o partes de él:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing es Software Libre y de Código Abierto con licencia 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 se está reiniciando.",
"Syncthing is upgrading.": "Syncthing se está actualizando.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ahora soporta el reportar automáticamente las fallas a los desarrolladores. Esta característica está habilitada por defecto.",
@@ -334,6 +346,7 @@
"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.": "Se utilizan los siguientes intervalos: para la primera hora se mantiene una versión cada 30 segundos, para el primer día se mantiene una versión cada hora, para los primeros 30 días se mantiene una versión diaria hasta la edad máxima de una semana.",
"The following items could not be synchronized.": "Los siguientes elementos no pueden ser sincronizados.",
"The following items were changed locally.": "Los siguientes elementos fueron cambiados localmente.",
"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.": "Los siguientes elementos inesperados fueron encontrados.",
"The interval must be a positive number of seconds.": "El intervalo debe ser un número de segundos positivo.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "El intervalo, en segundos, para ejecutar la limpieza del directorio de versiones. Cero para desactivar la limpieza periódica.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
"This Device": "Este Dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier fichero de tu equipo.",
"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.": "Hay una actualización importante.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Este ajuste controla el espacio libre necesario en el disco principal (por ejemplo, el índice de la base de datos).",
"Time": "Hora",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Bertsio garbiketa",
"Cleanup Interval": "Garbiketa-tartea",
"Click to see discovery failures": "Klik egin hutsegiteak ikusteko",
"Click to see full identification string and QR code.": "Egin klik identifikazio kate osoa eta QR kodea ikusteko.",
"Close": "Hetsi",
"Command": "Kontrolagailua",
"Comment, when used at the start of a line": "Komentarioa, lerro baten hastean delarik",
@@ -93,6 +94,9 @@
"Discovered": "Agertua",
"Discovery": "Agertzea",
"Discovery Failures": "Agertze hutsegiteak",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Ez berrezarri",
"Do not restore all": "Ez berrezarri denak",
"Do you want to enable watching for changes for all your folders?": "Zure karpeta guztietan aldaketen kontrola aktibatu nahi duzu?",
@@ -153,6 +157,7 @@
"Help": "Laguntza",
"Home page": "Harrera",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Hala ere, zure ezarpenen arabera baliteke aktibatu nahi ez izatea. Txostenen bidalketa automatikoa desaktibatu dugu zuretzat.",
"Identification": "Identifikazioa",
"If untrusted, enter encryption password": "Fidagarria ez bada, idatzi enkriptatzeko pasahitza",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Ordenagailu honetako beste erabiltzaile batzuk Syncthing-era eta haren bidez zure fitxategietara sartzea eragotzi nahi baduzu, kontuan izan autentifikazioa konfiguratu behar duzula.",
"Ignore": "Kontuan ez hartu",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Emandako tartearen aldizkako miaketa, eta desaktibatuta aldaketak ikusteko",
"Periodic scanning at given interval and enabled watching for changes": "Emandako tartearen aldizkako miaketa, eta aktibatuta aldaketak ikusteko",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Emandako tartearen aldizkako miaketa, eta huts egin du aldaketak ikusteko ezarpenak, berriro saiatuz minuturo:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Baaimenak",
"Please consult the release notes before performing a major upgrade.": "Aktualizatze garrantzitsu bat egin baino lehen, bertsioaren oharrak begira itzazu.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Konfigurazio leihoan asma itzazu erabiltzale izen bat eta pasahitz bat",
@@ -284,6 +290,8 @@
"Sharing": "Partekatuz",
"Show ID": "Erakutsi ene ID-a",
"Show QR": "Erakutsi QR-a",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Erakutsi aurreko bertsioarekiko aldeak",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Tresnaren ID-aren ordez erakutsia, taldearen egoeran. Beste tresneri erakutsia izanen da, izen erabilgarri bat bezala",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Tresnaren ID-aren ordez erakutsia, taldearen egoeran. Hutsa utzia balin bada, urrun den tresnak proposatu izenarekin aktualizatua izanen da",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Hein bakar bateko jokerra (karpetaren barnean bakarrik dagokiona)",
"Size": "Tamaina",
"Smallest First": "Ttipienak lehenik",
"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:": "Zenbat fitxategi ezin izan dira berreskuratu",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Iturri kodea",
"Stable releases and release candidates": "iraunkor eta aintzin-bertsioak",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Iraunkor bertsioak bi astez (nonbait han) gibelatuak dira. Bitartean, aintzin-bertsio gisa probatuak izanen dira.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing gelditua izan da",
"Syncthing includes the following software or portions thereof:": "Syncthing-ek programa hauk integratzen ditu (edo programa hauetatik datozten elementuak):",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing software librea da eta kode irekikoa, MPL v2.0 lizentziarekin.",
"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 berriz pizten ari",
"Syncthing is upgrading.": "Syncthing aktualizatzen ari da",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing-en bidez, automatikoki bidal diezazkiekezu garatzaileei blokeo-txostenak. Funtzio hau berez aktibatuta dago.",
@@ -334,6 +346,7 @@
"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.": "Hunako tarteak erabiliak dira: lehen orduan bertsio bat kontserbatua da 30 segundu guziz. Lehen egunean, bertsio bat ordu bakoitz, lehen 30 egunetan bertsio bat egunero. Handik harat, adinaren mugetan egonez, bertsio bat astero.",
"The following items could not be synchronized.": "Ondoko fitxero hauk ez dira sinkronizatuak ahal izan",
"The following items were changed locally.": "Elementu hauek tokiz aldatu dira.",
"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.": "Ustekabeko elementu hauek aurkitu dira.",
"The interval must be a positive number of seconds.": "Tarteak segundo kopuru positiboa izan behar du.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Bertsioen direktorioko garbiketa exekutatzeko tartea, segundotan. Zero aldizkako garbiketa desgaitzeko.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Errorea zuzendua izanen delarik, automatikoki berriz entseatuak et sinkronizatuak izanen dira",
"This Device": "Tresna hau",
"This can easily give hackers access to read and change any files on your computer.": "Hunek errexki irakurtzen eta aldatzen uzten ahal du zure ordenagailuko edozein fitxero, nahiz eta sartu denak ez haizu izan!",
"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.": "Aktualizatze garrantzitsu bat da",
"This setting controls the free space required on the home (i.e., index database) disk.": "Behar den espazio kontrolatzen du egokitze honek, zure erabiltzale partekatzea geritzatzen duen diskoan (hori da, indexazio datu-basean)",
"Time": "Ordua",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Klikkaa nähdäksesi etsinnässä tapahtuneet virheet",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Sulje",
"Command": "Komento",
"Comment, when used at the start of a line": "Kommentti, käytettäessä rivin alussa",
@@ -93,6 +94,9 @@
"Discovered": "Löydetty",
"Discovery": "Etsintä",
"Discovery Failures": "Etsinnässä tapahtuneet virheet",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Älä palauta",
"Do not restore all": "Älä palauta kaikkia",
"Do you want to enable watching for changes for all your folders?": "Haluatko aktivoida jatkuvan muutoksien seurannan kaikkiin kansioihin?",
@@ -153,6 +157,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",
"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",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Ajoitettu skannaus päällä. Muutosten seuranta pois päältä",
"Periodic scanning at given interval and enabled watching for changes": "Ajoitettu skannaus ja muutosten seuranta päällä",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Ajoitettu skannaus päällä. Muutosten seurannan käyttöönotto epäonnistui, yritetään uudelleen minuutin välein:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Oikeudet",
"Please consult the release notes before performing a major upgrade.": "Tutustu julkaisutietoihin ennen kuin teet pääversion päivityksen.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ole hyvä ja aseta käyttäjätunnus ja salasana käyttöliittymää varten asetusvalikossa.",
@@ -284,6 +290,8 @@
"Sharing": "Jakaminen",
"Show ID": "Näytä ID",
"Show QR": "Näytä QR-koodi",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Näytä muutokset edelliseen versioon",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Näytetään ryhmän tiedoissa laitteen ID:n sijaan. Ilmoitetaan muille laitteille vaihtoehtoisena oletusnimenä.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Näytetään ryhmän tiedoissa laitteen ID:n sijaan. Tyhjä nimi päivitetään laitteen ilmoittamaksi nimeksi.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Yksitasoinen jokerimerkki (vaikuttaa vain kyseisen kansion sisällä)",
"Size": "Koko",
"Smallest First": "Pienin ensin",
"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:": "Joitakin tiedostoja ei voitu palauttaa:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Lähdekoodi",
"Stable releases and release candidates": "Vakaat julkaisut ja julkaisuehdokkaat",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Julkaisuversiot on viivästetty kaksi viikkoa, jonka aikana ne käyvät testauksen lävitse, kuten RC-versiot.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing on sammutettu.",
"Syncthing includes the following software or portions thereof:": "Syncthing sisältää seuraavat ohjelmistot tai sen osat:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing on avointa lähdekoodia, joka on lisensöity MPL v2.0 lisenssillä.",
"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 käynnistyy uudelleen.",
"Syncthing is upgrading.": "Syncthing päivittyy.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Synthing tukee automaattista kaatumisraportointia. Tämä ominaisuus on oletuksena käytössä.",
@@ -334,6 +346,7 @@
"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.": "Seuraavat aikavälit ovat käytössä: ensimmäisen tunnin ajalta uusi versio säilytetään joka 30 sekunti, ensimmäisen päivän ajalta uusi versio säilytetään tunneittain ja ensimmäisen 30 päivän aikana uusi versio säilytetään päivittäin. Lopulta uusi versio säilytetään viikoittain, kunnes maksimi-ikä saavutetaan.",
"The following items could not be synchronized.": "Seuraavia nimikkeitä ei voitu synkronoida.",
"The following items were changed locally.": "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 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.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Niiden synkronointia yritetään uudelleen automaattisesti.",
"This Device": "Tämä laite",
"This can easily give hackers access to read and change any files on your computer.": "Tämä voi helposti sallia vihamielisille tahoille pääsyn lukea ja muokata kaikkia tiedostojasi",
"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.": "Tämä on pääversion päivitys.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Tämä asetus määrittää vaaditun vapaan levytilan kotikansiossa (se missä index-tietokanta on).",
"Time": "Aika",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Purge des versions",
"Cleanup Interval": "Intervalle de purge",
"Click to see discovery failures": "Voir les échecs de découverte",
"Click to see full identification string and QR code.": "Voir l'identifiant complet et le code QR correspondant.",
"Close": "Fermer",
"Command": "Commande",
"Comment, when used at the start of a line": "Commentaire lorsque utilisé en début de ligne",
@@ -62,7 +63,7 @@
"Currently Shared With Devices": "Appareils membres actuels de ce partage :",
"Danger!": "Attention !",
"Debugging Facilities": "Outils de débogage",
"Default Configuration": "Personnalisation des créations (non rétroactif)",
"Default Configuration": "Préférences pour les créations (non rétroactif)",
"Default Device": "Nouveaux appareils",
"Default Folder": "Nouveaux partages",
"Default Folder Path": "Chemin parent par défaut pour les nouveaux partages",
@@ -93,6 +94,9 @@
"Discovered": "Découvert",
"Discovery": "Découverte",
"Discovery Failures": "Échecs de découverte",
"Dismiss": "Écarter",
"Do not add it to the ignore list, so this notification may recur.": "Ne pas l'ajouter à la liste à ignorer, pour que cette notification puisse réapparaître.",
"Do not add it to the ignore list, so this notification may recurr.": "Ne pas l'ajouter à la liste à ignorer, pour que cette notification puisse réapparaître.",
"Do not restore": "Ne pas restaurer",
"Do not restore all": "Ne pas tout restaurer",
"Do you want to enable watching for changes for all your folders?": "Voulez-vous activer la surveillance des changements sur tous vos partages ?",
@@ -153,6 +157,7 @@
"Help": "Aide (en anglais)",
"Home page": "Page d'accueil",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Cependant, vos réglages indiquent que vous pourriez souhaiter ne pas l'activer. Nous avons désactivé pour vous l'envoi automatique des rapports.",
"Identification": "Identifiant abrégé",
"If untrusted, enter encryption password": "Entrez un mot de passe pour chiffrer",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si vous voulez empêcher d'autres utilisateurs de cet appareil à accéder à Syncthing, et via Syncthing à vos fichiers, prenez quelques secondes pour régler l'authentification. ",
"Ignore": "Refuser",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Analyse périodique à intervalle défini et surveillance des changements désactivée.",
"Periodic scanning at given interval and enabled watching for changes": "Analyse périodique à intervalle défini et surveillance des changements activée.",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Analyse périodique à intervalle défini et échec d'activation de la surveillance des changements. Nouvel essai toutes les 1mn :",
"Permanently add it to the ignore list, suppressing further notifications.": "L'ajouter à la liste des ignorés pour éviter des notifications ultérieures.",
"Permissions": "Permissions",
"Please consult the release notes before performing a major upgrade.": "Veuillez consulter les notes de version avant de réaliser une mise à jour majeure.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Veuillez définir un nom d'utilisateur et un mot de passe dans la fenêtre de Configuration.",
@@ -284,6 +290,8 @@
"Sharing": "Partages",
"Show ID": "Afficher mon ID",
"Show QR": "Afficher le QR",
"Show detailed discovery status": "Afficher l'état détaillé de découverte",
"Show detailed listener status": "Afficher l'état détaillé de l'écouteur",
"Show diff with previous version": "Afficher les différences avec la version précédente",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Sera diffusé aux autres appareils comme nom convivial optionnel par défaut.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Nom convivial local affiché à la place de l'ID de l'appareil dans la plupart des écrans. Si laissé vide, c'est le nom convivial local de l'appareil distant qui sera utilisé. (Modifiable ultérieurement).",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "N'importe quel nombre, dont 0, de n'importe quels caractères (sauf le séparateur de répertoires).",
"Size": "Taille",
"Smallest First": "Les plus petits en premier",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Certaines méthodes de découverte n'ont pas pu être établies pour trouver d'autres appareils ou annoncer celui-ci :",
"Some items could not be restored:": "Certains éléments n'ont pas pu être restaurés :",
"Some listening addresses could not be enabled to accept connections:": "Certaines adresses d'écoute n'ont pas pu être activées pour accepter des connexions :",
"Source Code": "Code source",
"Stable releases and release candidates": "Versions stables et préliminaires",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Les versions stables sont reportées d'environ deux semaines. Pendant ce temps elles sont testées en tant que versions préliminaires.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing a été arrêté.",
"Syncthing includes the following software or portions thereof:": "Syncthing intègre les logiciels suivants (ou des éléments provenant de ces logiciels) :",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing est un logiciel Libre et Open Source sous licence MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing écoute sur les adresses réseau suivantes les tentatives de connexions des autres appareils :",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing n'écoute les tentatives de connexion des autres appareils sur aucune adresse. Seules les connexions sortantes de cet appareil peuvent fonctionner.",
"Syncthing is restarting.": "Syncthing redémarre.",
"Syncthing is upgrading.": "Syncthing se met à jour.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing permet maintenant d'envoyer automatiquement aux développeurs des rapports de plantage. Cette fonctionnalité est activée par défaut.",
@@ -334,6 +346,7 @@
"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.": "Les seuils de durée suivants définissent le nombre maximum de versions pour chaque fichier : pendant la première heure une version est conservée toutes les 30 secondes. Le premier jour, une version par heure - des versions de la première heure sont alors progressivement effacées pour finir par n'en garder que la dernière. Pour les 30 jours passés, une version par jour - des versions horaires du premier jour sont alors progressivement effacées pour n'en garder qu'une par jour. Au-delà et jusqu'à la limite d'âge, une version est conservée par semaine - des versions journalières du premier mois sont alors progressivement effacées pour n'en garder qu'une.",
"The following items could not be synchronized.": "Les fichiers suivants n'ont pas pu être synchronisés.",
"The following items were changed locally.": "Les éléments suivants ont été modifiés localement.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Les méthodes suivantes de découverte des autres appareils et d'annonce de cet appareil sont utilisées :",
"The following unexpected items were found.": "Les éléments inattendus suivants ont été détectés.",
"The interval must be a positive number of seconds.": "L'intervalle doit être un nombre positif exprimé en secondes",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "L'intervalle, en secondes, de l'exécution du nettoyage du répertoire des versions. Définir à 0 pour désactiver la purge périodique (Dans ce cas, elle n'est effectuée qu'au démarrage).",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Ils seront automatiquement retentés et synchronisés quand l'erreur sera résolue.",
"This Device": "Cet appareil",
"This can easily give hackers access to read and change any files on your computer.": "Ceci peut aisément permettre à un intrus de lire et modifier n'importe quel fichier de votre ordinateur.",
"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.": "Cet appareil ne peut ni découvrir automatiquement les autres, ni annoncer sa propre présence pour être découvert pas les autres. Seuls les appareils configurés avec des connexions statiques peuvent se connecter.",
"This is a major version upgrade.": "Il s'agit d'une mise à jour majeure.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Ce réglage contrôle l'espace disque requis dans le disque qui abrite votre répertoire utilisateur (pour la base de données d'indexation).",
"Time": "Heure",

View File

@@ -18,15 +18,15 @@
"Advanced": "Avansearre",
"Advanced Configuration": "Avansearre konfiguraasje",
"All Data": "Alle data",
"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.",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Alle mappen dy't mei dit apparaat dielt binne, moatte wurde beskerme mei in wachtwurd, sadat alle ferstjoerde data net lêsber is sûnder it opjûne wachtwurd .",
"Allow Anonymous Usage Reporting?": "Anonime brûkensrapportaazje tastean?",
"Allowed Networks": "Tasteane Netwurken",
"Alphabetic": "Alfabetysk",
"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.": "In ekstern kommando soarget foar it ferzjebehear. It moat de triem út de dielde map fuortsmite. As it paad nei de applikaasje romtes hat, moat it tusken oanheltekens sette wurden.",
"Anonymous Usage Reporting": "Anonym brûkensrapportaazje",
"Anonymous usage report format has changed. Would you like to move to the new format?": "It formaat fan de rapportaazje fan anonime gebrûksynformaasje is feroare. Wolle jo op dit nije formaat oerstappe?",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"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 continue?": "Binne jo der wis fan dat jo trochgean wolle?",
"Are you sure you want to permanently delete all these files?": "Binne jo der wis fan dat jo al dizze bestannen permanint wiskje wolle?",
"Are you sure you want to remove device {%name%}?": "Bist der wis fan datsto apparaat {{name}} fuortsmite wolst?",
"Are you sure you want to remove folder {%label%}?": "Bist der wis fan datsto map {{label}} fuortsmite wolst?",
"Are you sure you want to restore {%count%} files?": "Bist der wis fan datsto {{count}} triemen weromsette wolst?",
@@ -42,9 +42,10 @@
"Bugs": "Brekkings",
"Changelog": "Feroaringslochboek",
"Clean out after": "Opromje nei",
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Cleaning Versions": "Reinigingsferzjes",
"Cleanup Interval": "Reinigingsynterfal",
"Click to see discovery failures": "Klik om ûntdekkingsflaters te besjen",
"Click to see full identification string and QR code.": "Klikje om folsleine identifikaasje-string en QR-koade te sjen",
"Close": "Slute",
"Command": "Kommando",
"Comment, when used at the start of a line": "Kommentaar, wannear as brûkt by it begjin fan in rige",
@@ -62,22 +63,22 @@
"Currently Shared With Devices": "Op dit stuit Dielt mei Apparaten",
"Danger!": "Gefaar!",
"Debugging Facilities": "Debug-foarsjennings",
"Default Configuration": "Default Configuration",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Configuration": "Standertkonfiguraasje",
"Default Device": "Standertapparaat",
"Default Folder": "Standertmap",
"Default Folder Path": "Standert Map-paad",
"Defaults": "Defaults",
"Delete Unexpected Items": "Delete Unexpected Items",
"Defaults": "Standertwearden",
"Delete Unexpected Items": " Unferwachte items wiskje",
"Deleted": "Fuortsmiten",
"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.": "Deselect folders to stop sharing with this device.",
"Deselect folders to stop sharing with this device.": "Deselektearje mappen om it dielen mei dit apparaat te stopjen.",
"Device": "Apparaat",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Apparaat \"{{name}}\" {{device}} op ({{address}}) wol ferbining meitsje. Nij apparaat taheakje?",
"Device ID": "Apparaat-ID",
"Device Identification": "Apparaatidentifikaasje",
"Device Name": "Apparaatnamme",
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
"Device is untrusted, enter encryption password": "Apparaat is net fertroud, fier fersiferingswachtwurd yn",
"Device rate limits": "Apparaatfluggenslimiet",
"Device that last modified the item": "Apparaat dat dit item it lêst oanpast hat",
"Devices": "Apparaten",
@@ -86,13 +87,16 @@
"Disabled periodic scanning and disabled watching for changes": "Periodic scanning útskeakele en feroarings wurde net mear yn'e gaten hâlden.",
"Disabled periodic scanning and enabled watching for changes": "Periodic scanning útskeakele en feroarings wurde yn'e gaten hâlden.",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Periodic scanning útskeakele en it ynskeakeljen fan it yn'e gaten hâlden fan feroarings is mislearre, wurd eltse 1m opnij besocht:",
"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).",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Skakelt fergelykjen en syngronisearjen fan bestânrjochten út. Nuttich op systemen mei net-besteande as oanpaste tagongsrjochten (bgl. FAT, exFAT, Synology, Android).",
"Discard": "Fuortsmite\n",
"Disconnected": "Ferbining ferbrutsen",
"Disconnected (Unused)": "Ferbining ferbrutsen (Net Brûkt)",
"Discovered": "Untdekt",
"Discovery": "Untdekking",
"Discovery Failures": "Untdekkingsflaters",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Net tebeksette",
"Do not restore all": "Hielendal net tebeksette",
"Do you want to enable watching for changes for all your folders?": "Wolle jo it konstant byhâlden fan feroarings foar al jo mappen oansette?",
@@ -102,9 +106,9 @@
"Downloading": "Oan it downloaden",
"Edit": "Bewurkje",
"Edit Device": "Apparaat Bewurkje",
"Edit Device Defaults": "Edit Device Defaults",
"Edit Device Defaults": "Bewurkje standertwearden foar Apparaat",
"Edit Folder": "Map Bewurkje",
"Edit Folder Defaults": "Edit Folder Defaults",
"Edit Folder Defaults": "Bewurkje standertwearden foar Map",
"Editing {%path%}.": "{{path}} wurd bewurke.",
"Enable Crash Reporting": "Automatyske Rapportaazje fan Fêstrinners Oansette",
"Enable NAT traversal": "NAT-trochkruse ynskeakelje",
@@ -134,15 +138,15 @@
"Folder Label": "Map-opskrift",
"Folder Path": "Map-paad",
"Folder Type": "Maptype",
"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.",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Maptype \"{{receiveEncrypted}}\" kin allinich ynsteld wurde as jo in nije map tafoegje.",
"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.": "Maptype \"{{receiveEncrypted}}\" kin net wizige wurde nei it tafoegjen fan de map. Jo moatte de map fuortsmite, de gegevens op 'e skiif wiskje of ûntsiferje, en de map opnij tafoegje.",
"Folders": "Mappen",
"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.": "By it starten fan it sjen foar feroarings is der foar de folgende mappen is in flater ûnstean. It wurd eltse minuut opnij besike, dus de flaters koene wol ris gau fuortgean. At se der bliuwe, besykje dan it ûnderlizzende probleem te ferhelpen en freegje om help dat dit net slagget.",
"Full Rescan Interval (s)": "Folledich wersken ynterfal (s)",
"GUI": "GUI",
"GUI Authentication Password": "Wachtwurd foar ferifikaasje yn GUI",
"GUI Authentication User": "Brûkers-ID foar ferifikaasje yn GUI",
"GUI Authentication: Set User and Password": "GUI Authentication: Set User and Password",
"GUI Authentication: Set User and Password": "GUI-ferifikaasje: Brûker en wachtwurd ynstelle",
"GUI Listen Address": "GUI-harkadres",
"GUI Theme": "Ynterfaasjetema",
"General": "Algemien",
@@ -153,8 +157,9 @@
"Help": "Help",
"Home page": "Hiemstee",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Lykwols, jo aktuele ynstellings litte sjen dat jo it miskien net oan sette wol. Wy hawwe automatysk rapportearjen fan fêstrinnen foar jo útsetten.",
"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.",
"Identification": "Identifikaasje",
"If untrusted, enter encryption password": "As net fertroud, fier dan fersiferingswachtwurd yn",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "As jo wolle foarkomme dat oare brûkers op dizze kompjûter tagong krije ta Syncthing en dêrtroch jo bestannen, oerweeg dan it ynstellen fan ferifikaasje.",
"Ignore": "Negearje",
"Ignore Patterns": "Negear-patroanen",
"Ignore Permissions": "Negear-rjochten",
@@ -202,7 +207,7 @@
"No File Versioning": "Gjin triemferzjebehear",
"No files will be deleted as a result of this operation.": "Der wurde gjin triemen fuortsmiten as gefolch fan dizze aksje.",
"No upgrades": "Gjin fernijings",
"Not shared": "Not shared",
"Not shared": "Net dielt",
"Notice": "Notysje",
"OK": "Okee",
"Off": "Ut",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning op opjûn ynterfal en feroarings wurde net yn'e gaten hâlden.",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning op opjûn ynterfal en feroarings wurde yn'e gaten hâlden.",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodic scanning op opjûn ynterfal en it ynskeakeljen fan it yn'e gaten hâlden fan feroarings is mislearre, wurd eltse 1m opnij besocht:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Rjochten",
"Please consult the release notes before performing a major upgrade.": "Foardat jo in wichtige fernijing ynstallearre, graach earst de fernijingsoantekenings lêze.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Graach foar GUI-ferifikaasje in brûkers-ID en wachtwurd ynstelle yn it ynstellingsdialooch.",
@@ -236,15 +242,15 @@
"Preview Usage Report": "Foarbyld fan brûkensrapport ",
"Quick guide to supported patterns": "Fluch-paadwizer foar stipe patroanen",
"Random": "Willekeurich",
"Receive Encrypted": "Receive Encrypted",
"Receive Encrypted": "Untfange fersifere",
"Receive Only": "Allinnich Untfange",
"Received data is already encrypted": "Received data is already encrypted",
"Received data is already encrypted": "Untfongen gegevens binne al fersifere",
"Recent Changes": "Resinte Feroarings",
"Reduced by ignore patterns": "Ferlytse troch negear-patroanen",
"Release Notes": "Utjeftenotysjes",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Ferzje kandidaten hawwe de lêste mooglikheden en ferbetterings. Se binne allyksa de tradisjonele twa-wyklikse Syncthing ferzjes.",
"Remote Devices": "Apparaten op Ofstân",
"Remote GUI": "Remote GUI",
"Remote GUI": "GUI op ôfstân",
"Remove": "Fuortsmite",
"Remove Device": "Apparaat Fuortsmite",
"Remove Folder": "Map Fourtsmite",
@@ -268,7 +274,7 @@
"Select All": "Alles Selektearje",
"Select a version": "Kies in ferzje",
"Select additional devices to share this folder with.": "Doch apparaten derby om dizze map mei te dielen.",
"Select additional folders to share with this device.": "Select additional folders to share with this device.",
"Select additional folders to share with this device.": "Selektearje mear mappen om te dielen mei dit apparaat.",
"Select latest version": "Selektearje de nijste ferzje",
"Select oldest version": "Selektearje de âldste ferzje",
"Select the folders to share with this device.": "Sykje de mappen út om mei dit apparaat te dielen.",
@@ -279,11 +285,13 @@
"Share Folder": "Map diele",
"Share Folders With Device": "Map diele mei apparaat",
"Share this folder?": "Dizze map diele?",
"Shared Folders": "Shared Folders",
"Shared Folders": "Dielde Mappen",
"Shared With": "Dielt mei",
"Sharing": "Dielen",
"Show ID": "ID sjen litte",
"Show QR": "QR sjen litte",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Ferskil (diff) mei de foarige ferzje sjen litte",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Wurd ynstee fan apparaat-ID sjen litten by de bondeltastân. Wurd nei oare apparaten advertearre as in mooglike standertnamme.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Wurd yn de bondeltastân sjen litten ynstee fan apparaat-ID. Wannear't leech litten wurd, wurd it fernijt nei de namme die it apparaat útstjoert.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Inkel-nivo jokerteken (wildcard) (fergeliket allinnich binnen in map)",
"Size": "Grutte",
"Smallest First": "Lytste earst",
"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:": "Guon uûnderdielen koenen net tebeksetten wurde.",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Boarnekoade",
"Stable releases and release candidates": "Stabyle ferzjes en ferzje kanditaten",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabyle ferzjes wurde likernôch twa wiken útstelt. Yn die tiid wurde se testen as ferzje kandidaten.",
@@ -302,7 +312,7 @@
"Start Browser": "Browser iepenje wannear't Syncthing start",
"Statistics": "Statistiken",
"Stopped": "Stoppe",
"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.",
"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.": "Bewarret en syngroniseart allinich fersifere gegevens. Mappen op alle oansletten apparaten moatte mei itselde wachtwurd ynsteld hawwe of ek fan it type \"{{receiveEncrypted}}\" wêze.",
"Support": "Help (Forum)",
"Support Bundle": "Helpbundel",
"Sync Protocol Listen Addresses": "Sync-protokolharkadressen",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing is útsetten",
"Syncthing includes the following software or portions thereof:": "Syncthing befettet de folgende sêftguod of parten dêrfan:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Fergees en Iepenboarne Programmatuer mei in MPL V2.0 lisinsje.",
"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 oan it werstarten.",
"Syncthing is upgrading.": "Syncthing is oan it fernijen.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Synthing stipet no it automatysk rapportearjen fan fêstrinners nei de ûntwikkelders. Dizze eigenskip stiet standert út.",
@@ -317,10 +329,10 @@
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "It liket dêrop dat Syncthing swierrichheden ûnderfynt mei it ferwurkjen fan jo fersyk. Graach de stee ferfarskje of Syncthing werstarte as it probleem der bliuwt.",
"Take me back": "Bring my werom",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "It ynterfaasje-adres waard oerskreaun troch opstart-opsjes. Feroarings wurde hjir net ynstelt wylst dizze oerskriuw-ynstelling aktyf is.",
"The Syncthing Authors": "The Syncthing Authors",
"The Syncthing Authors": "De Makkers fan Syncthing",
"The Syncthing admin interface is configured to allow remote access without a password.": "De Syncthing haadbrûker-ynterfaasje is sa ynstelt dat tagong fan ôfstân sûnder wachtwurd tastean is.",
"The aggregated statistics are publicly available at the URL below.": "De fersammele statistiken binnen yn it publyk beskikber fia ûndersteande keppeling.",
"The cleanup interval cannot be blank.": "The cleanup interval cannot be blank.",
"The cleanup interval cannot be blank.": "It ynterfal foar opromjen kin net leech wêze.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "De konfiguraasje is bewarre mar noch net aktivearre. Syncthing moat werstarte om de nije konfiguraasje te aktivearren.",
"The device ID cannot be blank.": "It apparaat-ID kin net leech wêze.",
"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).": "It apparaat-ID dat hjir ynfierd wurde kin, kin fûn wurde yn in it \"Askjes > ID sjen litte\" dialooch op de oare apparaten. Spaasjes en streepkes binne mooglik (wurde negeard).",
@@ -328,15 +340,16 @@
"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.": "Dit ynfierde apparaat-ID liket ûnjildich. It moat in tekenrige (string) wêze mei in lingte fan 52 of 56 karakters besteande út letters en nûmers, mei spaasjes en streepkes mooglik.",
"The folder ID cannot be blank.": "It map-ID mei net leech wêze.",
"The folder ID must be unique.": "It map-ID moat unyk wêze.",
"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 content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "De mapynhâld op oare apparaten sil oerskreaun wurde om identyk te wurden mei dit apparaat. Bestannen dy't hjir net oanwêzich binne, sille wiske wurde op oare apparaten.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "De mapynhâld op dit apparaat wurdt oerskreaun om identyk te wurden mei oare apparaten. Bestannen dy't hjir nij binne tafoege, sille wiske wurde.",
"The folder path cannot be blank.": "It map-paad mei net leech wêze.",
"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.": "De folgende yntervals wurd brûkt: foar it earste oere wurd eltse 30 sekonden in ferzje bewarre, foar de earste dei wurd eltse oere in ferzje bewarre, foar de earste 30 dagen wurd eltse dei in ferzje bewarre, oant ta de maksimale âldens wurd eltse wike in ferzje bewarre.",
"The following items could not be synchronized.": "De folgende items koene net syngronisearre wurde.",
"The following items were changed locally.": "De neikommende items binne lokaal feroare.",
"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 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.": "De folgjende ûnferwachte items waarden fûn.",
"The interval must be a positive number of seconds.": "It ynterfal moat in posityf oantal sekonden wêze.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "It ynterfal, yn sekonden, foar skjinmeitsjen yn 'e ferzjesmap. Nul om periodyk skjin te meitsjen.",
"The maximum age must be a number and cannot be blank.": "De maksimale âldens moat in nûmer en net leech wêze.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "De maksimale tiid dat in ferzje bewarre wurde moat (yn dagen, stel yn op 0 om ferzjes foar immer te bewarjen).",
"The number of days must be a number and cannot be blank.": "It tal fan dagen moat in nûmer wêze en mei net leech wêze.",
@@ -347,10 +360,11 @@
"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 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 folders to share with this device.": "There are no folders to share with this device.",
"There are no folders to share with this device.": "D'r binne gjin mappen te dielen mei dit apparaat.",
"They are retried automatically and will be synced when the error is resolved.": "Sy wurde automatysk opnij probearre en sille syngronisearre wurde wannear at de flater oplost is.",
"This Device": "Dit Apparaat",
"This can easily give hackers access to read and change any files on your computer.": "Dit kin samar ynkringers (hackers) tagong jaan om elke triem op jo kompjûter te besjen en te feroarjen.",
"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.": "Dit is in wichtige ferzjefernijing.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Dizze ynstelling bepaalt de frije romte dy't noadich is op de home-skiif (fan de yndeks-databank).",
"Time": "Tiid",
@@ -361,14 +375,14 @@
"Unavailable": "Net beskikber",
"Unavailable/Disabled by administrator or maintainer": "Net beskikber/Utsetten troch administrator of ûnderhâlder",
"Undecided (will prompt)": "Noch net beslist (wurd noch frege)",
"Unexpected Items": "Unexpected Items",
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
"Unexpected Items": "Unferwachte items",
"Unexpected items have been found in this folder.": "Unferwachte items binne yn dizze map fûn.",
"Unignore": "Net mear negeare",
"Unknown": "Unbekend",
"Unshared": "Net dielt",
"Unshared Devices": "Net-dielde Apparaten",
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Unshared Folders": "Net-dielde mappen",
"Untrusted": "Net betroud",
"Up to Date": "By de tiid",
"Updated": "Fernijt",
"Upgrade": "Fernije",
@@ -378,15 +392,15 @@
"Uptime": "Rintiid",
"Usage reporting is always enabled for candidate releases.": "Brûkersrapportaazje stiet altyd oan foar ferzje kandidaten.",
"Use HTTPS for GUI": "Brûk HTTPS foar GUI",
"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.",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Brûkersnamme / wachtwurd is net ynsteld foar de GUI-ferifikaasje. Tink der asjebleaft oer nei om dit yn te stellen.",
"Version": "Ferzje",
"Versions": "Ferzjes",
"Versions Path": "Ferzjes-paad",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Ferzjes wurde automatysk fuortsmiten wannear't se âlder binne dan de maksimale âldens of wannear it tal fan triemen yn in ynterval grutter is dan tastean.",
"Waiting to Clean": "Waiting to Clean",
"Waiting to Clean": "Wachtsje om skjin te meitsjen",
"Waiting to Scan": "Wachtet om te Skennen",
"Waiting to Sync": "Wachten om te Synchronisearren",
"Warning": "Warning",
"Warning": "Warskôging",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Warskôging, dit paad is in boppelizzende triemtafel fan in besteande map \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warskôging, dit paad is in boppelizzende triemtafel fan in besteande map \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warskôging, dit paad is in ûnderlizzende triemtafel fan in besteande map \"{{otherFolder}}\".",
@@ -405,14 +419,14 @@
"You have no ignored folders.": "Jo hawwe gjin negearde mappen.",
"You have unsaved changes. Do you really want to discard them?": "Jo hawwe noch net-opsleine feroarings. Wolle jo dizze echt fuortsmite?",
"You must keep at least one version.": "Jo moatte minstens ien ferzje bewarje.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Jo moatte nea lokaal wat tafoegje of feroarje yn in map \"{{receiveEncrypted}}\".",
"days": "dagen",
"directories": "triemtafels",
"files": "triemmen",
"full documentation": "komplete dokumintaasje",
"items": "items",
"seconds": "seconds",
"seconds": "sekonden",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wol map \"{{folder}}\" diele.",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wol map \"{{folderlabel}}\" ({{folder}}) diele.",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kin dit apparaat opnij yntrodusearje."
}

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Tisztító verziók",
"Cleanup Interval": "Tisztítási intervallum",
"Click to see discovery failures": "Kattints ide a felfedezési hibák megtekintéséhez",
"Click to see full identification string and QR code.": "Kattintás a teljes azonosító és a QR-kód megjelenítéséhez.",
"Close": "Bezárás",
"Command": "Parancs",
"Comment, when used at the start of a line": "Megjegyzés, a sor elején használva",
@@ -86,13 +87,16 @@
"Disabled periodic scanning and disabled watching for changes": "A periodikus átnézés és a változások keresése letiltva",
"Disabled periodic scanning and enabled watching for changes": "A periodikus átnézés letiltva a változások keresése engedélyezve",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "A periodikus átnézés letiltva és a változások keresésének beállítása sikertelen, 1 percenként újrapróbálkozás:",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Letiltja a fájljogosultságok összehasonlítását és szinkronizálást. Hasznos olyan rendszerek esetén, ahol nincsenek jogosultságok vagy egyediek vannak (pl. FAT, exFAT, Sonology, Android).",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Letiltja a fájljogosultságok összehasonlítását és szinkronizálást. Hasznos olyan rendszerek esetén, ahol nincsenek jogosultságok vagy egyediek vannak (pl. FAT, exFAT, Synology, Android).",
"Discard": "Elvetés",
"Disconnected": "Kapcsolat bontva",
"Disconnected (Unused)": "Kapcsolat bontva (használaton kívül)",
"Discovered": "Felfedezett",
"Discovery": "Felfedezés",
"Discovery Failures": "Felfedezési hibák",
"Dismiss": "Elutasítás",
"Do not add it to the ignore list, so this notification may recur.": "Ne vegye fel a mellőzési listára, így ez az értesítés megismétlődhet. ",
"Do not add it to the ignore list, so this notification may recurr.": "Ne vegye fel a mellőzési listára, így ez az értesítés megismétlődhet. ",
"Do not restore": "Ne legyen visszaállítva",
"Do not restore all": "Semmit se állítson vissza",
"Do you want to enable watching for changes for all your folders?": "Minden mappára bekapcsolható a változásfigyelés?",
@@ -143,7 +147,7 @@
"GUI Authentication Password": "Grafikus felület jelszava",
"GUI Authentication User": "Grafikus felület felhasználói neve ",
"GUI Authentication: Set User and Password": "Azonosítás a grafikus felületen: felhasználó és jelszó beállítása",
"GUI Listen Address": "Grafikus felület címe",
"GUI Listen Address": "Grafikus felület figyelő címe",
"GUI Theme": "Grafikus felület témája",
"General": "Általános",
"Generate": "Generálás",
@@ -153,6 +157,7 @@
"Help": "Súgó",
"Home page": "Főoldal",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "A jelenlegi beállítások azonban azt jelzik, hogy nem kívánja engedélyezni. Az automatikus összeomlás-jelentés ezért letiltásra került.",
"Identification": "Azonosítás",
"If untrusted, enter encryption password": "Ha nem megbízható, adjon hozzá titkosítási jelszót",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Ha kívánatos, hogy a számítógép többi felhasználója ne tudja elérni a Syncthinget, sem annak fájljait, érdemes az azonosítást beállítani.",
"Ignore": "Mellőzés",
@@ -174,7 +179,7 @@
"Latest Change": "Utolsó módosítás",
"Learn more": "Tudj meg többet",
"Limit": "Sebességkorlát",
"Listeners": "Kapcsolatok",
"Listeners": "Figyelők",
"Loading data...": "Adatok betöltése...",
"Loading...": "Betöltés...",
"Local Additions": "Helyi hozzáadások",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodikus átnézés a megadott időközönként és a változások keresése letiltva",
"Periodic scanning at given interval and enabled watching for changes": "Periodikus átnézés a megadott időközönként és a változások keresése engedélyezve",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodikus átnézés a megadott időközönként és a változások keresésének beállítása sikertelen, 1 percenként újrapróbálkozás:",
"Permanently add it to the ignore list, suppressing further notifications.": "Vegye fel véglegesen a mellőzési listára egyúttal le is tiltva a további értesítéseket.",
"Permissions": "Jogosultságok",
"Please consult the release notes before performing a major upgrade.": "Nagyobb frissítés előtt ellenőrizni kell a kiadási megjegyzéseket.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Be kell állítani a grafikus felület felhasználónevét és jelszavát a Beállítások párbeszédablakban.",
@@ -284,6 +290,8 @@
"Sharing": "Megosztás",
"Show ID": "Azonosító megjelenítése",
"Show QR": "QR-kód megjelenítése",
"Show detailed discovery status": "Részletes felderítési állapot megjelenítése",
"Show detailed listener status": "Részletes figyelő állapot megjelenítése",
"Show diff with previous version": "Előző verzió eltérésének megjelenítése",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Az eszközazonosító helyett jelenik meg. A többi eszközön alapértelmezett névként használható. ",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Az eszközazonosító helyett jelenik meg. Üresen hagyva az eszköz saját neve lesz alkalmazva.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Egyszintű helyettesítő karakter (csak egy mappára érvényes)",
"Size": "Méret",
"Smallest First": "Kisebb először",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Egyes felderítési módszereket nem lehetett létrehozni más eszközök megtalálására vagy az eszköz bejelentésére:",
"Some items could not be restored:": "Néhány elemet nem sikerült visszaállítani:",
"Some listening addresses could not be enabled to accept connections:": "Néhány figyelő címet nem lehetett engedélyezni a kapcsolatok fogadására:",
"Source Code": "Forráskód",
"Stable releases and release candidates": "Stabil és előzetes kiadások ",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "A stabil kiadások nagyjából két héttel el vannak csúsztatva. Ez alatt előzetes kiadásként tesztelésen mennek keresztül.",
@@ -305,11 +315,13 @@
"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.": "Csak titkosított adatokat tárol és szinkronizál. Minden kapcsolatban lévő eszközön a mappákat ugyanazzal a jelszóval kell védeni vagy „{{receiveEncrypted}}” típusúnak kell lenniük.",
"Support": "Támogatás",
"Support Bundle": "Támogatási csomag",
"Sync Protocol Listen Addresses": "Szinkronizációs protokoll címe",
"Sync Protocol Listen Addresses": "Figyelő címek szinkronizációs protokollja",
"Syncing": "Szinkronizálás",
"Syncthing has been shut down.": "Syncthing leállítva",
"Syncthing includes the following software or portions thereof:": "A Syncthing a következő programokat, vagy komponenseket tartalmazza.",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "A Syncthing szabad és nyílt forráskódú szoftver MPL v2.0 licenccel.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "A Syncthing a következő hálózati címeken figyel más eszközök csatlakozási kísérleteire:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "A Syncthing semmilyen címen nem figyel más eszközök csatlakozási kísérleteire. Csak kimenő kapcsolatok működhetnek erről az eszközről.",
"Syncthing is restarting.": "Syncthing újraindul",
"Syncthing is upgrading.": "Syncthing frissül",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "A Syncthing már támogatja az automatikus összeomlás-jelentések küldését a fejlesztők felé. Ez a funkció alapértelmezetten be van kapcsolva.",
@@ -334,6 +346,7 @@
"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.": "A következő intervallumokat használjuk: egy régi verziót őrzünk meg az első órában minden 30 másodpercben, az első nap minden órában, az első 30 napban minden nap, egészen addig amíg el nem érjük a maximálisan megtartható verziók számát minden héten.",
"The following items could not be synchronized.": "A következő elemek nem szinkronizálhatóak.",
"The following items were changed locally.": "A következő elemek változtak helyileg.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "A következő módszereket használja a hálózaton lévő más eszközök felderítésére és az eszköz bejelentésére, hogy mások is megtalálhassák:",
"The following unexpected items were found.": "A következő váratlan elemek találhatóak.",
"The interval must be a positive number of seconds.": "A tisztítási intervallum egy pozitív számmal kifejezett másodperc kell legyen.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "A verziók mappáin futó tisztítási folyamat intervalluma másodpercekben kifejezve. A nullával letiltható az időszakos tisztítás.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "A hiba javítása után automatikusan újra megpróbálja a szinkronizálást.",
"This Device": "Ez az eszköz",
"This can easily give hackers access to read and change any files on your computer.": "Így a hekkerek könnyedén hozzáférést szerezhetnek a gépen tárolt fájlok olvasásához és módosításához.",
"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.": "Ez az eszköz nem képes automatikusan felderíteni más eszközöket, illetve nem tudja bejelenteni a saját címét, hogy mások megtalálják. Csak statikusan konfigurált címekkel rendelkező eszközök tudnak csatlakozni.",
"This is a major version upgrade.": "Ez egy főverzió-frissítés.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Ez e beállítás szabályozza a szükséges szabad helyet a fő (pl: index, adatbázis) lemezen.",
"Time": "Idő",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Pulizia Versioni",
"Cleanup Interval": "Intervallo di Pulizia",
"Click to see discovery failures": "Clicca per vedere gli errori di rilevamento",
"Click to see full identification string and QR code.": "Fare clic per visualizzare la stringa di identificazione completa e il codice QR.",
"Close": "Chiudi",
"Command": "Comando",
"Comment, when used at the start of a line": "Per commentare, va inserito all'inizio di una riga",
@@ -93,6 +94,9 @@
"Discovered": "Individuato",
"Discovery": "Individuazione",
"Discovery Failures": "Individuazione fallita",
"Dismiss": "Dismiss",
"Do not add it to the ignore list, so this notification may recur.": "Non aggiungerlo all'elenco da ignorare, quindi questa notifica potrebbe ripresentarsi.",
"Do not add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Non ripristinare",
"Do not restore all": "Non ripristinare tutto",
"Do you want to enable watching for changes for all your folders?": "Vuoi abilitare il monitoraggio delle modifiche per tutte le tue cartelle?",
@@ -134,7 +138,7 @@
"Folder Label": "Etichetta per la Cartella",
"Folder Path": "Percorso Cartella",
"Folder Type": "Tipo di Cartella",
"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%}\" can only be set when adding a new folder.": "Il tipo di cartella \"{{receiveEncrypted}}\" può essere impostata solo quando si aggiunge una nuova cartella.",
"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.": "Il tipo di cartella \"{{receiveEncrypted}}\" non può essere modificato dopo aver aggiunto la cartella. È necessario rimuovere la cartella, eliminare o decrittografare i dati sul disco e aggiungere nuovamente la cartella.",
"Folders": "Cartelle",
"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.": "Per le seguenti cartelle si è verificato un errore durante l'avvio della ricerca delle modifiche. Sarà ripetuto ogni minuto, quindi gli errori potrebbero risolversi presto. Se persistono, prova a risolvere il problema sottostante e chiedi aiuto se non puoi.",
@@ -153,6 +157,7 @@
"Help": "Aiuto",
"Home page": "Pagina home",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Tuttavia, le impostazioni correnti indicano che potresti non volerla attiva. Abbiamo disattivato la segnalazione automatica degli arresti anomali per te.",
"Identification": "Identificazione",
"If untrusted, enter encryption password": "Se non attendibile, immettere la password di crittografia",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Se vuoi impedire ad altri utenti di questo computer di accedere a Syncthing e attraverso di esso ai tuoi file, prendi in considerazione la configurazione dell'autenticazione.",
"Ignore": "Ignora",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Scansione periodica a intervalli determinati e monitoraggio cambiamenti disabilitata",
"Periodic scanning at given interval and enabled watching for changes": "Scansione periodica a intervalli determinati e monitoraggio cambiamenti abilitata",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Scansione periodica a intervalli determinati e configurazione fallita del monitoraggio cambiamenti, nuovo tentativo ogni 1m:",
"Permanently add it to the ignore list, suppressing further notifications.": "Aggiungi in modo permanente all'elenco da ignorare, sopprimendo ulteriori notifiche.",
"Permissions": "Permessi",
"Please consult the release notes before performing a major upgrade.": "Si prega di consultare le note di rilascio prima di eseguire un aggiornamento principale.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Per favore impostare Utente e Password dell'Interfaccia Grafica nelle Impostazioni.",
@@ -284,6 +290,8 @@
"Sharing": "Condivisione",
"Show ID": "Mostra ID",
"Show QR": "Mostra QR",
"Show detailed discovery status": "Mostra stato dettagliato dell'individuazione",
"Show detailed listener status": "Mostra lo stato dettagliato di ascolto",
"Show diff with previous version": "Mostra le differenze con la versione precedente",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visibile al posto dell'ID Dispositivo nello stato del cluster. Negli altri dispositivi verrà presentato come nome predefinito opzionale.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visibile al posto dell'ID Dispositivo nello stato del cluster. Se viene lasciato vuoto, verrà utilizzato il nome proposto dal dispositivo.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Metacarattere di singolo livello (per corrispondenze solo all'interno di una cartella)",
"Size": "Dimensione",
"Smallest First": "Prima il più piccolo",
"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:": "Alcuni elementi non possono essere ripristinati:",
"Some listening addresses could not be enabled to accept connections:": "Alcuni indirizzi di ascolto non possono essere abilitati per accettare connessioni:",
"Source Code": "Codice Sorgente",
"Stable releases and release candidates": "Versioni stabili e versioni candidate al rilascio",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Le versioni stabili sono in ritardo di circa due settimane. Durante questo tempo verranno testati come candidati di rilascio.",
@@ -305,11 +315,13 @@
"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.": "Memorizza e sincronizza solo i dati crittografati. Le cartelle su tutti i dispositivi collegati devono essere configurate con la stessa password o essere del tipo \"{{receiveEncrypted}}\".",
"Support": "Supporto",
"Support Bundle": "Pacchetto di supporto",
"Sync Protocol Listen Addresses": "Indirizzi del Protocollo di Sincronizzazione",
"Sync Protocol Listen Addresses": "Indirizzi di ascolto del Protocollo di Sincronizzazione",
"Syncing": "Sincronizzazione in corso",
"Syncthing has been shut down.": "Syncthing è stato arrestato.",
"Syncthing includes the following software or portions thereof:": "Syncthing utilizza i seguenti software o porzioni di questi:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing è un software Libero e Open Source concesso in licenza MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing è in ascolto sui seguenti indirizzi di rete per i tentativi di connessione da altri dispositivi:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing non è in ascolto di tentativi di connessione da altri dispositivi su qualsiasi indirizzo. Possono funzionare solo le connessioni in uscita da questo dispositivo.",
"Syncthing is restarting.": "Riavvio di Syncthing in corso.",
"Syncthing is upgrading.": "Aggiornamento di Syncthing in corso.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ora supporta la segnalazione automaticamente agli sviluppatori degli arresti anomali. Questa funzione è abilitata per impostazione predefinita.",
@@ -328,12 +340,13 @@
"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.": "L'ID del dispositivo inserito non sembra valido. Dovrebbe essere una stringa di 52 o 56 caratteri costituita da lettere e numeri, con spazi e trattini opzionali.",
"The folder ID cannot be blank.": "L'ID della cartella non può essere vuoto.",
"The folder ID must be unique.": "L'ID della cartella dev'essere unico.",
"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 content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Il contenuto della cartella su altri dispositivi verrà sovrascritto per diventare identico a questo dispositivo. I file non presenti qui verranno eliminati su altri dispositivi.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Il contenuto della cartella su questo dispositivo verrà sovrascritto per diventare identico ad altri dispositivi. I file appena aggiunti qui verranno eliminati.",
"The folder path cannot be blank.": "Il percorso della cartella non può essere vuoto.",
"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.": "Vengono utilizzati i seguenti intervalli temporali: per la prima ora viene mantenuta una versione ogni 30 secondi, per il primo giorno viene mantenuta una versione ogni ora, per i primi 30 giorni viene mantenuta una versione al giorno, successivamente viene mantenuta una versione ogni settimana fino al periodo massimo impostato.",
"The following items could not be synchronized.": "Non è stato possibile sincronizzare i seguenti elementi.",
"The following items were changed locally.": "I seguenti elementi sono stati modificati localmente.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "I seguenti metodi vengono utilizzati per rilevare altri dispositivi sulla rete e annunciare questo dispositivo per essere trovato da altri:",
"The following unexpected items were found.": "Sono stati trovati i seguenti elementi imprevisti.",
"The interval must be a positive number of seconds.": "L'intervallo deve essere un numero positivo di secondi.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "L'intervallo, in secondi, per l'esecuzione della pulizia nella directory delle versioni. Zero per disabilitare la pulizia periodica.",
@@ -351,6 +364,7 @@
"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 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",

View File

@@ -25,11 +25,11 @@
"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": "匿名での使用状況レポート",
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名での使用状況レポートのフォーマットが変わりました。新形式でのレポートに移行しますか?",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"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 continue?": "続行してもよろしいですか?",
"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?": "Are you sure you want to restore {{count}} files?",
"Are you sure you want to restore {%count%} files?": "{{count}} ファイルを復元してもよろしいですか?",
"Are you sure you want to upgrade?": "アップグレードしてよろしいですか?",
"Auto Accept": "自動承諾",
"Automatic Crash Reporting": "自動クラッシュレポート",
@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "接続に失敗した探索サーバーを確認するにはクリックしてください",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "閉じる",
"Command": "コマンド",
"Comment, when used at the start of a line": "行頭で使用するとコメント行になります",
@@ -63,11 +64,11 @@
"Danger!": "危険!",
"Debugging Facilities": "デバッグ機能",
"Default Configuration": "デフォルトの設定",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Device": "デフォルトのデバイス",
"Default Folder": "デフォルトのフォルダー",
"Default Folder Path": "デフォルトのフォルダーパス",
"Defaults": "Defaults",
"Delete Unexpected Items": "Delete Unexpected Items",
"Defaults": "デフォルト",
"Delete Unexpected Items": "予期しないアイテムを削除",
"Deleted": "削除",
"Deselect All": "すべて選択解除",
"Deselect devices to stop sharing this folder with.": "このフォルダの共有を停止したいデバイスがある場合は、当該デバイスの選択を解除してください。",
@@ -93,6 +94,9 @@
"Discovered": "探索結果",
"Discovery": "探索サーバー",
"Discovery Failures": "探索サーバーへの接続失敗",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Do not restore",
"Do not restore all": "Do not restore all",
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
@@ -153,6 +157,7 @@
"Help": "ヘルプ",
"Home page": "ホームページ",
"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": "信頼しない場合、暗号化パスワードを入力",
"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": "無視",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "定期スキャンは有効で変更の監視は無効です",
"Periodic scanning at given interval and enabled watching for changes": "定期スキャンと変更の監視はいずれも有効です",
"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": "パーミッション",
"Please consult the release notes before performing a major upgrade.": "メジャーアップグレードを行う前にリリースノートを参照してください。",
"Please set a GUI Authentication User and Password in the Settings dialog.": "設定ダイアログから、GUI認証ユーザー名とパスワードを設定してください。",
@@ -284,6 +290,8 @@
"Sharing": "共有",
"Show ID": "IDを表示",
"Show QR": "QRコードを表示",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "前バージョンとの差分を表示",
"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の代わりに表示されます。空欄にすると相手側デバイスが通知してきた名前で更新されます。",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "ワイルドカード (単一のディレクトリ内だけでマッチします)",
"Size": "サイズ",
"Smallest First": "小さい順",
"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:": "Some items could not be restored:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "ソースコード",
"Stable releases and release candidates": "安定版とリリース候補版",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "安定版は、リリース候補版としてのテスト後およそ2週間でリリースされます。",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthingをシャットダウンしました。",
"Syncthing includes the following software or portions thereof:": "Syncthingは以下のソフトウェアまたはその一部を内包しています:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthingはフリーでオープンソースのソフトウェアであり、ライセンスは 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を再起動しています。",
"Syncthing is upgrading.": "Syncthingをアップグレード中です。",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing は、開発者にクラッシュに関する情報を自動的に報告することができます。この機能はデフォルトで有効になっています。",
@@ -334,6 +346,7 @@
"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.": "保存間隔は次の通りです。初めの1時間は30秒ごとに古いバージョンを保存します。同様に、初めの1日間は1時間ごと、初めの30日間は1日ごと、その後最大保存日数までは1週間ごとに、古いバージョンを保存します。",
"The following items could not be synchronized.": "以下の項目は同期できませんでした。",
"The following items were changed locally.": "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 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.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "エラーが解決すると、自動的に再試行され同期されます。",
"This Device": "このデバイス",
"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.": "メジャーアップグレードです。",
"This setting controls the free space required on the home (i.e., index database) disk.": "この設定は、ホームディスク (インデックスデータベースがあるディスク) で必要な空き容量を管理します。",
"Time": "日時",

View File

@@ -22,14 +22,14 @@
"Allow Anonymous Usage Reporting?": "익명 사용 보고서를 보내시겠습니까?",
"Allowed Networks": "허가된 네트워크",
"Alphabetic": "알파벳순",
"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": "익명 사용 보고서",
"Anonymous usage report format has changed. Would you like to move to the new format?": "익명 사용 리포트의 형식이 변경되었습니다. 새 형식으로 이동 하시겠습니까?",
"Are you sure you want to continue?": "Are you sure you want to continue?",
"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%}?": "{{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 continue?": "계속하시겠습니까?",
"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 upgrade?": "업그레이드를 하시겠습니까?",
"Auto Accept": "자동 수락",
"Automatic Crash Reporting": "자동 충돌 보고",
@@ -45,6 +45,7 @@
"Cleaning Versions": "버전 정리 중",
"Cleanup Interval": "정리 간격",
"Click to see discovery failures": "탐색 실패 보기",
"Click to see full identification string and QR code.": "클릭해서 기기 식별자와 QR 코드 보기.",
"Close": "닫기",
"Command": "커맨드",
"Comment, when used at the start of a line": "명령행에서 시작을 할수 있어요.",
@@ -58,16 +59,16 @@
"Copied from elsewhere": "다른 곳에서 복사됨",
"Copied from original": "원본에서 복사됨",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "무시 패턴 만들기, {{path}}에 존재하는 파일을 덮어쓰기 합니다",
"Currently Shared With Devices": "현재 공유된 기기",
"Danger!": "경고!",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "무시 패턴 생성 중, {{path}}에 존재하는 파일을 덮어니다.",
"Currently Shared With Devices": "현재 공유된 기기",
"Danger!": "위험!",
"Debugging Facilities": "디버깅 기능",
"Default Configuration": "기본 설정",
"Default Device": "기본 기기",
"Default Folder": "기본 폴더",
"Default Folder Path": "기본 폴더 경로",
"Defaults": "기본 설정",
"Delete Unexpected Items": "Delete Unexpected Items",
"Delete Unexpected Items": "예기치 못한 항목 삭제",
"Deleted": "삭제됨",
"Deselect All": "모두 선택 해제",
"Deselect devices to stop sharing this folder with.": "Deselect devices to stop sharing this folder with.",
@@ -77,8 +78,8 @@
"Device ID": "기기 ID",
"Device Identification": "기기 식별자",
"Device Name": "기기 이름",
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
"Device rate limits": "Device rate limits",
"Device is untrusted, enter encryption password": "신뢰하지 않는 기기, 암호화 비밀번호를 입력하세요",
"Device rate limits": "기기 속도 제한",
"Device that last modified the item": "항목을 마지막으로 수정 한 기기",
"Devices": "기기",
"Disable Crash Reporting": "충돌 보고 해제",
@@ -87,12 +88,15 @@
"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": "Discard",
"Discard": "무시",
"Disconnected": "연결 끊김",
"Disconnected (Unused)": "연결 해제됨 (미사용)",
"Discovered": "탐색됨",
"Discovery": "탐색",
"Discovery Failures": "탐색 실패",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "복구 하지 않기",
"Do not restore all": "모두 복구 하지 않기",
"Do you want to enable watching for changes for all your folders?": "변경 사항 감시를 당신의 모든 폴더에서 활성화 하는걸 원하시나요?",
@@ -118,7 +122,7 @@
"Error": "오류",
"External File Versioning": "외부 파일 버전 관리",
"Failed Items": "실패한 항목",
"Failed to setup, retrying": "Failed to setup, retrying",
"Failed to setup, retrying": "설정 적용 실패, 재시도 중",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 네트워크에 연결되지 않은 경우 IPv6 서버에 연결 할 수 없습니다.",
"File Pull Order": "파일 동기화 순서",
"File Versioning": "파일 버전 관리",
@@ -153,10 +157,11 @@
"Help": "도움말",
"Home page": "홈페이지",
"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.",
"If untrusted, enter encryption password": "If untrusted, enter encryption password",
"Identification": "식별자",
"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": "무시",
"Ignore Patterns": "패턴 무시",
"Ignore Patterns": "무시 패턴",
"Ignore Permissions": "권한 무시",
"Ignored Devices": "무시된 기기",
"Ignored Folders": "무시된 폴더",
@@ -173,15 +178,15 @@
"Last seen": "마지막 접속",
"Latest Change": "최신 변경",
"Learn more": "더 알아보기",
"Limit": "Limit",
"Limit": "제한",
"Listeners": "수신자",
"Loading data...": "데이터 불러오는중...",
"Loading...": "불러오는 중...",
"Local Additions": "로컬 변경사항",
"Local Additions": "로컬 변경 항목",
"Local Discovery": "로컬 노드 검색",
"Local State": "로컬 상태",
"Local State (Total)": "로컬 상태 (합계)",
"Locally Changed Items": "Locally Changed Items",
"Locally Changed Items": "로컬 변경 항목",
"Log": "기록",
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
"Logs": "기록",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
"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": "권한",
"Please consult the release notes before performing a major upgrade.": "메이저 업데이트를 하기 전에 먼저 릴리즈 노트를 살펴보세요.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "설정에서 GUI 인증용 User와 암호를 입력해주세요.",
@@ -238,7 +244,7 @@
"Random": "무작위",
"Receive Encrypted": "암호화 수신",
"Receive Only": "수신 전용",
"Received data is already encrypted": "Received data is already encrypted",
"Received data is already encrypted": "수신된 데이터는 이미 암호화되어 있습니다",
"Recent Changes": "최근 변경",
"Reduced by ignore patterns": "무시 패턴으로 축소",
"Release Notes": "릴리즈 노트",
@@ -260,7 +266,7 @@
"Resume": "재개",
"Resume All": "모두 재개",
"Reused": "재개",
"Revert Local Changes": "로컬 변경사항",
"Revert Local Changes": "로컬 변경 항목",
"Save": "저장",
"Scan Time Remaining": "탐색 남은 시간",
"Scanning": "탐색 중",
@@ -268,7 +274,7 @@
"Select All": "모두 선택",
"Select a version": "버전 선택",
"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 folders to share with this device.": "이 기기와 공유할 추가 폴더를 선택하세요.",
"Select latest version": "가장 최신 버전 선택",
"Select oldest version": "가장 오래된 버전 선택",
"Select the folders to share with this device.": "이 기기와 공유할 폴더를 선택합니다.",
@@ -284,6 +290,8 @@
"Sharing": "공유",
"Show ID": "내 기기 ID",
"Show QR": "QR 코드 보기",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "이전 버전과 변경사항 보기",
"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.": "아이디가 비어 있는 경우 기본 값으로 다른 기기에 업데이트됩니다.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "단일 레벨 와일드카드 (하나의 디렉토리만 일치하는 경우)",
"Size": "크기",
"Smallest First": "작은 파일순",
"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:": "몇몇 항목들은 복구할 수 없었습니다:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "소스 코드",
"Stable releases and release candidates": "안정 버전과 출시 후보",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "안정 버전은 약 2주 정도 지연되어 출시됩니다. 그 기간 동안 출시 후보에 대한 테스트를 거칩니다.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing이 종료되었습니다.",
"Syncthing includes the following software or portions thereof:": "Syncthing은 다음과 같은 소프트웨어나 그 일부를 포함합니다:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing는 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이 재시작 중입니다.",
"Syncthing is upgrading.": "Syncthing이 업데이트 중입니다.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing는 이제 개발자에게 충돌보고를 자동으로 지원합니다. 이 기능은 기본적으로 활성화 되어 있습니다.",
@@ -333,8 +345,9 @@
"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초마다 유지되며, 첫 하루 동안은 매 시간, 첫 한 달 동안은 매 일마다 유지됩니다. 그리고 최대 날짜까지는 버전이 매 주마다 유지됩니다.",
"The following items could not be synchronized.": "이 항목들은 동기화 할 수 없습니다.",
"The following items were changed locally.": "The following items were changed locally.",
"The following unexpected items were found.": "The following unexpected items were found.",
"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.": "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.": "최대 보존 기간은 숫자여야 하며 비워 둘 수 없습니다.",
@@ -347,10 +360,11 @@
"The rate limit must be a non-negative number (0: no limit)": "대역폭 제한 설정은 반드시 양수로 입력해야 합니다 (0: 무제한)",
"The rescan interval must be a non-negative number of seconds.": "재탐색 간격은 초단위이며 양수로 입력해야 합니다.",
"There are no devices to share this folder with.": "이 폴더를 공유할 기기가 없습니다.",
"There are no folders to share with this device.": "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.": "오류가 해결되면 자동적으로 동기화 됩니다.",
"This Device": "현재 기기",
"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.": "이 업데이트는 메이저 버전입니다.",
"This setting controls the free space required on the home (i.e., index database) disk.": "이 설정은 홈 디스크에 필요한 여유 공간을 제어합니다. (즉, 인덱스 데이터베이스)",
"Time": "시간",
@@ -361,14 +375,14 @@
"Unavailable": "불가능",
"Unavailable/Disabled by administrator or maintainer": "운영자 또는 관리자에 의해 불가능/비활성화 됨",
"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",
"Unexpected Items": "예기치 못한 항목",
"Unexpected items have been found in this folder.": "이 폴더에 예기치 못한 항목이 발견되었습니다.",
"Unignore": "무시 취소",
"Unknown": "알 수 없음",
"Unshared": "공유되지 않음",
"Unshared Devices": "공유되지 않은 기기",
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Unshared Folders": "공유되지 않은 폴더",
"Untrusted": "신뢰하지 않음",
"Up to Date": "최신 데이터",
"Updated": "업데이트 완료",
"Upgrade": "업데이트",
@@ -386,12 +400,12 @@
"Waiting to Clean": "정리 대기 중",
"Waiting to Scan": "탐색 대기 중",
"Waiting to Sync": "동기화 대기 중",
"Warning": "Warning",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "경고, 이 경로는 현재 존재하는 폴더 \"{{otherFolder}}\" 의 상위 폴더 입니다.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "경고, 이 경로는 현재 존재하는 폴더 \"{{otherFolderLabel}}\" ({{otherFolder}}) 의 상위 폴더 입니다.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "경고, 이 경로는 현재 존재하는 폴더 \"{{otherFolder}}\" 의 하위 폴더 입니다.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "경고, 이 경로는 현재 존재하는 폴더 \"{{otherFolderLabel}}\" ({{otherFolder}}) 의 하위 폴더 입니다.",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "경고 : {{syncthingInotify}} 와 같은 외부 감시 도구를 사용하는 경우 비활성화되어 있는지 확인해야 합니다.",
"Warning": "경고",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "경고 : 이 경로는 현재 존재하는 폴더 \"{{otherFolder}}\"의 상위 폴더입니다.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "경고 : 이 경로는 현재 존재하는 폴더 \"{{otherFolderLabel}}\" ({{otherFolder}})의 상위 폴더입니다.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "경고 : 이 경로는 현재 존재하는 폴더 \"{{otherFolder}}\"의 하위 폴더입니다.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "경고 : 이 경로는 현재 존재하는 폴더 \"{{otherFolderLabel}}\" ({{otherFolder}})의 하위 폴더입니다.",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "경고 : {{syncthingInotify}}와 같은 외부 감시 도구를 사용하는 경우 비활성화되어 있는지 확인해야 합니다.",
"Watch for Changes": "변경 사항 감시",
"Watching for Changes": "변경 사항 감시",
"Watching for changes discovers most changes without periodic scanning.": "Watching for changes discovers most changes without periodic scanning.",
@@ -402,7 +416,7 @@
"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.": "이 두 개의 출시 채널에 대해 아래 링크에서 자세하게 읽어 보실 수 있습니다.",
"You have no ignored devices.": "무시된 기기가 없습니다.",
"You have no ignored folders.": "You have no ignored folders.",
"You have no ignored folders.": "무시된 폴더가 없습니다.",
"You have unsaved changes. Do you really want to discard them?": "저장되지 않은 변경이 있습니다. 변경사항을 무시하시겠습니까?",
"You must keep at least one version.": "최소 한 개의 버전은 유지해야 합니다.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Spustelėkite, norėdami pamatyti matomumo nesėkmes",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Užverti",
"Command": "Komanda",
"Comment, when used at the start of a line": "Komentaras naudojamas naujoje eilutėje",
@@ -93,6 +94,9 @@
"Discovered": "Atrastas",
"Discovery": "Lokacija",
"Discovery Failures": "Matomumo nesėkmės",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Neatkurti",
"Do not restore all": "Neatkurti visus",
"Do you want to enable watching for changes for all your folders?": "Ar norite įjungti pakeitimų stebėjimą visiems savo aplankams?",
@@ -153,6 +157,7 @@
"Help": "Pagalba",
"Home page": "Pagrindinis puslapis",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Vis dėlto, jūsų esami nustatymai nurodo, kad jūs, greičiausiai, nenorite turėti jas įjungtas. Mes jums išjungėme automatines ataskaitas apie strigtis.",
"Identification": "Identification",
"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.": "Jei norite neleisti kitiems šio kompiuterio vartotojams gauti prieigą prie Syncthing, o per ją, prieigą prie jūsų failų, tuomet apsvarstykite galimybę nusistatyti tapatybės nustatymą.",
"Ignore": "Nepaisyti",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodinis nuskaitymas nurodytu intervalu ir išjungtas pakeitimų stebėjimas",
"Periodic scanning at given interval and enabled watching for changes": "Periodinis nuskaitymas nurodytu intervalu ir įjungtas pakeitimų stebėjimas",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodinis nuskaitymas nurodytu intervalu ir nepavykęs nustatyti pakeitimų stebėjimas, bandoma iš naujo kas 1 min.:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Leidimai",
"Please consult the release notes before performing a major upgrade.": "Peržvelkite laidos informaciją prieš atlikdami stambų atnaujinimą.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Prašome nustatymų dialoge nustatyti valdymo skydelio vartotojo vardą ir slaptažodį.",
@@ -284,6 +290,8 @@
"Sharing": "Dalinamasis",
"Show ID": "Rodyti ID",
"Show QR": "Rodyti QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Rodyti skirtumus su ankstesne versija",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Grupės būsenoje rodomas vietoje įrenginio vardo. Kiti įrenginiai matys kaip pasirinktinį vardą.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Grupės būsenoje rodomas vietoje įrenginio vardo. Bus atnaujintas į įrenginio vardą jei nieko neįrašysite.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Vieno lygio pakaitos simbolis (atitinka tik vieną katalogo lygį)",
"Size": "Dydis",
"Smallest First": "Mažiausi pirmiau",
"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:": "Kai kurių elementų atkurti nepavyko:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Išeities kodas",
"Stable releases and release candidates": "Stabilios versijos ir kandidatinės versijos",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabilios versijos pasirodo maždaug dvi savaites vėliau. Per tą laiką, jos pereina testavimą kaip kandidatinės versijos.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing išjungtas",
"Syncthing includes the following software or portions thereof:": "Syncthing naudoja šias programas ar jų dalis:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing yra laisva ir atvirojo kodo programinė įranga, licencijuota pagal 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 perleidžiamas",
"Syncthing is upgrading.": "Syncthing atsinaujina.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Dabar, Syncthing palaiko ir automatiškai plėtotojams siunčia ataskaitas apie strigtis. Pagal numatymą, ši ypatybė yra įjungta.",
@@ -334,6 +346,7 @@
"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.": "Šie pertraukų nustatymai naudojami: pirmą valandą versijos laikomos 30 sekundžių, pirmą dieną versijos laikomos valandą, pirmas 30 dienų versijos laikomos parą, kol nebus viršytas nustatytas maksimalus amžius.",
"The following items could not be synchronized.": "Nepavyko parsiųsti šių failų.",
"The following items were changed locally.": "Šie elementai buvo pakeisti vietoje.",
"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.": "Buvo rasti šie netikėti elementai.",
"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.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Failus bus automatiškai bandoma parsiųsti dar kartą kai išspręsite klaidas.",
"This Device": "Šis įrenginys",
"This can easily give hackers access to read and change any files on your computer.": "Tai gali suteikti programišiams lengvą prieigą skaityti ir keisti bet kokius failus jūsų kompiuteryje.",
"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.": "Tai yra stambus atnaujinimas.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Šis nustatymas valdo laisvą vietą, kuri yra reikalinga namų (duomenų bazės) diske.",
"Time": "Laikas",
@@ -361,8 +375,8 @@
"Unavailable": "Neprieinama",
"Unavailable/Disabled by administrator or maintainer": "Neprieinama/Išjungta administratoriaus ar prižiūrėtojo",
"Undecided (will prompt)": "Nenuspręsta (bus klausiama)",
"Unexpected Items": "Unexpected Items",
"Unexpected items have been found in this folder.": "Unexpected items have been found in this folder.",
"Unexpected Items": "Netikėti elementai",
"Unexpected items have been found in this folder.": "Šiame aplanke rasta netikėtų elementų.",
"Unignore": "Nustoti nepaisyti",
"Unknown": "Nežinoma",
"Unshared": "Nesidalinama",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Klikk for å se oppslagsfeil",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Lukk",
"Command": "Kommando",
"Comment, when used at the start of a line": "Kommentar, når det blir brukt i starten av en linje.",
@@ -93,6 +94,9 @@
"Discovered": "Oppdaget",
"Discovery": "Oppslag",
"Discovery Failures": "Oppslagsfeil",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Ikke gjenopprett",
"Do not restore all": "Ikke gjenopprett alle",
"Do you want to enable watching for changes for all your folders?": "Ønsker du å skru på oppsyn med endringer for alle dine mapper?",
@@ -153,6 +157,7 @@
"Help": "Hjelp",
"Home page": "Hjemmeside",
"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": "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": "Ignorer",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodisk skanning på gitte intervaller og avskrudd oppsyn med endringer",
"Periodic scanning at given interval and enabled watching for changes": "Periodisk skanning på gitte intervall og påskrudd oppsyn med endringer",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning på gitte intervall og mislyktes i å sette opp oppsyn med endringer, prøver igjen hvert minutt:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Tilganger",
"Please consult the release notes before performing a major upgrade.": "Sjekk utgivelsesnotatene før en storoppgradering utføres.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Vennligst angi bruker og passord for GUI-autentisering i innstillingsvinduet.",
@@ -284,6 +290,8 @@
"Sharing": "Deling",
"Show ID": "Vis ID",
"Show QR": "Vis QR-kode",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Vis diff med forrige version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Vis i stedet for enhets-ID i gruppestatus. Vil bli kringkastet til andre enheter som et valgfritt forvalgsnavn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vist i stedet for mappe-ID i gruppestatus. Vil bli oppdatert til navnet enheten kringkaster dersom tomt.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Enkeltnivåsøk (søker kun i en mappe)",
"Size": "Størrelse",
"Smallest First": "Den minste først",
"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:": "Noen elementer kunne ikke gjenopprettes:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Kildekode",
"Stable releases and release candidates": "Ferdige utgaver og utgivelseskandidater",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Ferdige utgaver blir holdt tilbake i to uker. I løpet av denne tiden blir de testet som utgivelseskandidater.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing har blitt slått av.",
"Syncthing includes the following software or portions thereof:": "Syncthing inkluderer helt eller delvis følgende programvare:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing er fri programvare med MPL v2.0-lisens.",
"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 starter på ny.",
"Syncthing is upgrading.": "Syncthing oppgraderer.",
"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.",
@@ -334,6 +346,7 @@
"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.": "Følgende intervall blir brukt: Den første timen blir en versjon lagret hvert 30. sekund, den første dagen blir en versjon lagret hver time, de første 30 dagene blir en versjon lagret hver dag, og inntil maksimal levetid blir en versjon lagret hver uke.",
"The following items could not be synchronized.": "Følgende filer kunne ikke synkroniseres.",
"The following items were changed locally.": "Følgende elementer ble endret lokalt.",
"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.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Disse hentes automatisk og vil synkroniseres når feilen er blitt utbedret.",
"This Device": "Denne enheten",
"This can easily give hackers access to read and change any files on your computer.": "Dette kan lett gi hackere tilgang til å lese og endre alle filer på datamaskinen din.",
"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.": "Dette er en storoppgradering",
"This setting controls the free space required on the home (i.e., index database) disk.": "Denne innstillingen kontrollerer ledig diskplass krevd på hjemme- (f.eks. indekseringsdatabase-) disken.",
"Time": "Klokkeslett",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Versies opruimen",
"Cleanup Interval": "Opruiminterval",
"Click to see discovery failures": "Klikken om detectieproblemen weer te geven",
"Click to see full identification string and QR code.": "Klikken om volledige identificatiestring en QR-code weer te geven",
"Close": "Sluiten",
"Command": "Opdracht",
"Comment, when used at the start of a line": "Opmerking, wanneer gebruikt aan het begin van een regel",
@@ -93,6 +94,9 @@
"Discovered": "Gedetecteerd",
"Discovery": "Netwerkdetectie",
"Discovery Failures": "Detectiefouten",
"Dismiss": "Verwerpen",
"Do not add it to the ignore list, so this notification may recur.": "Niet toevoegen aan de negeerlijst zodat deze melding kan terugkomen.",
"Do not add it to the ignore list, so this notification may recurr.": "Niet toevoegen aan de negeerlijst zodat deze melding kan terugkomen.",
"Do not restore": "Niet herstellen",
"Do not restore all": "Niet alles herstellen",
"Do you want to enable watching for changes for all your folders?": "Wilt u het opvolgen van wijzigingen voor al uw mappen inschakelen?",
@@ -153,6 +157,7 @@
"Help": "Help",
"Home page": "Startpagina",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Uw huidige instellingen geven echter aan dat u het misschien niet wilt inschakelen. We hebben de automatische crashrapportage voor u uitgeschakeld.",
"Identification": "Identificatie",
"If untrusted, enter encryption password": "Indien niet vertrouwd, versleutelingswachtwoord opgeven",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Als u wilt voorkomen dat andere gebruikers op deze computer toegang krijgen tot Syncthing en daardoor uw bestanden, kunt u overwegen om authenticatie in te stellen.",
"Ignore": "Negeren",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Regelmatig scannen met opgegeven interval; opvolgen van wijzigingen uitgeschakeld",
"Periodic scanning at given interval and enabled watching for changes": "Regelmatig scannen met opgegeven interval; opvolgen van wijzigingen ingeschakeld",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Regelmatig scannen met opgegeven interval; instellen van opvolgen van wijzigingen mislukt. Elke minuut wordt opnieuw geprobeerd:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanent toevoegen aan de negeerlijst zodat verdere meldingen worden onderdrukt.",
"Permissions": "Machtigingen",
"Please consult the release notes before performing a major upgrade.": "Lees eerst de release-opmerkingen voordat u een grote upgrade uitvoert.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Stel een gebruikersnaam en wachtwoord voor GUI-authenticatie in via het instellingen-venster.",
@@ -284,6 +290,8 @@
"Sharing": "Delen",
"Show ID": "ID weergeven",
"Show QR": "QR weergeven",
"Show detailed discovery status": "Gedetailleerde detectiestatus weergeven",
"Show detailed listener status": "Gedetailleerde status van luisteraars weergeven",
"Show diff with previous version": "Verschil met vorige versie weergeven",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Weergegeven in plaats van de apparaat-ID in de cluster-status. Zal aan andere apparaten voorgesteld worden als een optionele standaardnaam.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Weergegeven in plaats van de apparaat-ID in de clusterstatus. Zal bijgewerkt worden met de naam die het apparaat voorstelt wanneer leeg gelaten.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Wildcard op enkelvoudig niveau (komt alleen overeen binnen een map)",
"Size": "Grootte",
"Smallest First": "Kleinste eerst",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Sommige detectiemethodes konden niet worden ingesteld om andere apparaten te vinden of dit apparaat aan te kondigen:",
"Some items could not be restored:": "Sommige items konden niet hersteld worden:",
"Some listening addresses could not be enabled to accept connections:": "Sommige luisteradressen konden niet worden ingeschakeld om verbindingen te aanvaarden:",
"Source Code": "Broncode",
"Stable releases and release candidates": "Stabiele releases en release candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabiele releases worden met ongeveer twee weken uitgesteld. Tijdens deze periode worden ze als release canditates getest.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing werd afgesloten.",
"Syncthing includes the following software or portions thereof:": "Syncthing bevat de volgende software of delen daarvan:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is gratis en opensource software onder licentie van MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing luistert op de volgende netwerkadressen naar verbindingspogingen van andere apparaten:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing luistert op geen enkel adres naar verbindingspogingen van andere apparaten. Alleen uitgaande verbindingen vanaf dit apparaat zouden kunnen werken.",
"Syncthing is restarting.": "Syncthing wordt opnieuw gestart.",
"Syncthing is upgrading.": "Syncthing is aan het bijwerken.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ondersteunt nu automatisch rapporteren van crashes naar de ontwikkelaars. De functie is standaard ingeschakeld.",
@@ -334,6 +346,7 @@
"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.": "De volgende intervallen worden gebruikt: het eerste uur worden versies iedere 30 seconden bewaard, de eerste dag worden versies ieder uur bewaard, de eerste 30 dagen worden versies iedere dag bewaard, tot de maximale leeftijd worden versies iedere week bewaard.",
"The following items could not be synchronized.": "De volgende items konden niet gesynchroniseerd worden.",
"The following items were changed locally.": "De volgende items werden lokaal gewijzigd.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "De volgende methodes worden gebruikt om andere apparaten in het netwerk te detecteren en dit apparaat aan te kondigen zodat het door anderen kan worden gevonden:",
"The following unexpected items were found.": "De volgende onverwachte items zijn teruggevonden.",
"The interval must be a positive number of seconds.": "Het interval moet een positief aantal seconden zijn.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Het interval, in seconden, voor het uitvoeren van opruiming in de versie-map. Nul om de regelmatige schoonmaak uit te schakelen.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Ze worden automatisch opnieuw geprobeerd en zullen gesynchroniseerd worden wanneer de fout opgelost is.",
"This Device": "Dit apparaat",
"This can easily give hackers access to read and change any files on your computer.": "Dit kan hackers eenvoudig toegang geven om bestanden op uw computer te lezen en te wijzigen.",
"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.": "Dit apparaat kan andere apparaten niet automatisch detecteren of zijn eigen adres aankondigen om door anderen gevonden te worden. Alleen apparaten met statisch geconfigureerde adressen kunnen verbinding maken.",
"This is a major version upgrade.": "Dit is een grote versie-upgrade.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Deze instelling bepaalt de benodigde vrije ruimte op de home-schijf (d.w.z. de indexdatabase).",
"Time": "Tijd",

View File

@@ -18,7 +18,7 @@
"Advanced": "Zaawansowane",
"Advanced Configuration": "Zaawansowane ustawienia",
"All Data": "Wszystkie dane",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Wszystkie foldery współdzielone z tym urządzeniem muszą być zabezpieczone hasłem, tak aby żadne przesyłane dane nie były możliwe do odczytu bez podania tego hasła.",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Wszystkie foldery współdzielone z tym urządzeniem muszą być zabezpieczone hasłem, tak aby wszystkie przesyłane dane były nie do odczytu bez podania danego hasła.",
"Allow Anonymous Usage Reporting?": "Czy chcesz zezwolić na anonimowe statystyki użycia?",
"Allowed Networks": "Dozwolone sieci",
"Alphabetic": "Alfabetycznie",
@@ -45,6 +45,7 @@
"Cleaning Versions": "Czyszczenie wersji",
"Cleanup Interval": "Przedział czasowy czyszczenia",
"Click to see discovery failures": "Kliknij tutaj, aby zobaczyć błędy odnajdywania",
"Click to see full identification string and QR code.": "Kliknij tutaj, aby zobaczyć pełny identyfikator oraz kod QR.",
"Close": "Zamknij",
"Command": "Polecenie",
"Comment, when used at the start of a line": "Komentarz, jeżeli znajduje się na początku linii",
@@ -58,7 +59,7 @@
"Copied from elsewhere": "Skopiowane z innego miejsca ",
"Copied from original": "Skopiowane z oryginału",
"Copyright © 2014-2019 the following Contributors:": "Wszelkie prawa zastrzeżone © 2014-2019 dla współtwórców:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Ustawianie wzorów ignorowania, nadpisze istniejący plik w {{path}}.",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Tworzenie wzorów ignorowania, nadpisze istniejący plik w {{path}}.",
"Currently Shared With Devices": "Obecnie współdzielony z urządzeniami",
"Danger!": "Niebezpieczeństwo!",
"Debugging Facilities": "Narzędzia do debugowania",
@@ -93,6 +94,9 @@
"Discovered": "Odnaleziony",
"Discovery": "Odnajdywanie",
"Discovery Failures": "Błędy odnajdywania",
"Dismiss": "Odrzuć",
"Do not add it to the ignore list, so this notification may recur.": "Nie dodaje do listy ignorowanych, więc powiadomienie to może się powtórzyć.",
"Do not add it to the ignore list, so this notification may recurr.": "Nie dodaje do listy ignorowanych, więc powiadomienie to może się powtórzyć.",
"Do not restore": "Nie przywracaj",
"Do not restore all": "Nie przywracaj wszystkich",
"Do you want to enable watching for changes for all your folders?": "Czy chcesz włączyć obserwowanie zmian we wszystkich folderach?",
@@ -153,6 +157,7 @@
"Help": "Pomoc",
"Home page": "Strona domowa",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Niemniej jednak, obecne ustawienia wskazują, że możesz nie chcieć włączać tej funkcji. Automatyczne zgłaszanie awarii zostało wyłączone na tym urządzeniu.",
"Identification": "Identyfikator",
"If untrusted, enter encryption password": "Jeżeli folder jest niezaufany, wprowadź szyfrujące hasło",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Jeżeli chcesz zakazać innym użytkownikom tego komputera dostępu do Syncthing, a przez niego do swoich plików, zastanów się nad włączeniem uwierzytelniania.",
"Ignore": "Ignoruj",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Okresowe skanowanie w podanym przedziale czasowym i wyłączone obserwowanie zmian",
"Periodic scanning at given interval and enabled watching for changes": "Okresowe skanowanie w podanym przedziale czasowym i włączone obserwowanie zmian",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Okresowe skanowanie w podanym przedziale czasowym i nieudane ustawienie obserwowania zmian, ponawiam co minutę:",
"Permanently add it to the ignore list, suppressing further notifications.": "Dodaje na stałe od listy ignorowanych, wyciszając kolejne powiadomienia.",
"Permissions": "Uprawnienia",
"Please consult the release notes before performing a major upgrade.": "Zapoznaj się z informacjami o wersji przed przeprowadzeniem dużej aktualizacji.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ustaw użytkownika i hasło do uwierzytelniania GUI w oknie Ustawień.",
@@ -284,6 +290,8 @@
"Sharing": "Współdzielenie",
"Show ID": "Pokaż ID",
"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ż różnice 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 ID urządzenia. Zostanie wysłana 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 ID urządzenia. Zostanie zaktualizowana do nazwy wysłanej przez urządzenie, jeżeli pozostanie pusta.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Wieloznacznik jednopoziomowy (wyszukuje tylko wewnątrz katalogu)",
"Size": "Rozmiar",
"Smallest First": "Najmniejsze na początku",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Nie udało się ustawić niektórych metod odnajdywania w celu szukania innych urządzeń oraz ogłaszania tego urządzenia:",
"Some items could not be restored:": "Niektórych elementów nie udało się przywrócić:",
"Some listening addresses could not be enabled to accept connections:": "Nie udało się włączyć niektórych adresów nasłuchujących w celu przyjmowania połączeń:",
"Source Code": "Kod źródłowy",
"Stable releases and release candidates": "Wydania stabilne i wydania kandydujące",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Wydania stabilne są opóźnione o ok. dwa tygodnie. W tym czasie są one testowane jako wydania kandydujące.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing został wyłączony.",
"Syncthing includes the following software or portions thereof:": "Syncthing zawiera następujące oprogramowanie lub ich części:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing to wolne i otwarte oprogramowanie na licencji MPL 2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing nasłuchuje prób połączeń z innych urządzeń pod następującymi adresami sieciowymi:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing nie nasłuchuje prób połączeń z innych urządzeń pod żadnym adresem. Tylko połączenia wychodzące z tego urządzenia są w stanie działać.",
"Syncthing is restarting.": "Syncthing jest uruchamiany ponownie.",
"Syncthing is upgrading.": "Syncthing jest aktualizowany.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing zawiera teraz automatyczne zgłaszanie awarii do autorów. Ta funkcja jest domyślnie włączona.",
@@ -334,6 +346,7 @@
"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.": "Używane są następujące przedziały czasowe: w pierwszej godzinie wersja zachowywana jest co 30 sekund, w pierwszym dniu co godzinę, w pierwszych 30 dniach codziennie, a do czasu osiągnięcia maksymalnego wieku co tydzień.",
"The following items could not be synchronized.": "Następujące elementy nie mogły zostać zsynchronizowane.",
"The following items were changed locally.": "Następujące elementy zostały zmienione lokalnie.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Poniższe metody są używane do odnajdywania innych urządzeń w sieci oraz ogłaszania tego urządzenia, aby mogło ono zostać znalezione przez nie:",
"The following unexpected items were found.": "Znaleziono następujące elementy nieoczekiwane.",
"The interval must be a positive number of seconds.": "Przedział czasowy musi być dodatnią liczbą sekund.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Przedział czasowy, w sekundach, w którym nastąpi czyszczenie katalogu wersjonowania. Ustaw na zero, aby wyłączyć czyszczenie okresowe.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Ponowne próby zachodzą automatycznie. Synchronizacja nastąpi po usunięciu błędu.",
"This Device": "To urządzenie",
"This can easily give hackers access to read and change any files on your computer.": "Może to umożliwić hakerom dostęp do odczytu i zmian dowolnych plików na tym komputerze.",
"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.": "To urządzenie nie jest w stanie automatycznie odnajdować innych urządzeń oraz ogłaszać swojego adresu, aby mogło ono zostać znalezione przez nie. Tylko urządzenia z adresem ustawionym statycznie są w stanie się połączyć.",
"This is a major version upgrade.": "To jest duża aktualizacja.",
"This setting controls the free space required on the home (i.e., index database) disk.": "To ustawienie kontroluje ilość wolnej przestrzeni na dysku domowym (np. do indeksowania bazy danych).",
"Time": "Czas",

View File

@@ -12,17 +12,17 @@
"Add Remote Device": "Adicionar dispositivo remoto",
"Add devices from the introducer to our device list, for mutually shared folders.": "Adicionar dispositivos do introdutor à sua lista de dispositivos para pastas compartilhadas mutualmente.",
"Add new folder?": "Adicionar nova pasta?",
"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.": "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.",
"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.": "Além disso, o intervalo de nova verificação será aumentado (vezes 60, ou seja, novo padrão de 1h). Você também pode configurá-lo manualmente para cada pasta depois de escolher Não.",
"Address": "Endereço",
"Addresses": "Endereços",
"Advanced": "Avançado",
"Advanced Configuration": "Configuração avançada",
"All Data": "Todos os dados",
"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.",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Todas as pastas compartilhadas com este dispositivo devem ser protegidas por uma senha, de forma que todos os dados enviados sejam ilegíveis sem a senha fornecida.",
"Allow Anonymous Usage Reporting?": "Permitir envio de relatórios anônimos de uso?",
"Allowed Networks": "Redes permitidas",
"Alphabetic": "Alfabética",
"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.",
"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.": "Um comando externo controla o controle de versão. Tem que remover o arquivo da pasta compartilhada. Se o caminho para o aplicativo contiver espaços, ele deve ser colocado entre aspas.",
"Anonymous Usage Reporting": "Relatórios anônimos de uso",
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anônimo de uso mudou. Gostaria de usar o formato novo?",
"Are you sure you want to continue?": "Deseja realmente continuar?",
@@ -45,6 +45,7 @@
"Cleaning Versions": "Limpando Versões",
"Cleanup Interval": "Intervalo de Limpeza",
"Click to see discovery failures": "Clique para ver as falhas na descoberta de dispositivos",
"Click to see full identification string and QR code.": "Clique para ver a string de identificação completa e o QR code.",
"Close": "Fechar",
"Command": "Comando",
"Comment, when used at the start of a line": "Comentário, se usado no início de uma linha",
@@ -54,7 +55,7 @@
"Connection Error": "Erro de conexão",
"Connection Type": "Tipo da conexão",
"Connections": "Conexões",
"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.": "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.",
"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.": "Observar continuamente as alterações agora está disponível no Syncthing. Isso detectará mudanças no disco e fará uma varredura apenas nos caminhos modificados. Os benefícios são que as alterações são propagadas mais rapidamente e menos verificações completas são necessárias.",
"Copied from elsewhere": "Copiado de outro lugar",
"Copied from original": "Copiado do original",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 dos Contribuintes:",
@@ -86,13 +87,16 @@
"Disabled periodic scanning and disabled watching for changes": "Verificação periódica e verificação automática de mudanças desabilitadas",
"Disabled periodic scanning and enabled watching for changes": "Verificação periódica desabilitada. Verificação automática de mudanças habilitada.",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Verificação periódica desabilitada. Não foi possível configurar a verificação automática de mudanças, tentando novamente a cada 1 minuto:",
"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).",
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Desativa a comparação e sincronização de permissões de arquivo. Útil em sistemas com permissões inexistentes ou personalizadas (por exemplo, FAT, exFAT, Synology, Android).",
"Discard": "Descartar",
"Disconnected": "Desconectado",
"Disconnected (Unused)": "Desconectado (Não usado)",
"Discovered": "Descoberto",
"Discovery": "Descoberta",
"Discovery Failures": "Falhas na descoberta",
"Dismiss": "Descartar",
"Do not add it to the ignore list, so this notification may recur.": "Não o adicione à lista de ignorados, portanto, esta notificação pode ocorrer novamente.",
"Do not add it to the ignore list, so this notification may recurr.": "Não o adicione à lista de ignorados, portanto, esta notificação pode ocorrer novamente.",
"Do not restore": "Não restaurar",
"Do not restore all": "Não restaurar nenhum",
"Do you want to enable watching for changes for all your folders?": "Você deseja ativar a observação de alterações em todas as suas pastas?",
@@ -112,9 +116,9 @@
"Enabled": "Habilitado",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Insira um número não negativo (por exemplo, 2.35) e escolha uma unidade. Porcentagens são como parte do tamanho total do disco.",
"Enter a non-privileged port number (1024 - 65535).": "Insira um número de porta não privilegiada (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 comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Digite os endereços separados por vírgula (\"tcp://ip:porta\", \"tcp://host:porta\") ou \"dinâmico\" para realizar a descoberta automática do endereço.",
"Enter ignore patterns, one per line.": "Insira os filtros, um por linha.",
"Enter up to three octal digits.": "Enter up to three octal digits.",
"Enter up to three octal digits.": "Insira até três dígitos octais.",
"Error": "Erro",
"External File Versioning": "Externo",
"Failed Items": "Itens com falha",
@@ -125,8 +129,8 @@
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Os arquivos são movidos para o diretório .stversions quando substituídos ou apagados pelo Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Os arquivos são renomeados com suas datas e movidos para o diretório .stversions quando substituídos ou apagados pelo Syncthing.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Os arquivos estão protegidos contra alterações feitas em outros dispositivos, mas alterações feitas neste dispositivo serão enviadas ao resto dos dispositivos.",
"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": "Filesystem Watcher Errors",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Os arquivos são sincronizados do cluster, mas as alterações feitas localmente não serão enviadas para outros dispositivos.",
"Filesystem Watcher Errors": "Erros do Observador do Sistema de Arquivos",
"Filter by date": "Filtrar por data",
"Filter by name": "Filtrar por nome",
"Folder": "Pasta",
@@ -134,15 +138,15 @@
"Folder Label": "Rótulo da pasta",
"Folder Path": "Caminho da pasta",
"Folder Type": "Tipo da pasta",
"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.",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "O tipo de pasta \"{{receiveEncrypted}}\" só pode ser definido ao adicionar uma nova pasta.",
"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.": "O tipo de pasta \"{{receiveEncrypted}}\" não pode ser alterado após adicionar a pasta. Você precisa remover a pasta, excluir ou descriptografar os dados do disco e adicionar a pasta novamente.",
"Folders": "Pastas",
"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)": "Full Rescan Interval (s)",
"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.": "Nas pastas a seguir, ocorreu um erro ao começar a observar as alterações. Ele será repetido a cada minuto, portanto, os erros podem desaparecer em breve. Se eles persistirem, tente consertar o problema subjacente e peça ajuda, se não puder.",
"Full Rescan Interval (s)": "Intervalo(s) de Verificações Completas",
"GUI": "Interface gráfica",
"GUI Authentication Password": "Senha para acesso à interface",
"GUI Authentication User": "Nome de usuário para acesso à interface",
"GUI Authentication: Set User and Password": "GUI Authentication: Set User and Password",
"GUI Authentication: Set User and Password": "Autenticação da GUI: Definir Usuário e Senha",
"GUI Listen Address": "Endereço de escuta da interface web",
"GUI Theme": "Tema da interface",
"General": "Geral",
@@ -152,9 +156,10 @@
"Global State": "Estado global",
"Help": "Ajuda",
"Home page": "Página inicial",
"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.",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "No entanto, suas configurações atuais indicam que você pode não querer ativá-lo. Desativamos os relatórios automáticos de falha para você.",
"Identification": "Identificação",
"If untrusted, enter encryption password": "Se o dispositivo não é confiável, digite a senha de criptografia",
"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.",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Se você deseja impedir que outros usuários deste computador acessem o Syncthing e, por meio dele, seus arquivos, considere configurar a autenticação.",
"Ignore": "Ignorar",
"Ignore Patterns": "Filtros",
"Ignore Permissions": "Ignorar permissões",
@@ -183,15 +188,15 @@
"Local State (Total)": "Estado local (total)",
"Locally Changed Items": "Itens modificados localmente",
"Log": "Log",
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing pausado. Role até o final para continuar.",
"Logs": "Logs",
"Major Upgrade": "Atualização \"major\"",
"Mass actions": "Ações em massa",
"Maximum Age": "Idade máxima",
"Metadata Only": "Somente metadados",
"Minimum Free Disk Space": "Espaço livre mínimo no disco",
"Mod. Device": "Mod. Device",
"Mod. Time": "Mod. Time",
"Mod. Device": "Dispositivo Modificado",
"Mod. Time": "Hora da Modificação",
"Move to top of queue": "Mover para o topo da lista",
"Multi level wildcard (matches multiple directory levels)": "Coringa multi-nível (faz corresponder a vários níveis de pastas)",
"Never": "Nunca",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Verificação periódica habilitada no intervalo escolhido. Verificação automática de mudanças desabilitada",
"Periodic scanning at given interval and enabled watching for changes": "Verificação periódica habilitada no intervalo escolhido. Verificação automática de mudanças habilitada",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Verificação periódica habilitada no intervalo escolhido. Não foi possível configurar a verificação automática de mudanças, tentando novamente a cada 1 minuto:",
"Permanently add it to the ignore list, suppressing further notifications.": "Adicione-o permanentemente à lista de ignorados, suprimindo notificações futuras.",
"Permissions": "Permissões",
"Please consult the release notes before performing a major upgrade.": "Por favor, consulte as notas de lançamento antes de atualizar para uma versão \"major\".",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Por favor, defina um nome de usuário e senha para acesso à interface web, nas configurações.",
@@ -284,6 +290,8 @@
"Sharing": "Compartilhamento",
"Show ID": "Mostrar ID",
"Show QR": "Mostrar QR Code",
"Show detailed discovery status": "Mostrar status de descoberta detalhado",
"Show detailed listener status": "Mostrar status detalhado do ouvinte",
"Show diff with previous version": "Mostrar diferenças da versão anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrado no lugar do ID do dispositivo no indicador de estado do grupo. Será divulgado aos outros dispositivos como um nome opcional e pré-definido.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrado no lugar do ID do dispositivo no indicador de estado do grupo. Será atualizado para o nome que o dispositivo divulga, caso seja deixado em branco.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Coringa de único nível (faz corresponder apenas dentro de uma pasta)",
"Size": "Tamanho",
"Smallest First": "Menor primeiro",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Alguns métodos de descoberta não puderam ser estabelecidos para encontrar outros dispositivos ou anunciar este dispositivo:",
"Some items could not be restored:": "Alguns itens não puderam ser restaurados:",
"Some listening addresses could not be enabled to accept connections:": "Alguns endereços de escuta não puderam ser ativados para aceitar conexões:",
"Source Code": "Código-fonte",
"Stable releases and release candidates": "Versões estáveis e candidatas ao lançamento",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Versões estáveis são lançadas aproximadamente a cada duas semanas. Durante esse tempo elas são testadas como candidatas.",
@@ -302,21 +312,23 @@
"Start Browser": "Iniciar navegador",
"Statistics": "Estatísticas",
"Stopped": "Parado",
"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.",
"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.": "Armazena e sincroniza apenas dados criptografados. As pastas em todos os dispositivos conectados precisam ser configuradas com a mesma senha ou ser do tipo \"{{receiveEncrypted}}\" também.",
"Support": "Suporte",
"Support Bundle": "Support Bundle",
"Support Bundle": "Pacote de Suporte",
"Sync Protocol Listen Addresses": "Endereços de escuta do protocolo de sincronização",
"Syncing": "Sincronizando",
"Syncthing has been shut down.": "O Syncthing foi desligado.",
"Syncthing includes the following software or portions thereof:": "O Syncthing inclui os seguintes programas ou partes deles:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "O Syncthing é um software de código aberto licenciado pela MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "O Syncthing está escutando nos seguintes endereços de rede para tentativas de conexão de outros dispositivos:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "O Syncthing não está ouvindo tentativas de conexão de outros dispositivos em qualquer endereço. Apenas conexões de saída deste dispositivo podem funcionar.",
"Syncthing is restarting.": "O Syncthing está sendo reiniciado.",
"Syncthing is upgrading.": "O Syncthing está sendo atualizado.",
"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 now supports automatically reporting crashes to the developers. This feature is enabled by default.": "O Syncthing agora oferece suporte a relatórios automáticos de falhas para os desenvolvedores. Este recurso é habilitado por padrão.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Parece que o Syncthing está desligado ou há um problema com a sua conexão de internet. Tentando novamente...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Parece que o Syncthing está tendo problemas no processamento da requisição. Por favor, atualize a página ou reinicie o Syncthing caso o problema persista.",
"Take me back": "Tire-me daqui",
"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 GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "O endereço da GUI é substituído pelas opções de inicialização. As alterações aqui não terão efeito enquanto a substituição estiver em vigor.",
"The Syncthing Authors": "Autores do Syncthing",
"The Syncthing admin interface is configured to allow remote access without a password.": "A interface de administração do Syncthing está configurada para permitir acesso remoto sem uma senha.",
"The aggregated statistics are publicly available at the URL below.": "As estatísticas agregadas estão disponíveis no endereço abaixo.",
@@ -328,15 +340,16 @@
"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.": "O ID de dispositivo inserido não parece ser válido. Ele deve ter entre 52 e 56 caracteres e ser composto de letras e números, com espaços e hífens opcionais.",
"The folder ID cannot be blank.": "O ID da pasta não pode ficar vazio.",
"The folder ID must be unique.": "O ID da pasta deve ser único.",
"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 content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "O conteúdo da pasta em outros dispositivos será sobrescrito para se tornar idêntico a este dispositivo. Os arquivos não presentes aqui serão excluídos em outros dispositivos.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "O conteúdo da pasta neste dispositivo será sobrescrito para se tornar idêntico a outros dispositivos. Os arquivos recém-adicionados aqui serão excluídos.",
"The folder path cannot be blank.": "O caminho da pasta não pode ficar vazio.",
"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.": "São utilizados os seguintes intervalos: na primeira hora é guardada uma versão a cada 30 segundos, no primeiro dia é guardada uma versão a cada hora, nos primeiros 30 dias é guardada uma versão por dia e, até que atinja a idade máxima, é guardada uma versão por semana.",
"The following items could not be synchronized.": "Os itens a seguir não puderam ser sincronizados.",
"The following items were changed locally.": "Os seguintes itens foram alterados localmente.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Os métodos a seguir são usados para descobrir outros dispositivos na rede e anunciar que este dispositivo pode ser encontrado por outros:",
"The following unexpected items were found.": "Os seguintes itens inesperados foram encontrados.",
"The interval must be a positive number of seconds.": "O intervalo deve ser um número positivo de segundos.",
"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 interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "O intervalo, em segundos, para executar a limpeza na pasta de versões. Zero para desativar a limpeza periódica.",
"The maximum age must be a number and cannot be blank.": "A idade máxima deve ser um valor numérico. O campo não pode ficar vazio.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "O número máximo de dias em que uma versão é guardada. (Use 0 para manter para sempre).",
"The number of days must be a number and cannot be blank.": "O número de dias deve ser um número válido e não pode ficar em branco.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Serão tentadas automaticamente e sincronizadas após o erro ter sido resolvido.",
"This Device": "Este dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Isto pode dar a hackers poder de leitura e escrita de qualquer arquivo em seu dispositivo.",
"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.": "Este dispositivo não pode descobrir automaticamente outros dispositivos ou anunciar seu próprio endereço para ser encontrado por outros. Apenas dispositivos com endereços configurados estaticamente podem se conectar.",
"This is a major version upgrade.": "Esta é uma atualização para uma versão \"major\".",
"This setting controls the free space required on the home (i.e., index database) disk.": "Este ajuste controla o espaço livre necessário no disco que contém o banco de dados do Syncthing.",
"Time": "Hora",
@@ -378,7 +392,7 @@
"Uptime": "Tempo ligado",
"Usage reporting is always enabled for candidate releases.": "O relatório de uso está sempre habilitado em versões candidatas ao lançamento",
"Use HTTPS for GUI": "Usar HTTPS para a interface web",
"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.",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "O Usuário/Senha não foi definido para a autenticação da GUI. Por favor, considere defini-los.",
"Version": "Versão",
"Versions": "Versões",
"Versions Path": "Caminho do versionamento",
@@ -394,7 +408,7 @@
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Aviso: caso você esteja usando um observador externo como o {{syncthingInotify}}, tenha certeza de que ele está desativado.",
"Watch for Changes": "Observar alterações",
"Watching for Changes": "Observando alterações",
"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.": "Observar as mudanças descobre a maioria das mudanças sem uma verificação periódica.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quando estiver adicionando um dispositivo, lembre-se de que este dispositivo deve ser adicionado do outro lado também.",
"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 adicionar uma nova pasta, lembre-se que o ID da pasta é utilizado para ligar pastas entre dispositivos. Ele é sensível às diferenças entre maiúsculas e minúsculas e deve ser o mesmo em todos os dispositivos.",
"Yes": "Sim",
@@ -405,7 +419,7 @@
"You have no ignored folders.": "Você não possui nenhuma pasta ignorada.",
"You have unsaved changes. Do you really want to discard them?": "Você tem alterações não salvas. Você realmente deseja descartá-las?",
"You must keep at least one version.": "Você deve manter pelo menos uma versão.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "You should never add or change anything locally in a \"{{receiveEncrypted}}\" folder.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Você nunca deve adicionar ou alterar nada localmente em uma pasta \"{{receiveEncrypted}}\".",
"days": "dias",
"directories": "diretórios",
"files": "arquivos",

View File

@@ -26,7 +26,7 @@
"Anonymous Usage Reporting": "Enviar relatórios anónimos de utilização",
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anónimo de utilização foi alterado. Gostaria de mudar para o novo formato?",
"Are you sure you want to continue?": "Tem a certeza de que quer continuar?",
"Are you sure you want to permanently delete all these files?": "Tem a certeza que quer eliminar permanentemente todos estes ficheiros?",
"Are you sure you want to permanently delete all these files?": "Tem a certeza de que quer eliminar permanentemente todos estes ficheiros?",
"Are you sure you want to remove device {%name%}?": "Tem a certeza que quer remover o dispositivo {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Tem a certeza que quer remover a pasta {{label}}?",
"Are you sure you want to restore {%count%} files?": "Tem a certeza que quer restaurar {{count}} ficheiros?",
@@ -45,6 +45,7 @@
"Cleaning Versions": "Limpando versões",
"Cleanup Interval": "Intervalo entre limpezas",
"Click to see discovery failures": "Clique para ver as falhas da pesquisa",
"Click to see full identification string and QR code.": "Clique para ver a identificação completa e o código QR.",
"Close": "Fechar",
"Command": "Comando",
"Comment, when used at the start of a line": "Comentário, quando usado no início de uma linha",
@@ -71,13 +72,13 @@
"Deleted": "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 terminar a partilha com este dispositivo.",
"Deselect folders to stop sharing with this device.": "Retire a selecção das pastas para deixar de as partilhar com este dispositivo.",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "O dispositivo \"{{name}}\" ({{device}} em {{address}}) quer conectar-se. Adiciono este novo dispositivo?",
"Device ID": "ID do dispositivo",
"Device Identification": "Identificação do dispositivo",
"Device Name": "Nome do dispositivo",
"Device is untrusted, enter encryption password": "Não existe uma relação de confiança com o dispositivo, insira senha de encriptação",
"Device is untrusted, enter encryption password": "Dispositivo não fiável, insira senha de encriptação",
"Device rate limits": "Limites de velocidade do dispositivo",
"Device that last modified the item": "Último dispositivo a modificar o item",
"Devices": "Dispositivos",
@@ -93,6 +94,9 @@
"Discovered": "Descoberto",
"Discovery": "Pesquisa",
"Discovery Failures": "Falhas da pesquisa",
"Dismiss": "Descartar",
"Do not add it to the ignore list, so this notification may recur.": "Não adicionar à lista dos ignorados, para que esta notificação volte a aparecer.",
"Do not add it to the ignore list, so this notification may recurr.": "Não adicionar à lista dos ignorados, para que esta notificação volte a aparecer.",
"Do not restore": "Não restaurar",
"Do not restore all": "Não restaurar nenhum",
"Do you want to enable watching for changes for all your folders?": "Quer activar a vigilância de alterações para todas as suas pastas?",
@@ -135,7 +139,7 @@
"Folder Path": "Caminho da pasta",
"Folder Type": "Tipo de pasta",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "O tipo de pasta \"{{receiveEncrypted}}\" apenas pode ser definido aquando da adição de uma nova pasta.",
"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.": "Pastas do tipo \"{{receiveEncrypted}}\" não podem ser modificadas depois de adicionar a pasta. Tem de remover a pasta, eliminar ou desencriptar os dados no disco e adicionar a pasta novamente.",
"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.": "O tipo de pasta \"{{receiveEncrypted}}\" não pode ser modificado depois de adicionar a pasta. Tem de remover a pasta, eliminar ou desencriptar os dados no disco e adicionar a pasta novamente.",
"Folders": "Pastas",
"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.": "Nas pastas seguintes ocorreu um erro durante o arranque da vigilância de alterações. Será tentado novamente a cada minuto, por isso os erros poderão desaparecer brevemente. Se persistirem, tente resolver o erro subjacente e, caso não consiga, peça ajuda.",
"Full Rescan Interval (s)": "Intervalo entre verificações completas (s)",
@@ -153,6 +157,7 @@
"Help": "Ajuda",
"Home page": "Página do projecto",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Contudo, a sua configuração actual indica que pode não a querer activada. Nós desactivámos automaticamente o relatório de estouro para si.",
"Identification": "Identificação",
"If untrusted, enter encryption password": "Se não for fiável, insira uma senha de encriptação",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Se quiser evitar que outros utilizadores neste computador acedam ao Syncthing e, através dele, aos seus ficheiros, considere configurar a autenticação.",
"Ignore": "Ignorar",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Verificação periódica no intervalo dado e desactivada a vigilância de alterações",
"Periodic scanning at given interval and enabled watching for changes": "Verificação periódica no intervalo dado e activada a vigilância de alterações",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Verificação periódica no intervalo dado e falha ao preparar a vigilância de alterações, tentando novamente a cada minuto:",
"Permanently add it to the ignore list, suppressing further notifications.": "Adicionar permanentemente à lista de ignorados, inibindo novas notificações.",
"Permissions": "Permissões",
"Please consult the release notes before performing a major upgrade.": "Consulte as notas de lançamento antes de fazer uma actualização importante.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Por favor, defina um utilizador e senha de autenticação para a interface gráfica, nas configurações.",
@@ -236,8 +242,8 @@
"Preview Usage Report": "Pré-visualizar relatório de utilização",
"Quick guide to supported patterns": "Guia rápido dos padrões suportados",
"Random": "Aleatória",
"Receive Encrypted": "Receber encriptados",
"Receive Only": "Recebe apenas",
"Receive Encrypted": "recebe dados encriptados",
"Receive Only": "recebe apenas",
"Received data is already encrypted": "Os dados recebidos já estão encriptados",
"Recent Changes": "Alterações recentes",
"Reduced by ignore patterns": "Reduzido por padrões de exclusão",
@@ -272,8 +278,8 @@
"Select latest version": "Seleccionar a última versão",
"Select oldest version": "Seleccionar a versão mais antiga",
"Select the folders to share with this device.": "Seleccione as pastas a partilhar com este dispositivo.",
"Send & Receive": "Envia e recebe",
"Send Only": "Envia apenas",
"Send & Receive": "envia e recebe",
"Send Only": "envia apenas",
"Settings": "Configurações",
"Share": "Partilhar",
"Share Folder": "Partilhar pasta",
@@ -284,6 +290,8 @@
"Sharing": "Partilha",
"Show ID": "Mostrar ID",
"Show QR": "Mostrar QR",
"Show detailed discovery status": "Apresentar o estado da pesquisa detalhado",
"Show detailed listener status": "Apresentar estado de auscultação detalhado",
"Show diff with previous version": "Mostrar diferenças em relação à versão anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Apresentado ao invés do ID do dispositivo no indicador de estado do grupo. Será divulgado aos outros dispositivos como um nome predefinido opcional.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Apresentado ao invés do ID do dispositivo no indicador de estado do grupo. Será actualizado para o nome que o dispositivo divulga, se for deixado em branco.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Símbolo polivalente de um só nível (faz corresponder apenas dentro de uma pasta)",
"Size": "Tamanho",
"Smallest First": "Primeiro os menores",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Não foi possível estabelecer alguns dos métodos de pesquisa para encontrar outros dispositivos ou anunciar este dispositivo:",
"Some items could not be restored:": "Não foi possível restaurar alguns dos itens:",
"Some listening addresses could not be enabled to accept connections:": "Alguns endereços de auscultação não puderam ser activados para aceitar ligações:",
"Source Code": "Código fonte",
"Stable releases and release candidates": "Versões estáveis e versões candidatas a lançamento",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Versões estáveis são adiadas por cerca de duas semanas. Durante esse período são submetidas a testes sob a forma de versões candidatas a lançamento.",
@@ -302,7 +312,7 @@
"Start Browser": "Iniciar navegador",
"Statistics": "Estatísticas",
"Stopped": "Parado",
"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.": "Armazena e sincroniza apenas dados encriptados. Pastas em todos os dispositivos conectados têm de ser configuradas com a mesma senha ou ser do tipo \"{{receiveEncrypted}}\" também.",
"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.": "Armazena e sincroniza apenas dados encriptados. As pastas em todos os dispositivos conectados têm de ser configuradas com a mesma senha ou ser também do tipo \"{{receiveEncrypted}}\".",
"Support": "Suporte",
"Support Bundle": "Pacote de suporte",
"Sync Protocol Listen Addresses": "Endereços de escuta do protocolo de sincronização",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "O Syncthing foi desligado.",
"Syncthing includes the following software or portions thereof:": "O Syncthing inclui as seguintes aplicações ou partes delas:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing é Software Livre e de Código Aberto licenciado como MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "O Syncthing está à escuta de tentativas de ligação por parte de outros dispositivos nos seguintes endereços de rede:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "O Syncthing não está à escuta de tentativas de ligação por parte de outros dispositivos em nenhum endereço. Apenas poderão funcionar ligações deste dispositivo para fora.",
"Syncthing is restarting.": "O Syncthing está a reiniciar.",
"Syncthing is upgrading.": "O Syncthing está a actualizar-se.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "O Syncthing agora suporta o envio automático de relatórios de estouro para os programadores. Esta funcionalidade vem inicialmente activada.",
@@ -334,6 +346,7 @@
"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.": "São utilizados os seguintes intervalos: na primeira hora é guardada uma versão a cada 30 segundos, no primeiro dia é guardada uma versão a cada hora, nos primeiros 30 dias é guardada uma versão por dia e, até que atinja a idade máxima, é guardada uma versão por semana.",
"The following items could not be synchronized.": "Não foi possível sincronizar os elementos seguintes.",
"The following items were changed locally.": "Os itens seguintes foram alterados localmente.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Os métodos seguintes são usados para pesquisar outros dispositivos na rede e anunciar este dispositivo para que seja encontrado pelos outros.",
"The following unexpected items were found.": "Foram encontrados os seguinte itens inesperados.",
"The interval must be a positive number of seconds.": "O intervalo tem que ser um número positivo de segundos.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "O intervalo, em segundos, para executar limpezas na pasta das versões. Coloque zero para desactivar a limpeza periódica.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Será tentado automaticamente e os itens serão sincronizados assim que o erro seja resolvido.",
"This Device": "Este dispositivo",
"This can easily give hackers access to read and change any files on your computer.": "Isso facilmente dará acesso aos piratas informáticos para lerem e modificarem quaisquer ficheiros no seu computador.",
"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.": "Este dispositivo não pode pesquisar automaticamente outros dispositivos ou anunciar o seu próprio endereço para que seja encontrado pelos outros. Apenas dispositivos com endereços configurados estaticamente podem estabelecer ligação.",
"This is a major version upgrade.": "Esta é uma actualização para uma versão importante.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Este parâmetro controla o espaço livre necessário no disco base (ou seja, o disco da base de dados do índice).",
"Time": "Quando",
@@ -405,7 +419,7 @@
"You have no ignored folders.": "Não tem pastas ignoradas.",
"You have unsaved changes. Do you really want to discard them?": "Fez alterações que não foram guardadas. Quer mesmo descartá-las?",
"You must keep at least one version.": "Tem que manter pelo menos uma versão.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca deve adicionar ou modificar algo localmente numa pasta \"{{receiveEncrypted}}\".",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca deve adicionar ou modificar algo localmente numa pasta do tipo \"{{receiveEncrypted}}\".",
"days": "dias",
"directories": "pastas",
"files": "ficheiros",
@@ -414,5 +428,5 @@
"seconds": "segundos",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer partilhar a pasta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quer partilhar a pasta \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} poderá reintroduzir este dispositivo"
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} poderá reintroduzir este dispositivo."
}

View File

@@ -0,0 +1,432 @@
{
"A device with that ID is already added.": "Un dispozitiv cu acest ID este deja adăugat.",
"A negative number of days doesn't make sense.": "Un număr negativ de zile nu are sens.",
"A new major version may not be compatible with previous versions.": "Este posibil ca o nouă versiune majoră să nu fie compatibilă cu versiunile anterioare.",
"API Key": "Cheie API",
"About": "Despre",
"Action": "Acționează",
"Actions": "Acțiuni",
"Add": "Adaugă",
"Add Device": "Adaugă Dispozitiv",
"Add Folder": "Adaugă Mapă",
"Add Remote Device": "Adaugă dispozitiv la distanță",
"Add devices from the introducer to our device list, for mutually shared folders.": "Adăugați dispozitive de la introductor la lista dispozitivelor noastre, pentru folderele partajate reciproc.",
"Add new folder?": "Adauga o mapă nouă?",
"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.": "În plus, intervalul complet de rescanare va fi mărit (de 60 de ori, adică noul implict de 1 oră). Puteți, de asemenea, să o configurați manual pentru fiecare dosar mai târziu după alegerea nr.",
"Address": "Adresă",
"Addresses": "Adrese",
"Advanced": "Avansat",
"Advanced Configuration": "Configurari avansate",
"All Data": "Toate Datele",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Toate folderele partajate cu acest dispozitiv trebuie protejate printr-o parolă, astfel încât toate datele trimise să nu poată fi citite fără parola dată.",
"Allow Anonymous Usage Reporting?": "Permiteţi raportarea anonimă de folosire a aplicaţiei?",
"Allowed Networks": "Rețele permise",
"Alphabetic": "Alfabetic",
"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.": "O comandă externă gestionează versiunea. Trebuie să elimine fișierul din folderul partajat. Dacă calea către aplicație conține spații, ar trebui să fie pusă între ghilimele.",
"Anonymous Usage Reporting": "Raport Anonim despre Folosirea Aplicației",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formatul raportului de utilizare anonim s-a schimbat. Doriți să vă mutați în noul format?",
"Are you sure you want to continue?": "Ești sigur ca vrei sa continui?",
"Are you sure you want to permanently delete all these files?": "Sigur doriți să ștergeți definitiv toate aceste fișiere?",
"Are you sure you want to remove device {%name%}?": "Sigur doriți să eliminați dispozitivul {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Sigur doriți să eliminați dosarul {{label}}?",
"Are you sure you want to restore {%count%} files?": "Sigur doriți să restaurați {{count}} fișiere?",
"Are you sure you want to upgrade?": "Sigur doriți să faceți upgrade?",
"Auto Accept": "Acceptare automată",
"Automatic Crash Reporting": "Raportarea automată a problemelor întâmpinate",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Actualizarea automată oferă acum posibilitatea de a alege între versiuni stabile și candidați la versiuni.",
"Automatic upgrades": "Actualizare automată",
"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.": "Automatically create or share folders that this device advertises at the default path.",
"Available debug logging facilities:": "Available debug logging facilities:",
"Be careful!": "Fii atent!",
"Bugs": "Bug-uri",
"Changelog": "Noutăți",
"Clean out after": "Clean out after",
"Cleaning Versions": "Cleaning Versions",
"Cleanup Interval": "Cleanup Interval",
"Click to see discovery failures": "Click to see discovery failures",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Închide",
"Command": "Comandă",
"Comment, when used at the start of a line": "Comentariu, când este folosit la începutul unei linii",
"Compression": "Compresie",
"Configured": "Configured",
"Connected (Unused)": "Connected (Unused)",
"Connection Error": "Eroare de conexiune",
"Connection Type": "Connection Type",
"Connections": "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.": "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.",
"Copied from elsewhere": "Copiat din altă parte",
"Copied from original": "Copiat din original",
"Copyright © 2014-2019 the following Contributors:": "Copyright © 2014-2019 the following Contributors:",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creating ignore patterns, overwriting an existing file at {{path}}.",
"Currently Shared With Devices": "Currently Shared With Devices",
"Danger!": "Danger!",
"Debugging Facilities": "Debugging Facilities",
"Default Configuration": "Default Configuration",
"Default Device": "Default Device",
"Default Folder": "Default Folder",
"Default Folder Path": "Default Folder Path",
"Defaults": "Defaults",
"Delete Unexpected Items": "Delete Unexpected Items",
"Deleted": "Șters",
"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.",
"Device": "Device",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Device \"{{name}}\" ({{device}} at {{address}}) wants to connect. Add new device?",
"Device ID": "ID Dispozitiv",
"Device Identification": "Identificare Dispozitiv",
"Device Name": "Nume Dispozitiv",
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
"Device rate limits": "Device rate limits",
"Device that last modified the item": "Device that last modified the item",
"Devices": "Dispozitiv",
"Disable Crash Reporting": "Disable Crash Reporting",
"Disabled": "Disabled",
"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": "Discard",
"Disconnected": "Deconectat",
"Disconnected (Unused)": "Disconnected (Unused)",
"Discovered": "Discovered",
"Discovery": "Discovery",
"Discovery Failures": "Discovery Failures",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Do not restore",
"Do not restore all": "Do not restore all",
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
"Documentation": "Documentaţie",
"Download Rate": "Viteză de Descărcare",
"Downloaded": "Descărcat",
"Downloading": "Se descarcă",
"Edit": "Modifică",
"Edit Device": "Modifică Dispozitiv",
"Edit Device Defaults": "Edit Device Defaults",
"Edit Folder": "Modifică Mapa",
"Edit Folder Defaults": "Edit Folder Defaults",
"Editing {%path%}.": "Editing {{path}}.",
"Enable Crash Reporting": "Enable Crash Reporting",
"Enable NAT traversal": "Enable NAT traversal",
"Enable Relaying": "Enable Relaying",
"Enabled": "Enabled",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.",
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (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.": "Adaugă șabloanele de ignorare, câte una pe linie.",
"Enter up to three octal digits.": "Enter up to three octal digits.",
"Error": "Eroare",
"External File Versioning": "Administrare externă a versiunilor documentului",
"Failed Items": "Failed Items",
"Failed to setup, retrying": "Failed to setup, retrying",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"File Pull Order": "File Pull Order",
"File Versioning": "Versiune Fișier",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Files are moved to .stversions directory when replaced or deleted by Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Fișierele sunt protejate de schimbările făcute pe alte dispozitive dar schimbările efectuate pe acest dispozitiv vor fi trimise catre restul grupului.",
"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": "Filesystem Watcher Errors",
"Filter by date": "Filter by date",
"Filter by name": "Filter by name",
"Folder": "Mapă",
"Folder ID": "ID Mapă",
"Folder Label": "Folder Label",
"Folder Path": "Locaţie Mapei",
"Folder Type": "Folder Type",
"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": "Mapă",
"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)": "Full Rescan Interval (s)",
"GUI": "GUI",
"GUI Authentication Password": "Parolă Interfaţă",
"GUI Authentication User": "User Interfaţă",
"GUI Authentication: Set User and Password": "GUI Authentication: Set User and Password",
"GUI Listen Address": "GUI Listen Address",
"GUI Theme": "GUI Theme",
"General": "General",
"Generate": "Generează",
"Global Discovery": "Găsire Globală",
"Global Discovery Servers": "Global Discovery Servers",
"Global State": "Status Global",
"Help": "Help",
"Home page": "Home page",
"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": "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": "Ignoră",
"Ignore Patterns": "Reguli de excludere",
"Ignore Permissions": "Ignoră Permisiuni",
"Ignored Devices": "Ignored Devices",
"Ignored Folders": "Ignored Folders",
"Ignored at": "Ignored at",
"Incoming Rate Limit (KiB/s)": "Limită Viteză de Download (KB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Incorrect configuration may damage your folder contents and render Syncthing inoperable.",
"Introduced By": "Introduced By",
"Introducer": "Dispozitiv Inițiator",
"Inversion of the given condition (i.e. do not exclude)": "Inversarea condiției (de ex., nu exclude)",
"Keep Versions": "Păstrează Versiuni",
"LDAP": "LDAP",
"Largest First": "Largest First",
"Last Scan": "Last Scan",
"Last seen": "Ultima vizionare",
"Latest Change": "Latest Change",
"Learn more": "Learn more",
"Limit": "Limit",
"Listeners": "Listeners",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
"Local Additions": "Local Additions",
"Local Discovery": "Găsire Locală",
"Local State": "Status Local",
"Local State (Total)": "Local State (Total)",
"Locally Changed Items": "Locally Changed Items",
"Log": "Log",
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing paused. Scroll to the bottom to continue.",
"Logs": "Logs",
"Major Upgrade": "Major Upgrade",
"Mass actions": "Mass actions",
"Maximum Age": "Vârsta Maximă",
"Metadata Only": "Doar Metadate",
"Minimum Free Disk Space": "Minimum Free Disk Space",
"Mod. Device": "Mod. Device",
"Mod. Time": "Mod. Time",
"Move to top of queue": "Mută la începutul listei",
"Multi level wildcard (matches multiple directory levels)": "Asterisc de nivel multiplu (corespunde fișierelor și sub-fișierelor)",
"Never": "Niciodată",
"New Device": "Dispozitiv Nou",
"New Folder": "Mapă Nouă",
"Newest First": "Newest First",
"No": "Nu",
"No File Versioning": "Fără versiuni ale documentelor",
"No files will be deleted as a result of this operation.": "No files will be deleted as a result of this operation.",
"No upgrades": "No upgrades",
"Not shared": "Not shared",
"Notice": "Mențiuni",
"OK": "OK",
"Off": "Închis",
"Oldest First": "Oldest First",
"Optional descriptive label for the folder. Can be different on each device.": "Optional descriptive label for the folder. Can be different on each device.",
"Options": "Opțiuni",
"Out of Sync": "Out of Sync",
"Out of Sync Items": "Elemente Nesincronizate",
"Outgoing Rate Limit (KiB/s)": "Limită Viteză de Upload (KB/s)",
"Override Changes": "Suprascrie Schimbări",
"Path": "Path",
"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": "Localizarea fișierului în acest computer. Dacă nu există, va fi creat. Tilda (~) înlocuiește ",
"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%}.": "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}}.",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).",
"Pause": "Pauză",
"Pause All": "Pause All",
"Paused": "Paused",
"Paused (Unused)": "Paused (Unused)",
"Pending changes": "Pending changes",
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
"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": "Permissions",
"Please consult the release notes before performing a major upgrade.": "Please consult the release notes before performing a major upgrade.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Please set a GUI Authentication User and Password in the Settings dialog.",
"Please wait": "Aşteaptă",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Preparing to Sync": "Preparing to Sync",
"Preview": "Previzualizează",
"Preview Usage Report": "Vezi raportul de utilizare",
"Quick guide to supported patterns": "Ghid rapid pentru regulile suportate",
"Random": "Random",
"Receive Encrypted": "Receive Encrypted",
"Receive Only": "Receive Only",
"Received data is already encrypted": "Received data is already encrypted",
"Recent Changes": "Recent Changes",
"Reduced by ignore patterns": "Reduced by ignore patterns",
"Release Notes": "Release Notes",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.",
"Remote Devices": "Remote Devices",
"Remote GUI": "Remote GUI",
"Remove": "Remove",
"Remove Device": "Remove Device",
"Remove Folder": "Remove Folder",
"Required identifier for the folder. Must be the same on all cluster devices.": "Required identifier for the folder. Must be the same on all cluster devices.",
"Rescan": "Rescanează",
"Rescan All": "Rescaneaza Tot",
"Rescans": "Rescans",
"Restart": "Restart",
"Restart Needed": "Restart Necesar",
"Restarting": "Se restartează",
"Restore": "Restore",
"Restore Versions": "Restore Versions",
"Resume": "Resume",
"Resume All": "Resume All",
"Reused": "Refolosit",
"Revert Local Changes": "Revert Local Changes",
"Save": "Salvează",
"Scan Time Remaining": "Scan Time Remaining",
"Scanning": "Scanează",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Select a version",
"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": "Select latest version",
"Select oldest version": "Select oldest version",
"Select the folders to share with this device.": "Alege mapele pe care vrei sa le imparți cu acest dispozitiv.",
"Send & Receive": "Send & Receive",
"Send Only": "Send Only",
"Settings": "Setări",
"Share": "Împarte",
"Share Folder": "Împarte Mapa",
"Share Folders With Device": "Împarte Mapa Cu Dispozitivul",
"Share this folder?": "Împarte această mapă?",
"Shared Folders": "Shared Folders",
"Shared With": "Împarte Cu",
"Sharing": "Sharing",
"Show ID": "Arată ID",
"Show QR": "Show QR",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Show diff with previous version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Vizibil în locul ID-ului dispozitivului într-un grup. Va fi sugerat celorlalte dispozitive ca nume opţional. ",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vizibil în locul ID-ului dispozitivului într-un grup. Va fi înlocuit de numele sugerat de dispozitiv daca nu este completat. ",
"Shutdown": "Opreşte",
"Shutdown Complete": "Oprește Complet",
"Simple File Versioning": "Versiuni simple ale documentelor",
"Single level wildcard (matches within a directory only)": "Asterisc de nivel simplu (corespunde doar unui fişier)",
"Size": "Size",
"Smallest First": "Smallest First",
"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:": "Some items could not be restored:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Cod Sursă",
"Stable releases and release candidates": "Stable releases and release candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.",
"Stable releases only": "Stable releases only",
"Staggered File Versioning": "Versiuni eşalonate ale documentelor",
"Start Browser": "Lansează Browser",
"Statistics": "Statistici",
"Stopped": "Oprit",
"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": "Suport Tehnic",
"Support Bundle": "Support Bundle",
"Sync Protocol Listen Addresses": "Adresa protocolului de sincronizare",
"Syncing": "Se sincronizează",
"Syncthing has been shut down.": "Sincronizarea a fost oprită.",
"Syncthing includes the following software or portions thereof:": "Syncthing include următoarele soft-uri sau părţi din ele:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as 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 se restartează.",
"Syncthing is upgrading.": "Syncthing se actualizează.",
"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 pare a fi oprit sau aveţi probleme cu conexiunea la internet. Reluare... ",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing pare a avea probleme prelucrînd solicitarea dumneavoastră. Reîncărcaţi pagina sau porniţi Syncthing din nou dacă problema continuă. ",
"Take me back": "Take me back",
"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.": "The aggregated statistics are publicly available at the URL below.",
"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.": "Configuraţia a fost salvată dar nu şi activată. Syncthing trebuie să repornească pentru a activa noua configuraţie.",
"The device ID cannot be blank.": "ID-ul dispozitivului nu poate fi gol.",
"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 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.": "Raportul codat de utilizare este trimit zilnic. Este folosit pentru studierea platformelor comune, dimensiunilor fişierelor şi versiunea aplicaţiilor. În cazul în care setul de date trimis este modificat, acest dialog va aparea din nou. ",
"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-ul dispozitivului nu pare a fi valid.El trebuie sa fie format dintrun șir din 52 ori 56 de caractere formate din litere și cifre, cu spatii și linii opțional.",
"The folder ID cannot be blank.": "ID-ul mapei nu poate fi gol.",
"The folder ID must be unique.": "ID-ul mapei trebuie să fie unic.",
"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.": "Locaţia mapei nu poate fi goală.",
"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.": "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 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.": "Vârsta maximă trebuie să fie un număr şi nu poate fi goală.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Câte zile să se păstreze o versiune (setează 0 pentru nelimitat)",
"The number of days must be a number and cannot be blank.": "The number of days must be a number and cannot be blank.",
"The number of days to keep files in the trash can. Zero means forever.": "Numărul de zile pentru a păstra fișierele in urnă.Zero înseamnă permanent.",
"The number of old versions to keep, per file.": "Numărul de versiuni vechi de salvat per fişier.",
"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 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 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": "This Device",
"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.": "This is a major version upgrade.",
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
"Time": "Time",
"Time the item was last modified": "Time the item was last modified",
"Trash Can File Versioning": "Trash Can File Versioning",
"Type": "Type",
"UNIX Permissions": "UNIX Permissions",
"Unavailable": "Unavailable",
"Unavailable/Disabled by administrator or maintainer": "Unavailable/Disabled by administrator or maintainer",
"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": "Necunoscut",
"Unshared": "Neîmpărțit",
"Unshared Devices": "Unshared Devices",
"Unshared Folders": "Unshared Folders",
"Untrusted": "Untrusted",
"Up to Date": "La Zi",
"Updated": "Updated",
"Upgrade": "Upgrade",
"Upgrade To {%version%}": "Actualizează La Versiunea {{version}}",
"Upgrading": "Se Actualizează",
"Upload Rate": "Viteză Upload",
"Uptime": "Uptime",
"Usage reporting is always enabled for candidate releases.": "Usage reporting is always enabled for candidate releases.",
"Use HTTPS for GUI": "Foloseşte HTTPS pentru interfaţă",
"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": "Versiune",
"Versions": "Versions",
"Versions Path": "Locaţie Versiuni",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versiunile sînt şterse în mod automat dacă sînt mai vechi decît vîrsta maximă sau depăşesc numărul de documente permise într-un anume interval. ",
"Waiting to Clean": "Waiting to Clean",
"Waiting to Scan": "Waiting to Scan",
"Waiting to Sync": "Waiting to 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": "Watch for Changes",
"Watching for Changes": "Watching for Changes",
"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.": "Când adaugi un dispozitiv nou, trebuie să adaugi şi dispozitivul curent în dispozitivul nou.",
"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.": "Cînd adăugaţi un fişier nou, nu uitaţi că ID-ul fişierului va rămîne acelaşi pe toate dispozitivele. Iar literele mari sînt diferite de literele mici. ",
"Yes": "Da",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "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.": "You can read more about the two release channels at the link below.",
"You have no ignored devices.": "You have no ignored devices.",
"You have no ignored folders.": "You have no ignored folders.",
"You have unsaved changes. Do you really want to discard them?": "You have unsaved changes. Do you really want to discard them?",
"You must keep at least one version.": "Trebuie să păstrezi cel puţin o versiune.",
"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": "Zile",
"directories": "directories",
"files": "files",
"full documentation": "toată documentaţia",
"items": "obiecte",
"seconds": "seconds",
"{%device%} wants to share folder \"{%folder%}\".": "{{Dispozitivul}} vrea să transmită mapa {{Mapa}}",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
}

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Очистка Версий",
"Cleanup Interval": "Интервал очистки",
"Click to see discovery failures": "Щёлкните, чтобы посмотреть ошибки",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Закрыть",
"Command": "Команда",
"Comment, when used at the start of a line": "Комментарий, если используется в начале строки",
@@ -93,6 +94,9 @@
"Discovered": "Обнаружено",
"Discovery": "Обнаружение",
"Discovery Failures": "Ошибки обнаружения",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Не восстанавливать",
"Do not restore all": "Не восстанавливать все",
"Do you want to enable watching for changes for all your folders?": "Хотите включить слежение за изменениями для всех своих папок?",
@@ -153,6 +157,7 @@
"Help": "Помощь",
"Home page": "Сайт",
"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": "Если ненадёжное, укажите пароль шифрования",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Если вы хотите запретить другим пользователям на этом компьютере доступ к Syncthing и через него к вашим файлам, подумайте о настройке аутентификации.",
"Ignore": "Игнорировать",
@@ -183,7 +188,7 @@
"Local State (Total)": "Локальное состояние (всего)",
"Locally Changed Items": "Объекты, изменённые на этом компьютере",
"Log": "Журнал",
"Log tailing paused. Scroll to the bottom to continue.": "Вывод журнала приостановлен. Чтобы продолжить, прокрутите до журнал конца.",
"Log tailing paused. Scroll to the bottom to continue.": "Вывод журнала приостановлен. Чтобы продолжить, прокрутите до конца журнала.",
"Logs": "Журналы",
"Major Upgrade": "Обновление основной версии",
"Mass actions": "Массовые действия",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Периодическое сканирование с заданным интервалом, отслеживание изменений отключено",
"Periodic scanning at given interval and enabled watching for changes": "Периодическое сканирование с заданным интервалом и включено отслеживание изменений",
"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": "Разрешения",
"Please consult the release notes before performing a major upgrade.": "Перед проведением обновления основной версии ознакомьтесь, пожалуйста, с замечаниями к версии",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Установите имя пользователя и пароль для интерфейса в настройках",
@@ -284,6 +290,8 @@
"Sharing": "Предоставление доступа",
"Show ID": "Показать ID",
"Show QR": "Показать QR-код",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Показать различия с предыдущей версией",
"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 устройства в статусе группы. Если поле не заполнено, то будет установлено имя, передаваемое этим устройством.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Одноуровневая маска (поиск совпадений только внутри папки)",
"Size": "Размер",
"Smallest First": "Сначала маленькие",
"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:": "Не удалось восстановить некоторые объекты:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Исходный код",
"Stable releases and release candidates": "Стабильные выпуски и кандидаты в релизы",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Стабильные выпуски выходят с задержкой около двух недель. За это время они проходят тестирование в качестве кандидатов в релизы.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing был выключен.",
"Syncthing includes the following software or portions thereof:": "Syncthing включает в себя следующее ПО или его части:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing — свободное программное обеспечение с открытым кодом под лицензией 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.",
"Syncthing is upgrading.": "Обновление Syncthing.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing теперь поддерживает автоматическую отправку отчетов о сбоях разработчикам. Эта функция включена по умолчанию.",
@@ -334,6 +346,7 @@
"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 секунд, в первый день - каждый час, первые 30 дней - каждый день, после, до максимального срока - каждую неделю.",
"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.": "Интервал в секундах для запуска очистки в каталоге версий. Ноль, чтобы отключить периодическую очистку.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Будут синхронизированы автоматически когда ошибка будет исправлена.",
"This Device": "Это устройство",
"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.": "Это обновление основной версии продукта.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Эта настройка управляет свободным местом, необходимым на домашнем диске (например, для базы индексов).",
"Time": "Время",
@@ -365,9 +379,9 @@
"Unexpected items have been found in this folder.": "В папке найдены неожиданные элементы.",
"Unignore": "Не игнорировать",
"Unknown": "Неизвестно",
"Unshared": "Необщедоступно",
"Unshared": "Не общедоступно",
"Unshared Devices": "Устройства без общего доступа",
"Unshared Folders": "Необщедоступные папки",
"Unshared Folders": "Не общедоступные папки",
"Untrusted": "Ненадёжный",
"Up to Date": "В актуальном состоянии",
"Updated": "Обновлено",

View File

@@ -45,6 +45,7 @@
"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",
@@ -93,6 +94,9 @@
"Discovered": "Zistené",
"Discovery": "Zisťovanie",
"Discovery Failures": "Zlyhania zisťovania",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"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?",
@@ -153,6 +157,7 @@
"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ť",
@@ -225,6 +230,7 @@
"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í.",
@@ -284,6 +290,8 @@
"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.",
@@ -293,7 +301,9 @@
"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.",
@@ -310,6 +320,8 @@
"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.",
@@ -334,6 +346,7 @@
"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.",
@@ -351,6 +364,7 @@
"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 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",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Rensningsversioner",
"Cleanup Interval": "Rensningsintervall",
"Click to see discovery failures": "Klicka för att se upptäcktsmisslyckanden",
"Click to see full identification string and QR code.": "Klicka för att se fullständig identifieringssträng och QR-kod.",
"Close": "Stäng",
"Command": "Kommando",
"Comment, when used at the start of a line": "Kommentara, vid användning i början av en rad.",
@@ -93,6 +94,9 @@
"Discovered": "Upptäckt",
"Discovery": "Annonsering",
"Discovery Failures": "Annonseringsmisslyckanden",
"Dismiss": "Avfärda",
"Do not add it to the ignore list, so this notification may recur.": "Lägg inte till den i ignoreringslistan, så denna avisering kan återkomma.",
"Do not add it to the ignore list, so this notification may recurr.": "Lägg inte till den i ignoreringslistan, så denna avisering kan återkomma.",
"Do not restore": "Återställ inte",
"Do not restore all": "Återställ inte allt",
"Do you want to enable watching for changes for all your folders?": "Vill du aktivera bevakning av ändringar på alla dina mappar?",
@@ -153,6 +157,7 @@
"Help": "Hjälp",
"Home page": "Webbplats",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Dina aktuella inställningar visar dock att du kanske inte vill att den ska aktiveras. Vi har inaktiverat automatisk krasch rapportering för dig.",
"Identification": "Identifiering",
"If untrusted, enter encryption password": "Om opålitlig, ange krypteringslösenord",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Om du vill förhindra att andra användare på denna dator får åtkomst till Syncthing och genom det dina filer, överväg att ställa in autentisering.",
"Ignore": "Ignorera",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Periodisk skanning vid givet intervall och inaktiverad bevakning av ändringar",
"Periodic scanning at given interval and enabled watching for changes": "Periodisk skanning vid givet intervall och aktiverad bevakning av ändringar",
"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.",
@@ -284,6 +290,8 @@
"Sharing": "Delning",
"Show ID": "Visa ID",
"Show QR": "Visa QR",
"Show detailed discovery status": "Visa detaljerad upptäcktsstatus",
"Show detailed listener status": "Visa detaljerad lyssnarstatus",
"Show diff with previous version": "Visa skillnad med tidigare version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visas istället för enhets-ID i klusterstatus. Kommer att annonseras på andra enheter som ett valfritt standardnamn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visas istället för enhets-ID i klusterstatus. Uppdateras till namnet som enheten annonserar om den lämnas tom.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Jokertecken som representerar noll eller fler godtyckliga tecken i ett filnamn.",
"Size": "Storlek",
"Smallest First": "Minsta först",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Vissa upptäcktsmetoder kunde inte fastställas för att hitta andra enheter eller tillkännage denna enhet:",
"Some items could not be restored:": "Vissa objekt kunde inte återställas:",
"Some listening addresses could not be enabled to accept connections:": "Vissa lyssningsadresser kunde inte aktiveras för att acceptera anslutningar:",
"Source Code": "Källkod",
"Stable releases and release candidates": "Stabila utgåvor och utgåvskandidater",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabila utgåvor är försenade med cirka två veckor. Under denna tid går de igenom testning som utgåvskandidater.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing har stängts.",
"Syncthing includes the following software or portions thereof:": "Syncthing innehåller följande mjukvarupaket eller delar av dem:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing har fri och öppen källkod licensierad som MPL v2.0.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing lyssnar på följande nätverksadresser för anslutningsförsök från andra enheter:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing lyssnar inte efter anslutningsförsök från andra enheter på någon adress. Endast utgående anslutningar från denna enhet kanske fungerar.",
"Syncthing is restarting.": "Syncthing startar om.",
"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.",
@@ -334,6 +346,7 @@
"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.": "De följande intervallen används: varje 30 sekunder under den första timmen; varje timme under den första dagen; varje dag för de första 30 dagarna; varje vecka tills den maximala åldersgränsen uppnås.",
"The following items could not be synchronized.": "Följande objekt kunde inte synkroniseras.",
"The following items were changed locally.": "Följande objekt ändrades lokalt.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Följande metoder används för att upptäcka andra enheter i nätverket och meddela att denna enhet ska hittas av andra:",
"The following unexpected items were found.": "Följande oväntade objekt hittades.",
"The interval must be a positive number of seconds.": "Intervallet måste vara ett positivt antal sekunder.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Intervallet, i sekunder, för att rensa i versionskatalogen. Noll för att inaktivera periodisk rensning.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "De omprövas automatiskt och kommer att synkroniseras när felet är löst.",
"This Device": "Denna enhet",
"This can easily give hackers access to read and change any files on your computer.": "Detta kan lätt ge hackare tillgång till att läsa och ändra några filer på datorn.",
"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.": "Denna enhet kan inte automatiskt upptäcka andra enheter eller meddela sin egen adress som andra kan hitta. Endast enheter med statiskt konfigurerade adresser kan ansluta.",
"This is a major version upgrade.": "Det här är en stor uppgradering.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Denna inställning styr hur mycket ledigt utrymme som krävs på hemdisken (dvs. indexdatabasen).",
"Time": "Tid",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Sürümleri Temizleme",
"Cleanup Interval": "Aralığı Temizle",
"Click to see discovery failures": "Keşif hatalarını görmek için tıklayın",
"Click to see full identification string and QR code.": "Tam kimlik dizgisini ve QR kodunu görmek için tıklayın.",
"Close": "Kapat",
"Command": "Komut",
"Comment, when used at the start of a line": "Açıklama, bir satırın başında kullanıldığında",
@@ -93,6 +94,9 @@
"Discovered": "Keşfedildi",
"Discovery": "Keşif",
"Discovery Failures": "Keşif Hataları",
"Dismiss": "Yoksay",
"Do not add it to the ignore list, so this notification may recur.": "Yoksayma listesine eklemeyin, böylece bu bildirim tekrarlayabilir.",
"Do not add it to the ignore list, so this notification may recurr.": "Yoksayma listesine eklemeyin, böylece bu bildirim tekrarlayabilir.",
"Do not restore": "Geri yükleme yapma",
"Do not restore all": "Hiçbirini geri yükleme",
"Do you want to enable watching for changes for all your folders?": "Tüm klasörleriniz için değişiklikleri izlemeyi etkinleştirmek istiyor musunuz?",
@@ -153,6 +157,7 @@
"Help": "Yardım",
"Home page": "Ana Sayfa",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Ancak, şu anki ayarlarınız etkinleştirilmesini istemediğinizi gösterir. Sizin için otomatik çökme bildirmeyi etkisizleştirdik.",
"Identification": "Kimlik",
"If untrusted, enter encryption password": "Eğer güvenilmezse, şifreleme parolasını girin",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Eğer bu bilgisayardaki diğer kullanıcıların Syncthing'e ve onun aracılığıyla dosyalarınıza erişmesini önlemek istiyorsanız, kimlik doğrulamasını ayarlamayı düşünün.",
"Ignore": "Yoksay",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Belirli aralıklarla düzenli tarama ve değişiklikleri izleme etkisizleştirildi",
"Periodic scanning at given interval and enabled watching for changes": "Belirli aralıklarla düzenli tarama ve değişiklikleri izleme etkinleştirildi",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Belirli aralıklarla düzenli tarama ve değişiklikleri izlemeyi ayarlama başarısız oldu, her 1 dakikada yeniden deneniyor:",
"Permanently add it to the ignore list, suppressing further notifications.": "Diğer bildirimleri bastırarak, yoksayma listesine kalıcı olarak ekleyin.",
"Permissions": "İzinler",
"Please consult the release notes before performing a major upgrade.": "Büyük bir yükseltme gerçekleştirmeden önce lütfen yayım notlarına başvurun.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Lütfen Ayarlar ileti öğesinde GKA Kimlik Doğrulama Kullanıcısı ve Parolasını ayarlayın.",
@@ -284,6 +290,8 @@
"Sharing": "Paylaşma",
"Show ID": "Kimliği Göster",
"Show QR": "QR Göster",
"Show detailed discovery status": "Ayrıntılı keşif durumunu göster",
"Show detailed listener status": "Ayrıntılı dinleyici durumunu göster",
"Show diff with previous version": "Önceki sürüm ile farklılıkları göster",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Küme durumunda Cihaz Kimliği yerine gösterilir. İsteğe bağlı varsayılan ad olarak diğer cihazlara duyurulacaktır.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Küme durumunda Cihaz Kimliği yerine gösterilir. Boş bırakılırsa duyurulan cihaz adına güncellenecektir.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Tek seviyeli joker karakter (yalnızca bir dizin içinde eşleşir)",
"Size": "Boyut",
"Smallest First": "Önce En Küçük Olan",
"Some discovery methods could not be established for finding other devices or announcing this device:": "Bazı keşif yöntemleri, diğer cihazları bulmak veya bu cihazı duyurmak için kurulamadı:",
"Some items could not be restored:": "Bazı öğeler geri yüklenemedi:",
"Some listening addresses could not be enabled to accept connections:": "Bazı dinleme adresleri bağlantıları kabul etmek için etkinleştirilemedi:",
"Source Code": "Kaynak Kodu",
"Stable releases and release candidates": "Kararlı yayımlar ve yayım adayları",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Kararlı yayımlar yaklaşık iki hafta gecikir. Bu süre zarfında yayım adayları olarak denemelerden geçerler.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing kapatıldı.",
"Syncthing includes the following software or portions thereof:": "Syncthing aşağıdaki yazılımları veya bunların bölümlerini içermektedir:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing, MPL v2.0 ile lisanslanan Özgür ve Açık Kaynaklı Yazılım'dır.",
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing, diğer cihazlardan gelen bağlantı girişimleri için aşağıdaki ağ adreslerini dinliyor:",
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing, herhangi bir adresteki diğer cihazlardan gelen bağlantı girişimlerini dinlemiyor. Bu cihazdan yalnızca giden bağlantılar çalışabilir.",
"Syncthing is restarting.": "Syncthing yeniden başlatılıyor.",
"Syncthing is upgrading.": "Syncthing yükseltiliyor.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing artık çökmeleri geliştiricilere otomatik olarak bildirmeyi destekler. Bu özellik varsayılan olarak etkinleştirilmiştir.",
@@ -334,6 +346,7 @@
"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.": "Şu aralıklar kullanılır: ilk saat için her 30 saniyede bir sürüm tutulur, ilk gün için her saat bir sürüm tutulur, ilk 30 gün için her gün bir sürüm tutulur, en fazla yaşa kadar her hafta bir sürüm tutulur.",
"The following items could not be synchronized.": "Aşağıdaki öğeler eşitlenemedi.",
"The following items were changed locally.": "Aşağıdaki öğeler yerel olarak değiştirildi.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Aşağıdaki yöntemler, ağdaki diğer cihazları keşfetmek ve bu cihazı başkaları tarafından bulunacak şekilde duyurmak için kullanılır:",
"The following unexpected items were found.": "Aşağıdaki beklenmeyen öğeler bulundu.",
"The interval must be a positive number of seconds.": "Aralık, pozitif bir saniye sayısı olmak zorundadır.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Sürüm dizininde temizlemeyi çalıştırmak için saniye olarak aralık değeri. Düzenli temizliği etkisizleştirmek için sıfır.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Otomatik olarak yeniden denenirler ve hata çözüldüğünde eşitleneceklerdir.",
"This Device": "Bu Cihaz",
"This can easily give hackers access to read and change any files on your computer.": "Bu, bilgisayar korsanlarının bilgisayarınızdaki herhangi bir dosyayı okumasına ve değiştirmesine kolayca erişim sağlayabilir.",
"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.": "Bu cihaz diğer cihazları otomatik olarak keşfedemez veya başkaları tarafından bulunacak kendi adresini duyuramaz. Yalnızca sabit olarak yapılandırılmış adreslere sahip cihazlar bağlanabilir.",
"This is a major version upgrade.": "Bu büyük sürüm yükseltmesidir.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Bu ayar, ev (yani indeks veritabanı) diskindeki gereken boş alanı denetler.",
"Time": "Zaman",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "Очищення версій",
"Cleanup Interval": "Інтервал очищення",
"Click to see discovery failures": "Клікніть, щоб переглянути помилки виявлення",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "Закрити",
"Command": "Команда",
"Comment, when used at the start of a line": "Коментар, якщо використовується на початку рядка",
@@ -93,6 +94,9 @@
"Discovered": "Виявлено",
"Discovery": "Сервери координації NAT",
"Discovery Failures": "Помилки виявлення",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "Не відновлювати",
"Do not restore all": "Не відновлювати все",
"Do you want to enable watching for changes for all your folders?": "Бажаєте увімкнути стеження за змінами у всіх ваших папках?",
@@ -153,6 +157,7 @@
"Help": "Допомога",
"Home page": "Домашня сторінка",
"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": "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.": "Якщо ви хочете заборонити іншим користувачам цього комп’ютера отримувати доступ до Syncthing і через нього до своїх файлів, подумайте про налаштування автентифікації.",
"Ignore": "Ігнорувати",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "Періодичне сканування через визначений інтервал та відключене відстеження змін",
"Periodic scanning at given interval and enabled watching for changes": "Періодичне сканування через визначений інтервал та увімкнене відстеження змін",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Періодичне сканування через визначений інтервал та невдале відстеження змін, повторні спроби кожну 1 хв.:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "Дозволи",
"Please consult the release notes before performing a major upgrade.": "Будь ласка, перегляньте примітки до випуску перед мажорним оновленням. ",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Будь ласка, встановіть у налаштуваннях ім'я користувача та пароль до графічного інтерфейсу.",
@@ -284,6 +290,8 @@
"Sharing": "Спільне використання",
"Show ID": "Показати ID",
"Show QR": "Показати QR-код",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Показати відмінності від попередньої версії",
"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 пристрою в статусі кластера. Буде оновлено ім’ям, яке розголошене пристроєм, якщо залишити порожнім.",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "Однорівнева маска (пошук збігів лише в середині директорії) ",
"Size": "Розмір",
"Smallest First": "Спершу найменші",
"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:": "Деякі елементи не можуть бути відновлені: ",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Сирцевий код",
"Stable releases and release candidates": "Стабільні випуски та реліз-кандидати",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Стабільні випуски затримуються на два тижні. У цей час вони тестуються як реліз-кандидати.",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing вимкнено (закрито).",
"Syncthing includes the following software or portions thereof:": "Syncthing містить наступне програмне забезпечення (або його частини):",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing - це вільне та програмне забезпечення з відкритим кодом, ліцензоване як 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 перезавантажується.",
"Syncthing is upgrading.": "Syncthing оновлюється.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing тепер підтримує автоматичне звітування розобникам про збої. Ця функція увімкнена за умовчанням.",
@@ -334,6 +346,7 @@
"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 секунд, для першого дня версія зберігається щогодини, для перших 30 днів версія зберігається кожен день, опісля, до максимального строку, версія зберігається щотижня.",
"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 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.": "Інтервал, в секундах, для запуску очищення в директорії версій. Нуль для вимкнення періодичної чистки.",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "Вони будуть автоматично повторно синхронізовані, коли помилку буде усунено. ",
"This Device": "Локальний пристрій",
"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.": "Це оновлення мажорної версії",
"This setting controls the free space required on the home (i.e., index database) disk.": "Це налаштування визначає необхідний вільний простір на домашньому (тобто той, що містить базу даних) диску.",
"Time": "Час",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "清除版本",
"Cleanup Interval": "清除间隔",
"Click to see discovery failures": "点击查看设备发现错误",
"Click to see full identification string and QR code.": "点击查看完整的识别字符串和二维码。",
"Close": "关闭",
"Command": "命令",
"Comment, when used at the start of a line": "注释,在行首使用",
@@ -93,6 +94,9 @@
"Discovered": "已发现",
"Discovery": "设备发现",
"Discovery Failures": "设备发现错误",
"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 add it to the ignore list, so this notification may recurr.": "不将其添加到忽略列表中,该通知可能会重复出现。",
"Do not restore": "不要恢复",
"Do not restore all": "不要全部恢复",
"Do you want to enable watching for changes for all your folders?": "您想要启用对监视您所有文件夹的更改吗?",
@@ -153,6 +157,7 @@
"Help": "帮助",
"Home page": "主页",
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "我们已经为您关闭了自动崩溃报告发送功能,因为您当前的设置显示您可能并不想启用该功能。",
"Identification": "识别",
"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.": "如果要阻止此计算机上的其他用户访问Syncthing并通过它访问文件请考虑设置身份验证。",
"Ignore": "忽略",
@@ -225,11 +230,12 @@
"Periodic scanning at given interval and disabled watching for changes": "正以给定的间隔定期扫描并已禁用更改监视",
"Periodic scanning at given interval and enabled watching for changes": "正以给定的间隔定期扫描并已启用更改监视",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "正以给定的间隔定期扫描但设置更改监视失败,正在以每 1m 一次重试:",
"Permanently add it to the ignore list, suppressing further notifications.": "将其永久添加到忽略列表中,禁止进一步通知。",
"Permissions": "权限",
"Please consult the release notes before performing a major upgrade.": "请在进行重大更新前查看发布说明。",
"Please set a GUI Authentication User and Password in the Settings dialog.": "请在设置对话框中设置 GUI 验证用户及其密码。",
"Please wait": "请稍候",
"Prefix indicating that the file can be deleted if preventing directory removal": "表示如果删除了阻止目录则文件可被删除的前缀",
"Prefix indicating that the file can be deleted if preventing directory removal": "可以删除文件的前缀来防止目录删除",
"Prefix indicating that the pattern should be matched without case sensitivity": "此前缀表示,后面的模式在匹配时不区分大小写",
"Preparing to Sync": "准备同步",
"Preview": "预览",
@@ -284,6 +290,8 @@
"Sharing": "共享",
"Show ID": "显示 ID",
"Show QR": "显示 QR 码",
"Show detailed discovery status": "显示详细的发现状态",
"Show detailed listener status": "显示详细的监听器状态",
"Show diff with previous version": "显示与先前版本的差异",
"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。如果设置为空则会使用目标设备自报的默认名称。",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "单级通配符(仅匹配单层文件夹)",
"Size": "大小",
"Smallest First": "小文件优先",
"Some discovery methods could not be established for finding other devices or announcing this device:": "有些探索方法无法用于发现其它设备或广播该设备:",
"Some items could not be restored:": "有些项目无法被恢复:",
"Some listening addresses could not be enabled to accept connections:": "某些监听地址无法接受连接:",
"Source Code": "源代码",
"Stable releases and release candidates": "稳定版本和发布候选版",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "稳定版本约延迟两个星期。这段时间它们将作为发布候选版来测试。",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing 已关闭。",
"Syncthing includes the following software or portions thereof:": "Syncthing 使用了下列软件或其中的一部分:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing 是个以 MPL v2.0 授权的免费开源软件。",
"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 不会在任何地址上侦听来自其他设备的连接尝试。只有来自该设备的传出连接可能有效。",
"Syncthing is restarting.": "Syncthing 正在重启。",
"Syncthing is upgrading.": "Syncthing 正在升级。",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing 现在已经支持将崩溃报告自动发送给开发者。该功能默认开启。",
@@ -334,6 +346,7 @@
"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 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 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表示禁用定期清除。",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "系统将会自动重试,当错误被解决时,它们将会被同步。",
"This Device": "当前设备",
"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": "时间",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "清除版本",
"Cleanup Interval": "清除間隔",
"Click to see discovery failures": "點擊查看設備發現錯誤",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "關閉",
"Command": "命令",
"Comment, when used at the start of a line": "註釋,在行首使用",
@@ -93,6 +94,9 @@
"Discovered": "已發現",
"Discovery": "設備發現",
"Discovery Failures": "設備發現錯誤",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "不要恢復",
"Do not restore all": "不要全部恢復",
"Do you want to enable watching for changes for all your folders?": "您想要啟用對監視您所有文件夾的更改嗎?",
@@ -153,6 +157,7 @@
"Help": "幫助",
"Home page": "主頁",
"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": "如果不受信任,請輸入加密密碼",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "如果要阻止此計算機上的其他用戶訪問Syncthing並通過它訪問文件請考慮設置身份驗證。",
"Ignore": "忽略",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "正以給定的間隔定期掃瞄並已禁用更改監視",
"Periodic scanning at given interval and enabled watching for changes": "正以給定的間隔定期掃瞄並已啟用更改監視",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "正以給定的間隔定期掃瞄但設置更改監視失敗,正在以每 1m 一次重試:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "權限",
"Please consult the release notes before performing a major upgrade.": "請在進行重大更新前查看發佈說明。",
"Please set a GUI Authentication User and Password in the Settings dialog.": "請在設置對話框中設置 GUI 驗證用戶及其密碼。",
@@ -284,6 +290,8 @@
"Sharing": "共享",
"Show ID": "顯示 ID",
"Show QR": "顯示 QR 碼",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "顯示與先前版本的差異",
"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。如果設置為空則會使用目標設備自報的默認名稱。",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "單級通配符(僅匹配單層文件夾)",
"Size": "大小",
"Smallest First": "小文件優先",
"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:": "有些項目無法被恢復:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "源代碼",
"Stable releases and release candidates": "穩定版本和發佈候選版",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "穩定版本約延遲兩個星期。這段時間它們將作為發佈候選版來測試。",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing 已關閉。",
"Syncthing includes the following software or portions thereof:": "Syncthing 使用了下列軟件或其中的一部分:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing 是個以 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 正在重啟。",
"Syncthing is upgrading.": "Syncthing 正在升級。",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing 現在已經支持將崩潰報告自動發送給開發者。該功能默認開啟。",
@@ -334,6 +346,7 @@
"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 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表示禁用定期清除。",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "系統將會自動重試,當錯誤被解決時,它們將會被同步。",
"This Device": "當前設備",
"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.": "這是一個重大版本更新。",
"This setting controls the free space required on the home (i.e., index database) disk.": "此設置控制主(例如索引數據庫)磁盤上需要的可用空間。",
"Time": "時間",

View File

@@ -45,6 +45,7 @@
"Cleaning Versions": "正在清除歷史版本",
"Cleanup Interval": "清除間隔",
"Click to see discovery failures": "點擊以查閱失敗的探索",
"Click to see full identification string and QR code.": "Click to see full identification string and QR code.",
"Close": "關閉",
"Command": "指令",
"Comment, when used at the start of a line": "註解,當輸入在一行的開頭時",
@@ -93,6 +94,9 @@
"Discovered": "已發現",
"Discovery": "探索",
"Discovery Failures": "探索失敗",
"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 add it to the ignore list, so this notification may recurr.": "Do not add it to the ignore list, so this notification may recurr.",
"Do not restore": "不要還原",
"Do not restore all": "不要還原全部",
"Do you want to enable watching for changes for all your folders?": "您要對全部的資料夾啟用變動監視嗎?",
@@ -153,6 +157,7 @@
"Help": "說明",
"Home page": "首頁",
"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": "如未受信任,請輸入加密密碼",
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "如果您想防止在此電腦上的其他使用者存取 Syncthing 及其文件,請考慮設定身份驗證。",
"Ignore": "忽略",
@@ -225,6 +230,7 @@
"Periodic scanning at given interval and disabled watching for changes": "在一定的時間間隔,定期掃描及關閉觀察變動",
"Periodic scanning at given interval and enabled watching for changes": "在一定的時間間隔,定期掃描及啟用觀察變動",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "在一定的時間間隔,定期掃描,無法設定觀察變動,每 1 分鐘重試:",
"Permanently add it to the ignore list, suppressing further notifications.": "Permanently add it to the ignore list, suppressing further notifications.",
"Permissions": "權限",
"Please consult the release notes before performing a major upgrade.": "執行重大更新前請先參閱版本資訊。",
"Please set a GUI Authentication User and Password in the Settings dialog.": "請在設定對話框內設置 GUI 驗證使用者名稱及密碼。",
@@ -284,6 +290,8 @@
"Sharing": "正在共享",
"Show ID": "顯示識別碼",
"Show QR": "顯示 QR 碼",
"Show detailed discovery status": "Show detailed discovery status",
"Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "顯示與前一個版本的差異",
"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.": "代替裝置識別碼顯示在叢集狀態中。本欄若未填寫則將被更新為此裝置所廣播的名稱。",
@@ -293,7 +301,9 @@
"Single level wildcard (matches within a directory only)": "單階層萬用字元 (只在單個資料夾階層內比對)",
"Size": "大小",
"Smallest First": "最小的優先",
"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:": "有些項目無法被還原:",
"Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "原始碼",
"Stable releases and release candidates": "穩定版及候選發行版",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "穩定版大約延遲兩週發佈。這段期間將作為候選發行版來測試。",
@@ -310,6 +320,8 @@
"Syncthing has been shut down.": "Syncthing 已經關閉。",
"Syncthing includes the following software or portions thereof:": "Syncthing 包括以下軟體或其中的一部分:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing 為自由且開源授權條款為 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 正在重新啟動。",
"Syncthing is upgrading.": "Syncthing 正在進行升級。",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing 已支援將當機報告回傳至開發者。此功能預設為啟用。",
@@ -334,6 +346,7 @@
"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 秒保留一個版本,在第一天內每小時保留一個版本,在第 30 天內每一天保留一個版本,在達到最長保留時間前每一星期保留一個版本。",
"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 。",
@@ -351,6 +364,7 @@
"They are retried automatically and will be synced when the error is resolved.": "解決問題後,將會自動重試和同步。",
"This Device": "本機",
"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.": "這是一個重大版本更新。",
"This setting controls the free space required on the home (i.e., index database) disk.": "此設定控制家目錄(即:索引資料庫)的必須可用空間。",
"Time": "時間",

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","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)","ru":"Russian","sk":"Slovak","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","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","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","it","ja","ko-KR","lt","nb","nl","pl","pt-BR","pt-PT","ru","sk","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","it","ja","ko-KR","lt","nb","nl","pl","pt-BR","pt-PT","ro-RO","ru","sk","sv","tr","uk","zh-CN","zh-HK","zh-TW"]

View File

@@ -102,7 +102,7 @@
</a>
<ul class="dropdown-menu">
<li><a href="" ng-click="showSettings()"><span class="fas fa-fw fa-cog"></span>&nbsp;<span translate>Settings</span></a></li>
<li><a href="" data-toggle="modal" data-target="#idqr" ng-click="currentDevice=thisDevice()"><span class="fas fa-fw fa-qrcode"></span>&nbsp;<span translate>Show ID</span></a></li>
<li><a href="" ng-click="showDeviceIdentification(thisDevice())"><span class="fas fa-fw fa-qrcode"></span>&nbsp;<span translate>Show ID</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a href="" ng-click="shutdown()"><span class="fas fa-fw fa-power-off"></span>&nbsp;<span translate>Shutdown</span></a></li>
<li><a href="" ng-click="restart()"><span class="fas fa-fw fa-refresh"></span>&nbsp;<span translate>Restart</span></a></li>
@@ -209,10 +209,13 @@
</div>
<div class="panel-footer clearfix">
<div class="pull-right">
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingDevice(deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification may recur.' | translate}}">
<span class="far fa-clock"></span>&nbsp;<span translate>Dismiss</span>
</button>
<button type="button" class="btn btn-sm btn-success" ng-click="addDevice(deviceID, pendingDevice.name)">
<span class="fas fa-plus"></span>&nbsp;<span translate>Add Device</span>
</button>
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreDevice(deviceID, pendingDevice)">
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreDevice(deviceID, pendingDevice)" tooltip data-original-title="{{'Permanently add it to the ignore list, suppressing further notifications.' | translate}}">
<span class="fas fa-times"></span>&nbsp;<span translate>Ignore</span>
</button>
</div>
@@ -250,13 +253,16 @@
</div>
<div class="panel-footer clearfix">
<div class="pull-right">
<button type="button" class="btn btn-sm btn-default" ng-click="dismissPendingFolder(folderID, deviceID)" tooltip data-original-title="{{'Do not add it to the ignore list, so this notification may recur.' | translate}}">
<span class="far fa-clock"></span>&nbsp;<span translate>Dismiss</span>
</button>
<button type="button" class="btn btn-sm btn-success" ng-click="addFolderAndShare(folderID, pendingFolder, deviceID)" ng-if="!folders[folderID]">
<span class="fas fa-check"></span>&nbsp;<span translate>Add</span>
</button>
<button type="button" class="btn btn-sm btn-success" ng-click="shareFolderWithDevice(folderID, deviceID)" ng-if="folders[folderID]">
<span class="fas fa-check"></span>&nbsp;<span translate>Share</span>
</button>
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreFolder(deviceID, folderID, offeringDevice)">
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreFolder(deviceID, folderID, offeringDevice)" tooltip data-original-title="{{'Permanently add it to the ignore list, suppressing further notifications.' | translate}}">
<span class="fas fa-times"></span>&nbsp;<span translate>Ignore</span>
</button>
</div>
@@ -655,26 +661,20 @@
<tr>
<th><span class="fas fa-fw fa-sitemap"></span>&nbsp;<span translate>Listeners</span></th>
<td class="text-right">
<span ng-if="listenersFailed.length == 0" class="data text-success">
<span>{{listenersTotal}}/{{listenersTotal}}</span>
</span>
<span ng-if="listenersFailed.length != 0" class="data" ng-class="{'text-danger': listenersFailed.length == listenersTotal}">
<span popover data-trigger="hover" data-placement="bottom" data-html="true" data-content="{{listenersFailed.join('<br>\n')}}">
{{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
</span>
<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>
</span>
</td>
</tr>
<tr ng-if="system.discoveryEnabled">
<th><span class="fas fa-fw fa-map-signs"></span>&nbsp;<span translate>Discovery</span></th>
<td class="text-right">
<span ng-if="discoveryFailed.length == 0" class="data text-success">
<span>{{discoveryTotal}}/{{discoveryTotal}}</span>
</span>
<span ng-if="discoveryFailed.length != 0" class="data" ng-class="{'text-danger': discoveryFailed.length == discoveryTotal}">
<span popover data-trigger="hover" data-placement="bottom" data-content="{{'Click to see discovery failures' | translate}}.">
<a href="" style="color:inherit" ng-click="showDiscoveryFailures()">{{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}</a>
</span>
<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>
</span>
</td>
</tr>
@@ -682,6 +682,14 @@
<th><span class="far fa-fw fa-clock"></span>&nbsp;<span translate>Uptime</span></th>
<td class="text-right">{{system.uptime | duration:"m"}}</td>
</tr>
<tr>
<th><span class="fas fa-fw fa-qrcode"></span>&nbsp;<span translate>Identification</span></th>
<td class="text-right">
<span tooltip data-original-title="{{'Click to see full identification string and QR code.' | translate}}">
<a href="" ng-click="showDeviceIdentification(thisDevice())">{{deviceShortID(deviceCfg.deviceID)}}</a>
</span>
</td>
</tr>
<tr>
<th><span class="fas fa-fw fa-tag"></span>&nbsp;<span translate>Version</span></th>
<td class="text-right">
@@ -806,6 +814,14 @@
<th><span class="far fa-fw fa-handshake-o"></span>&nbsp;<span translate>Introduced By</span></th>
<td class="text-right">{{ deviceName(devices[deviceCfg.introducedBy]) || deviceCfg.introducedBy.substring(0, 5) }}</td>
</tr>
<tr>
<th><span class="fas fa-fw fa-qrcode"></span>&nbsp;<span translate>Identification</span></th>
<td class="text-right">
<span tooltip data-original-title="{{'Click to see full identification string and QR code.' | translate}}">
<a href="" ng-click="showDeviceIdentification(deviceCfg)">{{deviceShortID(deviceCfg.deviceID)}}</a>
</span>
</td>
</tr>
<tr ng-if="connections[deviceCfg.deviceID].clientVersion">
<th><span class="fas fa-fw fa-tag"></span>&nbsp;<span translate>Version</span></th>
<td class="text-right">{{connections[deviceCfg.deviceID].clientVersion}}</td>
@@ -905,7 +921,7 @@
<ng-include src="'syncthing/core/upgradeModalView.html'"></ng-include>
<ng-include src="'syncthing/core/majorUpgradeModalView.html'"></ng-include>
<ng-include src="'syncthing/core/aboutModalView.html'"></ng-include>
<ng-include src="'syncthing/core/discoveryFailuresModalView.html'"></ng-include>
<ng-include src="'syncthing/core/connectivityStatusModalView.html'"></ng-include>
<ng-include src="'syncthing/folder/removeFolderDialogView.html'"></ng-include>
<ng-include src="'syncthing/folder/revertOverrideView.html'"></ng-include>
<ng-include src="'syncthing/device/removeDeviceDialogView.html'"></ng-include>

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, Tomasz Wilczyński, Wulf Weich, dependabot-preview[bot], greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, 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, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, 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, 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, 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 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, 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, deepsource-autofix[bot], dependabot[bot], derekriemer, desbma, georgespatton, ghjklw, janost, jaseg, jelle van der Waa, klemens, marco-m, mclang, mv1005, otbutz, overkill, perewa, rubenbe, 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, dependabot-preview[bot], greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, 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, 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 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, 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 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, 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, deepsource-autofix[bot], dependabot[bot], derekriemer, desbma, georgespatton, ghjklw, janost, jaseg, jelle van der Waa, jtagcat, klemens, marco-m, mclang, mv1005, otbutz, overkill, perewa, rubenbe, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙
</div>
</div>
<hr />

View File

@@ -0,0 +1,60 @@
<modal id="connectivity-status" status="{{connectivityStatusParams.status}}" icon="fas fa-fw {{connectivityStatusParams.type == 'listeners' ? 'fa-sitemap' : 'fa-map-signs'}}" heading="{{connectivityStatusParams.heading}}" large="no" closeable="yes">
<div class="modal-body" ng-switch="connectivityStatusParams.type">
<div ng-switch-when="listeners">
<p translate ng-if="listenersRunning.length == 0">
Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.
</p>
<div ng-if="listenersRunning.length > 0">
<p translate>
Syncthing is listening on the following network addresses for connection attempts from other devices:
</p>
<ul>
<li ng-repeat="listener in listenersRunning">{{listener}}</li>
</ul>
</div>
<div ng-if="listenersFailed.length > 0">
<p translate>
Some listening addresses could not be enabled to accept connections:
</p>
<ul>
<li ng-repeat="listener in listenersFailed">{{listener}}</li>
</ul>
</div>
</div>
<div ng-switch-default><!-- discovery methods -->
<p translate ng-if="discoveryRunning.length == 0">
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.
</p>
<div ng-if="discoveryRunning.length > 0">
<p translate>
The following methods are used to discover other devices on the network and announce this device to be found by others:
</p>
<ul>
<li ng-repeat="discovery in discoveryRunning">{{discovery}}</li>
</ul>
</div>
<div ng-if="discoveryFailed.length > 0">
<p translate>
Some discovery methods could not be established for finding other devices or announcing this device:
</p>
<ul>
<li ng-repeat="discovery in discoveryFailed">{{discovery}}</li>
</ul>
</div>
<div class="row">
<div class="col-md-offset-2 col-md-8">
<div class="panel panel-default">
<div translate class="panel-body">
Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>
</div>
</modal>

View File

@@ -1,21 +0,0 @@
<modal id="discovery-failures" status="danger" icon="fas fa-exclamation-circle" heading="{{'Discovery Failures' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<ul>
<li ng-repeat="failure in discoveryFailed">{{failure}}</li>
</ul>
</div>
<div class="row">
<div class="col-md-offset-2 col-md-8">
<div class="panel panel-default">
<div translate class="panel-body">
Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>
</div>
</modal>

View File

@@ -109,46 +109,49 @@ angular.module('syncthing.core')
console.log('UIOnline');
refreshSystem();
refreshDiscoveryCache();
refreshConfig();
refreshCluster();
refreshConnectionStats();
refreshDeviceStats();
refreshFolderStats();
refreshGlobalChanges();
refreshThemes();
$http.get(urlbase + '/system/version').success(function (data) {
console.log("version", data);
if ($scope.version.version && $scope.version.version !== data.version) {
// We already have a version response, but it differs from
// the new one. Reload the full GUI in case it's changed.
document.location.reload(true);
}
$q.all([
refreshSystem(),
refreshDiscoveryCache(),
refreshConfig(),
refreshCluster(),
refreshConnectionStats(),
]).then(function() {
$http.get(urlbase + '/system/version').success(function (data) {
console.log("version", data);
if ($scope.version.version && $scope.version.version !== data.version) {
// We already have a version response, but it differs from
// the new one. Reload the full GUI in case it's changed.
document.location.reload(true);
}
$scope.version = data;
}).error($scope.emitHTTPError);
$scope.version = data;
}).error($scope.emitHTTPError);
$http.get(urlbase + '/svc/report').success(function (data) {
$scope.reportData = data;
if ($scope.system && $scope.config.options.urAccepted > -1 && $scope.config.options.urSeen < $scope.system.urVersionMax && $scope.config.options.urAccepted < $scope.system.urVersionMax) {
// Usage reporting format has changed, prompt the user to re-accept.
$('#ur').modal();
}
}).error($scope.emitHTTPError);
$http.get(urlbase + '/svc/report').success(function (data) {
$scope.reportData = data;
if ($scope.system && $scope.config.options.urAccepted > -1 && $scope.config.options.urSeen < $scope.system.urVersionMax && $scope.config.options.urAccepted < $scope.system.urVersionMax) {
// Usage reporting format has changed, prompt the user to re-accept.
$('#ur').modal();
}
}).error($scope.emitHTTPError);
$http.get(urlbase + '/system/upgrade').success(function (data) {
$scope.upgradeInfo = data;
}).error(function () {
$scope.upgradeInfo = null;
});
$http.get(urlbase + '/system/upgrade').success(function (data) {
$scope.upgradeInfo = data;
}).error(function () {
$scope.upgradeInfo = null;
});
online = true;
restarting = false;
$('#networkError').modal('hide');
$('#restarting').modal('hide');
$('#shutdown').modal('hide');
online = true;
restarting = false;
$('#networkError').modal('hide');
$('#restarting').modal('hide');
$('#shutdown').modal('hide');
}).catch($scope.emitHTTPError);
});
$scope.$on(Events.OFFLINE, function () {
@@ -462,7 +465,7 @@ angular.module('syncthing.core')
}
function refreshSystem() {
$http.get(urlbase + '/system/status').success(function (data) {
return $http.get(urlbase + '/system/status').success(function (data) {
$scope.myID = data.myID;
$scope.system = data;
@@ -471,22 +474,30 @@ angular.module('syncthing.core')
}
var listenersFailed = [];
var listenersRunning = [];
for (var address in data.connectionServiceStatus) {
if (data.connectionServiceStatus[address].error) {
listenersFailed.push(address + ": " + data.connectionServiceStatus[address].error);
} else {
listenersRunning.push(address);
}
}
$scope.listenersFailed = listenersFailed;
$scope.listenersRunning = listenersRunning;
$scope.listenersTotal = $scope.sizeOf(data.connectionServiceStatus);
$scope.discoveryTotal = data.discoveryMethods;
var discoveryFailed = [];
for (var disco in data.discoveryErrors) {
if (data.discoveryErrors[disco]) {
discoveryFailed.push(disco + ": " + data.discoveryErrors[disco]);
var discoveryRunning = [];
for (var disco in data.discoveryStatus) {
if (data.discoveryStatus[disco] && data.discoveryStatus[disco].error) {
discoveryFailed.push(disco + ": " + data.discoveryStatus[disco].error);
} else {
discoveryRunning.push(disco);
}
}
$scope.discoveryFailed = discoveryFailed;
$scope.discoveryRunning = discoveryRunning;
$scope.discoveryTotal = $scope.sizeOf(data.discoveryStatus);
refreshNoAuthWarning();
@@ -518,18 +529,20 @@ angular.module('syncthing.core')
}
function refreshCluster() {
$http.get(urlbase + '/cluster/pending/devices').success(function (data) {
$scope.pendingDevices = data;
console.log("refreshCluster devices", data);
}).error($scope.emitHTTPError);
$http.get(urlbase + '/cluster/pending/folders').success(function (data) {
$scope.pendingFolders = data;
console.log("refreshCluster folders", data);
}).error($scope.emitHTTPError);
return $q.all([
$http.get(urlbase + '/cluster/pending/devices').success(function (data) {
$scope.pendingDevices = data;
console.log("refreshCluster devices", data);
}).error($scope.emitHTTPError),
$http.get(urlbase + '/cluster/pending/folders').success(function (data) {
$scope.pendingFolders = data;
console.log("refreshCluster folders", data);
}).error($scope.emitHTTPError),
]);
}
function refreshDiscoveryCache() {
$http.get(urlbase + '/system/discovery').success(function (data) {
return $http.get(urlbase + '/system/discovery').success(function (data) {
for (var device in data) {
for (var i = 0; i < data[device].addresses.length; i++) {
// Relay addresses are URLs with
@@ -579,10 +592,10 @@ angular.module('syncthing.core')
$scope.completion[device]._needItems = items + deletes;
}
if (needed == 0 && deletes > 0) {
// We don't need any data, but we have deletes that we need
// to do. Drop down the completion percentage to indicate
// that we have stuff to do.
if (needed == 0 && deletes + items > 0 ) {
// We don't need any data, but we have deletes or
// dirs/links/empty files that we need to do. Drop down the
// completion percentage to indicate that we have stuff to do.
$scope.completion[device]._total = 95;
}
@@ -610,7 +623,7 @@ angular.module('syncthing.core')
}
function refreshConnectionStats() {
$http.get(urlbase + '/system/connections').success(function (data) {
return $http.get(urlbase + '/system/connections').success(function (data) {
var now = Date.now(),
td = (now - prevDate) / 1000,
id;
@@ -652,14 +665,15 @@ angular.module('syncthing.core')
}
function refreshConfig() {
$http.get(urlbase + '/config').success(function (data) {
updateLocalConfig(data);
console.log("refreshConfig", data);
}).error($scope.emitHTTPError);
$http.get(urlbase + '/config/insync').success(function (data) {
$scope.configInSync = data.configInSync;
}).error($scope.emitHTTPError);
return $q.all([
$http.get(urlbase + '/config').success(function (data) {
updateLocalConfig(data);
console.log("refreshConfig", data);
}),
$http.get(urlbase + '/config/insync').success(function (data) {
$scope.configInSync = data.configInSync;
}),
]);
}
$scope.refreshNeed = function (page, perpage) {
@@ -790,6 +804,14 @@ angular.module('syncthing.core')
});
}
$scope.pendingIsRemoteEncrypted = function(folderID, deviceID) {
var pending = $scope.pendingFolders[folderID];
if (!pending || !pending.offeredBy || !pending.offeredBy[deviceID]) {
return false;
}
return pending.offeredBy[deviceID].remoteEncrypted;
};
$scope.refreshFailed = function (page, perpage) {
if (!$scope.failed || !$scope.failed.folder) {
return;
@@ -1202,13 +1224,20 @@ angular.module('syncthing.core')
};
$scope.deviceName = function (deviceCfg) {
if (typeof deviceCfg === 'undefined' || typeof deviceCfg.deviceID === 'undefined') {
if (typeof deviceCfg === 'undefined') {
return "";
}
if (deviceCfg.name) {
return deviceCfg.name;
}
return deviceCfg.deviceID.substr(0, 6);
return $scope.deviceShortID(deviceCfg.deviceID);
};
$scope.deviceShortID = function (deviceID) {
if (typeof deviceID === 'undefined') {
return "";
}
return deviceID.substr(0, 6);
};
$scope.thisDeviceName = function () {
@@ -1222,6 +1251,11 @@ angular.module('syncthing.core')
return device.deviceID.substr(0, 6);
};
$scope.showDeviceIdentification = function (deviceCfg) {
$scope.currentDevice = deviceCfg;
$('#idqr').modal();
};
$scope.setDevicePause = function (device, pause) {
$scope.devices[device].paused = pause;
$scope.config.devices = $scope.deviceList();
@@ -1237,8 +1271,34 @@ angular.module('syncthing.core')
}
};
$scope.showDiscoveryFailures = function () {
$('#discovery-failures').modal();
$scope.showListenerStatus = function () {
var params = {
type: 'listeners',
};
if ($scope.listenersFailed.length > 0) {
params.status = 'danger';
params.heading = $translate.instant("Listener Failures");
} else {
params.status = 'default';
params.heading = $translate.instant("Listener Status");
}
$scope.connectivityStatusParams = params;
$('#connectivity-status').modal();
};
$scope.showDiscoveryStatus = function () {
var params = {
type: 'discovery',
};
if ($scope.discoveryFailed.length > 0) {
params.status = 'danger';
params.heading = $translate.instant("Discovery Failures");
} else {
params.status = 'default';
params.heading = $translate.instant("Discovery Status");
}
$scope.connectivityStatusParams = params;
$('#connectivity-status').modal();
};
$scope.logging = {
@@ -1367,16 +1427,11 @@ angular.module('syncthing.core')
'Content-Type': 'application/json'
}
};
$http.put(urlbase + '/config', cfg, opts).success(function () {
refreshConfig();
$http.put(urlbase + '/config', cfg, opts).finally(refreshConfig).then(function() {
if (callback) {
callback();
}
}).error(function (data, status, headers, config) {
refreshConfig();
$scope.emitHTTPError(data, status, headers, config);
});
}, $scope.emitHTTPError);
};
$scope.urVersions = function () {
@@ -1703,6 +1758,10 @@ angular.module('syncthing.core')
$scope.saveConfig();
};
$scope.dismissPendingDevice = function (deviceID) {
$http.delete(urlbase + '/cluster/pending/devices?device=' + encodeURIComponent(deviceID));
};
$scope.unignoreDeviceFromTemporaryConfig = function (ignoredDevice) {
$scope.tmpRemoteIgnoredDevices = $scope.tmpRemoteIgnoredDevices.filter(function (existingIgnoredDevice) {
return ignoredDevice.deviceID !== existingIgnoredDevice.deviceID;
@@ -1881,17 +1940,20 @@ angular.module('syncthing.core')
$('#globalChanges').modal();
};
function editFolderModal() {
function editFolderModal(initialTab) {
initVersioningEditing();
$scope.currentFolder._recvEnc = $scope.currentFolder.type === 'receiveencrypted';
$scope.folderPathErrors = {};
$scope.folderEditor.$setPristine();
if (!initialTab) {
initialTab = "#folder-general";
}
$('.nav-tabs a[href="' + initialTab + '"]').tab('show');
$('#editFolder').modal().one('shown.bs.tab', function (e) {
if (e.target.attributes.href.value === "#folder-ignores") {
$('#folder-ignores textarea').focus();
}
}).one('hidden.bs.modal', function () {
$('.nav-tabs a[href="#folder-general"]').tab('show');
window.location.hash = "";
$scope.currentFolder = {};
});
@@ -1920,7 +1982,7 @@ angular.module('syncthing.core')
return 'fas fa-folder';
};
function editFolder() {
function editFolder(initialTab) {
if ($scope.currentFolder.path.length > 1 && $scope.currentFolder.path.slice(-1) === $scope.system.pathSeparator) {
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
} else if (!$scope.currentFolder.path) {
@@ -1928,7 +1990,7 @@ angular.module('syncthing.core')
$scope.currentFolder.path = '';
}
initShareEditing('folder');
editFolderModal();
editFolderModal(initialTab);
}
$scope.internalVersioningEnabled = function(guiVersioning) {
@@ -1969,7 +2031,7 @@ angular.module('syncthing.core')
}
};
$scope.editFolderExisting = function(folderCfg) {
$scope.editFolderExisting = function(folderCfg, initialTab) {
$scope.editingExisting = true;
$scope.editingDefaults = false;
$scope.currentFolder = angular.copy(folderCfg);
@@ -1989,7 +2051,7 @@ angular.module('syncthing.core')
$scope.emitHTTPError(err);
});
editFolder();
editFolder(initialTab);
};
$scope.editFolderDefaults = function() {
@@ -2064,11 +2126,18 @@ angular.module('syncthing.core')
}
$scope.shareFolderWithDevice = function (folder, device) {
$scope.folders[folder].devices.push({
deviceID: device
});
$scope.config.folders = folderList($scope.folders);
$scope.saveConfig();
var folderCfg = $scope.folders[folder];
if (folderCfg.type == "receiveencrypted" || !$scope.pendingIsRemoteEncrypted(folder, device)) {
$scope.folders[folder].devices.push({
deviceID: device
});
$scope.config.folders = folderList($scope.folders);
$scope.saveConfig();
} else {
// Open edit folder dialog to enter encryption password
$scope.editFolderExisting(folderCfg, "#folder-sharing");
$scope.currentSharing.selected[device] = true;
}
};
$scope.saveFolder = function () {
@@ -2117,7 +2186,7 @@ angular.module('syncthing.core')
folderCfg.versioning.params.command = '' + folderCfg._guiVersioning.externalCommand;
break;
default:
delete folderCfg.versioning;
folderCfg.versioning = {type: ''};
}
delete folderCfg._guiVersioning;
@@ -2174,6 +2243,11 @@ angular.module('syncthing.core')
}
};
$scope.dismissPendingFolder = function (folderID, deviceID) {
$http.delete(urlbase + '/cluster/pending/folders?folder=' + encodeURIComponent(folderID)
+ '&device=' + encodeURIComponent(deviceID));
};
$scope.sharesFolder = function (folderCfg) {
var names = [];
folderCfg.devices.forEach(function (device) {

View File

@@ -11,7 +11,14 @@
<div ng-if="!editingDefaults" class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
<label translate for="deviceID">Device ID</label>
<div ng-if="!editingExisting">
<input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
<div class="input-group">
<input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
<div class="input-group-btn">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#idqr" ng-disabled="!deviceEditor.deviceID.$valid">
<span class="fas fa-qrcode"></span>&nbsp;<span translate>Show QR</span>
</button>
</div>
</div>
<datalist id="discovery-list">
<option ng-repeat="id in discovery" value="{{id}}" />
</datalist>
@@ -22,14 +29,21 @@
</ul>
</p>
<p class="help-block">
<span translate ng-if="deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine">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).</span>
<span translate ng-if="deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine">The device ID to enter here can be found in the "Actions &gt; Show ID" dialog on the other device. Spaces and dashes are optional (ignored).</span>
<span translate ng-show="deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine">When adding a new device, keep in mind that this device must be added on the other side too.</span>
<span translate ng-if="deviceEditor.deviceID.$error.required && deviceEditor.deviceID.$dirty">The device ID cannot be blank.</span>
<span translate ng-if="deviceEditor.deviceID.$error.validDeviceid && deviceEditor.deviceID.$dirty">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.</span>
<span translate ng-if="deviceEditor.deviceID.$error.unique && deviceEditor.deviceID.$dirty">A device with that ID is already added.</span>
</p>
</div>
<div ng-if="editingExisting" class="well well-sm text-monospace" select-on-click>{{currentDevice.deviceID}}</div>
<div ng-if="editingExisting" class="input-group">
<div class="well well-sm text-monospace form-control" style="height: auto;" select-on-click>{{currentDevice.deviceID}}</div>
<div class="input-group-btn">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#idqr">
<span class="fas fa-qrcode"></span>&nbsp;<span translate>Show QR</span>
</button>
</div>
</div>
</div>
<div class="form-group">
<label translate for="name">Device Name</label>
@@ -151,9 +165,6 @@
<button type="button" class="btn btn-primary btn-sm" ng-click="saveDevice()" ng-disabled="deviceEditor.$invalid">
<span class="fas fa-check"></span>&nbsp;<span translate>Save</span>
</button>
<button ng-if="!editingDefaults" type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#idqr" ng-if="editingExisting || deviceEditor.deviceID.$valid">
<span class="fas fa-qrcode"></span>&nbsp;<span translate>Show QR</span>
</button>
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>

View File

@@ -56,7 +56,7 @@
<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" />
<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)" />
</div>
</div>
<div class="form-horizontal" ng-if="currentSharing.unrelated.length || otherDevices().length <= 0">
@@ -70,7 +70,7 @@
<span translate>There are no devices to share this folder with.</span>
</p>
<div class="form-group" ng-repeat="device in currentSharing.unrelated" ng-init="id = device.deviceID; folder = currentFolder">
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{device.deviceID}}" label="{{deviceName(device)}}" folder-type="{{currentFolder.type}}" untrusted="device.untrusted" />
<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)" />
</div>
</div>
</div>
@@ -128,7 +128,7 @@
<div class="form-group" ng-if="currentFolder._guiVersioning.selector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}">
<p translate class="help-block">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.</p>
<label translate for="externalCommand">Command</label>
<textarea name="externalCommand" id="externalCommand" class="form-control" rows="1" ng-model="currentFolder._guiVersioning.externalCommand" required="" aria-required="true" />
<textarea name="externalCommand" id="externalCommand" class="form-control" rows="1" ng-model="currentFolder._guiVersioning.externalCommand" required="" aria-required="true"></textarea>
<p class="help-block">
<span translate ng-if="folderEditor.externalCommand.$valid || folderEditor.externalCommand.$pristine">See external versioning help for supported templated command line parameters.</span>
<span translate ng-if="folderEditor.externalCommand.$error.required && folderEditor.externalCommand.$dirty">The path cannot be blank.</span>

View File

@@ -9,10 +9,10 @@
<div class="panel-group" id="advancedAccordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="guiHeading" data-toggle="collapse" data-parent="#advancedAccordion" href="#guiConfig" aria-expanded="true" aria-controls="guiConfig" style="cursor: pointer;">
<div class="panel-heading" role="tab" id="guiHeading" data-toggle="collapse" data-parent="#advancedAccordion" href="#guiConfig" aria-expanded="false" aria-controls="guiConfig" style="cursor: pointer;">
<h4 class="panel-title" translate tabindex="0">GUI</h4>
</div>
<div id="guiConfig" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="guiHeading">
<div id="guiConfig" class="panel-collapse collapse" role="tabpanel" aria-labelledby="guiHeading">
<div class="panel-body">
<form class="form-horizontal" role="form">
<div ng-repeat="(key, value) in advancedConfig.gui" ng-init="type = inputTypeFor(key, value)" ng-if="type != 'skip'" class="form-group">

View File

@@ -291,6 +291,10 @@ func (s *service) Serve(ctx context.Context) error {
restMux.HandlerFunc(http.MethodPost, "/rest/system/resume", s.makeDevicePauseHandler(false)) // [device]
restMux.HandlerFunc(http.MethodPost, "/rest/system/debug", s.postSystemDebug) // [enable] [disable]
// The DELETE handlers
restMux.HandlerFunc(http.MethodDelete, "/rest/cluster/pending/devices", s.deletePendingDevices) // device
restMux.HandlerFunc(http.MethodDelete, "/rest/cluster/pending/folders", s.deletePendingFolders) // folder [device]
// Config endpoints
configBuilder := &configMuxBuilder{
@@ -632,6 +636,21 @@ func (s *service) getPendingDevices(w http.ResponseWriter, r *http.Request) {
sendJSON(w, devices)
}
func (s *service) deletePendingDevices(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query()
device := qs.Get("device")
deviceID, err := protocol.DeviceIDFromString(device)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := s.model.DismissPendingDevice(deviceID); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (s *service) getPendingFolders(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query()
@@ -650,6 +669,22 @@ func (s *service) getPendingFolders(w http.ResponseWriter, r *http.Request) {
sendJSON(w, folders)
}
func (s *service) deletePendingFolders(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query()
device := qs.Get("device")
deviceID, err := protocol.DeviceIDFromString(device)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
folderID := qs.Get("folder")
if err := s.model.DismissPendingFolder(deviceID, folderID); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (s *service) restPing(w http.ResponseWriter, r *http.Request) {
sendJSON(w, map[string]string{"ping": "pong"})
}
@@ -1023,16 +1058,16 @@ func (s *service) getSystemStatus(w http.ResponseWriter, r *http.Request) {
res["tilde"] = tilde
if s.cfg.Options().LocalAnnEnabled || s.cfg.Options().GlobalAnnEnabled {
res["discoveryEnabled"] = true
discoErrors := make(map[string]string)
discoMethods := 0
for disco, err := range s.discoverer.ChildErrors() {
discoMethods++
if err != nil {
discoErrors[disco] = err.Error()
discoStatus := s.discoverer.ChildErrors()
res["discoveryStatus"] = discoveryStatusMap(discoStatus)
res["discoveryMethods"] = len(discoStatus) // DEPRECATED: Redundant, only for backwards compatibility, should be removed.
discoErrors := make(map[string]*string, len(discoStatus))
for s, e := range discoStatus {
if e != nil {
discoErrors[s] = errorString(e)
}
}
res["discoveryMethods"] = discoMethods
res["discoveryErrors"] = discoErrors
res["discoveryErrors"] = discoErrors // DEPRECATED: Redundant, only for backwards compatibility, should be removed.
}
res["connectionServiceStatus"] = s.connectionsService.ListenerStatus()
@@ -1150,7 +1185,7 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
}
// Report Data as a JSON
if r, err := s.urService.ReportData(context.TODO()); err != nil {
if r, err := s.urService.ReportDataPreview(r.Context(), ur.Version); err != nil {
l.Warnln("Support bundle: failed to create usage-reporting.json.txt:", err)
} else {
if usageReportingData, err := json.MarshalIndent(r, "", " "); err != nil {
@@ -1905,6 +1940,20 @@ func errorString(err error) *string {
return nil
}
type discoveryStatusEntry struct {
Error *string `json:"error"`
}
func discoveryStatusMap(errs map[string]error) map[string]discoveryStatusEntry {
out := make(map[string]discoveryStatusEntry, len(errs))
for s, e := range errs {
out[s] = discoveryStatusEntry{
Error: errorString(e),
}
}
return out
}
// sanitizedHostname returns the given name in a suitable form for use as
// the common name in a certificate, or an error.
func sanitizedHostname(name string) (string, error) {

View File

@@ -17,6 +17,7 @@ import (
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
)
type configMuxBuilder struct {
@@ -63,10 +64,15 @@ func (c *configMuxBuilder) registerFolders(path string) {
})
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
var folders []config.FolderConfiguration
if err := unmarshalTo(r.Body, &folders); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
data, err := unmarshalToRawMessages(r.Body)
folders := make([]config.FolderConfiguration, len(data))
defaultFolder := c.cfg.DefaultFolder()
for i, bs := range data {
folders[i] = defaultFolder.Copy()
if err := json.Unmarshal(bs, &folders[i]); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
cfg.SetFolders(folders)
@@ -79,7 +85,7 @@ func (c *configMuxBuilder) registerFolders(path string) {
})
c.HandlerFunc(http.MethodPost, path, func(w http.ResponseWriter, r *http.Request) {
c.adjustFolder(w, r, config.FolderConfiguration{}, false)
c.adjustFolder(w, r, c.cfg.DefaultFolder(), false)
})
}
@@ -89,10 +95,15 @@ func (c *configMuxBuilder) registerDevices(path string) {
})
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
var devices []config.DeviceConfiguration
if err := unmarshalTo(r.Body, &devices); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
data, err := unmarshalToRawMessages(r.Body)
devices := make([]config.DeviceConfiguration, len(data))
defaultDevice := c.cfg.DefaultDevice()
for i, bs := range data {
devices[i] = defaultDevice.Copy()
if err := json.Unmarshal(bs, &devices[i]); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
cfg.SetDevices(devices)
@@ -105,19 +116,7 @@ func (c *configMuxBuilder) registerDevices(path string) {
})
c.HandlerFunc(http.MethodPost, path, func(w http.ResponseWriter, r *http.Request) {
var device config.DeviceConfiguration
if err := unmarshalTo(r.Body, &device); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
waiter, err := c.cfg.Modify(func(cfg *config.Configuration) {
cfg.SetDevice(device)
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
c.finish(w, waiter)
c.adjustDevice(w, r, c.cfg.DefaultDevice(), false)
})
}
@@ -132,7 +131,7 @@ func (c *configMuxBuilder) registerFolder(path string) {
})
c.Handle(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
c.adjustFolder(w, r, config.FolderConfiguration{}, false)
c.adjustFolder(w, r, c.cfg.DefaultFolder(), false)
})
c.Handle(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
@@ -176,7 +175,7 @@ func (c *configMuxBuilder) registerDevice(path string) {
})
c.Handle(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
c.adjustDevice(w, r, config.DeviceConfiguration{}, false)
c.adjustDevice(w, r, c.cfg.DefaultDevice(), false)
})
c.Handle(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
@@ -206,7 +205,9 @@ func (c *configMuxBuilder) registerDefaultFolder(path string) {
})
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
c.adjustFolder(w, r, config.FolderConfiguration{}, true)
var cfg config.FolderConfiguration
util.SetDefaults(&cfg)
c.adjustFolder(w, r, cfg, true)
})
c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) {
@@ -220,7 +221,9 @@ func (c *configMuxBuilder) registerDefaultDevice(path string) {
})
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
c.adjustDevice(w, r, config.DeviceConfiguration{}, true)
var cfg config.DeviceConfiguration
util.SetDefaults(&cfg)
c.adjustDevice(w, r, cfg, true)
})
c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) {
@@ -234,7 +237,9 @@ func (c *configMuxBuilder) registerOptions(path string) {
})
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
c.adjustOptions(w, r, config.OptionsConfiguration{})
var cfg config.OptionsConfiguration
util.SetDefaults(&cfg)
c.adjustOptions(w, r, cfg)
})
c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) {
@@ -248,7 +253,9 @@ func (c *configMuxBuilder) registerLDAP(path string) {
})
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
c.adjustLDAP(w, r, config.LDAPConfiguration{})
var cfg config.LDAPConfiguration
util.SetDefaults(&cfg)
c.adjustLDAP(w, r, cfg)
})
c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) {
@@ -262,7 +269,9 @@ func (c *configMuxBuilder) registerGUI(path string) {
})
c.HandlerFunc(http.MethodPut, path, func(w http.ResponseWriter, r *http.Request) {
c.adjustGUI(w, r, config.GUIConfiguration{})
var cfg config.GUIConfiguration
util.SetDefaults(&cfg)
c.adjustGUI(w, r, cfg)
})
c.HandlerFunc(http.MethodPatch, path, func(w http.ResponseWriter, r *http.Request) {
@@ -403,6 +412,12 @@ func unmarshalTo(body io.ReadCloser, to interface{}) error {
return json.Unmarshal(bs, to)
}
func unmarshalToRawMessages(body io.ReadCloser) ([]json.RawMessage, error) {
var data []json.RawMessage
err := unmarshalTo(body, &data)
return data, err
}
func checkGUIPassword(oldPassword, newPassword string) (string, error) {
if newPassword == oldPassword {
return newPassword, nil

View File

@@ -163,19 +163,44 @@ func ReadXML(r io.Reader, myID protocol.DeviceID) (Configuration, int, error) {
}
func ReadJSON(r io.Reader, myID protocol.DeviceID) (Configuration, error) {
var cfg Configuration
util.SetDefaults(&cfg)
bs, err := ioutil.ReadAll(r)
if err != nil {
return Configuration{}, err
}
var cfg Configuration
util.SetDefaults(&cfg)
if err := json.Unmarshal(bs, &cfg); err != nil {
return Configuration{}, err
}
// Unmarshal list of devices and folders separately to set defaults
var rawFoldersDevices struct {
Folders []json.RawMessage
Devices []json.RawMessage
}
if err := json.Unmarshal(bs, &rawFoldersDevices); err != nil {
return Configuration{}, err
}
cfg.Folders = make([]FolderConfiguration, len(rawFoldersDevices.Folders))
for i, bs := range rawFoldersDevices.Folders {
cfg.Folders[i] = cfg.Defaults.Folder.Copy()
if err := json.Unmarshal(bs, &cfg.Folders[i]); err != nil {
return Configuration{}, err
}
}
cfg.Devices = make([]DeviceConfiguration, len(rawFoldersDevices.Devices))
for i, bs := range rawFoldersDevices.Devices {
cfg.Devices[i] = cfg.Defaults.Device.Copy()
if err := json.Unmarshal(bs, &cfg.Devices[i]); err != nil {
return Configuration{}, err
}
}
if err := cfg.prepare(myID); err != nil {
return Configuration{}, err
}
@@ -433,13 +458,9 @@ func (cfg *Configuration) FolderMap() map[string]FolderConfiguration {
// folders that have an encryption password set.
func (cfg Configuration) FolderPasswords(device protocol.DeviceID) map[string]string {
res := make(map[string]string, len(cfg.Folders))
nextFolder:
for _, folder := range cfg.Folders {
for _, dev := range folder.Devices {
if dev.DeviceID == device && dev.EncryptionPassword != "" {
res[folder.ID] = dev.EncryptionPassword
continue nextFolder
}
if dev, ok := folder.Device(device); ok && dev.EncryptionPassword != "" {
res[folder.ID] = dev.EncryptionPassword
}
}
return res

View File

@@ -231,6 +231,71 @@ func TestConnectionStatus(t *testing.T) {
check(nil, nil)
}
func TestNextDialRegistryCleanup(t *testing.T) {
now := time.Now()
firsts := []time.Time{
now.Add(-dialCoolDownInterval + time.Second),
now.Add(-dialCoolDownDelay + time.Second),
now.Add(-2 * dialCoolDownDelay),
}
r := make(nextDialRegistry)
// Cases where the device should be cleaned up
r[protocol.LocalDeviceID] = nextDialDevice{}
r.sleepDurationAndCleanup(now)
if l := len(r); l > 0 {
t.Errorf("Expected empty to be cleaned up, got length %v", l)
}
for _, dev := range []nextDialDevice{
// attempts below threshold, outside of interval
{
attempts: 1,
coolDownIntervalStart: firsts[1],
},
{
attempts: 1,
coolDownIntervalStart: firsts[2],
},
// Threshold reached, but outside of cooldown delay
{
attempts: dialCoolDownMaxAttemps,
coolDownIntervalStart: firsts[2],
},
} {
r[protocol.LocalDeviceID] = dev
r.sleepDurationAndCleanup(now)
if l := len(r); l > 0 {
t.Errorf("attempts: %v, start: %v: Expected all cleaned up, got length %v", dev.attempts, dev.coolDownIntervalStart, l)
}
}
// Cases where the device should stay monitored
for _, dev := range []nextDialDevice{
// attempts below threshold, inside of interval
{
attempts: 1,
coolDownIntervalStart: firsts[0],
},
// attempts at threshold, inside delay
{
attempts: dialCoolDownMaxAttemps,
coolDownIntervalStart: firsts[0],
},
{
attempts: dialCoolDownMaxAttemps,
coolDownIntervalStart: firsts[1],
},
} {
r[protocol.LocalDeviceID] = dev
r.sleepDurationAndCleanup(now)
if l := len(r); l != 1 {
t.Errorf("attempts: %v, start: %v: Expected device still tracked, got length %v", dev.attempts, dev.coolDownIntervalStart, l)
}
}
}
func BenchmarkConnections(pb *testing.B) {
addrs := []string{
"tcp://127.0.0.1:0",

View File

@@ -45,7 +45,9 @@ type quicDialer struct {
func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL) (internalConn, error) {
uri = fixupPort(uri, config.DefaultQUICPort)
addr, err := net.ResolveUDPAddr("udp", uri.Host)
network := quicNetwork(uri)
addr, err := net.ResolveUDPAddr(network, uri.Host)
if err != nil {
return internalConn{}, err
}

View File

@@ -13,7 +13,6 @@ import (
"crypto/tls"
"net"
"net/url"
"strings"
"sync"
"sync/atomic"
"syscall"
@@ -81,7 +80,7 @@ func (t *quicListener) OnExternalAddressChanged(address *stun.Host, via string)
}
func (t *quicListener) serve(ctx context.Context) error {
network := strings.ReplaceAll(t.uri.Scheme, "quic", "udp")
network := quicNetwork(t.uri)
udpAddr, err := net.ResolveUDPAddr(network, t.uri.Host)
if err != nil {
@@ -204,7 +203,7 @@ func (t *quicListener) LANAddresses() []*url.URL {
uri := maybeReplacePort(t.uri, t.laddr)
t.mut.Unlock()
addrs := []*url.URL{uri}
network := strings.ReplaceAll(uri.Scheme, "quic", "udp")
network := quicNetwork(uri)
addrs = append(addrs, getURLsForAllAdaptersIfUnspecified(network, uri)...)
return addrs
}

View File

@@ -11,6 +11,7 @@ package connections
import (
"crypto/tls"
"net"
"net/url"
"github.com/lucas-clemente/quic-go"
"github.com/syncthing/syncthing/lib/util"
@@ -23,6 +24,17 @@ var (
}
)
func quicNetwork(uri *url.URL) string {
switch uri.Scheme {
case "quic4":
return "udp4"
case "quic6":
return "udp6"
default:
return "udp"
}
}
type quicTlsConn struct {
quic.Session
quic.Stream

View File

@@ -66,6 +66,8 @@ const (
worstDialerPriority = math.MaxInt32
recentlySeenCutoff = 7 * 24 * time.Hour
shortLivedConnectionThreshold = 5 * time.Second
dialMaxParallel = 64
dialMaxParallelPerDevice = 8
)
// From go/src/crypto/tls/cipher_suites.go
@@ -142,10 +144,13 @@ type service struct {
natService *nat.Service
evLogger events.Logger
deviceAddressesChanged chan struct{}
listenersMut sync.RWMutex
listeners map[string]genericListener
listenerTokens map[string]suture.ServiceToken
dialNow chan struct{}
dialNowDevices map[protocol.DeviceID]struct{}
dialNowDevicesMut sync.Mutex
listenersMut sync.RWMutex
listeners map[string]genericListener
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 {
@@ -166,10 +171,13 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t
natService: nat.NewService(myID, cfg),
evLogger: evLogger,
deviceAddressesChanged: make(chan struct{}, 1),
listenersMut: sync.NewRWMutex(),
listeners: make(map[string]genericListener),
listenerTokens: make(map[string]suture.ServiceToken),
dialNowDevicesMut: sync.NewMutex(),
dialNow: make(chan struct{}, 1),
dialNowDevices: make(map[protocol.DeviceID]struct{}),
listenersMut: sync.NewRWMutex(),
listeners: make(map[string]genericListener),
listenerTokens: make(map[string]suture.ServiceToken),
}
cfg.Subscribe(service)
@@ -324,6 +332,13 @@ func (s *service) handle(ctx context.Context) error {
rd, wr := s.limiter.getLimiters(remoteID, c, isLAN)
protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression, s.cfg.FolderPasswords(remoteID))
go func() {
<-protoConn.Closed()
s.dialNowDevicesMut.Lock()
s.dialNowDevices[remoteID] = struct{}{}
s.scheduleDialNow()
s.dialNowDevicesMut.Unlock()
}()
l.Infof("Established secure connection to %s at %s", remoteID, c)
@@ -334,7 +349,7 @@ func (s *service) handle(ctx context.Context) error {
func (s *service) connect(ctx context.Context) error {
// Map of when to earliest dial each given device + address again
nextDialAt := make(map[string]time.Time)
nextDialAt := make(nextDialRegistry)
// Used as delay for the first few connection attempts (adjusted up to
// minConnectionLoopSleep), increased exponentially until it reaches
@@ -369,7 +384,7 @@ func (s *service) connect(ctx context.Context) error {
// The sleep time is until the next dial scheduled in nextDialAt,
// clamped by stdConnectionLoopSleep as we don't want to sleep too
// long (config changes might happen).
sleep = filterAndFindSleepDuration(nextDialAt, now)
sleep = nextDialAt.sleepDurationAndCleanup(now)
}
// ... while making sure not to loop too quickly either.
@@ -379,9 +394,20 @@ func (s *service) connect(ctx context.Context) error {
l.Debugln("Next connection loop in", sleep)
timeout := time.NewTimer(sleep)
select {
case <-s.deviceAddressesChanged:
case <-time.After(sleep):
case <-s.dialNow:
// Remove affected devices from nextDialAt to dial immediately,
// regardless of when we last dialed it (there's cool down in the
// registry for too many repeat dials).
s.dialNowDevicesMut.Lock()
for device := range s.dialNowDevices {
nextDialAt.redialDevice(device, now)
}
s.dialNowDevices = make(map[protocol.DeviceID]struct{})
s.dialNowDevicesMut.Unlock()
timeout.Stop()
case <-timeout.C:
case <-ctx.Done():
return ctx.Err()
}
@@ -401,7 +427,7 @@ func (s *service) bestDialerPriority(cfg config.Configuration) int {
return bestDialerPriority
}
func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Configuration, bestDialerPriority int, nextDialAt map[string]time.Time, initial bool) {
func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Configuration, bestDialerPriority int, nextDialAt nextDialRegistry, initial bool) {
// Figure out current connection limits up front to see if there's any
// point in resolving devices and such at all.
allowAdditional := 0 // no limit
@@ -466,18 +492,44 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
// Perform dials according to the queue, stopping when we've reached the
// allowed additional number of connections (if limited).
numConns := 0
for _, entry := range queue {
if conn, ok := s.dialParallel(ctx, entry.id, entry.targets); ok {
s.conns <- conn
numConns++
if allowAdditional > 0 && numConns >= allowAdditional {
break
}
var numConnsMut stdsync.Mutex
dialSemaphore := util.NewSemaphore(dialMaxParallel)
dialWG := new(stdsync.WaitGroup)
dialCtx, dialCancel := context.WithCancel(ctx)
defer func() {
dialWG.Wait()
dialCancel()
}()
for i := range queue {
select {
case <-dialCtx.Done():
return
default:
}
dialWG.Add(1)
go func(entry dialQueueEntry) {
defer dialWG.Done()
conn, ok := s.dialParallel(dialCtx, entry.id, entry.targets, dialSemaphore)
if !ok {
return
}
numConnsMut.Lock()
if allowAdditional == 0 || numConns < allowAdditional {
select {
case s.conns <- conn:
numConns++
if allowAdditional > 0 && numConns >= allowAdditional {
dialCancel()
}
case <-dialCtx.Done():
}
}
numConnsMut.Unlock()
}(queue[i])
}
}
func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg config.Configuration, deviceCfg config.DeviceConfiguration, nextDialAt map[string]time.Time, initial bool, priorityCutoff int) []dialTarget {
func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg config.Configuration, deviceCfg config.DeviceConfiguration, nextDialAt nextDialRegistry, initial bool, priorityCutoff int) []dialTarget {
deviceID := deviceCfg.DeviceID
addrs := s.resolveDeviceAddrs(ctx, deviceCfg)
@@ -485,18 +537,16 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
dialTargets := make([]dialTarget, 0, len(addrs))
for _, addr := range addrs {
// Use a special key that is more than just the address, as you
// might have two devices connected to the same relay
nextDialKey := deviceID.String() + "/" + addr
when, ok := nextDialAt[nextDialKey]
if ok && !initial && when.After(now) {
// Use both device and address, as you might have two devices connected
// to the same relay
if !initial && nextDialAt.get(deviceID, addr).After(now) {
l.Debugf("Not dialing %s via %v as it's not time yet", deviceID, addr)
continue
}
// If we fail at any step before actually getting the dialer
// retry in a minute
nextDialAt[nextDialKey] = now.Add(time.Minute)
nextDialAt.set(deviceID, addr, now.Add(time.Minute))
uri, err := url.Parse(addr)
if err != nil {
@@ -532,7 +582,7 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
}
dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg)
nextDialAt[nextDialKey] = now.Add(dialer.RedialFrequency())
nextDialAt.set(deviceID, addr, now.Add(dialer.RedialFrequency()))
// For LAN addresses, increase the priority so that we
// try these first.
@@ -735,21 +785,31 @@ func (s *service) CommitConfiguration(from, to config.Configuration) bool {
}
func (s *service) checkAndSignalConnectLoopOnUpdatedDevices(from, to config.Configuration) {
oldDevices := make(map[protocol.DeviceID]config.DeviceConfiguration, len(from.Devices))
for _, dev := range from.Devices {
oldDevices[dev.DeviceID] = dev
}
oldDevices := from.DeviceMap()
dial := false
s.dialNowDevicesMut.Lock()
for _, dev := range to.Devices {
oldDev, ok := oldDevices[dev.DeviceID]
if !ok || !util.EqualStrings(oldDev.Addresses, dev.Addresses) {
select {
case s.deviceAddressesChanged <- struct{}{}:
default:
// channel is blocked - a config update is already pending for the connection loop.
}
break
if dev.Paused {
continue
}
if oldDev, ok := oldDevices[dev.DeviceID]; !ok || oldDev.Paused {
s.dialNowDevices[dev.DeviceID] = struct{}{}
dial = true
} else if !util.EqualStrings(oldDev.Addresses, dev.Addresses) {
dial = true
}
}
if dial {
s.scheduleDialNow()
}
s.dialNowDevicesMut.Unlock()
}
func (s *service) scheduleDialNow() {
select {
case s.dialNow <- struct{}{}:
default:
// channel is blocked - a config update is already pending for the connection loop.
}
}
@@ -877,21 +937,6 @@ func getListenerFactory(cfg config.Configuration, uri *url.URL) (listenerFactory
return listenerFactory, nil
}
func filterAndFindSleepDuration(nextDialAt map[string]time.Time, now time.Time) time.Duration {
sleep := stdConnectionLoopSleep
for key, next := range nextDialAt {
if next.Before(now) {
// Expired entry, address was not seen in last pass(es)
delete(nextDialAt, key)
continue
}
if cur := next.Sub(now); cur < sleep {
sleep = cur
}
}
return sleep
}
func urlsToStrings(urls []*url.URL) []string {
strings := make([]string, len(urls))
for i, url := range urls {
@@ -952,7 +997,7 @@ func IsAllowedNetwork(host string, allowed []string) bool {
return false
}
func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID, dialTargets []dialTarget) (internalConn, bool) {
func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID, dialTargets []dialTarget, parentSema *util.Semaphore) (internalConn, bool) {
// Group targets into buckets by priority
dialTargetBuckets := make(map[int][]dialTarget, len(dialTargets))
for _, tgt := range dialTargets {
@@ -968,13 +1013,19 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID,
// Sort the priorities so that we dial lowest first (which means highest...)
sort.Ints(priorities)
sema := util.MultiSemaphore{util.NewSemaphore(dialMaxParallelPerDevice), parentSema}
for _, prio := range priorities {
tgts := dialTargetBuckets[prio]
res := make(chan internalConn, len(tgts))
wg := stdsync.WaitGroup{}
for _, tgt := range tgts {
sema.Take(1)
wg.Add(1)
go func(tgt dialTarget) {
defer func() {
wg.Done()
sema.Give(1)
}()
conn, err := tgt.Dial(ctx)
if err == nil {
// Closes the connection on error
@@ -987,7 +1038,6 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID,
l.Debugln("dialing", deviceID, tgt.uri, "success:", conn)
res <- conn
}
wg.Done()
}(tgt)
}
@@ -1050,3 +1100,90 @@ func (s *service) validateIdentity(c internalConn, expectedID protocol.DeviceID)
return nil
}
type nextDialRegistry map[protocol.DeviceID]nextDialDevice
type nextDialDevice struct {
nextDial map[string]time.Time
coolDownIntervalStart time.Time
attempts int
}
func (r nextDialRegistry) get(device protocol.DeviceID, addr string) time.Time {
return r[device].nextDial[addr]
}
const (
dialCoolDownInterval = 2 * time.Minute
dialCoolDownDelay = 5 * time.Minute
dialCoolDownMaxAttemps = 3
)
// redialDevice marks the device for immediate redial, unless the remote keeps
// dropping established connections. Thus we keep track of when the first forced
// re-dial happened, and how many attempts happen in the dialCoolDownInterval
// after that. If it's more than dialCoolDownMaxAttempts, don't force-redial
// that device for dialCoolDownDelay (regular dials still happen).
func (r nextDialRegistry) redialDevice(device protocol.DeviceID, now time.Time) {
dev, ok := r[device]
if !ok {
r[device] = nextDialDevice{
nextDial: make(map[string]time.Time),
coolDownIntervalStart: now,
attempts: 1,
}
return
}
if dev.attempts == 0 || now.Before(dev.coolDownIntervalStart.Add(dialCoolDownInterval)) {
if dev.attempts >= dialCoolDownMaxAttemps {
// Device has been force redialed too often - let it cool down.
return
}
if dev.attempts == 0 {
dev.coolDownIntervalStart = now
}
dev.attempts++
dev.nextDial = make(map[string]time.Time)
return
}
if dev.attempts >= dialCoolDownMaxAttemps && now.Before(dev.coolDownIntervalStart.Add(dialCoolDownDelay)) {
return // Still cooling down
}
delete(r, device)
}
func (r nextDialRegistry) set(device protocol.DeviceID, addr string, next time.Time) {
if _, ok := r[device]; !ok {
r[device] = nextDialDevice{nextDial: make(map[string]time.Time)}
}
r[device].nextDial[addr] = next
}
func (r nextDialRegistry) sleepDurationAndCleanup(now time.Time) time.Duration {
sleep := stdConnectionLoopSleep
for id, dev := range r {
for address, next := range dev.nextDial {
if next.Before(now) {
// Expired entry, address was not seen in last pass(es)
delete(dev.nextDial, address)
continue
}
if cur := next.Sub(now); cur < sleep {
sleep = cur
}
}
if dev.attempts > 0 {
interval := dialCoolDownInterval
if dev.attempts >= dialCoolDownMaxAttemps {
interval = dialCoolDownDelay
}
if now.After(dev.coolDownIntervalStart.Add(interval)) {
dev.attempts = 0
}
}
if len(dev.nextDial) == 0 && dev.attempts == 0 {
delete(r, id)
}
}
return sleep
}

View File

@@ -70,6 +70,9 @@ type Lowlevel struct {
recheckInterval time.Duration
oneFileSetCreated chan struct{}
evLogger events.Logger
blockFilter *bloomFilter
versionFilter *bloomFilter
}
func NewLowlevel(backend backend.Backend, evLogger events.Logger, opts ...Option) (*Lowlevel, error) {
@@ -686,29 +689,30 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) {
// Indirection GC needs to run when there are no modifications to the
// FileInfos or indirected items.
db.gcMut.Lock()
defer db.gcMut.Unlock()
l.Debugln("Starting database GC")
l.Debugln("Started database GC")
// Create a new set of bloom filters, while holding the gcMut which
// guarantees that no other modifications are happening concurrently.
db.gcMut.Lock()
capacity := indirectGCBloomCapacity
if db.gcKeyCount > capacity {
capacity = db.gcKeyCount
}
db.blockFilter = newBloomFilter(capacity)
db.versionFilter = newBloomFilter(capacity)
db.gcMut.Unlock()
defer func() {
// Forget the bloom filters on the way out.
db.gcMut.Lock()
db.blockFilter = nil
db.versionFilter = nil
db.gcMut.Unlock()
}()
var discardedBlocks, matchedBlocks, discardedVersions, matchedVersions int
internalCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
// Only print something if the process takes more than "a moment".
select {
case <-internalCtx.Done():
case <-time.After(10 * time.Second):
l.Infoln("Database GC started - many Syncthing operations will be unresponsive until it's finished")
<-internalCtx.Done()
if err != nil || ctx.Err() != nil {
return
}
l.Infof("Database GC done (discarded/remaining: %v/%v blocks, %v/%v versions)", discardedBlocks, matchedBlocks, discardedVersions, matchedVersions)
}
}()
t, err := db.newReadWriteTransaction()
if err != nil {
return err
@@ -720,16 +724,13 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) {
// items. For simplicity's sake we track just one count, which is the
// highest of the various indirected items.
capacity := indirectGCBloomCapacity
if db.gcKeyCount > capacity {
capacity = db.gcKeyCount
}
blockFilter := newBloomFilter(capacity)
versionFilter := newBloomFilter(capacity)
// Iterate the FileInfos, unmarshal the block and version hashes and
// add them to the filter.
// This happens concurrently with normal database modifications, though
// those modifications will now also add their blocks and versions to
// the bloom filters.
it, err := t.NewPrefixIterator([]byte{KeyTypeDevice})
if err != nil {
return err
@@ -746,18 +747,35 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) {
if err := hashes.Unmarshal(it.Value()); err != nil {
return err
}
if len(hashes.BlocksHash) > 0 {
blockFilter.add(hashes.BlocksHash)
}
if len(hashes.VersionHash) > 0 {
versionFilter.add(hashes.VersionHash)
}
db.recordIndirectionHashes(hashes)
}
it.Release()
if err := it.Error(); err != nil {
return err
}
// For the next phase we grab the GC lock again and hold it for the rest
// of the method call. Now there can't be any further modifications to
// the database or the bloom filters.
db.gcMut.Lock()
defer db.gcMut.Unlock()
// Only print something if the process takes more than "a moment".
logWait := make(chan struct{})
logTimer := time.AfterFunc(10*time.Second, func() {
l.Infoln("Database GC in progress - many Syncthing operations will be unresponsive until it's finished")
close(logWait)
})
defer func() {
if logTimer.Stop() {
return
}
<-logWait // Make sure messages are sent in order.
l.Infof("Database GC complete (discarded/remaining: %v/%v blocks, %v/%v versions)",
discardedBlocks, matchedBlocks, discardedVersions, matchedVersions)
}()
// Iterate over block lists, removing keys with hashes that don't match
// the filter.
@@ -774,7 +792,7 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) {
}
key := blockListKey(it.Key())
if blockFilter.has(key.Hash()) {
if db.blockFilter.has(key.Hash()) {
matchedBlocks++
continue
}
@@ -803,7 +821,7 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) {
}
key := versionKey(it.Key())
if versionFilter.has(key.Hash()) {
if db.versionFilter.has(key.Hash()) {
matchedVersions++
continue
}
@@ -827,17 +845,31 @@ func (db *Lowlevel) gcIndirect(ctx context.Context) (err error) {
return err
}
l.Debugf("Finished GC, starting compaction (discarded/remaining: %v/%v blocks, %v/%v versions)", discardedBlocks, matchedBlocks, discardedVersions, matchedVersions)
l.Debugf("Finished GC (discarded/remaining: %v/%v blocks, %v/%v versions)", discardedBlocks, matchedBlocks, discardedVersions, matchedVersions)
return db.Compact()
return nil
}
func newBloomFilter(capacity int) bloomFilter {
func (db *Lowlevel) recordIndirectionHashesForFile(f *protocol.FileInfo) {
db.recordIndirectionHashes(IndirectionHashesOnly{BlocksHash: f.BlocksHash, VersionHash: f.VersionHash})
}
func (db *Lowlevel) recordIndirectionHashes(hs IndirectionHashesOnly) {
// must be called with gcMut held (at least read-held)
if db.blockFilter != nil && len(hs.BlocksHash) > 0 {
db.blockFilter.add(hs.BlocksHash)
}
if db.versionFilter != nil && len(hs.VersionHash) > 0 {
db.versionFilter.add(hs.VersionHash)
}
}
func newBloomFilter(capacity int) *bloomFilter {
var buf [16]byte
io.ReadFull(rand.Reader, buf[:])
return bloomFilter{
f: blobloom.NewOptimized(blobloom.Config{
return &bloomFilter{
f: blobloom.NewSyncOptimized(blobloom.Config{
Capacity: uint64(capacity),
FPRate: indirectGCBloomFalsePositiveRate,
MaxBits: 8 * indirectGCBloomMaxBytes,
@@ -849,7 +881,7 @@ func newBloomFilter(capacity int) bloomFilter {
}
type bloomFilter struct {
f *blobloom.Filter
f *blobloom.SyncFilter
k0, k1 uint64 // Random key for SipHash.
}
@@ -903,6 +935,8 @@ func (db *Lowlevel) getMetaAndCheckGCLocked(folder string) (*metadataTracker, er
l.Infof("Repaired %d global entries for folder %v in database", fixed, folder)
}
oldMeta := newMetadataTracker(db.keyer, db.evLogger)
_ = oldMeta.fromDB(db, []byte(folder)) // Ignore error, it leads to index id reset too
meta, err := db.recalcMeta(folder)
if err != nil {
return nil, fmt.Errorf("recalculating metadata: %w", err)
@@ -920,6 +954,10 @@ func (db *Lowlevel) getMetaAndCheckGCLocked(folder string) (*metadataTracker, er
}
}
if err := db.checkSequencesUnchanged(folder, oldMeta, meta); err != nil {
return nil, fmt.Errorf("checking for changed sequences: %w", err)
}
return meta, nil
}
@@ -930,7 +968,6 @@ func (db *Lowlevel) loadMetadataTracker(folder string) (*metadataTracker, error)
l.Infof("Stored folder metadata for %q is inconsistent; recalculating", folder)
} else {
l.Infof("No stored folder metadata for %q; recalculating", folder)
}
return db.getMetaAndCheck(folder)
}
@@ -1249,6 +1286,62 @@ func (db *Lowlevel) checkLocalNeed(folder []byte) (int, error) {
return repaired, nil
}
// checkSequencesUnchanged resets delta indexes for any device where the
// sequence changed.
func (db *Lowlevel) checkSequencesUnchanged(folder string, oldMeta, meta *metadataTracker) error {
t, err := db.newReadWriteTransaction()
if err != nil {
return err
}
defer t.close()
var key []byte
deleteIndexID := func(devID protocol.DeviceID) error {
key, err = db.keyer.GenerateIndexIDKey(key, devID[:], []byte(folder))
if err != nil {
return err
}
return t.Delete(key)
}
if oldMeta.Sequence(protocol.LocalDeviceID) != meta.Sequence(protocol.LocalDeviceID) {
if err := deleteIndexID(protocol.LocalDeviceID); err != nil {
return err
}
l.Infof("Local sequence for folder %v changed while repairing - dropping delta indexes", folder)
}
oldDevices := oldMeta.devices()
oldSequences := make(map[protocol.DeviceID]int64, len(oldDevices))
for _, devID := range oldDevices {
oldSequences[devID] = oldMeta.Sequence(devID)
}
for _, devID := range meta.devices() {
oldSeq := oldSequences[devID]
delete(oldSequences, devID)
// A lower sequence number just means we will receive some indexes again.
if oldSeq >= meta.Sequence(devID) {
if oldSeq > meta.Sequence(devID) {
db.evLogger.Log(events.Failure, "lower remote sequence after recalculating metadata")
}
continue
}
db.evLogger.Log(events.Failure, "higher remote sequence after recalculating metadata")
if err := deleteIndexID(devID); err != nil {
return err
}
l.Infof("Sequence of device %v for folder %v changed while repairing - dropping delta indexes", devID.Short(), folder)
}
for devID := range oldSequences {
if err := deleteIndexID(devID); err != nil {
return err
}
l.Debugf("Removed indexID of device %v for folder %v which isn't present anymore", devID.Short(), folder)
}
return t.Commit()
}
func (db *Lowlevel) needsRepairPath() string {
path := db.Location()
if path == "" {

View File

@@ -7,6 +7,7 @@
package db
import (
"fmt"
"time"
"github.com/syncthing/syncthing/lib/protocol"
@@ -26,11 +27,9 @@ func (db *Lowlevel) AddOrUpdatePendingDevice(device protocol.DeviceID, name, add
return db.Put(key, bs)
}
func (db *Lowlevel) RemovePendingDevice(device protocol.DeviceID) {
func (db *Lowlevel) RemovePendingDevice(device protocol.DeviceID) error {
key := db.keyer.GeneratePendingDeviceKey(nil, device[:])
if err := db.Delete(key); err != nil {
l.Warnf("Failed to remove pending device entry: %v", err)
}
return db.Delete(key)
}
// PendingDevices enumerates all entries. Invalid ones are dropped from the database
@@ -79,32 +78,35 @@ func (db *Lowlevel) AddOrUpdatePendingFolder(id string, of ObservedFolder, devic
}
// RemovePendingFolderForDevice removes entries for specific folder / device combinations.
func (db *Lowlevel) RemovePendingFolderForDevice(id string, device protocol.DeviceID) {
func (db *Lowlevel) RemovePendingFolderForDevice(id string, device protocol.DeviceID) error {
key, err := db.keyer.GeneratePendingFolderKey(nil, device[:], []byte(id))
if err != nil {
return
}
if err := db.Delete(key); err != nil {
l.Warnf("Failed to remove pending folder entry: %v", err)
return err
}
return db.Delete(key)
}
// RemovePendingFolder removes all entries matching a specific folder ID.
func (db *Lowlevel) RemovePendingFolder(id string) {
func (db *Lowlevel) RemovePendingFolder(id string) error {
iter, err := db.NewPrefixIterator([]byte{KeyTypePendingFolder})
if err != nil {
l.Infof("Could not iterate through pending folder entries: %v", err)
return
return fmt.Errorf("creating iterator: %w", err)
}
defer iter.Release()
var iterErr error
for iter.Next() {
if id != string(db.keyer.FolderFromPendingFolderKey(iter.Key())) {
continue
}
if err := db.Delete(iter.Key()); err != nil {
l.Warnf("Failed to remove pending folder entry: %v", err)
if err = db.Delete(iter.Key()); err != nil {
if iterErr != nil {
l.Debugf("Repeat error removing pending folder: %v", err)
} else {
iterErr = err
}
}
}
return iterErr
}
// Consolidated information about a pending folder
@@ -117,7 +119,7 @@ func (db *Lowlevel) PendingFolders() (map[string]PendingFolder, error) {
}
// PendingFoldersForDevice enumerates only entries matching the given device ID, unless it
// is EmptyDeviceID. Invalid ones are dropped from the database after a warning log
// is EmptyDeviceID. Invalid ones are dropped from the database after a info log
// message, as a side-effect.
func (db *Lowlevel) PendingFoldersForDevice(device protocol.DeviceID) (map[string]PendingFolder, error) {
var err error

View File

@@ -20,7 +20,7 @@ import (
// do not put restrictions on downgrades (e.g. for repairs after a bugfix).
const (
dbVersion = 14
dbMigrationVersion = 18
dbMigrationVersion = 19
dbMinSyncthingVersion = "v1.9.0"
)
@@ -102,7 +102,7 @@ func (db *schemaUpdater) updateSchema() error {
{14, 14, "v1.9.0", db.updateSchemaTo14},
{14, 16, "v1.9.0", db.checkRepairMigration},
{14, 17, "v1.9.0", db.migration17},
{14, 18, "v1.9.0", db.dropIndexIDsMigration},
{14, 19, "v1.9.0", db.dropIndexIDsMigration},
}
for _, m := range migrations {

View File

@@ -1784,6 +1784,41 @@ func TestConcurrentIndexID(t *testing.T) {
}
}
func TestNeedRemoveLastValid(t *testing.T) {
db := newLowlevelMemory(t)
defer db.Close()
folder := "test"
testFs := fs.NewFilesystem(fs.FilesystemTypeFake, "")
fs := newFileSet(t, folder, testFs, db)
files := []protocol.FileInfo{
{Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1},
}
fs.Update(remoteDevice0, files)
files[0].Version = files[0].Version.Update(myID)
fs.Update(remoteDevice1, files)
files[0].LocalFlags = protocol.FlagLocalIgnored
fs.Update(protocol.LocalDeviceID, files)
snap := snapshot(t, fs)
c := snap.NeedSize(remoteDevice0)
if c.Files != 1 {
t.Errorf("Expected 1 needed files initially, got %v", c.Files)
}
snap.Release()
fs.Drop(remoteDevice1)
snap = snapshot(t, fs)
c = snap.NeedSize(remoteDevice0)
if c.Files != 0 {
t.Errorf("Expected no needed files, got %v", c.Files)
}
snap.Release()
}
func replace(fs *db.FileSet, device protocol.DeviceID, files []protocol.FileInfo) {
fs.Drop(device)
fs.Update(device, files)

View File

@@ -331,15 +331,12 @@ func (vl *VersionList) pop(device, name []byte) (FileVersion, bool, bool, error)
oldFV := vl.RawVersions[i].copy()
if invDevice {
vl.RawVersions[i].InvalidDevices = popDeviceAt(vl.RawVersions[i].InvalidDevices, j)
} else {
vl.RawVersions[i].Devices = popDeviceAt(vl.RawVersions[i].Devices, j)
return oldFV, true, false, nil
}
vl.RawVersions[i].Devices = popDeviceAt(vl.RawVersions[i].Devices, j)
// If the last valid device of the previous global was removed above,
// the next entry is now the global entry (unless all entries are invalid).
if len(vl.RawVersions[i].Devices) == 0 && globalPos == i {
return oldFV, true, globalPos == vl.findGlobal(), nil
}
return oldFV, true, false, nil
// the global changed.
return oldFV, true, len(vl.RawVersions[i].Devices) == 0 && globalPos == i, nil
}
// Get returns a FileVersion that contains the given device and whether it has

View File

@@ -403,6 +403,7 @@ type ObservedFolder struct {
Time time.Time `protobuf:"bytes,1,opt,name=time,proto3,stdtime" json:"time" xml:"time"`
Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label" xml:"label"`
ReceiveEncrypted bool `protobuf:"varint,3,opt,name=receive_encrypted,json=receiveEncrypted,proto3" json:"receiveEncrypted" xml:"receiveEncrypted"`
RemoteEncrypted bool `protobuf:"varint,4,opt,name=remote_encrypted,json=remoteEncrypted,proto3" json:"remoteEncrypted" xml:"remoteEncrypted"`
}
func (m *ObservedFolder) Reset() { *m = ObservedFolder{} }
@@ -494,98 +495,100 @@ func init() {
func init() { proto.RegisterFile("lib/db/structs.proto", fileDescriptor_5465d80e8cba02e3) }
var fileDescriptor_5465d80e8cba02e3 = []byte{
// 1445 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcf, 0x6f, 0x1b, 0x45,
0x14, 0xce, 0xc6, 0xce, 0x0f, 0x8f, 0x9d, 0xb4, 0xd9, 0xd2, 0xc8, 0x04, 0xf0, 0x98, 0x69, 0x2a,
0x19, 0x90, 0x1c, 0x29, 0x55, 0x23, 0x54, 0x09, 0xaa, 0x6e, 0x43, 0xda, 0x54, 0xa5, 0x45, 0x93,
0xaa, 0x20, 0x38, 0x58, 0xfb, 0x63, 0xe2, 0xac, 0xba, 0xde, 0x35, 0x3b, 0x9b, 0xa4, 0xee, 0x8d,
0x0b, 0x12, 0xb7, 0xaa, 0xe2, 0x80, 0x10, 0x42, 0x3d, 0xf1, 0x27, 0xf0, 0x17, 0x70, 0xe8, 0x31,
0x47, 0xc4, 0x61, 0x51, 0x93, 0x0b, 0x58, 0xe2, 0xe2, 0x13, 0xe2, 0x84, 0xe6, 0xcd, 0xec, 0xec,
0xba, 0x51, 0x51, 0x5b, 0x72, 0xdb, 0xf7, 0xbd, 0xef, 0x3d, 0xef, 0xbc, 0xf9, 0xde, 0xdb, 0x67,
0xf4, 0x5a, 0xe0, 0x3b, 0x2b, 0x9e, 0xb3, 0xc2, 0x93, 0x78, 0xd7, 0x4d, 0x78, 0xbb, 0x1f, 0x47,
0x49, 0x64, 0x4e, 0x7a, 0xce, 0xd2, 0xb9, 0x98, 0xf5, 0x23, 0xbe, 0x02, 0x80, 0xb3, 0xbb, 0xbd,
0xd2, 0x8d, 0xba, 0x11, 0x18, 0xf0, 0x24, 0x89, 0x4b, 0xb8, 0x1b, 0x45, 0xdd, 0x80, 0xe5, 0xac,
0xc4, 0xef, 0x31, 0x9e, 0xd8, 0xbd, 0xbe, 0x22, 0x2c, 0x8a, 0xfc, 0xf0, 0xe8, 0x46, 0xc1, 0x8a,
0xc3, 0x32, 0xbc, 0xc2, 0xee, 0x27, 0xf2, 0x91, 0xfc, 0x38, 0x89, 0xaa, 0x1b, 0x7e, 0xc0, 0xee,
0xb2, 0x98, 0xfb, 0x51, 0x68, 0xde, 0x44, 0x33, 0x7b, 0xf2, 0xb1, 0x6e, 0x34, 0x8d, 0x56, 0x75,
0xf5, 0x74, 0x3b, 0x4b, 0xd0, 0xbe, 0xcb, 0xdc, 0x24, 0x8a, 0xad, 0xe6, 0x93, 0x14, 0x4f, 0x0c,
0x53, 0x9c, 0x11, 0x47, 0x29, 0x9e, 0xbb, 0xdf, 0x0b, 0x2e, 0x11, 0x65, 0x13, 0x9a, 0x79, 0xcc,
0x35, 0x34, 0xe3, 0xb1, 0x80, 0x25, 0xcc, 0xab, 0x4f, 0x36, 0x8d, 0xd6, 0xac, 0xf5, 0xa6, 0x88,
0x53, 0x90, 0x8e, 0x53, 0x36, 0xa1, 0x99, 0xc7, 0xbc, 0x28, 0xe2, 0xf6, 0x7c, 0x97, 0xf1, 0x7a,
0xa9, 0x59, 0x6a, 0xd5, 0xac, 0x37, 0x64, 0x1c, 0x40, 0xa3, 0x14, 0xd7, 0x54, 0x9c, 0xb0, 0x21,
0x0c, 0x1c, 0x26, 0x45, 0xa7, 0xfc, 0x70, 0xcf, 0x0e, 0x7c, 0xaf, 0x93, 0x85, 0x97, 0x21, 0xfc,
0x9d, 0x61, 0x8a, 0xe7, 0x95, 0x6b, 0x5d, 0x67, 0x39, 0x03, 0x59, 0xc6, 0x60, 0x42, 0x9f, 0xa1,
0x91, 0xaf, 0x0c, 0x54, 0x55, 0xc5, 0xb9, 0xe9, 0xf3, 0xc4, 0x0c, 0xd0, 0xac, 0x3a, 0x1d, 0xaf,
0x1b, 0xcd, 0x52, 0xab, 0xba, 0x7a, 0xaa, 0xed, 0x39, 0xed, 0x42, 0x0d, 0xad, 0xcb, 0xa2, 0x40,
0x87, 0x29, 0xae, 0x52, 0x7b, 0x5f, 0x61, 0x7c, 0x98, 0x62, 0x1d, 0x77, 0xac, 0x60, 0x8f, 0x0e,
0x96, 0x8b, 0x5c, 0xaa, 0x99, 0x97, 0xca, 0xdf, 0x3d, 0xc6, 0x13, 0xe4, 0x6f, 0x84, 0x16, 0xc4,
0x0f, 0x6c, 0x86, 0xdb, 0xd1, 0x9d, 0x78, 0x37, 0x74, 0x6d, 0x51, 0xa4, 0x77, 0x51, 0x39, 0xb4,
0x7b, 0x0c, 0xee, 0xa9, 0x62, 0x2d, 0x0e, 0x53, 0x0c, 0xf6, 0x28, 0xc5, 0x08, 0xb2, 0x0b, 0x83,
0x50, 0xc0, 0x04, 0x97, 0xfb, 0x0f, 0x58, 0xbd, 0xd4, 0x34, 0x5a, 0x25, 0xc9, 0x15, 0xb6, 0xe6,
0x0a, 0x83, 0x50, 0xc0, 0xcc, 0xcb, 0x08, 0xf5, 0x22, 0xcf, 0xdf, 0xf6, 0x99, 0xd7, 0xe1, 0xf5,
0x29, 0x88, 0x68, 0x0e, 0x53, 0x5c, 0xc9, 0xd0, 0xad, 0x51, 0x8a, 0x4f, 0x41, 0x98, 0x46, 0x08,
0xcd, 0xbd, 0xe6, 0xcf, 0x06, 0xaa, 0xea, 0x0c, 0xce, 0xa0, 0x5e, 0x6b, 0x1a, 0xad, 0xb2, 0xf5,
0xad, 0x21, 0xca, 0xf2, 0x5b, 0x8a, 0x2f, 0x74, 0xfd, 0x64, 0x67, 0xd7, 0x69, 0xbb, 0x51, 0x6f,
0x85, 0x0f, 0x42, 0x37, 0xd9, 0xf1, 0xc3, 0x6e, 0xe1, 0xa9, 0x28, 0xda, 0xf6, 0xd6, 0x4e, 0x14,
0x27, 0x9b, 0xeb, 0xc3, 0x14, 0xeb, 0x97, 0xb2, 0x06, 0xa3, 0x14, 0x9f, 0x1e, 0xfb, 0x7d, 0x6b,
0x40, 0xbe, 0x3f, 0x58, 0x7e, 0x95, 0xc4, 0xb4, 0x90, 0xb6, 0x28, 0xfe, 0xca, 0xff, 0x17, 0xff,
0x25, 0x34, 0xcb, 0xd9, 0x97, 0xbb, 0x2c, 0x74, 0x59, 0x1d, 0x41, 0x15, 0x1b, 0x42, 0x05, 0x19,
0x36, 0x4a, 0xf1, 0xbc, 0xac, 0xbd, 0x02, 0x08, 0xd5, 0x3e, 0xf3, 0x36, 0x9a, 0xe7, 0x83, 0x5e,
0xe0, 0x87, 0xf7, 0x3a, 0x89, 0x1d, 0x77, 0x59, 0x52, 0x5f, 0x80, 0x5b, 0x6e, 0x0d, 0x53, 0x3c,
0xa7, 0x3c, 0x77, 0xc0, 0xa1, 0x75, 0x3c, 0x86, 0x12, 0x3a, 0xce, 0x32, 0xaf, 0xa2, 0xaa, 0x13,
0x44, 0xee, 0x3d, 0xde, 0xd9, 0xb1, 0xf9, 0x4e, 0xdd, 0x6c, 0x1a, 0xad, 0x9a, 0x45, 0x44, 0x59,
0x25, 0x7c, 0xdd, 0xe6, 0x3b, 0xba, 0xac, 0x39, 0x44, 0x68, 0xc1, 0x6f, 0x7e, 0x88, 0x2a, 0x2c,
0x74, 0xe3, 0x41, 0x5f, 0x34, 0xf4, 0x19, 0x48, 0x01, 0xc2, 0xd0, 0xa0, 0x16, 0x86, 0x46, 0x08,
0xcd, 0xbd, 0xa6, 0x85, 0xca, 0xc9, 0xa0, 0xcf, 0x60, 0x16, 0xcc, 0xaf, 0x2e, 0xe6, 0xc5, 0xd5,
0xe2, 0x1e, 0xf4, 0x99, 0x54, 0xa7, 0xe0, 0x69, 0x75, 0x0a, 0x83, 0x50, 0xc0, 0xcc, 0x0d, 0x54,
0xed, 0xb3, 0xb8, 0xe7, 0x73, 0xd9, 0x82, 0xe5, 0xa6, 0xd1, 0x9a, 0xb3, 0x96, 0x87, 0x29, 0x2e,
0xc2, 0xa3, 0x14, 0x2f, 0x40, 0x64, 0x01, 0x23, 0xb4, 0xc8, 0x30, 0x6f, 0x14, 0x34, 0x1a, 0xf2,
0x7a, 0xb5, 0x69, 0xb4, 0xa6, 0x60, 0x4e, 0x68, 0x41, 0xdc, 0xe2, 0xc7, 0x74, 0x76, 0x8b, 0x93,
0x7f, 0x52, 0x5c, 0xf2, 0xc3, 0x84, 0x16, 0x68, 0xe6, 0x36, 0x92, 0x55, 0xea, 0x40, 0x8f, 0xcd,
0x41, 0xaa, 0x6b, 0x87, 0x29, 0xae, 0x51, 0x7b, 0xdf, 0x12, 0x8e, 0x2d, 0xff, 0x01, 0x13, 0x85,
0x72, 0x32, 0x43, 0x17, 0x4a, 0x23, 0x59, 0xe2, 0x47, 0x07, 0xcb, 0x63, 0x61, 0x34, 0x0f, 0x32,
0xd7, 0x51, 0x35, 0x88, 0x5c, 0x3b, 0xe8, 0x6c, 0x07, 0x76, 0x97, 0xd7, 0xff, 0x98, 0x81, 0xc3,
0xc3, 0x2d, 0x02, 0xbe, 0x21, 0x60, 0xfd, 0xd2, 0x39, 0x44, 0x68, 0xc1, 0x6f, 0x5e, 0x47, 0x35,
0x25, 0x51, 0xa9, 0x85, 0x3f, 0x67, 0xe0, 0x26, 0xa1, 0x86, 0xca, 0xa1, 0xd4, 0xb0, 0x50, 0x54,
0xb6, 0x94, 0x43, 0x91, 0x51, 0x1c, 0xef, 0xd3, 0x2f, 0x33, 0xde, 0x29, 0x9a, 0x51, 0x53, 0xb6,
0x3e, 0x03, 0x71, 0xef, 0x1f, 0xa6, 0x18, 0x51, 0x7b, 0x7f, 0x53, 0xa2, 0x22, 0x8b, 0x22, 0xe8,
0x2c, 0xca, 0x16, 0xb3, 0xb2, 0xc0, 0xa4, 0x19, 0x4f, 0x74, 0x4c, 0x18, 0x75, 0x8a, 0xd2, 0x98,
0x85, 0xd4, 0xd0, 0x31, 0x61, 0xf4, 0xc9, 0x98, 0x38, 0x64, 0xc7, 0x8c, 0xa1, 0x84, 0x8e, 0xb3,
0xd4, 0xe8, 0xfd, 0x14, 0x55, 0xe0, 0x2a, 0x60, 0xf6, 0xdf, 0x40, 0xd3, 0xb2, 0x1b, 0xd4, 0xe4,
0x3f, 0x93, 0x2b, 0x18, 0x48, 0x42, 0xc2, 0xd6, 0x5b, 0x6a, 0x42, 0x28, 0xea, 0x28, 0xc5, 0xd5,
0xfc, 0xa6, 0x09, 0x55, 0x30, 0xf9, 0xc9, 0x40, 0x67, 0x37, 0x43, 0xcf, 0x8f, 0x99, 0x9b, 0xa8,
0x7a, 0x32, 0x7e, 0x3b, 0x0c, 0x06, 0x27, 0xd3, 0xaa, 0x27, 0x76, 0xc9, 0xe4, 0x87, 0x32, 0x9a,
0xbe, 0x1a, 0xed, 0x86, 0x09, 0x37, 0x2f, 0xa2, 0xa9, 0x6d, 0x3f, 0x60, 0x1c, 0x3e, 0x39, 0x53,
0x16, 0x1e, 0xa6, 0x58, 0x02, 0xfa, 0x90, 0x60, 0xe9, 0x1e, 0x91, 0x4e, 0xf3, 0x63, 0x54, 0x95,
0xe7, 0x8c, 0x62, 0x9f, 0x71, 0xe8, 0xfe, 0x29, 0xeb, 0x3d, 0xf1, 0x26, 0x05, 0x58, 0xbf, 0x49,
0x01, 0xd3, 0x89, 0x8a, 0x44, 0xf3, 0x0a, 0x9a, 0x55, 0xb3, 0x8d, 0xc3, 0xf7, 0x6c, 0xca, 0x3a,
0x0f, 0x73, 0x55, 0x61, 0xf9, 0x5c, 0x55, 0x80, 0xce, 0xa2, 0x29, 0xe6, 0x07, 0xb9, 0x70, 0xcb,
0x90, 0xe1, 0xdc, 0x7f, 0x09, 0x37, 0x8b, 0xd7, 0xfa, 0x6d, 0xa3, 0x29, 0x67, 0x90, 0xb0, 0xec,
0xe3, 0x58, 0x17, 0x75, 0x00, 0x20, 0xbf, 0x6c, 0x61, 0x11, 0x2a, 0xd1, 0xb1, 0x2f, 0xc1, 0xf4,
0x4b, 0x7e, 0x09, 0xb6, 0x50, 0x45, 0xee, 0x32, 0x1d, 0xdf, 0x83, 0x8f, 0x40, 0xcd, 0x5a, 0x3b,
0x4c, 0xf1, 0xac, 0xdc, 0x4f, 0xe0, 0xcb, 0x38, 0x2b, 0x09, 0x9b, 0x9e, 0x4e, 0x94, 0x01, 0xa2,
0x5b, 0x34, 0x93, 0x6a, 0x9e, 0x90, 0x58, 0x71, 0x90, 0x98, 0xaf, 0x32, 0x47, 0x54, 0x83, 0x7c,
0x6d, 0xa0, 0x8a, 0x94, 0xc7, 0x16, 0x4b, 0xcc, 0x2b, 0x68, 0xda, 0x05, 0x43, 0x75, 0x08, 0x12,
0xbb, 0x91, 0x74, 0xe7, 0x8d, 0x21, 0x19, 0xba, 0x56, 0x60, 0x12, 0xaa, 0x60, 0x31, 0x54, 0xdc,
0x98, 0xd9, 0xd9, 0xce, 0x58, 0x92, 0x43, 0x45, 0x41, 0xfa, 0x6e, 0x94, 0x4d, 0x68, 0xe6, 0x21,
0xdf, 0x4c, 0xa2, 0xb3, 0x85, 0x2d, 0x6c, 0x9d, 0xf5, 0x63, 0x26, 0x17, 0xa5, 0x93, 0xdd, 0x69,
0x57, 0xd1, 0xb4, 0xac, 0x23, 0xbc, 0x5e, 0xcd, 0x5a, 0x12, 0x47, 0x92, 0xc8, 0xb1, 0xcd, 0x54,
0xe1, 0xe2, 0x4c, 0xd9, 0xc0, 0x2b, 0xe5, 0x83, 0xf2, 0x79, 0x23, 0x2e, 0x1f, 0x6a, 0x6b, 0xe3,
0x3a, 0x7d, 0xd1, 0x01, 0x4b, 0xf6, 0xd1, 0xd9, 0xc2, 0xce, 0x5a, 0x28, 0xc5, 0x67, 0xc7, 0xb6,
0xd7, 0xd7, 0x9f, 0xd9, 0x5e, 0x73, 0xb2, 0xf5, 0xb6, 0x2a, 0xca, 0xf3, 0x17, 0xd7, 0x63, 0x9b,
0xea, 0x5f, 0x06, 0x9a, 0xbf, 0xed, 0x70, 0x16, 0xef, 0x31, 0x6f, 0x23, 0x0a, 0x3c, 0x16, 0x9b,
0xb7, 0x50, 0x59, 0xfc, 0x2f, 0x51, 0xa5, 0x5f, 0x6a, 0xcb, 0x3f, 0x2d, 0xed, 0xec, 0x4f, 0x4b,
0xfb, 0x4e, 0xf6, 0xa7, 0xc5, 0x6a, 0xa8, 0xdf, 0x03, 0x7e, 0xfe, 0xf1, 0xf7, 0x7b, 0x8c, 0x3c,
0xfc, 0x1d, 0x1b, 0x14, 0x70, 0xd1, 0x7c, 0x81, 0xed, 0xb0, 0x00, 0xca, 0x5f, 0x91, 0xcd, 0x07,
0x80, 0x16, 0x14, 0x58, 0x84, 0x4a, 0xd4, 0xfc, 0x02, 0x2d, 0xc4, 0xcc, 0x65, 0xfe, 0x1e, 0xeb,
0xe4, 0xcb, 0x8b, 0xbc, 0x85, 0xf6, 0x30, 0xc5, 0xa7, 0x95, 0xf3, 0xa3, 0xc2, 0x0e, 0xb3, 0x08,
0x69, 0x9e, 0x75, 0x10, 0x7a, 0x8c, 0x4b, 0x7e, 0x29, 0x9c, 0x57, 0xf6, 0xd9, 0x89, 0x9f, 0x37,
0x5b, 0xf3, 0x27, 0x5f, 0x60, 0xcd, 0x5f, 0x43, 0x33, 0xb6, 0xe7, 0xc5, 0x8c, 0xcb, 0xc9, 0x58,
0x91, 0x7a, 0x51, 0x90, 0xbe, 0x3d, 0x65, 0x13, 0x9a, 0x79, 0xac, 0x6b, 0x4f, 0x9e, 0x36, 0x26,
0x0e, 0x9e, 0x36, 0x26, 0x9e, 0x1c, 0x36, 0x8c, 0x83, 0xc3, 0x86, 0xf1, 0xf0, 0xa8, 0x31, 0xf1,
0xf8, 0xa8, 0x61, 0x1c, 0x1c, 0x35, 0x26, 0x7e, 0x3d, 0x6a, 0x4c, 0x7c, 0x7e, 0xfe, 0x05, 0x76,
0x6b, 0xcf, 0x71, 0xa6, 0xe1, 0x98, 0x17, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x3c, 0x81,
0x94, 0xda, 0x0e, 0x00, 0x00,
// 1476 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x4d, 0x6f, 0xdb, 0x46,
0x13, 0x36, 0x2d, 0xf9, 0x43, 0x2b, 0xf9, 0x8b, 0x79, 0x6d, 0xe8, 0xf5, 0xfb, 0x56, 0xab, 0x6e,
0x1c, 0x40, 0xfd, 0x80, 0x0c, 0x38, 0x88, 0x51, 0x04, 0x68, 0x83, 0x30, 0xae, 0x13, 0x07, 0x69,
0x52, 0xac, 0x83, 0xa4, 0x68, 0x0f, 0x82, 0x48, 0xae, 0x65, 0x22, 0x14, 0xa9, 0x72, 0x69, 0x3b,
0xca, 0xad, 0x97, 0x02, 0xbd, 0x05, 0x41, 0x0f, 0x45, 0x51, 0x14, 0x39, 0xf5, 0x27, 0xf4, 0x17,
0x14, 0x45, 0x8e, 0x3e, 0x16, 0x3d, 0xb0, 0x88, 0x7d, 0x69, 0x75, 0xd4, 0xa9, 0xe8, 0xa9, 0xd8,
0xd9, 0xe5, 0x92, 0xb2, 0x91, 0x22, 0x49, 0x7d, 0xd3, 0x3c, 0xf3, 0xcc, 0x88, 0x9c, 0x7d, 0x66,
0x76, 0x88, 0xfe, 0xe3, 0x7b, 0xf6, 0xaa, 0x6b, 0xaf, 0xf2, 0x38, 0xda, 0x73, 0x62, 0xde, 0xec,
0x45, 0x61, 0x1c, 0x9a, 0xe3, 0xae, 0xbd, 0x7c, 0x3e, 0x62, 0xbd, 0x90, 0xaf, 0x02, 0x60, 0xef,
0xed, 0xac, 0x76, 0xc2, 0x4e, 0x08, 0x06, 0xfc, 0x92, 0xc4, 0x65, 0xdc, 0x09, 0xc3, 0x8e, 0xcf,
0x32, 0x56, 0xec, 0x75, 0x19, 0x8f, 0xdb, 0xdd, 0x9e, 0x22, 0x2c, 0x89, 0xfc, 0xf0, 0xd3, 0x09,
0xfd, 0x55, 0x9b, 0xa5, 0x78, 0x89, 0x3d, 0x8c, 0xe5, 0x4f, 0xf2, 0xfd, 0x38, 0x2a, 0x6f, 0x7a,
0x3e, 0xbb, 0xc7, 0x22, 0xee, 0x85, 0x81, 0x79, 0x0b, 0x4d, 0xed, 0xcb, 0x9f, 0x55, 0xa3, 0x6e,
0x34, 0xca, 0x6b, 0xf3, 0xcd, 0x34, 0x41, 0xf3, 0x1e, 0x73, 0xe2, 0x30, 0xb2, 0xea, 0xcf, 0x12,
0x3c, 0x36, 0x48, 0x70, 0x4a, 0x1c, 0x26, 0x78, 0xe6, 0x61, 0xd7, 0xbf, 0x4c, 0x94, 0x4d, 0x68,
0xea, 0x31, 0xd7, 0xd1, 0x94, 0xcb, 0x7c, 0x16, 0x33, 0xb7, 0x3a, 0x5e, 0x37, 0x1a, 0xd3, 0xd6,
0xff, 0x45, 0x9c, 0x82, 0x74, 0x9c, 0xb2, 0x09, 0x4d, 0x3d, 0xe6, 0x25, 0x11, 0xb7, 0xef, 0x39,
0x8c, 0x57, 0x0b, 0xf5, 0x42, 0xa3, 0x62, 0xfd, 0x4f, 0xc6, 0x01, 0x34, 0x4c, 0x70, 0x45, 0xc5,
0x09, 0x1b, 0xc2, 0xc0, 0x61, 0x52, 0x34, 0xe7, 0x05, 0xfb, 0x6d, 0xdf, 0x73, 0x5b, 0x69, 0x78,
0x11, 0xc2, 0xdf, 0x1a, 0x24, 0x78, 0x56, 0xb9, 0x36, 0x74, 0x96, 0x73, 0x90, 0x65, 0x04, 0x26,
0xf4, 0x04, 0x8d, 0x7c, 0x61, 0xa0, 0xb2, 0x2a, 0xce, 0x2d, 0x8f, 0xc7, 0xa6, 0x8f, 0xa6, 0xd5,
0xdb, 0xf1, 0xaa, 0x51, 0x2f, 0x34, 0xca, 0x6b, 0x73, 0x4d, 0xd7, 0x6e, 0xe6, 0x6a, 0x68, 0x5d,
0x11, 0x05, 0x3a, 0x4a, 0x70, 0x99, 0xb6, 0x0f, 0x14, 0xc6, 0x07, 0x09, 0xd6, 0x71, 0xa7, 0x0a,
0xf6, 0xe4, 0x70, 0x25, 0xcf, 0xa5, 0x9a, 0x79, 0xb9, 0xf8, 0xcd, 0x53, 0x3c, 0x46, 0xfe, 0x44,
0x68, 0x41, 0xfc, 0xc1, 0x56, 0xb0, 0x13, 0xde, 0x8d, 0xf6, 0x02, 0xa7, 0x2d, 0x8a, 0xf4, 0x36,
0x2a, 0x06, 0xed, 0x2e, 0x83, 0x73, 0x2a, 0x59, 0x4b, 0x83, 0x04, 0x83, 0x3d, 0x4c, 0x30, 0x82,
0xec, 0xc2, 0x20, 0x14, 0x30, 0xc1, 0xe5, 0xde, 0x23, 0x56, 0x2d, 0xd4, 0x8d, 0x46, 0x41, 0x72,
0x85, 0xad, 0xb9, 0xc2, 0x20, 0x14, 0x30, 0xf3, 0x0a, 0x42, 0xdd, 0xd0, 0xf5, 0x76, 0x3c, 0xe6,
0xb6, 0x78, 0x75, 0x02, 0x22, 0xea, 0x83, 0x04, 0x97, 0x52, 0x74, 0x7b, 0x98, 0xe0, 0x39, 0x08,
0xd3, 0x08, 0xa1, 0x99, 0xd7, 0xfc, 0xd1, 0x40, 0x65, 0x9d, 0xc1, 0xee, 0x57, 0x2b, 0x75, 0xa3,
0x51, 0xb4, 0xbe, 0x36, 0x44, 0x59, 0x7e, 0x4d, 0xf0, 0xc5, 0x8e, 0x17, 0xef, 0xee, 0xd9, 0x4d,
0x27, 0xec, 0xae, 0xf2, 0x7e, 0xe0, 0xc4, 0xbb, 0x5e, 0xd0, 0xc9, 0xfd, 0xca, 0x8b, 0xb6, 0xb9,
0xbd, 0x1b, 0x46, 0xf1, 0xd6, 0xc6, 0x20, 0xc1, 0xfa, 0xa1, 0xac, 0xfe, 0x30, 0xc1, 0xf3, 0x23,
0xff, 0x6f, 0xf5, 0xc9, 0xb7, 0x87, 0x2b, 0xaf, 0x93, 0x98, 0xe6, 0xd2, 0xe6, 0xc5, 0x5f, 0xfa,
0xf7, 0xe2, 0xbf, 0x8c, 0xa6, 0x39, 0xfb, 0x7c, 0x8f, 0x05, 0x0e, 0xab, 0x22, 0xa8, 0x62, 0x4d,
0xa8, 0x20, 0xc5, 0x86, 0x09, 0x9e, 0x95, 0xb5, 0x57, 0x00, 0xa1, 0xda, 0x67, 0xde, 0x41, 0xb3,
0xbc, 0xdf, 0xf5, 0xbd, 0xe0, 0x41, 0x2b, 0x6e, 0x47, 0x1d, 0x16, 0x57, 0x17, 0xe0, 0x94, 0x1b,
0x83, 0x04, 0xcf, 0x28, 0xcf, 0x5d, 0x70, 0x68, 0x1d, 0x8f, 0xa0, 0x84, 0x8e, 0xb2, 0xcc, 0x6b,
0xa8, 0x6c, 0xfb, 0xa1, 0xf3, 0x80, 0xb7, 0x76, 0xdb, 0x7c, 0xb7, 0x6a, 0xd6, 0x8d, 0x46, 0xc5,
0x22, 0xa2, 0xac, 0x12, 0xbe, 0xd1, 0xe6, 0xbb, 0xba, 0xac, 0x19, 0x44, 0x68, 0xce, 0x6f, 0x7e,
0x80, 0x4a, 0x2c, 0x70, 0xa2, 0x7e, 0x4f, 0x34, 0xf4, 0x39, 0x48, 0x01, 0xc2, 0xd0, 0xa0, 0x16,
0x86, 0x46, 0x08, 0xcd, 0xbc, 0xa6, 0x85, 0x8a, 0x71, 0xbf, 0xc7, 0x60, 0x16, 0xcc, 0xae, 0x2d,
0x65, 0xc5, 0xd5, 0xe2, 0xee, 0xf7, 0x98, 0x54, 0xa7, 0xe0, 0x69, 0x75, 0x0a, 0x83, 0x50, 0xc0,
0xcc, 0x4d, 0x54, 0xee, 0xb1, 0xa8, 0xeb, 0x71, 0xd9, 0x82, 0xc5, 0xba, 0xd1, 0x98, 0xb1, 0x56,
0x06, 0x09, 0xce, 0xc3, 0xc3, 0x04, 0x2f, 0x40, 0x64, 0x0e, 0x23, 0x34, 0xcf, 0x30, 0x6f, 0xe6,
0x34, 0x1a, 0xf0, 0x6a, 0xb9, 0x6e, 0x34, 0x26, 0x60, 0x4e, 0x68, 0x41, 0xdc, 0xe6, 0xa7, 0x74,
0x76, 0x9b, 0x93, 0xbf, 0x12, 0x5c, 0xf0, 0x82, 0x98, 0xe6, 0x68, 0xe6, 0x0e, 0x92, 0x55, 0x6a,
0x41, 0x8f, 0xcd, 0x40, 0xaa, 0xeb, 0x47, 0x09, 0xae, 0xd0, 0xf6, 0x81, 0x25, 0x1c, 0xdb, 0xde,
0x23, 0x26, 0x0a, 0x65, 0xa7, 0x86, 0x2e, 0x94, 0x46, 0xd2, 0xc4, 0x4f, 0x0e, 0x57, 0x46, 0xc2,
0x68, 0x16, 0x64, 0x6e, 0xa0, 0xb2, 0x1f, 0x3a, 0x6d, 0xbf, 0xb5, 0xe3, 0xb7, 0x3b, 0xbc, 0xfa,
0xfb, 0x14, 0xbc, 0x3c, 0x9c, 0x22, 0xe0, 0x9b, 0x02, 0xd6, 0x0f, 0x9d, 0x41, 0x84, 0xe6, 0xfc,
0xe6, 0x0d, 0x54, 0x51, 0x12, 0x95, 0x5a, 0xf8, 0x63, 0x0a, 0x4e, 0x12, 0x6a, 0xa8, 0x1c, 0x4a,
0x0d, 0x0b, 0x79, 0x65, 0x4b, 0x39, 0xe4, 0x19, 0xf9, 0xf1, 0x3e, 0xf9, 0x2a, 0xe3, 0x9d, 0xa2,
0x29, 0x35, 0x65, 0xab, 0x53, 0x10, 0xf7, 0xde, 0x51, 0x82, 0x11, 0x6d, 0x1f, 0x6c, 0x49, 0x54,
0x64, 0x51, 0x04, 0x9d, 0x45, 0xd9, 0x62, 0x56, 0xe6, 0x98, 0x34, 0xe5, 0x89, 0x8e, 0x09, 0xc2,
0x56, 0x5e, 0x1a, 0xd3, 0x90, 0x1a, 0x3a, 0x26, 0x08, 0x3f, 0x1e, 0x11, 0x87, 0xec, 0x98, 0x11,
0x94, 0xd0, 0x51, 0x96, 0x1a, 0xbd, 0xf7, 0x51, 0x09, 0x8e, 0x02, 0x66, 0xff, 0x4d, 0x34, 0x29,
0xbb, 0x41, 0x4d, 0xfe, 0x73, 0x99, 0x82, 0x81, 0x24, 0x24, 0x6c, 0xbd, 0xa1, 0x26, 0x84, 0xa2,
0x0e, 0x13, 0x5c, 0xce, 0x4e, 0x9a, 0x50, 0x05, 0x93, 0x1f, 0x0c, 0xb4, 0xb8, 0x15, 0xb8, 0x5e,
0xc4, 0x9c, 0x58, 0xd5, 0x93, 0xf1, 0x3b, 0x81, 0xdf, 0x3f, 0x9b, 0x56, 0x3d, 0xb3, 0x43, 0x26,
0xdf, 0x15, 0xd1, 0xe4, 0xb5, 0x70, 0x2f, 0x88, 0xb9, 0x79, 0x09, 0x4d, 0xec, 0x78, 0x3e, 0xe3,
0x70, 0xe5, 0x4c, 0x58, 0x78, 0x90, 0x60, 0x09, 0xe8, 0x97, 0x04, 0x4b, 0xf7, 0x88, 0x74, 0x9a,
0x1f, 0xa1, 0xb2, 0x7c, 0xcf, 0x30, 0xf2, 0x18, 0x87, 0xee, 0x9f, 0xb0, 0xde, 0x11, 0x4f, 0x92,
0x83, 0xf5, 0x93, 0xe4, 0x30, 0x9d, 0x28, 0x4f, 0x34, 0xaf, 0xa2, 0x69, 0x35, 0xdb, 0x38, 0xdc,
0x67, 0x13, 0xd6, 0x05, 0x98, 0xab, 0x0a, 0xcb, 0xe6, 0xaa, 0x02, 0x74, 0x16, 0x4d, 0x31, 0xdf,
0xcf, 0x84, 0x5b, 0x84, 0x0c, 0xe7, 0xff, 0x49, 0xb8, 0x69, 0xbc, 0xd6, 0x6f, 0x13, 0x4d, 0xd8,
0xfd, 0x98, 0xa5, 0x97, 0x63, 0x55, 0xd4, 0x01, 0x80, 0xec, 0xb0, 0x85, 0x45, 0xa8, 0x44, 0x47,
0x6e, 0x82, 0xc9, 0x57, 0xbc, 0x09, 0xb6, 0x51, 0x49, 0xee, 0x32, 0x2d, 0xcf, 0x85, 0x4b, 0xa0,
0x62, 0xad, 0x1f, 0x25, 0x78, 0x5a, 0xee, 0x27, 0x70, 0x33, 0x4e, 0x4b, 0xc2, 0x96, 0xab, 0x13,
0xa5, 0x80, 0xe8, 0x16, 0xcd, 0xa4, 0x9a, 0x27, 0x24, 0x96, 0x1f, 0x24, 0xe6, 0xeb, 0xcc, 0x11,
0xd5, 0x20, 0x5f, 0x1a, 0xa8, 0x24, 0xe5, 0xb1, 0xcd, 0x62, 0xf3, 0x2a, 0x9a, 0x74, 0xc0, 0x50,
0x1d, 0x82, 0xc4, 0x6e, 0x24, 0xdd, 0x59, 0x63, 0x48, 0x86, 0xae, 0x15, 0x98, 0x84, 0x2a, 0x58,
0x0c, 0x15, 0x27, 0x62, 0xed, 0x74, 0x67, 0x2c, 0xc8, 0xa1, 0xa2, 0x20, 0x7d, 0x36, 0xca, 0x26,
0x34, 0xf5, 0x90, 0xaf, 0xc6, 0xd1, 0x62, 0x6e, 0x0b, 0xdb, 0x60, 0xbd, 0x88, 0xc9, 0x45, 0xe9,
0x6c, 0x77, 0xda, 0x35, 0x34, 0x29, 0xeb, 0x08, 0x8f, 0x57, 0xb1, 0x96, 0xc5, 0x2b, 0x49, 0xe4,
0xd4, 0x66, 0xaa, 0x70, 0xf1, 0x4e, 0xe9, 0xc0, 0x2b, 0x64, 0x83, 0xf2, 0x45, 0x23, 0x2e, 0x1b,
0x6a, 0xeb, 0xa3, 0x3a, 0x7d, 0xd9, 0x01, 0x4b, 0x0e, 0xd0, 0x62, 0x6e, 0x67, 0xcd, 0x95, 0xe2,
0x93, 0x53, 0xdb, 0xeb, 0x7f, 0x4f, 0x6c, 0xaf, 0x19, 0xd9, 0x7a, 0x53, 0x15, 0xe5, 0xc5, 0x8b,
0xeb, 0xa9, 0x4d, 0xf5, 0xe7, 0x71, 0x34, 0x7b, 0xc7, 0xe6, 0x2c, 0xda, 0x67, 0xee, 0x66, 0xe8,
0xbb, 0x2c, 0x32, 0x6f, 0xa3, 0xa2, 0xf8, 0x2e, 0x51, 0xa5, 0x5f, 0x6e, 0xca, 0x8f, 0x96, 0x66,
0xfa, 0xd1, 0xd2, 0xbc, 0x9b, 0x7e, 0xb4, 0x58, 0x35, 0xf5, 0x7f, 0xc0, 0xcf, 0x2e, 0x7f, 0xaf,
0xcb, 0xc8, 0xe3, 0xdf, 0xb0, 0x41, 0x01, 0x17, 0xcd, 0xe7, 0xb7, 0x6d, 0xe6, 0x43, 0xf9, 0x4b,
0xb2, 0xf9, 0x00, 0xd0, 0x82, 0x02, 0x8b, 0x50, 0x89, 0x9a, 0x9f, 0xa1, 0x85, 0x88, 0x39, 0xcc,
0xdb, 0x67, 0xad, 0x6c, 0x79, 0x91, 0xa7, 0xd0, 0x1c, 0x24, 0x78, 0x5e, 0x39, 0x3f, 0xcc, 0xed,
0x30, 0x4b, 0x90, 0xe6, 0xa4, 0x83, 0xd0, 0x53, 0x5c, 0xf3, 0x3e, 0x9a, 0x8f, 0x58, 0x37, 0x8c,
0xf3, 0xb9, 0xe5, 0x49, 0xbd, 0x3b, 0x48, 0xf0, 0x9c, 0xf4, 0xe5, 0x53, 0x2f, 0xaa, 0xd4, 0x23,
0x38, 0xa1, 0x27, 0x99, 0xe4, 0x27, 0x23, 0x2b, 0xa4, 0x6c, 0xe0, 0x33, 0x2f, 0x64, 0xfa, 0xfd,
0x30, 0xfe, 0x12, 0xdf, 0x0f, 0xeb, 0x68, 0xaa, 0xed, 0xba, 0x11, 0xe3, 0x72, 0xe4, 0x96, 0xa4,
0x10, 0x15, 0xa4, 0x65, 0xa1, 0x6c, 0x42, 0x53, 0x8f, 0x75, 0xfd, 0xd9, 0xf3, 0xda, 0xd8, 0xe1,
0xf3, 0xda, 0xd8, 0xb3, 0xa3, 0x9a, 0x71, 0x78, 0x54, 0x33, 0x1e, 0x1f, 0xd7, 0xc6, 0x9e, 0x1e,
0xd7, 0x8c, 0xc3, 0xe3, 0xda, 0xd8, 0x2f, 0xc7, 0xb5, 0xb1, 0x4f, 0x2f, 0xbc, 0xc4, 0xd2, 0xee,
0xda, 0xf6, 0x24, 0xbc, 0xe6, 0xc5, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xb3, 0xa4, 0xea, 0xfc,
0x33, 0x0f, 0x00, 0x00,
}
func (m *FileVersion) Marshal() (dAtA []byte, err error) {
@@ -1145,6 +1148,16 @@ func (m *ObservedFolder) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.RemoteEncrypted {
i--
if m.RemoteEncrypted {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x20
}
if m.ReceiveEncrypted {
i--
if m.ReceiveEncrypted {
@@ -1472,6 +1485,9 @@ func (m *ObservedFolder) ProtoSize() (n int) {
if m.ReceiveEncrypted {
n += 2
}
if m.RemoteEncrypted {
n += 2
}
return n
}
@@ -3108,6 +3124,26 @@ func (m *ObservedFolder) Unmarshal(dAtA []byte) error {
}
}
m.ReceiveEncrypted = bool(v != 0)
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RemoteEncrypted", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowStructs
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.RemoteEncrypted = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipStructs(dAtA[iNdEx:])

View File

@@ -35,11 +35,15 @@ func (db *Lowlevel) newReadOnlyTransaction() (readOnlyTransaction, error) {
if err != nil {
return readOnlyTransaction{}, err
}
return db.readOnlyTransactionFromBackendTransaction(tran), nil
}
func (db *Lowlevel) readOnlyTransactionFromBackendTransaction(tran backend.ReadTransaction) readOnlyTransaction {
return readOnlyTransaction{
ReadTransaction: tran,
keyer: db.keyer,
evLogger: db.evLogger,
}, nil
}
}
func (t readOnlyTransaction) close() {
@@ -398,12 +402,16 @@ func (t *readOnlyTransaction) withBlocksHash(folder, hash []byte, iterator Itera
f.Name = osutil.NativeFilename(f.Name)
if !bytes.Equal(f.BlocksHash, hash) {
l.Warnf("Mismatching block map list hashes: got %x expected %x", f.BlocksHash, hash)
msg := "Mismatching block map list hashes"
t.evLogger.Log(events.Failure, fmt.Sprintln(msg, "in withBlocksHash"))
l.Warnf("%v: got %x expected %x", msg, f.BlocksHash, hash)
continue
}
if f.IsDeleted() || f.IsInvalid() || f.IsDirectory() || f.IsSymlink() {
l.Warnf("Found something of unexpected type in block list map: %s", f)
msg := "Found something of unexpected type in block list map"
t.evLogger.Log(events.Failure, fmt.Sprintln(msg, "in withBlocksHash"))
l.Warnf("%v: %s", msg, f)
continue
}
@@ -534,6 +542,11 @@ func (t *readOnlyTransaction) withNeedLocal(folder []byte, truncate bool, fn Ite
type readWriteTransaction struct {
backend.WriteTransaction
readOnlyTransaction
indirectionTracker
}
type indirectionTracker interface {
recordIndirectionHashesForFile(f *protocol.FileInfo)
}
func (db *Lowlevel) newReadWriteTransaction(hooks ...backend.CommitHook) (readWriteTransaction, error) {
@@ -542,11 +555,9 @@ func (db *Lowlevel) newReadWriteTransaction(hooks ...backend.CommitHook) (readWr
return readWriteTransaction{}, err
}
return readWriteTransaction{
WriteTransaction: tran,
readOnlyTransaction: readOnlyTransaction{
ReadTransaction: tran,
keyer: db.keyer,
},
WriteTransaction: tran,
readOnlyTransaction: db.readOnlyTransactionFromBackendTransaction(tran),
indirectionTracker: db,
}, nil
}
@@ -606,6 +617,8 @@ func (t readWriteTransaction) putFile(fkey []byte, fi protocol.FileInfo) error {
fi.VersionHash = nil
}
t.indirectionTracker.recordIndirectionHashesForFile(&fi)
fiBs := mustMarshal(&fi)
return t.Put(fkey, fiBs)
}
@@ -647,10 +660,7 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
// Must happen before updating global meta: If this is the first
// item from this device, it will be initialized with the global state.
needBefore := false
if haveOldGlobal {
needBefore = Need(oldGlobalFV, haveRemoved, removedFV.Version)
}
needBefore := haveOldGlobal && Need(oldGlobalFV, haveRemoved, removedFV.Version)
needNow := Need(globalFV, true, file.Version)
if needBefore {
if keyBuf, oldGlobal, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, oldGlobalFV); err != nil {
@@ -665,7 +675,8 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
}
}
if needNow {
if keyBuf, global, err = t.updateGlobalGetGlobal(keyBuf, folder, name, file, globalFV); err != nil {
keyBuf, global, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, globalFV)
if err != nil {
return nil, false, err
}
gotGlobal = true
@@ -699,8 +710,11 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
// Add the new global to the global size counter
if !gotGlobal {
if keyBuf, global, err = t.updateGlobalGetGlobal(keyBuf, folder, name, file, globalFV); err != nil {
return nil, false, err
if globalFV.Version.Equal(file.Version) {
// The inserted file is the global file
global = file
} else {
keyBuf, global, err = t.getGlobalFromFileVersion(keyBuf, folder, name, true, globalFV)
}
gotGlobal = true
}
@@ -709,10 +723,7 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
// check for local (if not already done before)
if !bytes.Equal(device, protocol.LocalDeviceID[:]) {
localFV, haveLocal := fl.Get(protocol.LocalDeviceID[:])
needBefore := false
if haveOldGlobal {
needBefore = Need(oldGlobalFV, haveLocal, localFV.Version)
}
needBefore := haveOldGlobal && Need(oldGlobalFV, haveLocal, localFV.Version)
needNow := Need(globalFV, haveLocal, localFV.Version)
if needBefore {
meta.removeNeeded(protocol.LocalDeviceID, oldGlobal)
@@ -749,14 +760,6 @@ func (t readWriteTransaction) updateGlobal(gk, keyBuf, folder, device []byte, fi
return keyBuf, true, nil
}
func (t readWriteTransaction) updateGlobalGetGlobal(keyBuf, folder, name []byte, file protocol.FileInfo, fv FileVersion) ([]byte, protocol.FileIntf, error) {
if fv.Version.Equal(file.Version) {
// Inserted a new newest version
return keyBuf, file, nil
}
return t.getGlobalFromFileVersion(keyBuf, folder, name, true, fv)
}
func (t readWriteTransaction) updateLocalNeed(keyBuf, folder, name []byte, add bool) ([]byte, error) {
var err error
keyBuf, err = t.keyer.GenerateNeedFileKey(keyBuf, folder, name)
@@ -812,6 +815,7 @@ func (t readWriteTransaction) removeFromGlobal(gk, keyBuf, folder, device, file
}
oldGlobalFV, haveOldGlobal := fl.GetGlobal()
oldGlobalFV = oldGlobalFV.copy()
if !haveOldGlobal {
// Shouldn't ever happen, but doesn't hurt to handle.

View File

@@ -0,0 +1,17 @@
// Copyright (C) 2021 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/.
// +build darwin,!kqueue,cgo
package fs
import "github.com/syncthing/notify"
const (
subEventMask = notify.Create | notify.Remove | notify.Write | notify.Rename | notify.FSEventsInodeMetaMod
permEventMask = 0
rmEventMask = notify.Remove | notify.Rename
)

View File

@@ -4,8 +4,7 @@
// 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/.
// +build !linux,!windows,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris
// +build !darwin darwin,cgo
// +build !linux,!windows,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!darwin,!cgo
// Catch all platforms that are not specifically handled to use the generic
// event types.

View File

@@ -12,6 +12,7 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -445,6 +446,61 @@ func TestWatchModTime(t *testing.T) {
}
}
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false)
}
func TestModifyFile(t *testing.T) {
name := "modify"
old := createTestFile(name, "file")
modifyTestFile(name, old, "syncthing")
testCase := func() {
modifyTestFile(name, old, "modified")
}
expectedEvents := []Event{
{old, NonRemove},
}
allowedEvents := []Event{
{name, NonRemove},
}
sleepMs(1000)
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false)
}
func TestTruncateFileOnly(t *testing.T) {
name := "truncate"
file := createTestFile(name, "file")
modifyTestFile(name, file, "syncthing")
// modified the content to empty use os.WriteFile will first truncate the file
// (/os/file.go:696) then write nothing. This logic is also used in many editors,
// such as when emptying a file in VSCode or JetBrain
//
// darwin will only modified the inode's metadata, such us mtime, file size, etc.
// but would not modified the file directly, so FSEvent 'FSEventsModified' will not
// be received
//
// we should watch the FSEvent 'FSEventsInodeMetaMod' to watch the Inode metadata,
// and that should be considered as an NonRemove Event
//
// notify also considered FSEventsInodeMetaMod as Write Event
// /watcher_fsevents.go:89
testCase := func() {
modifyTestFile(name, file, "")
}
expectedEvents := []Event{
{file, NonRemove},
}
allowedEvents := []Event{
{file, NonRemove},
}
sleepMs(1000)
testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, true)
}
@@ -470,6 +526,15 @@ func renameTestFile(name string, old string, new string) {
}
}
func modifyTestFile(name string, file string, content string) {
joined := filepath.Join(testDirAbs, name, file)
err := ioutil.WriteFile(joined, []byte(content), 0755)
if err != nil {
panic(fmt.Sprintf("Failed to modify test file %s: %s", joined, err))
}
}
func sleepMs(ms int) {
time.Sleep(time.Duration(ms) * time.Millisecond)
}

View File

@@ -386,7 +386,6 @@ func (f *caseFilesystem) checkCaseExisting(name string) error {
}
type defaultRealCaser struct {
fs Filesystem
cache caseCache
}
@@ -396,13 +395,12 @@ func newDefaultRealCaser(fs Filesystem) *defaultRealCaser {
if err != nil {
panic(err)
}
caser := &defaultRealCaser{
fs: fs,
return &defaultRealCaser{
cache: caseCache{
fs: fs,
TwoQueueCache: cache,
},
}
return caser
}
func (r *defaultRealCaser) realCase(name string) (string, error) {
@@ -414,27 +412,6 @@ func (r *defaultRealCaser) realCase(name string) (string, error) {
for _, comp := range PathComponents(name) {
node := r.cache.getExpireAdd(realName)
node.once.Do(func() {
dirNames, err := r.fs.DirNames(realName)
if err != nil {
r.cache.Remove(realName)
node.err = err
return
}
num := len(dirNames)
node.children = make(map[string]struct{}, num)
node.lowerToReal = make(map[string]string, num)
lastLower := ""
for _, n := range dirNames {
node.children[n] = struct{}{}
lower := UnicodeLowercaseNormalized(n)
if lower != lastLower {
node.lowerToReal[lower] = n
lastLower = n
}
}
})
if node.err != nil {
return "", node.err
}
@@ -457,10 +434,29 @@ func (r *defaultRealCaser) dropCache() {
r.cache.Purge()
}
func newCaseNode() *caseNode {
return &caseNode{
expires: time.Now().Add(caseCacheTimeout),
type caseCache struct {
*lru.TwoQueueCache
fs Filesystem
mut sync.Mutex
}
// getExpireAdd gets an entry for the given key. If no entry exists, or it is
// expired a new one is created and added to the cache.
func (c *caseCache) getExpireAdd(key string) *caseNode {
c.mut.Lock()
defer c.mut.Unlock()
v, ok := c.Get(key)
if !ok {
node := newCaseNode(key, c.fs)
c.Add(key, node)
return node
}
node := v.(*caseNode)
if node.expires.Before(time.Now()) {
node = newCaseNode(key, c.fs)
c.Add(key, node)
}
return node
}
// The keys to children are "real", case resolved names of the path
@@ -474,30 +470,32 @@ type caseNode struct {
expires time.Time
lowerToReal map[string]string
children map[string]struct{}
once sync.Once
err error
}
type caseCache struct {
*lru.TwoQueueCache
mut sync.Mutex
}
// getExpireAdd gets an entry for the given key. If no entry exists, or it is
// expired a new one is created and added to the cache.
func (c *caseCache) getExpireAdd(key string) *caseNode {
c.mut.Lock()
defer c.mut.Unlock()
v, ok := c.Get(key)
if !ok {
node := newCaseNode()
c.Add(key, node)
func newCaseNode(name string, filesystem Filesystem) *caseNode {
node := new(caseNode)
dirNames, err := filesystem.DirNames(name)
// Set expiry after calling DirNames in case this is super-slow
// (e.g. dirs with many children on android)
node.expires = time.Now().Add(caseCacheTimeout)
if err != nil {
node.err = err
return node
}
node := v.(*caseNode)
if node.expires.Before(time.Now()) {
node = newCaseNode()
c.Add(key, node)
num := len(dirNames)
node.children = make(map[string]struct{}, num)
node.lowerToReal = make(map[string]string, num)
lastLower := ""
for _, n := range dirNames {
node.children[n] = struct{}{}
lower := UnicodeLowercaseNormalized(n)
if lower != lastLower {
node.lowerToReal[lower] = n
lastLower = n
}
}
return node
}

View File

@@ -885,7 +885,13 @@ func (f *fakeFile) Truncate(size int64) error {
defer f.mut.Unlock()
if f.content != nil {
f.content = f.content[:int(size)]
if int64(cap(f.content)) < size {
c := make([]byte, size)
copy(c[:len(f.content)], f.content)
f.content = c
} else {
f.content = f.content[:int(size)]
}
}
f.rng = nil
f.size = size

View File

@@ -1,109 +0,0 @@
// Copyright (C) 2018 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 https://mozilla.org/MPL/2.0/.
package model
import (
"context"
"sync"
)
type byteSemaphore struct {
max int
available int
mut sync.Mutex
cond *sync.Cond
}
func newByteSemaphore(max int) *byteSemaphore {
if max < 0 {
max = 0
}
s := byteSemaphore{
max: max,
available: max,
}
s.cond = sync.NewCond(&s.mut)
return &s
}
func (s *byteSemaphore) takeWithContext(ctx context.Context, bytes int) error {
done := make(chan struct{})
var err error
go func() {
err = s.takeInner(ctx, bytes)
close(done)
}()
select {
case <-done:
case <-ctx.Done():
s.cond.Broadcast()
<-done
}
return err
}
func (s *byteSemaphore) take(bytes int) {
_ = s.takeInner(context.Background(), bytes)
}
func (s *byteSemaphore) takeInner(ctx context.Context, bytes int) error {
// Checking context for bytes <= s.available is required for testing and doesn't do any harm.
select {
case <-ctx.Done():
return ctx.Err()
default:
}
s.mut.Lock()
defer s.mut.Unlock()
if bytes > s.max {
bytes = s.max
}
for bytes > s.available {
s.cond.Wait()
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if bytes > s.max {
bytes = s.max
}
}
s.available -= bytes
return nil
}
func (s *byteSemaphore) give(bytes int) {
s.mut.Lock()
if bytes > s.max {
bytes = s.max
}
if s.available+bytes > s.max {
s.available = s.max
} else {
s.available += bytes
}
s.cond.Broadcast()
s.mut.Unlock()
}
func (s *byteSemaphore) setCapacity(cap int) {
if cap < 0 {
cap = 0
}
s.mut.Lock()
diff := cap - s.max
s.max = cap
s.available += diff
if s.available < 0 {
s.available = 0
} else if s.available > s.max {
s.available = s.max
}
s.cond.Broadcast()
s.mut.Unlock()
}

View File

@@ -27,14 +27,18 @@ func newFakeConnection(id protocol.DeviceID, model Model) *fakeConnection {
Connection: new(protocolmocks.Connection),
id: id,
model: model,
closed: make(chan struct{}),
}
f.RequestCalls(func(ctx context.Context, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
return f.fileData[name], nil
})
f.IDReturns(id)
f.CloseCalls(func(err error) {
f.closeOnce.Do(func() {
close(f.closed)
})
model.Closed(id, err)
f.ClosedReturns(true)
f.ClosedReturns(f.closed)
})
return f
}
@@ -47,6 +51,8 @@ type fakeConnection struct {
fileData map[string][]byte
folder string
model Model
closed chan struct{}
closeOnce sync.Once
mut sync.Mutex
}

View File

@@ -38,7 +38,7 @@ type folder struct {
stateTracker
config.FolderConfiguration
*stats.FolderStatisticsReference
ioLimiter *byteSemaphore
ioLimiter *util.Semaphore
localFlags uint32
@@ -91,7 +91,7 @@ type puller interface {
pull() (bool, error) // true when successful and should not be retried
}
func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, evLogger events.Logger, ioLimiter *byteSemaphore, ver versioner.Versioner) folder {
func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, evLogger events.Logger, ioLimiter *util.Semaphore, ver versioner.Versioner) folder {
f := folder{
stateTracker: newStateTracker(cfg.ID, evLogger),
FolderConfiguration: cfg,
@@ -375,10 +375,10 @@ func (f *folder) pull() (success bool, err error) {
if f.Type != config.FolderTypeSendOnly {
f.setState(FolderSyncWaiting)
if err := f.ioLimiter.takeWithContext(f.ctx, 1); err != nil {
if err := f.ioLimiter.TakeWithContext(f.ctx, 1); err != nil {
return true, err
}
defer f.ioLimiter.give(1)
defer f.ioLimiter.Give(1)
}
startTime := time.Now()
@@ -439,10 +439,10 @@ func (f *folder) scanSubdirs(subDirs []string) error {
f.setState(FolderScanWaiting)
defer f.setState(FolderIdle)
if err := f.ioLimiter.takeWithContext(f.ctx, 1); err != nil {
if err := f.ioLimiter.TakeWithContext(f.ctx, 1); err != nil {
return err
}
defer f.ioLimiter.give(1)
defer f.ioLimiter.Give(1)
for i := range subDirs {
sub := osutil.NativeFilename(subDirs[i])
@@ -473,16 +473,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
f.setState(FolderScanning)
f.clearScanErrors(subDirs)
batch := db.NewFileInfoBatch(func(fs []protocol.FileInfo) error {
if err := f.getHealthErrorWithoutIgnores(); err != nil {
l.Debugf("Stopping scan of folder %s due to: %s", f.Description(), err)
return err
}
f.updateLocalsFromScanning(fs)
return nil
})
batchAppend := f.scanSubdirsBatchAppendFunc(batch)
batch := f.newScanBatch()
// Schedule a pull after scanning, but only if we actually detected any
// changes.
@@ -494,7 +485,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
}
}()
changesHere, err := f.scanSubdirsChangedAndNew(subDirs, batch, batchAppend)
changesHere, err := f.scanSubdirsChangedAndNew(subDirs, batch)
changes += changesHere
if err != nil {
return err
@@ -513,7 +504,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
// Do a scan of the database for each prefix, to check for deleted and
// ignored files.
changesHere, err = f.scanSubdirsDeletedAndIgnored(subDirs, batch, batchAppend)
changesHere, err = f.scanSubdirsDeletedAndIgnored(subDirs, batch)
changes += changesHere
if err != nil {
return err
@@ -527,58 +518,57 @@ func (f *folder) scanSubdirs(subDirs []string) error {
return nil
}
type batchAppendFunc func(protocol.FileInfo, *db.Snapshot) bool
func (f *folder) scanSubdirsBatchAppendFunc(batch *db.FileInfoBatch) batchAppendFunc {
// Resolve items which are identical with the global state.
switch f.Type {
case config.FolderTypeReceiveOnly:
return func(fi protocol.FileInfo, snap *db.Snapshot) bool {
switch gf, ok := snap.GetGlobal(fi.Name); {
case !ok:
case gf.IsEquivalentOptional(fi, f.modTimeWindow, false, false, protocol.FlagLocalReceiveOnly):
// What we have locally is equivalent to the global file.
fi.Version = gf.Version
l.Debugf("%v scanning: Merging identical locally changed item with global", f, fi)
fallthrough
case fi.IsDeleted() && (gf.IsReceiveOnlyChanged() || gf.IsDeleted()):
// Our item is deleted and the global item is our own
// receive only file or deleted too. In the former
// case we can't delete file infos, so we just
// pretend it is a normal deleted file (nobody
// cares about that).
l.Debugf("%v scanning: Marking item as not locally changed", f, fi)
fi.LocalFlags &^= protocol.FlagLocalReceiveOnly
}
batch.Append(fi)
return true
}
case config.FolderTypeReceiveEncrypted:
return func(fi protocol.FileInfo, _ *db.Snapshot) bool {
// This is a "virtual" parent directory of encrypted files.
// We don't track it, but check if anything still exists
// within and delete it otherwise.
if fi.IsDirectory() && protocol.IsEncryptedParent(fs.PathComponents(fi.Name)) {
if names, err := f.mtimefs.DirNames(fi.Name); err == nil && len(names) == 0 {
f.mtimefs.Remove(fi.Name)
}
return false
}
// Any local change must not be sent as index entry to
// remotes and show up as an error in the UI.
fi.LocalFlags = protocol.FlagLocalReceiveOnly
batch.Append(fi)
return true
}
default:
return func(fi protocol.FileInfo, _ *db.Snapshot) bool {
batch.Append(fi)
return true
}
}
type scanBatch struct {
*db.FileInfoBatch
f *folder
}
func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *db.FileInfoBatch, batchAppend batchAppendFunc) (int, error) {
func (f *folder) newScanBatch() *scanBatch {
b := &scanBatch{
f: f,
}
b.FileInfoBatch = db.NewFileInfoBatch(func(fs []protocol.FileInfo) error {
if err := b.f.getHealthErrorWithoutIgnores(); err != nil {
l.Debugf("Stopping scan of folder %s due to: %s", b.f.Description(), err)
return err
}
b.f.updateLocalsFromScanning(fs)
return nil
})
return b
}
// Append adds the fileinfo to the batch for updating, and does a few checks.
// It returns false if the checks result in the file not going to be updated or removed.
func (b *scanBatch) Append(fi protocol.FileInfo, snap *db.Snapshot) bool {
// Check for a "virtual" parent directory of encrypted files. We don't track
// it, but check if anything still exists within and delete it otherwise.
if b.f.Type == config.FolderTypeReceiveEncrypted && fi.IsDirectory() && protocol.IsEncryptedParent(fs.PathComponents(fi.Name)) {
if names, err := b.f.mtimefs.DirNames(fi.Name); err == nil && len(names) == 0 {
b.f.mtimefs.Remove(fi.Name)
}
return false
}
// Resolve receive-only items which are identical with the global state or
// the global item is our own receive-only item.
// Except if they are in a receive-encrypted folder and are locally added.
// Those must never be sent in index updates and thus must retain the flag.
switch gf, ok := snap.GetGlobal(fi.Name); {
case !ok:
case gf.IsReceiveOnlyChanged():
if b.f.Type == config.FolderTypeReceiveOnly && fi.Deleted {
l.Debugf("%v scanning: Marking deleted item as not locally changed", b.f, fi)
fi.LocalFlags &^= protocol.FlagLocalReceiveOnly
}
case gf.IsEquivalentOptional(fi, b.f.modTimeWindow, false, false, protocol.FlagLocalReceiveOnly):
l.Debugf("%v scanning: Replacing scanned file info with global as it's equivalent", b.f, fi)
fi = gf
}
b.FileInfoBatch.Append(fi)
return true
}
func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *scanBatch) (int, error) {
changes := 0
snap, err := f.dbSnapshot()
if err != nil {
@@ -630,7 +620,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *db.FileInfoBa
return changes, err
}
if batchAppend(res.File, snap) {
if batch.Append(res.File, snap) {
changes++
}
@@ -638,7 +628,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *db.FileInfoBa
case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted:
default:
if nf, ok := f.findRename(snap, res.File, alreadyUsedOrExisting); ok {
if batchAppend(nf, snap) {
if batch.Append(nf, snap) {
changes++
}
}
@@ -648,7 +638,7 @@ func (f *folder) scanSubdirsChangedAndNew(subDirs []string, batch *db.FileInfoBa
return changes, nil
}
func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *db.FileInfoBatch, batchAppend batchAppendFunc) (int, error) {
func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *scanBatch) (int, error) {
var toIgnore []db.FileInfoTruncated
ignoredParent := ""
changes := 0
@@ -679,7 +669,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *db.FileIn
for _, file := range toIgnore {
l.Debugln("marking file as ignored", file)
nf := file.ConvertToIgnoredFileInfo()
if batchAppend(nf, snap) {
if batch.Append(nf, snap) {
changes++
}
if err := batch.FlushIfFull(); err != nil {
@@ -709,7 +699,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *db.FileIn
l.Debugln("marking file as ignored", file)
nf := file.ConvertToIgnoredFileInfo()
if batchAppend(nf, snap) {
if batch.Append(nf, snap) {
changes++
}
@@ -739,23 +729,35 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *db.FileIn
nf.Version = protocol.Vector{}
}
l.Debugln("marking file as deleted", nf)
if batchAppend(nf, snap) {
if batch.Append(nf, snap) {
changes++
}
case file.IsDeleted() && file.IsReceiveOnlyChanged() && f.Type == config.FolderTypeReceiveOnly && len(snap.Availability(file.Name)) == 0:
file.Version = protocol.Vector{}
file.LocalFlags &^= protocol.FlagLocalReceiveOnly
l.Debugln("marking deleted item that doesn't exist anywhere as not receive-only", file)
if batchAppend(file.ConvertDeletedToFileInfo(), snap) {
changes++
}
case file.IsDeleted() && file.IsReceiveOnlyChanged() && f.Type != config.FolderTypeReceiveOnly:
// No need to bump the version for a file that was and is
// deleted and just the folder type/local flags changed.
file.LocalFlags &^= protocol.FlagLocalReceiveOnly
l.Debugln("removing receive-only flag on deleted item", file)
if batchAppend(file.ConvertDeletedToFileInfo(), snap) {
changes++
case file.IsDeleted() && file.IsReceiveOnlyChanged():
switch f.Type {
case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted:
if gf, _ := snap.GetGlobal(file.Name); gf.IsDeleted() {
// Our item is deleted and the global item is deleted too. We just
// pretend it is a normal deleted file (nobody cares about that).
// Except if this is a receive-encrypted folder and it
// is a locally added file. Those must never be sent
// in index updates and thus must retain the flag.
if f.Type == config.FolderTypeReceiveEncrypted && gf.IsReceiveOnlyChanged() {
return true
}
l.Debugf("%v scanning: Marking globally deleted item as not locally changed: %v", f, file.Name)
file.LocalFlags &^= protocol.FlagLocalReceiveOnly
if batch.Append(file.ConvertDeletedToFileInfo(), snap) {
changes++
}
}
default:
// No need to bump the version for a file that was and is
// deleted and just the folder type/local flags changed.
file.LocalFlags &^= protocol.FlagLocalReceiveOnly
l.Debugln("removing receive-only flag on deleted item", file)
if batch.Append(file.ConvertDeletedToFileInfo(), snap) {
changes++
}
}
}
@@ -772,7 +774,7 @@ func (f *folder) scanSubdirsDeletedAndIgnored(subDirs []string, batch *db.FileIn
for _, file := range toIgnore {
l.Debugln("marking file as ignored", f)
nf := file.ConvertToIgnoredFileInfo()
if batchAppend(nf, snap) {
if batch.Append(nf, snap) {
changes++
}
if iterError = batch.FlushIfFull(); iterError != nil {
@@ -870,10 +872,10 @@ func (f *folder) versionCleanupTimerFired() {
f.setState(FolderCleanWaiting)
defer f.setState(FolderIdle)
if err := f.ioLimiter.takeWithContext(f.ctx, 1); err != nil {
if err := f.ioLimiter.TakeWithContext(f.ctx, 1); err != nil {
return
}
defer f.ioLimiter.give(1)
defer f.ioLimiter.Give(1)
f.setState(FolderCleaning)

View File

@@ -16,6 +16,7 @@ import (
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/versioner"
)
@@ -27,8 +28,10 @@ type receiveEncryptedFolder struct {
*sendReceiveFolder
}
func newReceiveEncryptedFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service {
return &receiveEncryptedFolder{newSendReceiveFolder(model, fset, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder)}
func newReceiveEncryptedFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *util.Semaphore) service {
f := &receiveEncryptedFolder{newSendReceiveFolder(model, fset, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder)}
f.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files
return f
}
func (f *receiveEncryptedFolder) Revert() {
@@ -108,5 +111,6 @@ func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string, snap *db.Snapsh
if err := f.deleteDirOnDisk(dir, snap, scanChan); err != nil {
f.newScanError(dir, fmt.Errorf("deleting unexpected dir: %w", err))
}
scanChan <- dir
}
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/versioner"
)
@@ -56,7 +57,7 @@ type receiveOnlyFolder struct {
*sendReceiveFolder
}
func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service {
func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *util.Semaphore) service {
sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder)
sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files
return &receiveOnlyFolder{sr}
@@ -100,12 +101,21 @@ func (f *receiveOnlyFolder) revert() error {
}
fi.LocalFlags &^= protocol.FlagLocalReceiveOnly
if len(fi.Version.Counters) == 1 && fi.Version.Counters[0].ID == f.shortID {
// We are the only device mentioned in the version vector so the
// file must originate here. A revert then means to delete it.
switch gf, ok := snap.GetGlobal(fi.Name); {
case !ok:
msg := "Unexpected global file that we have locally"
l.Debugf("%v revert: %v: %v", f, msg, fi.Name)
f.evLogger.Log(events.Failure, msg)
return true
case gf.IsReceiveOnlyChanged():
// The global file is our own. A revert then means to delete it.
// We'll delete files directly, directories get queued and
// handled below.
if fi.Deleted {
fi.Version = protocol.Vector{} // if this file ever resurfaces anywhere we want our delete to be strictly older
break
}
handled, err := delQueue.handle(fi, snap)
if err != nil {
l.Infof("Revert: deleting %s: %v\n", fi.Name, err)
@@ -114,10 +124,12 @@ func (f *receiveOnlyFolder) revert() error {
if !handled {
return true // continue
}
fi.SetDeleted(f.shortID)
fi.Version = protocol.Vector{} // if this file ever resurfaces anywhere we want our delete to be strictly older
} else {
case gf.IsEquivalentOptional(fi, f.modTimeWindow, false, false, protocol.FlagLocalReceiveOnly):
// What we have locally is equivalent to the global file.
fi = gf
default:
// Revert means to throw away our local changes. We reset the
// version to the empty vector, which is strictly older than any
// other existing version. It is not in conflict with anything,

View File

@@ -14,6 +14,7 @@ import (
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/scanner"
@@ -29,6 +30,7 @@ func TestRecvOnlyRevertDeletes(t *testing.T) {
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
addFakeConn(m, device1, f.ID)
// Create some test data
@@ -110,6 +112,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
addFakeConn(m, device1, f.ID)
// Create some test data
@@ -136,7 +139,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
if size.Files != 1 || size.Directories != 1 {
t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
}
size = needSize(t, m, "ro")
size = needSizeLocal(t, m, "ro")
if size.Files+size.Directories > 0 {
t.Fatalf("Need: expected nothing: %+v", size)
}
@@ -165,7 +168,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
t.Fatalf("Local: expected the new file to be reflected: %+v", size)
}
size = needSize(t, m, "ro")
size = needSizeLocal(t, m, "ro")
if size.Files+size.Directories > 0 {
t.Fatalf("Need: expected nothing: %+v", size)
}
@@ -186,7 +189,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
t.Fatalf("Local: expected the local size to remain: %+v", size)
}
size = needSize(t, m, "ro")
size = needSizeLocal(t, m, "ro")
if size.Files != 1 || size.Bytes != int64(len(oldData)) {
t.Fatalf("Local: expected to need the old file data: %+v", size)
}
@@ -199,6 +202,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
addFakeConn(m, device1, f.ID)
// Create some test data
@@ -225,7 +229,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
if size.Files != 1 || size.Directories != 1 {
t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
}
size = needSize(t, m, "ro")
size = needSizeLocal(t, m, "ro")
if size.Files+size.Directories > 0 {
t.Fatalf("Need: expected nothing: %+v", size)
}
@@ -268,6 +272,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
addFakeConn(m, device1, f.ID)
// Create some test data
@@ -294,7 +299,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
if size.Files != 1 || size.Directories != 1 {
t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
}
size = needSize(t, m, "ro")
size = needSizeLocal(t, m, "ro")
if size.Files+size.Directories > 0 {
t.Fatalf("Need: expected nothing: %+v", size)
}
@@ -332,6 +337,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
addFakeConn(m, device1, f.ID)
// Create some test data
@@ -358,7 +364,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
if size.Files != 1 || size.Directories != 1 {
t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
}
size = needSize(t, m, "ro")
size = needSizeLocal(t, m, "ro")
if size.Files+size.Directories > 0 {
t.Fatalf("Need: expected nothing: %+v", size)
}
@@ -410,6 +416,70 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) {
}
}
func TestRecvOnlyRevertOwnID(t *testing.T) {
// If the folder was receive-only in the past, the global item might have
// only our id in the version vector and be valid. There was a bug based on
// the incorrect assumption that this can never happen.
// Get us a model up and running
m, f, wcfgCancel := setupROFolder(t)
defer wcfgCancel()
ffs := f.Filesystem()
defer cleanupModel(m)
addFakeConn(m, device1, f.ID)
// Create some test data
must(t, ffs.MkdirAll(".stfolder", 0755))
data := []byte("hello\n")
name := "foo"
must(t, writeFile(ffs, name, data, 0644))
// Make sure the file is scanned and locally changed
must(t, m.ScanFolder("ro"))
fi, ok := m.testCurrentFolderFile(f.ID, name)
if !ok {
t.Fatal("File missing")
} else if !fi.IsReceiveOnlyChanged() {
t.Fatal("File should be receiveonly changed")
}
fi.LocalFlags = 0
v := fi.Version.Counters[0].Value
fi.Version.Counters[0].Value = uint64(time.Unix(int64(v), 0).Add(-10 * time.Second).Unix())
// Monitor the outcome
sub := f.evLogger.Subscribe(events.LocalIndexUpdated)
defer sub.Unsubscribe()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
for {
select {
case <-ctx.Done():
return
case <-sub.C():
if file, _ := m.testCurrentFolderFile(f.ID, name); file.Deleted {
t.Error("local file was deleted")
cancel()
} else if file.IsEquivalent(fi, f.modTimeWindow) {
cancel() // That's what we are waiting for
}
}
}
}()
// Receive an index update with an older version, but valid and then revert
must(t, m.Index(device1, f.ID, []protocol.FileInfo{fi}))
f.Revert()
select {
case <-ctx.Done():
case <-time.After(10 * time.Second):
t.Fatal("timed out")
}
}
func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.FileInfo {
t.Helper()

View File

@@ -12,6 +12,7 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/ignore"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/versioner"
)
@@ -23,7 +24,7 @@ type sendOnlyFolder struct {
folder
}
func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service {
func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, evLogger events.Logger, ioLimiter *util.Semaphore) service {
f := &sendOnlyFolder{
folder: newFolder(model, fset, ignores, cfg, evLogger, ioLimiter, nil),
}

View File

@@ -28,6 +28,7 @@ import (
"github.com/syncthing/syncthing/lib/scanner"
"github.com/syncthing/syncthing/lib/sha256"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/versioner"
"github.com/syncthing/syncthing/lib/weakhash"
)
@@ -123,17 +124,17 @@ type sendReceiveFolder struct {
queue *jobQueue
blockPullReorderer blockPullReorderer
writeLimiter *byteSemaphore
writeLimiter *util.Semaphore
tempPullErrors map[string]string // pull errors that might be just transient
}
func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service {
func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *util.Semaphore) service {
f := &sendReceiveFolder{
folder: newFolder(model, fset, ignores, cfg, evLogger, ioLimiter, ver),
queue: newJobQueue(),
blockPullReorderer: newBlockPullReorderer(cfg.BlockPullOrder, model.id, cfg.DeviceIDs()),
writeLimiter: newByteSemaphore(cfg.MaxConcurrentWrites),
writeLimiter: util.NewSemaphore(cfg.MaxConcurrentWrites),
}
f.folder.puller = f
@@ -1435,7 +1436,7 @@ func (f *sendReceiveFolder) verifyBuffer(buf []byte, block protocol.BlockInfo) e
}
func (f *sendReceiveFolder) pullerRoutine(snap *db.Snapshot, in <-chan pullBlockState, out chan<- *sharedPullerState) {
requestLimiter := newByteSemaphore(f.PullerMaxPendingKiB * 1024)
requestLimiter := util.NewSemaphore(f.PullerMaxPendingKiB * 1024)
wg := sync.NewWaitGroup()
for state := range in {
@@ -1453,7 +1454,7 @@ func (f *sendReceiveFolder) pullerRoutine(snap *db.Snapshot, in <-chan pullBlock
state := state
bytes := int(state.block.Size)
if err := requestLimiter.takeWithContext(f.ctx, bytes); err != nil {
if err := requestLimiter.TakeWithContext(f.ctx, bytes); err != nil {
state.fail(err)
out <- state.sharedPullerState
continue
@@ -1463,7 +1464,7 @@ func (f *sendReceiveFolder) pullerRoutine(snap *db.Snapshot, in <-chan pullBlock
go func() {
defer wg.Done()
defer requestLimiter.give(bytes)
defer requestLimiter.Give(bytes)
f.pullBlock(state, snap, out)
}()
@@ -2085,10 +2086,10 @@ func (f *sendReceiveFolder) limitedWriteAt(fd io.WriterAt, data []byte, offset i
}
func (f *sendReceiveFolder) withLimiter(fn func() error) error {
if err := f.writeLimiter.takeWithContext(f.ctx, 1); err != nil {
if err := f.writeLimiter.TakeWithContext(f.ctx, 1); err != nil {
return err
}
defer f.writeLimiter.give(1)
defer f.writeLimiter.Give(1)
return fn()
}

View File

@@ -1280,6 +1280,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
m, f, wcfgCancel := setupSendReceiveFolder(t)
defer cleanupSRFolder(f, m, wcfgCancel)
addFakeConn(m, device1, f.ID)
name := "foo"
if fd, err := f.mtimefs.Create(name); err != nil {

575
lib/model/indexhandler.go Normal file
View File

@@ -0,0 +1,575 @@
// Copyright (C) 2020 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 https://mozilla.org/MPL/2.0/.
package model
import (
"context"
"fmt"
"sync"
"time"
"github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/svcutil"
)
type indexHandler struct {
conn protocol.Connection
downloads *deviceDownloadState
folder string
folderIsReceiveEncrypted bool
prevSequence int64
evLogger events.Logger
token suture.ServiceToken
cond *sync.Cond
paused bool
fset *db.FileSet
runner service
}
func newIndexHandler(conn protocol.Connection, downloads *deviceDownloadState, folder config.FolderConfiguration, fset *db.FileSet, runner service, startInfo *clusterConfigDeviceInfo, evLogger events.Logger) *indexHandler {
myIndexID := fset.IndexID(protocol.LocalDeviceID)
mySequence := fset.Sequence(protocol.LocalDeviceID)
var startSequence int64
// This is the other side's description of what it knows
// about us. Lets check to see if we can start sending index
// updates directly or need to send the index from start...
if startInfo.local.IndexID == myIndexID {
// They say they've seen our index ID before, so we can
// send a delta update only.
if startInfo.local.MaxSequence > mySequence {
// Safety check. They claim to have more or newer
// index data than we have - either we have lost
// index data, or reset the index without resetting
// the IndexID, or something else weird has
// happened. We send a full index to reset the
// situation.
l.Infof("Device %v folder %s is delta index compatible, but seems out of sync with reality", conn.ID().Short(), folder.Description())
startSequence = 0
} else {
l.Debugf("Device %v folder %s is delta index compatible (mlv=%d)", conn.ID().Short(), folder.Description(), startInfo.local.MaxSequence)
startSequence = startInfo.local.MaxSequence
}
} else if startInfo.local.IndexID != 0 {
// They say they've seen an index ID from us, but it's
// not the right one. Either they are confused or we
// must have reset our database since last talking to
// them. We'll start with a full index transfer.
l.Infof("Device %v folder %s has mismatching index ID for us (%v != %v)", conn.ID().Short(), folder.Description(), startInfo.local.IndexID, myIndexID)
startSequence = 0
} else {
l.Debugf("Device %v folder %s has no index ID for us", conn.ID().Short(), folder.Description())
}
// This is the other side's description of themselves. We
// check to see that it matches the IndexID we have on file,
// otherwise we drop our old index data and expect to get a
// completely new set.
theirIndexID := fset.IndexID(conn.ID())
if startInfo.remote.IndexID == 0 {
// They're not announcing an index ID. This means they
// do not support delta indexes and we should clear any
// information we have from them before accepting their
// index, which will presumably be a full index.
l.Debugf("Device %v folder %s does not announce an index ID", conn.ID().Short(), folder.Description())
fset.Drop(conn.ID())
} else if startInfo.remote.IndexID != theirIndexID {
// The index ID we have on file is not what they're
// announcing. They must have reset their database and
// will probably send us a full index. We drop any
// information we have and remember this new index ID
// instead.
l.Infof("Device %v folder %s has a new index ID (%v)", conn.ID().Short(), folder.Description(), startInfo.remote.IndexID)
fset.Drop(conn.ID())
fset.SetIndexID(conn.ID(), startInfo.remote.IndexID)
}
return &indexHandler{
conn: conn,
downloads: downloads,
folder: folder.ID,
folderIsReceiveEncrypted: folder.Type == config.FolderTypeReceiveEncrypted,
prevSequence: startSequence,
evLogger: evLogger,
fset: fset,
runner: runner,
cond: sync.NewCond(new(sync.Mutex)),
}
}
// waitForFileset waits for the handler to resume and fetches the current fileset.
func (s *indexHandler) waitForFileset(ctx context.Context) (*db.FileSet, error) {
s.cond.L.Lock()
defer s.cond.L.Unlock()
for s.paused {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
s.cond.Wait()
}
}
return s.fset, nil
}
func (s *indexHandler) Serve(ctx context.Context) (err error) {
l.Debugf("Starting index handler for %s to %s at %s (slv=%d)", s.folder, s.conn.ID(), s.conn, s.prevSequence)
stop := make(chan struct{})
defer func() {
err = svcutil.NoRestartErr(err)
l.Debugf("Exiting index handler for %s to %s at %s: %v", s.folder, s.conn.ID(), s.conn, err)
close(stop)
}()
// Broadcast the pause cond when the context quits
go func() {
select {
case <-ctx.Done():
s.cond.Broadcast()
case <-stop:
}
}()
// We need to send one index, regardless of whether there is something to send or not
fset, err := s.waitForFileset(ctx)
if err != nil {
return err
}
err = s.sendIndexTo(ctx, fset)
// Subscribe to LocalIndexUpdated (we have new information to send) and
// DeviceDisconnected (it might be us who disconnected, so we should
// exit).
sub := s.evLogger.Subscribe(events.LocalIndexUpdated | events.DeviceDisconnected)
defer sub.Unsubscribe()
evChan := sub.C()
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for err == nil {
fset, err = s.waitForFileset(ctx)
if err != nil {
return err
}
// While we have sent a sequence at least equal to the one
// currently in the database, wait for the local index to update. The
// local index may update for other folders than the one we are
// sending for.
if fset.Sequence(protocol.LocalDeviceID) <= s.prevSequence {
select {
case <-ctx.Done():
return ctx.Err()
case <-evChan:
case <-ticker.C:
}
continue
}
err = s.sendIndexTo(ctx, fset)
// Wait a short amount of time before entering the next loop. If there
// are continuous changes happening to the local index, this gives us
// time to batch them up a little.
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(250 * time.Millisecond):
}
}
return err
}
// resume might be called because the folder was actually resumed, or just
// because the folder config changed (and thus the runner and potentially fset).
func (s *indexHandler) resume(fset *db.FileSet, runner service) {
s.cond.L.Lock()
s.paused = false
s.fset = fset
s.runner = runner
s.cond.Broadcast()
s.cond.L.Unlock()
}
func (s *indexHandler) pause() {
s.cond.L.Lock()
if s.paused {
s.evLogger.Log(events.Failure, "index handler got paused while already paused")
}
s.paused = true
s.fset = nil
s.runner = nil
s.cond.Broadcast()
s.cond.L.Unlock()
}
// sendIndexTo sends file infos with a sequence number higher than prevSequence and
// returns the highest sent sequence number.
func (s *indexHandler) sendIndexTo(ctx context.Context, fset *db.FileSet) error {
initial := s.prevSequence == 0
batch := db.NewFileInfoBatch(nil)
batch.SetFlushFunc(func(fs []protocol.FileInfo) error {
l.Debugf("%v: Sending %d files (<%d bytes)", s, len(fs), batch.Size())
if initial {
initial = false
return s.conn.Index(ctx, s.folder, fs)
}
return s.conn.IndexUpdate(ctx, s.folder, fs)
})
var err error
var f protocol.FileInfo
snap, err := fset.Snapshot()
if err != nil {
return svcutil.AsFatalErr(err, svcutil.ExitError)
}
defer snap.Release()
previousWasDelete := false
snap.WithHaveSequence(s.prevSequence+1, func(fi protocol.FileIntf) bool {
// This is to make sure that renames (which is an add followed by a delete) land in the same batch.
// Even if the batch is full, we allow a last delete to slip in, we do this by making sure that
// the batch ends with a non-delete, or that the last item in the batch is already a delete
if batch.Full() && (!fi.IsDeleted() || previousWasDelete) {
if err = batch.Flush(); err != nil {
return false
}
}
if shouldDebug() {
if fi.SequenceNo() < s.prevSequence+1 {
panic(fmt.Sprintln("sequence lower than requested, got:", fi.SequenceNo(), ", asked to start at:", s.prevSequence+1))
}
}
if f.Sequence > 0 && fi.SequenceNo() <= f.Sequence {
l.Warnln("Non-increasing sequence detected: Checking and repairing the db...")
// Abort this round of index sending - the next one will pick
// up from the last successful one with the repeaired db.
defer func() {
if fixed, dbErr := fset.RepairSequence(); dbErr != nil {
l.Warnln("Failed repairing sequence entries:", dbErr)
panic("Failed repairing sequence entries")
} else {
s.evLogger.Log(events.Failure, "detected and repaired non-increasing sequence")
l.Infof("Repaired %v sequence entries in database", fixed)
}
}()
return false
}
f = fi.(protocol.FileInfo)
// If this is a folder receiving encrypted files only, we
// mustn't ever send locally changed file infos. Those aren't
// encrypted and thus would be a protocol error at the remote.
if s.folderIsReceiveEncrypted && fi.IsReceiveOnlyChanged() {
return true
}
f = prepareFileInfoForIndex(f)
previousWasDelete = f.IsDeleted()
batch.Append(f)
return true
})
if err != nil {
return err
}
err = batch.Flush()
// True if there was nothing to be sent
if f.Sequence == 0 {
return err
}
s.prevSequence = f.Sequence
return err
}
func (s *indexHandler) receive(fs []protocol.FileInfo, update bool, op string) error {
deviceID := s.conn.ID()
s.cond.L.Lock()
paused := s.paused
fset := s.fset
runner := s.runner
s.cond.L.Unlock()
if paused {
l.Infof("%v for paused folder %q", op, s.folder)
return fmt.Errorf("%v: %w", s.folder, ErrFolderPaused)
}
defer runner.SchedulePull()
s.downloads.Update(s.folder, makeForgetUpdate(fs))
if !update {
fset.Drop(deviceID)
}
for i := range fs {
// The local attributes should never be transmitted over the wire.
// Make sure they look like they weren't.
fs[i].LocalFlags = 0
fs[i].VersionHash = nil
}
fset.Update(deviceID, fs)
seq := fset.Sequence(deviceID)
s.evLogger.Log(events.RemoteIndexUpdated, map[string]interface{}{
"device": deviceID.String(),
"folder": s.folder,
"items": len(fs),
"sequence": seq,
"version": seq, // legacy for sequence
})
return nil
}
func prepareFileInfoForIndex(f protocol.FileInfo) protocol.FileInfo {
// Mark the file as invalid if any of the local bad stuff flags are set.
f.RawInvalid = f.IsInvalid()
// If the file is marked LocalReceive (i.e., changed locally on a
// receive only folder) we do not want it to ever become the
// globally best version, invalid or not.
if f.IsReceiveOnlyChanged() {
f.Version = protocol.Vector{}
}
// never sent externally
f.LocalFlags = 0
f.VersionHash = nil
return f
}
func (s *indexHandler) String() string {
return fmt.Sprintf("indexHandler@%p for %s to %s at %s", s, s.folder, s.conn.ID().Short(), s.conn)
}
type indexHandlerRegistry struct {
sup *suture.Supervisor
evLogger events.Logger
conn protocol.Connection
downloads *deviceDownloadState
indexHandlers map[string]*indexHandler
startInfos map[string]*clusterConfigDeviceInfo
folderStates map[string]*indexHandlerFolderState
mut sync.Mutex
}
type indexHandlerFolderState struct {
cfg config.FolderConfiguration
fset *db.FileSet
runner service
}
func newIndexHandlerRegistry(conn protocol.Connection, downloads *deviceDownloadState, closed chan struct{}, parentSup *suture.Supervisor, evLogger events.Logger) *indexHandlerRegistry {
r := &indexHandlerRegistry{
conn: conn,
downloads: downloads,
evLogger: evLogger,
indexHandlers: make(map[string]*indexHandler),
startInfos: make(map[string]*clusterConfigDeviceInfo),
folderStates: make(map[string]*indexHandlerFolderState),
mut: sync.Mutex{},
}
r.sup = suture.New(r.String(), svcutil.SpecWithDebugLogger(l))
ourToken := parentSup.Add(r.sup)
r.sup.Add(svcutil.AsService(func(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-closed:
parentSup.Remove(ourToken)
}
return nil
}, fmt.Sprintf("%v/waitForClosed", r)))
return r
}
func (r *indexHandlerRegistry) String() string {
return fmt.Sprintf("indexHandlerRegistry/%v", r.conn.ID().Short())
}
func (r *indexHandlerRegistry) GetSupervisor() *suture.Supervisor {
return r.sup
}
func (r *indexHandlerRegistry) startLocked(folder config.FolderConfiguration, fset *db.FileSet, runner service, startInfo *clusterConfigDeviceInfo) {
if is, ok := r.indexHandlers[folder.ID]; ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexHandlers, folder.ID)
}
delete(r.startInfos, folder.ID)
is := newIndexHandler(r.conn, r.downloads, folder, fset, runner, startInfo, r.evLogger)
is.token = r.sup.Add(is)
r.indexHandlers[folder.ID] = is
}
// AddIndexInfo starts an index handler for given folder, unless it is paused.
// If it is paused, the given startInfo is stored to start the sender once the
// folder is resumed.
// If an index handler is already running, it will be stopped first.
func (r *indexHandlerRegistry) AddIndexInfo(folder string, startInfo *clusterConfigDeviceInfo) {
r.mut.Lock()
defer r.mut.Unlock()
if is, ok := r.indexHandlers[folder]; ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexHandlers, folder)
l.Debugf("Removed index sender for device %v and folder %v due to added pending", r.conn.ID().Short(), folder)
}
folderState, ok := r.folderStates[folder]
if !ok {
l.Debugf("Pending index handler for device %v and folder %v", r.conn.ID().Short(), folder)
r.startInfos[folder] = startInfo
return
}
r.startLocked(folderState.cfg, folderState.fset, folderState.runner, startInfo)
}
// Remove stops a running index handler or removes one pending to be started.
// It is a noop if the folder isn't known.
func (r *indexHandlerRegistry) Remove(folder string) {
r.mut.Lock()
defer r.mut.Unlock()
l.Debugf("Removing index handler for device %v and folder %v", r.conn.ID().Short(), folder)
if is, ok := r.indexHandlers[folder]; ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexHandlers, folder)
}
delete(r.startInfos, folder)
l.Debugf("Removed index handler for device %v and folder %v", r.conn.ID().Short(), folder)
}
// RemoveAllExcept stops all running index handlers and removes those pending to be started,
// except mentioned ones.
// It is a noop if the folder isn't known.
func (r *indexHandlerRegistry) RemoveAllExcept(except map[string]struct{}) {
r.mut.Lock()
defer r.mut.Unlock()
for folder, is := range r.indexHandlers {
if _, ok := except[folder]; !ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexHandlers, folder)
l.Debugf("Removed index handler for device %v and folder %v (removeAllExcept)", r.conn.ID().Short(), folder)
}
}
for folder := range r.startInfos {
if _, ok := except[folder]; !ok {
delete(r.startInfos, folder)
l.Debugf("Removed pending index handler for device %v and folder %v (removeAllExcept)", r.conn.ID().Short(), folder)
}
}
}
// RegisterFolderState must be called whenever something about the folder
// changes. The exception being if the folder is removed entirely, then call
// Remove. The fset and runner arguments may be nil, if given folder is paused.
func (r *indexHandlerRegistry) RegisterFolderState(folder config.FolderConfiguration, fset *db.FileSet, runner service) {
if !folder.SharedWith(r.conn.ID()) {
r.Remove(folder.ID)
return
}
r.mut.Lock()
if folder.Paused {
r.folderPausedLocked(folder.ID)
} else {
r.folderRunningLocked(folder, fset, runner)
}
r.mut.Unlock()
}
// folderPausedLocked stops a running index handler.
// It is a noop if the folder isn't known or has not been started yet.
func (r *indexHandlerRegistry) folderPausedLocked(folder string) {
l.Debugf("Pausing index handler for device %v and folder %v", r.conn.ID().Short(), folder)
delete(r.folderStates, folder)
if is, ok := r.indexHandlers[folder]; ok {
is.pause()
l.Debugf("Paused index handler for device %v and folder %v", r.conn.ID().Short(), folder)
} else {
l.Debugf("No index handler for device %v and folder %v to pause", r.conn.ID().Short(), folder)
}
}
// folderRunningLocked resumes an already running index handler or starts it, if it
// was added while paused.
// It is a noop if the folder isn't known.
func (r *indexHandlerRegistry) folderRunningLocked(folder config.FolderConfiguration, fset *db.FileSet, runner service) {
r.folderStates[folder.ID] = &indexHandlerFolderState{
cfg: folder,
fset: fset,
runner: runner,
}
is, isOk := r.indexHandlers[folder.ID]
if info, ok := r.startInfos[folder.ID]; ok {
if isOk {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexHandlers, folder.ID)
l.Debugf("Removed index handler for device %v and folder %v in resume", r.conn.ID().Short(), folder.ID)
}
r.startLocked(folder, fset, runner, info)
delete(r.startInfos, folder.ID)
l.Debugf("Started index handler for device %v and folder %v in resume", r.conn.ID().Short(), folder.ID)
} else if isOk {
l.Debugf("Resuming index handler for device %v and folder %v", r.conn.ID().Short(), folder)
is.resume(fset, runner)
} else {
l.Debugf("Not resuming index handler for device %v and folder %v as none is paused and there is no start info", r.conn.ID().Short(), folder.ID)
}
}
func (r *indexHandlerRegistry) ReceiveIndex(folder string, fs []protocol.FileInfo, update bool, op string) error {
r.mut.Lock()
defer r.mut.Unlock()
is, isOk := r.indexHandlers[folder]
if !isOk {
l.Infof("%v for nonexistent or paused folder %q", op, folder)
return ErrFolderMissing
}
return is.receive(fs, update, op)
}
// makeForgetUpdate takes an index update and constructs a download progress update
// causing to forget any progress for files which we've just been sent.
func makeForgetUpdate(files []protocol.FileInfo) []protocol.FileDownloadProgressUpdate {
updates := make([]protocol.FileDownloadProgressUpdate, 0, len(files))
for _, file := range files {
if file.IsSymlink() || file.IsDirectory() || file.IsDeleted() {
continue
}
updates = append(updates, protocol.FileDownloadProgressUpdate{
Name: file.Name,
Version: file.Version,
UpdateType: protocol.FileDownloadProgressUpdateTypeForget,
})
}
return updates
}

View File

@@ -1,430 +0,0 @@
// Copyright (C) 2020 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 https://mozilla.org/MPL/2.0/.
package model
import (
"context"
"fmt"
"sync"
"time"
"github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/svcutil"
)
type indexSender struct {
conn protocol.Connection
folder string
folderIsReceiveEncrypted bool
fset *db.FileSet
prevSequence int64
evLogger events.Logger
connClosed chan struct{}
done chan struct{}
token suture.ServiceToken
pauseChan chan struct{}
resumeChan chan *db.FileSet
}
func (s *indexSender) Serve(ctx context.Context) (err error) {
l.Debugf("Starting indexSender for %s to %s at %s (slv=%d)", s.folder, s.conn.ID(), s.conn, s.prevSequence)
defer func() {
close(s.done)
err = svcutil.NoRestartErr(err)
l.Debugf("Exiting indexSender for %s to %s at %s: %v", s.folder, s.conn.ID(), s.conn, err)
}()
// We need to send one index, regardless of whether there is something to send or not
err = s.sendIndexTo(ctx)
// Subscribe to LocalIndexUpdated (we have new information to send) and
// DeviceDisconnected (it might be us who disconnected, so we should
// exit).
sub := s.evLogger.Subscribe(events.LocalIndexUpdated | events.DeviceDisconnected)
defer sub.Unsubscribe()
paused := false
evChan := sub.C()
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for err == nil {
select {
case <-ctx.Done():
return ctx.Err()
case <-s.connClosed:
return nil
default:
}
// While we have sent a sequence at least equal to the one
// currently in the database, wait for the local index to update. The
// local index may update for other folders than the one we are
// sending for.
if s.fset.Sequence(protocol.LocalDeviceID) <= s.prevSequence {
select {
case <-ctx.Done():
return ctx.Err()
case <-s.connClosed:
return nil
case <-evChan:
case <-ticker.C:
case <-s.pauseChan:
paused = true
case s.fset = <-s.resumeChan:
paused = false
}
continue
}
if !paused {
err = s.sendIndexTo(ctx)
}
// Wait a short amount of time before entering the next loop. If there
// are continuous changes happening to the local index, this gives us
// time to batch them up a little.
time.Sleep(250 * time.Millisecond)
}
return err
}
func (s *indexSender) resume(fset *db.FileSet) {
select {
case <-s.done:
case s.resumeChan <- fset:
}
}
func (s *indexSender) pause() {
select {
case <-s.done:
case s.pauseChan <- struct{}{}:
}
}
// sendIndexTo sends file infos with a sequence number higher than prevSequence and
// returns the highest sent sequence number.
func (s *indexSender) sendIndexTo(ctx context.Context) error {
initial := s.prevSequence == 0
batch := db.NewFileInfoBatch(nil)
batch.SetFlushFunc(func(fs []protocol.FileInfo) error {
l.Debugf("%v: Sending %d files (<%d bytes)", s, len(fs), batch.Size())
if initial {
initial = false
return s.conn.Index(ctx, s.folder, fs)
}
return s.conn.IndexUpdate(ctx, s.folder, fs)
})
var err error
var f protocol.FileInfo
snap, err := s.fset.Snapshot()
if err != nil {
return svcutil.AsFatalErr(err, svcutil.ExitError)
}
defer snap.Release()
previousWasDelete := false
snap.WithHaveSequence(s.prevSequence+1, func(fi protocol.FileIntf) bool {
// This is to make sure that renames (which is an add followed by a delete) land in the same batch.
// Even if the batch is full, we allow a last delete to slip in, we do this by making sure that
// the batch ends with a non-delete, or that the last item in the batch is already a delete
if batch.Full() && (!fi.IsDeleted() || previousWasDelete) {
if err = batch.Flush(); err != nil {
return false
}
}
if shouldDebug() {
if fi.SequenceNo() < s.prevSequence+1 {
panic(fmt.Sprintln("sequence lower than requested, got:", fi.SequenceNo(), ", asked to start at:", s.prevSequence+1))
}
}
if f.Sequence > 0 && fi.SequenceNo() <= f.Sequence {
l.Warnln("Non-increasing sequence detected: Checking and repairing the db...")
// Abort this round of index sending - the next one will pick
// up from the last successful one with the repeaired db.
defer func() {
if fixed, dbErr := s.fset.RepairSequence(); dbErr != nil {
l.Warnln("Failed repairing sequence entries:", dbErr)
panic("Failed repairing sequence entries")
} else {
s.evLogger.Log(events.Failure, "detected and repaired non-increasing sequence")
l.Infof("Repaired %v sequence entries in database", fixed)
}
}()
return false
}
f = fi.(protocol.FileInfo)
// If this is a folder receiving encrypted files only, we
// mustn't ever send locally changed file infos. Those aren't
// encrypted and thus would be a protocol error at the remote.
if s.folderIsReceiveEncrypted && fi.IsReceiveOnlyChanged() {
return true
}
f = prepareFileInfoForIndex(f)
previousWasDelete = f.IsDeleted()
batch.Append(f)
return true
})
if err != nil {
return err
}
err = batch.Flush()
// True if there was nothing to be sent
if f.Sequence == 0 {
return err
}
s.prevSequence = f.Sequence
return err
}
func prepareFileInfoForIndex(f protocol.FileInfo) protocol.FileInfo {
// Mark the file as invalid if any of the local bad stuff flags are set.
f.RawInvalid = f.IsInvalid()
// If the file is marked LocalReceive (i.e., changed locally on a
// receive only folder) we do not want it to ever become the
// globally best version, invalid or not.
if f.IsReceiveOnlyChanged() {
f.Version = protocol.Vector{}
}
// never sent externally
f.LocalFlags = 0
f.VersionHash = nil
return f
}
func (s *indexSender) String() string {
return fmt.Sprintf("indexSender@%p for %s to %s at %s", s, s.folder, s.conn.ID(), s.conn)
}
type indexSenderRegistry struct {
deviceID protocol.DeviceID
sup *suture.Supervisor
evLogger events.Logger
conn protocol.Connection
closed chan struct{}
indexSenders map[string]*indexSender
startInfos map[string]*indexSenderStartInfo
mut sync.Mutex
}
func newIndexSenderRegistry(conn protocol.Connection, closed chan struct{}, sup *suture.Supervisor, evLogger events.Logger) *indexSenderRegistry {
return &indexSenderRegistry{
deviceID: conn.ID(),
conn: conn,
closed: closed,
sup: sup,
evLogger: evLogger,
indexSenders: make(map[string]*indexSender),
startInfos: make(map[string]*indexSenderStartInfo),
mut: sync.Mutex{},
}
}
// add starts an index sender for given folder.
// If an index sender is already running, it will be stopped first.
func (r *indexSenderRegistry) add(folder config.FolderConfiguration, fset *db.FileSet, startInfo *indexSenderStartInfo) {
r.mut.Lock()
r.addLocked(folder, fset, startInfo)
l.Debugf("Started index sender for device %v and folder %v", r.deviceID.Short(), folder.ID)
r.mut.Unlock()
}
func (r *indexSenderRegistry) addLocked(folder config.FolderConfiguration, fset *db.FileSet, startInfo *indexSenderStartInfo) {
myIndexID := fset.IndexID(protocol.LocalDeviceID)
mySequence := fset.Sequence(protocol.LocalDeviceID)
var startSequence int64
// This is the other side's description of what it knows
// about us. Lets check to see if we can start sending index
// updates directly or need to send the index from start...
if startInfo.local.IndexID == myIndexID {
// They say they've seen our index ID before, so we can
// send a delta update only.
if startInfo.local.MaxSequence > mySequence {
// Safety check. They claim to have more or newer
// index data than we have - either we have lost
// index data, or reset the index without resetting
// the IndexID, or something else weird has
// happened. We send a full index to reset the
// situation.
l.Infof("Device %v folder %s is delta index compatible, but seems out of sync with reality", r.deviceID, folder.Description())
startSequence = 0
} else {
l.Debugf("Device %v folder %s is delta index compatible (mlv=%d)", r.deviceID, folder.Description(), startInfo.local.MaxSequence)
startSequence = startInfo.local.MaxSequence
}
} else if startInfo.local.IndexID != 0 {
// They say they've seen an index ID from us, but it's
// not the right one. Either they are confused or we
// must have reset our database since last talking to
// them. We'll start with a full index transfer.
l.Infof("Device %v folder %s has mismatching index ID for us (%v != %v)", r.deviceID, folder.Description(), startInfo.local.IndexID, myIndexID)
startSequence = 0
} else {
l.Debugf("Device %v folder %s has no index ID for us", r.deviceID, folder.Description())
}
// This is the other side's description of themselves. We
// check to see that it matches the IndexID we have on file,
// otherwise we drop our old index data and expect to get a
// completely new set.
theirIndexID := fset.IndexID(r.deviceID)
if startInfo.remote.IndexID == 0 {
// They're not announcing an index ID. This means they
// do not support delta indexes and we should clear any
// information we have from them before accepting their
// index, which will presumably be a full index.
l.Debugf("Device %v folder %s does not announce an index ID", r.deviceID, folder.Description())
fset.Drop(r.deviceID)
} else if startInfo.remote.IndexID != theirIndexID {
// The index ID we have on file is not what they're
// announcing. They must have reset their database and
// will probably send us a full index. We drop any
// information we have and remember this new index ID
// instead.
l.Infof("Device %v folder %s has a new index ID (%v)", r.deviceID, folder.Description(), startInfo.remote.IndexID)
fset.Drop(r.deviceID)
fset.SetIndexID(r.deviceID, startInfo.remote.IndexID)
}
if is, ok := r.indexSenders[folder.ID]; ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder.ID)
}
delete(r.startInfos, folder.ID)
is := &indexSender{
conn: r.conn,
connClosed: r.closed,
done: make(chan struct{}),
folder: folder.ID,
folderIsReceiveEncrypted: folder.Type == config.FolderTypeReceiveEncrypted,
fset: fset,
prevSequence: startSequence,
evLogger: r.evLogger,
pauseChan: make(chan struct{}),
resumeChan: make(chan *db.FileSet),
}
is.token = r.sup.Add(is)
r.indexSenders[folder.ID] = is
}
// addPending stores the given info to start an index sender once resume is called
// for this folder.
// If an index sender is already running, it will be stopped.
func (r *indexSenderRegistry) addPending(folder string, startInfo *indexSenderStartInfo) {
r.mut.Lock()
defer r.mut.Unlock()
if is, ok := r.indexSenders[folder]; ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder)
l.Debugf("Removed index sender for device %v and folder %v due to added pending", r.deviceID.Short(), folder)
}
r.startInfos[folder] = startInfo
l.Debugf("Pending index sender for device %v and folder %v", r.deviceID.Short(), folder)
}
// remove stops a running index sender or removes one pending to be started.
// It is a noop if the folder isn't known.
func (r *indexSenderRegistry) remove(folder string) {
r.mut.Lock()
defer r.mut.Unlock()
if is, ok := r.indexSenders[folder]; ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder)
}
delete(r.startInfos, folder)
l.Debugf("Removed index sender for device %v and folder %v", r.deviceID.Short(), folder)
}
// removeAllExcept stops all running index senders and removes those pending to be started,
// except mentioned ones.
// It is a noop if the folder isn't known.
func (r *indexSenderRegistry) removeAllExcept(except map[string]struct{}) {
r.mut.Lock()
defer r.mut.Unlock()
for folder, is := range r.indexSenders {
if _, ok := except[folder]; !ok {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder)
l.Debugf("Removed index sender for device %v and folder %v (removeAllExcept)", r.deviceID.Short(), folder)
}
}
for folder := range r.startInfos {
if _, ok := except[folder]; !ok {
delete(r.startInfos, folder)
l.Debugf("Removed pending index sender for device %v and folder %v (removeAllExcept)", r.deviceID.Short(), folder)
}
}
}
// pause stops a running index sender.
// It is a noop if the folder isn't known or has not been started yet.
func (r *indexSenderRegistry) pause(folder string) {
r.mut.Lock()
defer r.mut.Unlock()
if is, ok := r.indexSenders[folder]; ok {
is.pause()
l.Debugf("Paused index sender for device %v and folder %v", r.deviceID.Short(), folder)
} else {
l.Debugf("No index sender for device %v and folder %v to pause", r.deviceID.Short(), folder)
}
}
// resume unpauses an already running index sender or starts it, if it was added
// while paused.
// It is a noop if the folder isn't known.
func (r *indexSenderRegistry) resume(folder config.FolderConfiguration, fset *db.FileSet) {
r.mut.Lock()
defer r.mut.Unlock()
is, isOk := r.indexSenders[folder.ID]
if info, ok := r.startInfos[folder.ID]; ok {
if isOk {
r.sup.RemoveAndWait(is.token, 0)
delete(r.indexSenders, folder.ID)
l.Debugf("Removed index sender for device %v and folder %v in resume", r.deviceID.Short(), folder.ID)
}
r.addLocked(folder, fset, info)
delete(r.startInfos, folder.ID)
l.Debugf("Started index sender for device %v and folder %v in resume", r.deviceID.Short(), folder.ID)
} else if isOk {
is.resume(fset)
l.Debugf("Resume index sender for device %v and folder %v", r.deviceID.Short(), folder.ID)
} else {
l.Debugf("Not resuming index sender for device %v and folder %v as none is paused and there is no start info", r.deviceID.Short(), folder.ID)
}
}
type indexSenderStartInfo struct {
local, remote protocol.Device
}

View File

@@ -177,6 +177,29 @@ type Model struct {
result1 map[protocol.DeviceID]stats.DeviceStatistics
result2 error
}
DismissPendingDeviceStub func(protocol.DeviceID) error
dismissPendingDeviceMutex sync.RWMutex
dismissPendingDeviceArgsForCall []struct {
arg1 protocol.DeviceID
}
dismissPendingDeviceReturns struct {
result1 error
}
dismissPendingDeviceReturnsOnCall map[int]struct {
result1 error
}
DismissPendingFolderStub func(protocol.DeviceID, string) error
dismissPendingFolderMutex sync.RWMutex
dismissPendingFolderArgsForCall []struct {
arg1 protocol.DeviceID
arg2 string
}
dismissPendingFolderReturns struct {
result1 error
}
dismissPendingFolderReturnsOnCall map[int]struct {
result1 error
}
DownloadProgressStub func(protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) error
downloadProgressMutex sync.RWMutex
downloadProgressArgsForCall []struct {
@@ -1332,6 +1355,129 @@ func (fake *Model) DeviceStatisticsReturnsOnCall(i int, result1 map[protocol.Dev
}{result1, result2}
}
func (fake *Model) DismissPendingDevice(arg1 protocol.DeviceID) error {
fake.dismissPendingDeviceMutex.Lock()
ret, specificReturn := fake.dismissPendingDeviceReturnsOnCall[len(fake.dismissPendingDeviceArgsForCall)]
fake.dismissPendingDeviceArgsForCall = append(fake.dismissPendingDeviceArgsForCall, struct {
arg1 protocol.DeviceID
}{arg1})
stub := fake.DismissPendingDeviceStub
fakeReturns := fake.dismissPendingDeviceReturns
fake.recordInvocation("DismissPendingDevice", []interface{}{arg1})
fake.dismissPendingDeviceMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *Model) DismissPendingDeviceCallCount() int {
fake.dismissPendingDeviceMutex.RLock()
defer fake.dismissPendingDeviceMutex.RUnlock()
return len(fake.dismissPendingDeviceArgsForCall)
}
func (fake *Model) DismissPendingDeviceCalls(stub func(protocol.DeviceID) error) {
fake.dismissPendingDeviceMutex.Lock()
defer fake.dismissPendingDeviceMutex.Unlock()
fake.DismissPendingDeviceStub = stub
}
func (fake *Model) DismissPendingDeviceArgsForCall(i int) protocol.DeviceID {
fake.dismissPendingDeviceMutex.RLock()
defer fake.dismissPendingDeviceMutex.RUnlock()
argsForCall := fake.dismissPendingDeviceArgsForCall[i]
return argsForCall.arg1
}
func (fake *Model) DismissPendingDeviceReturns(result1 error) {
fake.dismissPendingDeviceMutex.Lock()
defer fake.dismissPendingDeviceMutex.Unlock()
fake.DismissPendingDeviceStub = nil
fake.dismissPendingDeviceReturns = struct {
result1 error
}{result1}
}
func (fake *Model) DismissPendingDeviceReturnsOnCall(i int, result1 error) {
fake.dismissPendingDeviceMutex.Lock()
defer fake.dismissPendingDeviceMutex.Unlock()
fake.DismissPendingDeviceStub = nil
if fake.dismissPendingDeviceReturnsOnCall == nil {
fake.dismissPendingDeviceReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.dismissPendingDeviceReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *Model) DismissPendingFolder(arg1 protocol.DeviceID, arg2 string) error {
fake.dismissPendingFolderMutex.Lock()
ret, specificReturn := fake.dismissPendingFolderReturnsOnCall[len(fake.dismissPendingFolderArgsForCall)]
fake.dismissPendingFolderArgsForCall = append(fake.dismissPendingFolderArgsForCall, struct {
arg1 protocol.DeviceID
arg2 string
}{arg1, arg2})
stub := fake.DismissPendingFolderStub
fakeReturns := fake.dismissPendingFolderReturns
fake.recordInvocation("DismissPendingFolder", []interface{}{arg1, arg2})
fake.dismissPendingFolderMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *Model) DismissPendingFolderCallCount() int {
fake.dismissPendingFolderMutex.RLock()
defer fake.dismissPendingFolderMutex.RUnlock()
return len(fake.dismissPendingFolderArgsForCall)
}
func (fake *Model) DismissPendingFolderCalls(stub func(protocol.DeviceID, string) error) {
fake.dismissPendingFolderMutex.Lock()
defer fake.dismissPendingFolderMutex.Unlock()
fake.DismissPendingFolderStub = stub
}
func (fake *Model) DismissPendingFolderArgsForCall(i int) (protocol.DeviceID, string) {
fake.dismissPendingFolderMutex.RLock()
defer fake.dismissPendingFolderMutex.RUnlock()
argsForCall := fake.dismissPendingFolderArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *Model) DismissPendingFolderReturns(result1 error) {
fake.dismissPendingFolderMutex.Lock()
defer fake.dismissPendingFolderMutex.Unlock()
fake.DismissPendingFolderStub = nil
fake.dismissPendingFolderReturns = struct {
result1 error
}{result1}
}
func (fake *Model) DismissPendingFolderReturnsOnCall(i int, result1 error) {
fake.dismissPendingFolderMutex.Lock()
defer fake.dismissPendingFolderMutex.Unlock()
fake.DismissPendingFolderStub = nil
if fake.dismissPendingFolderReturnsOnCall == nil {
fake.dismissPendingFolderReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.dismissPendingFolderReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *Model) DownloadProgress(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileDownloadProgressUpdate) error {
var arg3Copy []protocol.FileDownloadProgressUpdate
if arg3 != nil {
@@ -3254,6 +3400,10 @@ func (fake *Model) Invocations() map[string][][]interface{} {
defer fake.delayScanMutex.RUnlock()
fake.deviceStatisticsMutex.RLock()
defer fake.deviceStatisticsMutex.RUnlock()
fake.dismissPendingDeviceMutex.RLock()
defer fake.dismissPendingDeviceMutex.RUnlock()
fake.dismissPendingFolderMutex.RLock()
defer fake.dismissPendingFolderMutex.RUnlock()
fake.downloadProgressMutex.RLock()
defer fake.downloadProgressMutex.RUnlock()
fake.folderErrorsMutex.RLock()

View File

@@ -40,6 +40,7 @@ import (
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/ur/contract"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/versioner"
)
@@ -107,6 +108,8 @@ type Model interface {
PendingDevices() (map[protocol.DeviceID]db.ObservedDevice, error)
PendingFolders(device protocol.DeviceID) (map[string]db.PendingFolder, error)
DismissPendingDevice(device protocol.DeviceID) error
DismissPendingFolder(device protocol.DeviceID, folder string) error
StartDeadlockDetector(timeout time.Duration)
GlobalDirectoryTree(folder, prefix string, levels int, dirsOnly bool) ([]*TreeEntry, error)
@@ -130,10 +133,10 @@ type model struct {
shortID protocol.ShortID
// globalRequestLimiter limits the amount of data in concurrent incoming
// requests
globalRequestLimiter *byteSemaphore
globalRequestLimiter *util.Semaphore
// folderIOLimiter limits the number of concurrent I/O heavy operations,
// such as scans and pulls.
folderIOLimiter *byteSemaphore
folderIOLimiter *util.Semaphore
fatalChan chan error
started chan struct{}
@@ -153,18 +156,18 @@ type model struct {
// fields protected by pmut
pmut sync.RWMutex
conn map[protocol.DeviceID]protocol.Connection
connRequestLimiters map[protocol.DeviceID]*byteSemaphore
connRequestLimiters map[protocol.DeviceID]*util.Semaphore
closed map[protocol.DeviceID]chan struct{}
helloMessages map[protocol.DeviceID]protocol.Hello
deviceDownloads map[protocol.DeviceID]*deviceDownloadState
remotePausedFolders map[protocol.DeviceID]map[string]struct{} // deviceID -> folders
indexSenders map[protocol.DeviceID]*indexSenderRegistry
indexHandlers map[protocol.DeviceID]*indexHandlerRegistry
// for testing only
foldersRunning int32
}
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *byteSemaphore) service
type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *util.Semaphore) service
var (
folderFactories = make(map[config.FolderType]folderFactory)
@@ -192,12 +195,9 @@ var (
errEncryptionPassword = errors.New("different encryption passwords used")
errEncryptionTokenRead = errors.New("failed to read encryption token")
errEncryptionTokenWrite = errors.New("failed to write encryption token")
errEncryptionNeedToken = errors.New("require password token for receive-encrypted token")
errMissingRemoteInClusterConfig = errors.New("remote device missing in cluster config")
errMissingLocalInClusterConfig = errors.New("local device missing in cluster config")
errConnLimitReached = errors.New("connection limit reached")
// messages for failure reports
failureUnexpectedGenerateCCError = "unexpected error occurred in generateClusterConfig"
)
// NewModel creates and starts a new model. The model starts in read-only mode,
@@ -221,8 +221,8 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
finder: db.NewBlockFinder(ldb),
progressEmitter: NewProgressEmitter(cfg, evLogger),
shortID: id.Short(),
globalRequestLimiter: newByteSemaphore(1024 * cfg.Options().MaxConcurrentIncomingRequestKiB()),
folderIOLimiter: newByteSemaphore(cfg.Options().MaxFolderConcurrency()),
globalRequestLimiter: util.NewSemaphore(1024 * cfg.Options().MaxConcurrentIncomingRequestKiB()),
folderIOLimiter: util.NewSemaphore(cfg.Options().MaxFolderConcurrency()),
fatalChan: make(chan error),
started: make(chan struct{}),
@@ -241,12 +241,12 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
// fields protected by pmut
pmut: sync.NewRWMutex(),
conn: make(map[protocol.DeviceID]protocol.Connection),
connRequestLimiters: make(map[protocol.DeviceID]*byteSemaphore),
connRequestLimiters: make(map[protocol.DeviceID]*util.Semaphore),
closed: make(map[protocol.DeviceID]chan struct{}),
helloMessages: make(map[protocol.DeviceID]protocol.Hello),
deviceDownloads: make(map[protocol.DeviceID]*deviceDownloadState),
remotePausedFolders: make(map[protocol.DeviceID]map[string]struct{}),
indexSenders: make(map[protocol.DeviceID]*indexSenderRegistry),
indexHandlers: make(map[protocol.DeviceID]*indexHandlerRegistry),
}
for devID := range cfg.Devices() {
m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(m.db, devID)
@@ -485,8 +485,8 @@ func (m *model) removeFolder(cfg config.FolderConfiguration) {
}
m.cleanupFolderLocked(cfg)
for _, r := range m.indexSenders {
r.remove(cfg.ID)
for _, r := range m.indexHandlers {
r.Remove(cfg.ID)
}
m.fmut.Unlock()
@@ -558,21 +558,9 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
// Care needs to be taken because we already hold fmut and the lock order
// must be the same everywhere. As fmut is acquired first, this is fine.
// toDeviceIDs := to.DeviceIDs()
m.pmut.RLock()
for _, id := range to.DeviceIDs() {
indexSenders, ok := m.indexSenders[id]
if !ok {
continue
}
// In case the folder was newly shared with us we already got a
// cluster config and wont necessarily get another soon - start
// sending indexes if connected.
if to.Paused {
indexSenders.pause(to.ID)
} else if !from.SharedWith(indexSenders.deviceID) || fsetNil || from.Paused {
indexSenders.resume(to, fset)
}
for _, indexRegistry := range m.indexHandlers {
indexRegistry.RegisterFolderState(to, fset, m.folderRunners[to.ID])
}
m.pmut.RUnlock()
@@ -601,18 +589,19 @@ func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool
m.fmut.Lock()
defer m.fmut.Unlock()
m.addAndStartFolderLocked(cfg, fset, cacheIgnoredFiles)
// Cluster configs might be received and processed before reaching this
// point, i.e. before the folder is started. If that's the case, start
// index senders here.
// Care needs to be taken because we already hold fmut and the lock order
// must be the same everywhere. As fmut is acquired first, this is fine.
m.pmut.RLock()
for _, id := range cfg.DeviceIDs() {
if is, ok := m.indexSenders[id]; ok {
is.resume(cfg, fset)
}
for _, indexRegistry := range m.indexHandlers {
indexRegistry.RegisterFolderState(cfg, fset, m.folderRunners[cfg.ID])
}
m.pmut.RUnlock()
m.addAndStartFolderLocked(cfg, fset, cacheIgnoredFiles)
return nil
}
@@ -1151,46 +1140,23 @@ func (m *model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot
return errors.Wrap(ErrFolderPaused, folder)
}
m.fmut.RLock()
files, existing := m.folderFiles[folder]
runner, running := m.folderRunners[folder]
m.fmut.RUnlock()
if !existing {
l.Infof("%v for nonexistent folder %q", op, folder)
return errors.Wrap(ErrFolderMissing, folder)
}
if running {
defer runner.SchedulePull()
}
m.pmut.RLock()
downloads := m.deviceDownloads[deviceID]
indexHandler, ok := m.indexHandlers[deviceID]
m.pmut.RUnlock()
downloads.Update(folder, makeForgetUpdate(fs))
if !update {
files.Drop(deviceID)
if !ok {
// This should be impossible, as an index handler always exists for an
// open connection, and this method can't be called on a closed
// connection
m.evLogger.Log(events.Failure, "index sender does not exist for connection on which indexes were received")
l.Debugf("%v for folder (ID %q) sent from device %q: missing index handler", op, folder, deviceID)
return errors.Wrap(errors.New("index handler missing"), folder)
}
for i := range fs {
// The local attributes should never be transmitted over the wire.
// Make sure they look like they weren't.
fs[i].LocalFlags = 0
fs[i].VersionHash = nil
}
files.Update(deviceID, fs)
seq := files.Sequence(deviceID)
m.evLogger.Log(events.RemoteIndexUpdated, map[string]interface{}{
"device": deviceID.String(),
"folder": folder,
"items": len(fs),
"sequence": seq,
"version": seq, // legacy for sequence
})
return indexHandler.ReceiveIndex(folder, fs, update, op)
}
return nil
type clusterConfigDeviceInfo struct {
local, remote protocol.Device
}
func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) error {
@@ -1199,8 +1165,10 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
// Also, collect a list of folders we do share, and if he's interested in
// temporary indexes, subscribe the connection.
l.Debugf("Handling ClusterConfig from %v", deviceID.Short())
m.pmut.RLock()
indexSenderRegistry, ok := m.indexSenders[deviceID]
indexHandlerRegistry, ok := m.indexHandlers[deviceID]
m.pmut.RUnlock()
if !ok {
panic("bug: ClusterConfig called on closed or nonexistent connection")
@@ -1214,9 +1182,9 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
// Assemble the device information from the connected device about
// themselves and us for all folders.
ccDeviceInfos := make(map[string]*indexSenderStartInfo, len(cm.Folders))
ccDeviceInfos := make(map[string]*clusterConfigDeviceInfo, len(cm.Folders))
for _, folder := range cm.Folders {
info := &indexSenderStartInfo{}
info := &clusterConfigDeviceInfo{}
for _, dev := range folder.Devices {
if dev.ID == m.id {
info.local = dev
@@ -1269,7 +1237,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
w.Wait()
}
tempIndexFolders, paused, err := m.ccHandleFolders(cm.Folders, deviceCfg, ccDeviceInfos, indexSenderRegistry)
tempIndexFolders, paused, err := m.ccHandleFolders(cm.Folders, deviceCfg, ccDeviceInfos, indexHandlerRegistry)
if err != nil {
return err
}
@@ -1310,7 +1278,7 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
return nil
}
func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.DeviceConfiguration, ccDeviceInfos map[string]*indexSenderStartInfo, indexSenders *indexSenderRegistry) ([]string, map[string]struct{}, error) {
func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.DeviceConfiguration, ccDeviceInfos map[string]*clusterConfigDeviceInfo, indexHandlers *indexHandlerRegistry) ([]string, map[string]struct{}, error) {
var folderDevice config.FolderDeviceConfiguration
tempIndexFolders := make([]string, 0, len(folders))
paused := make(map[string]struct{}, len(folders))
@@ -1330,7 +1298,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
folderDevice, ok = cfg.Device(deviceID)
}
if !ok {
indexSenders.remove(folder.ID)
indexHandlers.Remove(folder.ID)
if deviceCfg.IgnoredFolder(folder.ID) {
l.Infof("Ignoring folder %s from device %s since we are configured to", folder.Description(), deviceID)
continue
@@ -1338,15 +1306,19 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
delete(expiredPending, folder.ID)
of.Label = folder.Label
of.ReceiveEncrypted = len(ccDeviceInfos[folder.ID].local.EncryptionPasswordToken) > 0
of.RemoteEncrypted = len(ccDeviceInfos[folder.ID].remote.EncryptionPasswordToken) > 0
if err := m.db.AddOrUpdatePendingFolder(folder.ID, of, deviceID); err != nil {
l.Warnf("Failed to persist pending folder entry to database: %v", err)
}
indexSenders.addPending(folder.ID, ccDeviceInfos[folder.ID])
if !folder.Paused {
indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
}
updatedPending = append(updatedPending, updatedPendingFolder{
FolderID: folder.ID,
FolderLabel: folder.Label,
DeviceID: deviceID,
ReceiveEncrypted: of.ReceiveEncrypted,
RemoteEncrypted: of.RemoteEncrypted,
})
// DEPRECATED: Only for backwards compatibility, should be removed.
m.evLogger.Log(events.FolderRejected, map[string]string{
@@ -1359,24 +1331,26 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
}
if folder.Paused {
indexSenders.remove(folder.ID)
indexHandlers.Remove(folder.ID)
paused[cfg.ID] = struct{}{}
continue
}
if cfg.Paused {
indexSenders.addPending(folder.ID, ccDeviceInfos[folder.ID])
indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
continue
}
if err := m.ccCheckEncryption(cfg, folderDevice, ccDeviceInfos[folder.ID], deviceCfg.Untrusted); err != nil {
sameError := false
m.fmut.Lock()
if devs, ok := m.folderEncryptionFailures[folder.ID]; ok {
sameError = devs[deviceID] == err
} else {
m.folderEncryptionFailures[folder.ID] = make(map[protocol.DeviceID]error)
}
m.folderEncryptionFailures[folder.ID][deviceID] = err
m.fmut.Unlock()
msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), err)
if sameError {
l.Debugln(msg)
@@ -1389,6 +1363,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
}
return tempIndexFolders, paused, err
}
m.fmut.Lock()
if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
if len(devErrs) == 1 {
delete(m.folderEncryptionFailures, folder.ID)
@@ -1396,6 +1371,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
delete(m.folderEncryptionFailures[folder.ID], deviceID)
}
}
m.fmut.Unlock()
// Handle indexes
@@ -1403,40 +1379,24 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
tempIndexFolders = append(tempIndexFolders, folder.ID)
}
m.fmut.RLock()
fs, ok := m.folderFiles[folder.ID]
m.fmut.RUnlock()
if !ok {
// Shouldn't happen because !cfg.Paused, but might happen
// if the folder is about to be unpaused, but not yet.
l.Debugln("ccH: no fset", folder.ID)
indexSenders.addPending(folder.ID, ccDeviceInfos[folder.ID])
indexHandlers.AddIndexInfo(folder.ID, ccDeviceInfos[folder.ID])
}
indexHandlers.RemoveAllExcept(seenFolders)
expiredPendingList := make([]map[string]string, 0, len(expiredPending))
for folder := range expiredPending {
if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil {
msg := "Failed to remove pending folder-device entry"
l.Warnf("%v (%v, %v): %v", msg, folder, deviceID, err)
m.evLogger.Log(events.Failure, msg)
continue
}
indexSenders.add(cfg, fs, ccDeviceInfos[folder.ID])
// We might already have files that we need to pull so let the
// folder runner know that it should recheck the index data.
m.fmut.RLock()
if runner := m.folderRunners[folder.ID]; runner != nil {
defer runner.SchedulePull()
}
m.fmut.RUnlock()
expiredPendingList = append(expiredPendingList, map[string]string{
"folderID": folder,
"deviceID": deviceID.String(),
})
}
indexSenders.removeAllExcept(seenFolders)
for folder := range expiredPending {
m.db.RemovePendingFolderForDevice(folder, deviceID)
}
if len(updatedPending) > 0 || len(expiredPending) > 0 {
expiredPendingList := make([]map[string]string, 0, len(expiredPending))
for folderID := range expiredPending {
expiredPendingList = append(expiredPendingList, map[string]string{
"folderID": folderID,
"deviceID": deviceID.String(),
})
}
if len(updatedPending) > 0 || len(expiredPendingList) > 0 {
m.evLogger.Log(events.PendingFoldersChanged, map[string]interface{}{
"added": updatedPending,
"removed": expiredPendingList,
@@ -1446,7 +1406,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
return tempIndexFolders, paused, nil
}
func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice config.FolderDeviceConfiguration, ccDeviceInfos *indexSenderStartInfo, deviceUntrusted bool) error {
func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice config.FolderDeviceConfiguration, ccDeviceInfos *clusterConfigDeviceInfo, deviceUntrusted bool) error {
hasTokenRemote := len(ccDeviceInfos.remote.EncryptionPasswordToken) > 0
hasTokenLocal := len(ccDeviceInfos.local.EncryptionPasswordToken) > 0
isEncryptedRemote := folderDevice.EncryptionPassword != ""
@@ -1472,9 +1432,9 @@ func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice
if !(hasTokenRemote || hasTokenLocal) {
if isEncryptedRemote {
return errEncryptionPlainForReceiveEncrypted
} else {
return errEncryptionPlainForRemoteEncrypted
} else {
return errEncryptionPlainForReceiveEncrypted
}
}
@@ -1565,15 +1525,7 @@ func (m *model) sendClusterConfig(ids []protocol.DeviceID) {
m.pmut.RUnlock()
// Generating cluster-configs acquires fmut -> must happen outside of pmut.
for _, conn := range ccConns {
cm, passwords, err := m.generateClusterConfig(conn.ID())
if err != nil {
if err != errEncryptionNeedToken {
m.evLogger.Log(events.Failure, failureUnexpectedGenerateCCError)
continue
}
go conn.Close(err)
continue
}
cm, passwords := m.generateClusterConfig(conn.ID())
conn.SetFolderPasswords(passwords)
go conn.ClusterConfig(cm)
}
@@ -1616,6 +1568,11 @@ func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm
continue
}
if fcfg.Type != config.FolderTypeReceiveEncrypted && device.EncryptionPasswordToken != nil {
l.Infof("Cannot share folder %s with %v because the introducer %v encrypts data, which requires a password", folder.Description(), device.ID, introducerCfg.DeviceID)
continue
}
// We don't yet share this folder with this device. Add the device
// to sharing list of the folder.
l.Infof("Sharing folder %s with %v (vouched for by introducer %v)", folder.Description(), device.ID, introducerCfg.DeviceID)
@@ -1688,7 +1645,7 @@ func (m *model) handleDeintroductions(introducerCfg config.DeviceConfiguration,
// handleAutoAccepts handles adding and sharing folders for devices that have
// AutoAcceptFolders set to true.
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *indexSenderStartInfo, cfg config.FolderConfiguration, haveCfg bool, defaultPath string) (config.FolderConfiguration, bool) {
func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *clusterConfigDeviceInfo, cfg config.FolderConfiguration, haveCfg bool, defaultPath string) (config.FolderConfiguration, bool) {
if !haveCfg {
defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
pathAlternatives := []string{
@@ -1783,7 +1740,7 @@ func (m *model) Closed(device protocol.DeviceID, err error) {
delete(m.remotePausedFolders, device)
closed := m.closed[device]
delete(m.closed, device)
delete(m.indexSenders, device)
delete(m.indexHandlers, device)
m.pmut.Unlock()
m.progressEmitter.temporaryIndexUnsubscribe(conn)
@@ -1954,23 +1911,15 @@ func (m *model) Request(deviceID protocol.DeviceID, folder, name string, blockNo
// skipping nil limiters, then returns a requestResponse of the given size.
// When the requestResponse is closed the limiters are given back the bytes,
// in reverse order.
func newLimitedRequestResponse(size int, limiters ...*byteSemaphore) *requestResponse {
for _, limiter := range limiters {
if limiter != nil {
limiter.take(size)
}
}
func newLimitedRequestResponse(size int, limiters ...*util.Semaphore) *requestResponse {
multi := util.MultiSemaphore(limiters)
multi.Take(size)
res := newRequestResponse(size)
go func() {
res.Wait()
for i := range limiters {
limiter := limiters[len(limiters)-1-i]
if limiter != nil {
limiter.give(size)
}
}
multi.Give(size)
}()
return res
@@ -2244,6 +2193,9 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
return
}
// The slightly unusual locking sequence here is because we must acquire
// fmut before pmut. (The locks can be *released* in any order.)
m.fmut.RLock()
m.pmut.Lock()
if oldConn, ok := m.conn[deviceID]; ok {
l.Infoln("Replacing old connection", oldConn, "with", conn, "for", deviceID)
@@ -2253,9 +2205,12 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
// actual close without holding pmut as the connection will call
// back into Closed() for the cleanup.
closed := m.closed[deviceID]
m.fmut.RUnlock()
m.pmut.Unlock()
oldConn.Close(errReplacingConnection)
<-closed
// Again, lock fmut before pmut.
m.fmut.RLock()
m.pmut.Lock()
}
@@ -2263,13 +2218,18 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
closed := make(chan struct{})
m.closed[deviceID] = closed
m.deviceDownloads[deviceID] = newDeviceDownloadState()
m.indexSenders[deviceID] = newIndexSenderRegistry(conn, closed, m.Supervisor, m.evLogger)
indexRegistry := newIndexHandlerRegistry(conn, m.deviceDownloads[deviceID], closed, m.Supervisor, m.evLogger)
for id, fcfg := range m.folderCfgs {
indexRegistry.RegisterFolderState(fcfg, m.folderFiles[id], m.folderRunners[id])
}
m.indexHandlers[deviceID] = indexRegistry
m.fmut.RUnlock()
// 0: default, <0: no limiting
switch {
case device.MaxRequestKiB > 0:
m.connRequestLimiters[deviceID] = newByteSemaphore(1024 * device.MaxRequestKiB)
m.connRequestLimiters[deviceID] = util.NewSemaphore(1024 * device.MaxRequestKiB)
case device.MaxRequestKiB == 0:
m.connRequestLimiters[deviceID] = newByteSemaphore(1024 * defaultPullerPendingKiB)
m.connRequestLimiters[deviceID] = util.NewSemaphore(1024 * defaultPullerPendingKiB)
}
m.helloMessages[deviceID] = hello
@@ -2295,12 +2255,7 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
m.pmut.Unlock()
// Acquires fmut, so has to be done outside of pmut.
cm, passwords, err := m.generateClusterConfig(deviceID)
// We ignore errEncryptionNeedToken on a new connection, as the missing
// token should be delivered in the cluster-config about to be received.
if err != nil && err != errEncryptionNeedToken {
m.evLogger.Log(events.Failure, failureUnexpectedGenerateCCError)
}
cm, passwords := m.generateClusterConfig(deviceID)
conn.SetFolderPasswords(passwords)
conn.ClusterConfig(cm)
@@ -2463,7 +2418,7 @@ func (m *model) numHashers(folder string) int {
// generateClusterConfig returns a ClusterConfigMessage that is correct and the
// set of folder passwords for the given peer device
func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string, error) {
func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string) {
var message protocol.ClusterConfig
m.fmut.RLock()
@@ -2476,15 +2431,11 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
continue
}
var encryptionToken []byte
var hasEncryptionToken bool
if folderCfg.Type == config.FolderTypeReceiveEncrypted {
if encryptionToken, hasEncryptionToken = m.folderEncryptionPasswordTokens[folderCfg.ID]; !hasEncryptionToken {
// We haven't gotten a token yet and without one the other side
// can't validate us - reset the connection to trigger a new
// cluster-config and get the token.
return message, nil, errEncryptionNeedToken
}
encryptionToken, hasEncryptionToken := m.folderEncryptionPasswordTokens[folderCfg.ID]
if folderCfg.Type == config.FolderTypeReceiveEncrypted && !hasEncryptionToken {
// We haven't gotten a token for us yet and without one the other
// side can't validate us - pretend we don't have the folder yet.
continue
}
protocolFolder := protocol.Folder{
@@ -2541,7 +2492,7 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
message.Folders = append(message.Folders, protocolFolder)
}
return message, passwords, nil
return message, passwords
}
func (m *model) State(folder string) (string, time.Time, error) {
@@ -2820,6 +2771,13 @@ func (m *model) String() string {
}
func (m *model) VerifyConfiguration(from, to config.Configuration) error {
toFolders := to.FolderMap()
for _, from := range from.Folders {
to, ok := toFolders[from.ID]
if ok && from.Type != to.Type && (from.Type == config.FolderTypeReceiveEncrypted || to.Type == config.FolderTypeReceiveEncrypted) {
return errors.New("folder type must not be changed from/to receive-encrypted")
}
}
return nil
}
@@ -2833,6 +2791,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// Tracks devices affected by any configuration change to resend ClusterConfig.
clusterConfigDevices := make(deviceIDSet, len(from.Devices)+len(to.Devices))
closeDevices := make([]protocol.DeviceID, 0, len(to.Devices))
fromFolders := mapFolders(from.Folders)
toFolders := mapFolders(to.Folders)
@@ -2875,7 +2834,23 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
return true
}
clusterConfigDevices.add(fromCfg.DeviceIDs())
clusterConfigDevices.add(toCfg.DeviceIDs())
if toCfg.Type != config.FolderTypeReceiveEncrypted {
clusterConfigDevices.add(toCfg.DeviceIDs())
} else {
// If we don't have the encryption token yet, we need to drop
// the connection to make the remote re-send the cluster-config
// and with it the token.
m.fmut.RLock()
_, ok := m.folderEncryptionPasswordTokens[toCfg.ID]
m.fmut.RUnlock()
if !ok {
for _, id := range toCfg.DeviceIDs() {
closeDevices = append(closeDevices, id)
}
} else {
clusterConfigDevices.add(toCfg.DeviceIDs())
}
}
}
// Emit the folder pause/resume event
@@ -2898,7 +2873,6 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// Pausing a device, unpausing is handled by the connection service.
fromDevices := from.DeviceMap()
toDevices := to.DeviceMap()
closeDevices := make([]protocol.DeviceID, 0, len(to.Devices))
for deviceID, toCfg := range toDevices {
fromCfg, ok := fromDevices[deviceID]
if !ok {
@@ -2913,17 +2887,17 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
continue
}
// Ignored folder was removed, reconnect to retrigger the prompt.
if !toCfg.Paused && len(fromCfg.IgnoredFolders) > len(toCfg.IgnoredFolders) {
closeDevices = append(closeDevices, deviceID)
}
if toCfg.Paused {
l.Infoln("Pausing", deviceID)
closeDevices = append(closeDevices, deviceID)
delete(clusterConfigDevices, deviceID)
m.evLogger.Log(events.DevicePaused, map[string]string{"device": deviceID.String()})
} else {
// Ignored folder was removed, reconnect to retrigger the prompt.
if len(fromCfg.IgnoredFolders) > len(toCfg.IgnoredFolders) {
closeDevices = append(closeDevices, deviceID)
}
l.Infoln("Resuming", deviceID)
m.evLogger.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()})
}
}
@@ -2939,11 +2913,13 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
m.pmut.RLock()
for _, id := range closeDevices {
delete(clusterConfigDevices, id)
if conn, ok := m.conn[id]; ok {
go conn.Close(errDevicePaused)
}
}
for _, id := range removedDevices {
delete(clusterConfigDevices, id)
if conn, ok := m.conn[id]; ok {
go conn.Close(errDeviceRemoved)
}
@@ -2955,8 +2931,8 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
ignoredDevices := observedDeviceSet(to.IgnoredDevices)
m.cleanPending(toDevices, toFolders, ignoredDevices, removedFolders)
m.globalRequestLimiter.setCapacity(1024 * to.Options.MaxConcurrentIncomingRequestKiB())
m.folderIOLimiter.setCapacity(to.Options.MaxFolderConcurrency())
m.globalRequestLimiter.SetCapacity(1024 * to.Options.MaxConcurrentIncomingRequestKiB())
m.folderIOLimiter.SetCapacity(to.Options.MaxFolderConcurrency())
// Some options don't require restart as those components handle it fine
// by themselves. Compare the options structs containing only the
@@ -2973,7 +2949,9 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
var removedPendingFolders []map[string]string
pendingFolders, err := m.db.PendingFolders()
if err != nil {
l.Infof("Could not iterate through pending folder entries for cleanup: %v", err)
msg := "Could not iterate through pending folder entries for cleanup"
l.Warnf("%v: %v", msg, err)
m.evLogger.Log(events.Failure, msg)
// Continue with pending devices below, loop is skipped.
}
for folderID, pf := range pendingFolders {
@@ -2982,10 +2960,15 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
// folders as well, assuming the folder is no longer of interest
// at all (but might become pending again).
l.Debugf("Discarding pending removed folder %v from all devices", folderID)
m.db.RemovePendingFolder(folderID)
removedPendingFolders = append(removedPendingFolders, map[string]string{
"folderID": folderID,
})
if err := m.db.RemovePendingFolder(folderID); err != nil {
msg := "Failed to remove pending folder entry"
l.Warnf("%v (%v): %v", msg, folderID, err)
m.evLogger.Log(events.Failure, msg)
} else {
removedPendingFolders = append(removedPendingFolders, map[string]string{
"folderID": folderID,
})
}
continue
}
for deviceID := range pf.OfferedBy {
@@ -3004,7 +2987,12 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
}
continue
removeFolderForDevice:
m.db.RemovePendingFolderForDevice(folderID, deviceID)
if err := m.db.RemovePendingFolderForDevice(folderID, deviceID); err != nil {
msg := "Failed to remove pending folder-device entry"
l.Warnf("%v (%v, %v): %v", msg, folderID, deviceID, err)
m.evLogger.Log(events.Failure, msg)
continue
}
removedPendingFolders = append(removedPendingFolders, map[string]string{
"folderID": folderID,
"deviceID": deviceID.String(),
@@ -3020,7 +3008,9 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
var removedPendingDevices []map[string]string
pendingDevices, err := m.db.PendingDevices()
if err != nil {
l.Infof("Could not iterate through pending device entries for cleanup: %v", err)
msg := "Could not iterate through pending device entries for cleanup"
l.Warnf("%v: %v", msg, err)
m.evLogger.Log(events.Failure, msg)
return
}
for deviceID := range pendingDevices {
@@ -3034,7 +3024,12 @@ func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.Device
}
continue
removeDevice:
m.db.RemovePendingDevice(deviceID)
if err := m.db.RemovePendingDevice(deviceID); err != nil {
msg := "Failed to remove pending device entry"
l.Warnf("%v: %v", msg, err)
m.evLogger.Log(events.Failure, msg)
continue
}
removedPendingDevices = append(removedPendingDevices, map[string]string{
"deviceID": deviceID.String(),
})
@@ -3076,6 +3071,57 @@ func (m *model) PendingFolders(device protocol.DeviceID) (map[string]db.PendingF
return m.db.PendingFoldersForDevice(device)
}
// DismissPendingDevices removes the record of a specific pending device.
func (m *model) DismissPendingDevice(device protocol.DeviceID) error {
l.Debugf("Discarding pending device %v", device)
err := m.db.RemovePendingDevice(device)
if err != nil {
return err
}
removedPendingDevices := []map[string]string{
{"deviceID": device.String()},
}
m.evLogger.Log(events.PendingDevicesChanged, map[string]interface{}{
"removed": removedPendingDevices,
})
return nil
}
// DismissPendingFolders removes records of pending folders. Either a specific folder /
// device combination, or all matching a specific folder ID if the device argument is
// specified as EmptyDeviceID.
func (m *model) DismissPendingFolder(device protocol.DeviceID, folder string) error {
var removedPendingFolders []map[string]string
if device == protocol.EmptyDeviceID {
l.Debugf("Discarding pending removed folder %s from all devices", folder)
err := m.db.RemovePendingFolder(folder)
if err != nil {
return err
}
removedPendingFolders = []map[string]string{
{"folderID": folder},
}
} else {
l.Debugf("Discarding pending folder %s from device %v", folder, device)
err := m.db.RemovePendingFolderForDevice(folder, device)
if err != nil {
return err
}
removedPendingFolders = []map[string]string{
{
"folderID": folder,
"deviceID": device.String(),
},
}
}
if len(removedPendingFolders) > 0 {
m.evLogger.Log(events.PendingFoldersChanged, map[string]interface{}{
"removed": removedPendingFolders,
})
}
return nil
}
// mapFolders returns a map of folder ID to folder configuration for the given
// slice of folder configurations.
func mapFolders(folders []config.FolderConfiguration) map[string]config.FolderConfiguration {
@@ -3119,23 +3165,6 @@ func readOffsetIntoBuf(fs fs.Filesystem, file string, offset int64, buf []byte)
return n, err
}
// makeForgetUpdate takes an index update and constructs a download progress update
// causing to forget any progress for files which we've just been sent.
func makeForgetUpdate(files []protocol.FileInfo) []protocol.FileDownloadProgressUpdate {
updates := make([]protocol.FileDownloadProgressUpdate, 0, len(files))
for _, file := range files {
if file.IsSymlink() || file.IsDirectory() || file.IsDeleted() {
continue
}
updates = append(updates, protocol.FileDownloadProgressUpdate{
Name: file.Name,
Version: file.Version,
UpdateType: protocol.FileDownloadProgressUpdateTypeForget,
})
}
return updates
}
// folderDeviceSet is a set of (folder, deviceID) pairs
type folderDeviceSet map[string]map[protocol.DeviceID]struct{}
@@ -3242,6 +3271,7 @@ type updatedPendingFolder struct {
FolderLabel string `json:"folderLabel"`
DeviceID protocol.DeviceID `json:"deviceID"`
ReceiveEncrypted bool `json:"receiveEncrypted"`
RemoteEncrypted bool `json:"remoteEncrypted"`
}
// redactPathError checks if the error is actually a os.PathError, and if yes

View File

@@ -38,6 +38,7 @@ import (
protocolmocks "github.com/syncthing/syncthing/lib/protocol/mocks"
srand "github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/testutils"
"github.com/syncthing/syncthing/lib/util"
"github.com/syncthing/syncthing/lib/versioner"
)
@@ -220,15 +221,16 @@ func BenchmarkIndex_100(b *testing.B) {
}
func benchmarkIndex(b *testing.B, nfiles int) {
m := setupModel(b, defaultCfgWrapper)
defer cleanupModel(m)
m, _, fcfg, wcfgCancel := setupModelWithConnection(b)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
files := genFiles(nfiles)
must(b, m.Index(device1, "default", files))
must(b, m.Index(device1, fcfg.ID, files))
b.ResetTimer()
for i := 0; i < b.N; i++ {
must(b, m.Index(device1, "default", files))
must(b, m.Index(device1, fcfg.ID, files))
}
b.ReportAllocs()
}
@@ -246,17 +248,18 @@ func BenchmarkIndexUpdate_10000_1(b *testing.B) {
}
func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
m := setupModel(b, defaultCfgWrapper)
defer cleanupModel(m)
m, _, fcfg, wcfgCancel := setupModelWithConnection(b)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
files := genFiles(nfiles)
ufiles := genFiles(nufiles)
must(b, m.Index(device1, "default", files))
must(b, m.Index(device1, fcfg.ID, files))
b.ResetTimer()
for i := 0; i < b.N; i++ {
must(b, m.IndexUpdate(device1, "default", ufiles))
must(b, m.IndexUpdate(device1, fcfg.ID, ufiles))
}
b.ReportAllocs()
}
@@ -428,8 +431,7 @@ func TestClusterConfig(t *testing.T) {
m.ServeBackground()
defer cleanupModel(m)
cm, _, err := m.generateClusterConfig(device2)
must(t, err)
cm, _ := m.generateClusterConfig(device2)
if l := len(cm.Folders); l != 2 {
t.Fatalf("Incorrect number of folders %d != 2", l)
@@ -516,12 +518,18 @@ func TestIntroducer(t *testing.T) {
},
},
})
cc := basicClusterConfig(myID, device1, "folder1")
cc := basicClusterConfig(myID, device1, "folder1", "folder2")
cc.Folders[0].Devices = append(cc.Folders[0].Devices, protocol.Device{
ID: device2,
Introducer: true,
SkipIntroductionRemovals: true,
})
cc.Folders[1].Devices = append(cc.Folders[1].Devices, protocol.Device{
ID: device2,
Introducer: true,
SkipIntroductionRemovals: true,
EncryptionPasswordToken: []byte("faketoken"),
})
m.ClusterConfig(device1, cc)
if newDev, ok := m.cfg.Device(device2); !ok || !newDev.Introducer || !newDev.SkipIntroductionRemovals {
@@ -532,6 +540,12 @@ func TestIntroducer(t *testing.T) {
t.Error("expected folder 1 to have device2 introduced by device 1")
}
for _, devCfg := range m.cfg.Folders()["folder2"].Devices {
if devCfg.DeviceID == device2 {
t.Error("Device was added even though it's untrusted")
}
}
cleanupModel(m)
cancel()
m, cancel = newState(t, config.Configuration{
@@ -854,8 +868,7 @@ func TestIssue4897(t *testing.T) {
defer cleanupModel(m)
cancel()
cm, _, err := m.generateClusterConfig(device1)
must(t, err)
cm, _ := m.generateClusterConfig(device1)
if l := len(cm.Folders); l != 1 {
t.Errorf("Cluster config contains %v folders, expected 1", l)
}
@@ -1695,9 +1708,8 @@ func TestRWScanRecovery(t *testing.T) {
}
func TestGlobalDirectoryTree(t *testing.T) {
w, fcfg, wCancel := tmpDefaultWrapper()
m, _, fcfg, wCancel := setupModelWithConnection(t)
defer wCancel()
m := setupModel(t, w)
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
b := func(isfile bool, path ...string) protocol.FileInfo {
@@ -1999,18 +2011,18 @@ func BenchmarkTree_100_10(b *testing.B) {
}
func benchmarkTree(b *testing.B, n1, n2 int) {
m := newModel(b, defaultCfgWrapper, myID, "syncthing", "dev", nil)
m.ServeBackground()
defer cleanupModel(m)
m, _, fcfg, wcfgCancel := setupModelWithConnection(b)
defer wcfgCancel()
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
m.ScanFolder("default")
m.ScanFolder(fcfg.ID)
files := genDeepFiles(n1, n2)
must(b, m.Index(device1, "default", files))
must(b, m.Index(device1, fcfg.ID, files))
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.GlobalDirectoryTree("default", "", -1, false)
m.GlobalDirectoryTree(fcfg.ID, "", -1, false)
}
b.ReportAllocs()
}
@@ -2234,8 +2246,10 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) {
t.Error("not shared with device2")
}
if conn2.Closed() {
select {
case <-conn2.Closed():
t.Error("conn already closed")
default:
}
if _, err := wcfg.RemoveDevice(device2); err != nil {
@@ -2260,7 +2274,9 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) {
}
}
if !conn2.Closed() {
select {
case <-conn2.Closed():
default:
t.Error("connection not closed")
}
@@ -2347,7 +2363,7 @@ func TestIssue3496(t *testing.T) {
t.Log(comp)
// Check that NeedSize does the correct thing
need := needSize(t, m, "default")
need := needSizeLocal(t, m, "default")
if need.Files != 1 || need.Bytes != 1234 {
// The one we added synthetically above
t.Errorf("Incorrect need size; %d, %d != 1, 1234", need.Files, need.Bytes)
@@ -2627,43 +2643,43 @@ func TestCustomMarkerName(t *testing.T) {
}
func TestRemoveDirWithContent(t *testing.T) {
defer func() {
defaultFs.RemoveAll("dirwith")
}()
m, _, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())
defaultFs.MkdirAll("dirwith", 0755)
tfs.MkdirAll("dirwith", 0755)
content := filepath.Join("dirwith", "content")
fd, err := defaultFs.Create(content)
fd, err := tfs.Create(content)
must(t, err)
fd.Close()
m := setupModel(t, defaultCfgWrapper)
defer cleanupModel(m)
must(t, m.ScanFolder(fcfg.ID))
dir, ok := m.testCurrentFolderFile("default", "dirwith")
dir, ok := m.testCurrentFolderFile(fcfg.ID, "dirwith")
if !ok {
t.Fatalf("Can't get dir \"dirwith\" after initial scan")
}
dir.Deleted = true
dir.Version = dir.Version.Update(device1.Short()).Update(device1.Short())
file, ok := m.testCurrentFolderFile("default", content)
file, ok := m.testCurrentFolderFile(fcfg.ID, content)
if !ok {
t.Fatalf("Can't get file \"%v\" after initial scan", content)
}
file.Deleted = true
file.Version = file.Version.Update(device1.Short()).Update(device1.Short())
must(t, m.IndexUpdate(device1, "default", []protocol.FileInfo{dir, file}))
must(t, m.IndexUpdate(device1, fcfg.ID, []protocol.FileInfo{dir, file}))
// Is there something we could trigger on instead of just waiting?
timeout := time.NewTimer(5 * time.Second)
for {
dir, ok := m.testCurrentFolderFile("default", "dirwith")
dir, ok := m.testCurrentFolderFile(fcfg.ID, "dirwith")
if !ok {
t.Fatalf("Can't get dir \"dirwith\" after index update")
}
file, ok := m.testCurrentFolderFile("default", content)
file, ok := m.testCurrentFolderFile(fcfg.ID, content)
if !ok {
t.Fatalf("Can't get file \"%v\" after index update", content)
}
@@ -3304,14 +3320,14 @@ func TestDeviceWasSeen(t *testing.T) {
}
func TestNewLimitedRequestResponse(t *testing.T) {
l0 := newByteSemaphore(0)
l1 := newByteSemaphore(1024)
l2 := (*byteSemaphore)(nil)
l0 := util.NewSemaphore(0)
l1 := util.NewSemaphore(1024)
l2 := (*util.Semaphore)(nil)
// Should take 500 bytes from any non-unlimited non-nil limiters.
res := newLimitedRequestResponse(500, l0, l1, l2)
if l1.available != 1024-500 {
if l1.Available() != 1024-500 {
t.Error("should have taken bytes from limited limiter")
}
@@ -3321,7 +3337,7 @@ func TestNewLimitedRequestResponse(t *testing.T) {
// Try to take 1024 bytes to make sure the bytes were returned.
done := make(chan struct{})
go func() {
l1.take(1024)
l1.Take(1024)
close(done)
}()
select {
@@ -3766,14 +3782,14 @@ func TestAddFolderCompletion(t *testing.T) {
}
func TestScanDeletedROChangedOnSR(t *testing.T) {
w, fcfg, wCancel := tmpDefaultWrapper()
defer wCancel()
fcfg.Type = config.FolderTypeReceiveOnly
setFolder(t, w, fcfg)
m := setupModel(t, w)
defer cleanupModel(m)
name := "foo"
m, _, fcfg, wCancel := setupModelWithConnection(t)
ffs := fcfg.Filesystem()
defer wCancel()
defer cleanupModelAndRemoveDir(m, ffs.URI())
fcfg.Type = config.FolderTypeReceiveOnly
setFolder(t, m.cfg, fcfg)
name := "foo"
must(t, writeFile(ffs, name, []byte(name), 0644))
m.ScanFolders()
@@ -3794,7 +3810,7 @@ func TestScanDeletedROChangedOnSR(t *testing.T) {
}
fcfg.Type = config.FolderTypeSendReceive
setFolder(t, w, fcfg)
setFolder(t, m.cfg, fcfg)
m.ScanFolders()
if receiveOnlyChangedSize(t, m, fcfg.ID).Deleted != 0 {
@@ -3889,6 +3905,8 @@ func TestIssue6961(t *testing.T) {
}
m.ServeBackground()
defer cleanupModelAndRemoveDir(m, tfs.URI())
addFakeConn(m, device1, fcfg.ID)
addFakeConn(m, device2, fcfg.ID)
m.ScanFolders()
name := "foo"
@@ -3937,9 +3955,8 @@ func TestIssue6961(t *testing.T) {
}
func TestCompletionEmptyGlobal(t *testing.T) {
wcfg, fcfg, wcfgCancel := tmpDefaultWrapper()
m, _, fcfg, wcfgCancel := setupModelWithConnection(t)
defer wcfgCancel()
m := setupModel(t, wcfg)
defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI())
files := []protocol.FileInfo{{Name: "foo", Version: protocol.Vector{}.Update(myID.Short()), Sequence: 1}}
m.fmut.Lock()
@@ -3960,6 +3977,8 @@ func TestNeedMetaAfterIndexReset(t *testing.T) {
addDevice2(t, w, fcfg)
m := setupModel(t, w)
defer cleanupModelAndRemoveDir(m, fcfg.Path)
addFakeConn(m, device1, fcfg.ID)
addFakeConn(m, device2, fcfg.ID)
var seq int64 = 1
files := []protocol.FileInfo{{Name: "foo", Size: 10, Version: protocol.Vector{}.Update(device1.Short()), Sequence: seq}}
@@ -4097,7 +4116,7 @@ func TestCcCheckEncryption(t *testing.T) {
dcfg.EncryptionPassword = pw
}
deviceInfos := &indexSenderStartInfo{
deviceInfos := &clusterConfigDeviceInfo{
remote: protocol.Device{ID: device1, EncryptionPasswordToken: tc.tokenRemote},
local: protocol.Device{ID: myID, EncryptionPasswordToken: tc.tokenLocal},
}
@@ -4144,8 +4163,7 @@ func TestCCFolderNotRunning(t *testing.T) {
defer cleanupModelAndRemoveDir(m, tfs.URI())
// A connection can happen before all the folders are started.
cc, _, err := m.generateClusterConfig(device1)
must(t, err)
cc, _ := m.generateClusterConfig(device1)
if l := len(cc.Folders); l != 1 {
t.Fatalf("Expected 1 folder in CC, got %v", l)
}

View File

@@ -1446,6 +1446,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) {
})
must(t, err)
waiter.Wait()
addFakeConn(m, device2, fcfg.ID)
tfs := fcfg.Filesystem()
defer cleanupModelAndRemoveDir(m, tfs.URI())

View File

@@ -194,7 +194,7 @@ func (m *testModel) testCurrentFolderFile(folder string, file string) (protocol.
}
func (m *testModel) testCompletion(device protocol.DeviceID, folder string) FolderCompletion {
comp, err := m.Completion(protocol.LocalDeviceID, "default")
comp, err := m.Completion(device, folder)
must(m.t, err)
return comp
}
@@ -276,7 +276,7 @@ func receiveOnlyChangedSize(t *testing.T, m Model, folder string) db.Counts {
return snap.ReceiveOnlyChangedSize()
}
func needSize(t *testing.T, m Model, folder string) db.Counts {
func needSizeLocal(t *testing.T, m Model, folder string) db.Counts {
t.Helper()
snap := dbSnapshot(t, m, folder)
defer snap.Release()

View File

@@ -224,7 +224,7 @@ func (e encryptedConnection) Close(err error) {
e.conn.Close(err)
}
func (e encryptedConnection) Closed() bool {
func (e encryptedConnection) Closed() <-chan struct{} {
return e.conn.Closed()
}

View File

@@ -16,15 +16,15 @@ type Connection struct {
closeArgsForCall []struct {
arg1 error
}
ClosedStub func() bool
ClosedStub func() <-chan struct{}
closedMutex sync.RWMutex
closedArgsForCall []struct {
}
closedReturns struct {
result1 bool
result1 <-chan struct{}
}
closedReturnsOnCall map[int]struct {
result1 bool
result1 <-chan struct{}
}
ClusterConfigStub func(protocol.ClusterConfig)
clusterConfigMutex sync.RWMutex
@@ -220,7 +220,7 @@ func (fake *Connection) CloseArgsForCall(i int) error {
return argsForCall.arg1
}
func (fake *Connection) Closed() bool {
func (fake *Connection) Closed() <-chan struct{} {
fake.closedMutex.Lock()
ret, specificReturn := fake.closedReturnsOnCall[len(fake.closedArgsForCall)]
fake.closedArgsForCall = append(fake.closedArgsForCall, struct {
@@ -244,32 +244,32 @@ func (fake *Connection) ClosedCallCount() int {
return len(fake.closedArgsForCall)
}
func (fake *Connection) ClosedCalls(stub func() bool) {
func (fake *Connection) ClosedCalls(stub func() <-chan struct{}) {
fake.closedMutex.Lock()
defer fake.closedMutex.Unlock()
fake.ClosedStub = stub
}
func (fake *Connection) ClosedReturns(result1 bool) {
func (fake *Connection) ClosedReturns(result1 <-chan struct{}) {
fake.closedMutex.Lock()
defer fake.closedMutex.Unlock()
fake.ClosedStub = nil
fake.closedReturns = struct {
result1 bool
result1 <-chan struct{}
}{result1}
}
func (fake *Connection) ClosedReturnsOnCall(i int, result1 bool) {
func (fake *Connection) ClosedReturnsOnCall(i int, result1 <-chan struct{}) {
fake.closedMutex.Lock()
defer fake.closedMutex.Unlock()
fake.ClosedStub = nil
if fake.closedReturnsOnCall == nil {
fake.closedReturnsOnCall = make(map[int]struct {
result1 bool
result1 <-chan struct{}
})
}
fake.closedReturnsOnCall[i] = struct {
result1 bool
result1 <-chan struct{}
}{result1}
}

View File

@@ -151,7 +151,7 @@ type Connection interface {
ClusterConfig(config ClusterConfig)
DownloadProgress(ctx context.Context, folder string, updates []FileDownloadProgressUpdate)
Statistics() Statistics
Closed() bool
Closed() <-chan struct{}
ConnectionInfo
}
@@ -380,13 +380,8 @@ func (c *rawConnection) ClusterConfig(config ClusterConfig) {
}
}
func (c *rawConnection) Closed() bool {
select {
case <-c.closed:
return true
default:
return false
}
func (c *rawConnection) Closed() <-chan struct{} {
return c.closed
}
// DownloadProgress sends the progress updates for the files that are currently being downloaded.
@@ -528,7 +523,7 @@ func (c *rawConnection) readMessageAfterHeader(hdr Header, fourByteBuf []byte) (
// Nothing
case MessageCompressionLZ4:
decomp, err := c.lz4Decompress(buf)
decomp, err := lz4Decompress(buf)
BufferPool.Put(buf)
if err != nil {
return nil, errors.Wrap(err, "decompressing message")
@@ -745,26 +740,56 @@ func (c *rawConnection) writerLoop() {
func (c *rawConnection) writeMessage(msg message) error {
msgContext, _ := messageContext(msg)
l.Debugf("Writing %v", msgContext)
if c.shouldCompressMessage(msg) {
return c.writeCompressedMessage(msg)
}
return c.writeUncompressedMessage(msg)
}
func (c *rawConnection) writeCompressedMessage(msg message) error {
size := msg.ProtoSize()
buf := BufferPool.Get(size)
if _, err := msg.MarshalTo(buf); err != nil {
BufferPool.Put(buf)
hdr := Header{
Type: c.typeOf(msg),
}
hdrSize := hdr.ProtoSize()
if hdrSize > 1<<16-1 {
panic("impossibly large header")
}
overhead := 2 + hdrSize + 4
totSize := overhead + size
buf := BufferPool.Get(totSize)
defer BufferPool.Put(buf)
// Message
if _, err := msg.MarshalTo(buf[2+hdrSize+4:]); err != nil {
return errors.Wrap(err, "marshalling message")
}
compressed, err := c.lz4Compress(buf)
if err != nil {
BufferPool.Put(buf)
return errors.Wrap(err, "compressing message")
if c.shouldCompressMessage(msg) {
ok, err := c.writeCompressedMessage(msg, buf[overhead:], overhead)
if ok {
return err
}
}
// Header length
binary.BigEndian.PutUint16(buf, uint16(hdrSize))
// Header
if _, err := hdr.MarshalTo(buf[2:]); err != nil {
return errors.Wrap(err, "marshalling header")
}
// Message length
binary.BigEndian.PutUint32(buf[2+hdrSize:], uint32(size))
n, err := c.cw.Write(buf)
l.Debugf("wrote %d bytes on the wire (2 bytes length, %d bytes header, 4 bytes message length, %d bytes message), err=%v", n, hdrSize, size, err)
if err != nil {
return errors.Wrap(err, "writing message")
}
return nil
}
// Write msg out compressed, given its uncompressed marshaled payload and overhead.
//
// The first return value indicates whether compression succeeded.
// If not, the caller should retry without compression.
func (c *rawConnection) writeCompressedMessage(msg message, marshaled []byte, overhead int) (ok bool, err error) {
hdr := Header{
Type: c.typeOf(msg),
Compression: MessageCompressionLZ4,
@@ -774,71 +799,32 @@ func (c *rawConnection) writeCompressedMessage(msg message) error {
panic("impossibly large header")
}
compressedSize := len(compressed)
totSize := 2 + hdrSize + 4 + compressedSize
buf = BufferPool.Upgrade(buf, totSize)
cOverhead := 2 + hdrSize + 4
maxCompressed := cOverhead + lz4.CompressBound(len(marshaled))
buf := BufferPool.Get(maxCompressed)
defer BufferPool.Put(buf)
compressedSize, err := lz4Compress(marshaled, buf[cOverhead:])
totSize := compressedSize + cOverhead
if err != nil || totSize >= len(marshaled)+overhead {
return false, nil
}
// Header length
binary.BigEndian.PutUint16(buf, uint16(hdrSize))
// Header
if _, err := hdr.MarshalTo(buf[2:]); err != nil {
BufferPool.Put(buf)
BufferPool.Put(compressed)
return errors.Wrap(err, "marshalling header")
return true, errors.Wrap(err, "marshalling header")
}
// Message length
binary.BigEndian.PutUint32(buf[2+hdrSize:], uint32(compressedSize))
// Message
copy(buf[2+hdrSize+4:], compressed)
BufferPool.Put(compressed)
n, err := c.cw.Write(buf)
BufferPool.Put(buf)
l.Debugf("wrote %d bytes on the wire (2 bytes length, %d bytes header, 4 bytes message length, %d bytes message (%d uncompressed)), err=%v", n, hdrSize, compressedSize, size, err)
if err != nil {
return errors.Wrap(err, "writing message")
}
return nil
}
func (c *rawConnection) writeUncompressedMessage(msg message) error {
size := msg.ProtoSize()
hdr := Header{
Type: c.typeOf(msg),
}
hdrSize := hdr.ProtoSize()
if hdrSize > 1<<16-1 {
panic("impossibly large header")
}
totSize := 2 + hdrSize + 4 + size
buf := BufferPool.Get(totSize)
// Header length
binary.BigEndian.PutUint16(buf, uint16(hdrSize))
// Header
if _, err := hdr.MarshalTo(buf[2:]); err != nil {
BufferPool.Put(buf)
return errors.Wrap(err, "marshalling header")
}
// Message length
binary.BigEndian.PutUint32(buf[2+hdrSize:], uint32(size))
// Message
if _, err := msg.MarshalTo(buf[2+hdrSize+4:]); err != nil {
BufferPool.Put(buf)
return errors.Wrap(err, "marshalling message")
}
n, err := c.cw.Write(buf[:totSize])
BufferPool.Put(buf)
l.Debugf("wrote %d bytes on the wire (2 bytes length, %d bytes header, 4 bytes message length, %d bytes message), err=%v", n, hdrSize, size, err)
l.Debugf("wrote %d bytes on the wire (2 bytes length, %d bytes header, 4 bytes message length, %d bytes message (%d uncompressed)), err=%v", n, hdrSize, compressedSize, len(marshaled), err)
if err != nil {
return errors.Wrap(err, "writing message")
return true, errors.Wrap(err, "writing message")
}
return nil
return true, nil
}
func (c *rawConnection) typeOf(msg message) MessageType {
@@ -1023,23 +1009,20 @@ func (c *rawConnection) Statistics() Statistics {
}
}
func (c *rawConnection) lz4Compress(src []byte) ([]byte, error) {
var err error
buf := BufferPool.Get(lz4.CompressBound(len(src)))
func lz4Compress(src, buf []byte) (int, error) {
compressed, err := lz4.Encode(buf, src)
if err != nil {
BufferPool.Put(buf)
return nil, err
return -1, err
}
if &compressed[0] != &buf[0] {
panic("bug: lz4.Compress allocated, which it must not (should use buffer pool)")
}
binary.BigEndian.PutUint32(compressed, binary.LittleEndian.Uint32(compressed))
return compressed, nil
return len(compressed), nil
}
func (c *rawConnection) lz4Decompress(src []byte) ([]byte, error) {
func lz4Decompress(src []byte) ([]byte, error) {
size := binary.BigEndian.Uint32(src)
binary.LittleEndian.PutUint32(src, size)
var err error

View File

@@ -17,6 +17,7 @@ import (
"testing/quick"
"time"
lz4 "github.com/bkaradzic/go-lz4"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/testutils"
)
@@ -439,9 +440,42 @@ func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
return true
}
func TestLZ4Compression(t *testing.T) {
c := new(rawConnection)
func TestWriteCompressed(t *testing.T) {
for _, random := range []bool{false, true} {
buf := new(bytes.Buffer)
c := &rawConnection{
cr: &countingReader{Reader: buf},
cw: &countingWriter{Writer: buf},
compression: CompressionAlways,
}
msg := &Response{Data: make([]byte, 10240)}
if random {
// This should make the message uncompressible.
rand.Read(msg.Data)
}
if err := c.writeMessage(msg); err != nil {
t.Fatal(err)
}
got, err := c.readMessage(make([]byte, 4))
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got.(*Response).Data, msg.Data) {
t.Error("received the wrong message")
}
hdr := Header{Type: c.typeOf(msg)}
size := int64(2 + hdr.ProtoSize() + 4 + msg.ProtoSize())
if c.cr.tot > size {
t.Errorf("compression enlarged message from %d to %d",
size, c.cr.tot)
}
}
}
func TestLZ4Compression(t *testing.T) {
for i := 0; i < 10; i++ {
dataLen := 150 + rand.Intn(150)
data := make([]byte, dataLen)
@@ -449,13 +483,15 @@ func TestLZ4Compression(t *testing.T) {
if err != nil {
t.Fatal(err)
}
comp, err := c.lz4Compress(data)
comp := make([]byte, lz4.CompressBound(dataLen))
compLen, err := lz4Compress(data, comp)
if err != nil {
t.Errorf("compressing %d bytes: %v", dataLen, err)
continue
}
res, err := c.lz4Decompress(comp)
res, err := lz4Decompress(comp[:compLen])
if err != nil {
t.Errorf("decompressing %d bytes to %d: %v", len(comp), dataLen, err)
continue
@@ -470,38 +506,6 @@ func TestLZ4Compression(t *testing.T) {
}
}
func TestStressLZ4CompressGrows(t *testing.T) {
c := new(rawConnection)
success := 0
for i := 0; i < 100; i++ {
// Create a slize that is precisely one min block size, fill it with
// random data. This shouldn't compress at all, so will in fact
// become larger when LZ4 does its thing.
data := make([]byte, MinBlockSize)
if _, err := rand.Reader.Read(data); err != nil {
t.Fatal("randomness failure")
}
comp, err := c.lz4Compress(data)
if err != nil {
t.Fatal("unexpected compression error: ", err)
}
if len(comp) < len(data) {
// data size should grow. We must have been really unlucky in
// the random generation, try again.
continue
}
// Putting it into the buffer pool shouldn't panic because the block
// should come from there to begin with.
BufferPool.Put(comp)
success++
}
if success == 0 {
t.Fatal("unable to find data that grows when compressed")
}
}
func TestCheckFilename(t *testing.T) {
cases := []struct {
name string

View File

@@ -11,58 +11,43 @@ import (
"github.com/syncthing/syncthing/lib/relay/protocol"
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
"github.com/thejerf/suture/v4"
)
type relayClientFactory func(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation, timeout time.Duration) RelayClient
var (
supportedSchemes = map[string]relayClientFactory{
"relay": newStaticClient,
"dynamic+http": newDynamicClient,
"dynamic+https": newDynamicClient,
}
)
type RelayClient interface {
suture.Service
Error() error
Latency() time.Duration
String() string
Invitations() chan protocol.SessionInvitation
Invitations() <-chan protocol.SessionInvitation
URI() *url.URL
}
func NewClient(uri *url.URL, certs []tls.Certificate, timeout time.Duration) (RelayClient, error) {
factory, ok := supportedSchemes[uri.Scheme]
if !ok {
invitations := make(chan protocol.SessionInvitation)
switch uri.Scheme {
case "relay":
return newStaticClient(uri, certs, invitations, timeout), nil
case "dynamic+http", "dynamic+https":
return newDynamicClient(uri, certs, invitations, timeout), nil
default:
return nil, fmt.Errorf("unsupported scheme: %s", uri.Scheme)
}
invitations := make(chan protocol.SessionInvitation)
return factory(uri, certs, invitations, timeout), nil
}
type commonClient struct {
svcutil.ServiceWithError
invitations chan protocol.SessionInvitation
mut sync.RWMutex
}
func newCommonClient(invitations chan protocol.SessionInvitation, serve func(context.Context) error, creator string) commonClient {
c := commonClient{
invitations: invitations,
mut: sync.NewRWMutex(),
return commonClient{
ServiceWithError: svcutil.AsService(serve, creator),
invitations: invitations,
}
c.ServiceWithError = svcutil.AsService(serve, creator)
return c
}
func (c *commonClient) Invitations() chan protocol.SessionInvitation {
c.mut.RLock()
defer c.mut.RUnlock()
func (c *commonClient) Invitations() <-chan protocol.SessionInvitation {
return c.invitations
}

View File

@@ -11,6 +11,7 @@ import (
"net/http"
"net/url"
"sort"
"sync"
"time"
"github.com/syncthing/syncthing/lib/osutil"
@@ -25,10 +26,11 @@ type dynamicClient struct {
certs []tls.Certificate
timeout time.Duration
client RelayClient
mut sync.RWMutex // Protects client.
client *staticClient
}
func newDynamicClient(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation, timeout time.Duration) RelayClient {
func newDynamicClient(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation, timeout time.Duration) *dynamicClient {
c := &dynamicClient{
pooladdr: uri,
certs: certs,
@@ -114,15 +116,6 @@ func (c *dynamicClient) Error() error {
return c.client.Error()
}
func (c *dynamicClient) Latency() time.Duration {
c.mut.RLock()
defer c.mut.RUnlock()
if c.client == nil {
return time.Hour
}
return c.client.Latency()
}
func (c *dynamicClient) String() string {
return fmt.Sprintf("DynamicClient:%p:%s@%s", c, c.URI(), c.pooladdr)
}

View File

@@ -28,12 +28,9 @@ type staticClient struct {
connectTimeout time.Duration
conn *tls.Conn
connected bool
latency time.Duration
}
func newStaticClient(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation, timeout time.Duration) RelayClient {
func newStaticClient(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation, timeout time.Duration) *staticClient {
c := &staticClient{
uri: uri,
@@ -67,10 +64,6 @@ func (c *staticClient) serve(ctx context.Context) error {
l.Infof("Joined relay %s://%s", c.uri.Scheme, c.uri.Host)
c.mut.Lock()
c.connected = true
c.mut.Unlock()
messages := make(chan interface{})
errorsc := make(chan error, 1)
@@ -128,20 +121,6 @@ func (c *staticClient) serve(ctx context.Context) error {
}
}
func (c *staticClient) StatusOK() bool {
c.mut.RLock()
con := c.connected
c.mut.RUnlock()
return con
}
func (c *staticClient) Latency() time.Duration {
c.mut.RLock()
lat := c.latency
c.mut.RUnlock()
return lat
}
func (c *staticClient) String() string {
return fmt.Sprintf("StaticClient:%p@%s", c, c.URI())
}
@@ -155,7 +134,6 @@ func (c *staticClient) connect(ctx context.Context) error {
return fmt.Errorf("unsupported relay scheme: %v", c.uri.Scheme)
}
t0 := time.Now()
timeoutCtx, cancel := context.WithTimeout(ctx, c.connectTimeout)
defer cancel()
tcpConn, err := dialer.DialContext(timeoutCtx, "tcp", c.uri.Host)
@@ -163,10 +141,6 @@ func (c *staticClient) connect(ctx context.Context) error {
return err
}
c.mut.Lock()
c.latency = time.Since(t0)
c.mut.Unlock()
conn := tls.Client(tcpConn, c.config)
if err := conn.SetDeadline(time.Now().Add(c.connectTimeout)); err != nil {
@@ -185,10 +159,6 @@ func (c *staticClient) connect(ctx context.Context) error {
func (c *staticClient) disconnect() {
l.Debugln(c, "disconnecting")
c.mut.Lock()
c.connected = false
c.mut.Unlock()
c.conn.Close()
}

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