Compare commits

...

50 Commits

Author SHA1 Message Date
Jakob Borg
6d280e7b64 Update docs & translations 2016-03-23 09:26:04 +01:00
Wulf Weich
e6918a5857 gui: Better accessibility for folder & device panels (fixes #2288) 2016-03-23 09:26:04 +01:00
Jakob Borg
dca8245ba4 cmd/syncthing: Return 500 with an error object instead of empty 200 on marshalling failure in REST response 2016-03-23 09:26:04 +01:00
Audrius Butkevicius
9550817078 lib/relay/client: Log relay client messages (fixes #2624) 2016-03-23 09:26:04 +01:00
Jakob Borg
7a91860735 meta: Amend wweich in NICKS 2016-03-23 09:26:04 +01:00
Jakob Borg
0f4abac8c2 gui: Update translation files and assets 2016-03-23 09:26:04 +01:00
Wulf Weich
b16050b978 gui: Differentiate local and remote devices more clearly 2016-03-23 09:26:04 +01:00
Jakob Borg
aced62fec3 gui: Improve layout of footer on narrow screens (fixes #2663) 2016-03-23 09:26:04 +01:00
Jakob Borg
5155e24bc7 Amend wweich 2016-03-23 09:26:04 +01:00
Jakob Borg
3aabe3a51d Add wweich (noreply-address) 2016-03-23 09:26:04 +01:00
wweich
17de015b90 Increase contrast for readonly form controls in dark theme (fixes #2820)
Increase the dark theme color value for text in readonly form controls for better contrast between text and background.
2016-03-23 09:26:04 +01:00
Audrius Butkevicius
f19e71b333 Clarify GUI stuff (fixes #2819) 2016-03-23 09:26:03 +01:00
Jakob Borg
8fea354b74 lib/model: Properly handle deleting multiple files when doing scans with subs (fixes #2851) 2016-03-23 09:26:03 +01:00
Jakob Borg
7a81c27cc6 lib/model: Correct handling of multiple subs when scanning (ref #2851)
Previously the code failed in that it would return top-level plus a sub,
i.e. ["", "foo"], and it would consider "usr/lib" a prefix of
"usr/libexec" which it is not.
2016-03-23 09:26:03 +01:00
Jakob Borg
ea5808d833 Only check specified paths in check-authors.go 2016-03-06 11:59:03 +01:00
Jakob Borg
8b045a826a Update docs & translations 2016-03-06 11:55:53 +01:00
Laurent Arnoud
1a6e078510 Add priority,section and homepage to debian/control 2016-03-06 11:54:43 +01:00
Laurent Arnoud
daff8010cd Fix description-contains-tabs and improve description 2016-03-06 11:54:39 +01:00
Audrius Butkevicius
4035930e0e Add kralo 2016-03-06 11:54:27 +01:00
Max Schulze
fc63a384b2 gui: add a lock icon to the folder title for easy overview (fixes #2703)
(to indicate it is a master directory)
2016-03-06 11:54:20 +01:00
Max Schulze
2f3449b651 gui: add html tooltips (title) to the folder path and syncthing version elements (fixes #2758) 2016-03-06 11:54:15 +01:00
Stefan Tatschner
31c65a39e4 systemd: Add syncthing-resume.service
This systemd service restarts Syncthing after resume from suspend
via sending SIGHUP. By default Syncthing detects resume from sleep
on its own by looking for jumps in the system clock. Since systemd
knows exactly when the system resumes from sleep let's trigger
the Syncthing restart from there. Doing this in systemd eliminates
some annoying delay, as the service is restarted immediately after
resume. Also, using the systemd dependency mechanism syncthing-inotify
is restarted as well.

$ journalctl -e --identifier syncthing --identifier syncthing-inotify --identifier systemd
Feb 22 09:44:27 kronos systemd[1]: Reached target Sleep.
Feb 22 09:44:27 kronos systemd[1]: Starting Suspend...
Feb 22 09:44:33 kronos systemd[1]: Time has been changed
Feb 22 09:44:33 kronos systemd[963]: Time has been changed
Feb 22 09:44:33 kronos systemd[1]: Started Suspend.
Feb 22 09:44:33 kronos systemd[1]: sleep.target: Unit not needed anymore. Stopping.
Feb 22 09:44:33 kronos systemd[1]: Stopped target Sleep.
Feb 22 09:44:33 kronos systemd[1]: Reached target Suspend.
Feb 22 09:44:33 kronos systemd[1]: suspend.target: Unit is bound to inactive unit systemd-suspend.service. Stopping, too.
Feb 22 09:44:33 kronos systemd[1]: Stopped target Suspend.
Feb 22 09:44:33 kronos systemd[1]: Starting Restart Syncthing after resume...
Feb 22 09:44:33 kronos syncthing[2561]: [35K66] OK: Exiting
Feb 22 09:44:33 kronos systemd[1]: Started Restart Syncthing after resume.
Feb 22 09:44:34 kronos systemd[963]: syncthing.service: Service hold-off time over, scheduling restart.
Feb 22 09:44:34 kronos systemd[963]: Stopping Syncthing Inotify File Watcher...
Feb 22 09:44:34 kronos systemd[963]: Stopped Syncthing Inotify File Watcher.
Feb 22 09:44:34 kronos systemd[963]: Stopped Syncthing - Open Source Continuous File Synchronization.
Feb 22 09:44:34 kronos systemd[963]: Started Syncthing - Open Source Continuous File Synchronization.
Feb 22 09:44:34 kronos systemd[963]: Started Syncthing Inotify File Watcher.
Feb 22 09:44:34 kronos syncthing[2836]: [35K66] INFO: syncthing v0.12.19 "Beryllium Bedbug" (go1.5.3 linux-amd64) builduser@svetlemodry 2016-02-14 19:26:33 UTC

This system service has to be located in "/etc/systemd/system/syncthing-resume.service",
and for packages in "/usr/lib/systemd/system/syncthing-resume.service". It can be
enabled using "systemctl enable syncthing-resume.service".
2016-03-06 11:54:08 +01:00
Jakob Borg
7ba20928c3 Only test with -race on supported platforms (fixes #2765) 2016-03-06 11:53:54 +01:00
Jakob Borg
c482cbbe70 Docs & translations 2016-02-14 20:26:33 +01:00
Jakob Borg
4042a3e406 Merge branch 'master' into v0.12
* master:
  Report versioning usage in usage report
  Swap the corsMiddleware and the csrfMiddleware to the unauthenticated OPTIONS requests are first processed.
  Return "No such object in the index" when /rest/db/file gets called on something that doesn't exist
  Revert "Add .arcconfig to project root"
  Add .arcconfig to project root
2016-02-14 19:39:59 +01:00
Jakob Borg
392132dc3b Merge commit '6f2de31' into v0.12 (no changes taken)
* commit '6f2de31':
  Use v2 of XDR package (actual changes)
  Use v2 of XDR package (auto generated)
  Use v2 of XDR package (deps)
2016-02-14 19:39:34 +01:00
Jakob Borg
459a3dc58c Update docs & translations 2016-02-08 17:39:45 +01:00
Jakob Borg
194a8b0922 Merge commit 'a7a9d7d' into v0.12
* commit 'a7a9d7d':
  Return correct content type for /rest/events
  Rename RawAPIKey -> APIKey in GUIConfiguration
  Add -paths option to print config, key, database paths
  Clean up error handling a bit in protocol.readMessage
  Remove old reference to moved protocol
  Support multiple API keys (command-line and config) (fixes #2747)
2016-02-08 17:38:52 +01:00
Jakob Borg
8a8336ae08 Merge branch 'master' into v0.12
* master:
  Update docs & translations
  build.sh prerelease should rebuild author credits in about dialog
  Use dialer in relay checks (fixes #2732)
  Handle null case for invalid ng-model value (fixes #2392)
  Return status code 307 instead of 302 when redirecting from HTTP to HTTPS
  Benchmark for single database update
  Add a CORS handler to deal with preflight OPTIONS requests
  Add letiemble
  Update docs and translations
  Correct order of pkill(1) arguments in debian script (fixes #2728)
2016-01-31 10:39:10 +01:00
Jakob Borg
458e0b3b8b Merge branch 'master' into v0.12
* master:
  Update docs and translations
  Model.internalScanFolder: Don't ignore special .stfolder and .stignore files.
  Model.internalScanFolderSubs: Scan only requested subs.
  A couple of protocol tests
  Humanize serialization of version vectors (again)
  FetchLatestReleases: fix the error log message
  Add postinst script to restart after upgrade
  Templatize Debian files
  Don't require restart for usage reporting changes (fixes #2704)
  RLimit comment typo
2016-01-24 08:09:28 +01:00
Jakob Borg
9758dc6422 Update docs and translations 2016-01-24 08:08:08 +01:00
Jakob Borg
e4a9fb8a27 Merge master into v0.12 (no changes) 2016-01-17 22:13:34 +01:00
Jakob Borg
f0473fde17 Docs and translations update 2016-01-17 10:57:20 +01:00
Jakob Borg
e2980a5210 Don't crash on folder remove while pulling (fixes #2705) 2016-01-17 10:55:39 +01:00
Jakob Borg
764da14440 Improve API/GUI shutdown handling (fixes #2694)
This fixes both a race condition where we could assign s.stop from one
goroutine and then read it from another without locking, and handles the
fact that listener may be nil at shutdown if we've had a bad
CommitConfiguration call in the meantime.
2016-01-17 10:55:26 +01:00
Audrius Butkevicius
7da6c627fe Handle race within the job queue (fixes #1263) 2016-01-17 10:55:19 +01:00
Jakob Borg
0a092b5b7f Codesign binaries in Mac OS X distribution packages 2016-01-17 10:54:42 +01:00
Jakob Borg
9f8af2327d Update docs & translations 2016-01-13 21:11:02 +01:00
kluppy
345e24142e Update 'Edit' menu to 'Action' menu (fixes #2662) 2016-01-13 21:09:17 +01:00
kluppy
0fdd03ddee Fix location of build translation scripts. 2016-01-13 21:08:34 +01:00
Jakob Borg
a0fa288cb6 Undo the hash algorithm additions; retain flag checks 2016-01-12 16:03:06 +01:00
Jakob Borg
70bac24832 Always run relaying when enabled (fixes #2665) 2016-01-12 16:02:59 +01:00
Jakob Borg
1df40fbdeb Mend protocol tests, for sure 2016-01-12 16:02:38 +01:00
Jakob Borg
91e9ffff85 Rebuild assets 2016-01-12 14:15:00 +01:00
Jakob Borg
853df14e2f Improve protocol tests, close handling 2016-01-12 10:08:50 +01:00
Jakob Borg
e17a772bb6 Don't leak sendIndexes on disconnect (fixes #2589)
Adds a Closed() method on protocol.Connection and clears up
wireformatConnection a little too.
2016-01-12 10:08:41 +01:00
Audrius Butkevicius
90e027d9a4 Silence the linter 2016-01-12 10:08:32 +01:00
alessandro.g89
fdc9a5d8b0 Add dark theme by alessandro.g89
Source: https://userstyles.org/styles/122502/syncthing-dark
2016-01-12 10:08:23 +01:00
Audrius Butkevicius
543891a0a0 Add support for themes (fixes #1925) 2016-01-12 10:08:04 +01:00
Jakob Borg
06921443fc Docs & translations update 2016-01-10 10:10:53 +01:00
119 changed files with 2748 additions and 3189 deletions

View File

@@ -57,6 +57,7 @@ Marc Pujol <kilburn@la3.org>
Marcin Dziadus <dziadus.marcin@gmail.com>
Mateusz Naściszewski <matin1111@wp.pl>
Matt Burke <mburke@amplify.com> <burkemw3@gmail.com>
Max Schulze <max.schulze@online.de> <kralo@users.noreply.github.com>
Michael Jephcote <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
Michael Ploujnikov <ploujj@gmail.com>
Michael Tilli <pyfisch@gmail.com>
@@ -80,4 +81,5 @@ Veeti Paananen <veeti.paananen@rojekti.fi>
Victor Buinsky <vix_booja@tut.by>
Vil Brekin <vilbrekin@gmail.com>
William A. Kennington III <william@wkennington.com>
Wulf Weich <wweich@users.noreply.github.com> <wweich@gmx.de>
Yannic A. <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>

5
Godeps/Godeps.json generated
View File

@@ -11,18 +11,15 @@
},
{
"ImportPath": "github.com/calmh/du",
"Comment": "v1.0.0",
"Rev": "3c0690cca16228b97741327b1b6781397afbdb24"
},
{
"ImportPath": "github.com/calmh/luhn",
"Comment": "v1.0.0",
"Rev": "0c8388ff95fa92d4094011e5a04fc99dea3d1632"
},
{
"ImportPath": "github.com/calmh/xdr",
"Comment": "v2.0.0",
"Rev": "b6e0c321c9b5b28ba5ee21e828323e4b982c6976"
"Rev": "9eb3e1a622d9364deb39c831f7e5f164393d7e37"
},
{
"ImportPath": "github.com/golang/snappy",

View File

@@ -6,5 +6,7 @@ xdr
[![API Documentation](http://img.shields.io/badge/api-Godoc-blue.svg?style=flat)](http://godoc.org/github.com/calmh/xdr)
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT)
This is an XDR marshalling/unmarshalling library. It uses code generation and
not reflection.
This is an XDR encoding/decoding library. It uses code generation and
not reflection. It supports the IPDR bastardized XDR format when built
with `-tags ipdr`.

View File

@@ -36,73 +36,31 @@ type structInfo struct {
Fields []fieldInfo
}
func (i structInfo) SizeExpr() string {
var xdrSizes = map[string]int{
"int8": 4,
"uint8": 4,
"int16": 4,
"uint16": 4,
"int32": 4,
"uint32": 4,
"int64": 8,
"uint64": 8,
"int": 8,
"bool": 4,
}
var terms []string
nl := ""
for _, f := range i.Fields {
if size := xdrSizes[f.FieldType]; size > 0 {
if f.IsSlice {
terms = append(terms, nl+"4+len(o."+f.Name+")*"+strconv.Itoa(size))
} else {
terms = append(terms, strconv.Itoa(size))
}
} else {
switch f.FieldType {
case "string", "[]byte":
if f.IsSlice {
terms = append(terms, nl+"4+xdr.SizeOfSlice(o."+f.Name+")")
} else {
terms = append(terms, nl+"4+len(o."+f.Name+")+xdr.Padding(len(o."+f.Name+"))")
}
default:
if f.IsSlice {
terms = append(terms, nl+"4+xdr.SizeOfSlice(o."+f.Name+")")
} else {
terms = append(terms, nl+"o."+f.Name+".XDRSize()")
}
}
}
nl = "\n"
}
return strings.Join(terms, "+")
}
var headerData = `// ************************************************************
var headerTpl = template.Must(template.New("header").Parse(`// ************************************************************
// This file is automatically generated by genxdr. Do not edit.
// ************************************************************
package {{.Package}}
import (
"bytes"
"io"
"github.com/calmh/xdr"
)
`
`))
var encoderData = `
func (o {{.Name}}) XDRSize() int {
return {{.SizeExpr}}
var encodeTpl = template.Must(template.New("encoder").Parse(`
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
var xw = xdr.NewWriter(w)
return o.EncodeXDRInto(xw)
}//+n
func (o {{.Name}}) MarshalXDR() ([]byte, error) {
buf:= make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}//+n
func (o {{.Name}}) MustMarshalXDR() []byte {
func (o {{.TypeName}}) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
@@ -110,155 +68,141 @@ func (o {{.Name}}) MustMarshalXDR() []byte {
return bs
}//+n
func (o {{.Name}}) MarshalXDRInto(m *xdr.Marshaller) error {
{{range $fi := .Fields}}
{{if $fi.IsSlice}}
{{template "marshalSlice" $fi}}
{{else}}
{{template "marshalValue" $fi}}
{{end}}
{{end}}
return m.Error
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
_, err := o.EncodeXDRInto(xw)
return []byte(aw), err
}//+n
{{define "marshalValue"}}
{{if ne .Convert ""}}
m.Marshal{{.Encoder}}({{.Convert}}(o.{{.Name}}))
{{else if .IsBasic}}
{{if ge .Max 1}}
if l := len(o.{{.Name}}); l > {{.Max}} {
return xdr.ElementSizeExceeded("{{.Name}}", l, {{.Max}})
func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) {
{{range $fieldInfo := .Fields}}
{{if not $fieldInfo.IsSlice}}
{{if ne $fieldInfo.Convert ""}}
xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}))
{{else if $fieldInfo.IsBasic}}
{{if ge $fieldInfo.Max 1}}
if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} {
return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}})
}
{{end}}
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
{{else}}
_, err := o.{{$fieldInfo.Name}}.EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
{{end}}
{{else}}
{{if ge $fieldInfo.Max 1}}
if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} {
return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}})
}
{{end}}
xw.WriteUint32(uint32(len(o.{{$fieldInfo.Name}})))
for i := range o.{{$fieldInfo.Name}} {
{{if ne $fieldInfo.Convert ""}}
xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}[i]))
{{else if $fieldInfo.IsBasic}}
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i])
{{else}}
_, err := o.{{$fieldInfo.Name}}[i].EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
{{end}}
}
{{end}}
m.Marshal{{.Encoder}}(o.{{.Name}})
{{else}}
if err := o.{{.Name}}.MarshalXDRInto(m); err != nil {
return err
}
{{end}}
{{end}}
{{define "marshalSlice"}}
{{if ge .Max 1}}
if l := len(o.{{.Name}}); l > {{.Max}} {
return xdr.ElementSizeExceeded("{{.Name}}", l, {{.Max}})
}
{{end}}
m.MarshalUint32(uint32(len(o.{{.Name}})))
for i := range o.{{.Name}} {
{{if ne .Convert ""}}
m.Marshal{{.Encoder}}({{.Convert}}(o.{{.Name}}[i]))
{{else if .IsBasic}}
m.Marshal{{.Encoder}}(o.{{.Name}}[i])
{{else}}
if err := o.{{.Name}}[i].MarshalXDRInto(m); err != nil {
return err
}
{{end}}
}
{{end}}
func (o *{{.Name}}) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
}
func (o *{{.Name}}) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
{{range $fi := .Fields}}
{{if $fi.IsSlice}}
{{template "unmarshalSlice" $fi}}
{{else}}
{{template "unmarshalValue" $fi}}
{{end}}
{{end}}
return u.Error
return xw.Tot(), xw.Error()
}//+n
{{define "unmarshalValue"}}
{{if ne .Convert ""}}
o.{{.Name}} = {{.FieldType}}(u.Unmarshal{{.Encoder}}())
{{else if .IsBasic}}
{{if ge .Max 1}}
o.{{.Name}} = u.Unmarshal{{.Encoder}}Max({{.Max}})
{{else}}
o.{{.Name}} = u.Unmarshal{{.Encoder}}()
{{end}}
{{else}}
(&o.{{.Name}}).UnmarshalXDRFrom(u)
{{end}}
{{end}}
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
xr := xdr.NewReader(r)
return o.DecodeXDRFrom(xr)
}//+n
{{define "unmarshalSlice"}}
_{{.Name}}Size := int(u.UnmarshalUint32())
if _{{.Name}}Size < 0 {
return xdr.ElementSizeExceeded("{{.Name}}", _{{.Name}}Size, {{.Max}})
} else if _{{.Name}}Size == 0 {
o.{{.Name}} = nil
} else {
{{if ge .Max 1}}
if _{{.Name}}Size > {{.Max}} {
return xdr.ElementSizeExceeded("{{.Name}}", _{{.Name}}Size, {{.Max}})
}
{{end}}
if _{{.Name}}Size <= len(o.{{.Name}}) {
{{if eq .FieldType "string"}}
for i := _{{.Name}}Size; i < len(o.{{.Name}}); i++ { o.{{.Name}}[i] = "" }
{{end}}
{{if eq .FieldType "[]byte"}}
for i := _{{.Name}}Size; i < len(o.{{.Name}}); i++ { o.{{.Name}}[i] = nil }
{{end}}
o.{{.Name}} = o.{{.Name}}[:_{{.Name}}Size]
} else {
o.{{.Name}} = make([]{{.FieldType}}, _{{.Name}}Size)
}
for i := range o.{{.Name}} {
{{if ne .Convert ""}}
o.{{.Name}}[i] = {{.FieldType}}(u.Unmarshal{{.Encoder}}())
{{else if .IsBasic}}
{{if ge .Submax 1}}
o.{{.Name}}[i] = u.Unmarshal{{.Encoder}}Max({{.Submax}})
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}//+n
func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error {
{{range $fieldInfo := .Fields}}
{{if not $fieldInfo.IsSlice}}
{{if ne $fieldInfo.Convert ""}}
o.{{$fieldInfo.Name}} = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
{{else if $fieldInfo.IsBasic}}
{{if ge $fieldInfo.Max 1}}
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Max}})
{{else}}
o.{{.Name}}[i] = u.Unmarshal{{.Encoder}}()
o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}()
{{end}}
{{else}}
(&o.{{.Name}}[i]).UnmarshalXDRFrom(u)
(&o.{{$fieldInfo.Name}}).DecodeXDRFrom(xr)
{{end}}
}
}
{{end}}
`
var (
encodeTpl = template.Must(template.New("encoder").Parse(encoderData))
headerTpl = template.Must(template.New("header").Parse(headerData))
)
{{else}}
_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
if _{{$fieldInfo.Name}}Size < 0 {
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
}
{{if ge $fieldInfo.Max 1}}
if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
}
{{end}}
o.{{$fieldInfo.Name}} = make([]{{$fieldInfo.FieldType}}, _{{$fieldInfo.Name}}Size)
for i := range o.{{$fieldInfo.Name}} {
{{if ne $fieldInfo.Convert ""}}
o.{{$fieldInfo.Name}}[i] = {{$fieldInfo.FieldType}}(xr.Read{{$fieldInfo.Encoder}}())
{{else if $fieldInfo.IsBasic}}
{{if ge $fieldInfo.Submax 1}}
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}Max({{$fieldInfo.Submax}})
{{else}}
o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}()
{{end}}
{{else}}
(&o.{{$fieldInfo.Name}}[i]).DecodeXDRFrom(xr)
{{end}}
}
{{end}}
{{end}}
return xr.Error()
}`))
var emptyTypeTpl = template.Must(template.New("encoder").Parse(`
func (o {{.Name}}) XDRSize() int {
return 0
}
func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
return 0, nil
}//+n
func (o {{.Name}}) MarshalXDR() ([]byte, error) {
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
return nil, nil
}//+n
func (o {{.Name}}) MustMarshalXDR() []byte {
func (o {{.TypeName}}) MustMarshalXDR() []byte {
return nil
}//+n
func (o {{.Name}}) MarshalXDRInto(m *xdr.Marshaller) error {
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
return bs, nil
}//+n
func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) {
return xw.Tot(), xw.Error()
}//+n
func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error {
return nil
}//+n
func (o *{{.Name}}) UnmarshalXDR(bs []byte) error {
func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error {
return nil
}//+n
func (o *{{.Name}}) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
return nil
}//+n
`))
func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error {
return xr.Error()
}`))
var maxRe = regexp.MustCompile(`(?:\Wmax:)(\d+)(?:\s*,\s*(\d+))?`)
@@ -342,7 +286,7 @@ func handleStruct(t *ast.StructType) []fieldInfo {
f = fieldInfo{
Name: fn,
IsBasic: true,
FieldType: "[]" + tn,
FieldType: tn,
Encoder: enc.Encoder,
Convert: enc.Type,
Max: max1,
@@ -385,14 +329,17 @@ func handleStruct(t *ast.StructType) []fieldInfo {
}
func generateCode(output io.Writer, s structInfo) {
name := s.Name
fs := s.Fields
var buf bytes.Buffer
var err error
if len(s.Fields) == 0 {
if len(fs) == 0 {
// This is an empty type. We can create a quite simple codec for it.
err = emptyTypeTpl.Execute(&buf, s)
err = emptyTypeTpl.Execute(&buf, map[string]interface{}{"TypeName": name})
} else {
// Generate with the default template.
err = encodeTpl.Execute(&buf, s)
err = encodeTpl.Execute(&buf, map[string]interface{}{"TypeName": name, "Fields": fs})
}
if err != nil {
panic(err)
@@ -400,7 +347,12 @@ func generateCode(output io.Writer, s structInfo) {
bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)
output.Write(bs)
bs, err = format.Source(bs)
if err != nil {
panic(err)
}
fmt.Fprintln(output, string(bs))
}
func uncamelize(s string) string {
@@ -432,46 +384,46 @@ func generateDiagram(output io.Writer, s structInfo) {
tn := f.FieldType
name := uncamelize(f.Name)
suffix := ""
if f.IsSlice {
fmt.Fprintf(output, "| %s |\n", center("Number of "+name, 61))
fmt.Fprintln(output, line)
suffix = " (n items)"
fmt.Fprintf(output, "/ %s /\n", center("", 61))
}
switch tn {
case "bool":
fmt.Fprintf(output, "| %s |V|\n", center(name+" (V=0 or 1)", 59))
fmt.Fprintln(output, line)
case "int16", "uint16":
fmt.Fprintf(output, "| %s | %s |\n", center("16 zero bits", 29), center(name, 29))
case "int8", "uint8":
fmt.Fprintf(output, "| %s | %s |\n", center("24 zero bits", 45), center(name, 13))
fmt.Fprintf(output, "| %s | %s |\n", center("0x0000", 29), center(name, 29))
fmt.Fprintln(output, line)
case "int32", "uint32":
fmt.Fprintf(output, "| %s |\n", center(name+suffix, 61))
fmt.Fprintf(output, "| %s |\n", center(name, 61))
fmt.Fprintln(output, line)
case "int64", "uint64":
fmt.Fprintf(output, "| %-61s |\n", "")
fmt.Fprintf(output, "+ %s +\n", center(name+" (64 bits)", 61))
fmt.Fprintf(output, "| %-61s |\n", "")
case "string", "[]byte":
fmt.Fprintln(output, line)
case "string", "byte": // XXX We assume slice of byte!
fmt.Fprintf(output, "| %s |\n", center("Length of "+name, 61))
fmt.Fprintln(output, line)
fmt.Fprintf(output, "/ %61s /\n", "")
fmt.Fprintf(output, "\\ %s \\\n", center(name+" (length + padded data)", 61))
fmt.Fprintf(output, "\\ %s \\\n", center(name+" (variable length)", 61))
fmt.Fprintf(output, "/ %61s /\n", "")
fmt.Fprintln(output, line)
default:
if f.IsSlice {
tn = "Zero or more " + tn + " Structures"
fmt.Fprintf(output, "/ %s /\n", center("", 61))
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
fmt.Fprintf(output, "/ %s /\n", center("", 61))
} else {
tn = tn + " Structure"
fmt.Fprintf(output, "/ %s /\n", center("", 61))
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
fmt.Fprintf(output, "/ %s /\n", center("", 61))
}
fmt.Fprintln(output, line)
}
if f.IsSlice {
fmt.Fprintf(output, "/ %s /\n", center("", 61))
}
fmt.Fprintln(output, line)
}
fmt.Fprintln(output)
fmt.Fprintln(output)
@@ -496,9 +448,9 @@ func generateXdr(output io.Writer, s structInfo) {
}
switch tn {
case "int8", "int16", "int32":
case "int16", "int32":
fmt.Fprintf(output, "\tint %s%s;\n", fn, suf)
case "uint8", "uint16", "uint32":
case "uint16", "uint32":
fmt.Fprintf(output, "\tunsigned int %s%s;\n", fn, suf)
case "int64":
fmt.Fprintf(output, "\thyper %s%s;\n", fn, suf)
@@ -506,7 +458,7 @@ func generateXdr(output io.Writer, s structInfo) {
fmt.Fprintf(output, "\tunsigned hyper %s%s;\n", fn, suf)
case "string":
fmt.Fprintf(output, "\tstring %s<%s>;\n", fn, l)
case "[]byte":
case "byte":
fmt.Fprintf(output, "\topaque %s<%s>;\n", fn, l)
default:
fmt.Fprintf(output, "\t%s %s%s;\n", tn, fn, suf)
@@ -558,22 +510,6 @@ func main() {
i := inspector(&structs)
ast.Inspect(f, i)
buf := new(bytes.Buffer)
headerTpl.Execute(buf, map[string]string{"Package": f.Name.Name})
for _, s := range structs {
fmt.Fprintf(buf, "\n/*\n\n")
generateDiagram(buf, s)
generateXdr(buf, s)
fmt.Fprintf(buf, "*/\n")
generateCode(buf, s)
}
bs, err := format.Source(buf.Bytes())
if err != nil {
log.Print(buf.String())
log.Fatal(err)
}
var output io.Writer = os.Stdout
if *outputFile != "" {
fd, err := os.Create(*outputFile)
@@ -582,5 +518,13 @@ func main() {
}
output = fd
}
output.Write(bs)
headerTpl.Execute(output, map[string]string{"Package": f.Name.Name})
for _, s := range structs {
fmt.Fprintf(output, "\n/*\n\n")
generateDiagram(output, s)
generateXdr(output, s)
fmt.Fprintf(output, "*/\n")
generateCode(output, s)
}
}

View File

@@ -1,59 +0,0 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
package xdr
import (
"fmt"
"reflect"
)
var padBytes = []byte{0, 0, 0}
// Pad returns the number of bytes that should be added to an item of length l
// bytes to conform to the XDR padding standard. This function is used by the
// generated marshalling code.
func Padding(l int) int {
d := l % 4
if d == 0 {
return 0
}
return 4 - d
}
// ElementSizeExceeded returns an error describing the violated size
// constraint. This function is used by the generated marshalling code.
func ElementSizeExceeded(field string, size, limit int) error {
return fmt.Errorf("%s exceeds size limit; %d > %d", field, size, limit)
}
type XDRSizer interface {
XDRSize() int
}
// SizeOfSlice returns the XDR encoded size of the given []T. Supported types
// for T are string, []byte and types implementing XDRSizer. SizeOfSlice
// panics if the parameter is not a slice or if T is not one of the supported
// types. This function is used by the generated marshalling code.
func SizeOfSlice(ss interface{}) int {
l := 0
switch ss := ss.(type) {
case []string:
for _, s := range ss {
l += 4 + len(s) + Padding(len(s))
}
case [][]byte:
for _, s := range ss {
l += 4 + len(s) + Padding(len(s))
}
default:
v := reflect.ValueOf(ss)
for i := 0; i < v.Len(); i++ {
l += v.Index(i).Interface().(XDRSizer).XDRSize()
}
}
return l
}

16
Godeps/_workspace/src/github.com/calmh/xdr/debug.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
package xdr
import (
"log"
"os"
)
var (
debug = len(os.Getenv("XDRTRACE")) > 0
dl = log.New(os.Stdout, "xdr: ", log.Lshortfile|log.Ltime|log.Lmicroseconds)
)
const maxDebugBytes = 32

View File

@@ -1,5 +1,5 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
// Package xdr implements an XDR (RFC 4506) marshaller/unmarshaller.
// Package xdr implements an XDR (RFC 4506) encoder/decoder.
package xdr

View File

@@ -1,123 +0,0 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
package xdr
import "io"
// The Marshaller is a thin wrapper around a byte buffer. The buffer must be
// of sufficient size to hold the complete marshalled object, or an
// io.ErrShortBuffer error will result. The Marshal... methods don't
// individually return an error - the intention is that multiple fields are
// marshalled in rapid succession, followed by a check of the Error field on
// the Marshaller.
type Marshaller struct {
Data []byte
Error error
offset int
}
// MarshalRaw copies the raw bytes to the buffer, without a size prefix or
// padding. This is suitable for appending data already in XDR format from
// another source.
func (m *Marshaller) MarshalRaw(bs []byte) {
if m.Error != nil {
return
}
if len(m.Data) < m.offset+len(bs) {
m.Error = io.ErrShortBuffer
return
}
m.offset += copy(m.Data[m.offset:], bs)
}
// MarshalString appends the string to the buffer, with a size prefix and
// correct padding.
func (m *Marshaller) MarshalString(s string) {
if m.Error != nil {
return
}
if len(m.Data) < m.offset+4+len(s)+Padding(len(s)) {
m.Error = io.ErrShortBuffer
return
}
m.MarshalUint32(uint32(len(s)))
m.offset += copy(m.Data[m.offset:], s)
m.offset += copy(m.Data[m.offset:], padBytes[:Padding(len(s))])
}
// MarshalString appends the bytes to the buffer, with a size prefix and
// correct padding.
func (m *Marshaller) MarshalBytes(bs []byte) {
if m.Error != nil {
return
}
if len(m.Data) < m.offset+4+len(bs)+Padding(len(bs)) {
m.Error = io.ErrShortBuffer
return
}
m.MarshalUint32(uint32(len(bs)))
m.offset += copy(m.Data[m.offset:], bs)
m.offset += copy(m.Data[m.offset:], padBytes[:Padding(len(bs))])
}
// MarshalString appends the bool to the buffer, as an uint32.
func (m *Marshaller) MarshalBool(v bool) {
if v {
m.MarshalUint8(1)
} else {
m.MarshalUint8(0)
}
}
// MarshalString appends the uint8 to the buffer, as an uint32.
func (m *Marshaller) MarshalUint8(v uint8) {
m.MarshalUint32(uint32(v))
}
// MarshalString appends the uint16 to the buffer, as an uint32.
func (m *Marshaller) MarshalUint16(v uint16) {
m.MarshalUint32(uint32(v))
}
// MarshalString appends the uint32 to the buffer.
func (m *Marshaller) MarshalUint32(v uint32) {
if m.Error != nil {
return
}
if len(m.Data) < m.offset+4 {
m.Error = io.ErrShortBuffer
return
}
m.Data[m.offset+0] = byte(v >> 24)
m.Data[m.offset+1] = byte(v >> 16)
m.Data[m.offset+2] = byte(v >> 8)
m.Data[m.offset+3] = byte(v)
m.offset += 4
}
// MarshalString appends the uint64 to the buffer.
func (m *Marshaller) MarshalUint64(v uint64) {
if m.Error != nil {
return
}
if len(m.Data) < m.offset+8 {
m.Error = io.ErrShortBuffer
return
}
m.Data[m.offset+0] = byte(v >> 56)
m.Data[m.offset+1] = byte(v >> 48)
m.Data[m.offset+2] = byte(v >> 40)
m.Data[m.offset+3] = byte(v >> 32)
m.Data[m.offset+4] = byte(v >> 24)
m.Data[m.offset+5] = byte(v >> 16)
m.Data[m.offset+6] = byte(v >> 8)
m.Data[m.offset+7] = byte(v)
m.offset += 8
}

10
Godeps/_workspace/src/github.com/calmh/xdr/pad_ipdr.go generated vendored Normal file
View File

@@ -0,0 +1,10 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
// +build ipdr
package xdr
func pad(l int) int {
return 0
}

14
Godeps/_workspace/src/github.com/calmh/xdr/pad_xdr.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
// +build !ipdr
package xdr
func pad(l int) int {
d := l % 4
if d == 0 {
return 0
}
return 4 - d
}

171
Godeps/_workspace/src/github.com/calmh/xdr/reader.go generated vendored Normal file
View File

@@ -0,0 +1,171 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package xdr
import (
"fmt"
"io"
"reflect"
"unsafe"
)
type Reader struct {
r io.Reader
err error
b [8]byte
}
func NewReader(r io.Reader) *Reader {
return &Reader{
r: r,
}
}
func (r *Reader) ReadRaw(bs []byte) (int, error) {
if r.err != nil {
return 0, r.err
}
var n int
n, r.err = io.ReadFull(r.r, bs)
return n, r.err
}
func (r *Reader) ReadString() string {
return r.ReadStringMax(0)
}
func (r *Reader) ReadStringMax(max int) string {
buf := r.ReadBytesMaxInto(max, nil)
bh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
sh := reflect.StringHeader{
Data: bh.Data,
Len: bh.Len,
}
return *((*string)(unsafe.Pointer(&sh)))
}
func (r *Reader) ReadBytes() []byte {
return r.ReadBytesInto(nil)
}
func (r *Reader) ReadBytesMax(max int) []byte {
return r.ReadBytesMaxInto(max, nil)
}
func (r *Reader) ReadBytesInto(dst []byte) []byte {
return r.ReadBytesMaxInto(0, dst)
}
func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte {
if r.err != nil {
return nil
}
l := int(r.ReadUint32())
if r.err != nil {
return nil
}
if l < 0 || max > 0 && l > max {
// l may be negative on 32 bit builds
r.err = ElementSizeExceeded("bytes field", l, max)
return nil
}
if fullLen := l + pad(l); fullLen > len(dst) {
dst = make([]byte, fullLen)
} else {
dst = dst[:fullLen]
}
var n int
n, r.err = io.ReadFull(r.r, dst)
if r.err != nil {
if debug {
dl.Printf("rd bytes (%d): %v", len(dst), r.err)
}
return nil
}
if debug {
if n > maxDebugBytes {
dl.Printf("rd bytes (%d): %x...", len(dst), dst[:maxDebugBytes])
} else {
dl.Printf("rd bytes (%d): %x", len(dst), dst)
}
}
return dst[:l]
}
func (r *Reader) ReadBool() bool {
return r.ReadUint8() != 0
}
func (r *Reader) ReadUint32() uint32 {
if r.err != nil {
return 0
}
_, r.err = io.ReadFull(r.r, r.b[:4])
if r.err != nil {
if debug {
dl.Printf("rd uint32: %v", r.err)
}
return 0
}
v := uint32(r.b[3]) | uint32(r.b[2])<<8 | uint32(r.b[1])<<16 | uint32(r.b[0])<<24
if debug {
dl.Printf("rd uint32=%d (0x%08x)", v, v)
}
return v
}
func (r *Reader) ReadUint64() uint64 {
if r.err != nil {
return 0
}
_, r.err = io.ReadFull(r.r, r.b[:8])
if r.err != nil {
if debug {
dl.Printf("rd uint64: %v", r.err)
}
return 0
}
v := uint64(r.b[7]) | uint64(r.b[6])<<8 | uint64(r.b[5])<<16 | uint64(r.b[4])<<24 |
uint64(r.b[3])<<32 | uint64(r.b[2])<<40 | uint64(r.b[1])<<48 | uint64(r.b[0])<<56
if debug {
dl.Printf("rd uint64=%d (0x%016x)", v, v)
}
return v
}
type XDRError struct {
op string
err error
}
func (e XDRError) Error() string {
return "xdr " + e.op + ": " + e.err.Error()
}
func (e XDRError) IsEOF() bool {
return e.err == io.EOF
}
func (r *Reader) Error() error {
if r.err == nil {
return nil
}
return XDRError{"read", r.err}
}
func ElementSizeExceeded(field string, size, limit int) error {
return fmt.Errorf("%s exceeds size limit; %d > %d", field, size, limit)
}

View File

@@ -0,0 +1,49 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build ipdr
package xdr
import "io"
func (r *Reader) ReadUint8() uint8 {
if r.err != nil {
return 0
}
_, r.err = io.ReadFull(r.r, r.b[:1])
if r.err != nil {
if debug {
dl.Printf("rd uint8: %v", r.err)
}
return 0
}
if debug {
dl.Printf("rd uint8=%d (0x%02x)", r.b[0], r.b[0])
}
return r.b[0]
}
func (r *Reader) ReadUint16() uint16 {
if r.err != nil {
return 0
}
_, r.err = io.ReadFull(r.r, r.b[:2])
if r.err != nil {
if debug {
dl.Printf("rd uint16: %v", r.err)
}
return 0
}
v := uint16(r.b[1]) | uint16(r.b[0])<<8
if debug {
dl.Printf("rd uint16=%d (0x%04x)", v, v)
}
return v
}

View File

@@ -0,0 +1,15 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build !ipdr
package xdr
func (r *Reader) ReadUint8() uint8 {
return uint8(r.ReadUint32())
}
func (r *Reader) ReadUint16() uint16 {
return uint16(r.ReadUint32())
}

View File

@@ -1,147 +0,0 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package xdr
import (
"io"
"reflect"
"unsafe"
)
type Unmarshaller struct {
Error error
Data []byte
}
func (u *Unmarshaller) UnmarshalRaw(l int) []byte {
if u.Error != nil {
return nil
}
if len(u.Data) < l {
u.Error = io.ErrUnexpectedEOF
return nil
}
v := u.Data[:l]
u.Data = u.Data[l:]
return v
}
func (u *Unmarshaller) UnmarshalString() string {
return u.UnmarshalStringMax(0)
}
func (u *Unmarshaller) UnmarshalStringMax(max int) string {
buf := u.UnmarshalBytesMax(max)
if len(buf) == 0 || u.Error != nil {
return ""
}
var v string
p := (*reflect.StringHeader)(unsafe.Pointer(&v))
p.Data = uintptr(unsafe.Pointer(&buf[0]))
p.Len = len(buf)
return v
}
func (u *Unmarshaller) UnmarshalBytes() []byte {
return u.UnmarshalBytesMax(0)
}
func (u *Unmarshaller) UnmarshalBytesMax(max int) []byte {
if u.Error != nil {
return nil
}
if len(u.Data) < 4 {
u.Error = io.ErrUnexpectedEOF
return nil
}
l := int(u.Data[3]) | int(u.Data[2])<<8 | int(u.Data[1])<<16 | int(u.Data[0])<<24
if l == 0 {
u.Data = u.Data[4:]
return nil
}
if l < 0 || max > 0 && l > max {
// l may be negative on 32 bit builds
u.Error = ElementSizeExceeded("bytes field", l, max)
return nil
}
if len(u.Data) < l+4 {
u.Error = io.ErrUnexpectedEOF
return nil
}
v := u.Data[4 : 4+l]
u.Data = u.Data[4+l+Padding(l):]
return v
}
func (u *Unmarshaller) UnmarshalBool() bool {
return u.UnmarshalUint8() != 0
}
func (u *Unmarshaller) UnmarshalUint8() uint8 {
if u.Error != nil {
return 0
}
if len(u.Data) < 4 {
u.Error = io.ErrUnexpectedEOF
return 0
}
v := uint8(u.Data[3])
u.Data = u.Data[4:]
return v
}
func (u *Unmarshaller) UnmarshalUint16() uint16 {
if u.Error != nil {
return 0
}
if len(u.Data) < 4 {
u.Error = io.ErrUnexpectedEOF
return 0
}
v := uint16(u.Data[3]) | uint16(u.Data[2])<<8
u.Data = u.Data[4:]
return v
}
func (u *Unmarshaller) UnmarshalUint32() uint32 {
if u.Error != nil {
return 0
}
if len(u.Data) < 4 {
u.Error = io.ErrUnexpectedEOF
return 0
}
v := uint32(u.Data[3]) | uint32(u.Data[2])<<8 | uint32(u.Data[1])<<16 | uint32(u.Data[0])<<24
u.Data = u.Data[4:]
return v
}
func (u *Unmarshaller) UnmarshalUint64() uint64 {
if u.Error != nil {
return 0
}
if len(u.Data) < 8 {
u.Error = io.ErrUnexpectedEOF
return 0
}
v := uint64(u.Data[7]) | uint64(u.Data[6])<<8 | uint64(u.Data[5])<<16 | uint64(u.Data[4])<<24 |
uint64(u.Data[3])<<32 | uint64(u.Data[2])<<40 | uint64(u.Data[1])<<48 | uint64(u.Data[0])<<56
u.Data = u.Data[8:]
return v
}

146
Godeps/_workspace/src/github.com/calmh/xdr/writer.go generated vendored Normal file
View File

@@ -0,0 +1,146 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
package xdr
import (
"io"
"reflect"
"unsafe"
)
var padBytes = []byte{0, 0, 0}
type Writer struct {
w io.Writer
tot int
err error
b [8]byte
}
type AppendWriter []byte
func (w *AppendWriter) Write(bs []byte) (int, error) {
*w = append(*w, bs...)
return len(bs), nil
}
func NewWriter(w io.Writer) *Writer {
return &Writer{
w: w,
}
}
func (w *Writer) WriteRaw(bs []byte) (int, error) {
if w.err != nil {
return 0, w.err
}
var n int
n, w.err = w.w.Write(bs)
return n, w.err
}
func (w *Writer) WriteString(s string) (int, error) {
sh := *((*reflect.StringHeader)(unsafe.Pointer(&s)))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return w.WriteBytes(*(*[]byte)(unsafe.Pointer(&bh)))
}
func (w *Writer) WriteBytes(bs []byte) (int, error) {
if w.err != nil {
return 0, w.err
}
w.WriteUint32(uint32(len(bs)))
if w.err != nil {
return 0, w.err
}
if debug {
if len(bs) > maxDebugBytes {
dl.Printf("wr bytes (%d): %x...", len(bs), bs[:maxDebugBytes])
} else {
dl.Printf("wr bytes (%d): %x", len(bs), bs)
}
}
var l, n int
n, w.err = w.w.Write(bs)
l += n
if p := pad(len(bs)); w.err == nil && p > 0 {
n, w.err = w.w.Write(padBytes[:p])
l += n
}
w.tot += l
return l, w.err
}
func (w *Writer) WriteBool(v bool) (int, error) {
if v {
return w.WriteUint8(1)
} else {
return w.WriteUint8(0)
}
}
func (w *Writer) WriteUint32(v uint32) (int, error) {
if w.err != nil {
return 0, w.err
}
if debug {
dl.Printf("wr uint32=%d", v)
}
w.b[0] = byte(v >> 24)
w.b[1] = byte(v >> 16)
w.b[2] = byte(v >> 8)
w.b[3] = byte(v)
var l int
l, w.err = w.w.Write(w.b[:4])
w.tot += l
return l, w.err
}
func (w *Writer) WriteUint64(v uint64) (int, error) {
if w.err != nil {
return 0, w.err
}
if debug {
dl.Printf("wr uint64=%d", v)
}
w.b[0] = byte(v >> 56)
w.b[1] = byte(v >> 48)
w.b[2] = byte(v >> 40)
w.b[3] = byte(v >> 32)
w.b[4] = byte(v >> 24)
w.b[5] = byte(v >> 16)
w.b[6] = byte(v >> 8)
w.b[7] = byte(v)
var l int
l, w.err = w.w.Write(w.b[:8])
w.tot += l
return l, w.err
}
func (w *Writer) Tot() int {
return w.tot
}
func (w *Writer) Error() error {
if w.err == nil {
return nil
}
return XDRError{"write", w.err}
}

View File

@@ -0,0 +1,41 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
// +build ipdr
package xdr
func (w *Writer) WriteUint8(v uint8) (int, error) {
if w.err != nil {
return 0, w.err
}
if debug {
dl.Printf("wr uint8=%d", v)
}
w.b[0] = byte(v)
var l int
l, w.err = w.w.Write(w.b[:1])
w.tot += l
return l, w.err
}
func (w *Writer) WriteUint16(v uint16) (int, error) {
if w.err != nil {
return 0, w.err
}
if debug {
dl.Printf("wr uint8=%d", v)
}
w.b[0] = byte(v >> 8)
w.b[1] = byte(v)
var l int
l, w.err = w.w.Write(w.b[:2])
w.tot += l
return l, w.err
}

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
// is governed by an MIT-style license that can be found in the LICENSE file.
// +build !ipdr
package xdr
func (w *Writer) WriteUint8(v uint8) (int, error) {
return w.WriteUint32(uint32(v))
}
func (w *Writer) WriteUint16(v uint16) (int, error) {
return w.WriteUint32(uint32(v))
}

View File

@@ -1,24 +0,0 @@
Copyright 2012 Suryandaru Triandana <syndtr@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,22 +0,0 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

View File

@@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,22 +0,0 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

View File

@@ -1,156 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs defs_linux.go
// +build linux,mips64
package ipv6
const (
sysIPV6_ADDRFORM = 0x1
sysIPV6_2292PKTINFO = 0x2
sysIPV6_2292HOPOPTS = 0x3
sysIPV6_2292DSTOPTS = 0x4
sysIPV6_2292RTHDR = 0x5
sysIPV6_2292PKTOPTIONS = 0x6
sysIPV6_CHECKSUM = 0x7
sysIPV6_2292HOPLIMIT = 0x8
sysIPV6_NEXTHOP = 0x9
sysIPV6_FLOWINFO = 0xb
sysIPV6_UNICAST_HOPS = 0x10
sysIPV6_MULTICAST_IF = 0x11
sysIPV6_MULTICAST_HOPS = 0x12
sysIPV6_MULTICAST_LOOP = 0x13
sysIPV6_ADD_MEMBERSHIP = 0x14
sysIPV6_DROP_MEMBERSHIP = 0x15
sysMCAST_JOIN_GROUP = 0x2a
sysMCAST_LEAVE_GROUP = 0x2d
sysMCAST_JOIN_SOURCE_GROUP = 0x2e
sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
sysMCAST_BLOCK_SOURCE = 0x2b
sysMCAST_UNBLOCK_SOURCE = 0x2c
sysMCAST_MSFILTER = 0x30
sysIPV6_ROUTER_ALERT = 0x16
sysIPV6_MTU_DISCOVER = 0x17
sysIPV6_MTU = 0x18
sysIPV6_RECVERR = 0x19
sysIPV6_V6ONLY = 0x1a
sysIPV6_JOIN_ANYCAST = 0x1b
sysIPV6_LEAVE_ANYCAST = 0x1c
sysIPV6_FLOWLABEL_MGR = 0x20
sysIPV6_FLOWINFO_SEND = 0x21
sysIPV6_IPSEC_POLICY = 0x22
sysIPV6_XFRM_POLICY = 0x23
sysIPV6_RECVPKTINFO = 0x31
sysIPV6_PKTINFO = 0x32
sysIPV6_RECVHOPLIMIT = 0x33
sysIPV6_HOPLIMIT = 0x34
sysIPV6_RECVHOPOPTS = 0x35
sysIPV6_HOPOPTS = 0x36
sysIPV6_RTHDRDSTOPTS = 0x37
sysIPV6_RECVRTHDR = 0x38
sysIPV6_RTHDR = 0x39
sysIPV6_RECVDSTOPTS = 0x3a
sysIPV6_DSTOPTS = 0x3b
sysIPV6_RECVPATHMTU = 0x3c
sysIPV6_PATHMTU = 0x3d
sysIPV6_DONTFRAG = 0x3e
sysIPV6_RECVTCLASS = 0x42
sysIPV6_TCLASS = 0x43
sysIPV6_ADDR_PREFERENCES = 0x48
sysIPV6_PREFER_SRC_TMP = 0x1
sysIPV6_PREFER_SRC_PUBLIC = 0x2
sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
sysIPV6_PREFER_SRC_COA = 0x4
sysIPV6_PREFER_SRC_HOME = 0x400
sysIPV6_PREFER_SRC_CGA = 0x8
sysIPV6_PREFER_SRC_NONCGA = 0x800
sysIPV6_MINHOPCOUNT = 0x49
sysIPV6_ORIGDSTADDR = 0x4a
sysIPV6_RECVORIGDSTADDR = 0x4a
sysIPV6_TRANSPARENT = 0x4b
sysIPV6_UNICAST_IF = 0x4c
sysICMPV6_FILTER = 0x1
sysICMPV6_FILTER_BLOCK = 0x1
sysICMPV6_FILTER_PASS = 0x2
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
sysICMPV6_FILTER_PASSONLY = 0x4
sysSizeofKernelSockaddrStorage = 0x80
sysSizeofSockaddrInet6 = 0x1c
sysSizeofInet6Pktinfo = 0x14
sysSizeofIPv6Mtuinfo = 0x20
sysSizeofIPv6FlowlabelReq = 0x20
sysSizeofIPv6Mreq = 0x14
sysSizeofGroupReq = 0x88
sysSizeofGroupSourceReq = 0x108
sysSizeofICMPv6Filter = 0x20
)
type sysKernelSockaddrStorage struct {
Family uint16
X__data [126]int8
}
type sysSockaddrInet6 struct {
Family uint16
Port uint16
Flowinfo uint32
Addr [16]byte /* in6_addr */
Scope_id uint32
}
type sysInet6Pktinfo struct {
Addr [16]byte /* in6_addr */
Ifindex int32
}
type sysIPv6Mtuinfo struct {
Addr sysSockaddrInet6
Mtu uint32
}
type sysIPv6FlowlabelReq struct {
Dst [16]byte /* in6_addr */
Label uint32
Action uint8
Share uint8
Flags uint16
Expires uint16
Linger uint16
X__flr_pad uint32
}
type sysIPv6Mreq struct {
Multiaddr [16]byte /* in6_addr */
Ifindex int32
}
type sysGroupReq struct {
Interface uint32
Pad_cgo_0 [4]byte
Group sysKernelSockaddrStorage
}
type sysGroupSourceReq struct {
Interface uint32
Pad_cgo_0 [4]byte
Group sysKernelSockaddrStorage
Source sysKernelSockaddrStorage
}
type sysICMPv6Filter struct {
Data [8]uint32
}

View File

@@ -1,156 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs defs_linux.go
// +build linux,mips64le
package ipv6
const (
sysIPV6_ADDRFORM = 0x1
sysIPV6_2292PKTINFO = 0x2
sysIPV6_2292HOPOPTS = 0x3
sysIPV6_2292DSTOPTS = 0x4
sysIPV6_2292RTHDR = 0x5
sysIPV6_2292PKTOPTIONS = 0x6
sysIPV6_CHECKSUM = 0x7
sysIPV6_2292HOPLIMIT = 0x8
sysIPV6_NEXTHOP = 0x9
sysIPV6_FLOWINFO = 0xb
sysIPV6_UNICAST_HOPS = 0x10
sysIPV6_MULTICAST_IF = 0x11
sysIPV6_MULTICAST_HOPS = 0x12
sysIPV6_MULTICAST_LOOP = 0x13
sysIPV6_ADD_MEMBERSHIP = 0x14
sysIPV6_DROP_MEMBERSHIP = 0x15
sysMCAST_JOIN_GROUP = 0x2a
sysMCAST_LEAVE_GROUP = 0x2d
sysMCAST_JOIN_SOURCE_GROUP = 0x2e
sysMCAST_LEAVE_SOURCE_GROUP = 0x2f
sysMCAST_BLOCK_SOURCE = 0x2b
sysMCAST_UNBLOCK_SOURCE = 0x2c
sysMCAST_MSFILTER = 0x30
sysIPV6_ROUTER_ALERT = 0x16
sysIPV6_MTU_DISCOVER = 0x17
sysIPV6_MTU = 0x18
sysIPV6_RECVERR = 0x19
sysIPV6_V6ONLY = 0x1a
sysIPV6_JOIN_ANYCAST = 0x1b
sysIPV6_LEAVE_ANYCAST = 0x1c
sysIPV6_FLOWLABEL_MGR = 0x20
sysIPV6_FLOWINFO_SEND = 0x21
sysIPV6_IPSEC_POLICY = 0x22
sysIPV6_XFRM_POLICY = 0x23
sysIPV6_RECVPKTINFO = 0x31
sysIPV6_PKTINFO = 0x32
sysIPV6_RECVHOPLIMIT = 0x33
sysIPV6_HOPLIMIT = 0x34
sysIPV6_RECVHOPOPTS = 0x35
sysIPV6_HOPOPTS = 0x36
sysIPV6_RTHDRDSTOPTS = 0x37
sysIPV6_RECVRTHDR = 0x38
sysIPV6_RTHDR = 0x39
sysIPV6_RECVDSTOPTS = 0x3a
sysIPV6_DSTOPTS = 0x3b
sysIPV6_RECVPATHMTU = 0x3c
sysIPV6_PATHMTU = 0x3d
sysIPV6_DONTFRAG = 0x3e
sysIPV6_RECVTCLASS = 0x42
sysIPV6_TCLASS = 0x43
sysIPV6_ADDR_PREFERENCES = 0x48
sysIPV6_PREFER_SRC_TMP = 0x1
sysIPV6_PREFER_SRC_PUBLIC = 0x2
sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100
sysIPV6_PREFER_SRC_COA = 0x4
sysIPV6_PREFER_SRC_HOME = 0x400
sysIPV6_PREFER_SRC_CGA = 0x8
sysIPV6_PREFER_SRC_NONCGA = 0x800
sysIPV6_MINHOPCOUNT = 0x49
sysIPV6_ORIGDSTADDR = 0x4a
sysIPV6_RECVORIGDSTADDR = 0x4a
sysIPV6_TRANSPARENT = 0x4b
sysIPV6_UNICAST_IF = 0x4c
sysICMPV6_FILTER = 0x1
sysICMPV6_FILTER_BLOCK = 0x1
sysICMPV6_FILTER_PASS = 0x2
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
sysICMPV6_FILTER_PASSONLY = 0x4
sysSizeofKernelSockaddrStorage = 0x80
sysSizeofSockaddrInet6 = 0x1c
sysSizeofInet6Pktinfo = 0x14
sysSizeofIPv6Mtuinfo = 0x20
sysSizeofIPv6FlowlabelReq = 0x20
sysSizeofIPv6Mreq = 0x14
sysSizeofGroupReq = 0x88
sysSizeofGroupSourceReq = 0x108
sysSizeofICMPv6Filter = 0x20
)
type sysKernelSockaddrStorage struct {
Family uint16
X__data [126]int8
}
type sysSockaddrInet6 struct {
Family uint16
Port uint16
Flowinfo uint32
Addr [16]byte /* in6_addr */
Scope_id uint32
}
type sysInet6Pktinfo struct {
Addr [16]byte /* in6_addr */
Ifindex int32
}
type sysIPv6Mtuinfo struct {
Addr sysSockaddrInet6
Mtu uint32
}
type sysIPv6FlowlabelReq struct {
Dst [16]byte /* in6_addr */
Label uint32
Action uint8
Share uint8
Flags uint16
Expires uint16
Linger uint16
X__flr_pad uint32
}
type sysIPv6Mreq struct {
Multiaddr [16]byte /* in6_addr */
Ifindex int32
}
type sysGroupReq struct {
Interface uint32
Pad_cgo_0 [4]byte
Group sysKernelSockaddrStorage
}
type sysGroupSourceReq struct {
Interface uint32
Pad_cgo_0 [4]byte
Group sysKernelSockaddrStorage
Source sysKernelSockaddrStorage
}
type sysICMPv6Filter struct {
Data [8]uint32
}

View File

@@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,22 +0,0 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

2
NICKS
View File

@@ -41,6 +41,7 @@ KayoticSully <kayoticsully@gmail.com>
kilburn <kilburn@la3.org>
kluppy <kluppy@going2blue.com>
kozec <kozec@kozec.com>
kralo <max.schulze@online.de>
krozycki <rozycki.karol@gmail.com>
letiemble <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
LordLandon <lordlandon@gmail.com>
@@ -79,5 +80,6 @@ veeti <veeti.paananen@rojekti.fi>
Vilbrekin <vilbrekin@gmail.com>
wkennington <william@wkennington.com>
wsgcsysadmin <e.meitner@willystreet.coo>
wweich <wweich@users.noreply.github.com> <wweich@gmx.de>
Zillode <zillode@zillode.be>
zukoo <fxgsell@gmail.com>

View File

@@ -188,7 +188,18 @@ func setup() {
func test(pkg string) {
setBuildEnv()
runPrint("go", "test", "-short", "-race", "-timeout", "60s", pkg)
useRace := runtime.GOARCH == "amd64"
switch runtime.GOOS {
case "darwin", "linux", "freebsd", "windows":
default:
useRace = false
}
if useRace {
runPrint("go", "test", "-short", "-race", "-timeout", "60s", pkg)
} else {
runPrint("go", "test", "-short", "-timeout", "60s", pkg)
}
}
func bench(pkg string) {

View File

@@ -149,7 +149,16 @@ func (s *apiService) getListener(guiCfg config.GUIConfiguration) (net.Listener,
func sendJSON(w http.ResponseWriter, jsonObject interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(jsonObject)
// Marshalling might fail, in which case we should return a 500 with the
// actual error.
bs, err := json.Marshal(jsonObject)
if err != nil {
// This Marshal() can't fail though.
bs, _ = json.Marshal(map[string]string{"error": err.Error()})
http.Error(w, string(bs), http.StatusInternalServerError)
return
}
w.Write(bs)
}
func (s *apiService) Serve() {
@@ -267,8 +276,8 @@ func (s *apiService) Serve() {
defer s.fss.Stop()
s.fss.ServeBackground()
l.Infoln("API listening on", listener.Addr())
l.Infoln("GUI URL is", guiCfg.URL())
l.Infoln("GUI and API listening on", listener.Addr())
l.Infoln("Access the GUI via the following URL:", guiCfg.URL())
if s.started != nil {
// only set when run by the tests
close(s.started)

View File

@@ -48,7 +48,7 @@ var locations = map[locationEnum]string{
locKeyFile: "${config}/key.pem",
locHTTPSCertFile: "${config}/https-cert.pem",
locHTTPSKeyFile: "${config}/https-key.pem",
locDatabase: "${config}/index-v0.13.0.db",
locDatabase: "${config}/index-v0.11.0.db",
locLogFile: "${config}/syncthing.log", // -logfile on Windows
locCsrfTokens: "${config}/csrftokens.txt",
locPanicLog: "${config}/panic-${timestamp}.log",

View File

@@ -49,16 +49,15 @@ import (
)
var (
Version = "unknown-dev"
Codename = "Copper Cockroach"
BuildStamp = "0"
BuildDate time.Time
BuildHost = "unknown"
BuildUser = "unknown"
IsRelease bool
IsBeta bool
LongVersion string
allowedVersionExp = regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\.\d+)*(\+\d+-g[0-9a-f]+)?(-dirty)?$`)
Version = "unknown-dev"
Codename = "Beryllium Bedbug"
BuildStamp = "0"
BuildDate time.Time
BuildHost = "unknown"
BuildUser = "unknown"
IsRelease bool
IsBeta bool
LongVersion string
)
const (
@@ -90,8 +89,9 @@ const (
func init() {
if Version != "unknown-dev" {
// If not a generic dev build, version string should come from git describe
if !allowedVersionExp.MatchString(Version) {
l.Fatalf("Invalid version string %q;\n\tdoes not match regexp %v", Version, allowedVersionExp)
exp := regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\+\d+-g[0-9a-f]+)?(-dirty)?$`)
if !exp.MatchString(Version) {
l.Fatalf("Invalid version string %q;\n\tdoes not match regexp %v", Version, exp)
}
}
@@ -340,7 +340,7 @@ func main() {
if err != nil {
l.Fatalln("Upgrade:", err) // exits 1
}
l.Infoln("Upgraded from", options.upgradeTo)
l.Okln("Upgraded from", options.upgradeTo)
return
}
@@ -464,14 +464,14 @@ func performUpgrade(release upgrade.Release) {
if err != nil {
l.Fatalln("Upgrade:", err)
}
l.Infof("Upgraded to %q", release.Tag)
l.Okf("Upgraded to %q", release.Tag)
} else {
l.Infoln("Attempting upgrade through running Syncthing...")
err = upgradeViaRest()
if err != nil {
l.Fatalln("Upgrade:", err)
}
l.Infoln("Syncthing upgrading")
l.Okln("Syncthing upgrading")
os.Exit(exitUpgrading)
}
}
@@ -640,7 +640,6 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
dbFile := locations[locDatabase]
ldb, err := db.Open(dbFile)
if err != nil {
l.Fatalln("Cannot open database:", err, "- Is another copy of Syncthing already running?")
}
@@ -852,7 +851,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
mainService.Stop()
l.Infoln("Exiting")
l.Okln("Exiting")
if runtimeOptions.cpuProfile {
pprof.StopCPUProfile()
@@ -1171,13 +1170,12 @@ func autoUpgrade(cfg *config.Wrapper) {
// suitable time after they have gone out of fashion.
func cleanConfigDirectory() {
patterns := map[string]time.Duration{
"panic-*.log": 7 * 24 * time.Hour, // keep panic logs for a week
"audit-*.log": 7 * 24 * time.Hour, // keep audit logs for a week
"index": 14 * 24 * time.Hour, // keep old index format for two weeks
"index*.converted": 14 * 24 * time.Hour, // keep old converted indexes for two weeks
"config.xml.v*": 30 * 24 * time.Hour, // old config versions for a month
"*.idx.gz": 30 * 24 * time.Hour, // these should for sure no longer exist
"backup-of-v0.8": 30 * 24 * time.Hour, // these neither
"panic-*.log": 7 * 24 * time.Hour, // keep panic logs for a week
"audit-*.log": 7 * 24 * time.Hour, // keep audit logs for a week
"index": 14 * 24 * time.Hour, // keep old index format for two weeks
"config.xml.v*": 30 * 24 * time.Hour, // old config versions for a month
"*.idx.gz": 30 * 24 * time.Hour, // these should for sure no longer exist
"backup-of-v0.8": 30 * 24 * time.Hour, // these neither
}
for pat, dur := range patterns {

View File

@@ -175,29 +175,3 @@ func TestShortIDCheck(t *testing.T) {
t.Error("Should have gotten an error")
}
}
func TestAllowedVersions(t *testing.T) {
testcases := []struct {
ver string
allowed bool
}{
{"v0.13.0", true},
{"v0.12.11+22-gabcdef0", true},
{"v0.13.0-beta0", true},
{"v0.13.0-beta47", true},
{"v0.13.0-beta47+1-gabcdef0", true},
{"v0.13.0-beta.0", true},
{"v0.13.0-beta.47", true},
{"v0.13.0-beta.0+1-gabcdef0", true},
{"v0.13.0-beta.47+1-gabcdef0", true},
{"v0.13.0-some-weird-but-allowed-tag", true},
{"v0.13.0-not.allowed.to.do.this", false},
{"v0.13.0+not.allowed.to.do.this", false},
}
for i, c := range testcases {
if allowed := allowedVersionExp.MatchString(c.ver); allowed != c.allowed {
t.Errorf("%d: incorrect result %v != %v for %q", i, allowed, c.allowed, c.ver)
}
}
}

14
debian/control vendored
View File

@@ -1,8 +1,16 @@
Package: syncthing
Version: {{.version}}
Priority: optional
Section: net
Architecture: {{.arch}}
Depends: libc6, procps
Version: {{.version}}
Homepage: https://syncthing.net/
Maintainer: Syncthing Release Management <release@syncthing.net>
Description: Open Source Continuous File Synchronization
Syncthing does bidirectional synchronization of files between two or
more computers.
Syncthing is an application that lets you synchronize your files across
multiple devices. This means the creation, modification or deletion of files
on one machine will automatically be replicated to your other devices. We
believe your data is your data alone and you deserve to choose where it is
stored. Therefore Syncthing does not upload your data to the cloud but
exchanges your data across your machines as soon as they are online at the
same time.

View File

@@ -0,0 +1,10 @@
[Unit]
Description=Restart Syncthing after resume
After=suspend.target
[Service]
Type=oneshot
ExecStart=/usr/bin/pkill -HUP -x syncthing
[Install]
WantedBy=suspend.target

View File

@@ -437,7 +437,7 @@ code.ng-binding{
}
.well, .form-control[readonly="readonly"] { /* read-only fields*/
color: #444 !important;
color: #666 !important;
border-color: #444 !important;
background-color: #111 !important;
}

View File

@@ -39,6 +39,9 @@ ul+h5 {
text-overflow: ellipsis;
overflow: hidden;
}
.panel-title a:hover {
text-decoration: none;
}
identicon {
display: inline-block;
@@ -246,11 +249,32 @@ ul.three-columns li, ul.two-columns li {
/** Footer nav on small devices **/
@media (max-width: 1199px) {
/* Stay at the end of the page, with space reserved for the footer
usually taking up two rows. */
html {
position: relative;
min-height: 100%;
}
body {
padding-bottom: 0;
padding-bottom: 60px;
}
.navbar-fixed-bottom {
position: static;
position: absolute;
}
}
@media (max-width: 768px) {
/* Layout after the normal contents, as this is when the footer switches
to a vertical layout. */
body {
padding-bottom: 0px;
}
.navbar-fixed-bottom {
position: relative;
}
}

View File

@@ -8,11 +8,13 @@
"Add": "Добави",
"Add Device": "Добави устройство",
"Add Folder": "Добави папка",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Добави нова папка?",
"Address": "Адрес",
"Addresses": "Адреси",
"Advanced": "Допълнителни",
"Advanced Configuration": "Допълнителни настройки",
"Advanced settings": "Advanced settings",
"All Data": "Всички данни",
"Allow Anonymous Usage Reporting?": "Разреши анонимно докладване за употребата на програмата?",
"Alphabetic": "Азбучен ред",
@@ -32,6 +34,7 @@
"Connection Error": "Грешка при свързването",
"Copied from elsewhere": "Копиране от някъде другаде",
"Copied from original": "Копиран от оригинала",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Всички правата запазени © 2015 Сътрудници:",
"Danger!": "Опасност!",
"Delete": "Изтрий",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Защитава файловете от промени направени на други устройства, но промените направени на това устройство ще бъдат синхронизирани с останалите устройства.",
"Folder": "Папка",
"Folder ID": "Идентификатор на папката",
"Folder Label": "Folder Label",
"Folder Master": "Главна папка",
"Folder Path": "Път до папката",
"Folders": "Папки",
@@ -111,6 +115,7 @@
"OK": "ОК",
"Off": "Изключено",
"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": "Настройки",
"Out of Sync": "Несинхронизирано",
"Out of Sync Items": "Несинхронизирани елементи",
@@ -132,7 +137,9 @@
"Relayed via": "Препратено през",
"Relays": "Препращачи",
"Release Notes": "Бележки по обновяването",
"Remote Devices": "Remote Devices",
"Remove": "Премахни",
"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": "Сканирай повторно всички",
"Rescan Interval": "Интервал за повторно сканиране",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Ограничението на скоростта трябва да бъде положително число (0: неограничено)",
"The rescan interval must be a non-negative number of seconds.": "Интервала на сканиране трябва да бъде не отрицателно число в секунди.",
"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 is a major version upgrade.": "Това е нова основна версия.",
"Trash Can File Versioning": "Само на файловете в кошчето",
@@ -220,6 +228,7 @@
"Version": "Версия",
"Versions Path": "Път до версиите",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Версиите биват изтривани автоматично, когато са по-стари от максималната възраст или надминават броя файлове разрешени в даден интервал.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"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.": "Когато добавяш нов идентификатор на папка помни, че той се използва за свързване на папките на различни устройства. Главни/малки букви са от значение и трябва да са еднакви на всички устройства.",
"Yes": "Да",
@@ -227,5 +236,6 @@
"days": "дни",
"full documentation": "пълна документация",
"items": "елемента",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} желае да сподели папка \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} желае да сподели папка \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Afegir",
"Add Device": "Afegir dispositiu",
"Add Folder": "Afegir carpeta",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Afegir nova carpeta?",
"Address": "Adreça",
"Addresses": "Adreces",
"Advanced": "Avançat",
"Advanced Configuration": "Configuració Avançada",
"Advanced settings": "Advanced settings",
"All Data": "Totes les dades",
"Allow Anonymous Usage Reporting?": "Permetre l'enviament anònim d'informes d'ús?",
"Alphabetic": "Alfabètic",
@@ -32,6 +34,7 @@
"Connection Error": "Error de connexió",
"Copied from elsewhere": "Copiat d'un altre lloc",
"Copied from original": "Copiat de l'original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 els següents col·laboradors:",
"Danger!": "Perill!",
"Delete": "Esborrar",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Els fitxers estan protegits de canvis fets per altres dispositius, però els canvis fets en aquest dispositiu seran enviats a la resta del cluster.",
"Folder": "Carpeta",
"Folder ID": "ID de carpeta",
"Folder Label": "Folder Label",
"Folder Master": "Carpeta mestra",
"Folder Path": "Camí de carpeta",
"Folders": "Carpetes",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Desactivar",
"Oldest First": "Més antic primer",
"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": "Opcions",
"Out of Sync": "Fora de sincronia",
"Out of Sync Items": "Arxius encara no sincronitzats",
@@ -132,7 +137,9 @@
"Relayed via": "Retransmés a través",
"Relays": "Repetidors",
"Release Notes": "Notes de llançament",
"Remote Devices": "Remote Devices",
"Remove": "Esborrar",
"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": "Re-escanejar",
"Rescan All": "Re-escanejar tot",
"Rescan Interval": "Interval de re-escaneig",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "El límit de velocitat ha de ser un nombre positiu (0: sense límit)",
"The rescan interval must be a non-negative number of seconds.": "El interval de re-escaneig ha der ser un nombre positiu de segons.",
"They are retried automatically and will be synced when the error is resolved.": "Són reintentats automàticament i seran sincronitzats quan l'error estigui resolt.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "Això pot donar facilment accés a hackers per llegir i canviar qualsevol fitxer del teu ordinador.",
"This is a major version upgrade.": "Aquesta és una actualització de versió major.",
"Trash Can File Versioning": "Paperera de versionat de fitxers",
@@ -220,6 +228,7 @@
"Version": "Versió",
"Versions Path": "Carpeta de les Versions",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Les versions son automàticament eliminades si son més antigues que el màxim d'antiguitat o si excedeixen del nombre de fitxers permesos en un interval.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quan s'afegeix un nou dispositiu, recorda que aquest dispositiu tambè s'ha d'afegir a l'altre banda.",
"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.": "Quan s'afegeix una nova carpeta recorda que el ID d'aquesta s'utilitza per lligar repositoris entre els dispositius. Es distingeix entre majúscules i minúscules i ha de ser exactament iguals entre tots els dispositius.",
"Yes": "Si",
@@ -227,5 +236,6 @@
"days": "dies",
"full documentation": "documentació sencera",
"items": "Elements",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartir la carpeta \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartir la carpeta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Afegir",
"Add Device": "Afegir dispositiu",
"Add Folder": "Afegir carpeta",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Afegir nova carpeta?",
"Address": "Direcció",
"Addresses": "Direccions",
"Advanced": "Avançat",
"Advanced Configuration": "Configuració avançada",
"Advanced settings": "Advanced settings",
"All Data": "Totes les dades",
"Allow Anonymous Usage Reporting?": "Permetre informes d'ús anònim?",
"Alphabetic": "Alfabètic",
@@ -32,6 +34,7 @@
"Connection Error": "Error de connexió",
"Copied from elsewhere": "Copiat de qualsevol lloc",
"Copied from original": "Copiat de l'original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 els següents Col·laboradors:",
"Danger!": "Perill!",
"Delete": "Esborrar",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Els fitxers són protegits dels canvis fets en altres dispositius, però els canvis fets en aquest dispositiu seràn enviats a la resta del grup (cluster).",
"Folder": "Carpeta",
"Folder ID": "ID de carpeta",
"Folder Label": "Folder Label",
"Folder Master": "Carpeta principal",
"Folder Path": "Ruta de la carpeta",
"Folders": "Carpetes",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Off",
"Oldest First": "El més vell primer",
"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": "Opcions",
"Out of Sync": "Sense sincronització",
"Out of Sync Items": "Dispositius sense sincronitzar",
@@ -132,7 +137,9 @@
"Relayed via": "Transmitit via",
"Relays": "Transmissions",
"Release Notes": "Notes de la versió",
"Remote Devices": "Remote Devices",
"Remove": "Eliminar",
"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": "Tornar a buscar",
"Rescan All": "Tornar a buscar tot",
"Rescan Interval": "Interval de nova busca",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "El llímit del ritme deu ser un nombre no negatiu (0: sense llímit)",
"The rescan interval must be a non-negative number of seconds.": "L'interval de reescaneig deu ser un nombre positiu de segons.",
"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": "This Device",
"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 is a major version upgrade.": "Aquesta és una actualització important de la versió.",
"Trash Can File Versioning": "Versionat d'arxius de la paperera",
@@ -220,6 +228,7 @@
"Version": "Versió",
"Versions Path": "Ruta de les versions",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Les versions s'esborren automàticament si són més antigues que l'edat màxima o excedixen el nombre de fitxer permesos en un interval.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quant s'afig un nou dispositiu, hi ha que tindre en compte que aquest dispositiu deu ser afegit també en l'altre costat.",
"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.": "Quant s'afig una nova carpeta, hi ha que tindre en compte que l'ID de la carpeta s'utilitza per a juntar les carpetes entre dispositius. Són sensibles a les majúscules i deuen coincidir exactament entre tots els dispositius.",
"Yes": "Sí",
@@ -227,5 +236,6 @@
"days": "dies",
"full documentation": "Documentació completa",
"items": "Elements",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartit la carpeta \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartit la carpeta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Přidat",
"Add Device": "Přidat přístroj",
"Add Folder": "Přidat adresář",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Přidat nový adresář?",
"Address": "Adresa",
"Addresses": "Adresy",
"Advanced": "Pokročilé",
"Advanced Configuration": "Pokročilá nastavení",
"Advanced settings": "Advanced settings",
"All Data": "Všechna data",
"Allow Anonymous Usage Reporting?": "Povolit anonymní hlášení o používání?",
"Alphabetic": "Abecedně",
@@ -32,6 +34,7 @@
"Connection Error": "Chyba připojení",
"Copied from elsewhere": "Zkopírováno odjinud",
"Copied from original": "Zkopírováno z originálu",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 následující přispěvatelé:",
"Danger!": "Pozor!",
"Delete": "Smazat",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Soubory jsou chráněny před změnami na ostatních přístrojích, ale změny provedené z tohoto přístroje budou rozeslány na zbytek clusteru.",
"Folder": "Adresář",
"Folder ID": "ID adresáře",
"Folder Label": "Folder Label",
"Folder Master": "Master adresář",
"Folder Path": "Cesta k adresáři",
"Folders": "Adresáře",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Vypnuta",
"Oldest First": "Od nejstaršího",
"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": "Nastavení",
"Out of Sync": "Nesesynchronizováno",
"Out of Sync Items": "Nesesynchronizované položky",
@@ -132,7 +137,9 @@
"Relayed via": "Přenášené přes",
"Relays": "Přenašeče",
"Release Notes": "Poznámky k vydání",
"Remote Devices": "Remote Devices",
"Remove": "Odstranit",
"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": "Opakovat skenování",
"Rescan All": "Opakovat skenování všech",
"Rescan Interval": "Interval opakování skenování",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Limit rychlosti musí být nezáporné číslo (0: bez limitu)",
"The rescan interval must be a non-negative number of seconds.": "Interval opakování skenování musí být pozitivní číslo.",
"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": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "To může útočníkům jednoduše povolit čtení a úpravy souborů na vašem přístroji. ",
"This is a major version upgrade.": "Toto je důležitá aktualizace.",
"Trash Can File Versioning": "Verzování souborů v koši",
@@ -220,6 +228,7 @@
"Version": "Verze",
"Versions Path": "Cesta k verzím",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Verze jsou automaticky smazány, pokud jsou starší než maximální časový limit nebo překročí počet souborů povolených pro interval.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Při přidávání nového přístroje mějte na paměti, že je ho třeba také zadat na druhé straně.",
"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.": "Při přidávání nového adresáře mějte na paměti, že jeho ID je použito ke svázání adresářů napříč přístoji. Rozlišují se malá a velká písmena a musí přesně souhlasit mezi všemi přístroji.",
"Yes": "Ano",
@@ -227,5 +236,6 @@
"days": "dní",
"full documentation": "plná dokumentace",
"items": "položky",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce sdílet adresář \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce sdílet adresář \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Tilføj",
"Add Device": "Tilføj enhed",
"Add Folder": "Tilføj mappe",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Tilføj ny mappe",
"Address": "Adresse",
"Addresses": "Adresser",
"Advanced": "Avanceret",
"Advanced Configuration": "Avanceret konfiguration",
"Advanced settings": "Advanced settings",
"All Data": "Alt data",
"Allow Anonymous Usage Reporting?": "Tillad anonym brugerstatistik?",
"Alphabetic": "Alfabetisk",
@@ -32,6 +34,7 @@
"Connection Error": "Tilslutnings fejl",
"Copied from elsewhere": "Kopieret fra et andet sted",
"Copied from original": "Kopieret fra originalen",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 alle bidragsydere:",
"Danger!": "Fare!",
"Delete": "Slet",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer er beskyttet fra ændringer foretaget på andre enheder, men ændringerne på denne enhed vil blive sendt til alle andre tilknyttede enheder.",
"Folder": "Mappe",
"Folder ID": "Mappe-ID",
"Folder Label": "Folder Label",
"Folder Master": "Mastermappe",
"Folder Path": "Mappesti",
"Folders": "Mapper",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Slå fra",
"Oldest First": "Ældste først",
"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": "Indstillinger",
"Out of Sync": "Ikke synkroniseret",
"Out of Sync Items": "Endnu ikke synkroniserede filer",
@@ -132,7 +137,9 @@
"Relayed via": "Passeret gennem",
"Relays": "Passager",
"Release Notes": "Udgivelsesnoter",
"Remote Devices": "Remote Devices",
"Remove": "Fjern",
"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": "Skan igen",
"Rescan All": "Skan alt igen",
"Rescan Interval": "Genskannings interval",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Ratebegrænsningen må ikke være negative tal (0: ingen begrænsning)",
"The rescan interval must be a non-negative number of seconds.": "Genskanningsintervallet skal være et ikke-negativt antal sekunder",
"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": "This Device",
"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 is a major version upgrade.": "Dette er en ny version",
"Trash Can File Versioning": "Skraldespand fil versioner",
@@ -220,6 +228,7 @@
"Version": "Version",
"Versions Path": "Versions-sti",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versioner slettes automatisk, hvis de er ældre end den satte maksimum alder eller overstiger det tilladte antal filer i et interval.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Når der tilføjes en ny enhed, vær da opmærksom på, at denne enhed også skal tilføjes på den anden side.",
"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.": "Når der tilføjes en ny enhed, vær da opmærksom på at samme ID bruges til at forbinde mapperne på de forskellige enheder. Der er forskel på store og små bogstaver, og ID skal være fuldstændig identisk på alle enheder.",
"Yes": "Ja",
@@ -227,5 +236,6 @@
"days": "dage",
"full documentation": "Fuld dokumentation",
"items": "poster",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønsker at dele mappen \"{{folder}}\". "
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønsker at dele mappen \"{{folder}}\". ",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Hinzufügen",
"Add Device": "Gerät hinzufügen",
"Add Folder": "Verzeichnis hinzufügen",
"Add Remote Device": "Remote-Gerät hinzufügen",
"Add new folder?": "Neues Verzeichnis hinzufügen?",
"Address": "Adresse",
"Addresses": "Adressen",
"Advanced": "Erweitert",
"Advanced Configuration": "Erweiterte Konfiguration",
"Advanced settings": "Erweiterte Einstellungen",
"All Data": "Alle Daten",
"Allow Anonymous Usage Reporting?": "Übertragung von anonymen Nutzungsberichten erlauben?",
"Alphabetic": "Alphabetisch",
@@ -32,6 +34,7 @@
"Connection Error": "Verbindungsfehler",
"Copied from elsewhere": "Von anderer Quelle kopiert",
"Copied from original": "Vom Original kopiert",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 die folgenden Unterstützer:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 die folgenden Unterstützer:",
"Danger!": "Achtung!",
"Delete": "Löschen",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Dateien sind auf diesem Gerät schreibgeschützt. Auf diesem Gerät durchgeführte Veränderungen werden aber auf den Rest des Verbunds übertragen.",
"Folder": "Verzeichnis",
"Folder ID": "Verzeichnis ID",
"Folder Label": "Verzeichnisbezeichnung",
"Folder Master": "Master Verzeichnis - schreibgeschützt",
"Folder Path": "Verzeichnispfad",
"Folders": "Verzeichnisse",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Aus",
"Oldest First": "Älteste zuerst",
"Optional descriptive label for the folder. Can be different on each device.": "Optionale beschreibende Bezeichnung des Verzeichnisses. Kann auf jeden Gerät unterschiedlich sein.",
"Options": "Optionen",
"Out of Sync": "Nicht synchronisiert",
"Out of Sync Items": "Nicht synchronisierte Objekte",
@@ -132,7 +137,9 @@
"Relayed via": "Weitergeleitet über",
"Relays": "Weiterleitungen",
"Release Notes": "Veröffentlichungsnotizen",
"Remote Devices": "Remote-Geräte",
"Remove": "Entfernen",
"Required identifier for the folder. Must be the same on all cluster devices.": "Erforderliche ID für das Verzeichnis. Muss auf allen Verbunds-Geräten gleich sein.",
"Rescan": "Neu scannen",
"Rescan All": "Alle neu scannen",
"Rescan Interval": "Scanintervall",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Das Daterate-Limit muss eine nicht negative Anzahl sein (0 = kein Limit).",
"The rescan interval must be a non-negative number of seconds.": "Das Scanintervall muss eine nicht negative Anzahl (in Sekunden) sein.",
"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 is a major version upgrade.": "Dies ist eine neue Hauptversion.",
"Trash Can File Versioning": "Papierkorb Dateiversionierung",
@@ -220,6 +228,7 @@
"Version": "Version",
"Versions Path": "Versionierungspfad",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Alte Dateiversionen werden automatisch gelöscht, wenn sie älter als das angegebene Höchstalter sind oder die angegebene Höchstzahl an Dateien erreicht ist.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warnung, dieser Pfad ist ein Unterverzeichnis des existierenden Verzeichnisses \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Beachte beim Hinzufügen eines neuen Gerätes, dass dieses Gerät auch auf den anderen Geräten hinzugefügt werden muss.",
"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.": "Beachte bitte beim Hinzufügen eines neuen Verzeichnisses, dass die Verzeichnis ID dazu verwendet wird, Verzeichnisse zwischen Geräten zu verbinden. Die ID muss also auf allen Geräten gleich sein, die Groß- und Kleinschreibung muss dabei beachtet werden.",
"Yes": "Ja",
@@ -227,5 +236,6 @@
"days": "Tage",
"full documentation": "Komplette Dokumentation",
"items": "Objekte",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} möchte das Verzeichnis \"{{folder}}\" teilen."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} möchte das Verzeichnis \"{{folder}}\" teilen.",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} möchte das Verzeichnis \"{{folderLabel}}\" ({{folder}}) teilen."
}

View File

@@ -8,11 +8,13 @@
"Add": "Προσθήκη",
"Add Device": "Προσθήκη συσκευής",
"Add Folder": "Προσθήκη φακέλου",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Προσθήκη νέου φακέλου;",
"Address": "Διεύθυνση",
"Addresses": "Διευθύνσεις",
"Advanced": "Προχωρημένες",
"Advanced Configuration": "Προχωρημένες ρυθμίσεις",
"Advanced settings": "Advanced settings",
"All Data": "Όλα τα δεδομένα",
"Allow Anonymous Usage Reporting?": "Να επιτρέπεται η αποστολή ανώνυμων στοιχείων χρήσης;",
"Alphabetic": "Αλφαβητικά",
@@ -32,6 +34,7 @@
"Connection Error": "Σφάλμα σύνδεσης",
"Copied from elsewhere": "Έχει αντιγραφεί από κάπου αλλού",
"Copied from original": "Έχει αντιγραφεί από το πρωτότυπο",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 από τους παρακάτω συνεισφορείς:",
"Danger!": "Προσοχή!",
"Delete": "Διαγραφή",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Τα αρχεία προστατεύονται από αλλαγές που γίνονται σε άλλες συσκευές, αλλά όποιες αλλαγές γίνουν σε αυτή τη συσκευή θα αποσταλούν σε όλη τη συστάδα συσκευών.",
"Folder": "Φάκελος",
"Folder ID": "Ταυτότητα φακέλου",
"Folder Label": "Folder Label",
"Folder Master": "Να μην επιτρέπονται αλλαγές",
"Folder Path": "Μονοπάτι φακέλου",
"Folders": "Φάκελοι",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Απενεργοποιημένο",
"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": "Επιλογές",
"Out of Sync": "Μη συγχρονισμένα",
"Out of Sync Items": "Μη συγχρονισμένα αντικείμενα",
@@ -132,7 +137,9 @@
"Relayed via": "Αναμετάδοση μέσω",
"Relays": "Αναμεταδόσεις",
"Release Notes": "Σημείωμα έκδοσης",
"Remote Devices": "Remote Devices",
"Remove": "Αφαίρεση",
"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": "Έλεγξέ τα όλα για αλλαγές",
"Rescan Interval": "Κάθε πότε θα ελέγχεται για αλλαγές ",
@@ -181,7 +188,7 @@
"The aggregated statistics are publicly available at {%url%}.": "Τα στατιστικά που έχουν συλλεγεί είναι δημόσια διαθέσιμα στο {{url}}.",
"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 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 device ID to enter here can be found in the \"Edit > 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 χαρακτήρες (γράμματα και αριθμοί). Τα κενά και οι παύλες είναι προαιρετικά (αδιάφορα).",
@@ -194,16 +201,17 @@
"The following items could not be synchronized.": "Δεν ήταν δυνατόν να συγχρονιστούν τα παρακάτω αρχεία.",
"The maximum age must be a number and cannot be blank.": "Η μέγιστη ηλικία πρέπει να είναι αριθμός και σίγουρα όχι κενό.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Η μέγιστη ηλικία παλιότερων εκδόσεων (σε ημέρες, αν δώσεις 0 οι παλιότερες εκδόσεις θα διατηρούνται για πάντα).",
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).",
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Το ποσοστό του ελάχιστου διαθέσιμου αποθηκευτικόυ χώρου πρέπει να είναι έναν μη-αρνητικός αριθμός μεταξύ του 0 και του 100 (συμπεριλαμβανομένων)",
"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.": "Ο αριθμός ημερών που θα διατηρούντα τα αρχεία στον κάδο. Μηδέν σημαίνει διατήρηση για πάντα.",
"The number of old versions to keep, per file.": "Πόσες παλιότερες εκδόσεις θα διατηρούνται, ανά αρχείο.",
"The number of versions must be a number and cannot be blank.": "Ο αριθμός εκδόσεων πρέπει να είναι αριθμός και σίγουρα όχι κενό.",
"The path cannot be blank.": "Το μονοπάτι δεν μπορεί να είναι κενό.",
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
"The rate limit must be a non-negative number (0: no limit)": "Το όριο ταχύτητας πρέπει να είναι ένας μη-αρνητικός αριθμός (0: χωρίς όριο)",
"The rescan interval must be a non-negative number of seconds.": "Ο χρόνος επανελέγχου για αλλαγές είναι σε δευτερόλεπτα (δηλ. θετικός αριθμός).",
"They are retried automatically and will be synced when the error is resolved.": "Όταν επιλυθεί το σφάλμα θα κατεβούν και θα συχρονιστούν αυτόματα.",
"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": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "Αυτό μπορεί εύκολα να δώσει πρόσβαση ανάγνωσης και επεξεργασίας αρχείων του υπολογιστή σας σε χάκερς.",
"This is a major version upgrade.": "Αυτή είναι μιας σημαντική αναβάθμιση.",
"Trash Can File Versioning": "Ο κάδος μπορεί να τηρεί εκδόσεις",
"Unknown": "Άγνωστο",
@@ -220,6 +228,7 @@
"Version": "Έκδοση",
"Versions Path": "Φάκελος τήρησης εκδόσεων",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Οι παλιές εκδόσεις θα σβήνονται αυτόματα όταν ξεπεράσουν τη μέγιστη ηλικία ή όταν ξεπεραστεί ο μέγιστος αριθμός αρχείων ανά περίοδο.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"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.": "Όταν προσθέτεις έναν νέο φάκελο, θυμήσου πως η ταυτότητα ενός φακέλου χρησιμοποιείται για να να συσχετίσει φακέλους μεταξύ συσκευών. Η ταυτότητα του φακέλου θα πρέπει να είναι η ίδια σε όλες τις συσκευές και έχουν σημασία τα πεζά ή κεφαλαία γράμματα.",
"Yes": "Ναι",
@@ -227,5 +236,6 @@
"days": "Μέρες",
"full documentation": "πλήρης τεκμηρίωση",
"items": "εγγραφές",
"{%device%} wants to share folder \"{%folder%}\".": "Η συσκευή {{device}} θέλει να μοιράσει τον φάκελο «{{folder}}»."
"{%device%} wants to share folder \"{%folder%}\".": "Η συσκευή {{device}} θέλει να μοιράσει τον φάκελο «{{folder}}».",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Add",
"Add Device": "Add Device",
"Add Folder": "Add Folder",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Add new folder?",
"Address": "Address",
"Addresses": "Addresses",
"Advanced": "Advanced",
"Advanced Configuration": "Advanced Configuration",
"Advanced settings": "Advanced settings",
"All Data": "All Data",
"Allow Anonymous Usage Reporting?": "Allow Anonymous Usage Reporting?",
"Alphabetic": "Alphabetic",
@@ -32,6 +34,7 @@
"Connection Error": "Connection Error",
"Copied from elsewhere": "Copied from elsewhere",
"Copied from original": "Copied from original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 the following Contributors:",
"Danger!": "Danger!",
"Delete": "Delete",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.",
"Folder": "Folder",
"Folder ID": "Folder ID",
"Folder Label": "Folder Label",
"Folder Master": "Folder Master",
"Folder Path": "Folder Path",
"Folders": "Folders",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Off",
"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": "Options",
"Out of Sync": "Out of Sync",
"Out of Sync Items": "Out of Sync Items",
@@ -132,7 +137,9 @@
"Relayed via": "Relayed via",
"Relays": "Relays",
"Release Notes": "Release Notes",
"Remote Devices": "Remote Devices",
"Remove": "Remove",
"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",
"Rescan All": "Rescan All",
"Rescan Interval": "Rescan Interval",
@@ -203,6 +210,7 @@
"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.": "The rescan interval must be a non-negative number of seconds.",
"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 is a major version upgrade.": "This is a major version upgrade.",
"Trash Can File Versioning": "Rubbish Bin File Versioning",
@@ -220,6 +228,7 @@
"Version": "Version",
"Versions Path": "Versions Path",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"Yes": "Yes",
@@ -227,5 +236,6 @@
"days": "days",
"full documentation": "full documentation",
"items": "items",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Add",
"Add Device": "Add Device",
"Add Folder": "Add Folder",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Add new folder?",
"Address": "Address",
"Addresses": "Addresses",
"Advanced": "Advanced",
"Advanced Configuration": "Advanced Configuration",
"Advanced settings": "Advanced settings",
"All Data": "All Data",
"Allow Anonymous Usage Reporting?": "Allow Anonymous Usage Reporting?",
"Alphabetic": "Alphabetic",
@@ -32,6 +34,7 @@
"Connection Error": "Connection Error",
"Copied from elsewhere": "Copied from elsewhere",
"Copied from original": "Copied from original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 the following Contributors:",
"Danger!": "Danger!",
"Delete": "Delete",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.",
"Folder": "Folder",
"Folder ID": "Folder ID",
"Folder Label": "Folder Label",
"Folder Master": "Folder Master",
"Folder Path": "Folder Path",
"Folders": "Folders",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Off",
"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": "Options",
"Out of Sync": "Out of Sync",
"Out of Sync Items": "Out of Sync Items",
@@ -132,7 +137,9 @@
"Relayed via": "Relayed via",
"Relays": "Relays",
"Release Notes": "Release Notes",
"Remote Devices": "Remote Devices",
"Remove": "Remove",
"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",
"Rescan All": "Rescan All",
"Rescan Interval": "Rescan Interval",
@@ -203,6 +210,7 @@
"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.": "The rescan interval must be a non-negative number of seconds.",
"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 is a major version upgrade.": "This is a major version upgrade.",
"Trash Can File Versioning": "Trash Can File Versioning",
@@ -227,5 +235,6 @@
"days": "days",
"full documentation": "full documentation",
"items": "items",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Agregar",
"Add Device": "Agregar Dispositivo",
"Add Folder": "Agregar Carpeta",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "¿Agregar una carpeta nueva?",
"Address": "Dirección",
"Addresses": "Direcciones",
"Advanced": "Avanzado",
"Advanced Configuration": "Configuración Avanzada",
"Advanced settings": "Advanced settings",
"All Data": "Todos los datos",
"Allow Anonymous Usage Reporting?": "¿Deseas permitir el envío anónimo de informes de uso?",
"Alphabetic": "Alfabético",
@@ -32,6 +34,7 @@
"Connection Error": "Error de conexión",
"Copied from elsewhere": "Copiado de otro sitio",
"Copied from original": "Copiado del original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 los siguientes Colaboradores:",
"Danger!": "¡Peligro!",
"Delete": "Eliminar",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Los ficheros son protegidos por los cambios hechos en otros dispositivos, pero los cambios hechos en este dispositivo serán enviados al resto del grupo (cluster).",
"Folder": "Carpeta",
"Folder ID": "ID de carpeta",
"Folder Label": "Folder Label",
"Folder Master": "Carpeta principal",
"Folder Path": "Ruta de la carpeta",
"Folders": "Carpetas",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Desconectar",
"Oldest First": "El más antiguo primero",
"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": "Opciones",
"Out of Sync": "No sincronizado",
"Out of Sync Items": "Elementos no sincronizados",
@@ -132,7 +137,9 @@
"Relayed via": "Respaldada a través",
"Relays": "Respaldos",
"Release Notes": "Notas de la versión",
"Remote Devices": "Remote Devices",
"Remove": "Eliminar",
"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": "Volver a analizar",
"Rescan All": "Volver a analizar Todo",
"Rescan Interval": "Intervalo de análisis",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
"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": "This Device",
"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 is a major version upgrade.": "Hay una actualización importante.",
"Trash Can File Versioning": "Versionado de archivos de la papelera",
@@ -220,6 +228,7 @@
"Version": "Versión",
"Versions Path": "Ruta de las versiones",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Las versiones se borran automáticamente si son más antiguas que la edad máxima o exceden el número de ficheros permitidos en un intervalo.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Cuando añada un nuevo dispositivo, tenga en cuenta que este debe añadirse también en el otro lado.",
"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.": "Cuando añada una nueva carpeta, tenga en cuenta que su ID se usa para unir carpetas entre dispositivos. Son sensibles a las mayúsculas y deben coincidir exactamente entre todos los dispositivos.",
"Yes": "Si",
@@ -227,5 +236,6 @@
"days": "días",
"full documentation": "Documentación completa",
"items": "Elementos",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -1,5 +1,5 @@
{
"A device with that ID is already added.": "A device with that ID is already added.",
"A device with that ID is already added.": "Un dispositivo con esta ID ya está añadido.",
"A negative number of days doesn't make sense.": "Un número negativo no tiene sentido",
"A new major version may not be compatible with previous versions.": "Una versión mayor nueva puede ser incompatible con versiones anteriores.",
"API Key": "Clave API",
@@ -8,11 +8,13 @@
"Add": "Agregar",
"Add Device": "Agregar Dispositivo",
"Add Folder": "Agregar Repositorio",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "¿Agregar nueva carpeta?",
"Address": "Dirección",
"Addresses": "Direcciones",
"Advanced": "Avanzada",
"Advanced Configuration": "Configuración avanzada",
"Advanced settings": "Advanced settings",
"All Data": "Todos los datos",
"Allow Anonymous Usage Reporting?": "Permitir reporte anónimo de uso?",
"Alphabetic": "Alfabético",
@@ -32,8 +34,9 @@
"Connection Error": "Error de conexión",
"Copied from elsewhere": "Copiado desde otra parte.",
"Copied from original": "Copiado del original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Derechos de autor © 2015 los siguientes colaboradores:",
"Danger!": "Danger!",
"Danger!": "Peligro!",
"Delete": "Suprimir",
"Deleted": "Suprimido",
"Device ID": "ID del dispositivo",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Los archivos están protegidos frente a los cambios realizados en otros dispositivos, peros los cambios realizados en este dispositivo serán envíados al resto del grupo",
"Folder": "Carpeta",
"Folder ID": "ID del repositorio",
"Folder Label": "Folder Label",
"Folder Master": "Repositorio maestro",
"Folder Path": "Ruta del repositorio",
"Folders": "Repositorios",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Apagado",
"Oldest First": "Antiguo primero",
"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": "Opciones",
"Out of Sync": "Fuera de sincronización",
"Out of Sync Items": "Ítems no sincronizados",
@@ -132,7 +137,9 @@
"Relayed via": "retransmitida vía",
"Relays": "Retransmisores",
"Release Notes": "Notas de lanzamiento",
"Remote Devices": "Remote Devices",
"Remove": "Eliminar",
"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": "Reescanear",
"Rescan All": "Reescanear todo",
"Rescan Interval": "Intervalo de reescaneo",
@@ -155,7 +162,7 @@
"Shared With": "Compartido con",
"Short identifier for the folder. Must be the same on all cluster devices.": "Identificador corto para el repositorio. Debe ser el mismo en todos los dispositivos del grupo.",
"Show ID": "Mostrar ID",
"Show QR": "Show QR",
"Show QR": "Mostrar QR",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrado en lugar de la ID del dispositivo en el estado del grupo. Será sugerido a otros dispositivos como nombre predeterminado opcional.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrado en lugar de la ID del dispositivo en el estado del grupo. Si se deja en blanco, será usado el nombre sugerido por el dispositivo.",
"Shutdown": "Apagar",
@@ -177,7 +184,7 @@
"Syncthing is upgrading.": "Syncthing se está actualizando.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing parece estar apagado, o hay un problema con su conexión de Internet. Reintentando...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing parece estar experimentando un problema al procesar su solicitud. Por favor, recargue el navegador o reinicie Syncthing si el problema persiste.",
"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 Syncthing admin interface is configured to allow remote access without a password.": "La interfaz administrativa del Syncthing está configurada para permitir acceso remoto sin una contraseña.",
"The aggregated statistics are publicly available at {%url%}.": "Las estadísticas acumuladas están disponibles públicamente en {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuración ha sido guardada pero no activada.\nSyncthing debe reiniciarse para activar la nueva configuración.",
"The device ID cannot be blank.": "La ID del dispositivo no puede estar en blanco.",
@@ -203,6 +210,7 @@
"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.": "El intervalo de reescaneo debe ser un número no negativo de segundos.",
"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 is a major version upgrade.": "Esta es una actualización de version mayor.",
"Trash Can File Versioning": "Versiones como cubo de basura",
@@ -220,6 +228,7 @@
"Version": "Versión",
"Versions Path": "Ruta de versiones",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Las versiones se eliminan automáticamente si son mayores de la edad máxima o mayor que el número de archivos permitidos en un intervalo.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Al agregar un nuevo dispositivo, tenga en cuenta que este dispositivo se debe agregar en el otro lado también.",
"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.": "Al agregar un nuevo repositorio, tenga en cuenta que la ID del repositorio se utiliza para conectar los repositorios entre dispositivos. Se distingue entre mayúsculas y minúsculas y debe ser exactamente igual en todos los dispositivos.",
"Yes": "Sí",
@@ -227,5 +236,6 @@
"days": "días",
"full documentation": "documentación completa",
"items": "ítems",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir repositorio \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir repositorio \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Lisää",
"Add Device": "Lisää laite",
"Add Folder": "Lisää kansio",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Lisää uusi kansio?",
"Address": "Osoite",
"Addresses": "Osoitteet",
"Advanced": "Advanced",
"Advanced Configuration": "Advanced Configuration",
"Advanced settings": "Advanced settings",
"All Data": "Kaikki data",
"Allow Anonymous Usage Reporting?": "Salli anonyymi käyttöraportointi?",
"Alphabetic": "Alphabetic",
@@ -32,6 +34,7 @@
"Connection Error": "Yhteysvirhe",
"Copied from elsewhere": "Kopioitu muualta",
"Copied from original": "Kopioitu alkuperäisestä lähteestä",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Tekijänoikeus © 2015 seuraavat avustajat:",
"Danger!": "Vaara!",
"Delete": "Poista",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Tiedostot on suojattu muilla laitteilla tehdyiltä muutoksilta, mutta tällä laitteella tehdyt muutokset lähetetään muuhun ryhmään.",
"Folder": "Kansio",
"Folder ID": "Kansion ID",
"Folder Label": "Folder Label",
"Folder Master": "Hallitsijakansio",
"Folder Path": "Kansion polku",
"Folders": "Kansiot",
@@ -76,7 +80,7 @@
"Generate": "Generoi",
"Global Discovery": "Globaali etsintä",
"Global Discovery Server": "Globaali etsintäpalvelin",
"Global Discovery Servers": "Global Discovery Servers",
"Global Discovery Servers": "Globaalit etsintäpalvelimet",
"Global State": "Globaali tila",
"Help": "Apua",
"Home page": "Kotisivu",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Pois",
"Oldest First": "Vanhin ensin",
"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": "Options",
"Out of Sync": "Ei ajan tasalla",
"Out of Sync Items": "Kohteet, jotka eivät ole ajan tasalla",
@@ -132,7 +137,9 @@
"Relayed via": "Käytetty välityspalvelin",
"Relays": "Välityspalvelimet",
"Release Notes": "Julkaisutiedot",
"Remote Devices": "Remote Devices",
"Remove": "Poista",
"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": "Skannaa uudelleen",
"Rescan All": "Skannaa kaikki uudelleen",
"Rescan Interval": "Uudelleenskannauksen aikaväli",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Nopeusrajan tulee olla positiivinen luku tai nolla. (0: ei rajaa)",
"The rescan interval must be a non-negative number of seconds.": "Uudelleenskannauksen aikavälin tulee olla ei-negatiivinen numero sekunteja.",
"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.": "Tämä voi helposti sallia vihamielisille tahoille pääsyn lukea ja muokata kaikkia tiedostojasi",
"This is a major version upgrade.": "Tämä on pääversion päivitys.",
"Trash Can File Versioning": "Roskakorin tiedostoversiointi",
@@ -220,6 +228,7 @@
"Version": "Versio",
"Versions Path": "Versioiden polku",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versiot poistetaan automaattisesti mikäli ne ovat vanhempia kuin maksimi-ikä tai niiden määrä ylittää sallitun määrän tietyllä aikavälillä.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lisättäessä laitetta, muista että tämä laite tulee myös lisätä toiseen laitteeseen.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Lisättäessä uutta kansiota, muista että kansion ID:tä käytetään solmimaan kansiot yhteen laitteiden välillä. Ne ovat riippuvaisia kirjankoosta ja niiden tulee täsmätä kaikkien laitteiden välillä.",
"Yes": "Kyllä",
@@ -227,5 +236,6 @@
"days": "päivää",
"full documentation": "täysi dokumentaatio",
"items": "kohteet",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} haluaa jakaa kansion \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} haluaa jakaa kansion \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Ajouter",
"Add Device": "Ajouter un périphérique",
"Add Folder": "Ajouter un répertoire",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Ajouter un nouveau dossier ?",
"Address": "Adresse",
"Addresses": "Adresses",
"Advanced": "Avancé",
"Advanced Configuration": "Configuration avancée",
"Advanced settings": "Advanced settings",
"All Data": "Toutes les données",
"Allow Anonymous Usage Reporting?": "Autoriser le rapport anonyme de statistiques d'utilisation ?",
"Alphabetic": "Alphabétique",
@@ -32,6 +34,7 @@
"Connection Error": "Erreur de connexion",
"Copied from elsewhere": "Copié d'ailleurs",
"Copied from original": "Copié depuis l'original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 Les contributeurs suivants:",
"Danger!": "Danger!",
"Delete": "Supprimer",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Les fichiers sont protégés des changements réalisés sur les autres appareils, mais les changements réalisés sur cet appareil seront transférés aux autres appareils.",
"Folder": "Dossier",
"Folder ID": "ID du répertoire",
"Folder Label": "Folder Label",
"Folder Master": "Répertoire maître",
"Folder Path": "Chemin du répertoire",
"Folders": "Dossiers",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Éteint",
"Oldest First": "Les plus anciens en premier",
"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": "Options",
"Out of Sync": "Désynchronisé",
"Out of Sync Items": "Objets non synchronisés",
@@ -132,7 +137,9 @@
"Relayed via": "Relayée par",
"Relays": "Relais",
"Release Notes": "Notes de version",
"Remote Devices": "Remote Devices",
"Remove": "Enlever",
"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": "Rescanner",
"Rescan All": "Réanalyser tout",
"Rescan Interval": "Intervalle de scan",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "La limite de débit ne doit pas être négative (0: Aucune limite)",
"The rescan interval must be a non-negative number of seconds.": "L'intervalle d'analyse ne doit pas être un nombre négatif de secondes.",
"They are retried automatically and will be synced when the error is resolved.": "Ils seront réessayés automatiquement et synchronisés quand l'erreur sera résolue.",
"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 is a major version upgrade.": "Ceci est une mise à jour majeure.",
"Trash Can File Versioning": "Gestion des versions de fichier style poubelle.",
@@ -220,6 +228,7 @@
"Version": "Version",
"Versions Path": "Emplacement des versions",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Les versions seront supprimées automatiquement, si elles dépassent la durée maximum de conservation, ou si leur nombre est supérieur à la valeur autorisée dans l'intervalle.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lorsqu'un appareil est ajouté, gardez à l'esprit que cet appareil doit aussi être ajouté de l'autre coté.",
"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.": "Lorsqu'un nouveau répertoire est ajouté, gardez à l'esprit que son ID est utilisé pour lier les répertoires à travers les appareils. Les ID sont sensibles à la casse et doivent être identiques à travers tous les nœuds.",
"Yes": "Oui",
@@ -227,5 +236,6 @@
"days": "Jours",
"full documentation": "documentation complète",
"items": "éléments",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} veut partager le dossier \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} veut partager le dossier \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,17 +8,19 @@
"Add": "Ajouter",
"Add Device": "Ajouter une machine",
"Add Folder": "Ajouter un dossier",
"Add Remote Device": "Ajouter une machine distante",
"Add new folder?": "Ajouter un nouveau dossier ?",
"Address": "Adresse",
"Addresses": "Adresses",
"Advanced": "Avancé",
"Advanced Configuration": "Configuration avancée",
"Advanced settings": "Configuration avancée",
"All Data": "Toutes les données",
"Allow Anonymous Usage Reporting?": "Autoriser le rapport anonyme de statistiques d'utilisation ?",
"Alphabetic": "Alphabétique",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Une commande externe gère les versions de fichiers. Elle supprime les fichiers dans le dossier synchronisé.",
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
"Any devices configured on an introducer device will be added to this device as well.": "Toute machine ajoutée depuis une machine introductrice sera aussi ajoutée sur cette machine.",
"Any devices configured on an introducer device will be added to this device as well.": "Toute machine ajoutée depuis une machine initiatrice sera aussi ajoutée sur cette machine.",
"Automatic upgrades": "Mises à jour automatiques",
"Be careful!": "Faites attention !",
"Bugs": "Bugs",
@@ -32,6 +34,7 @@
"Connection Error": "Erreur de connexion",
"Copied from elsewhere": "Copié d'ailleurs",
"Copied from original": "Copié depuis l'original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016, les contributeurs suivants:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 Les contributeurs suivants:",
"Danger!": "Attention !",
"Delete": "Supprimer",
@@ -44,7 +47,7 @@
"Disconnected": "Déconnecté",
"Discovery": "Découverte",
"Documentation": "Documentation",
"Download Rate": "Débit de réception",
"Download Rate": "Vitesse de réception",
"Downloaded": "Téléchargé",
"Downloading": "En cours de téléchargement",
"Edit": "Éditer",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Les fichiers sont protégés des changements réalisés sur les autres machines, mais les changements réalisés sur celle-ci seront transférés aux autres machines.",
"Folder": "Dossier",
"Folder ID": "ID du dossier",
"Folder Label": "Étiquette du dossier",
"Folder Master": "Dossier maître",
"Folder Path": "Chemin du dossier",
"Folders": "Dossiers",
@@ -83,7 +87,7 @@
"Ignore": "Ignorer",
"Ignore Patterns": "Modèles à éviter",
"Ignore Permissions": "Ignorer les permissions",
"Incoming Rate Limit (KiB/s)": "Limite du débit entrant (KiB/s)",
"Incoming Rate Limit (KiB/s)": "Limite du débit de réception (Ko/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Une configuration incorrecte peut créer des dommages dans vos dossiers et mettre hors-service Syncthing",
"Introducer": "Initiateur",
"Inversion of the given condition (i.e. do not exclude)": "Inverser la condition donnée (i.e. ne pas exclure)",
@@ -111,10 +115,11 @@
"OK": "OK",
"Off": "Éteint",
"Oldest First": "Les plus anciens en premier",
"Optional descriptive label for the folder. Can be different on each device.": "Étiquette optionnelle pour le dossier. Peut être différente pour chaque machine.",
"Options": "Options",
"Out of Sync": "Désynchronisé",
"Out of Sync Items": "Fichiers non synchronisés",
"Outgoing Rate Limit (KiB/s)": "Limite du débit sortant (KiB/s)",
"Outgoing Rate Limit (KiB/s)": "Limite du débit d'émission (Ko/s)",
"Override Changes": "Écraser les changements",
"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": "Le chemin du dossier sur l'ordinateur local sera créé si il n'existe pas. Le caractère tilde (~) peut être utilisé comme raccourci vers",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Chemin où les versions doivent être conservées (laisser vide pour le chemin par défaut de .stversions dans le répertoire)",
@@ -132,7 +137,9 @@
"Relayed via": "Relayée par",
"Relays": "Relais",
"Release Notes": "Notes de version",
"Remote Devices": "Machines distantes",
"Remove": "Enlever",
"Required identifier for the folder. Must be the same on all cluster devices.": "Identifiant pour le dossier. Doit être le même sur l'ensemble des machines du cluster.",
"Rescan": "Réanalyse",
"Rescan All": "Réanalyser tout",
"Rescan Interval": "Intervalle d'analyse",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "La limite de débit ne doit pas être négative (0: Aucune limite)",
"The rescan interval must be a non-negative number of seconds.": "L'intervalle d'analyse ne doit pas être un nombre négatif de secondes.",
"They are retried automatically and will be synced when the error is resolved.": "Ils seront réessayés automatiquement et synchronisés quand l'erreur sera résolue.",
"This Device": "Cette machine",
"This can easily give hackers access to read and change any files on your computer.": "Cela permet facilement aux pirates de lire et modifier n'importe quel fichier de votre machine.",
"This is a major version upgrade.": "Ceci est une mise à jour majeure.",
"Trash Can File Versioning": "Gestion des versions de fichier style poubelle.",
@@ -214,12 +222,13 @@
"Upgrade": "Mettre à jour",
"Upgrade To {%version%}": "Mettre à jour vers {{version}}",
"Upgrading": "Mise à jour de Syncthing",
"Upload Rate": "Débit d'envoi",
"Upload Rate": "Vitesse d'émission",
"Uptime": "Durée de fonctionnement",
"Use HTTPS for GUI": "Utiliser l'HTTPS pour le GUI",
"Version": "Version",
"Versions Path": "Emplacement des versions",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Les versions seront supprimées automatiquement, si elles dépassent la durée maximum de conservation, ou si leur nombre est supérieur à la valeur autorisée dans l'intervalle.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Attention, ce chemin est un sous-répertoire du dossier existant \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lorsqu'une machine est ajoutée, gardez à l'esprit que cette machine doit aussi être ajoutée de l'autre coté.",
"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.": "Lorsqu'un nouveau dossier est ajouté, gardez à l'esprit que son ID est utilisé pour lier les dossiers à travers les machines. Les ID sont sensibles à la casse et doivent être identiques à travers tous les nœuds.",
"Yes": "Oui",
@@ -227,5 +236,6 @@
"days": "Jours",
"full documentation": "documentation complète",
"items": "fichiers",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} veut partager le dossier \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} veut partager le dossier \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} veut partager le dossier \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Taheakje",
"Add Device": "Apparaat taheakje",
"Add Folder": "Map taheakje",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Nije map taheakje?",
"Address": "Adres",
"Addresses": "Adressen",
"Advanced": "Avansearre",
"Advanced Configuration": "Avansearre konfiguraasje",
"Advanced settings": "Advanced settings",
"All Data": "Alle data",
"Allow Anonymous Usage Reporting?": "Anonime brûkensrapportaazje tastean?",
"Alphabetic": "Alfabetysk",
@@ -32,6 +34,7 @@
"Connection Error": "Ferbiningsflater",
"Copied from elsewhere": "Oernommen fan earne oars",
"Copied from original": "Oernommen fan orizjineel",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 de folgende bydragers:",
"Danger!": "Gefaar!",
"Delete": "Fuortsmite",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Triemen binne ymmún foar feroarings makke troch oare apparaten, mar feroarings makke op dit apparaat wurde nei de rest ferstjoerd.",
"Folder": "Map",
"Folder ID": "Map-ID",
"Folder Label": "Folder Label",
"Folder Master": "Map-master",
"Folder Path": "Map-paad",
"Folders": "Mappen",
@@ -111,6 +115,7 @@
"OK": "Okee",
"Off": "Ut",
"Oldest First": "Aldste earst",
"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": "Opsjes",
"Out of Sync": "Net syngronisearre",
"Out of Sync Items": "Net syngronisearre items",
@@ -132,7 +137,9 @@
"Relayed via": "Trochjûn fia",
"Relays": "Trochjouers",
"Release Notes": "Utjeftenotysjes",
"Remote Devices": "Remote Devices",
"Remove": "Fuortsmite",
"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": "Sken opnij",
"Rescan All": "Sken alles opnij",
"Rescan Interval": "Wersken ynterval",
@@ -169,7 +176,7 @@
"Statistics": "Statistiken",
"Stopped": "Stoppe",
"Support": "Understeuning",
"Sync Protocol Listen Addresses": "Sync-protokol harkadres",
"Sync Protocol Listen Addresses": "Sync-protokolharkadressen",
"Syncing": "Oan it Syncen",
"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:",
@@ -203,6 +210,7 @@
"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.",
"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": "This Device",
"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 is a major version upgrade.": "Dit is in wichtige ferzjefernijing.",
"Trash Can File Versioning": "Jiskefet-triemferzjebehear",
@@ -220,6 +228,7 @@
"Version": "Ferzje",
"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.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Hâld by it taheakjen fan in nij apparaat yn de holle dat it apparaat oan de oare kant ek taheakke wurde moat. ",
"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.": "Hâld by it taheakjen fan in nije map yn de holle dat de map-ID brûkt wurd om de mappen tusken apparaten mei-inoar te ferbinen. Se binne haadlettergefoelich en moatte oer alle apparaten eksakt oerienkomme.",
"Yes": "Ja",
@@ -227,5 +236,6 @@
"days": "dagen",
"full documentation": "komplete dokumintaasje",
"items": "items",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wol map \"{{folder}}\" diele."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wol map \"{{folder}}\" diele.",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Hozzáadás",
"Add Device": "Eszköz hozzáadása",
"Add Folder": "Mappa hozzáadása",
"Add Remote Device": "Távoli eszköz hozzáadása",
"Add new folder?": " Új mappa hozzáadás?",
"Address": "Cím",
"Addresses": "Címek",
"Advanced": "Haladó",
"Advanced Configuration": "Haladó beállítások",
"Advanced settings": "Haladó beállítások",
"All Data": "Minden adat",
"Allow Anonymous Usage Reporting?": "Engedélyezed a névtelen felhasználási adatok küldését?",
"Alphabetic": "ABC rendben",
@@ -32,6 +34,7 @@
"Connection Error": "Kapcsolódási hiba",
"Copied from elsewhere": "Másolva máshonnan",
"Copied from original": "Másolva az eredetiről",
"Copyright © 2014-2016 the following Contributors:": "Szerzői jog © 2014-2016 az alábbi közreműködők:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 az alábbi közreműködők:",
"Danger!": "Veszély!",
"Delete": "Törlés",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "A fájlok védve vannak a más eszközökön történt változásokkal szemben, de az ezen az eszközön történt változások érvényesek lesznek a többire.",
"Folder": "Mappa",
"Folder ID": "Mappa azonosító",
"Folder Label": "Mappa címke",
"Folder Master": "Központi mappa",
"Folder Path": "Mappa elérési útja",
"Folders": "Mappák",
@@ -111,6 +115,7 @@
"OK": "Rendben",
"Off": "Kikapcsolva",
"Oldest First": "Régebbi először",
"Optional descriptive label for the folder. Can be different on each device.": "A mappa kiegészítő leírása. Minden eszközön különböző lehet.",
"Options": "Opciók",
"Out of Sync": "Nincs szinkronban",
"Out of Sync Items": "Nem szinkronizált elemek",
@@ -132,7 +137,9 @@
"Relayed via": "Közvetítve",
"Relays": "Közvetítések",
"Release Notes": "Kiadási megjegyzések",
"Remote Devices": "Távoli eszközök",
"Remove": "Eltávolítás",
"Required identifier for the folder. Must be the same on all cluster devices.": "A mappa szükséges azonosítója. Minden fürtözött eszközön azonosnak kell lennie.",
"Rescan": "Átnézés",
"Rescan All": "Összes átnézése",
"Rescan Interval": "Átnézési intervallum",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Az arány limitnek pozitív számnak kell lennie (0: nincs limit)",
"The rescan interval must be a non-negative number of seconds.": "Az átnézési intervallum nullánál nagyobb másodperc érték kell legyen",
"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érhetnek a számítógépen található fájlokhoz. ",
"This is a major version upgrade.": "Ez egy főverzió frissítés.",
"Trash Can File Versioning": "Szemetes fájl verziókövetés",
@@ -220,6 +228,7 @@
"Version": "Verzió",
"Versions Path": "Verziók útvonala",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "A régi verziók automatikusan törlődnek, amennyiben öregebbek mint a maximum kor, vagy már több van belőlük mint az adott időszakban megtartható maximum.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Figyelem, ez az útvonal egy meglévő mappa alkönyvtára \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Amikor új eszközt adsz hozzá, tartsd észben, hogy a másik oldalon ezt az eszközt is hozzá kell adni.",
"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.": "Amikor új mappát adsz hozzá, tartsd észben, hogy a mappa azonosító arra való hogy összekösd a mappákat az eszközeiden. Az azonosító kisbetű-nagybetű érzékeny és pontosan egyeznie kell az eszközökön.",
"Yes": "Igen",
@@ -227,5 +236,6 @@
"days": "nap",
"full documentation": "teljes dokumentáció",
"items": "elem",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} meg szeretné osztani a \"{{folder}}\" nevű mappát."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} meg szeretné osztani a \"{{folder}}\" nevű mappát.",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} meg szeretné osztani \"{{folderLabel}}\" ({{folder}}) mappát."
}

View File

@@ -8,11 +8,13 @@
"Add": "Tambah",
"Add Device": "Tambah Perangkat",
"Add Folder": "Tambah Folder",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Tambah folder baru",
"Address": "Alamat",
"Addresses": "Alamat",
"Advanced": "Tingkat Lanjut",
"Advanced Configuration": "Konfigurasi Tingkat Lanjut",
"Advanced settings": "Advanced settings",
"All Data": "Semua Data",
"Allow Anonymous Usage Reporting?": "Aktifkan Laporan Penggunaan Anonim?",
"Alphabetic": "Alfabet",
@@ -32,6 +34,7 @@
"Connection Error": "Koneksi Galat",
"Copied from elsewhere": "Tersalin dari tempat lain",
"Copied from original": "Tersalin dari asal",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Hak cipta © 2015 Kontributor berikut ini:",
"Danger!": "Bahaya!",
"Delete": "Hapus",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Berkas diproteksi dari perubahan oleh perangkat lain, tetapi perubahan yang dikirim dari perangkat ini akan dikirim ke perangkat lain dalam klaster.",
"Folder": "Folder",
"Folder ID": "ID Folder",
"Folder Label": "Folder Label",
"Folder Master": "Master Folder",
"Folder Path": "Path Folder",
"Folders": "Folder",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Off",
"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": "Options",
"Out of Sync": "Out of Sync",
"Out of Sync Items": "Out of Sync Items",
@@ -132,7 +137,9 @@
"Relayed via": "Relayed via",
"Relays": "Relays",
"Release Notes": "Release Notes",
"Remote Devices": "Remote Devices",
"Remove": "Remove",
"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",
"Rescan All": "Rescan All",
"Rescan Interval": "Rescan Interval",
@@ -203,6 +210,7 @@
"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.": "The rescan interval must be a non-negative number of seconds.",
"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 is a major version upgrade.": "This is a major version upgrade.",
"Trash Can File Versioning": "Trash Can File Versioning",
@@ -220,6 +228,7 @@
"Version": "Version",
"Versions Path": "Versions Path",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "When adding a new device, keep in mind that this device must be added on the other side too.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.",
"Yes": "Yes",
@@ -227,5 +236,6 @@
"days": "days",
"full documentation": "full documentation",
"items": "items",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Aggiungi",
"Add Device": "Aggiungi Dispositivo",
"Add Folder": "Aggiungi Cartella",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Aggiungere una nuova cartella?",
"Address": "Indirizzo",
"Addresses": "Indirizzi",
"Advanced": "Avanzato",
"Advanced Configuration": "Configurazione avanzata",
"Advanced settings": "Advanced settings",
"All Data": "Tutti i Dati",
"Allow Anonymous Usage Reporting?": "Abilitare Statistiche Anonime di Utilizzo?",
"Alphabetic": "Alfabetico",
@@ -32,6 +34,7 @@
"Connection Error": "Errore di Connessione",
"Copied from elsewhere": "Copiato da qualche altra parte",
"Copied from original": "Copiato dall'originale",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 i seguenti Collaboratori:",
"Danger!": "Pericolo!",
"Delete": "Elimina",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "I file sono protetti dalle modifiche effettuate negli altri dispositivi, ma le modifiche effettuate in questo dispositivo verranno inviate anche al resto del cluster.",
"Folder": "Cartella",
"Folder ID": "ID Cartella",
"Folder Label": "Folder Label",
"Folder Master": "Cartella Principale",
"Folder Path": "Percorso Cartella",
"Folders": "Cartelle",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Disattiva",
"Oldest First": "Prima il meno recente",
"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": "Opzioni",
"Out of Sync": "Non sincronizzato",
"Out of Sync Items": "Elementi Non Sincronizzati",
@@ -132,7 +137,9 @@
"Relayed via": "Reindirizzato tramite",
"Relays": "Servers di reindirizzamento",
"Release Notes": "Note di rilascio",
"Remote Devices": "Remote Devices",
"Remove": "Rimuovi",
"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": "Riscansiona",
"Rescan All": "Riscansiona Tutto",
"Rescan Interval": "Intervallo Scansione",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Il limite di banda deve essere un numero non negativo (da 0 a infinito)",
"The rescan interval must be a non-negative number of seconds.": "L'intervallo di scansione deve essere un numero superiore a zero secondi.",
"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": "This Device",
"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 is a major version upgrade.": "Questo è un aggiornamento di versione principale",
"Trash Can File Versioning": "Controllo Versione con Cestino",
@@ -220,6 +228,7 @@
"Version": "Versione",
"Versions Path": "Percorso Cartella Versioni",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Le versioni vengono eliminate automaticamente se superano la durata massima o il numero di file permessi in un determinato intervallo temporale.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Anche nel nuovo dispositivo devi aggiungere l'ID di questo, con la stessa procedura.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Quando aggiungi una nuova cartella, ricordati che gli ID vengono utilizzati per collegare le cartelle nei dispositivi. Distinguono maiuscole e minuscole e devono corrispondere esattamente su tutti i dispositivi.",
"Yes": "Sì",
@@ -227,5 +236,6 @@
"days": "giorni",
"full documentation": "documentazione completa",
"items": "elementi",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vuole condividere la cartella \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vuole condividere la cartella \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "追加",
"Add Device": "デバイスを追加",
"Add Folder": "フォルダーを追加",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "新しいフォルダーとして追加しますか?",
"Address": "アドレス",
"Addresses": "アドレス",
"Advanced": "高度な設定",
"Advanced Configuration": "高度な設定",
"Advanced settings": "Advanced settings",
"All Data": "全てのデータ",
"Allow Anonymous Usage Reporting?": "匿名で利用状況をレポートすることを許可しますか?",
"Alphabetic": "アルファベット順",
@@ -32,6 +34,7 @@
"Connection Error": "接続エラー",
"Copied from elsewhere": "別ファイルからコピー済",
"Copied from original": "元ファイルからコピー済",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 the following Contributors:",
"Danger!": "危険",
"Delete": "削除",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "ファイルを他のデバイスによる変更から保護します。一方、このデバイスでの変更は他のデバイスに送信されます。",
"Folder": "フォルダー",
"Folder ID": "フォルダーID",
"Folder Label": "Folder Label",
"Folder Master": "フォルダーのマスター",
"Folder Path": "フォルダーパス",
"Folders": "フォルダー",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "オフ",
"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": "オプション",
"Out of Sync": "未同期",
"Out of Sync Items": "同期の必要な項目",
@@ -132,7 +137,9 @@
"Relayed via": "経由している中継サーバーのアドレス",
"Relays": "中継サーバー",
"Release Notes": "リリースノート",
"Remote Devices": "Remote Devices",
"Remove": "除去",
"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": "すべて再スキャン",
"Rescan Interval": "再スキャン間隔",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "帯域制限の値として負の数は指定できません (0を指定すると無制限になります)。",
"The rescan interval must be a non-negative number of seconds.": "再スキャン間隔として負の数は指定できません。",
"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 is a major version upgrade.": "メジャーアップグレードです。",
"Trash Can File Versioning": "ゴミ箱によるバージョン管理",
@@ -220,6 +228,7 @@
"Version": "バージョン",
"Versions Path": "古いバージョンを保存するパス",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "古いバージョンは、最大寿命もしくは期間ごとの最大保存数を超えた場合、自動的に削除されます。",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "新しいデバイスを追加する際は、相手側のデバイスにもこのデバイスを追加する必要があることに留意してください。",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "新しいフォルダーを追加する際、フォルダーIDはデバイス間でフォルダーの対応づけに使われることに注意してください。フォルダーIDは大文字と小文字が区別され、共有するすべてのデバイスの間で完全に一致しなくてはなりません。",
"Yes": "はい",
@@ -227,5 +236,6 @@
"days": "日",
"full documentation": "詳細なマニュアル",
"items": "項目",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} がフォルダー \"{{folder}}\" を共有するよう求めています。"
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} がフォルダー \"{{folder}}\" を共有するよう求めています。",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "추가",
"Add Device": "기기 추가",
"Add Folder": "폴더 추가",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "새로운 폴더를 추가하시겠습니까?",
"Address": "주소",
"Addresses": "주소",
"Advanced": "Advanced",
"Advanced Configuration": "Advanced Configuration",
"Advanced settings": "Advanced settings",
"All Data": "전체 데이터",
"Allow Anonymous Usage Reporting?": "익명 사용 보고서를 보내시겠습니까?",
"Alphabetic": "알파벳순",
@@ -32,6 +34,7 @@
"Connection Error": "연결 에러",
"Copied from elsewhere": "다른 곳에서 복사됨",
"Copied from original": "원본에서 복사됨",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 the following Contributors:",
"Danger!": "Danger!",
"Delete": "삭제",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "다른 장치가 파일을 편집할 수 없으며 반드시 이 장치의 내용을 기준으로 동기화합니다.",
"Folder": "Folder",
"Folder ID": "폴더 ID",
"Folder Label": "Folder Label",
"Folder Master": "폴더 소유자",
"Folder Path": "폴더 경로",
"Folders": "폴더",
@@ -111,6 +115,7 @@
"OK": "확인",
"Off": "꺼짐",
"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": "Options",
"Out of Sync": "Out of Sync",
"Out of Sync Items": "동기화되지 않은 항목",
@@ -132,7 +137,9 @@
"Relayed via": "Relayed via",
"Relays": "Relays",
"Release Notes": "릴리즈 노트",
"Remote Devices": "Remote Devices",
"Remove": "Remove",
"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": "전체 재탐색",
"Rescan Interval": "재탐색 간격",
@@ -203,6 +210,7 @@
"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.": "재검색 간격은 초단위이며 양수로 입력해야 합니다.",
"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 is a major version upgrade.": "이 업데이트는 메이저 버전입니다.",
"Trash Can File Versioning": "Trash Can File Versioning",
@@ -220,6 +228,7 @@
"Version": "버전",
"Versions Path": "버전 저장 경로",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "최대 보존 기간보다 오래되었거나 지정한 개수를 넘긴 버전은 자동으로 삭제됩니다.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "새 장치를 추가할 시 추가한 기기 쪽에서도 이 장치를 추가해야 합니다.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "새 폴더를 추가할 시 폴더 ID는 장치간에 폴더를 묶을 때 사용됩니다. 대소문자를 구분하며 모든 장치에서 같은 ID를 사용해야 합니다.",
"Yes": "예",
@@ -227,5 +236,6 @@
"days": "days",
"full documentation": "전체 문서",
"items": "항목",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 에서 폴더 \\\"{{folder}}\\\" 를 공유하길 원합니다."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 에서 폴더 \\\"{{folder}}\\\" 를 공유하길 원합니다.",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Pridėti",
"Add Device": "Pridėti įrenginį",
"Add Folder": "Pridėti aplanką",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Pridėti naują aplanką?",
"Address": "Adresas",
"Addresses": "Adresai",
"Advanced": "Pažangus",
"Advanced Configuration": "Pažangus nustatymai",
"Advanced Configuration": "Išplėstinė konfigūracija",
"Advanced settings": "Advanced settings",
"All Data": "Visiems duomenims",
"Allow Anonymous Usage Reporting?": "Siųsti anonimišką vartojimo ataskaitą?",
"Alphabetic": "Abėcėlės tvarka",
@@ -32,9 +34,10 @@
"Connection Error": "Susijungimo klaida",
"Copied from elsewhere": "Nukopijuota iš betkur",
"Copied from original": "Nukopijuota iš originalo",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Visos teisės saugomos © 2015 šių bendraautorių:",
"Danger!": "Pavojus!",
"Delete": "Trinti",
"Delete": "Ištrinti",
"Deleted": "Ištrinta",
"Device ID": "Įrenginio ID",
"Device Identification": "Įrenginio identifikacija",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Failai apsaugoti nuo pakeitimų atliktų kituose įrenginiuose, bet pakeitimai šiame įrenginyje bus nusiųsti kitiems.",
"Folder": "Aplankas",
"Folder ID": "Aplanko ID",
"Folder Label": "Folder Label",
"Folder Master": "Aplanko vadovas",
"Folder Path": "Kelias iki apkanko",
"Folders": "Aplankai",
@@ -111,15 +115,16 @@
"OK": "Gerai",
"Off": "Netaikoma",
"Oldest First": "Seniausi pirmiau",
"Options": "Nustatymai",
"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": "Parametrai",
"Out of Sync": "Išsisinchronizavę",
"Out of Sync Items": "Nesutikrinta",
"Outgoing Rate Limit (KiB/s)": "Išeinančio srauto maksimalus greitis (KiB/s)",
"Override Changes": "Perrašyti pakeitimus",
"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": "Kelias iki aplanko šiame kompiuteryje. Bus sukurtas, jei neegzistuoja. Tildės simbolis (~) gali būti naudojamas kaip trumpinys",
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Kelias, kur bus saugomos versijos (palikite tuščią numatytam .stversions aplankui).",
"Pause": "Sustabdyti",
"Paused": "Sustabdyta",
"Pause": "Pristabdyti",
"Paused": "Pristabdyta",
"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į.",
"Please wait": "Prašome palaukti",
@@ -132,7 +137,9 @@
"Relayed via": "Retransliuojama per",
"Relays": "Retransliatoriai",
"Release Notes": "Laidos Informacija",
"Remote Devices": "Remote Devices",
"Remove": "Pašalinti",
"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": "Nuskaityti iš naujo",
"Rescan All": "Nuskaityti visus aplankus",
"Rescan Interval": "Pertrauka tarp nuskaitymų",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Srauto maksimalus greitis privalo būti ne neigiamas skaičius (0: nėra apribojimo)",
"The rescan interval must be a non-negative number of seconds.": "Nuskaitymo dažnis negali būti neigiamas skaičius.",
"They are retried automatically and will be synced when the error is resolved.": "Failus bus automatiškai badoma parsiųsti dar kartą kai išspręsite klaidas",
"This Device": "This Device",
"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 is a major version upgrade.": "Tai yra stambus atnaujinimas.",
"Trash Can File Versioning": "Šiukšliadėžės versijų valdymas",
@@ -220,6 +228,7 @@
"Version": "Versija",
"Versions Path": "Kelias iki versijos",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versijos ištrinamos jei senesnės už nustatyta maksimalų amžių arba jei viršytas maksimalus failų skaičius per nustatytą laiko tarpą.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Pridėdami įrenginį, turėkite omeny, kad šis įrenginys taip pat turi būti pridėtas kitoje pusėje.",
"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.": "Kai įvedate naują aplanką neužmirškite, kad jis bus naudojamas visuose įrenginiuose. Svarbu visur įvesti visiškai tokį pat aplanko vardą neužmirštant apie didžiąsias ir mažąsias raides.",
"Yes": "Taip",
@@ -227,5 +236,6 @@
"days": "dienos",
"full documentation": "pilna dokumentacija",
"items": "įrašai",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} nori dalintis aplanku \"{{folder}}\""
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} nori dalintis aplanku \"{{folder}}\"",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Legg til",
"Add Device": "Legg til Enhet",
"Add Folder": "Legg til Mappe",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Legg til ny mappe?",
"Address": "Adresse",
"Addresses": "Adresser",
"Advanced": "Avansert",
"Advanced Configuration": "Avanserte Innstillinger",
"Advanced settings": "Advanced settings",
"All Data": "Alle data",
"Allow Anonymous Usage Reporting?": "Tillat Anonym Innsamling Av Brukerdata?",
"Alphabetic": "Alfabetisk",
@@ -32,6 +34,7 @@
"Connection Error": "Tilkoblingsfeil",
"Copied from elsewhere": "Kopiert fra et annet sted",
"Copied from original": "Kopiert fra original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Opphavsrett © 2015 de følgende bidragsytere:",
"Danger!": "Fare!",
"Delete": "Slett",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer er beskyttet mot endringer som er gjort på andre enheter, men endringer som er gjort på denne enheten blir sendt til resten av gruppen.",
"Folder": "Katalog",
"Folder ID": "Mappe ID",
"Folder Label": "Folder Label",
"Folder Master": "Styrende Mappe",
"Folder Path": "Mappeplassering",
"Folders": "Mapper",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Av",
"Oldest First": "Den eldste først",
"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": "Valg",
"Out of Sync": "Ikke synkronisert",
"Out of Sync Items": "Ikke Synkroniserte Element",
@@ -132,7 +137,9 @@
"Relayed via": "Relé via",
"Relays": "Reléer",
"Release Notes": "Utgivelsesnotat",
"Remote Devices": "Remote Devices",
"Remove": "Fjern",
"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": "Gjennomsøk på nytt",
"Rescan All": "Gjennomsøk alt på nytt",
"Rescan Interval": "Intervall for gjennomsøking",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Hastighetsbegrensningen kan ikke være et negativt tall (0: ingen begrensing)",
"The rescan interval must be a non-negative number of seconds.": "Antall sekund for intervallet kan ikke være negativt.",
"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": "This Device",
"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 is a major version upgrade.": "Dette er en hovedoppgradering",
"Trash Can File Versioning": "Papirkurv Versjonskontroll",
@@ -220,6 +228,7 @@
"Version": "Versjon",
"Versions Path": "Plassering Av Versjoner",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versjoner blir automatisk slettet når maksimal levetid er nådd eller når antall filer er oversteget.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Merk at når en ny enhet blir lagt til må denne også legges til på andre siden.",
"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.": "Når en ny mappe blir lagt til, husk at Mappe-ID blir brukt til å binde sammen mapper mellom enheter. Det er forskjell på store og små bokstaver, så IDene må være identiske på alle enhetene.",
"Yes": "Ja",
@@ -227,5 +236,6 @@
"days": "dager",
"full documentation": "all dokumentasjon",
"items": "elementer",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønsker å dele mappen \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønsker å dele mappen \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Toevoegen",
"Add Device": "Apparaat toevoegen",
"Add Folder": "Map toevoegen",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Voeg nieuwe map toe?",
"Address": "Adres",
"Addresses": "Adressen",
"Advanced": "Geavanceerd",
"Advanced Configuration": "Geavanceerde configuratie",
"Advanced settings": "Advanced settings",
"All Data": "Alle gegevens",
"Allow Anonymous Usage Reporting?": "Versturen van anonieme gebruikersstatistieken toestaan?",
"Alphabetic": "Alfabetisch",
@@ -32,6 +34,7 @@
"Connection Error": "Verbindingsfout",
"Copied from elsewhere": "Gekopieerd vanaf elders",
"Copied from original": "Gekopieerd van het origineel",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 de volgende Bijdragers:",
"Danger!": "Gevaar!",
"Delete": "Verwijderen",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Bestanden zijn beschermt tegen aanpassingen die gemaakt zijn door andere apparaten, maar aanpassingen op dit apparaat worden doorgestuurd naar de rest van het cluster.",
"Folder": "Map",
"Folder ID": "Map-ID",
"Folder Label": "Folder Label",
"Folder Master": "Hoofdmap",
"Folder Path": "Maplocatie",
"Folders": "Mappen",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Uit",
"Oldest First": "Oudste eerst",
"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": "Opties",
"Out of Sync": "Niet gesynchroniseerd",
"Out of Sync Items": "Niet-gesynchroniseerde items",
@@ -132,7 +137,9 @@
"Relayed via": "Doorgestuurd via",
"Relays": "Relais",
"Release Notes": "Release notes",
"Remote Devices": "Remote Devices",
"Remove": "Verwijderen",
"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": "Opnieuw scannen",
"Rescan All": "Scan alles opnieuw",
"Rescan Interval": "Scanfrequentie",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "De snelheidslimiet moet een positief nummer zijn (0: geen limiet)",
"The rescan interval must be a non-negative number of seconds.": "De scanfrequentie moet een positief getal in seconden zijn.",
"They are retried automatically and will be synced when the error is resolved.": "Het wordt automatisch opnieuw geprobeerd. Bestanden worden gesynchroniseerd als de fout is hersteld.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "Dit kan kwaadwilligen eenvoudig toegang geven tot het lezen en wijzigen van bestanden op jouw computer.",
"This is a major version upgrade.": "Dit is een grote update.",
"Trash Can File Versioning": "Versiebeheer bestanden prullenbak",
@@ -220,6 +228,7 @@
"Version": "Versie",
"Versions Path": "Bestandspad versies",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versies worden automatisch verwijderd als deze ouder zijn dan de maximale leeftijd of als ze het maximaal aantal toegestane bestanden per interval overschrijden. ",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Wanneer een nieuw toestel wordt toegevoegd, houd er dan rekening mee dat dit toestel ook aan de andere kant moet worden toegevoegd.",
"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.": "Houd er bij het toevoegen van nieuwe mappen rekening mee dat het map-ID gebruikt wordt om mappen tussen apparaten te verbinden. Dit ID is hoofdlettergevoelig en moet identiek zijn op andere apparaten.",
"Yes": "Ja",
@@ -227,5 +236,6 @@
"days": "dagen",
"full documentation": "volledige documentatie",
"items": "objecten",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wil de map \"{{folder}}\" delen."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wil de map \"{{folder}}\" delen.",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Legg til",
"Add Device": "Legg Til Eining",
"Add Folder": "Legg Til Mappe",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Leggja til ny mappe?",
"Address": "Adresse",
"Addresses": "Adresser",
"Advanced": "Avansert",
"Advanced Configuration": "Avansert konfigurasjon",
"Advanced settings": "Advanced settings",
"All Data": "Alle data",
"Allow Anonymous Usage Reporting?": "Tillata anonymisert bruksrapportering?",
"Alphabetic": "Alfabetisk",
@@ -32,6 +34,7 @@
"Connection Error": "Tilkoplingsfeil",
"Copied from elsewhere": "Kopiert frå ein annan stad",
"Copied from original": "Kopiert frå originalen",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Opphavsrett © 2015 og desse bidragsytarane:",
"Danger!": "Fare!",
"Delete": "Slett",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer er beskytta mot endringar gjort på andre einingar, men endringar gjort på denne eininga vert sende til resten av klyngja.",
"Folder": "Mappe",
"Folder ID": "Mappe ID",
"Folder Label": "Folder Label",
"Folder Master": "Styrande Mappe",
"Folder Path": "Mappeplassering",
"Folders": "Mapper",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Av",
"Oldest First": "Elste fyrst",
"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": "Val",
"Out of Sync": "Ikkje synkronisert",
"Out of Sync Items": "Ikkje-synkroniserte element",
@@ -132,7 +137,9 @@
"Relayed via": "Relé via",
"Relays": "Reléer",
"Release Notes": "Utgivingsnotat",
"Remote Devices": "Remote Devices",
"Remove": "Fjern",
"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": "Skann På Ny",
"Rescan All": "Skann alle på nytt",
"Rescan Interval": "Skanneintervall",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Hastigheitsgrensa må ver eit positivt tall (0: ingen grensa)",
"The rescan interval must be a non-negative number of seconds.": "Talet på sekund i skanneintervallet kan ikkje vera negativt.",
"They are retried automatically and will be synced when the error is resolved.": "Desse vil bli prøvd på nytt automatisk og vil bli synkronisert når feilen har blitt utbetra.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "Dette kan lett gje dataekspertar tilgang til å lese og endre vilkårlege filer på denne maskina.",
"This is a major version upgrade.": "Dette er ei hovudoppgradering",
"Trash Can File Versioning": "Papirkorg filutgåvehandtering",
@@ -220,6 +228,7 @@
"Version": "Versjon",
"Versions Path": "Utgåvebane",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Utgåver vert automatisk sletta når maksimal levetid er nådd eller når det høgaste tillate talet på filer innan eit intervall vert overskride.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Hugs at når ei ny eining vert lagt til må ho òg leggjast til på andre sida.",
"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.": "Hugs at når ei ny mappe vert lagt til, vert mappe-ID-en brukt til å binda saman mappene mellom einingane. Det er skilnad på store og små bokstavar, så ID-ane må vera identiske på alle einingane.",
"Yes": "Ja",
@@ -227,5 +236,6 @@
"days": "dagar",
"full documentation": "all dokumentasjon",
"items": "element",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønskjer å dela mappa \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønskjer å dela mappa \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -1,5 +1,5 @@
{
"A device with that ID is already added.": "A device with that ID is already added.",
"A device with that ID is already added.": "Urządzenie o tym ID jest już dodane.",
"A negative number of days doesn't make sense.": "Ujemna ilość dni nie ma sensu.",
"A new major version may not be compatible with previous versions.": "Nowa wersja może być niekompatybilna z poprzednimi wersjami.",
"API Key": "Klucz API",
@@ -8,11 +8,13 @@
"Add": "Dodaj",
"Add Device": "Dodaj urządzenie",
"Add Folder": "Dodaj folder",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Dodać nowy folder?",
"Address": "Adres",
"Addresses": "Adresy",
"Advanced": "Zaawansowane",
"Advanced Configuration": "Konfiguracja zaawansowana",
"Advanced settings": "Advanced settings",
"All Data": "Wszystkie dane",
"Allow Anonymous Usage Reporting?": "Zezwalaj na anonimowe statystyki użycia?",
"Alphabetic": "Alfabetycznie",
@@ -32,8 +34,9 @@
"Connection Error": "Błąd połączenia",
"Copied from elsewhere": "Skopiowane z innego miejsca ",
"Copied from original": "Skopiowane z oryginału",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015: ",
"Danger!": "Danger!",
"Danger!": "Niebezpieczne!",
"Delete": "Usuń",
"Deleted": "Usunięto",
"Device ID": "ID urządzenia",
@@ -51,7 +54,7 @@
"Edit Device": "Edytuj urządzenie",
"Edit Folder": "Edytuj folder",
"Editing": "Edytowanie",
"Enable Relaying": "Enable Relaying",
"Enable Relaying": "Włącz przekazywanie",
"Enable UPnP": "Włącz UPnP",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Wpisz oddzielone przecinkiem adresy (\"tcp://ip:port\", \"tcp://host:port\") lub \"dynamic\" by przeprowadzić automatyczne odnalezienie adresu.",
"Enter ignore patterns, one per line.": "Wprowadź wzorce ignorowania, jeden w każdej linii.",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Pliki są zabezpieczone przed zmianami na innym urządzeniu, jednak zmiany w tym urządzeniu będą wysłane do reszty.",
"Folder": "Folder",
"Folder ID": "ID folderu",
"Folder Label": "Folder Label",
"Folder Master": "Główny folder",
"Folder Path": "Ścieżka folderu",
"Folders": "Foldery",
@@ -76,7 +80,7 @@
"Generate": "Generuj",
"Global Discovery": "Globalne odnajdywanie",
"Global Discovery Server": "Globalny serwer rozgłoszeniowy",
"Global Discovery Servers": "Global Discovery Servers",
"Global Discovery Servers": "Globalne serwery odkrywania",
"Global State": "Status globalny",
"Help": "Pomoc",
"Home page": "Strona domowa",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Wyłącz",
"Oldest First": "Najstarsze na początku",
"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": "Opcje",
"Out of Sync": "Niezsynchronizowane",
"Out of Sync Items": "Niezsynchronizowane pliki",
@@ -121,18 +126,20 @@
"Pause": "Zatrzymaj",
"Paused": "Zatrzymany",
"Please consult the release notes before performing a major upgrade.": "Zaleca się przeanalizowanie \"release notes\" przed przeprowadzeniem znaczącej aktualizacji.",
"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 set a GUI Authentication User and Password in the Settings dialog.": "Ustaw proszę użytkownika i hasło dostępowe do GUI w Ustawieniach",
"Please wait": "Proszę czekać",
"Preview": "Podgląd",
"Preview Usage Report": "Podgląd raportu użycia.",
"Quick guide to supported patterns": "Krótki przewodnik po obsługiwanych wzorcach",
"RAM Utilization": "Użycie pamięci RAM",
"Random": "Losowo",
"Relay Servers": "Relay Servers",
"Relay Servers": "Serwery przekazywania",
"Relayed via": "Przekazane przez",
"Relays": "Przekaźniki",
"Release Notes": "Informacje o wydaniu",
"Remote Devices": "Remote Devices",
"Remove": "Usuń",
"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": "Skanuj ponownie",
"Rescan All": "Skanuj wszystko ponownie",
"Rescan Interval": "Interwał skanowania",
@@ -142,7 +149,7 @@
"Resume": "Wznów",
"Reused": "Ponownie użyte",
"Save": "Zapisz",
"Scan Time Remaining": "Scan Time Remaining",
"Scan Time Remaining": "Pozostały czas skanowania",
"Scanning": "Skanowanie",
"Select the devices to share this folder with.": "Wybierz urządzenie, któremu udostępnić folder.",
"Select the folders to share with this device.": "Wybierz foldery do współdzielenia z tym urządzeniem.",
@@ -155,7 +162,7 @@
"Shared With": "Współdzielony z",
"Short identifier for the folder. Must be the same on all cluster devices.": "Krótki identyfikator folderu. Musi być taki sam na wszystkich urządzeniach.",
"Show ID": "Pokaż ID",
"Show QR": "Show QR",
"Show QR": "Pokaż kod QR",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Pokazane w statusie zamiast ID urządzenia.Zostanie wysłane do innych urządzeń jako opcjonalna domyślna nazwa.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Pokazane w statusie zamiast ID urządzenia. Zostanie zaktualizowane do nazwy urządzenia jeżeli pozostanie puste.",
"Shutdown": "Wyłącz",
@@ -177,11 +184,11 @@
"Syncthing is upgrading.": "Aktualizowanie Syncthing",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing wydaje się być wyłączony lub jest problem z twoim połączeniem internetowym. Próbuje ponownie...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing nie może przetworzyć twojego zapytania. Proszę przeładuj stronę lub zrestartuj Syncthing, jeśli problem pozostanie.",
"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 Syncthing admin interface is configured to allow remote access without a password.": "Interfejs administracyjny Syncthing jest skonfigurowany w sposób pozwalający na zdalny dostęp bez hasła.",
"The aggregated statistics are publicly available at {%url%}.": "Zebrane statystyki są publicznie dostępne pod adresem {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfiguracja została zapisana lecz nie jest aktywna. Syncthing musi zostać zrestartowany aby aktywować nową konfiguracje.",
"The device ID cannot be blank.": "ID urządzenia nie może być puste.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "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).": "ID urządzenia może być znalezione w \"Akcja > Pokaż ID\" na innym urządzeniu. Spacje i myślniki są opcjonalne (są one ignorowane).",
"The device ID to enter here can be found in the \"Edit > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ID urządzenia można znaleźć w \"Edytuj -> Pokaż ID\" na zdalnym urządzeniu.\nOdstępy i myślniki są opcjonalne (ignorowane)",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Zaszyfrowane raporty użycia są wysyłane codziennie. Są one używane w celach statystycznych platform, rozmiarów katalogów i wersji programu. Jeżeli zgłaszane dane ulegną zmianie, ponownie wyświetli się ta informacja.",
"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.": "Wprowadzone ID urządzenia wygląda na niepoprawne. Musi zawierać 52 lub 56 znaków składających się z liter i cyfr. Odstępy i myślniki są opcjonalne.",
@@ -203,7 +210,8 @@
"The rate limit must be a non-negative number (0: no limit)": "Ograniczenie prędkości powinno być nieujemną liczbą całkowitą (0: brak ograniczeń)",
"The rescan interval must be a non-negative number of seconds.": "Interwał skanowania musi być niezerową liczbą sekund.",
"They are retried automatically and will be synced when the error is resolved.": "Ponowne próby zachodzą automatycznie, synchronizacja nastąpi po usunięciu usterki.",
"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": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "Może to umożliwić osobom trzecim dostęp do odczytu i zmian dowolnych plików na urządzeniu.",
"This is a major version upgrade.": "To jest ważna aktualizacja",
"Trash Can File Versioning": "Kontrola werjsi plików w koszu",
"Unknown": "Nieznany",
@@ -220,6 +228,7 @@
"Version": "Wersja",
"Versions Path": "Ścieżka wersji",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Wersje zostają automatycznie usunięte jeżeli są starsze niż maksymalny wiek lub przekraczają liczbę dopuszczalnych wersji.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Gdy dodajesz nowe urządzenie, pamiętaj że urządzenie musi zostać dodane także po drugiej stronie.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Przy dodawaniu nowego folderu, pamiętaj, że ID użyte jest do łączenia folderów pomiędzy urządzeniami. Wielkość liter ciągu ma znaczenie musi zgadzać się na wszystkich urządzeniach.",
"Yes": "Tak",
@@ -227,5 +236,6 @@
"days": "dni",
"full documentation": "pełna dokumentacja",
"items": "pozycji",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce udostępnić folder \"{{folder}}\""
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce udostępnić folder \"{{folder}}\"",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Adicionar",
"Add Device": "Adicionar dispositivo",
"Add Folder": "Adicionar pasta",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Adicionar nova pasta?",
"Address": "Endereço",
"Addresses": "Endereços",
"Advanced": "Avançado",
"Advanced Configuration": "Configuração avançada",
"Advanced settings": "Advanced settings",
"All Data": "Todos os dados",
"Allow Anonymous Usage Reporting?": "Permitir envio de relatórios anônimos de uso?",
"Alphabetic": "Alfabética",
@@ -32,6 +34,7 @@
"Connection Error": "Erro de conexão",
"Copied from elsewhere": "Copiado de outro lugar",
"Copied from original": "Copiado do original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015. Direitos reservados aos seguintes colaboradores:",
"Danger!": "Perigo!",
"Delete": "Apagar",
@@ -66,6 +69,7 @@
"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.",
"Folder": "Pasta",
"Folder ID": "ID da pasta",
"Folder Label": "Folder Label",
"Folder Master": "Pasta mestre",
"Folder Path": "Caminho da pasta",
"Folders": "Pastas",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Desligada",
"Oldest First": "Mais antigo primeiro",
"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ções",
"Out of Sync": "Fora de sincronia",
"Out of Sync Items": "Fora de sincronia",
@@ -132,7 +137,9 @@
"Relayed via": "Retransmitido via",
"Relays": "Retransmissores",
"Release Notes": "Notas de lançamento",
"Remote Devices": "Remote Devices",
"Remove": "Remover",
"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": "Verificar agora",
"Rescan All": "Verificar todas",
"Rescan Interval": "Intervalo entre verificações",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "O limite de velocidade deve ser um número positivo (0: sem limite)",
"The rescan interval must be a non-negative number of seconds.": "O intervalo entre verificações deve ser um número positivo de segundos.",
"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": "This Device",
"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 is a major version upgrade.": "Esta é uma atualização para uma versão \"major\".",
"Trash Can File Versioning": "Versionamento de arquivos da lixeira",
@@ -220,6 +228,7 @@
"Version": "Versão",
"Versions Path": "Caminho das versões",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "As versões são automaticamente apagadas se elas são mais antigas do que a idade máxima ou excederem o número de arquivos permitido em um intervalo.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"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",
@@ -227,5 +236,6 @@
"days": "dias",
"full documentation": "documentação completa",
"items": "itens",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer compartilhar a pasta \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer compartilhar a pasta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Adicionar",
"Add Device": "Adicionar dispositivo",
"Add Folder": "Adicionar pasta",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Adicionar nova pasta?",
"Address": "Endereço",
"Addresses": "Endereços",
"Advanced": "Avançadas",
"Advanced Configuration": "Configuração avançada",
"Advanced settings": "Advanced settings",
"All Data": "Todos os dados",
"Allow Anonymous Usage Reporting?": "Permitir envio de relatórios anónimos de utilização?",
"Alphabetic": "Alfabética",
@@ -32,6 +34,7 @@
"Connection Error": "Erro de ligação",
"Copied from elsewhere": "Copiado doutro sítio",
"Copied from original": "Copiado do original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 os seguintes contribuidores:",
"Danger!": "Perigo!",
"Delete": "Eliminar",
@@ -66,6 +69,7 @@
"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 ficheiros estão protegidos contra alterações feitas noutros dispositivos, mas alterações feitas neste dispositivo serão enviadas ao resto do grupo.",
"Folder": "Pasta",
"Folder ID": "ID da pasta",
"Folder Label": "Folder Label",
"Folder Master": "Pasta mestre",
"Folder Path": "Caminho da pasta",
"Folders": "Pastas",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Desligada",
"Oldest First": "Primeiro os mais antigos",
"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ções",
"Out of Sync": "Fora de sincronia",
"Out of Sync Items": "Itens por sincronizar",
@@ -132,7 +137,9 @@
"Relayed via": "Retransmitido via",
"Relays": "Retransmissores",
"Release Notes": "Notas de lançamento",
"Remote Devices": "Remote Devices",
"Remove": "Remover",
"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": "Verificar agora",
"Rescan All": "Verificar todas agora",
"Rescan Interval": "Intervalo entre verificações",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "O limite de velocidade tem que ser um número que não seja negativo (0: sem limite)",
"The rescan interval must be a non-negative number of seconds.": "O intervalo entre verificações tem que ser um valor não negativo de segundos.",
"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": "This Device",
"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 is a major version upgrade.": "Esta é uma actualização para uma versão importante.",
"Trash Can File Versioning": "Reciclagem",
@@ -220,6 +228,7 @@
"Version": "Versão",
"Versions Path": "Caminho das versões",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "As versões são eliminadas automaticamente se forem mais antigas do que a idade máxima ou excederem o número máximo de ficheiros permitido num intervalo.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quando adicionar um novo dispositivo, lembre-se que este dispositivo tem que 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 as pastas entre dispositivos. É sensível às diferenças entre maiúsculas e minúsculas e tem que ter uma correspondência perfeita entre todos os dispositivos.",
"Yes": "Sim",
@@ -227,5 +236,6 @@
"days": "dias",
"full documentation": "documentação completa",
"items": "itens",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer partilhar a pasta \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer partilhar a pasta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -1,231 +0,0 @@
{
"A device with that ID is already added.": "A device with that ID is already added.",
"A negative number of days doesn't make sense.": "A negative number of days doesn't make sense.",
"A new major version may not be compatible with previous versions.": "A new major version may not be compatible with previous versions.",
"API Key": "Cheie API",
"About": "Despre",
"Actions": "Actions",
"Add": "Adaugă",
"Add Device": "Adaugă Dispozitiv",
"Add Folder": "Adaugă Mapă",
"Add new folder?": "Adauga o mapă nouă?",
"Address": "Adresă",
"Addresses": "Adrese",
"Advanced": "Advanced",
"Advanced Configuration": "Configurari avansate",
"All Data": "Toate Datele",
"Allow Anonymous Usage Reporting?": "Permiteţi raportarea anonimă de folosire a aplicaţiei?",
"Alphabetic": "Alphabetic",
"An external command handles the versioning. It has to remove the file from the synced folder.": "O comandă externă administrează versiunile. Trebuie să şteargă documentul din fişierul sincronizat. ",
"Anonymous Usage Reporting": "Raport Anonim despre Folosirea Aplicației",
"Any devices configured on an introducer device will be added to this device as well.": "Toate dispozitivele configurate pe un dispozitiv iniţiator vor fi adăugate şi pe acest dispozitiv. ",
"Automatic upgrades": "Actualizare automată",
"Be careful!": "Fii atent!",
"Bugs": "Bug-uri",
"CPU Utilization": "CPU ",
"Changelog": "Noutăți",
"Clean out after": "Clean out after",
"Close": "Închide",
"Command": "Comandă",
"Comment, when used at the start of a line": "Comentariu, când este folosit la începutul unei linii",
"Compression": "Compresie",
"Connection Error": "Eroare de conexiune",
"Copied from elsewhere": "Copiat din altă parte",
"Copied from original": "Copiat din original",
"Copyright © 2015 the following Contributors:": "Copyright ©2015 Următorii Contribuitori:",
"Danger!": "Danger!",
"Delete": "Şterge",
"Deleted": "Șters",
"Device ID": "ID Dispozitiv",
"Device Identification": "Identificare Dispozitiv",
"Device Name": "Nume Dispozitiv",
"Device {%device%} ({%address%}) wants to connect. Add new device?": "Dispozitivul{{dispoztiv}}({{adresă}})vrea sa se conecteze.Adaug un dispozitiv nou?",
"Devices": "Dispozitiv",
"Disconnected": "Deconectat",
"Discovery": "Discovery",
"Documentation": "Documentaţie",
"Download Rate": "Viteză de Descărcare",
"Downloaded": "Descărcat",
"Downloading": "Se descarcă",
"Edit": "Modifică",
"Edit Device": "Modifică Dispozitiv",
"Edit Folder": "Modifică Mapa",
"Editing": "Modificare",
"Enable Relaying": "Enable Relaying",
"Enable UPnP": "Activează UPnP",
"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.",
"Error": "Eroare",
"External File Versioning": "Administrare externă a versiunilor documentului",
"Failed Items": "Failed Items",
"File Pull Order": "File Pull Order",
"File Versioning": "Versiune Fișier",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Biții de autorizare sînt excluși cînd se analizează modificările. A se utiliza pe sisteme FAT. ",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Files are moved to .stversions folder when replaced or deleted by Syncthing.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Documentele sînt mutate într-un fișier .stversions conținînd versiuni datate atunci cînd sînt șterse sau înlocuite de 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.",
"Folder": "Mapă",
"Folder ID": "ID Mapă",
"Folder Master": "Master Măpi",
"Folder Path": "Locaţie Mapei",
"Folders": "Mapă",
"GUI": "GUI",
"GUI Authentication Password": "Parolă Interfaţă",
"GUI Authentication User": "User Interfaţă",
"GUI Listen Addresses": "Adresă Interfaţă",
"Generate": "Generează",
"Global Discovery": "Găsire Globală",
"Global Discovery Server": "Server pentru Găsirea Globală",
"Global Discovery Servers": "Global Discovery Servers",
"Global State": "Status Global",
"Help": "Help",
"Home page": "Home page",
"Ignore": "Ignoră",
"Ignore Patterns": "Reguli de excludere",
"Ignore Permissions": "Ignoră Permisiuni",
"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.",
"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",
"Largest First": "Largest First",
"Last File Received": "Ultimul Fișier Primit",
"Last seen": "Ultima vizionare",
"Later": "Mai tîrziu",
"Local Discovery": "Găsire Locală",
"Local State": "Status Local",
"Local State (Total)": "Local State (Total)",
"Major Upgrade": "Major Upgrade",
"Maximum Age": "Vârsta Maximă",
"Metadata Only": "Doar Metadate",
"Minimum Free Disk Space": "Minimum Free Disk Space",
"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",
"Notice": "Mențiuni",
"OK": "OK",
"Off": "Închis",
"Oldest First": "Oldest First",
"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 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 versions should be stored (leave empty for the default .stversions folder in the folder).": "Locul unde vor fi stocate versiunile (a se lăsa neschimbat pentru fișierul .stversions din fișier). ",
"Pause": "Pauză",
"Paused": "Paused",
"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ă",
"Preview": "Previzualizează",
"Preview Usage Report": "Vezi raportul de utilizare",
"Quick guide to supported patterns": "Ghid rapid pentru regulile suportate",
"RAM Utilization": "RAM",
"Random": "Random",
"Relay Servers": "Relay Servers",
"Relayed via": "Relayed via",
"Relays": "Relays",
"Release Notes": "Release Notes",
"Remove": "Remove",
"Rescan": "Rescanează",
"Rescan All": "Rescaneaza Tot",
"Rescan Interval": "Interval Scanare",
"Restart": "Restart",
"Restart Needed": "Restart Necesar",
"Restarting": "Se restartează",
"Resume": "Resume",
"Reused": "Refolosit",
"Save": "Salvează",
"Scan Time Remaining": "Scan Time Remaining",
"Scanning": "Scanează",
"Select the devices to share this folder with.": "Selectează dispozitivele pentru care să fie disponibil această mapă.",
"Select the folders to share with this device.": "Alege mapele pe care vrei sa le imparți cu acest dispozitiv.",
"Settings": "Setări",
"Share": "Împarte",
"Share Folder": "Împarte Mapa",
"Share Folders With Device": "Împarte Mapa Cu Dispozitivul",
"Share With Devices": "Împarte Cu Dispozitivul",
"Share this folder?": "Împarte această mapă?",
"Shared With": "Împarte Cu",
"Short identifier for the folder. Must be the same on all cluster devices.": "Identificator scurt al fișierului. Trebuie să fie același pe toate mașinile. ",
"Show ID": "Arată ID",
"Show QR": "Show QR",
"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)",
"Smallest First": "Smallest First",
"Source Code": "Cod Sursă",
"Staggered File Versioning": "Versiuni eşalonate ale documentelor",
"Start Browser": "Lansează Browser",
"Statistics": "Statistici",
"Stopped": "Oprit",
"Support": "Suport Tehnic",
"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 restarting.": "Syncthing se restartează.",
"Syncthing is upgrading.": "Syncthing se actualizează.",
"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ă. ",
"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 {%url%}.": "Statisticile în ansamblu sînt accesibile public la {{url}}.",
"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 device ID to enter here can be found in the \"Edit > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ID-ul dispozitivului ce trebuie introdus aici poate fi aflat la \"Editează > Arată ID-ul\" pe celălalt dispozitiv. Spaţiile şi liniile oblice sînt opţionale (vor fi ignorate). ",
"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 first command line parameter is the folder path and the second parameter is the relative path in the folder.": "The first command line parameter is the folder path and the second parameter is the relative path in the folder.",
"The folder ID cannot be blank.": "ID-ul mapei nu poate fi gol.",
"The folder ID must be a short identifier (64 characters or less) consisting of letters, numbers and the dot (.), dash (-) and underscode (_) characters only.": "The folder ID must be a short identifier (64 characters or less) consisting of letters, numbers and the dot (.), dash (-) and underscode (_) characters only.",
"The folder ID must be unique.": "ID-ul mapei trebuie să fie unic.",
"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 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 minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).",
"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. ",
"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 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 is a major version upgrade.": "This is a major version upgrade.",
"Trash Can File Versioning": "Trash Can File Versioning",
"Unknown": "Necunoscut",
"Unshared": "Neîmpărțit",
"Unused": "Nefolosit",
"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",
"Use HTTPS for GUI": "Foloseşte HTTPS pentru interfaţă",
"Version": "Versiune",
"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. ",
"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 must keep at least one version.": "Trebuie să păstrezi cel puţin o versiune.",
"days": "Zile",
"full documentation": "toată documentaţia",
"items": "obiecte",
"{%device%} wants to share folder \"{%folder%}\".": "{{Dispozitivul}} vrea să transmită mapa {{Mapa}}"
}

View File

@@ -8,11 +8,13 @@
"Add": "Добавить",
"Add Device": "Добавить устройство",
"Add Folder": "Добавить папку",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Добавить новую папку?",
"Address": "Адрес",
"Addresses": "Адреса",
"Advanced": "Дополнительно",
"Advanced Configuration": "Дополнительные настройки",
"Advanced settings": "Advanced settings",
"All Data": "Все данные",
"Allow Anonymous Usage Reporting?": "Разрешить сбор анонимной статистики использования?",
"Alphabetic": "По алфавиту",
@@ -32,6 +34,7 @@
"Connection Error": "Ошибка подключения",
"Copied from elsewhere": "Скопировано из другого места",
"Copied from original": "Скопировано с оригинала",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Все права защищены ©, 2015 участники:",
"Danger!": "Опасно!",
"Delete": "Удалить",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Файлы защищены от изменений сделанных на других устройствах, но изменения сделанные на этом устройстве будут отправлены всему кластеру.",
"Folder": "Папка",
"Folder ID": "ID папки",
"Folder Label": "Folder Label",
"Folder Master": "Папка-оригинал",
"Folder Path": "Путь к папке",
"Folders": "Папки",
@@ -111,6 +115,7 @@
"OK": "ОК",
"Off": "Отключить",
"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": "Настройки",
"Out of Sync": "Нет синхронизации",
"Out of Sync Items": "Не синхронизированные пункты",
@@ -132,7 +137,9 @@
"Relayed via": "Релей через",
"Relays": "Релеи",
"Release Notes": "Замечания к версии",
"Remote Devices": "Remote Devices",
"Remove": "Удалить",
"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": "Пересканировать все",
"Rescan Interval": "Интервал пересканирования",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Скорость должна быть неотрицательным числом (0: нет ограничения)",
"The rescan interval must be a non-negative number of seconds.": "Интервал пересканирования должен быть неотрицательным количеством секунд.",
"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 is a major version upgrade.": "Это обновление основной версии продукта.",
"Trash Can File Versioning": "Использовать версионность для файлов в Корзине",
@@ -220,6 +228,7 @@
"Version": "Версия",
"Versions Path": "Путь к версиям",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Версии удаляются автоматически, если они существуют дольше максимального срока или превышают разрешённое количество файлов за интервал.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Когда добавляете устройство, помните о том, что это же устройство должно быть добавлено и другой стороной.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Когда добавляете новую папку, помните, что ID папок используются для того, чтобы связывать папки между всеми устройствами. Они чувствительны к регистру и должны совпадать на всех используемых устройствах.",
"Yes": "Да",
@@ -227,5 +236,6 @@
"days": "Дней",
"full documentation": "полная документация",
"items": "элементы",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хочет поделиться папкой \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хочет поделиться папкой \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -1,5 +1,5 @@
{
"A device with that ID is already added.": "A device with that ID is already added.",
"A device with that ID is already added.": "En enhet med det ID är redan tillagt.",
"A negative number of days doesn't make sense.": "Negativt antal dagar är inte troligt.",
"A new major version may not be compatible with previous versions.": "En ny huvudversion kan eventuellt vara inkompatibel med tidigare versioner.",
"API Key": "API-nyckel",
@@ -8,11 +8,13 @@
"Add": "Lägg till",
"Add Device": "Lägg till enhet",
"Add Folder": "Lägg till katalog",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Lägg till katalog?",
"Address": "Adress",
"Addresses": "Adresser",
"Advanced": "Avancerat",
"Advanced Configuration": "Avancerad konfiguration",
"Advanced settings": "Advanced settings",
"All Data": "All data",
"Allow Anonymous Usage Reporting?": "Tillåt anonym användarstatistik?",
"Alphabetic": "Alfabetisk",
@@ -32,8 +34,9 @@
"Connection Error": "Anslutningsproblem",
"Copied from elsewhere": "Kopierat utifrån",
"Copied from original": "Oförändrat",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 följande medverkande:",
"Danger!": "Danger!",
"Danger!": "Fara!",
"Delete": "Radera",
"Deleted": "Borttaget",
"Device ID": "Enhets-ID",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer skyddas från ändringar gjorda på andra enheter, men ändringar som görs på den här noden skickas till de andra klustermedlemmarna.",
"Folder": "Katalog",
"Folder ID": "Katalog-ID",
"Folder Label": "Folder Label",
"Folder Master": "Huvudlagring",
"Folder Path": "Sökväg",
"Folders": "Kataloger",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Av",
"Oldest First": "Äldst först",
"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": "Alternativ",
"Out of Sync": "Osynkad",
"Out of Sync Items": "Osynkade poster",
@@ -132,7 +137,9 @@
"Relayed via": "Vidarbefordras via",
"Relays": "Vidarbefordringar",
"Release Notes": "versionsnyheter",
"Remote Devices": "Remote Devices",
"Remove": "Ta bort",
"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": "Uppdatera",
"Rescan All": "Uppdatera alla",
"Rescan Interval": "Uppdateringsintervall",
@@ -142,7 +149,7 @@
"Resume": "Återuppta",
"Reused": "Återanvänt",
"Save": "Spara",
"Scan Time Remaining": "Scan Time Remaining",
"Scan Time Remaining": "Skanna Återstående Tid",
"Scanning": "Uppdaterar",
"Select the devices to share this folder with.": "Ange enheterna att dela den här katalogen med.",
"Select the folders to share with this device.": "Välj kataloger att dela med den här enheten.",
@@ -155,7 +162,7 @@
"Shared With": "Delad med",
"Short identifier for the folder. Must be the same on all cluster devices.": "Kort identifieringssträng för katalogen. Måste vara samma på alla enheter i klustret.",
"Show ID": "Visa ID",
"Show QR": "Show QR",
"Show QR": "Visa QR",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visas i stället för enhets-ID. Skickas till andra enheter som namn på denna enhet.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visas i stället för enhets-ID. Sätts till namnet på den andra enheten vid första anslutning om det lämnas tomt.",
"Shutdown": "Stäng av",
@@ -177,11 +184,11 @@
"Syncthing is upgrading.": "Syncthing uppgraderas.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing verkar avstängd, eller finns det problem med din Internetanslutning. Försöker igen...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing verkar ha drabbats av ett problem. Uppdatera sidan eller starta om Syncthing om problemet kvarstår.",
"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 Syncthing admin interface is configured to allow remote access without a password.": "Syncthing administratör gränssnittet är konfigurerat för att tillåta fjärrtillträde utan ett lösenord.",
"The aggregated statistics are publicly available at {%url%}.": "Sammanställd statistik finns publikt tillgänglig på {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfigurationen har sparats men inte aktiverats. Syncthing måste startas om för att aktivera den nya konfigurationen.",
"The device ID cannot be blank.": "Enhets-ID kan inte vara tomt.",
"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 device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Enhets-ID som behövs här kan du hitta i \"Redigera > Visa ID\"-dialogen på den andra enheten. Mellanrum och bindestreck är valfria (ignoreras).",
"The device ID to enter here can be found in the \"Edit > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Enhets-ID som behövs här kan du hitta i \"Redigera > Visa ID\"-dialogen på den andra enheten. Mellanrum och bindestreck är valfria (ignoreras).",
"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.": "Den krypterade användarstatistiken skickas dagligen. Den används för att spåra vanliga plattformar, katalogstorlekar och versioner. Om datan som rapporteras ändras så kommer du att bli tillfrågad igen.",
"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.": "Det inmatade enhets-ID:t verkar inte korrekt. Det ska vara en 52 eller 56 teckens sträng bestående av siffror och bokstäver, eventuellt med mellanrum och bindestreck.",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Frekvensgränsen måste vara ett icke-negativt tal (0: ingen gräns)",
"The rescan interval must be a non-negative number of seconds.": "Förnyelseintervallet måste vara ett positivt antal sekunder",
"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": "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 is a major version upgrade.": "Det här är en stor uppgradering.",
"Trash Can File Versioning": "Versionshantering på filer i papperskorgen",
@@ -220,6 +228,7 @@
"Version": "Version",
"Versions Path": "Katalog för versioner",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versioner tas bort automatiskt när de är äldre än den maximala åldersgränsen eller överstiger frekvensen i sitt interval.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "När du lägger till en ny enhet, kom ihåg att den här enheten måste läggas till på den andra enheten också.",
"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.": "När du lägger till ny katalog, tänk på att katalog-ID:t knyter ihop katalogen mellan olika noder. De måste vara exakt desamma mellan noder och stora eller små bokstäver har betydelse.",
"Yes": "Ja",
@@ -227,5 +236,6 @@
"days": "dagar",
"full documentation": "fullständig dokumentation",
"items": "poster",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela katalogen \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela katalogen \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Ekle",
"Add Device": "Cihaz Ekle",
"Add Folder": "Klasör Ekle",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Yeni klasör ekle?",
"Address": "Adres",
"Addresses": "Adresler",
"Advanced": "Gelişmiş Düzey",
"Advanced Configuration": "Gelişmiş Yapılandırma",
"Advanced settings": "Advanced settings",
"All Data": "Bütün Veriler",
"Allow Anonymous Usage Reporting?": "Anonim kullanımın raporlanmasına izin veriyor musun ?",
"Alphabetic": "Alfabetik",
@@ -32,6 +34,7 @@
"Connection Error": "Bağlantı hatası",
"Copied from elsewhere": "Başka bir yerden kopyalanmış",
"Copied from original": "Aslından kopyalanmış",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Telif Hakkı © 2015 katkıda bulunanlar:",
"Danger!": "Tehlike!",
"Delete": "Sil",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Dosyalar diğer cihazlarda yapılan değişikliklerden korunur, ancak bu cihazdaki değişiklikler kümedeki diğer cihazlara gönderilir.",
"Folder": "Klasör",
"Folder ID": "Klasör ID",
"Folder Label": "Folder Label",
"Folder Master": "Ana Klasör",
"Folder Path": "Klasör Yolu",
"Folders": "Klasörler",
@@ -111,6 +115,7 @@
"OK": "Tamam",
"Off": "Kapalı",
"Oldest First": "En eski olan önce",
"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": "Seçenekler",
"Out of Sync": "Eşzamanlama Dışı",
"Out of Sync Items": "Eşzamanlama dışında kalan Öğeler",
@@ -132,7 +137,9 @@
"Relayed via": "Relayed via",
"Relays": "Röleler",
"Release Notes": "Sürüm Notları",
"Remote Devices": "Remote Devices",
"Remove": "Kaldır",
"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": "Tekrar Tara",
"Rescan All": "Tümünü Tekrar Tara",
"Rescan Interval": "Tarama Aralığı",
@@ -181,7 +188,7 @@
"The aggregated statistics are publicly available at {%url%}.": "Toplanan halka açık istatistiklere ulaşabileceğiniz adres {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Yapılandırma kaydedildi ancak etkinleştirilmedi. Etkinleştirmek için Syncthing yeniden başlatılmalı.",
"The device ID cannot be blank.": "Cihaz ID boş olamaz.",
"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 device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Buraya girilecek olan aygıt ID'si diğer cihazlarda, \"Eylemler > ID Göster\" penceresinde bulunabilir. Boşluklar ve çizgiler isteğe bağlıdır (yoksayılmış).",
"The device ID to enter here can be found in the \"Edit > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "Buraya girilecek cihaz ID'si diğer düğümde \"Düzenle > ID Göster\" menüsünden bulunabilir. Boşluk ve kısa çizginin olup olmaması önemli değildir. (İhmal edilir)",
"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.": "Şifrelenmiş kullanım bilgisi günlük olarak gönderilir. Platform, klasör büyüklüğü ve uygulama sürümü hakkında bilgi toplanır. Toplanan bilgi çeşidi değişecek olursa, sizden tekrar onay istenecek.",
"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.": "Girilen cihaz ID'si geçerli gibi gözükmüyor. 52 ya da 56 karakter uzunluğunda, harf ve rakamlardan oluşmalı. Boşlukların ve kısa çizgilerin olup olmaması önemli değildir.",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Hız sınırı pozitif bir sayı olmalıdır. (0: sınırsız)",
"The rescan interval must be a non-negative number of seconds.": "Tarama zaman aralığı, saniye cinsinden negatif olmayan bir sayı olmalıdır.",
"They are retried automatically and will be synced when the error is resolved.": "Otomatik olarak yeniden deneniyor; hata giderildiğinde eşzamanlama gerçekleştirilecek.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "Hacker'ların bilgisayarındaki dosyaları okuma ve değiştirme yetkisine kolayca erişebilmelerini sağlayabilir.",
"This is a major version upgrade.": "Birincil sürüm yükseltmesidir.",
"Trash Can File Versioning": "Çöp Kutusu Dosya Sürümleme",
@@ -220,6 +228,7 @@
"Version": "Sürüm",
"Versions Path": "Sürüm Dizin Yolu",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Sürümler, tanımlı azami süre veya belirlenen zaman aralığı için izin verilen dosya sayısıılmışsa kendiliğinden silinir.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Yeni bir cihaz eklendiğinde, bu cihazın karşı tarafa da eklenmesi gerektiğini unutmayın.",
"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.": "Yeni bir klasör eklendiğinde, Klasör ID'nin klasörleri cihazlar arasında bağlantılandırmak için kullanıldığını unutmayın. Klasör ID'ler büyük - küçük harf duyarlıdır ve bütün cihazlarda tamı tamına eşleşmelidir.",
"Yes": "Evet",
@@ -227,5 +236,6 @@
"days": "günler",
"full documentation": "belgelendirme içeriğinin tümü",
"items": "öğel",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} \"{{folder}}\" klasörünü paylaşmak istiyor."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} \"{{folder}}\" klasörünü paylaşmak istiyor.",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -1,5 +1,5 @@
{
"A device with that ID is already added.": "Пристрій з таким ID вже додано.",
"A device with that ID is already added.": "Пристрій з таким ID вже додано раніше.",
"A negative number of days doesn't make sense.": "Від'ємна кількість днів немає сенсу.",
"A new major version may not be compatible with previous versions.": "Нова мажорна версія може бути несумісною із попередніми версіями.",
"API Key": "API ключ",
@@ -8,11 +8,13 @@
"Add": "Додати",
"Add Device": "Додати пристрій",
"Add Folder": "Додати директорію",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Додати нову директорію?",
"Address": "Адреса",
"Addresses": "Адреси",
"Advanced": "Розширені",
"Advanced Configuration": "Розширена конфігурація",
"Advanced settings": "Advanced settings",
"All Data": "Усі дані",
"Allow Anonymous Usage Reporting?": "Дозволити програмі збирати анонімну статистику використання?",
"Alphabetic": "За алфавітом",
@@ -32,6 +34,7 @@
"Connection Error": "Помилка з’єднання",
"Copied from elsewhere": "Скопійовано з іншого місця",
"Copied from original": "Скопійовано з оригіналу",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 наступних контриб’юторів:",
"Danger!": "Небезпечно!",
"Delete": "Видалити",
@@ -42,7 +45,7 @@
"Device {%device%} ({%address%}) wants to connect. Add new device?": "Пристрій {{device}} ({{address}}) намагається під’єднатися. Додати новий пристрій?",
"Devices": "Пристрої",
"Disconnected": "З’єднання відсутнє",
"Discovery": "Виявлення",
"Discovery": "Сервери обходу NAT",
"Documentation": "Документація",
"Download Rate": "Швидкість завантаження",
"Downloaded": "Завантажено",
@@ -51,7 +54,7 @@
"Edit Device": "Редагувати пристрій",
"Edit Folder": "Редагувати директорію",
"Editing": "Редагування",
"Enable Relaying": "Enable Relaying",
"Enable Relaying": "Увімкнути ретрансляцію (relaying)",
"Enable UPnP": "Увімкнути UPnP",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Введіть розділені комою (\"tcp://ip:port\", \"tcp://host:port\") адреси або \"dynamic\" для автоматичного визначення адреси.",
"Enter ignore patterns, one per line.": "Введіть шаблони ігнорування, по одному на рядок.",
@@ -63,10 +66,11 @@
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Біти прав доступу до файлів будуть проігноровані під час пошуку змін. Використовуйте на файлових системах FAT.",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Файли, що замінюються або видаляються Syncthing, переміщуються у директорію .stversions. ",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Файли будуть поміщатися у директорію .stversions із відповідною позначкою часу, коли вони будуть замінятися або видалятися програмою.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Файли захищено від змін зроблених на інших пристроях, але зміни зроблені на цьому пристрої будуть надіслані решті кластеру.",
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Вміст папки захищено від змін, зроблених на інших пристроях, але зміни зроблені на цьому пристрої можна розіслати решті пристроїв кластеру.",
"Folder": "Директорія",
"Folder ID": "ID директорії",
"Folder Master": "Центральна директорія",
"Folder Label": "Folder Label",
"Folder Master": "Вважати за оригінал",
"Folder Path": "Шлях до директорії",
"Folders": "Директорії",
"GUI": "Графічний інтерфейс",
@@ -74,14 +78,14 @@
"GUI Authentication User": "Логін користувача для доступу до панелі управління",
"GUI Listen Addresses": "Адреса доступу до панелі управління",
"Generate": "Згенерувати",
"Global Discovery": "Глобальне виявлення",
"Global Discovery": "Глобальне виявлення (internet)",
"Global Discovery Server": "Сервер для глобального виявлення",
"Global Discovery Servers": "Global Discovery Servers",
"Global Discovery Servers": "Сервери глобального виявлення \n(координації обходу NAT)",
"Global State": "Глобальний статус",
"Help": "Допомога",
"Home page": "Домашня сторінка",
"Ignore": "Ігнорувати",
"Ignore Patterns": "Ігнорувати шаблони",
"Ignore Patterns": "Шаблони винятків",
"Ignore Permissions": "Ігнорувати права доступу до файлів",
"Incoming Rate Limit (KiB/s)": "Ліміт швидкості завантаження (КіБ/с)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Невірна конфігурація може пошкодити вміст вашої директорії та зробити Syncthing недієздатним.",
@@ -92,7 +96,7 @@
"Last File Received": "Останній завантажений файл",
"Last seen": "З’являвся останній раз",
"Later": "Пізніше",
"Local Discovery": "Локальне виявлення",
"Local Discovery": "Локальне виявлення (LAN)",
"Local State": "Локальний статус",
"Local State (Total)": "Локальний статус (загалом)",
"Major Upgrade": "Мажорне оновлення",
@@ -111,11 +115,12 @@
"OK": "Гаразд",
"Off": "Вимкнути",
"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": "Опції",
"Out of Sync": "Не синхронізовано",
"Out of Sync Items": "Не синхронізовані елементи",
"Outgoing Rate Limit (KiB/s)": "Ліміт швидкості віддачі (КіБ/с)",
"Override Changes": "Перезаписати зміни",
"Override Changes": "Розіслати мою версію",
"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 versions should be stored (leave empty for the default .stversions folder in the folder).": "Шлях, де повинні зберігатися версії (залиште порожнім для зберігання в .stversions в середині директорії)",
"Pause": "Пауза",
@@ -128,11 +133,13 @@
"Quick guide to supported patterns": "Швидкий посібник по шаблонам, що підтримуються",
"RAM Utilization": "Використання RAM",
"Random": "Випадково",
"Relay Servers": "Relay Servers",
"Relay Servers": "Сервери ретрансляції",
"Relayed via": "Ретранслювати через",
"Relays": "Ретрансляція",
"Relays": "Ретранслятори",
"Release Notes": "Примітки до випуску",
"Remote Devices": "Remote Devices",
"Remove": "Видалити",
"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": "Пересканувати усе",
"Rescan Interval": "Інтервал для повторного сканування",
@@ -169,7 +176,7 @@
"Statistics": "Статистика",
"Stopped": "Зупинено",
"Support": "Підтримка",
"Sync Protocol Listen Addresses": "Адреса панелі управління",
"Sync Protocol Listen Addresses": "Адреса і вхідний порт протоколу синхронізації",
"Syncing": "Синхронізація",
"Syncthing has been shut down.": "Syncthing вимкнено (закрито).",
"Syncthing includes the following software or portions thereof:": "Syncthing містить наступне програмне забезпечення (або його частини):",
@@ -181,7 +188,7 @@
"The aggregated statistics are publicly available at {%url%}.": "Зібрана статистика публічно доступна за посиланням {{url}}.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Конфігурацію збережено, але не активовано. Необхідно перезапустити Syncthing для того, щоби активувати нову конфігурацію.",
"The device ID cannot be blank.": "ID пристрою не може бути порожнім.",
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "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).": "ID пристрою, який необхідно додати. Може бути знайдений у вікні \"Редагувати > Показати ID\" на іншому пристрої. Пробіли та тире опціональні (будуть видалені програмою).",
"The device ID to enter here can be found in the \"Edit > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "ID пристрою, який необхідно додати. Може бути знайдений у вікні \"Редагувати > Показати ID\" на іншому пристрої. Пробіли та тире опціональні (вони ігноруються програмою).",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Зашифрована статистика використання відсилається щоденно. Вона використовується для того, щоб розробники розуміли, на яких платформах працює програма, розміри директорій та версії програми. Якщо набір даних, що збирається зазнає змін, ви обов’язково будете повідомлені через це діалогове вікно.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Введений ID пристрою невалідний. Ідентифікатор має вигляд строки довжиною 52 або 56 символів, що містить цифри та літери, із опціональними пробілами та тире.",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Швидкість має бути додатнім числом.",
"The rescan interval must be a non-negative number of seconds.": "Інтервал повторного сканування повинен бути неід’ємною величиною.",
"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 is a major version upgrade.": "Це оновлення мажорної версії",
"Trash Can File Versioning": "Версіонування файлів у кошику ",
@@ -220,12 +228,14 @@
"Version": "Версія",
"Versions Path": "Шлях до версій",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Версії автоматично видаляються, якщо вони старше, ніж максимальний вік, або перевищують допустиму кількість файлів за інтервал.",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Коли додаєте новий пристрій, пам’ятайте, що цей пристрій повинен бути доданий і на іншій стороні.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Коли додаєте нову директорію, пам’ятайте, що ID цієї директорії використовується для того, щоб зв’язувати директорії разом між вузлами. Назви є чутливими до регістра та повинні співпадати точно між усіма вузлами.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Коли додаєте новий вузол, пам’ятайте, що цей вузол повинен бути доданий і на іншій стороні.",
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Коли додаєте нову директорію, пам’ятайте, що ID цієї директорії використовується для того, щоб зв’язувати директорії разом між пристроями. Назви повинні точно співпадати між усіма пристроями, регістр символів має значення.",
"Yes": "Так",
"You must keep at least one version.": "Ви повинні зберігати щонайменше одну версію.",
"days": "днів",
"full documentation": "повна документація",
"items": "елементи",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хоче поділитися директорією \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хоче поділитися директорією \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "Thêm",
"Add Device": "Thêm thiết bị",
"Add Folder": "Thêm thư mục",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "Thêm thư mục mới?",
"Address": "Địa chỉ",
"Addresses": "Các địa chỉ",
"Advanced": "Nâng cao",
"Advanced Configuration": "Cấu hình nâng cao",
"Advanced settings": "Advanced settings",
"All Data": "Tất cả dữ liệu",
"Allow Anonymous Usage Reporting?": "Cho phép báo cáo sử dụng ẩn danh?",
"Alphabetic": "A-Z",
@@ -32,6 +34,7 @@
"Connection Error": "Lỗi kết nối",
"Copied from elsewhere": "Đã sao chép từ nơi khác",
"Copied from original": "Đã sao chép từ nguồn",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Bản quyền © 2015 thuộc về các nhà cộng tác sau:",
"Danger!": "Nguy hiểm!",
"Delete": "Xoá",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Các tập tin sẽ được bảo vệ khỏi những thay đổi trên các thiết bị khác, nhưng những thay đổi trên thiết bị này sẽ được chuyển tới các máy còn lại trong cụm.",
"Folder": "Thư mục",
"Folder ID": "ID thư mục",
"Folder Label": "Folder Label",
"Folder Master": "Thư mục Chủ",
"Folder Path": "Đường dẫn đến thư mục",
"Folders": "Các thư mục",
@@ -111,6 +115,7 @@
"OK": "OK",
"Off": "Tắt",
"Oldest First": "Cũ nhất đầu tiên",
"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": "Tuỳ chọn",
"Out of Sync": "Mất đồng bộ",
"Out of Sync Items": "Các nội dung mất đồng bộ",
@@ -132,7 +137,9 @@
"Relayed via": "Chuyển tiếp qua",
"Relays": "Các máy chuyển tiếp",
"Release Notes": "Lịch sử phát hành",
"Remote Devices": "Remote Devices",
"Remove": "Xoá",
"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": "Quét lại",
"Rescan All": "Quét lại tất cả",
"Rescan Interval": "Khoảng cách thời gian quét lại",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "Giới hạn tốc độ phải là một số không âm (0: không giới hạn)",
"The rescan interval must be a non-negative number of seconds.": "Khoảng thời gian quét lại phải là một số giây không âm.",
"They are retried automatically and will be synced when the error is resolved.": "Chúng sẽ được tự động thử lại và đồng bộ khi lỗi được khắc phục.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "Thao tác này có thể khiến tin tặc truy cập để đọc và thay đổi bất kỳ tập tin nào trên máy tính của bạn một cách dễ dàng.",
"This is a major version upgrade.": "Đây là bản nâng cấp quan trọng.",
"Trash Can File Versioning": "Kiểu thùng rác",
@@ -220,6 +228,7 @@
"Version": "Phiên bản",
"Versions Path": "Đường dẫn đến các phiên bản",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Các phiên bản sẽ tự động được xoá nếu chúng cũ hơn thời hạn tối đa hoặc vượt quá số tập tin cho phép trong một khoảng thời gian.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Khi thêm một thiết bị mới, hãy nhớ rằng thiết bị này cũng phải được thêm vào máy kia.",
"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.": "Khi thêm một thư mục mới, hãy nhớ rằng ID thư mục được dùng để gắn kết thư mục giữa các thiết bị với nhau. Chúng phải chính xác từng chữ, cả viết hoa và thường giữa tất cả thiết bị.",
"Yes": "Phải",
@@ -227,5 +236,6 @@
"days": "ngày",
"full documentation": "tài liệu đầy đủ",
"items": "nội dung",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} muốn chia sẻ thư mục \"{{folder}}\"."
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} muốn chia sẻ thư mục \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

@@ -8,11 +8,13 @@
"Add": "添加",
"Add Device": "添加设备",
"Add Folder": "添加文件夹",
"Add Remote Device": "添加远程设备",
"Add new folder?": "添加新文件夹?",
"Address": "地址",
"Addresses": "地址列表",
"Advanced": "高级",
"Advanced Configuration": "高级设置",
"Advanced settings": "高级设置",
"All Data": "所有数据",
"Allow Anonymous Usage Reporting?": "允许匿名使用报告?",
"Alphabetic": "字母顺序",
@@ -32,6 +34,7 @@
"Connection Error": "连接出错",
"Copied from elsewhere": "从其他设备复制",
"Copied from original": "从源复制",
"Copyright © 2014-2016 the following Contributors:": "版权所有© 2014-2016 以下贡献者:",
"Copyright © 2015 the following Contributors:": "版权 ©2015 由下列贡献者所有:",
"Danger!": "危险!",
"Delete": "删除",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "在其它设备中对该文件夹内文件的修改并不会被同步到本机,但是在本机上对其的修改,则会被同步到其它设备中。",
"Folder": "文件夹",
"Folder ID": "文件夹标识",
"Folder Label": "文件夹标签",
"Folder Master": "主文件夹",
"Folder Path": "文件夹路径",
"Folders": "文件夹",
@@ -111,6 +115,7 @@
"OK": "确定",
"Off": "关闭",
"Oldest First": "旧文件优先",
"Optional descriptive label for the folder. Can be different on each device.": "可选的文件夹表述标签。在不同设备上可以不一致。",
"Options": "选项",
"Out of Sync": "未同步",
"Out of Sync Items": "未同步的项目",
@@ -132,7 +137,9 @@
"Relayed via": "中转经由",
"Relays": "中转",
"Release Notes": "发布说明",
"Remote Devices": "远程设备",
"Remove": "移除",
"Required identifier for the folder. Must be the same on all cluster devices.": "需要给文件夹设置标识。在所有丛设备上必须一致。",
"Rescan": "重新扫描",
"Rescan All": "全部重新扫描",
"Rescan Interval": "扫描间隔",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "传输速度限制为非负整数0 表示不限制)",
"The rescan interval must be a non-negative number of seconds.": "扫描间隔单位为秒,且不能为负数。",
"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 is a major version upgrade.": "这是一个重大版本更新",
"Trash Can File Versioning": "回收站式版本控制",
@@ -220,6 +228,7 @@
"Version": "版本",
"Versions Path": "历史版本路径",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "超过最长保留时间,或者不满足下列条件的历史版本,将会被删除。",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "警告, 该路径是一个已经存在文件夹\"{{otherFolder}}\"的子文件夹.",
"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.": "若你添加了新文件夹,记住文件夹标识是用以在不同设备间建立联系的。在不同设备间拥有相同标识的文件夹将会被同步。且文件夹标识大小写敏感。",
"Yes": "是",
@@ -227,5 +236,6 @@
"days": "天",
"full documentation": "完整文档",
"items": "条目",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想将 “{{folder}}” 文件夹共享给您"
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想将 “{{folder}}” 文件夹共享给您",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}}想要分享文件夹\"{{folderLabel}}\" ({{folder}}).\n"
}

View File

@@ -8,11 +8,13 @@
"Add": "增加",
"Add Device": "增加裝置",
"Add Folder": "增加資料夾",
"Add Remote Device": "Add Remote Device",
"Add new folder?": "新增資料夾?",
"Address": "位址",
"Addresses": "位址",
"Advanced": "進階",
"Advanced Configuration": "進階設定",
"Advanced settings": "Advanced settings",
"All Data": "全部資料",
"Allow Anonymous Usage Reporting?": "允許匿名的使用資訊回報?",
"Alphabetic": "字母順序",
@@ -32,6 +34,7 @@
"Connection Error": "連線錯誤",
"Copied from elsewhere": "從別處複製",
"Copied from original": "從原來複製",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 the following Contributors:",
"Copyright © 2015 the following Contributors:": "Copyright © 2015 下列貢獻者:",
"Danger!": "Danger!",
"Delete": "刪除",
@@ -66,6 +69,7 @@
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "其他裝置做的改變不會影響到此裝置的檔案,但在此裝置上的變化將被發送到叢集中的其他部分。",
"Folder": "資料夾",
"Folder ID": "資料夾識別碼",
"Folder Label": "Folder Label",
"Folder Master": "主資料夾",
"Folder Path": "資料夾路徑",
"Folders": "資料夾",
@@ -111,6 +115,7 @@
"OK": "確定",
"Off": "關閉",
"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": "選項",
"Out of Sync": "Out of Sync",
"Out of Sync Items": "不同步物件",
@@ -132,7 +137,9 @@
"Relayed via": "中繼於",
"Relays": "中繼點",
"Release Notes": "版本資訊",
"Remote Devices": "Remote Devices",
"Remove": "移除",
"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": "全部重新掃描",
"Rescan Interval": "重新掃描間隔",
@@ -203,6 +210,7 @@
"The rate limit must be a non-negative number (0: no limit)": "限制速率必須為非負的數字 (0: 不設限制)",
"The rescan interval must be a non-negative number of seconds.": "重新掃描間隔必須為一個非負數的秒數。",
"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 is a major version upgrade.": "這是一個主要版本更新。",
"Trash Can File Versioning": "Trash Can File Versioning",
@@ -220,6 +228,7 @@
"Version": "版本",
"Versions Path": "歷史版本路徑",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "當檔案歷史版本的存留時間大於設定的最大值,或是其數量在一段時間內超出允許值時,則會被刪除。",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",
"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.": "當新增一個資料夾時,請記住,資料夾識別碼是用來將裝置之間的資料夾綁定在一起的。它們有區分大小寫,且必須在所有裝置之間完全相同。",
"Yes": "是",
@@ -227,5 +236,6 @@
"days": "日",
"full documentation": "完整說明文件",
"items": "個項目",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想要分享資料夾 \"{{folder}}\"。"
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想要分享資料夾 \"{{folder}}\"。",
"{%device%} wants to share folder \"{%folderLabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderLabel}}\" ({{folder}})."
}

View File

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

View File

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

View File

@@ -221,8 +221,9 @@
<div class="panel-heading" data-toggle="collapse" data-parent="#folders" href="#folder-{{$index}}" style="cursor: pointer">
<div class="panel-progress" ng-show="folderStatus(folder) == 'syncing'" ng-attr-style="width: {{syncPercentage(folder.id)}}%"></div>
<div class="panel-progress" ng-show="folderStatus(folder) == 'scanning' && scanProgress[folder.id] != undefined" ng-attr-style="width: {{scanPercentage(folder.id)}}%"></div>
<h3 class="panel-title">
<span class="fa fa-folder hidden-xs"></span>{{folder.id}}
<h4 class="panel-title">
<span class="fa hidden-xs fa-fw" ng-class="[folder.readOnly ? 'fa-lock' : 'fa-folder']"></span>
<a href="#folder-{{$index}}">{{folder.id}}</a>
<span class="pull-right text-{{folderClass(folder)}}" ng-switch="folderStatus(folder)">
<span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs">&#9724;</span></span>
@@ -241,7 +242,7 @@
</span>
<span ng-switch-when="outofsync"><span class="hidden-xs" translate>Out of Sync</span><span class="visible-xs">&#9724;</span></span>
</span>
</h3>
</h4>
</div>
<div id="folder-{{$index}}" class="panel-collapse collapse">
<div class="panel-body">
@@ -249,7 +250,7 @@
<tbody>
<tr>
<th><span class="fa fa-fw fa-folder-open"></span>&nbsp;<span translate>Folder Path</span></th>
<td class="text-right">{{folder.path}}</td>
<td class="text-right" title="{{folder.path}}">{{folder.path}}</td>
</tr>
<tr ng-if="model[folder.id].invalid || model[folder.id].error">
<th><span class="fa fa-fw fa-exclamation-triangle"></span>&nbsp;<span translate>Error</span></th>
@@ -379,12 +380,12 @@
<!-- This device -->
<div class="col-md-6">
<h3 translate>Devices</h3>
<h3 translate>This Device</h3>
<div class="panel panel-default" ng-repeat="deviceCfg in [thisDevice()]">
<div class="panel-heading" data-toggle="collapse" href="#device-this" style="cursor: pointer">
<h3 class="panel-title">
<h4 class="panel-title">
<identicon data-value="deviceCfg.deviceID"></identicon>&emsp;{{deviceName(deviceCfg)}}
</h3>
</h4>
</div>
<div id="device-this" class="panel-collapse collapse in">
<div class="panel-body">
@@ -442,7 +443,7 @@
</tr>
<tr>
<th><span class="fa fa-fw fa-tag"></span>&nbsp;<span translate>Version</span></th>
<td class="text-right">{{versionString()}}</td>
<td class="text-right" title="{{versionString()}}">{{versionString()}}</td>
</tr>
</tbody>
</table>
@@ -451,13 +452,13 @@
</div>
<!-- Remote devices -->
<h3 translate>Remote Devices</h3>
<div class="panel-group" id="devices">
<div class="panel panel-default" ng-repeat="deviceCfg in otherDevices()">
<div class="panel-heading" data-toggle="collapse" data-parent="#devices" href="#device-{{$index}}" style="cursor: pointer">
<div class="panel-progress" ng-show="deviceStatus(deviceCfg) == 'syncing'" ng-attr-style="width: {{completion[deviceCfg.deviceID]._total | number:0}}%"></div>
<h3 class="panel-title">
<identicon data-value="deviceCfg.deviceID"></identicon>&emsp;{{deviceName(deviceCfg)}}
<h4 class="panel-title">
<identicon data-value="deviceCfg.deviceID"></identicon>&emsp;<a href="#device-{{$index}}">{{deviceName(deviceCfg)}}</a>
<span ng-switch="deviceStatus(deviceCfg)" class="pull-right text-{{deviceClass(deviceCfg)}}">
<span ng-switch-when="insync"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="syncing">
@@ -467,7 +468,7 @@
<span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="unused"><span class="hidden-xs" translate>Unused</span><span class="visible-xs">&#9724;</span></span>
</span>
</h3>
</h4>
</div>
<div id="device-{{$index}}" class="panel-collapse collapse">
<div class="panel-body">
@@ -535,7 +536,7 @@
</div>
<div class="form-group">
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="addDevice()">
<span class="fa fa-plus"></span>&nbsp;<span translate>Add Device</span>
<span class="fa fa-plus"></span>&nbsp;<span translate>Add Remote Device</span>
</button>
<div class="clearfix"></div>
</div>

View File

@@ -69,6 +69,7 @@
<li class="auto-generated">Marcin Dziadus</li>
<li class="auto-generated">Mateusz Naściszewski</li>
<li class="auto-generated">Matt Burke</li>
<li class="auto-generated">Max Schulze</li>
<li class="auto-generated">Michael Jephcote</li>
<li class="auto-generated">Michael Ploujnikov</li>
<li class="auto-generated">Michael Tilli</li>
@@ -92,6 +93,7 @@
<li class="auto-generated">Victor Buinsky</li>
<li class="auto-generated">Vil Brekin</li>
<li class="auto-generated">William A. Kennington III</li>
<li class="auto-generated">Wulf Weich</li>
<li class="auto-generated">Yannic A.</li>
</ul>
</div>

View File

File diff suppressed because one or more lines are too long

1
lib/db/.gitignore vendored
View File

@@ -1,2 +1 @@
!*.zip
testdata/*.db

View File

@@ -4,9 +4,16 @@
// 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/.
// Package db provides a set type to track local/remote files with newness
// checks. We must do a certain amount of normalization in here. We will get
// fed paths with either native or wire-format separators and encodings
// depending on who calls us. We transform paths to wire-format (NFC and
// slashes) on the way to the database, and transform to native format
// (varying separator and encoding) on the way back out.
package db
import (
"bytes"
"encoding/binary"
"fmt"
@@ -23,10 +30,10 @@ const maxBatchSize = 256 << 10
type BlockMap struct {
db *Instance
folder uint32
folder string
}
func NewBlockMap(db *Instance, folder uint32) *BlockMap {
func NewBlockMap(db *Instance, folder string) *BlockMap {
return &BlockMap{
db: db,
folder: folder,
@@ -116,7 +123,7 @@ func (m *BlockMap) Discard(files []protocol.FileInfo) error {
// Drop block map, removing all entries related to this block map from the db.
func (m *BlockMap) Drop() error {
batch := new(leveldb.Batch)
iter := m.db.NewIterator(util.BytesPrefix(m.blockKeyInto(nil, nil, "")[:keyPrefixLen+keyFolderLen]), nil)
iter := m.db.NewIterator(util.BytesPrefix(m.blockKeyInto(nil, nil, "")[:1+64]), nil)
defer iter.Release()
for iter.Next() {
if batch.Len() > maxBatchSize {
@@ -166,13 +173,12 @@ func (f *BlockFinder) String() string {
func (f *BlockFinder) Iterate(folders []string, hash []byte, iterFn func(string, string, int32) bool) bool {
var key []byte
for _, folder := range folders {
folderID := f.db.folderIdx.ID([]byte(folder))
key = blockKeyInto(key, hash, folderID, "")
key = blockKeyInto(key, hash, folder, "")
iter := f.db.NewIterator(util.BytesPrefix(key), nil)
defer iter.Release()
for iter.Next() && iter.Error() == nil {
file := blockKeyName(iter.Key())
folder, file := fromBlockKey(iter.Key())
index := int32(binary.BigEndian.Uint32(iter.Value()))
if iterFn(folder, osutil.NativeFilename(file), index) {
return true
@@ -188,41 +194,48 @@ func (f *BlockFinder) Fix(folder, file string, index int32, oldHash, newHash []b
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, uint32(index))
folderID := f.db.folderIdx.ID([]byte(folder))
batch := new(leveldb.Batch)
batch.Delete(blockKeyInto(nil, oldHash, folderID, file))
batch.Put(blockKeyInto(nil, newHash, folderID, file), buf)
batch.Delete(blockKeyInto(nil, oldHash, folder, file))
batch.Put(blockKeyInto(nil, newHash, folder, file), buf)
return f.db.Write(batch, nil)
}
// m.blockKey returns a byte slice encoding the following information:
// keyTypeBlock (1 byte)
// folder (4 bytes)
// folder (64 bytes)
// block hash (32 bytes)
// file name (variable size)
func blockKeyInto(o, hash []byte, folder uint32, file string) []byte {
reqLen := keyPrefixLen + keyFolderLen + keyHashLen + len(file)
func blockKeyInto(o, hash []byte, folder, file string) []byte {
reqLen := 1 + 64 + 32 + len(file)
if cap(o) < reqLen {
o = make([]byte, reqLen)
} else {
o = o[:reqLen]
}
o[0] = KeyTypeBlock
binary.BigEndian.PutUint32(o[keyPrefixLen:], folder)
copy(o[keyPrefixLen+keyFolderLen:], []byte(hash))
copy(o[keyPrefixLen+keyFolderLen+keyHashLen:], []byte(file))
copy(o[1:], []byte(folder))
for i := len(folder); i < 64; i++ {
o[1+i] = 0
}
copy(o[1+64:], []byte(hash))
copy(o[1+64+32:], []byte(file))
return o
}
// blockKeyName returns the file name from the block key
func blockKeyName(data []byte) string {
if len(data) < keyPrefixLen+keyFolderLen+keyHashLen+1 {
func fromBlockKey(data []byte) (string, string) {
if len(data) < 1+64+32+1 {
panic("Incorrect key length")
}
if data[0] != KeyTypeBlock {
panic("Incorrect key type")
}
file := string(data[keyPrefixLen+keyFolderLen+keyHashLen:])
return file
file := string(data[1+64+32:])
slice := data[1 : 1+64]
izero := bytes.IndexByte(slice, 0)
if izero > -1 {
return string(slice[:izero]), file
}
return string(slice), file
}

View File

@@ -10,7 +10,6 @@ import (
"testing"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syndtr/goleveldb/leveldb/util"
)
func genBlocks(n int) []protocol.BlockInfo {
@@ -56,7 +55,7 @@ func setup() (*Instance, *BlockFinder) {
}
func dbEmpty(db *Instance) bool {
iter := db.NewIterator(util.BytesPrefix([]byte{KeyTypeBlock}), nil)
iter := db.NewIterator(nil, nil)
defer iter.Release()
if iter.Next() {
return false
@@ -71,7 +70,7 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
t.Fatal("db not empty")
}
m := NewBlockMap(db, db.folderIdx.ID([]byte("folder1")))
m := NewBlockMap(db, "folder1")
f3.Flags |= protocol.FlagDirectory
@@ -153,8 +152,8 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
func TestBlockFinderLookup(t *testing.T) {
db, f := setup()
m1 := NewBlockMap(db, db.folderIdx.ID([]byte("folder1")))
m2 := NewBlockMap(db, db.folderIdx.ID([]byte("folder2")))
m1 := NewBlockMap(db, "folder1")
m2 := NewBlockMap(db, "folder2")
err := m1.Add([]protocol.FileInfo{f1})
if err != nil {
@@ -222,7 +221,7 @@ func TestBlockFinderFix(t *testing.T) {
return true
}
m := NewBlockMap(db, db.folderIdx.ID([]byte("folder1")))
m := NewBlockMap(db, "folder1")
err := m.Add([]protocol.FileInfo{f1})
if err != nil {
t.Fatal(err)

View File

@@ -42,8 +42,6 @@ const (
KeyTypeDeviceStatistic
KeyTypeFolderStatistic
KeyTypeVirtualMtime
KeyTypeFolderIdx
KeyTypeDeviceIdx
)
type fileVersion struct {

View File

@@ -1,114 +0,0 @@
// Copyright (C) 2015 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/.
package db
import (
"bytes"
"github.com/syndtr/goleveldb/leveldb"
)
// convertKeyFormat converts from the v0.12 to the v0.13 database format, to
// avoid having to do rescan. The change is in the key format for folder
// labels, so we basically just iterate over the database rewriting keys as
// necessary.
func convertKeyFormat(from, to *leveldb.DB) error {
l.Infoln("Converting database key format")
blocks, files, globals, unchanged := 0, 0, 0, 0
dbi := newDBInstance(to)
i := from.NewIterator(nil, nil)
for i.Next() {
key := i.Key()
switch key[0] {
case KeyTypeBlock:
folder, file := oldFromBlockKey(key)
folderIdx := dbi.folderIdx.ID([]byte(folder))
hash := key[1+64:]
newKey := blockKeyInto(nil, hash, folderIdx, file)
if err := to.Put(newKey, i.Value(), nil); err != nil {
return err
}
blocks++
case KeyTypeDevice:
newKey := dbi.deviceKey(oldDeviceKeyFolder(key), oldDeviceKeyDevice(key), oldDeviceKeyName(key))
if err := to.Put(newKey, i.Value(), nil); err != nil {
return err
}
files++
case KeyTypeGlobal:
newKey := dbi.globalKey(oldGlobalKeyFolder(key), oldGlobalKeyName(key))
if err := to.Put(newKey, i.Value(), nil); err != nil {
return err
}
globals++
case KeyTypeVirtualMtime:
// Cannot be converted, we drop it instead :(
default:
if err := to.Put(key, i.Value(), nil); err != nil {
return err
}
unchanged++
}
}
l.Infof("Converted %d blocks, %d files, %d globals (%d unchanged).", blocks, files, globals, unchanged)
return nil
}
func oldDeviceKeyFolder(key []byte) []byte {
folder := key[1 : 1+64]
izero := bytes.IndexByte(folder, 0)
if izero < 0 {
return folder
}
return folder[:izero]
}
func oldDeviceKeyDevice(key []byte) []byte {
return key[1+64 : 1+64+32]
}
func oldDeviceKeyName(key []byte) []byte {
return key[1+64+32:]
}
func oldGlobalKeyName(key []byte) []byte {
return key[1+64:]
}
func oldGlobalKeyFolder(key []byte) []byte {
folder := key[1 : 1+64]
izero := bytes.IndexByte(folder, 0)
if izero < 0 {
return folder
}
return folder[:izero]
}
func oldFromBlockKey(data []byte) (string, string) {
if len(data) < 1+64+32+1 {
panic("Incorrect key length")
}
if data[0] != KeyTypeBlock {
panic("Incorrect key type")
}
file := string(data[1+64+32:])
slice := data[1 : 1+64]
izero := bytes.IndexByte(slice, 0)
if izero > -1 {
return string(slice[:izero]), file
}
return string(slice), file
}

View File

@@ -1,136 +0,0 @@
// Copyright (C) 2015 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/.
package db
import (
"archive/zip"
"io"
"os"
"path/filepath"
"testing"
"github.com/syndtr/goleveldb/leveldb"
)
func TestLabelConversion(t *testing.T) {
os.RemoveAll("testdata/oldformat.db")
defer os.RemoveAll("testdata/oldformat.db")
os.RemoveAll("testdata/newformat.db")
defer os.RemoveAll("testdata/newformat.db")
if err := unzip("testdata/oldformat.db.zip", "testdata"); err != nil {
t.Fatal(err)
}
odb, err := leveldb.OpenFile("testdata/oldformat.db", nil)
if err != nil {
t.Fatal(err)
}
ldb, err := leveldb.OpenFile("testdata/newformat.db", nil)
if err != nil {
t.Fatal(err)
}
if err = convertKeyFormat(odb, ldb); err != nil {
t.Fatal(err)
}
ldb.Close()
odb.Close()
inst, err := Open("testdata/newformat.db")
if err != nil {
t.Fatal(err)
}
fs := NewFileSet("default", inst)
files, deleted, _ := fs.GlobalSize()
if files+deleted != 953 {
// Expected number of global entries determined by
// ../../bin/stindex testdata/oldformat.db/ | grep global | grep -c default
t.Errorf("Conversion error, global list differs (%d != 953)", files+deleted)
}
files, deleted, _ = fs.LocalSize()
if files+deleted != 953 {
t.Errorf("Conversion error, device list differs (%d != 953)", files+deleted)
}
f := NewBlockFinder(inst)
// [block] F:"default" H:1c25dea9003cc16216e2a22900be1ec1cc5aaf270442904e2f9812c314e929d8 N:"f/f2/f25f1b3e6e029231b933531b2138796d" I:3
h := []byte{0x1c, 0x25, 0xde, 0xa9, 0x00, 0x3c, 0xc1, 0x62, 0x16, 0xe2, 0xa2, 0x29, 0x00, 0xbe, 0x1e, 0xc1, 0xcc, 0x5a, 0xaf, 0x27, 0x04, 0x42, 0x90, 0x4e, 0x2f, 0x98, 0x12, 0xc3, 0x14, 0xe9, 0x29, 0xd8}
found := 0
f.Iterate([]string{"default"}, h, func(folder, file string, idx int32) bool {
if folder == "default" && file == filepath.FromSlash("f/f2/f25f1b3e6e029231b933531b2138796d") && idx == 3 {
found++
}
return true
})
if found != 1 {
t.Errorf("Found %d blocks instead of expected 1", found)
}
inst.Close()
}
func unzip(src, dest string) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer func() {
if err := r.Close(); err != nil {
panic(err)
}
}()
os.MkdirAll(dest, 0755)
// Closure to address file descriptors issue with all the deferred .Close() methods
extractAndWriteFile := func(f *zip.File) error {
rc, err := f.Open()
if err != nil {
return err
}
defer func() {
if err := rc.Close(); err != nil {
panic(err)
}
}()
path := filepath.Join(dest, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(path, f.Mode())
} else {
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
panic(err)
}
}()
_, err = io.Copy(f, rc)
if err != nil {
return err
}
}
return nil
}
for _, f := range r.File {
err := extractAndWriteFile(f)
if err != nil {
return err
}
}
return nil
}

View File

@@ -8,15 +8,11 @@ package db
import (
"bytes"
"encoding/binary"
"os"
"path/filepath"
"sort"
"strings"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/iterator"
@@ -29,33 +25,14 @@ type deletionHandler func(t readWriteTransaction, folder, device, name []byte, d
type Instance struct {
*leveldb.DB
folderIdx *smallIndex
deviceIdx *smallIndex
}
const (
keyPrefixLen = 1
keyFolderLen = 4 // indexed
keyDeviceLen = 4 // indexed
keyHashLen = 32
)
func Open(file string) (*Instance, error) {
opts := &opt.Options{
OpenFilesCacheCapacity: 100,
WriteBuffer: 4 << 20,
}
if _, err := os.Stat(file); os.IsNotExist(err) {
// The file we are looking to open does not exist. This may be the
// first launch so we should look for an old version and try to
// convert it.
if err := checkConvertDatabase(file); err != nil {
l.Infoln("Converting old database:", err)
l.Infoln("Will rescan from scratch.")
}
}
db, err := leveldb.OpenFile(file, opts)
if leveldbIsCorrupted(err) {
db, err = leveldb.RecoverFile(file, opts)
@@ -83,12 +60,9 @@ func OpenMemory() *Instance {
}
func newDBInstance(db *leveldb.DB) *Instance {
i := &Instance{
return &Instance{
DB: db,
}
i.folderIdx = newSmallIndex(i, []byte{KeyTypeFolderIdx})
i.deviceIdx = newSmallIndex(i, []byte{KeyTypeDeviceIdx})
return i
}
func (db *Instance) Compact() error {
@@ -98,10 +72,13 @@ func (db *Instance) Compact() error {
func (db *Instance) genericReplace(folder, device []byte, fs []protocol.FileInfo, localSize, globalSize *sizeTracker, deleteFn deletionHandler) int64 {
sort.Sort(fileList(fs)) // sort list on name, same as in the database
start := db.deviceKey(folder, device, nil) // before all folder/device files
limit := db.deviceKey(folder, device, []byte{0xff, 0xff, 0xff, 0xff}) // after all folder/device files
t := db.newReadWriteTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, device, nil)[:keyPrefixLen+keyFolderLen+keyDeviceLen]), nil)
dbi := t.NewIterator(&util.Range{Start: start, Limit: limit}, nil)
defer dbi.Release()
moreDb := dbi.Next()
@@ -259,19 +236,15 @@ func (db *Instance) updateFiles(folder, device []byte, fs []protocol.FileInfo, l
return maxLocalVer
}
func (db *Instance) withHave(folder, device []byte, truncate bool, fn Iterator) {
func (db *Instance) withHave(folder, device, prefix []byte, truncate bool, fn Iterator) {
t := db.newReadOnlyTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, device, nil)[:keyPrefixLen+keyFolderLen+keyDeviceLen]), nil)
dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, device, prefix)[:1+64+32+len(prefix)]), nil)
defer dbi.Release()
for dbi.Next() {
// The iterator function may keep a reference to the unmarshalled
// struct, which in turn references the buffer it was unmarshalled
// from. dbi.Value() just returns an internal slice that it reuses, so
// we need to copy it.
f, err := unmarshalTrunc(append([]byte{}, dbi.Value()...), truncate)
f, err := unmarshalTrunc(dbi.Value(), truncate)
if err != nil {
panic(err)
}
@@ -282,20 +255,19 @@ func (db *Instance) withHave(folder, device []byte, truncate bool, fn Iterator)
}
func (db *Instance) withAllFolderTruncated(folder []byte, fn func(device []byte, f FileInfoTruncated) bool) {
start := db.deviceKey(folder, nil, nil) // before all folder/device files
limit := db.deviceKey(folder, protocol.LocalDeviceID[:], []byte{0xff, 0xff, 0xff, 0xff}) // after all folder/device files
t := db.newReadWriteTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, nil, nil)[:keyPrefixLen+keyFolderLen]), nil)
dbi := t.NewIterator(&util.Range{Start: start, Limit: limit}, nil)
defer dbi.Release()
for dbi.Next() {
device := db.deviceKeyDevice(dbi.Key())
var f FileInfoTruncated
// The iterator function may keep a reference to the unmarshalled
// struct, which in turn references the buffer it was unmarshalled
// from. dbi.Value() just returns an internal slice that it reuses, so
// we need to copy it.
err := f.UnmarshalXDR(append([]byte{}, dbi.Value()...))
err := f.UnmarshalXDR(dbi.Value())
if err != nil {
panic(err)
}
@@ -384,10 +356,7 @@ func (db *Instance) withGlobal(folder, prefix []byte, truncate bool, fn Iterator
l.Debugf("vl.versions[0].device: %x", vl.versions[0].device)
l.Debugf("name: %q (%x)", name, name)
l.Debugf("fk: %q", fk)
l.Debugf("fk: %x %x %x",
fk[keyPrefixLen:keyPrefixLen+keyFolderLen],
fk[keyPrefixLen+keyFolderLen:keyPrefixLen+keyFolderLen+keyDeviceLen],
fk[keyPrefixLen+keyFolderLen+keyDeviceLen:])
l.Debugf("fk: %x %x %x", fk[1:1+64], fk[1+64:1+64+32], fk[1+64+32:])
panic(err)
}
@@ -431,10 +400,13 @@ func (db *Instance) availability(folder, file []byte) []protocol.DeviceID {
}
func (db *Instance) withNeed(folder, device []byte, truncate bool, fn Iterator) {
start := db.globalKey(folder, nil)
limit := db.globalKey(folder, []byte{0xff, 0xff, 0xff, 0xff})
t := db.newReadOnlyTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.globalKey(folder, nil)[:keyPrefixLen+keyFolderLen]), nil)
dbi := t.NewIterator(&util.Range{Start: start, Limit: limit}, nil)
defer dbi.Release()
var fk []byte
@@ -571,7 +543,9 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
t := db.newReadWriteTransaction()
defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.globalKey(folder, nil)[:keyPrefixLen+keyFolderLen]), nil)
start := db.globalKey(folder, nil)
limit := db.globalKey(folder, []byte{0xff, 0xff, 0xff, 0xff})
dbi := t.NewIterator(&util.Range{Start: start, Limit: limit}, nil)
defer dbi.Release()
var fk []byte
@@ -621,72 +595,71 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
// deviceKey returns a byte slice encoding the following information:
// keyTypeDevice (1 byte)
// folder (4 bytes)
// device (4 bytes)
// folder (64 bytes)
// device (32 bytes)
// name (variable size)
func (db *Instance) deviceKey(folder, device, file []byte) []byte {
return db.deviceKeyInto(nil, folder, device, file)
}
func (db *Instance) deviceKeyInto(k []byte, folder, device, file []byte) []byte {
reqLen := keyPrefixLen + keyFolderLen + keyDeviceLen + len(file)
reqLen := 1 + 64 + 32 + len(file)
if len(k) < reqLen {
k = make([]byte, reqLen)
}
k[0] = KeyTypeDevice
binary.BigEndian.PutUint32(k[keyPrefixLen:], db.folderIdx.ID(folder))
binary.BigEndian.PutUint32(k[keyPrefixLen+keyFolderLen:], db.deviceIdx.ID(device))
copy(k[keyPrefixLen+keyFolderLen+keyDeviceLen:], []byte(file))
if len(folder) > 64 {
panic("folder name too long")
}
copy(k[1:], []byte(folder))
copy(k[1+64:], device[:])
copy(k[1+64+32:], []byte(file))
return k[:reqLen]
}
// deviceKeyName returns the device ID from the key
func (db *Instance) deviceKeyName(key []byte) []byte {
return key[keyPrefixLen+keyFolderLen+keyDeviceLen:]
return key[1+64+32:]
}
// deviceKeyFolder returns the folder name from the key
func (db *Instance) deviceKeyFolder(key []byte) []byte {
folder, ok := db.folderIdx.Val(binary.BigEndian.Uint32(key[keyPrefixLen:]))
if !ok {
panic("bug: lookup of nonexistent folder ID")
folder := key[1 : 1+64]
izero := bytes.IndexByte(folder, 0)
if izero < 0 {
return folder
}
return folder
return folder[:izero]
}
// deviceKeyDevice returns the device ID from the key
func (db *Instance) deviceKeyDevice(key []byte) []byte {
device, ok := db.deviceIdx.Val(binary.BigEndian.Uint32(key[keyPrefixLen+keyFolderLen:]))
if !ok {
panic("bug: lookup of nonexistent device ID")
}
return device
return key[1+64 : 1+64+32]
}
// globalKey returns a byte slice encoding the following information:
// keyTypeGlobal (1 byte)
// folder (4 bytes)
// folder (64 bytes)
// name (variable size)
func (db *Instance) globalKey(folder, file []byte) []byte {
k := make([]byte, keyPrefixLen+keyFolderLen+len(file))
k := make([]byte, 1+64+len(file))
k[0] = KeyTypeGlobal
binary.BigEndian.PutUint32(k[keyPrefixLen:], db.folderIdx.ID(folder))
copy(k[keyPrefixLen+keyFolderLen:], []byte(file))
if len(folder) > 64 {
panic("folder name too long")
}
copy(k[1:], []byte(folder))
copy(k[1+64:], []byte(file))
return k
}
// globalKeyName returns the filename from the key
func (db *Instance) globalKeyName(key []byte) []byte {
return key[keyPrefixLen+keyFolderLen:]
return key[1+64:]
}
// globalKeyFolder returns the folder name from the key
func (db *Instance) globalKeyFolder(key []byte) []byte {
folder, ok := db.folderIdx.Val(binary.BigEndian.Uint32(key[keyPrefixLen:]))
if !ok {
panic("bug: lookup of nonexistent folder ID")
folder := key[1 : 1+64]
izero := bytes.IndexByte(folder, 0)
if izero < 0 {
return folder
}
return folder
return folder[:izero]
}
func unmarshalTrunc(bs []byte, truncate bool) (FileIntf, error) {
@@ -716,132 +689,3 @@ func leveldbIsCorrupted(err error) bool {
return false
}
// checkConvertDatabase tries to convert an existing old (v0.11) database to
// new (v0.13) format.
func checkConvertDatabase(dbFile string) error {
oldLoc := filepath.Join(filepath.Dir(dbFile), "index-v0.11.0.db")
if _, err := os.Stat(oldLoc); os.IsNotExist(err) {
// The old database file does not exist; that's ok, continue as if
// everything succeeded.
return nil
} else if err != nil {
// Any other error is weird.
return err
}
// There exists a database in the old format. We run a one time
// conversion from old to new.
fromDb, err := leveldb.OpenFile(oldLoc, nil)
if err != nil {
return err
}
toDb, err := leveldb.OpenFile(dbFile, nil)
if err != nil {
return err
}
err = convertKeyFormat(fromDb, toDb)
if err != nil {
return err
}
err = toDb.Close()
if err != nil {
return err
}
// We've done this one, we don't want to do it again (if the user runs
// -reset or so). We don't care too much about errors any more at this stage.
fromDb.Close()
osutil.Rename(oldLoc, oldLoc+".converted")
return nil
}
// A smallIndex is an in memory bidirectional []byte to uint32 map. It gives
// fast lookups in both directions and persists to the database. Don't use for
// storing more items than fit comfortably in RAM.
type smallIndex struct {
db *Instance
prefix []byte
id2val map[uint32]string
val2id map[string]uint32
nextID uint32
mut sync.Mutex
}
func newSmallIndex(db *Instance, prefix []byte) *smallIndex {
idx := &smallIndex{
db: db,
prefix: prefix,
id2val: make(map[uint32]string),
val2id: make(map[string]uint32),
mut: sync.NewMutex(),
}
idx.load()
return idx
}
// load iterates over the prefix space in the database and populates the in
// memory maps.
func (i *smallIndex) load() {
tr := i.db.newReadOnlyTransaction()
it := tr.NewIterator(util.BytesPrefix(i.prefix), nil)
for it.Next() {
val := string(it.Value())
id := binary.BigEndian.Uint32(it.Key()[len(i.prefix):])
i.id2val[id] = val
i.val2id[val] = id
if id >= i.nextID {
i.nextID = id + 1
}
}
it.Release()
tr.close()
}
// ID returns the index number for the given byte slice, allocating a new one
// and persisting this to the database if necessary.
func (i *smallIndex) ID(val []byte) uint32 {
i.mut.Lock()
// intentionally avoiding defer here as we want this call to be as fast as
// possible in the general case (folder ID already exists). The map lookup
// with the conversion of []byte to string is compiler optimized to not
// copy the []byte, which is why we don't assign it to a temp variable
// here.
if id, ok := i.val2id[string(val)]; ok {
i.mut.Unlock()
return id
}
id := i.nextID
i.nextID++
valStr := string(val)
i.val2id[valStr] = id
i.id2val[id] = valStr
key := make([]byte, len(i.prefix)+8) // prefix plus uint32 id
copy(key, i.prefix)
binary.BigEndian.PutUint32(key[len(i.prefix):], id)
i.db.Put(key, val, nil)
i.mut.Unlock()
return id
}
// Val returns the value for the given index number, or (nil, false) if there
// is no such index number.
func (i *smallIndex) Val(id uint32) ([]byte, bool) {
i.mut.Lock()
val, ok := i.id2val[id]
i.mut.Unlock()
if !ok {
return nil, false
}
return []byte(val), true
}

View File

@@ -16,9 +16,7 @@ func TestDeviceKey(t *testing.T) {
dev := []byte("device67890123456789012345678901")
name := []byte("name")
db := OpenMemory()
db.folderIdx.ID(fld)
db.deviceIdx.ID(dev)
db := &Instance{}
key := db.deviceKey(fld, dev, name)
@@ -40,8 +38,7 @@ func TestGlobalKey(t *testing.T) {
fld := []byte("folder6789012345678901234567890123456789012345678901234567890123")
name := []byte("name")
db := OpenMemory()
db.folderIdx.ID(fld)
db := &Instance{}
key := db.globalKey(fld, name)

View File

@@ -5,6 +5,9 @@
package db
import (
"bytes"
"io"
"github.com/calmh/xdr"
)
@@ -19,8 +22,10 @@ fileVersion Structure:
\ Vector Structure \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length of device |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ device (length + padded data) \
\ device (variable length) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -32,15 +37,13 @@ struct fileVersion {
*/
func (o fileVersion) XDRSize() int {
return o.version.XDRSize() +
4 + len(o.device) + xdr.Padding(len(o.device))
func (o fileVersion) EncodeXDR(w io.Writer) (int, error) {
var xw = xdr.NewWriter(w)
return o.EncodeXDRInto(xw)
}
func (o fileVersion) MarshalXDR() ([]byte, error) {
buf := make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
return o.AppendXDR(make([]byte, 0, 128))
}
func (o fileVersion) MustMarshalXDR() []byte {
@@ -51,22 +54,37 @@ func (o fileVersion) MustMarshalXDR() []byte {
return bs
}
func (o fileVersion) MarshalXDRInto(m *xdr.Marshaller) error {
if err := o.version.MarshalXDRInto(m); err != nil {
return err
func (o fileVersion) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
_, err := o.EncodeXDRInto(xw)
return []byte(aw), err
}
func (o fileVersion) EncodeXDRInto(xw *xdr.Writer) (int, error) {
_, err := o.version.EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
m.MarshalBytes(o.device)
return m.Error
xw.WriteBytes(o.device)
return xw.Tot(), xw.Error()
}
func (o *fileVersion) DecodeXDR(r io.Reader) error {
xr := xdr.NewReader(r)
return o.DecodeXDRFrom(xr)
}
func (o *fileVersion) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}
func (o *fileVersion) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
(&o.version).UnmarshalXDRFrom(u)
o.device = u.UnmarshalBytes()
return u.Error
func (o *fileVersion) DecodeXDRFrom(xr *xdr.Reader) error {
(&o.version).DecodeXDRFrom(xr)
o.device = xr.ReadBytes()
return xr.Error()
}
/*
@@ -90,14 +108,13 @@ struct versionList {
*/
func (o versionList) XDRSize() int {
return 4 + xdr.SizeOfSlice(o.versions)
func (o versionList) EncodeXDR(w io.Writer) (int, error) {
var xw = xdr.NewWriter(w)
return o.EncodeXDRInto(xw)
}
func (o versionList) MarshalXDR() ([]byte, error) {
buf := make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
return o.AppendXDR(make([]byte, 0, 128))
}
func (o versionList) MustMarshalXDR() []byte {
@@ -108,35 +125,43 @@ func (o versionList) MustMarshalXDR() []byte {
return bs
}
func (o versionList) MarshalXDRInto(m *xdr.Marshaller) error {
m.MarshalUint32(uint32(len(o.versions)))
func (o versionList) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
_, err := o.EncodeXDRInto(xw)
return []byte(aw), err
}
func (o versionList) EncodeXDRInto(xw *xdr.Writer) (int, error) {
xw.WriteUint32(uint32(len(o.versions)))
for i := range o.versions {
if err := o.versions[i].MarshalXDRInto(m); err != nil {
return err
_, err := o.versions[i].EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
}
return m.Error
return xw.Tot(), xw.Error()
}
func (o *versionList) DecodeXDR(r io.Reader) error {
xr := xdr.NewReader(r)
return o.DecodeXDRFrom(xr)
}
func (o *versionList) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}
func (o *versionList) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
_versionsSize := int(u.UnmarshalUint32())
func (o *versionList) DecodeXDRFrom(xr *xdr.Reader) error {
_versionsSize := int(xr.ReadUint32())
if _versionsSize < 0 {
return xdr.ElementSizeExceeded("versions", _versionsSize, 0)
} else if _versionsSize == 0 {
o.versions = nil
} else {
if _versionsSize <= len(o.versions) {
o.versions = o.versions[:_versionsSize]
} else {
o.versions = make([]fileVersion, _versionsSize)
}
for i := range o.versions {
(&o.versions[i]).UnmarshalXDRFrom(u)
}
}
return u.Error
o.versions = make([]fileVersion, _versionsSize)
for i := range o.versions {
(&o.versions[i]).DecodeXDRFrom(xr)
}
return xr.Error()
}

View File

@@ -97,7 +97,7 @@ func NewFileSet(folder string, db *Instance) *FileSet {
localVersion: make(map[protocol.DeviceID]int64),
folder: folder,
db: db,
blockmap: NewBlockMap(db, db.folderIdx.ID([]byte(folder))),
blockmap: NewBlockMap(db, folder),
mutex: sync.NewMutex(),
}
@@ -171,14 +171,18 @@ func (s *FileSet) WithNeedTruncated(device protocol.DeviceID, fn Iterator) {
func (s *FileSet) WithHave(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithHave(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], false, nativeFileIterator(fn))
s.db.withHave([]byte(s.folder), device[:], nil, false, nativeFileIterator(fn))
}
func (s *FileSet) WithHaveTruncated(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithHaveTruncated(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], true, nativeFileIterator(fn))
s.db.withHave([]byte(s.folder), device[:], nil, true, nativeFileIterator(fn))
}
func (s *FileSet) WithPrefixedHaveTruncated(device protocol.DeviceID, prefix string, fn Iterator) {
l.Debugf("%s WithPrefixedHaveTruncated(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], []byte(prefix), true, nativeFileIterator(fn))
}
func (s *FileSet) WithGlobal(fn Iterator) {
l.Debugf("%s WithGlobal()", s.folder)
s.db.withGlobal([]byte(s.folder), nil, false, nativeFileIterator(fn))
@@ -244,7 +248,7 @@ func DropFolder(db *Instance, folder string) {
db.dropFolder([]byte(folder))
bm := &BlockMap{
db: db,
folder: db.folderIdx.ID([]byte(folder)),
folder: folder,
}
bm.Drop()
NewVirtualMtimeRepo(db, folder).Drop()

View File

Binary file not shown.

View File

@@ -7,6 +7,8 @@
package db
import (
"bytes"
"github.com/calmh/xdr"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -16,32 +18,32 @@ type FileInfoTruncated struct {
}
func (o *FileInfoTruncated) UnmarshalXDR(bs []byte) error {
return o.UnmarshalXDRFrom(&xdr.Unmarshaller{Data: bs})
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}
func (o *FileInfoTruncated) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
o.Name = u.UnmarshalStringMax(8192)
o.Flags = u.UnmarshalUint32()
o.Modified = int64(u.UnmarshalUint64())
(&o.Version).UnmarshalXDRFrom(u)
o.LocalVersion = int64(u.UnmarshalUint64())
_BlocksSize := int(u.UnmarshalUint32())
func (o *FileInfoTruncated) DecodeXDRFrom(xr *xdr.Reader) error {
o.Name = xr.ReadStringMax(8192)
o.Flags = xr.ReadUint32()
o.Modified = int64(xr.ReadUint64())
(&o.Version).DecodeXDRFrom(xr)
o.LocalVersion = int64(xr.ReadUint64())
_BlocksSize := int(xr.ReadUint32())
if _BlocksSize < 0 {
return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 1000000)
} else if _BlocksSize == 0 {
o.Blocks = nil
} else {
if _BlocksSize > 1000000 {
return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 1000000)
}
for i := 0; i < _BlocksSize; i++ {
size := int64(u.UnmarshalUint32())
o.CachedSize += size
u.UnmarshalBytes()
}
}
if _BlocksSize > 1000000 {
return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 1000000)
}
return u.Error
buf := make([]byte, 64)
for i := 0; i < _BlocksSize; i++ {
size := xr.ReadUint32()
o.CachedSize += int64(size)
xr.ReadBytesMaxInto(64, buf)
}
return xr.Error()
}
func BlocksToSize(num int) int64 {

View File

@@ -7,7 +7,6 @@
package db
import (
"encoding/binary"
"fmt"
"time"
)
@@ -25,12 +24,10 @@ type VirtualMtimeRepo struct {
}
func NewVirtualMtimeRepo(ldb *Instance, folder string) *VirtualMtimeRepo {
var prefix [5]byte // key type + 4 bytes folder idx number
prefix[0] = KeyTypeVirtualMtime
binary.BigEndian.PutUint32(prefix[1:], ldb.folderIdx.ID([]byte(folder)))
prefix := string(KeyTypeVirtualMtime) + folder
return &VirtualMtimeRepo{
ns: NewNamespacedKV(ldb, string(prefix[:])),
ns: NewNamespacedKV(ldb, prefix),
}
}

View File

@@ -5,6 +5,9 @@
package discover
import (
"bytes"
"io"
"github.com/calmh/xdr"
)
@@ -37,16 +40,13 @@ struct Announce {
*/
func (o Announce) XDRSize() int {
return 4 +
o.This.XDRSize() +
4 + xdr.SizeOfSlice(o.Extra)
func (o Announce) EncodeXDR(w io.Writer) (int, error) {
var xw = xdr.NewWriter(w)
return o.EncodeXDRInto(xw)
}
func (o Announce) MarshalXDR() ([]byte, error) {
buf := make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Announce) MustMarshalXDR() []byte {
@@ -57,49 +57,58 @@ func (o Announce) MustMarshalXDR() []byte {
return bs
}
func (o Announce) MarshalXDRInto(m *xdr.Marshaller) error {
m.MarshalUint32(o.Magic)
if err := o.This.MarshalXDRInto(m); err != nil {
return err
func (o Announce) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
_, err := o.EncodeXDRInto(xw)
return []byte(aw), err
}
func (o Announce) EncodeXDRInto(xw *xdr.Writer) (int, error) {
xw.WriteUint32(o.Magic)
_, err := o.This.EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
if l := len(o.Extra); l > 16 {
return xdr.ElementSizeExceeded("Extra", l, 16)
return xw.Tot(), xdr.ElementSizeExceeded("Extra", l, 16)
}
m.MarshalUint32(uint32(len(o.Extra)))
xw.WriteUint32(uint32(len(o.Extra)))
for i := range o.Extra {
if err := o.Extra[i].MarshalXDRInto(m); err != nil {
return err
_, err := o.Extra[i].EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
}
return m.Error
return xw.Tot(), xw.Error()
}
func (o *Announce) DecodeXDR(r io.Reader) error {
xr := xdr.NewReader(r)
return o.DecodeXDRFrom(xr)
}
func (o *Announce) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}
func (o *Announce) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
o.Magic = u.UnmarshalUint32()
(&o.This).UnmarshalXDRFrom(u)
_ExtraSize := int(u.UnmarshalUint32())
func (o *Announce) DecodeXDRFrom(xr *xdr.Reader) error {
o.Magic = xr.ReadUint32()
(&o.This).DecodeXDRFrom(xr)
_ExtraSize := int(xr.ReadUint32())
if _ExtraSize < 0 {
return xdr.ElementSizeExceeded("Extra", _ExtraSize, 16)
} else if _ExtraSize == 0 {
o.Extra = nil
} else {
if _ExtraSize > 16 {
return xdr.ElementSizeExceeded("Extra", _ExtraSize, 16)
}
if _ExtraSize <= len(o.Extra) {
o.Extra = o.Extra[:_ExtraSize]
} else {
o.Extra = make([]Device, _ExtraSize)
}
for i := range o.Extra {
(&o.Extra[i]).UnmarshalXDRFrom(u)
}
}
return u.Error
if _ExtraSize > 16 {
return xdr.ElementSizeExceeded("Extra", _ExtraSize, 16)
}
o.Extra = make([]Device, _ExtraSize)
for i := range o.Extra {
(&o.Extra[i]).DecodeXDRFrom(xr)
}
return xr.Error()
}
/*
@@ -109,8 +118,10 @@ Device Structure:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length of ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ ID (length + padded data) \
\ ID (variable length) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Number of Addresses |
@@ -135,16 +146,13 @@ struct Device {
*/
func (o Device) XDRSize() int {
return 4 + len(o.ID) + xdr.Padding(len(o.ID)) +
4 + xdr.SizeOfSlice(o.Addresses) +
4 + xdr.SizeOfSlice(o.Relays)
func (o Device) EncodeXDR(w io.Writer) (int, error) {
var xw = xdr.NewWriter(w)
return o.EncodeXDRInto(xw)
}
func (o Device) MarshalXDR() ([]byte, error) {
buf := make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Device) MustMarshalXDR() []byte {
@@ -155,75 +163,77 @@ func (o Device) MustMarshalXDR() []byte {
return bs
}
func (o Device) MarshalXDRInto(m *xdr.Marshaller) error {
func (o Device) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
_, err := o.EncodeXDRInto(xw)
return []byte(aw), err
}
func (o Device) EncodeXDRInto(xw *xdr.Writer) (int, error) {
if l := len(o.ID); l > 32 {
return xdr.ElementSizeExceeded("ID", l, 32)
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32)
}
m.MarshalBytes(o.ID)
xw.WriteBytes(o.ID)
if l := len(o.Addresses); l > 16 {
return xdr.ElementSizeExceeded("Addresses", l, 16)
return xw.Tot(), xdr.ElementSizeExceeded("Addresses", l, 16)
}
m.MarshalUint32(uint32(len(o.Addresses)))
xw.WriteUint32(uint32(len(o.Addresses)))
for i := range o.Addresses {
if err := o.Addresses[i].MarshalXDRInto(m); err != nil {
return err
_, err := o.Addresses[i].EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
}
if l := len(o.Relays); l > 16 {
return xdr.ElementSizeExceeded("Relays", l, 16)
return xw.Tot(), xdr.ElementSizeExceeded("Relays", l, 16)
}
m.MarshalUint32(uint32(len(o.Relays)))
xw.WriteUint32(uint32(len(o.Relays)))
for i := range o.Relays {
if err := o.Relays[i].MarshalXDRInto(m); err != nil {
return err
_, err := o.Relays[i].EncodeXDRInto(xw)
if err != nil {
return xw.Tot(), err
}
}
return m.Error
return xw.Tot(), xw.Error()
}
func (o *Device) DecodeXDR(r io.Reader) error {
xr := xdr.NewReader(r)
return o.DecodeXDRFrom(xr)
}
func (o *Device) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}
func (o *Device) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
o.ID = u.UnmarshalBytesMax(32)
_AddressesSize := int(u.UnmarshalUint32())
func (o *Device) DecodeXDRFrom(xr *xdr.Reader) error {
o.ID = xr.ReadBytesMax(32)
_AddressesSize := int(xr.ReadUint32())
if _AddressesSize < 0 {
return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 16)
} else if _AddressesSize == 0 {
o.Addresses = nil
} else {
if _AddressesSize > 16 {
return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 16)
}
if _AddressesSize <= len(o.Addresses) {
o.Addresses = o.Addresses[:_AddressesSize]
} else {
o.Addresses = make([]Address, _AddressesSize)
}
for i := range o.Addresses {
(&o.Addresses[i]).UnmarshalXDRFrom(u)
}
}
_RelaysSize := int(u.UnmarshalUint32())
if _AddressesSize > 16 {
return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 16)
}
o.Addresses = make([]Address, _AddressesSize)
for i := range o.Addresses {
(&o.Addresses[i]).DecodeXDRFrom(xr)
}
_RelaysSize := int(xr.ReadUint32())
if _RelaysSize < 0 {
return xdr.ElementSizeExceeded("Relays", _RelaysSize, 16)
} else if _RelaysSize == 0 {
o.Relays = nil
} else {
if _RelaysSize > 16 {
return xdr.ElementSizeExceeded("Relays", _RelaysSize, 16)
}
if _RelaysSize <= len(o.Relays) {
o.Relays = o.Relays[:_RelaysSize]
} else {
o.Relays = make([]Relay, _RelaysSize)
}
for i := range o.Relays {
(&o.Relays[i]).UnmarshalXDRFrom(u)
}
}
return u.Error
if _RelaysSize > 16 {
return xdr.ElementSizeExceeded("Relays", _RelaysSize, 16)
}
o.Relays = make([]Relay, _RelaysSize)
for i := range o.Relays {
(&o.Relays[i]).DecodeXDRFrom(xr)
}
return xr.Error()
}
/*
@@ -233,8 +243,10 @@ Address Structure:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length of URL |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ URL (length + padded data) \
\ URL (variable length) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -245,14 +257,13 @@ struct Address {
*/
func (o Address) XDRSize() int {
return 4 + len(o.URL) + xdr.Padding(len(o.URL))
func (o Address) EncodeXDR(w io.Writer) (int, error) {
var xw = xdr.NewWriter(w)
return o.EncodeXDRInto(xw)
}
func (o Address) MarshalXDR() ([]byte, error) {
buf := make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Address) MustMarshalXDR() []byte {
@@ -263,21 +274,35 @@ func (o Address) MustMarshalXDR() []byte {
return bs
}
func (o Address) MarshalXDRInto(m *xdr.Marshaller) error {
func (o Address) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
_, err := o.EncodeXDRInto(xw)
return []byte(aw), err
}
func (o Address) EncodeXDRInto(xw *xdr.Writer) (int, error) {
if l := len(o.URL); l > 2083 {
return xdr.ElementSizeExceeded("URL", l, 2083)
return xw.Tot(), xdr.ElementSizeExceeded("URL", l, 2083)
}
m.MarshalString(o.URL)
return m.Error
xw.WriteString(o.URL)
return xw.Tot(), xw.Error()
}
func (o *Address) DecodeXDR(r io.Reader) error {
xr := xdr.NewReader(r)
return o.DecodeXDRFrom(xr)
}
func (o *Address) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}
func (o *Address) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
o.URL = u.UnmarshalStringMax(2083)
return u.Error
func (o *Address) DecodeXDRFrom(xr *xdr.Reader) error {
o.URL = xr.ReadStringMax(2083)
return xr.Error()
}
/*
@@ -287,8 +312,10 @@ Relay Structure:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length of URL |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ URL (length + padded data) \
\ URL (variable length) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Latency |
@@ -302,14 +329,13 @@ struct Relay {
*/
func (o Relay) XDRSize() int {
return 4 + len(o.URL) + xdr.Padding(len(o.URL)) + 4
func (o Relay) EncodeXDR(w io.Writer) (int, error) {
var xw = xdr.NewWriter(w)
return o.EncodeXDRInto(xw)
}
func (o Relay) MarshalXDR() ([]byte, error) {
buf := make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Relay) MustMarshalXDR() []byte {
@@ -320,21 +346,35 @@ func (o Relay) MustMarshalXDR() []byte {
return bs
}
func (o Relay) MarshalXDRInto(m *xdr.Marshaller) error {
func (o Relay) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
_, err := o.EncodeXDRInto(xw)
return []byte(aw), err
}
func (o Relay) EncodeXDRInto(xw *xdr.Writer) (int, error) {
if l := len(o.URL); l > 2083 {
return xdr.ElementSizeExceeded("URL", l, 2083)
return xw.Tot(), xdr.ElementSizeExceeded("URL", l, 2083)
}
m.MarshalString(o.URL)
m.MarshalUint32(uint32(o.Latency))
return m.Error
xw.WriteString(o.URL)
xw.WriteUint32(uint32(o.Latency))
return xw.Tot(), xw.Error()
}
func (o *Relay) DecodeXDR(r io.Reader) error {
xr := xdr.NewReader(r)
return o.DecodeXDRFrom(xr)
}
func (o *Relay) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
var br = bytes.NewReader(bs)
var xr = xdr.NewReader(br)
return o.DecodeXDRFrom(xr)
}
func (o *Relay) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
o.URL = u.UnmarshalStringMax(2083)
o.Latency = int32(u.UnmarshalUint32())
return u.Error
func (o *Relay) DecodeXDRFrom(xr *xdr.Reader) error {
o.URL = xr.ReadStringMax(2083)
o.Latency = int32(xr.ReadUint32())
return xr.Error()
}

View File

@@ -23,6 +23,7 @@ const (
LevelDebug LogLevel = iota
LevelVerbose
LevelInfo
LevelOK
LevelWarn
LevelFatal
NumLevels
@@ -41,6 +42,8 @@ type Logger interface {
Verbosef(format string, vals ...interface{})
Infoln(vals ...interface{})
Infof(format string, vals ...interface{})
Okln(vals ...interface{})
Okf(format string, vals ...interface{})
Warnln(vals ...interface{})
Warnf(format string, vals ...interface{})
Fatalln(vals ...interface{})
@@ -162,6 +165,24 @@ func (l *logger) Infof(format string, vals ...interface{}) {
l.callHandlers(LevelInfo, s)
}
// Okln logs a line with an OK prefix.
func (l *logger) Okln(vals ...interface{}) {
l.mut.Lock()
defer l.mut.Unlock()
s := fmt.Sprintln(vals...)
l.logger.Output(2, "OK: "+s)
l.callHandlers(LevelOK, s)
}
// Okf logs a formatted line with an OK prefix.
func (l *logger) Okf(format string, vals ...interface{}) {
l.mut.Lock()
defer l.mut.Unlock()
s := fmt.Sprintf(format, vals...)
l.logger.Output(2, "OK: "+s)
l.callHandlers(LevelOK, s)
}
// Warnln logs a formatted line with a WARNING prefix.
func (l *logger) Warnln(vals ...interface{}) {
l.mut.Lock()

View File

@@ -19,6 +19,8 @@ func TestAPI(t *testing.T) {
l.AddHandler(LevelDebug, checkFunc(t, LevelDebug, &debug))
info := 0
l.AddHandler(LevelInfo, checkFunc(t, LevelInfo, &info))
ok := 0
l.AddHandler(LevelOK, checkFunc(t, LevelOK, &ok))
warn := 0
l.AddHandler(LevelWarn, checkFunc(t, LevelWarn, &warn))
@@ -26,15 +28,20 @@ func TestAPI(t *testing.T) {
l.Debugln("test", 0)
l.Infof("test %d", 1)
l.Infoln("test", 1)
l.Okf("test %d", 2)
l.Okln("test", 2)
l.Warnf("test %d", 3)
l.Warnln("test", 3)
if debug != 6 {
if debug != 8 {
t.Errorf("Debug handler called %d != 8 times", debug)
}
if info != 4 {
if info != 6 {
t.Errorf("Info handler called %d != 6 times", info)
}
if ok != 4 {
t.Errorf("Ok handler called %d != 4 times", ok)
}
if warn != 2 {
t.Errorf("Warn handler called %d != 2 times", warn)
}

View File

@@ -18,6 +18,7 @@ import (
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
stdsync "sync"
"time"
@@ -189,7 +190,7 @@ func (m *Model) StartFolderRW(folder string) {
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
m.fmut.Unlock()
l.Infoln("Ready to synchronize", folder, "(read-write)")
l.Okln("Ready to synchronize", folder, "(read-write)")
}
func (m *Model) warnAboutOverwritingProtectedFiles(folder string) {
@@ -241,7 +242,7 @@ func (m *Model) StartFolderRO(folder string) {
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
m.fmut.Unlock()
l.Infoln("Ready to synchronize", folder, "(read only; no external updates accepted)")
l.Okln("Ready to synchronize", folder, "(read only; no external updates accepted)")
}
func (m *Model) RemoveFolder(folder string) {
@@ -1318,28 +1319,13 @@ func (m *Model) internalScanFolderSubs(folder string, subs []string) error {
return err
}
// Required to make sure that we start indexing at a directory we're already
// aware off.
var unifySubs []string
nextSub:
for _, sub := range subs {
for sub != "" && sub != ".stfolder" && sub != ".stignore" {
if _, ok = fs.Get(protocol.LocalDeviceID, sub); ok {
break
}
sub = filepath.Dir(sub)
if sub == "." || sub == string(filepath.Separator) {
sub = ""
}
}
for _, us := range unifySubs {
if strings.HasPrefix(sub, us) {
continue nextSub
}
}
unifySubs = append(unifySubs, sub)
}
subs = unifySubs
// Clean the list of subitems to ensure that we start at a known
// directory, and don't scan subdirectories of things we've already
// scanned.
subs = unifySubs(subs, func(f string) bool {
_, ok := fs.Get(protocol.LocalDeviceID, f)
return ok
})
// The cancel channel is closed whenever we return (such as from an error),
// to signal the potentially still running walker to stop.
@@ -1405,76 +1391,69 @@ nextSub:
m.updateLocals(folder, batch)
}
if len(subs) == 0 {
// If we have no specific subdirectories to traverse, set it to one
// empty prefix so we traverse the entire folder contents once.
subs = []string{""}
}
// Do a scan of the database for each prefix, to check for deleted files.
batch = batch[:0]
// TODO: We should limit the Have scanning to start at sub
seenPrefix := false
var iterError error
fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi db.FileIntf) bool {
f := fi.(db.FileInfoTruncated)
hasPrefix := len(subs) == 0
for _, sub := range subs {
if strings.HasPrefix(f.Name, sub) {
hasPrefix = true
break
}
}
// Return true so that we keep iterating, until we get to the part
// of the tree we are interested in. Then return false so we stop
// iterating when we've passed the end of the subtree.
if !hasPrefix {
return !seenPrefix
}
for _, sub := range subs {
var iterError error
seenPrefix = true
if !f.IsDeleted() {
if f.IsInvalid() {
return true
}
if len(batch) == batchSizeFiles {
if err := m.CheckFolderHealth(folder); err != nil {
iterError = err
return false
fs.WithPrefixedHaveTruncated(protocol.LocalDeviceID, sub, func(fi db.FileIntf) bool {
f := fi.(db.FileInfoTruncated)
if !f.IsDeleted() {
if f.IsInvalid() {
return true
}
m.updateLocals(folder, batch)
batch = batch[:0]
}
if ignores.Match(f.Name) || symlinkInvalid(folder, f) {
// File has been ignored or an unsupported symlink. Set invalid bit.
l.Debugln("setting invalid bit on ignored", f)
nf := protocol.FileInfo{
Name: f.Name,
Flags: f.Flags | protocol.FlagInvalid,
Modified: f.Modified,
Version: f.Version, // The file is still the same, so don't bump version
if len(batch) == batchSizeFiles {
if err := m.CheckFolderHealth(folder); err != nil {
iterError = err
return false
}
m.updateLocals(folder, batch)
batch = batch[:0]
}
batch = append(batch, nf)
} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
// File has been deleted.
// We don't specifically verify that the error is
// os.IsNotExist because there is a corner case when a
// directory is suddenly transformed into a file. When that
// happens, files that were in the directory (that is now a
// file) are deleted but will return a confusing error ("not a
// directory") when we try to Lstat() them.
if ignores.Match(f.Name) || symlinkInvalid(folder, f) {
// File has been ignored or an unsupported symlink. Set invalid bit.
l.Debugln("setting invalid bit on ignored", f)
nf := protocol.FileInfo{
Name: f.Name,
Flags: f.Flags | protocol.FlagInvalid,
Modified: f.Modified,
Version: f.Version, // The file is still the same, so don't bump version
}
batch = append(batch, nf)
} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
// File has been deleted.
nf := protocol.FileInfo{
Name: f.Name,
Flags: f.Flags | protocol.FlagDeleted,
Modified: f.Modified,
Version: f.Version.Update(m.shortID),
// We don't specifically verify that the error is
// os.IsNotExist because there is a corner case when a
// directory is suddenly transformed into a file. When that
// happens, files that were in the directory (that is now a
// file) are deleted but will return a confusing error ("not a
// directory") when we try to Lstat() them.
nf := protocol.FileInfo{
Name: f.Name,
Flags: f.Flags | protocol.FlagDeleted,
Modified: f.Modified,
Version: f.Version.Update(m.shortID),
}
batch = append(batch, nf)
}
batch = append(batch, nf)
}
return true
})
if iterError != nil {
l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, iterError)
return iterError
}
return true
})
if iterError != nil {
l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, iterError)
return iterError
}
if err := m.CheckFolderHealth(folder); err != nil {
@@ -2082,3 +2061,42 @@ func stringSliceWithout(ss []string, s string) []string {
}
return ss
}
// unifySubs takes a list of files or directories and trims them down to
// themselves or the closest parent that exists() returns true for, while
// removing duplicates and subdirectories. That is, if we have foo/ in the
// list, we don't also need foo/bar/ because that's already covered.
func unifySubs(dirs []string, exists func(dir string) bool) []string {
var subs []string
// Trim each item to itself or its closest known parent
for _, sub := range dirs {
for sub != "" && sub != ".stfolder" && sub != ".stignore" {
if exists(sub) {
break
}
sub = filepath.Dir(sub)
if sub == "." || sub == string(filepath.Separator) {
// Shortcut. We are going to scan the full folder, so we can
// just return an empty list of subs at this point.
return nil
}
}
subs = append(subs, sub)
}
// Remove any paths that are already covered by their parent
sort.Strings(subs)
var cleaned []string
next:
for _, sub := range subs {
for _, existing := range cleaned {
if sub == existing || strings.HasPrefix(sub, existing+string(os.PathSeparator)) {
continue next
}
}
cleaned = append(cleaned, sub)
}
return cleaned
}

View File

@@ -15,6 +15,8 @@ import (
"net"
"os"
"path/filepath"
"reflect"
"runtime"
"strconv"
"testing"
"time"
@@ -1210,3 +1212,89 @@ func TestIgnoreDelete(t *testing.T) {
t.Fatal("foo should not be marked for deletion")
}
}
func TestUnifySubs(t *testing.T) {
cases := []struct {
in []string // input to unifySubs
exists []string // paths that exist in the database
out []string // expected output
}{
{
// trailing slashes are cleaned, known paths are just passed on
[]string{"foo/", "bar//"},
[]string{"foo", "bar"},
[]string{"bar", "foo"}, // the output is sorted
},
{
// "foo/bar" gets trimmed as it's covered by foo
[]string{"foo", "bar/", "foo/bar/"},
[]string{"foo", "bar"},
[]string{"bar", "foo"},
},
{
// "bar" gets trimmed to "" as it's unknown,
// "" gets simplified to the empty list
[]string{"foo", "bar", "foo/bar"},
[]string{"foo"},
nil,
},
{
// two independent known paths, both are kept
// "usr/lib" is not a prefix of "usr/libexec"
[]string{"usr/lib", "usr/libexec"},
[]string{"usr/lib", "usr/libexec"},
[]string{"usr/lib", "usr/libexec"},
},
{
// "usr/lib" is a prefix of "usr/lib/exec"
[]string{"usr/lib", "usr/lib/exec"},
[]string{"usr/lib", "usr/libexec"},
[]string{"usr/lib"},
},
{
// .stignore and .stfolder are special and are passed on
// verbatim even though they are unknown
[]string{".stfolder", ".stignore"},
[]string{},
[]string{".stfolder", ".stignore"},
},
{
// but the presense of something else unknown forces an actual
// scan
[]string{".stfolder", ".stignore", "foo/bar"},
[]string{},
nil,
},
}
if runtime.GOOS == "windows" {
// Fixup path separators
for i := range cases {
for j, p := range cases[i].in {
cases[i].in[j] = filepath.FromSlash(p)
}
for j, p := range cases[i].exists {
cases[i].exists[j] = filepath.FromSlash(p)
}
for j, p := range cases[i].out {
cases[i].out[j] = filepath.FromSlash(p)
}
}
}
for i, tc := range cases {
exists := func(f string) bool {
for _, e := range tc.exists {
if f == e {
return true
}
}
return false
}
out := unifySubs(tc.in, exists)
if !reflect.DeepEqual(tc.out, out) {
t.Errorf("Case %d failed; got %v, expected %v", i, out, tc.out)
}
}
}

View File

@@ -11,16 +11,15 @@ type header struct {
compression bool
}
func (h header) MarshalXDRInto(m *xdr.Marshaller) error {
v := encodeHeader(h)
m.MarshalUint32(v)
return m.Error
func (h header) encodeXDR(xw *xdr.Writer) (int, error) {
u := encodeHeader(h)
return xw.WriteUint32(u)
}
func (h *header) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
v := u.UnmarshalUint32()
*h = decodeHeader(v)
return u.Error
func (h *header) decodeXDR(xr *xdr.Reader) error {
u := xr.ReadUint32()
*h = decodeHeader(u)
return xr.Error()
}
func encodeHeader(h header) uint32 {

View File

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,6 @@ import (
"time"
lz4 "github.com/bkaradzic/go-lz4"
"github.com/calmh/xdr"
)
const (
@@ -131,7 +130,8 @@ type rawConnection struct {
pool sync.Pool
compression Compression
readerBuf []byte // used & reused by readMessage
rdbuf0 []byte // used & reused by readMessage
rdbuf1 []byte // used & reused by readMessage
}
type asyncResult struct {
@@ -146,8 +146,7 @@ type hdrMsg struct {
}
type encodable interface {
MarshalXDRInto(m *xdr.Marshaller) error
XDRSize() int
AppendXDR([]byte) ([]byte, error)
}
type isEofer interface {
@@ -375,14 +374,18 @@ func (c *rawConnection) readerLoop() (err error) {
}
func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
hdrBuf := make([]byte, 8)
_, err = io.ReadFull(c.cr, hdrBuf)
if cap(c.rdbuf0) < 8 {
c.rdbuf0 = make([]byte, 8)
} else {
c.rdbuf0 = c.rdbuf0[:8]
}
_, err = io.ReadFull(c.cr, c.rdbuf0)
if err != nil {
return
}
hdr = decodeHeader(binary.BigEndian.Uint32(hdrBuf[:4]))
msglen := int(binary.BigEndian.Uint32(hdrBuf[4:]))
hdr = decodeHeader(binary.BigEndian.Uint32(c.rdbuf0[0:4]))
msglen := int(binary.BigEndian.Uint32(c.rdbuf0[4:8]))
l.Debugf("read header %v (msglen=%d)", hdr, msglen)
@@ -396,40 +399,27 @@ func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
return
}
// c.readerBuf contains a buffer we can reuse. But once we've unmarshalled
// a message from the buffer we can't reuse it again as the unmarshalled
// message refers to the contents of the buffer. The only case we a buffer
// ends up in readerBuf for reuse is when the message is compressed, as we
// then decompress into a new buffer instead.
var msgBuf []byte
if cap(c.readerBuf) >= msglen {
// If we have a buffer ready in rdbuf we just use that.
msgBuf = c.readerBuf[:msglen]
if cap(c.rdbuf0) < msglen {
c.rdbuf0 = make([]byte, msglen)
} else {
// Otherwise we allocate a new buffer.
msgBuf = make([]byte, msglen)
c.rdbuf0 = c.rdbuf0[:msglen]
}
_, err = io.ReadFull(c.cr, msgBuf)
_, err = io.ReadFull(c.cr, c.rdbuf0)
if err != nil {
return
}
l.Debugf("read %d bytes", len(msgBuf))
l.Debugf("read %d bytes", len(c.rdbuf0))
msgBuf := c.rdbuf0
if hdr.compression && msglen > 0 {
// We're going to decompress msgBuf into a different newly allocated
// buffer, so keep msgBuf around for reuse on the next message.
c.readerBuf = msgBuf
msgBuf, err = lz4.Decode(nil, msgBuf)
c.rdbuf1 = c.rdbuf1[:cap(c.rdbuf1)]
c.rdbuf1, err = lz4.Decode(c.rdbuf1, c.rdbuf0)
if err != nil {
return
}
msgBuf = c.rdbuf1
l.Debugf("decompressed to %d bytes", len(msgBuf))
} else {
c.readerBuf = nil
}
if shouldDebug() {
@@ -611,14 +601,7 @@ func (c *rawConnection) writerLoop() {
case hm := <-c.outbox:
if hm.msg != nil {
// Uncompressed message in uncBuf
msgLen := hm.msg.XDRSize()
if cap(uncBuf) >= msgLen {
uncBuf = uncBuf[:msgLen]
} else {
uncBuf = make([]byte, msgLen)
}
m := &xdr.Marshaller{Data: uncBuf}
err = hm.msg.MarshalXDRInto(m)
uncBuf, err = hm.msg.AppendXDR(uncBuf[:0])
if hm.done != nil {
close(hm.done)
}

View File

@@ -3,6 +3,7 @@
package protocol
import (
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
@@ -54,13 +55,14 @@ func TestHeaderMarshalUnmarshal(t *testing.T) {
ver = int(uint(ver) % 16)
id = int(uint(id) % 4096)
typ = int(uint(typ) % 256)
buf := make([]byte, 4)
buf := new(bytes.Buffer)
xw := xdr.NewWriter(buf)
h0 := header{version: ver, msgID: id, msgType: typ}
h0.MarshalXDRInto(&xdr.Marshaller{Data: buf})
h0.encodeXDR(xw)
xr := xdr.NewReader(buf)
var h1 header
h1.UnmarshalXDRFrom(&xdr.Unmarshaller{Data: buf})
h1.decodeXDR(xr)
return h0 == h1
}
if err := quick.Check(f, nil); err != nil {
@@ -126,7 +128,8 @@ func TestVersionErr(t *testing.T) {
c0.ClusterConfig(ClusterConfigMessage{})
c1.ClusterConfig(ClusterConfigMessage{})
timeoutWriteHeader(c0.cw, header{
w := xdr.NewWriter(c0.cw)
timeoutWriteHeader(w, header{
version: 2, // higher than supported
msgID: 0,
msgType: messageTypeIndex,
@@ -151,7 +154,8 @@ func TestTypeErr(t *testing.T) {
c0.ClusterConfig(ClusterConfigMessage{})
c1.ClusterConfig(ClusterConfigMessage{})
timeoutWriteHeader(c0.cw, header{
w := xdr.NewWriter(c0.cw)
timeoutWriteHeader(w, header{
version: 0,
msgID: 0,
msgType: 42, // unknown type
@@ -201,7 +205,7 @@ func TestElementSizeExceededNested(t *testing.T) {
m := ClusterConfigMessage{
ClientName: "longstringlongstringlongstringinglongstringlongstringlonlongstringlongstringlon",
}
_, err := m.MarshalXDR()
_, err := m.EncodeXDR(ioutil.Discard)
if err == nil {
t.Errorf("ID length %d > max 64, but no error", len(m.Folders[0].ID))
}
@@ -209,19 +213,12 @@ func TestElementSizeExceededNested(t *testing.T) {
func TestMarshalIndexMessage(t *testing.T) {
f := func(m1 IndexMessage) bool {
if len(m1.Options) == 0 {
m1.Options = nil
}
for i, f := range m1.Files {
m1.Files[i].CachedSize = 0
if len(f.Blocks) == 0 {
m1.Files[i].Blocks = nil
} else {
for j := range f.Blocks {
f.Blocks[j].Offset = 0
if len(f.Blocks[j].Hash) == 0 {
f.Blocks[j].Hash = nil
}
for j := range f.Blocks {
f.Blocks[j].Offset = 0
if len(f.Blocks[j].Hash) == 0 {
f.Blocks[j].Hash = nil
}
}
}
@@ -236,9 +233,6 @@ func TestMarshalIndexMessage(t *testing.T) {
func TestMarshalRequestMessage(t *testing.T) {
f := func(m1 RequestMessage) bool {
if len(m1.Options) == 0 {
m1.Options = nil
}
return testMarshal(t, "request", &m1, &RequestMessage{})
}
@@ -262,9 +256,6 @@ func TestMarshalResponseMessage(t *testing.T) {
func TestMarshalClusterConfigMessage(t *testing.T) {
f := func(m1 ClusterConfigMessage) bool {
if len(m1.Options) == 0 {
m1.Options = nil
}
return testMarshal(t, "clusterconfig", &m1, &ClusterConfigMessage{})
}
@@ -284,11 +275,13 @@ func TestMarshalCloseMessage(t *testing.T) {
}
type message interface {
MarshalXDR() ([]byte, error)
UnmarshalXDR([]byte) error
EncodeXDR(io.Writer) (int, error)
DecodeXDR(io.Reader) error
}
func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
var buf bytes.Buffer
failed := func(bc []byte) {
bs, _ := json.MarshalIndent(m1, "", " ")
ioutil.WriteFile(prefix+"-1.txt", bs, 0644)
@@ -301,7 +294,7 @@ func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
}
}
buf, err := m1.MarshalXDR()
_, err := m1.EncodeXDR(&buf)
if err != nil && strings.Contains(err.Error(), "exceeds size") {
return true
}
@@ -310,20 +303,23 @@ func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
t.Fatal(err)
}
err = m2.UnmarshalXDR(buf)
bc := make([]byte, len(buf.Bytes()))
copy(bc, buf.Bytes())
err = m2.DecodeXDR(&buf)
if err != nil {
failed(buf)
failed(bc)
t.Fatal(err)
}
ok := reflect.DeepEqual(m1, m2)
if !ok {
failed(buf)
failed(bc)
}
return ok
}
func timeoutWriteHeader(w io.Writer, hdr header) {
func timeoutWriteHeader(w *xdr.Writer, hdr header) {
// This tries to write a message header to w, but times out after a while.
// This is useful because in testing, with a PipeWriter, it will block
// forever if the other side isn't reading any more. On the other hand we
@@ -336,7 +332,8 @@ func timeoutWriteHeader(w io.Writer, hdr header) {
done := make(chan struct{})
go func() {
w.Write(buf[:])
w.WriteRaw(buf[:])
l.Infoln("write completed")
close(done)
}()
select {

View File

@@ -7,29 +7,37 @@ import "github.com/calmh/xdr"
// This stuff is hacked up manually because genxdr doesn't support 'type
// Vector []Counter' declarations and it was tricky when I tried to add it...
func (v Vector) MarshalXDRInto(m *xdr.Marshaller) error {
m.MarshalUint32(uint32(len(v)))
for i := range v {
m.MarshalUint64(uint64(v[i].ID))
m.MarshalUint64(v[i].Value)
}
return m.Error
type xdrWriter interface {
WriteUint32(uint32) (int, error)
WriteUint64(uint64) (int, error)
}
type xdrReader interface {
ReadUint32() uint32
ReadUint64() uint64
}
func (v *Vector) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
l := int(u.UnmarshalUint32())
// EncodeXDRInto encodes the vector as an XDR object into the given XDR
// encoder.
func (v Vector) EncodeXDRInto(w xdrWriter) (int, error) {
w.WriteUint32(uint32(len(v)))
for i := range v {
w.WriteUint64(uint64(v[i].ID))
w.WriteUint64(v[i].Value)
}
return 4 + 16*len(v), nil
}
// DecodeXDRFrom decodes the XDR objects from the given reader into itself.
func (v *Vector) DecodeXDRFrom(r xdrReader) error {
l := int(r.ReadUint32())
if l > 1e6 {
return xdr.ElementSizeExceeded("number of counters", l, 1e6)
}
n := make(Vector, l)
for i := range n {
n[i].ID = ShortID(u.UnmarshalUint64())
n[i].Value = u.UnmarshalUint64()
n[i].ID = ShortID(r.ReadUint64())
n[i].Value = r.ReadUint64()
}
*v = n
return u.Error
}
func (v Vector) XDRSize() int {
return 4 + 16*len(v)
return nil
}

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