Compare commits

...

25 Commits

Author SHA1 Message Date
Jakob Borg
952ab7db1c cmd/syncthing: Updated code name 2018-12-26 17:23:05 +01:00
Jakob Borg
abb3fb8a31 lib/events: Become a service (fixes #5372) (#5373)
Here the event Logger is rewritten as a service with a main loop instead
of mutexes. This loop has a select with essentially two legs: incoming
events, and subscription changes. When both are possible select will
chose one randomly, thus ensuring that in practice unsubscribes will
happen timely and not block the system.
2018-12-13 13:42:28 +01:00
Jakob Borg
fc860df514 lib/watchaggregator: Properly unsubscribe from events when stopping (ref #5372) (#5374) 2018-12-13 08:11:51 +01:00
Simon Frei
c934918347 lib/scanner: Fix empty path on normalization error (fixes #5369) (#5370) 2018-12-12 12:09:39 +01:00
Jakob Borg
c98a34a5d4 gui, man, authors: Update docs, translations, and contributors 2018-12-12 07:45:23 +01:00
Jörg Thalheim
bdcffe703b build: Don't use go build -i (#5367) 2018-12-11 13:37:46 +01:00
Simon Frei
a09079ed25 all: Display list of locally changed items in UI (fixes #5336) (#5337) 2018-12-11 09:59:04 +01:00
Jakob Borg
1b59960ff9 lib/model: Attempt to unflake Deregister tests (fixes #5362) (#5366) 2018-12-11 09:42:03 +01:00
Jakob Borg
f04d054b5a lib/db: Document current keyspace 2018-12-10 09:55:21 +01:00
desbma
132789785d etc: Add hardening options to syncthing systemd services (fixes #5286) (#5351) 2018-12-07 14:58:12 +01:00
Jakob Borg
002de7b6a0 Merge branch 'release'
* release:
  gui: Don't use newfangled ES6 features (fixes #5348)
2018-12-05 12:24:36 +01:00
Jakob Borg
9e725026d1 gui: Don't use newfangled ES6 features (fixes #5348) 2018-12-05 12:24:27 +01:00
Jakob Borg
da39dfada3 gui: Don't use newfangled ES6 features (fixes #5348) 2018-12-05 12:18:45 +01:00
Audrius Butkevicius
ff2cde469e lib/model: Allow limiting number of concurrent scans (fixes #2760) (#4888) 2018-12-05 08:40:05 +01:00
Jakob Borg
0fe4c01a28 lib/db: Fix broken test on Windows (#5347) 2018-12-05 08:24:14 +01:00
Jakob Borg
351e855464 gui, man, authors: Update docs, translations, and contributors 2018-12-05 07:45:21 +01:00
Jakob Borg
7203ccb73d gui, man, authors: Update docs, translations, and contributors 2018-11-28 07:45:22 +01:00
Jakob Borg
e89c4c053a lib/db: Simple case sensitivity test 2018-11-26 17:58:00 +01:00
Simon Frei
0391c57a37 cmd/syncthing: Improve logged device information on startup (#5321) 2018-11-22 11:39:01 +01:00
Simon Frei
2f9840ddae lib: Introduce fs.IsParent (fixes #5324) (#5326) 2018-11-22 11:16:45 +01:00
Ben S
513d3bc374 gui: Display rate limit (fixes #5320) (#5328)
Display rate limit for own device and connected devices
2018-11-22 10:59:04 +01:00
Jakob Borg
c0a26c918a lib/scanner, vendor: Update github.com/chmduquesne/rollinghash (fixes #5334) (#5335)
Updates the package and fixes a test that depended on the old behavior
of Write() being equivalent to Reset()+Write() which is no longer the
case. The scanner already did resets after each block write, so this is
fine.
2018-11-22 08:50:06 +01:00
Jakob Borg
d1704d5304 gui, man, authors: Update docs, translations, and contributors 2018-11-21 07:45:22 +01:00
Jakob Borg
d3d0161fb9 gui, man, authors: Update docs, translations, and contributors 2018-11-14 07:45:23 +01:00
Simon Frei
db8777c29e lib/model: Add test for #5323 (#5325) 2018-11-13 08:36:16 +00:00
72 changed files with 1075 additions and 711 deletions

View File

@@ -53,6 +53,7 @@ Chris Joel (cdata) <chris@scriptolo.gy>
Chris Tonkinson <chris@masterbran.ch>
chucic <chucic@seznam.cz>
Colin Kennedy (moshen) <moshen.colin@gmail.com>
Cromefire_ <tim.l@nghorst.net>
Dale Visser <dale.visser@live.com>
Daniel Bergmann (brgmnn) <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
Daniel Harte (norgeous) <daniel@harte.me> <daniel@danielharte.co.uk> <norgeous@users.noreply.github.com>
@@ -62,6 +63,7 @@ David Rimmer (dinosore) <dinosore@dbrsoftware.co.uk>
Denis A. (dva) <denisva@gmail.com>
Dennis Wilson (snnd) <dw@risu.io>
derekriemer <derek.riemer@colorado.edu>
desbma <desbma@users.noreply.github.com>
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
Dominik Heidler (asdil12) <dominik@heidler.eu>
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
@@ -97,6 +99,7 @@ Johan Vromans (sciurius) <jvromans@squirrel.nl>
John Rinehart (fuzzybear3965) <johnrichardrinehart@gmail.com>
Jonathan Cross <jcross@gmail.com>
Jose Manuel Delicado (jmdaweb) <jmdaweb@hotmail.com> <jmdaweb@users.noreply.github.com>
Jörg Thalheim <Mic92@users.noreply.github.com>
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
Keith Turner <kturner@apache.org>
Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>

View File

@@ -450,7 +450,7 @@ func build(target target, tags []string) {
rmr(target.BinaryName())
args := []string{"build", "-i", "-v"}
args := []string{"build", "-v"}
args = appendParameters(args, tags, target)
os.Setenv("GOOS", goos)

View File

@@ -90,6 +90,7 @@ type modelIntf interface {
Revert(folder string)
NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated)
RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error)
LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated
NeedSize(folder string) db.Counts
ConnectionStats() map[string]interface{}
DeviceStatistics() map[string]stats.DeviceStatistics
@@ -258,6 +259,7 @@ func (s *apiService) Serve() {
getRestMux.HandleFunc("/rest/db/ignores", s.getDBIgnores) // folder
getRestMux.HandleFunc("/rest/db/need", s.getDBNeed) // folder [perpage] [page]
getRestMux.HandleFunc("/rest/db/remoteneed", s.getDBRemoteNeed) // device folder [perpage] [page]
getRestMux.HandleFunc("/rest/db/localchanged", s.getDBLocalChanged) // folder
getRestMux.HandleFunc("/rest/db/status", s.getDBStatus) // folder
getRestMux.HandleFunc("/rest/db/browse", s.getDBBrowse) // folder [prefix] [dirsonly] [levels]
getRestMux.HandleFunc("/rest/folder/versions", s.getFolderVersions) // folder
@@ -707,13 +709,13 @@ func folderSummary(cfg configIntf, m modelIntf, folder string) (map[string]inter
res["invalid"] = "" // Deprecated, retains external API for now
global := m.GlobalSize(folder)
res["globalFiles"], res["globalDirectories"], res["globalSymlinks"], res["globalDeleted"], res["globalBytes"] = global.Files, global.Directories, global.Symlinks, global.Deleted, global.Bytes
res["globalFiles"], res["globalDirectories"], res["globalSymlinks"], res["globalDeleted"], res["globalBytes"], res["globalTotalItems"] = global.Files, global.Directories, global.Symlinks, global.Deleted, global.Bytes, global.TotalItems()
local := m.LocalSize(folder)
res["localFiles"], res["localDirectories"], res["localSymlinks"], res["localDeleted"], res["localBytes"] = local.Files, local.Directories, local.Symlinks, local.Deleted, local.Bytes
res["localFiles"], res["localDirectories"], res["localSymlinks"], res["localDeleted"], res["localBytes"], res["localTotalItems"] = local.Files, local.Directories, local.Symlinks, local.Deleted, local.Bytes, local.TotalItems()
need := m.NeedSize(folder)
res["needFiles"], res["needDirectories"], res["needSymlinks"], res["needDeletes"], res["needBytes"] = need.Files, need.Directories, need.Symlinks, need.Deleted, need.Bytes
res["needFiles"], res["needDirectories"], res["needSymlinks"], res["needDeletes"], res["needBytes"], res["needTotalItems"] = need.Files, need.Directories, need.Symlinks, need.Deleted, need.Bytes, need.TotalItems()
if cfg.Folders()[folder].Type == config.FolderTypeReceiveOnly {
// Add statistics for things that have changed locally in a receive
@@ -724,6 +726,7 @@ func folderSummary(cfg configIntf, m modelIntf, folder string) (map[string]inter
res["receiveOnlyChangedSymlinks"] = ro.Symlinks
res["receiveOnlyChangedDeletes"] = ro.Deleted
res["receiveOnlyChangedBytes"] = ro.Bytes
res["receiveOnlyTotalItems"] = ro.TotalItems()
}
res["inSyncFiles"], res["inSyncBytes"] = global.Files-need.Files, global.Bytes-need.Bytes
@@ -791,9 +794,9 @@ func (s *apiService) getDBNeed(w http.ResponseWriter, r *http.Request) {
// Convert the struct to a more loose structure, and inject the size.
sendJSON(w, map[string]interface{}{
"progress": toNeedSlice(progress),
"queued": toNeedSlice(queued),
"rest": toNeedSlice(rest),
"progress": toJsonFileInfoSlice(progress),
"queued": toJsonFileInfoSlice(queued),
"rest": toJsonFileInfoSlice(rest),
"page": page,
"perpage": perpage,
})
@@ -816,13 +819,29 @@ func (s *apiService) getDBRemoteNeed(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusNotFound)
} else {
sendJSON(w, map[string]interface{}{
"files": toNeedSlice(files),
"files": toJsonFileInfoSlice(files),
"page": page,
"perpage": perpage,
})
}
}
func (s *apiService) getDBLocalChanged(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query()
folder := qs.Get("folder")
page, perpage := getPagingParams(qs)
files := s.model.LocalChangedFiles(folder, page, perpage)
sendJSON(w, map[string]interface{}{
"files": toJsonFileInfoSlice(files),
"page": page,
"perpage": perpage,
})
}
func (s *apiService) getSystemConnections(w http.ResponseWriter, r *http.Request) {
sendJSON(w, s.model.ConnectionStats())
}
@@ -1638,7 +1657,7 @@ func (s *apiService) getHeapProf(w http.ResponseWriter, r *http.Request) {
pprof.WriteHeapProfile(w)
}
func toNeedSlice(fs []db.FileInfoTruncated) []jsonDBFileInfo {
func toJsonFileInfoSlice(fs []db.FileInfoTruncated) []jsonDBFileInfo {
res := make([]jsonDBFileInfo, len(fs))
for i, f := range fs {
res[i] = jsonDBFileInfo(f)

View File

@@ -54,7 +54,7 @@ import (
var (
Version = "unknown-dev"
Codename = "Dysprosium Dragonfly"
Codename = "Erbium Earthworm"
BuildStamp = "0"
BuildDate time.Time
BuildHost = "unknown"
@@ -828,9 +828,11 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
pprof.StartCPUProfile(f)
}
myDev, _ := cfg.Device(myID)
l.Infof(`My name is "%v"`, myDev.Name)
for _, device := range cfg.Devices() {
if len(device.Name) > 0 {
l.Infof("Device %s is %q at %v", device.DeviceID, device.Name, device.Addresses)
if device.DeviceID != myID {
l.Infof(`Device %s is "%v" at %v`, device.DeviceID, device.Name, device.Addresses)
}
}

View File

@@ -146,3 +146,7 @@ func (m *mockedModel) FolderErrors(folder string) ([]model.FileError, error) {
func (m *mockedModel) WatchError(folder string) error {
return nil
}
func (m *mockedModel) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated {
return nil
}

View File

@@ -10,5 +10,12 @@ Restart=on-failure
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
# Hardening
ProtectSystem=full
PrivateTmp=true
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target

View File

@@ -8,5 +8,12 @@ Restart=on-failure
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
# Hardening
ProtectSystem=full
PrivateTmp=true
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true
[Install]
WantedBy=default.target

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Дебъгин Функционалост",
"Default Folder Path": "Път до папка по подразбиране",
"Deleted": "Изтрито",
"Deselect All": "Deselect All",
"Device": "Устройство",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство \"{{name}}\" ({{device}}) на {{address}} желае да се свърже. Добави ново устройство?",
"Device ID": "Идентификатор на устройство",
@@ -156,6 +157,7 @@
"Later": "По-късно",
"Latest Change": "Последна промяна",
"Learn more": "Научете повече",
"Limit": "Limit",
"Listeners": "Синхронизиращи устройства",
"Loading data...": "Зарежадне на информация...",
"Loading...": "Зареждане...",
@@ -247,6 +249,7 @@
"Scanning": "Сканиране",
"See external versioner help for supported templated command line parameters.": "Прегледайте документацията на външното приложение за версии и поддържаните от него командни параметри. ",
"See external versioning help for supported templated command line parameters.": "Прегледайте външната документацията за поддържаните командни параметри. ",
"Select All": "Select All",
"Select a version": "Изберте версия",
"Select latest version": "Избор на най-новата версия",
"Select oldest version": "Избор на най-старата версия",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Utilitats de Depuració",
"Default Folder Path": "Carpeta de la Ruta per Defecte",
"Deleted": "Esborrat",
"Deselect All": "Deselect All",
"Device": "Dispositiu",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Dispositiu \"{{name}}\" ({{device}} a l'adreça {{address}}) vol connectar. Afegir nou dispositiu?",
"Device ID": "ID del dispositiu",
@@ -156,6 +157,7 @@
"Later": "Més tard",
"Latest Change": "Últim Canvi",
"Learn more": "Saber més",
"Limit": "Limit",
"Listeners": "Escoltants",
"Loading data...": "Carregant dades...",
"Loading...": "Carregant...",
@@ -247,6 +249,7 @@
"Scanning": "Rastrejant",
"See external versioner help for supported templated command line parameters.": "Consulta l'ajuda externa sobre versions per a conéixer els paràmetres de la plantilla de la línia de comandaments.",
"See external versioning help for supported templated command line parameters.": "Consulta l'ajuda externa sobre versions per a conéixer els paràmetres de la plantilla de la línia de comandaments.",
"Select All": "Select All",
"Select a version": "Seleccionar una versió",
"Select latest version": "Seleccionar l'última versió",
"Select oldest version": "Seleccionar la versió més antiga",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Nástroje pro ladění",
"Default Folder Path": "Výchozí cesta k adresáři",
"Deleted": "Smazáno",
"Deselect All": "Zrušit výběr",
"Device": "Zařízení",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Zařízení \"{{name}}\" ({{device}} na {{address}}) se chce připojit. Přidat nové zařízení?",
"Device ID": "ID zařízení",
@@ -156,6 +157,7 @@
"Later": "Později",
"Latest Change": "Poslední změna",
"Learn more": "Zjistěte více",
"Limit": "Limit",
"Listeners": "Naslouchající",
"Loading data...": "Nahrávání dat...",
"Loading...": "Načítání...",
@@ -247,6 +249,7 @@
"Scanning": "Skenování",
"See external versioner help for supported templated command line parameters.": "Pro upřesnění požadovaných parametrů příkazu navštivte nápovědu pro externí verzování.",
"See external versioning help for supported templated command line parameters.": "Podporované šablonové parametry příkazové řádky jsou dostupné v nápovědě k externímu verzování.",
"Select All": "Vybrat vše",
"Select a version": "Vyberte verzi",
"Select latest version": "Vybrat nejnovější verzi",
"Select oldest version": "Vybrat nejstarší verzi",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Faciliteter til fejlretning",
"Default Folder Path": "Standardmappesti",
"Deleted": "Slettet",
"Deselect All": "Deselect All",
"Device": "Enhed",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enheden “{{name}}” ({{device}} på {{address}}) vil gerne forbinde. Tilføj denne enhed?",
"Device ID": "Enheds-ID",
@@ -156,6 +157,7 @@
"Later": "Senere",
"Latest Change": "Seneste ændring",
"Learn more": "Lær mere",
"Limit": "Limit",
"Listeners": "Lyttere",
"Loading data...": "Indlæser data…",
"Loading...": "Indlæser…",
@@ -247,6 +249,7 @@
"Scanning": "Skanner",
"See external versioner help for supported templated command line parameters.": "Se hjælp til ekstern versionering for understøttede kommandolinjeparametre.",
"See external versioning help for supported templated command line parameters.": "Se hjælp til ekstern versionering for understøttede kommandolinjeparametre.",
"Select All": "Select All",
"Select a version": "Vælg en version",
"Select latest version": "Vælg seneste version",
"Select oldest version": "Vælg ældste version",

View File

@@ -23,7 +23,7 @@
"Allowed Networks": "Erlaubte Netzwerke",
"Alphabetic": "Alphabetisch",
"An external command handles the versioning. It has to remove the file from the shared folder.": "Ein externer Befehl führt die Versionierung durch. Er muss die Datei aus dem geteilten Ordner entfernen.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ein externer Befehl beeinflusst die Versionierung. Die Datei aus dem freigegebenen Ordner muss entfernen werden. Wenn der Pfad der Anwendung Leerzeichen enthält, sollte dieser in Anführungszeichen stehen.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Ein externer Befehl behandelt die Versionierung. Die Datei aus dem freigegebenen Ordner muss entfernen werden. Wenn der Pfad der Anwendung Leerzeichen enthält, sollte dieser in Anführungszeichen stehen.",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Ein externer Programmaufruf handhabt die Versionierung. Es muss die Datei aus dem zu synchronisierendem Ordner entfernen.",
"Anonymous Usage Reporting": "Anonymer Nutzungsbericht",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Das Format des anonymen Nutzungsberichts hat sich geändert. Möchten Sie auf das neue Format umsteigen?",
@@ -50,7 +50,7 @@
"Connection Error": "Verbindungsfehler",
"Connection Type": "Verbindungstyp",
"Connections": "Verbindungen",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Kontinuierliches nach Änderungen suchen, ist jetzt in Syncthing verfügbar. Dadurch werden Änderungen auf der Festplatte erkannt und durch einen Scan nur die geänderten Pfade überprüft. Die Vorteile bestehen darin, dass Änderungen schneller festgestellt werden und weniger vollständige Scans erforderlich sind.",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Kontinuierliche Änderungssuche ist jetzt in Syncthing verfügbar. Dadurch werden Änderungen auf der Festplatte erkannt und durch einen Scan nur die geänderten Pfade überprüft. Die Vorteile bestehen darin, dass Änderungen schneller festgestellt werden und weniger vollständige Scans erforderlich sind.",
"Copied from elsewhere": "Von anderer Quelle kopiert",
"Copied from original": "Vom Original kopiert",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 der folgenden Unterstützer:",
@@ -60,12 +60,13 @@
"Debugging Facilities": "Debugging-Möglichkeiten",
"Default Folder Path": "Standardmäßiger Ordnerpfad",
"Deleted": "Gelöscht",
"Deselect All": "Alle abwählen",
"Device": "Gerät",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Gerät \"{{name}}\" ({{device}} {{address}}) möchte sich verbinden. Gerät hinzufügen?",
"Device ID": "Gerätekennung",
"Device Identification": "Geräteidentifikation",
"Device Name": "Gerätename",
"Device rate limits": "Teilnehmerbegrenzung",
"Device rate limits": "Gerät Datenratelimit",
"Device that last modified the item": "Gerät, das das Element zuletzt geändert hat",
"Devices": "Geräte",
"Disabled": "Deaktiviert",
@@ -81,7 +82,7 @@
"Do not restore all": "Nicht alle wiederherstellen",
"Do you want to enable watching for changes for all your folders?": "Möchten Sie das nach Änderungen für alle Ihre Ordner gesucht wird aktivieren?",
"Documentation": "Dokumentation",
"Download Rate": "Download",
"Download Rate": "Downloadrate",
"Downloaded": "Heruntergeladen",
"Downloading": "Lädt herunter",
"Edit": "Bearbeiten",
@@ -100,7 +101,7 @@
"External File Versioning": "Externe Dateiversionierung",
"Failed Items": "Fehlgeschlagene Objekte",
"Failed to load ignore patterns": "Fehler beim Laden der Ignoriermuster",
"Failed to setup, retrying": "Fehler beim Installieren, erneut versuchen",
"Failed to setup, retrying": "Fehler beim Installieren, versuche erneut",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Ein Verbindungsfehler zu IPv6-Servern ist zu erwarten, wenn es keine IPv6-Konnektivität gibt.",
"File Pull Order": "Dateiübertragungsreihenfolge",
"File Versioning": "Dateiversionierung",
@@ -143,7 +144,7 @@
"Ignored Devices": "Ignorierte Geräte",
"Ignored Folders": "Ignorierte Ordner",
"Ignored at": "Ignoriert bei/von",
"Incoming Rate Limit (KiB/s)": "Limit Datenrate (eingehend) (KB/s)",
"Incoming Rate Limit (KiB/s)": "Eingehendes Datenratelimit (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Eine falsche Konfiguration kann den Ordnerinhalt beschädigen und Syncthing in einen unausführbaren Zustand versetzen.",
"Introduced By": "Verteilt von",
"Introducer": "Verteilergerät",
@@ -156,6 +157,7 @@
"Later": "Später",
"Latest Change": "Letzte Änderung",
"Learn more": "Mehr erfahren",
"Limit": "Limit",
"Listeners": "Zuhörer",
"Loading data...": "Daten werden geladen...",
"Loading...": "Wird geladen...",
@@ -164,7 +166,7 @@
"Local State (Total)": "Lokaler Status (Gesamt)",
"Log": "Protokoll",
"Log tailing paused. Click here to continue.": "Protokollaufzeichnungen sind pausiert. Klicken Sie hier um fortzufahren.",
"Log tailing paused. Scroll to bottom continue.": "Protokoll-Tailing pausieren.\nScrolle nach unten zum fortfahren.",
"Log tailing paused. Scroll to bottom continue.": "Protokoll-Tailing pausiert.\nScrolle nach unten zum fortfahren.",
"Logs": "Protokolle",
"Major Upgrade": "Hauptversionsaktualisierung",
"Mass actions": "Massenaktionen",
@@ -193,7 +195,7 @@
"Options": "Optionen",
"Out of Sync": "Nicht synchronisiert",
"Out of Sync Items": "Nicht synchronisierte Objekte",
"Outgoing Rate Limit (KiB/s)": "Limit Datenrate (ausgehend) (KB/s)",
"Outgoing Rate Limit (KiB/s)": "Ausgehendes Datenratelimit (KiB/s)",
"Override Changes": "Änderungen überschreiben",
"Path": "Pfad",
"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": "Pfad zum Ordner auf dem lokalen Gerät. Ordner wird erzeugt, wenn er nicht existiert. Das Tilden-Zeichen (~) kann als Abkürzung benutzt werden für",
@@ -247,6 +249,7 @@
"Scanning": "Scannen",
"See external versioner help for supported templated command line parameters.": "Siehe Hilfe des externen Versionierers für unterstützte Befehlszeilenparameter.",
"See external versioning help for supported templated command line parameters.": "Siehe Hilfe zur externen Versionierung für unterstützte Befehlszeilenparameter.",
"Select All": "Alle auswählen",
"Select a version": "Wählen Sie eine Version",
"Select latest version": "Letzte Version auswählen",
"Select oldest version": "Älteste Version auswählen",
@@ -283,7 +286,7 @@
"Statistics": "Statistiken",
"Stopped": "Gestoppt",
"Support": "Support",
"Support Bundle": "Support Bundle",
"Support Bundle": "Supportpaket",
"Sync Protocol Listen Addresses": "Adresse(n) für das Synchronisierungsprotokoll",
"Syncing": "Synchronisiere",
"Syncthing has been shut down.": "Syncthing wurde heruntergefahren.",
@@ -315,7 +318,7 @@
"The number of old versions to keep, per file.": "Anzahl der alten Versionen, die von jeder Datei behalten werden sollen.",
"The number of versions must be a number and cannot be blank.": "Die Anzahl von Versionen muss eine Ganzzahl und darf nicht leer sein.",
"The path cannot be blank.": "Der Pfad darf nicht leer sein.",
"The rate limit must be a non-negative number (0: no limit)": "Das Datenrate-Limit muss eine nicht negative Zahl sein (0 = kein Limit).",
"The rate limit must be a non-negative number (0: no limit)": "Das Datenratelimit muss eine nicht negative Zahl 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",
@@ -338,7 +341,7 @@
"Upgrade": "Aktualisierung",
"Upgrade To {%version%}": "Aktualisierung auf {{version}}",
"Upgrading": "Wird aktualisiert",
"Upload Rate": "Upload",
"Upload Rate": "Uploadrate",
"Uptime": "Betriebszeit",
"Usage reporting is always enabled for candidate releases.": "Nutzungsbericht ist für Veröffentlichungskandidaten immer aktiviert.",
"Use HTTPS for GUI": "HTTPS für Benutzeroberfläche verwenden",
@@ -346,10 +349,10 @@
"Versions": "Versionen",
"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 parent directory of an existing folder \"{%otherFolder%}\".": "Warnung, dieser Pfad ist ein übergeordnetes Verzeichnis eines existierenden Ordners \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warnung, dieser Pfad ist ein übergeordnetes Verzeichnis eines existierenden Ordners \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Warnung, dieser Pfad ist ein übergeordneter Ordner eines existierenden Ordners \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warnung, dieser Pfad ist ein übergeordneter Ordner eines existierenden Ordners \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warnung, dieser Pfad ist ein Unterordner des existierenden Ordners \"{{otherFolder}}\".",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warnung, dieser Pfad ist ein Unterverzeichnis eines existierenden Ordners \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warnung, dieser Pfad ist ein Unterordner eines existierenden Ordners \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Achtung: Wenn Sie einen externen Beobachter wie {{syncthingInotify}} benutzen, sollten sie sicher sein das dieser deaktiviert ist.",
"Watch for Changes": "Auf Änderungen achten",
"Watching for Changes": "Auf Änderungen achten",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Εργαλεία αποσφαλμάτωσης",
"Default Folder Path": "Προκαθορισμένη διαδρομή φακέλων",
"Deleted": "Διαγραμμένα",
"Deselect All": "Deselect All",
"Device": "Συσκευή",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Η συσκευή \"{{name}}\" ({{device}} στη διεύθυνση {{address}}) επιθυμεί να συνδεθεί. Προσθήκη της νέας συσκευής;",
"Device ID": "Ταυτότητα συσκευής",
@@ -156,6 +157,7 @@
"Later": "Αργότερα",
"Latest Change": "Τελευταία αλλαγή",
"Learn more": "Μάθετε περισσότερα",
"Limit": "Limit",
"Listeners": "Ακροατές",
"Loading data...": "Φόρτωση δεδομένων...",
"Loading...": "Φόρτωση...",
@@ -247,6 +249,7 @@
"Scanning": "Έλεγχος για αλλαγές",
"See external versioner help for supported templated command line parameters.": "Ανατρέξτε στην τεκμηρίωση της εξωτερικής τήρησης εκδόσεων για πληροφορίες σχετικά με τις υποστηριζόμενες παραμέτρους της γραμμής εντολών.",
"See external versioning help for supported templated command line parameters.": "Ανατρέξτε στην τεκμηρίωση της εξωτερικής τήρησης εκδόσεων για πληροφορίες σχετικά με τις υποστηριζόμενες παραμέτρους της γραμμής εντολών.",
"Select All": "Select All",
"Select a version": "Επιλογή έκδοσης",
"Select latest version": "Επιλογή τελευταίας έκδοσης",
"Select oldest version": "Επιλογή παλαιότερης έκδοσης",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Default Folder Path",
"Deleted": "Deleted",
"Deselect All": "Deselect All",
"Device": "Device",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Device \"{{name}}\" ({{device}} at {{address}}) wants to connect. Add new device?",
"Device ID": "Device ID",
@@ -156,6 +157,7 @@
"Later": "Later",
"Latest Change": "Latest Change",
"Learn more": "Learn more",
"Limit": "Limit",
"Listeners": "Listeners",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
@@ -247,6 +249,7 @@
"Scanning": "Scanning",
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Select a version",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Default Folder Path",
"Deleted": "Deleted",
"Deselect All": "Deselect All",
"Device": "Device",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Device \"{{name}}\" ({{device}} at {{address}}) wants to connect. Add new device?",
"Device ID": "Device ID",
@@ -156,12 +157,14 @@
"Later": "Later",
"Latest Change": "Latest Change",
"Learn more": "Learn more",
"Limit": "Limit",
"Listeners": "Listeners",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
"Local Discovery": "Local Discovery",
"Local State": "Local State",
"Local State (Total)": "Local State (Total)",
"Locally Changed Items": "Locally Changed Items",
"Log": "Log",
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
@@ -247,6 +250,7 @@
"Scanning": "Scanning",
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Select a version",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",
@@ -307,6 +311,7 @@
"The folder path cannot be blank.": "The folder path cannot be blank.",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.",
"The following items could not be synchronized.": "The following items could not be synchronized.",
"The following items were changed locally.": "The following items were changed locally.",
"The maximum age must be a number and cannot be blank.": "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).": "The maximum time to keep a version (in days, set to 0 to keep versions forever).",
"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).",
@@ -346,6 +351,7 @@
"Versions": "Versions",
"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.",
"Waiting to scan": "Waiting to scan",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a parent directory of an existing folder \"{{otherFolder}}\".",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a parent directory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a subdirectory of an existing folder \"{{otherFolder}}\".",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Ayudas a la depuración",
"Default Folder Path": "Ruta de la carpeta por defecto",
"Deleted": "Eliminado",
"Deselect All": "Deselect All",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
"Device ID": "ID del Dispositivo",
@@ -156,6 +157,7 @@
"Later": "Más tarde",
"Latest Change": "Último Cambio",
"Learn more": "Saber más",
"Limit": "Limit",
"Listeners": "Oyentes",
"Loading data...": "Cargando datos...",
"Loading...": "Cargando...",
@@ -247,6 +249,7 @@
"Scanning": "Analizando",
"See external versioner help for supported templated command line parameters.": "Consultar la ayuda externa del versionador para ver las plantillas de los parámetros de línea de comandos",
"See external versioning help for supported templated command line parameters.": "Consultar la ayuda externa del versionado para ver las plantillas de los parámetros de línea de comandos",
"Select All": "Select All",
"Select a version": "Selecciona una versión",
"Select latest version": "Selecciona la última versión",
"Select oldest version": "Selecciona la versión más antigua",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Servicios de depuración",
"Default Folder Path": "Ruta de la carpeta por defecto",
"Deleted": "Eliminado",
"Deselect All": "Deselect All",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
"Device ID": "ID del Dispositivo",
@@ -156,6 +157,7 @@
"Later": "Más tarde",
"Latest Change": "Último Cambio",
"Learn more": "Saber más",
"Limit": "Limit",
"Listeners": "Oyentes",
"Loading data...": "Cargando datos...",
"Loading...": "Cargando...",
@@ -247,6 +249,7 @@
"Scanning": "Analizando",
"See external versioner help for supported templated command line parameters.": "Vea la ayuda del gestor de versiones externo para los parámetros de linea de comandos que usan una plantilla.",
"See external versioning help for supported templated command line parameters.": "Vea la ayuda del gestor de versiones externo para los parámetros de linea de comandos que usan una plantilla.",
"Select All": "Select All",
"Select a version": "Seleccione una versión",
"Select latest version": "Seleccione la última versión",
"Select oldest version": "Seleccione la versión más antigua",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Default Folder Path",
"Deleted": "Kendua",
"Deselect All": "Deselect All",
"Device": "Tresna",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Tresna \"{{name}}\" ({{device}} {{address}} era) konektatu nahi du. Onhartzen duzu ?",
"Device ID": "Tresnaren ID-a",
@@ -156,6 +157,7 @@
"Later": "Berantago",
"Latest Change": "Azken aldaketa",
"Learn more": "Gehiago jakiteko",
"Limit": "Limit",
"Listeners": "Entzungailuak",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
@@ -247,6 +249,7 @@
"Scanning": "Azterketa martxan",
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Select a version",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Debug -luokat",
"Default Folder Path": "Oletuspolku kansioille",
"Deleted": "Poistettu",
"Deselect All": "Poista valinnat",
"Device": "Laite",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Laite \"{{name}}\" {{device}} osoitteessa ({{address}}) haluaa yhdistää. Lisää uusi laite?",
"Device ID": "Laitteen ID",
@@ -156,6 +157,7 @@
"Later": "Myöhemmin",
"Latest Change": "Viimeisin muutos",
"Learn more": "Lisätietoja",
"Limit": "Rajoita",
"Listeners": "Kuuntelijat",
"Loading data...": "Lataa...",
"Loading...": "Lataa...",
@@ -247,6 +249,7 @@
"Scanning": "Skannataan",
"See external versioner help for supported templated command line parameters.": "Katso ulkopuolisen versiohallinnan tukisivu komentoriviparametreistä.",
"See external versioning help for supported templated command line parameters.": "Katso ulkopuolisen versiohallinnan tukisivu komentoriviparametreistä.",
"Select All": "Valitse kaikki",
"Select a version": "Valitse versio",
"Select latest version": "Valitse viimeisin versio",
"Select oldest version": "Valitse vanhin versio",
@@ -283,7 +286,7 @@
"Statistics": "Tilastot",
"Stopped": "Pysäytetty",
"Support": "Tuki",
"Support Bundle": "Support Bundle",
"Support Bundle": "Tukipaketti. (Tiedostot vianselvitystä varten.)",
"Sync Protocol Listen Addresses": "Synkronointiprotokollan kuunteluosoite",
"Syncing": "Synkronoidaan",
"Syncthing has been shut down.": "Syncthing on sammutettu.",
@@ -293,7 +296,7 @@
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing näyttää olevan alhaalla tai internetyhteydessä on ongelma. Yritetään uudelleen...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing ei pysty käsittelemään pyyntöäsi. Ole hyvä ja päivitä sivu tai käynnistä Syncthing uudelleen, jos ongelma jatkuu.",
"Take me back": "Takaisin",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Käyttöliittymän osoite on asetettu käynnistysparametreillä. Muutokset täällä tulevat voimaan vasta, kun käynnistysparametrejä ei käytetä.",
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthingin hallintakäyttöliittymä on asetettu sallimaan ulkoiset yhteydet ilman salasanaa.",
"The aggregated statistics are publicly available at the URL below.": "Koostetut tilastot ovat julkisesti saatavilla alla olevassa osoitteessa.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Asetukset on tallennettu, mutta niitä ei ole otettu käyttöön. Syncthingin täytyy käynnistyä uudelleen, jotta uudet asetukset saadaan käyttöön.",

View File

@@ -1,373 +0,0 @@
{
"A device with that ID is already added.": "L'appareil portant cette ID est déjà présent.",
"A negative number of days doesn't make sense.": "Ce champ n'accepte qu'un entier positif ou nul.",
"A new major version may not be compatible with previous versions.": "Une nouvelle version majeure peut présenter des incompatibilités avec les versions antérieures.",
"API Key": "Clé API",
"About": "À propos",
"Action": "Action",
"Actions": "Actions",
"Add": "Ajouter",
"Add Device": "Ajouter l'appareil",
"Add Folder": "Ajouter un partage",
"Add Remote Device": "Ajouter un appareil",
"Add devices from the introducer to our device list, for mutually shared folders.": "Lui permettre d'ajouter et enlever des membres à toutes mes listes de membres de partages dont il fait partie (ceci permet de créer toutes les liaisons point à point possibles en complétant mes listes par les siennes, meilleur débit de réception par cumul des débits d'envoi, indépendance vis à vis de l'introducteur, etc).",
"Add new folder?": "Ajouter ce partage ?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.",
"Address": "Adresse",
"Addresses": "Adresses",
"Advanced": "Avancé",
"Advanced Configuration": "Configuration avancée",
"Advanced settings": "Paramètres avancés",
"All Data": "Toutes les données",
"Allow Anonymous Usage Reporting?": "Autoriser l'envoi de statistiques d'utilisation anonymisées ?",
"Allowed Networks": "Réseaux autorisés",
"Alphabetic": "Alphabétique",
"An external command handles the versioning. It has to remove the file from the shared folder.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers dans le répertoire synchronisé.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.",
"An external command handles the versioning. It has to remove the file from the synced folder.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers dans le répertoire synchronisé.",
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymous usage report format has changed. Would you like to move to the new format?",
"Any devices configured on an introducer device will be added to this device as well.": "Lui permettre d'ajouter et enlever des membres à toutes mes listes de membres de partages dont il fait partie (ceci permet de créer toutes les liaisons point à point possibles en complétant mes listes par les siennes, meilleur débit de réception par cumul des débits d'envoi, indépendance vis à vis de l'introducteur, etc).",
"Are you sure you want to remove device {%name%}?": "Are you sure you want to remove device {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Are you sure you want to remove folder {{label}}?",
"Are you sure you want to restore {%count%} files?": "Are you sure you want to restore {{count}} files?",
"Auto Accept": "Auto Accept",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Le système de mise à jour automatique propose le choix entre versions stables et versions préliminaires.",
"Automatic upgrades": "Mises à jour automatiques",
"Automatically create or share folders that this device advertises at the default path.": "Automatically create or share folders that this device advertises at the default path.",
"Available debug logging facilities:": "Available debug logging facilities:",
"Be careful!": "Faites attention !",
"Bugs": "Bugs",
"CPU Utilization": "Utilisation du CPU",
"Changelog": "Historique des versions",
"Clean out after": "Purger après :",
"Click to see discovery failures": "Voir les échecs de découverte",
"Close": "Fermer",
"Command": "Commande",
"Comment, when used at the start of a line": "Commentaire lorsque utilisé en début de ligne",
"Compression": "Compression",
"Configured": "Configuré",
"Connection Error": "Erreur de connexion",
"Connection Type": "Type de connexion",
"Connections": "Connections",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.",
"Copied from elsewhere": "Copié d'ailleurs",
"Copied from original": "Copié depuis l'original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016, les contributeurs suivants:",
"Copyright © 2014-2017 the following Contributors:": "Copyright © 2014-2017, les contributeurs suivants :",
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Création de masques d'exclusion, remplacement du fichier existant : {{path}}.",
"Danger!": "Attention !",
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Default Folder Path",
"Deleted": "Supprimé",
"Device": "Appareil",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "L'appareil \"{{name}}\" ({{device}} à l'IP {{address}}) souhaite se connecter. L'acceptez-vous ?",
"Device ID": "ID de l'appareil",
"Device Identification": "Identifiant de l'appareil",
"Device Name": "Nom de l'appareil",
"Device rate limits": "Device rate limits",
"Device that last modified the item": "Device that last modified the item",
"Devices": "Appareil",
"Disabled": "Disabled",
"Disabled periodic scanning and disabled watching for changes": "Disabled periodic scanning and disabled watching for changes",
"Disabled periodic scanning and enabled watching for changes": "Disabled periodic scanning and enabled watching for changes",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:",
"Discard": "Discard",
"Disconnected": "Déconnecté",
"Discovered": "Découvert",
"Discovery": "Découverte",
"Discovery Failures": "Échecs de découverte",
"Do not restore": "Do not restore",
"Do not restore all": "Do not restore all",
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
"Documentation": "Documentation",
"Download Rate": "Débit de réception",
"Downloaded": "Reçu",
"Downloading": "Réception",
"Edit": "Modifier",
"Edit Device": "Modifier l'appareil",
"Edit Folder": "Modifier le partage",
"Editing": "Modifications",
"Editing {%path%}.": "Modification de {{path}}.",
"Enable NAT traversal": "Activer transfert d'adresses NAT",
"Enable Relaying": "Activer le relayage",
"Enabled": "Enabled",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Nombre positif (p.ex, \"2.35\") et unité. Pourcentage de l'espace disque total.",
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (1024 - 65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Entrer les adresses (\"tcp://ip:port\" ou \"tcp://hôte:port\") séparées par une virgule, ou \"dynamic\" afin d'activer la recherche automatique de l'adresse.",
"Enter ignore patterns, one per line.": "Entrer les masques d'exclusion, un par ligne.",
"Error": "Erreur",
"External File Versioning": "Gestion externe des versions de fichiers",
"Failed Items": "Éléments en échec",
"Failed to load ignore patterns": "Failed to load ignore patterns",
"Failed to setup, retrying": "Failed to setup, retrying",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connexion aux serveurs en IPv6 va échouer s'il n'y a pas de connectivité IPv6.",
"File Pull Order": "Ordre de récupération de fichier",
"File Versioning": "Méthode de préservation des fichiers",
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Les bits de permission de fichier sont ignorés lors de la recherche de changements. Utilisé sur les systèmes de fichiers FAT.",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Les fichiers sont déplacés dans le sous-répertoire .stversions quand ils sont remplacés ou supprimés par Syncthing. Leurs chemins d'accès relatifs y sont recréés si besoin.",
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Les fichiers sont déplacés dans le sous-répertoire .stversions quand ils sont remplacés ou supprimés par Syncthing. Leurs chemins d'accès relatifs y sont recréés si besoin.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Quand ils sont remplacés ou supprimés par Syncthing, les fichiers sont déplacés et horodatés vers le sous-répertoire .stversions dans une arborescence relative identique à celle de l'original.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Quand ils sont remplacés ou supprimés par Syncthing, les fichiers sont déplacés et horodatés vers le sous-répertoire .stversions dans une arborescence relative identique à celle de l'original.",
"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 celui-ci seront transférés aux autres.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.",
"Filesystem Notifications": "Filesystem Notifications",
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
"Filter by date": "Filter by date",
"Filter by name": "Filter by name",
"Folder": "Partage",
"Folder ID": "ID du partage",
"Folder Label": "Nom du partage",
"Folder Path": "Chemin racine du partage",
"Folder Type": "Type de partage",
"Folders": "Partages",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.",
"Full Rescan Interval (s)": "Full Rescan Interval (s)",
"GUI": "Interface graphique",
"GUI Authentication Password": "Mot de passe d'authentification GUI",
"GUI Authentication User": "Utilisateur autorisé GUI",
"GUI Listen Address": "GUI Listen Address",
"GUI Listen Addresses": "Adresses de l'interface (GUI)",
"GUI Theme": "Thème graphique",
"General": "General",
"Generate": "Générer",
"Global Changes": "Derniers changements",
"Global Discovery": "Découverte globale",
"Global Discovery Servers": "Serveurs de découverte globale",
"Global State": "État global",
"Help": "Aide",
"Home page": "Page d'accueil",
"Ignore": "Ignorer",
"Ignore Patterns": "Règles d'exclusion",
"Ignore Permissions": "Ignorer les permissions",
"Ignored Devices": "Ignored Devices",
"Ignored Folders": "Ignored Folders",
"Ignored at": "Ignored at",
"Incoming Rate Limit (KiB/s)": "Limite du débit de réception (Kio/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Une configuration incorrecte peut créer des dommages dans vos répertoires et mettre Syncthing hors-service.",
"Introduced By": "Introduit par",
"Introducer": "Appareil introducteur",
"Inversion of the given condition (i.e. do not exclude)": "Inverser la condition donnée (i.e. ne pas exclure)",
"Keep Versions": "Combien de versions conserver",
"Largest First": "Les plus volumineux d'abord",
"Last File Received": "Dernier changement",
"Last Scan": "Dernière analyse",
"Last seen": "Dernière apparition",
"Later": "Plus tard",
"Latest Change": "Dernier changement",
"Learn more": "En savoir plus",
"Listeners": "Systèmes à l'écoute",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
"Local Discovery": "Découverte locale",
"Local State": "État local",
"Local State (Total)": "État local (Total)",
"Log": "Log",
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
"Logs": "Logs",
"Major Upgrade": "Mise à jour majeure",
"Mass actions": "Mass actions",
"Master": "Maître",
"Maximum Age": "Ancienneté maximum",
"Metadata Only": "Métadonnées uniquement",
"Minimum Free Disk Space": "Espace disque libre minimum",
"Mod. Device": "Mod. Device",
"Mod. Time": "Mod. Time",
"Move to top of queue": "Déplacer en haut de la file",
"Multi level wildcard (matches multiple directory levels)": "Joker multi niveaux (correspond aux répertoires et sous-répertoires)",
"Never": "Jamais",
"New Device": "Nouvel appareil",
"New Folder": "Nouveau partage",
"Newest First": "Les plus récents en premier",
"No": "Non",
"No File Versioning": "Pas de préservation",
"No files will be deleted as a result of this operation.": "No files will be deleted as a result of this operation.",
"No upgrades": "Pas de mises à jour",
"Normal": "Normal",
"Notice": "Notification",
"OK": "OK",
"Off": "Désactivé(e)",
"Oldest First": "Les plus anciens en premier",
"Optional descriptive label for the folder. Can be different on each device.": "Nom convivial du partage, à votre guise. il peut être différent sur chaque appareil.",
"Options": "Options",
"Out of Sync": "Désynchronisé",
"Out of Sync Items": "Éléments non synchronisés",
"Outgoing Rate Limit (KiB/s)": "Limite du débit d'envoi (Kio/s)",
"Override Changes": "Écraser les changements",
"Path": "Chemin",
"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": "Chemin vers le répertoire à partager dans l'appareil local. Il sera créé s'il n'existe pas. Vous pouvez entrer un chemin absolu (p.ex \"/home/moi/Sync/Exemple\") ou relatif à celui du programme (p.ex \"..\\Partages\\Exemple\" - utile pour installation portable). Le caractère tilde (~, ou ~+Espace sous Windows XP+Azerty) peut être utilisé comme raccourci vers",
"Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.": "Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {{tilde}}.",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Chemin où les versions doivent être conservées (laisser vide pour le chemin par défaut de .stversions dans le répertoire partagé).\nChemin relatif ou absolu (recommandé), mais dans un répertoire non synchronisé (par masque ou hors du chemin du partage).\nSur la même partition ou système de fichiers (recommandé).",
"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 partagé).\nChemin relatif ou absolu (recommandé), mais dans un répertoire non synchronisé (par masque ou hors du chemin du partage).\nSur la même partition ou système de fichiers (recommandé).",
"Pause": "Pause",
"Pause All": "Tout suspendre",
"Paused": "En pause",
"Pending changes": "Pending changes",
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:",
"Permissions": "Permissions",
"Please consult the release notes before performing a major upgrade.": "Veuillez consulter les notes de version avant de réaliser une mise à jour majeure.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Veuillez définir un nom d'utilisateur et un mot de passe dans les réglages.",
"Please wait": "Merci de patienter",
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefix indicating that the file can be deleted if preventing directory removal",
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefix indicating that the pattern should be matched without case sensitivity",
"Preview": "Aperçu",
"Preview Usage Report": "Aperçu du rapport de statistiques d'utilisation",
"Quick guide to supported patterns": "Guide rapide des masques compatibles ci-dessous",
"RAM Utilization": "Utilisation de la RAM",
"Random": "Aléatoire",
"Receive Only": "Receive Only",
"Recent Changes": "Recent Changes",
"Reduced by ignore patterns": "(Limité par des masques d'exclusion)",
"Release Notes": "Notes de version",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Les versions préliminaires contiennent les dernières fonctionnalités et derniers correctifs. Elles sont identiques aux traditionnelles mises à jour bimensuelles.",
"Remote Devices": "Autres appareils",
"Remove": "Enlever",
"Remove Device": "Remove Device",
"Remove Folder": "Remove Folder",
"Required identifier for the folder. Must be the same on all cluster devices.": "Identifiant du partage. Doit être le même sur tous les appareils concernés.",
"Rescan": "Réanalyser",
"Rescan All": "Tout réanalyser",
"Rescan Interval": "Intervalle d'analyse",
"Rescans": "Rescans",
"Restart": "Redémarrer",
"Restart Needed": "Redémarrage nécessaire",
"Restarting": "Redémarrage en cours",
"Restore": "Restore",
"Restore Versions": "Restore Versions",
"Resume": "Reprise",
"Resume All": "Tout libérer",
"Reused": "Réutilisé",
"Revert Local Changes": "Revert Local Changes",
"Running": "Running",
"Save": "Enregistrer",
"Scan Time Remaining": "Temps d'analyse restant",
"Scanning": "Analyse en cours",
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select a version": "Select a version",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",
"Select the devices to share this folder with.": "Synchroniser avec :",
"Select the folders to share with this device.": "Sélectionner les partages auxquels participe cet appareil.",
"Send & Receive": "Envoi & réception",
"Send Only": "Envoi (lecture seule)",
"Settings": "Configuration",
"Share": "Partager",
"Share Folder": "Partager",
"Share Folders With Device": "Partages avec cet appareil",
"Share With Devices": "Synchroniser avec des appareils",
"Share this folder?": "Acceptez-vous ce partage ?",
"Shared With": "Synchronisé avec",
"Sharing": "Sharing",
"Show ID": "Afficher mon ID",
"Show QR": "Afficher l'image QR",
"Show diff with previous version": "Show diff with previous version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Sera diffusé aux autres appareils comme nom convivial optionnel par défaut.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Si laissé vide, il sera renseigné par le nom convivial proposé par l'appareil distant.",
"Shutdown": "Arrêter",
"Shutdown Complete": "Arrêté !",
"Simple File Versioning": "Suivi simplifié des versions",
"Single level wildcard (matches within a directory only)": "Joker à un seul niveau (correspond uniquement à lintérieur du répertoire)",
"Size": "Size",
"Smallest First": "Les plus petits d'abord",
"Some items could not be restored:": "Some items could not be restored:",
"Source Code": "Code source",
"Stable releases and release candidates": "Versions stables et préliminaires",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Les versions stables sont reportées d'environ deux semaines. Pendant ce temps elles sont testées en tant que versions préliminaires.",
"Stable releases only": "Seulement les versions stables",
"Staggered File Versioning": "Versions échelonnées",
"Start Browser": "Lancer le navigateur web",
"Statistics": "Statistiques",
"Stopped": "Arrêté",
"Support": "Forum",
"Support Bundle": "Support Bundle",
"Sync Protocol Listen Addresses": "Adresses d'écoute du protocole de synchronisation",
"Syncing": "En cours de synchronisation",
"Syncthing has been shut down.": "Syncthing a été arrêté.",
"Syncthing includes the following software or portions thereof:": "Syncthing intègre les logiciels suivants (ou des éléments provenant de ces logiciels) :",
"Syncthing is restarting.": "Syncthing redémarre.",
"Syncthing is upgrading.": "Syncthing se met à jour.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing semble être arrêté, ou il y a un problème avec votre connexion Internet. Nouvelle tentative ...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing semble avoir un problème pour traiter votre demande. S'il vous plaît, rafraîchissez la page ou redémarrez Syncthing si le problème persiste.",
"Take me back": "Take me back",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.",
"The Syncthing admin interface is configured to allow remote access without a password.": "L'interface d'administration de Syncthing est configuré pour accepter l'accès distant sans mot de passe !",
"The aggregated statistics are publicly available at the URL below.": "Les statistiques agrégées sont publiquement disponibles à l'adresse ci-dessous.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuration a été enregistrée mais pas activée. Syncthing doit redémarrer afin d'activer la nouvelle configuration.",
"The device ID cannot be blank.": "L'ID de l'appareil ne peut être vide.",
"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).": "L'ID d'appareil à saisir ici se trouve dans le menu \"Actions > Afficher mon ID\" sur l'appareil distant. Les tirets et espaces sont optionnels (et ignorés).",
"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.": "Le rapport d'utilisation chiffré est envoyé quotidiennement. Il sert à répertorier les plates-formes utilisées, la taille des partages et les versions de l'application. Si le jeu de données rapportées devait être changé, il vous serait demandé de valider de nouveau son envoi via ce message. Vous pouvez revenir sur votre décision via Actions/Configuration, et agir sur la fréquence d'envoi via Actions/Avancé/Options (urInitialDelayS).",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "L'ID de l'appareil inséré ne semble pas valide. Il devrait ressembler à une chaîne de 52 ou 56 caractères comprenant des lettres, des chiffres et potentiellement des espaces et des traits d'union.",
"The first command line parameter is the folder path and the second parameter is the relative path in the folder.": "Le premier paramètre de ligne de commande est le chemin du répertoire partagé, et le second est le chemin relatif dans le répertoire.",
"The folder ID cannot be blank.": "L'identifiant du partage ne peut être vide.",
"The folder ID must be unique.": "L'ID du partage doit être unique.",
"The folder path cannot be blank.": "Le chemin vers le répertoire ne peut pas être vide.",
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Les seuils de durée suivants définissent le nombre maximum de versions pour chaque fichier : pendant la première heure une version peut être conservée toutes les 30 secondes. Jusqu'à un jour, jusqu'à une version par heure - des versions de la première heure sont alors progressivement effacées pour n'en garder qu'une par heure. Jusqu'à 30 jours, jusqu'à une version par jour - des versions horaires du premier jour sont alors progressivement effacées pour n'en garder qu'une par jour. Au-delà, jusqu'à la limite d'âge, jusqu'à une version est conservée par semaine - des versions journalières du premier mois sont alors progressivement effacées pour n'en garder qu'une par semaine.",
"The following items could not be synchronized.": "Les fichiers suivants n'ont pas pu être synchronisés.",
"The maximum age must be a number and cannot be blank.": "L'âge maximum doit être un nombre et ne peut être vide.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Durée maximum de conservation d'une version (en jours, 0 pour conserver les versions indéfiniment)",
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Le pourcentage d'espace disque libre doit être un nombre positif compris entre 0 et 100 (inclus).",
"The number of days must be a number and cannot be blank.": "Le nombre de jours doit être numérique et ne peut pas être vide.",
"The number of days to keep files in the trash can. Zero means forever.": "Le nombre de jours de conservation des fichiers dans la poubelle. 0 signifie toujours.",
"The number of old versions to keep, per file.": "Le nombre maximum d'anciennes versions à garder indéfiniment, par fichier.",
"The number of versions must be a number and cannot be blank.": "Le nombre de versions doit être numérique, et ne peut pas être vide.",
"The path cannot be blank.": "Le chemin ne peut pas être vide.",
"The rate limit must be a non-negative number (0: no limit)": "La limite de débit ne doit pas être négative (0 = pas de limite)",
"The 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 automatiquement retentés et synchronisés quand l'erreur sera résolue.",
"This Device": "Cet appareil",
"This can easily give hackers access to read and change any files on your computer.": "Ceci peut aisément permettre à un intrus de lire et modifier n'importe quel fichier de votre ordinateur. ",
"This is a major version upgrade.": "Il s'agit d'une mise à jour majeure.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Ce réglage contrôle l'espace disque requis dans le disque qui abrite votre répertoire utilisateur (pour la base de données d'indexation).",
"Time": "Heure",
"Time the item was last modified": "Time the item was last modified",
"Trash Can File Versioning": "Style poubelle",
"Type": "Type",
"Unavailable": "Unavailable",
"Unavailable/Disabled by administrator or maintainer": "Unavailable/Disabled by administrator or maintainer",
"Undecided (will prompt)": "Undecided (will prompt)",
"Unignore": "Unignore",
"Unknown": "Inconnu",
"Unshared": "Non partagé",
"Unused": "Non utilisé",
"Up to Date": "À jour",
"Updated": "Mis à jour",
"Upgrade": "Mettre à jour",
"Upgrade To {%version%}": "Mettre à jour vers {{version}}",
"Upgrading": "Mise à jour de Syncthing",
"Upload Rate": "Débit d'envoi",
"Uptime": "Durée de fonctionnement",
"Usage reporting is always enabled for candidate releases.": "Les statistiques d'utilisation sont toujours envoyées pour les versions préliminaires.",
"Use HTTPS for GUI": "Utiliser l'HTTPS pour le GUI",
"Version": "Version",
"Versions": "Versions",
"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 plus anciennes versions seront supprimées automatiquement quand elles dépassent la durée maximum de conservation ou si leur nombre (par fichier) est supérieur à la limite prédéfinie pour l'intervalle.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Attention, ce chemin est un répertoire parent d'au moins un partage existant (par exemple \"{{otherFolder}}\"). Si vous continuez, vous devriez créer un nouveau sous-répertoire, sinon ceci peut causer des problèmes tels que duplications et/ou suppressions intempestives de fichiers.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Attention, ce chemin est un répertoire parent d'au moins un partage existant (par exemple \"{{otherFolderLabel}}\" ({{otherFolder}})). Si vous continuez, vous devriez créer un nouveau sous-répertoire, sinon ceci peut causer des problèmes tels que duplications et/ou suppressions intempestives de fichiers.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "ATTENTION, ce chemin est un sous-répertoire du partage existant \"{{otherFolder}}\". Ceci peut causer des problèmes tels que duplications et/ou suppressions intempestives de fichiers.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "ATTENTION, ce chemin est un sous-répertoire du partage existant \"{{otherFolderLabel}}\" ({{otherFolder}}). Ceci peut causer des problèmes tels que duplications et/ou suppressions intempestives de fichiers.",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Warning: If you are using an external watcher like {{syncthingInotify}}, you should make sure it is deactivated.",
"Watch for Changes": "Watch for Changes",
"Watching for Changes": "Watching for Changes",
"When adding a new device, keep in mind that this device must be added on the other side too.": "Lorsque vous ajoutez un appareil, gardez à l'esprit que le votre 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 partage est ajouté, gardez à l'esprit que son ID est utilisée pour lier les répertoires à travers les appareils. L'ID est sensible à la casse et sera forcément la même sur tous les appareils participant à ce partage.",
"Yes": "Oui",
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
"You can change your choice at any time in the Settings dialog.": "Vous pouvez changer votre choix dans la boîte de dialogue \"Configuration\".",
"You can read more about the two release channels at the link below.": "Vous pouvez en savoir plus sur les deux canaux de distribution via le lien ci-dessous.",
"You have no ignored devices.": "You have no ignored devices.",
"You have no ignored folders.": "You have no ignored folders.",
"You have unsaved changes. Do you really want to discard them?": "You have unsaved changes. Do you really want to discard them?",
"You must keep at least one version.": "Vous devez garder au minimum une version.",
"days": "Jours",
"directories": "répertoires",
"files": "fichiers",
"full documentation": "Documentation complète ici",
"items": "éléments",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vous invite au partage \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vous invite au partage \"{{folderlabel}}\" ({{folder}})."
}

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Outils de débogage",
"Default Folder Path": "Chemin parent par défaut pour les nouveaux partages",
"Deleted": "Supprimé",
"Deselect All": "Tout déselectionner",
"Device": "Appareil",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" ({{device}}), appareil actuellement à {{address}}, demande à se connecter.\nAcceptez-vous de l'ajouter à votre liste d'appareils connus ?",
"Device ID": "ID de l'appareil",
@@ -156,6 +157,7 @@
"Later": "Plus tard",
"Latest Change": "Dernier changement",
"Learn more": "En savoir plus",
"Limit": "Limite",
"Listeners": "Systèmes en écoute",
"Loading data...": "Chargement des données...",
"Loading...": "Chargement...",
@@ -247,6 +249,7 @@
"Scanning": "Analyse",
"See external versioner help for supported templated command line parameters.": "Voir l'aide sur la préservation externe des fichiers pour les paramètres supportés en lignes de commande dans les modèles.",
"See external versioning help for supported templated command line parameters.": "Consulter l'aide à la gestion externe des versions pour voir les paramètres de ligne de commande supportés.",
"Select All": "Tout sélectionner",
"Select a version": "Choisissez une version",
"Select latest version": "Restaurer la dernière version",
"Select oldest version": "Restaurer la plus ancienne version",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Debug-foarsjennings",
"Default Folder Path": "Standert Map-paad",
"Deleted": "Fuortsmiten",
"Deselect All": "Alles Deselektearje",
"Device": "Apparaat",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Apparaat \"{{name}}\" {{device}} op ({{address}}) wol ferbining meitsje. Nij apparaat taheakje?",
"Device ID": "Apparaat-ID",
@@ -156,6 +157,7 @@
"Later": "Letter",
"Latest Change": "Meast Resinte Feroarings",
"Learn more": "Mear witte",
"Limit": "Limit",
"Listeners": "Harkers",
"Loading data...": "Data oan it laden...",
"Loading...": "Oan it laden...",
@@ -247,6 +249,7 @@
"Scanning": "Oan it skennen",
"See external versioner help for supported templated command line parameters.": "Sjoch de eksterne help fan fersjebehearder foar stipe foarbylden fan kommando-rige-parameters.",
"See external versioning help for supported templated command line parameters.": "Sjoch de eksterne help fan fersjebehearder foar stipe foarbylden fan kommando-rige-parameters.",
"Select All": "Alles Selektearje",
"Select a version": "Kies in ferzje",
"Select latest version": "Selektearje de nijste ferzje",
"Select oldest version": "Selektearje de âldste ferzje",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Hibakeresési képességek",
"Default Folder Path": "Alapértelmezett mappa útvonala",
"Deleted": "Törölve",
"Deselect All": "Kijelölés megszüntetése",
"Device": "Eszköz",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" eszköz ({{device}} @ {{address}}) szeretne csatlakozni. Hozzáadható az új eszköz?",
"Device ID": "Eszközazonosító",
@@ -156,6 +157,7 @@
"Later": "Később",
"Latest Change": "Utolsó módosítás",
"Learn more": "Tudj meg többet",
"Limit": "Sebességkorlát",
"Listeners": "Kapcsolatok",
"Loading data...": "Adatok betöltése...",
"Loading...": "Betöltés...",
@@ -193,7 +195,7 @@
"Options": "Opciók",
"Out of Sync": "Nincs szinkronban",
"Out of Sync Items": "Nem szinkronizált elemek",
"Outgoing Rate Limit (KiB/s)": "Kimenő sávszélesség (KiB/mp)",
"Outgoing Rate Limit (KiB/s)": "Kimenő sebességkorlát (KiB/mp)",
"Override Changes": "Változtatások felülbírálása",
"Path": "Útvonal",
"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": "A mappa elérési útvonala az eszközön. Amennyiben nem létezik, a program automatikusan létrehozza. A hullámvonal (~) a következő helyettesítésre használható: ",
@@ -247,6 +249,7 @@
"Scanning": "Átnézés",
"See external versioner help for supported templated command line parameters.": "A támogatott parancssori paraméter sablonokat a külső verziókezelő súgójában találod.",
"See external versioning help for supported templated command line parameters.": "A támogatott parancssori paraméter sablonokat a külső verziókezelő súgójában találod.",
"Select All": "Mindent kijelöl",
"Select a version": "Válassz egy verziót",
"Select latest version": "Legfrissebb verzió kijelölése",
"Select oldest version": "Legrégebbi verzió kijelölése",
@@ -293,7 +296,7 @@
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Úgy tűnik, hogy a Syncthing nem működik, vagy valami probléma van a hálózati kapcsolattal. Újra próbálom...",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Úgy tűnik, hogy a Syncthing problémába ütközött a kérés feldolgozása során. Ha a probléma továbbra is fennáll, akkor frissíteni kell az oldalt, vagy újra kell indítani a Syncthinget.",
"Take me back": "Vissza",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "A grafikus felület címét egy indítási beállítás felülírta. Az itt történő módosítás hatástalan marad, amíg ez a felülírás érvényben van.",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "A grafikus felület címét az indítási beállítások felülírták. Az itt történő módosítások hatástalanok maradnak, amíg a felülírás érvényben van.",
"The Syncthing admin interface is configured to allow remote access without a password.": "A Syncthing adminisztrációs felületének távoli elérése be van kapcsolva jelszó nélkül.",
"The aggregated statistics are publicly available at the URL below.": "Az összesített statisztikák elérhetők az alábbi címen.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "A beállítások elmentésre kerültek, de nem lettek aktiválva. Újra kell indítani a Syncthing-et az aktiválásukhoz.",

View File

@@ -10,7 +10,7 @@
"Add Device": "Aggiungi Dispositivo",
"Add Folder": "Aggiungi Cartella",
"Add Remote Device": "Aggiungi Dispositivo Remoto",
"Add devices from the introducer to our device list, for mutually shared folders.": "Aggiungi dispositivi dall'introduttore al nostro elenco, per le cartelle condivise reciprocamente.",
"Add devices from the introducer to our device list, for mutually shared folders.": "Aggiungi dispositivi dall'introduttore all'elenco dei dispositivi, per cartelle condivise reciprocamente.",
"Add new folder?": "Aggiungere una nuova cartella?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Inoltre, verrà incrementato l'intervallo di scansione completo (60 volte, vale a dire un nuovo default di 1h). Puoi anche configurarlo manualmente per ogni cartella dopo aver scelto No.",
"Address": "Indirizzo",
@@ -60,6 +60,7 @@
"Debugging Facilities": "Servizi di Debug",
"Default Folder Path": "Percorso Cartella di Default",
"Deleted": "Cancellato",
"Deselect All": "Deseleziona tutto",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Il dispositivo \"{{name}}\" ({{device}} - {{address}}) chiede di connettersi. Aggiungere il nuovo dispositivo?",
"Device ID": "ID Dispositivo",
@@ -140,7 +141,7 @@
"Ignore": "Ignora",
"Ignore Patterns": "Schemi Esclusione File",
"Ignore Permissions": "Ignora Permessi",
"Ignored Devices": "Dispositivi Ignorati",
"Ignored Devices": "Dispositivi ignorati",
"Ignored Folders": "Cartelle ignorate",
"Ignored at": "Ignorato a",
"Incoming Rate Limit (KiB/s)": "Limite Velocità in Ingresso (KiB/s)",
@@ -156,6 +157,7 @@
"Later": "Più Tardi",
"Latest Change": "Ultima Modifica",
"Learn more": "Impara di piu",
"Limit": "Limite",
"Listeners": "In Ascolto",
"Loading data...": "Caricamento dati...",
"Loading...": "Caricamento...",
@@ -203,7 +205,7 @@
"Pause": "Pausa",
"Pause All": "Pausa Tutti",
"Paused": "In Pausa",
"Pending changes": "Modifiche in attessa",
"Pending changes": "Modifiche in attesa",
"Periodic scanning at given interval and disabled watching for changes": "Scansione periodica a intervalli determinati e monitoraggio cambiamenti disabilitata",
"Periodic scanning at given interval and enabled watching for changes": "Scansione periodica a intervalli determinati e monitoraggio cambiamenti abilitata",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Scansione periodica a intervalli determinati e configurazione fallita del monitoraggio cambiamenti, nuovo tentativo ogni 1m:",
@@ -247,6 +249,7 @@
"Scanning": "Scansione in corso",
"See external versioner help for supported templated command line parameters.": "Consultare la guida al controllo di versione per i modelli dei parametri di riga di comando supportati.",
"See external versioning help for supported templated command line parameters.": "Consultare la guida al controllo di versione per i modelli dei parametri di riga di comando supportati.",
"Select All": "Seleziona Tutto",
"Select a version": "Seleziona una versione",
"Select latest version": "Seleziona l'ultima versione",
"Select oldest version": "Seleziona la versione più vecchia",
@@ -329,7 +332,7 @@
"Unavailable": "Non disponibile",
"Unavailable/Disabled by administrator or maintainer": "Non disponibile/Disabilitato dall'amministratore o dal manutentore",
"Undecided (will prompt)": "Non deciso (verrà richiesto)",
"Unignore": "Non ingorare",
"Unignore": "Non ignorare",
"Unknown": "Sconosciuto",
"Unshared": "Non Condiviso",
"Unused": "Non Utilizzato",
@@ -359,8 +362,8 @@
"You can also select one of these nearby devices:": "È anche possibile selezionare uno di questi dispositivi nelle vicinanze:",
"You can change your choice at any time in the Settings dialog.": "Puoi sempre cambiare la tua scelta nel dialogo Impostazioni.",
"You can read more about the two release channels at the link below.": "Puoi ottenere piu informazioni riguarda i due canali di rilascio nel collegamento sottostante.",
"You have no ignored devices.": "Non ignorare i dispositivi.",
"You have no ignored folders.": "Non ignorare le cartelle.",
"You have no ignored devices.": "Non ci sono dispositivi ignorati.",
"You have no ignored folders.": "Non ci sono cartelle ignorate.",
"You have unsaved changes. Do you really want to discard them?": "Hai modifiche non salvate. Vuoi davvero scartarle?",
"You must keep at least one version.": "È necessario mantenere almeno una versione.",
"days": "giorni",

View File

@@ -31,7 +31,7 @@
"Are you sure you want to remove device {%name%}?": "Are you sure you want to remove device {{name}}?",
"Are you sure you want to remove folder {%label%}?": "Are you sure you want to remove folder {{label}}?",
"Are you sure you want to restore {%count%} files?": "Are you sure you want to restore {{count}} files?",
"Auto Accept": "Auto Accept",
"Auto Accept": "自動承諾",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "自動アップグレードは、安定版とリリース候補版のいずれかを選べるようになりました。",
"Automatic upgrades": "自動アップグレード",
"Automatically create or share folders that this device advertises at the default path.": "Automatically create or share folders that this device advertises at the default path.",
@@ -49,7 +49,7 @@
"Configured": "設定値",
"Connection Error": "接続エラー",
"Connection Type": "接続種別",
"Connections": "Connections",
"Connections": "接続",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.",
"Copied from elsewhere": "別ファイルからコピー済",
"Copied from original": "元ファイルからコピー済",
@@ -60,19 +60,20 @@
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Default Folder Path",
"Deleted": "削除",
"Deselect All": "すべて選択解除",
"Device": "デバイス",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "デバイス「{{name}}」 ({{address}} の {{device}}) が接続を求めています。新しいデバイスとして追加しますか?",
"Device ID": "デバイスID",
"Device Identification": "デバイスID",
"Device Name": "デバイス名",
"Device rate limits": "Device rate limits",
"Device rate limits": "デバイス速度制限",
"Device that last modified the item": "Device that last modified the item",
"Devices": "デバイス",
"Disabled": "無効",
"Disabled periodic scanning and disabled watching for changes": "Disabled periodic scanning and disabled watching for changes",
"Disabled periodic scanning and enabled watching for changes": "Disabled periodic scanning and enabled watching for changes",
"Disabled periodic scanning and disabled watching for changes": "定期スキャンと変更の監視はいずれも無効です",
"Disabled periodic scanning and enabled watching for changes": "定期スキャンは無効で変更の監視は有効です",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:",
"Discard": "Discard",
"Discard": "破棄",
"Disconnected": "切断中",
"Discovered": "探索結果",
"Discovery": "探索サーバー",
@@ -91,7 +92,7 @@
"Editing {%path%}.": "{{path}} を編集中",
"Enable NAT traversal": "NATトラバーサルを有効にする",
"Enable Relaying": "中継サーバー経由の通信を有効にする",
"Enabled": "Enabled",
"Enabled": "有効",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "0以上の数 (例: 2.35) を入力し、単位を選択してください。パーセントはディスク容量全体に対する割合です。",
"Enter a non-privileged port number (1024 - 65535).": "非特権ポート番号 (1024 - 65535) を入力してください。",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "アドレスを指定する場合は「tcp://IPアドレス:ポート, tcp://ホスト名:ポート」のようにコンマで区切って入力してください。自動探索を行う場合は「dynamic」と入力してください。",
@@ -129,7 +130,7 @@
"GUI Listen Address": "GUI待ち受けアドレス",
"GUI Listen Addresses": "GUI待ち受けアドレス",
"GUI Theme": "GUIテーマ",
"General": "General",
"General": "一般",
"Generate": "生成",
"Global Changes": "全変更点",
"Global Discovery": "グローバル探索",
@@ -140,9 +141,9 @@
"Ignore": "無視",
"Ignore Patterns": "無視するファイル名",
"Ignore Permissions": "パーミッションを無視する",
"Ignored Devices": "Ignored Devices",
"Ignored Folders": "Ignored Folders",
"Ignored at": "Ignored at",
"Ignored Devices": "無視したデバイス",
"Ignored Folders": "無視したフォルダー",
"Ignored at": "無視指定日時",
"Incoming Rate Limit (KiB/s)": "下り帯域制限 (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "間違った設定を行うと、フォルダーの内容を壊したり、Syncthingが動作しなくなる可能性があります。",
"Introduced By": "紹介元",
@@ -156,16 +157,17 @@
"Later": "後で設定",
"Latest Change": "最終変更内容",
"Learn more": "詳細を確認する",
"Limit": "Limit",
"Listeners": "待ち受けポート",
"Loading data...": "Loading data...",
"Loading...": "Loading...",
"Local Discovery": "LAN内で探索",
"Local State": "ローカル状態",
"Local State (Total)": "ローカル状態 (合計)",
"Log": "Log",
"Log": "ログ",
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
"Logs": "Logs",
"Logs": "ログ",
"Major Upgrade": "メジャーアップグレード",
"Mass actions": "Mass actions",
"Master": "マスター",
@@ -203,7 +205,7 @@
"Pause": "一時停止",
"Pause All": "すべて一時停止",
"Paused": "一時停止中",
"Pending changes": "Pending changes",
"Pending changes": "保留中の変更",
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:",
@@ -219,7 +221,7 @@
"RAM Utilization": "メモリ使用量",
"Random": "ランダム",
"Receive Only": "Receive Only",
"Recent Changes": "Recent Changes",
"Recent Changes": "最近の変更点",
"Reduced by ignore patterns": "無視パターン該当分を除く",
"Release Notes": "リリースノート",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "リリース候補版には最新の機能と修正が含まれます。これは従来の隔週リリースに近いものです。",
@@ -247,6 +249,7 @@
"Scanning": "スキャン中",
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
"See external versioning help for supported templated command line parameters.": "使用可能なコマンドラインパラメータについてはヘルプの外部バージョン管理の項目を参照してください。",
"Select All": "すべて選択",
"Select a version": "バージョンを選択してください",
"Select latest version": "Select latest version",
"Select oldest version": "Select oldest version",
@@ -261,7 +264,7 @@
"Share With Devices": "共有するデバイス",
"Share this folder?": "このフォルダーを共有しますか?",
"Shared With": "共有中のデバイス",
"Sharing": "Sharing",
"Sharing": "共有",
"Show ID": "IDを表示",
"Show QR": "QRコードを表示",
"Show diff with previous version": "前バージョンとの差分を表示",
@@ -329,7 +332,7 @@
"Unavailable": "Unavailable",
"Unavailable/Disabled by administrator or maintainer": "Unavailable/Disabled by administrator or maintainer",
"Undecided (will prompt)": "未決定(再確認する)",
"Unignore": "Unignore",
"Unignore": "無視を解除",
"Unknown": "不明",
"Unshared": "非共有",
"Unused": "未使用",
@@ -352,16 +355,16 @@
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolderLabel}}」 ({{otherFolder}}) のサブディレクトリです。",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Warning: If you are using an external watcher like {{syncthingInotify}}, you should make sure it is deactivated.",
"Watch for Changes": "Watch for Changes",
"Watching for Changes": "Watching for Changes",
"Watching for Changes": "変更の監視",
"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": "はい",
"You can also select one of these nearby devices:": "近くに検出された以下のデバイスの一つを選択できます。",
"You can change your choice at any time in the Settings dialog.": "この設定はいつでも変更できます。",
"You can read more about the two release channels at the link below.": "2種類のリリースチャネルについての詳細は、以下のリンク先を参照してください。",
"You have no ignored devices.": "You have no ignored devices.",
"You have no ignored folders.": "You have no ignored folders.",
"You have unsaved changes. Do you really want to discard them?": "You have unsaved changes. Do you really want to discard them?",
"You have no ignored devices.": "無視したデバイスはありません。",
"You have no ignored folders.": "無視したフォルダーはありません。",
"You have unsaved changes. Do you really want to discard them?": "未保存の変更があります。本当に破棄してよろしいですか?",
"You must keep at least one version.": "少なくとも一つのバージョンを保持する必要があります。",
"days": "日",
"directories": "個のディレクトリ",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "디버깅 기능",
"Default Folder Path": "기본 폴더 경로",
"Deleted": "삭제됨",
"Deselect All": "Deselect All",
"Device": "기기",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "다른 기기 {{device}} ({{address}}) 에서 접속을 요청했습니다. 새 장치를 추가하시겠습니까?",
"Device ID": "기기 ID",
@@ -156,6 +157,7 @@
"Later": "나중에",
"Latest Change": "최신 변경",
"Learn more": "더 알아보기",
"Limit": "Limit",
"Listeners": "수신자",
"Loading data...": "데이터 불러오는중...",
"Loading...": "불러오는 중...",
@@ -247,6 +249,7 @@
"Scanning": "탐색중",
"See external versioner help for supported templated command line parameters.": "지원되는 템플릿 명령 행 매개 변수에 대해서는 외부 버전 도움말을 참조하십시오.",
"See external versioning help for supported templated command line parameters.": "지원되는 템플릿 명령 행 매개 변수에 대해서는 외부 버전 도움말을 참조하십시오.",
"Select All": "Select All",
"Select a version": "버전 선택",
"Select latest version": "가장 최신 버전 선택",
"Select oldest version": "가장 오래된 버전 선택",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Derinimo priemonės",
"Default Folder Path": "Numatytojo aplanko kelias",
"Deleted": "Ištrinta",
"Deselect All": "Nuimti žymėjimą nuo visų",
"Device": "Įrenginys",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Įrenginys \"{{name}}\" ({{device}} {{address}}) nori prisijungti. Pridėti naują įrenginį?",
"Device ID": "Įrenginio ID",
@@ -143,7 +144,7 @@
"Ignored Devices": "Nepaisomi įrenginiai",
"Ignored Folders": "Nepaisomi aplankai",
"Ignored at": "Nepaisoma ties",
"Incoming Rate Limit (KiB/s)": "Įeinančio srauto maksimalus greitis (KiB/s)",
"Incoming Rate Limit (KiB/s)": "Atsiunčiamo srauto maksimalus greitis (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Neteisinga konfigūracija gali pažeisti jūsų aplankų turinį ir padaryti Syncthing neoperuotina.",
"Introduced By": "Supažindė",
"Introducer": "Supažindintojas",
@@ -156,6 +157,7 @@
"Later": "Vėliau",
"Latest Change": "Paskutinis pakeitimas",
"Learn more": "Sužinoti daugiau",
"Limit": "Apribojimas",
"Listeners": "Klausytojai",
"Loading data...": "Įkeliami duomenys...",
"Loading...": "Įkeliama...",
@@ -193,7 +195,7 @@
"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)",
"Outgoing Rate Limit (KiB/s)": "Išsiunčiamo srauto maksimalus greitis (KiB/s)",
"Override Changes": "Perrašyti pakeitimus",
"Path": "Kelias",
"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",
@@ -247,6 +249,7 @@
"Scanning": "Skenuojama",
"See external versioner help for supported templated command line parameters.": "Palaikomiems šabloniniams komandų eilutės parametrams, žiūrėkite išorinį versijų valdymo programos žinyną.",
"See external versioning help for supported templated command line parameters.": "Palaikomiems šabloniniams komandų eilutės parametrams, žiūrėkite išorinį versijų valdymo žinyną.",
"Select All": "Žymėti visus",
"Select a version": "Pasirinkti versiją",
"Select latest version": "Pasirinkti paskiausią versiją",
"Select oldest version": "Pasirinkti seniausią versiją",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Feilrettingsverktøy",
"Default Folder Path": "Forvalgt mappeplassering",
"Deleted": "Slettet",
"Deselect All": "Deselect All",
"Device": "Enhet",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enhet \"{{name}}\" ({{device}} på {{address}}) ønsker å koble til. Legge til ny enhet?",
"Device ID": "Enhets-ID",
@@ -156,6 +157,7 @@
"Later": "Senere",
"Latest Change": "Sist endret",
"Learn more": "Lær mer",
"Limit": "Limit",
"Listeners": "Lyttere",
"Loading data...": "Laster inn data…",
"Loading...": "Laster…",
@@ -247,6 +249,7 @@
"Scanning": "Gjennomsøker",
"See external versioner help for supported templated command line parameters.": "Se ekstern versjoneringshjelp for støttede mal-baserte kommandolinjeparameter.",
"See external versioning help for supported templated command line parameters.": "Se ekstern versjoneringshjelp for støttede mal-baserte kommandolinjeparameter.",
"Select All": "Select All",
"Select a version": "Velg en versjon",
"Select latest version": "Velg siste versjon",
"Select oldest version": "Velg eldste versjon",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Debugmogelijkheden",
"Default Folder Path": "Standaardmaplocatie",
"Deleted": "Verwijderd",
"Deselect All": "Alles deselecteren",
"Device": "Apparaat",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Apparaat \"{{name}}\" ({{device}} op {{address}}) wil verbinden. Nieuw apparaat toevoegen?",
"Device ID": "Apparaat-ID",
@@ -156,6 +157,7 @@
"Later": "Later",
"Latest Change": "Laatste wijziging",
"Learn more": "Lees meer",
"Limit": "Begrenzing",
"Listeners": "Luisteraars",
"Loading data...": "Gegevens laden...",
"Loading...": "Laden...",
@@ -247,6 +249,7 @@
"Scanning": "Scannen",
"See external versioner help for supported templated command line parameters.": "Bekijk de documentatie van de externe versiebeheerder voor ondersteunde sjabloon-opdrachtregelparameters.",
"See external versioning help for supported templated command line parameters.": "Bekijk de documentatie van extern versiebeheer voor ondersteunde sjabloon-opdrachtregelparameters.",
"Select All": "Alles selecteren",
"Select a version": "Selecteer een versie",
"Select latest version": "Laatste versie selecteren",
"Select oldest version": "Oudste versie selecteren",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Odpluskwianie",
"Default Folder Path": "Domyślna ścieżka folderu",
"Deleted": "Usunięto",
"Deselect All": "Odznacz wszystko",
"Device": "Urządzenie",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Urządzenie \"{{name}}\" {{device}} ({{address}}) chce się połączyć. Dodać nowe urządzenie?",
"Device ID": "ID urządzenia",
@@ -142,7 +143,7 @@
"Ignore Permissions": "Ignoruj uprawnienia",
"Ignored Devices": "Ignorowane urządzenia",
"Ignored Folders": "Ignorowane katalogi",
"Ignored at": "Ignored at",
"Ignored at": "Ignorowane od",
"Incoming Rate Limit (KiB/s)": "Ograniczenie prędkości odbierania (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Niepoprawna konfiguracja może uszkodzić zawartośc Twojego folderu i uczynić Syncthing niedziałającym.",
"Introduced By": "Wprowadzony przez",
@@ -156,6 +157,7 @@
"Later": "Później",
"Latest Change": "Ostatnia zmiana",
"Learn more": "Zobacz więcej",
"Limit": "Limit",
"Listeners": "Nasłuchujący",
"Loading data...": "Ładowanie danych...",
"Loading...": "Ładowanie...",
@@ -247,6 +249,7 @@
"Scanning": "Skanowanie",
"See external versioner help for supported templated command line parameters.": "Dostępne zmienne dla polecenia opisane są w dokumentacji w sekcji Zewnętrzne wersjonowanie plików.",
"See external versioning help for supported templated command line parameters.": "Dostępne zmienne dla polecenia opisane są w dokumentacji w sekcji Zewnętrzne wersjonowanie plików.",
"Select All": "Zaznacz wszystko",
"Select a version": "Wybierz wersję",
"Select latest version": "Wybierz najnowszą wersję",
"Select oldest version": "Wybierz najstarszą wersję",
@@ -283,7 +286,7 @@
"Statistics": "Statystyki",
"Stopped": "Zatrzymany",
"Support": "Wsparcie",
"Support Bundle": "Support Bundle",
"Support Bundle": "Wsparcie",
"Sync Protocol Listen Addresses": "Adres nasłuchu protokołu synchronizacji",
"Syncing": "Synchronizowanie",
"Syncthing has been shut down.": "Syncthing został wyłączony",
@@ -292,8 +295,8 @@
"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.",
"Take me back": "Take me back",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.",
"Take me back": "Zabierz mnie z powrotem",
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Adres GUI jest nadpisywany przez opcje uruchamiania. Zmiany tutaj nie będą obowiązywać, dopóki ta opcja uruchamiania jest używana.",
"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 the URL below.": "Zebrane statystyki są publicznie dostępne pod poniższym linkiem.",
"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.",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Facilidades de depuração",
"Default Folder Path": "Caminho padrão da pasta",
"Deleted": "Apagado",
"Deselect All": "Deselect All",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Dispositivo \"{{name}}\" ({{device}} em {{address}}) quer se conectar. Adicionar novo dispositivo?",
"Device ID": "ID do dispositivo",
@@ -156,6 +157,7 @@
"Later": "Depois",
"Latest Change": "Última mudança",
"Learn more": "Saiba mais",
"Limit": "Limit",
"Listeners": "Escutadores",
"Loading data...": "Carregando dados...",
"Loading...": "Carregando",
@@ -247,6 +249,7 @@
"Scanning": "Verificando",
"See external versioner help for supported templated command line parameters.": "Consulte a ajuda sobre versionamento externo para modelos de parâmetros de linha de comando aceitos.",
"See external versioning help for supported templated command line parameters.": "Consulte a ajuda sobre versionamento externo para modelos de parâmetros de linha de comando aceitos.",
"Select All": "Select All",
"Select a version": "Selecione uma versão",
"Select latest version": "Escolher a última versão",
"Select oldest version": "Escolher a versão mais antiga",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Recursos de depuração",
"Default Folder Path": "Caminho da pasta predefinida",
"Deleted": "Eliminado",
"Deselect All": "Desseleccionar tudo",
"Device": "Dispositivo",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "O dispositivo \"{{name}}\" ({{device}} em {{address}}) quer conectar-se. Adiciono este novo dispositivo?",
"Device ID": "ID do dispositivo",
@@ -156,6 +157,7 @@
"Later": "Mais tarde",
"Latest Change": "Última alteração",
"Learn more": "Saiba mais",
"Limit": "Limite",
"Listeners": "Auscultadores",
"Loading data...": "Carregando dados...",
"Loading...": "Carregando...",
@@ -247,6 +249,7 @@
"Scanning": "Verificando",
"See external versioner help for supported templated command line parameters.": "Veja a ajuda do gestor de versões externo para saber que parâmetros da linha de comandos são suportados.",
"See external versioning help for supported templated command line parameters.": "Veja a ajuda externa sobre gestão de versões para ver os modelos suportados de parâmetros para a linha de comandos.",
"Select All": "Seleccionar tudo",
"Select a version": "Seleccione uma versão",
"Select latest version": "Seleccionar a última versão",
"Select oldest version": "Seleccionar a versão mais antiga",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Средства отладки",
"Default Folder Path": "Путь для папок",
"Deleted": "Удалено",
"Deselect All": "Deselect All",
"Device": "Устройство",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Устройство «{{name}}» ({{device}} на {{address}}) хочет подключиться. Добавить новое устройство?",
"Device ID": "ID устройства",
@@ -156,6 +157,7 @@
"Later": "Позже",
"Latest Change": "Последнее изменение",
"Learn more": "Узнать больше",
"Limit": "Limit",
"Listeners": "Прослушиватель",
"Loading data...": "Загрузка данных...",
"Loading...": "Загрузка...",
@@ -247,6 +249,7 @@
"Scanning": "Сканирование",
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Выберите версию",
"Select latest version": "Выбрать последнюю версию",
"Select oldest version": "Выбрать самую старую версию",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Debugging Facilities",
"Default Folder Path": "Predvolená adresárová cesta",
"Deleted": "Zmazané",
"Deselect All": "Deselect All",
"Device": "Zariadenie",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Zariadenie \"{{name}}\" ({{device}} na {{address}}) sa chce pripojiť. Pridať nové zariadenie?",
"Device ID": "ID zariadenia",
@@ -156,6 +157,7 @@
"Later": "Neskôr",
"Latest Change": "Posledná zmena",
"Learn more": "Zisti viac",
"Limit": "Limit",
"Listeners": "Načúvajúci",
"Loading data...": "Načítavanie údajov...",
"Loading...": "Načítavanie...",
@@ -247,6 +249,7 @@
"Scanning": "Skenovanie",
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
"Select All": "Select All",
"Select a version": "Zvoliť verziu",
"Select latest version": "Zvoliť najnovšiu verziu",
"Select oldest version": "Zvoliť najstaršiu verziu",

View File

@@ -12,7 +12,7 @@
"Add Remote Device": "Lägg till fjärrenhet",
"Add devices from the introducer to our device list, for mutually shared folders.": "Lägg enheter från introduktören till vår enhetslista för ömsesidigt delade mappar.",
"Add new folder?": "Lägg till ny mapp?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dessutom kommer det fullständiga omskanningsintervallet att höjas (60 gånger, d.v.s. ny standard på 1h). Du kan också konfigurera det manuellt för varje mapp senare efter att du valt Nej.",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dessutom kommer det fullständiga återkommande genomsöksintervallet att höjas (60 gånger, d.v.s. ny standard på 1h). Du kan också konfigurera det manuellt för varje mapp senare efter att du valt Nej.",
"Address": "Adress",
"Addresses": "Adresser",
"Advanced": "Avancerat",
@@ -50,7 +50,7 @@
"Connection Error": "Anslutningsproblem",
"Connection Type": "Anslutningstyp",
"Connections": "Anslutningar",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Kontinuerligt utkik efter ändringar är nu tillgängligt för Syncthing. Detta kommer att upptäcka ändringar på disken och utfärda en skanning på endast de modifierade sökvägarna. Fördelarna är att förändringar sprids snabbare och att mindre fullständiga skanningar krävs.",
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Kontinuerligt utkik efter ändringar är nu tillgängligt för Syncthing. Detta kommer att upptäcka ändringar på disken och utfärda en genomsökning på endast de ändrade sökvägarna. Fördelarna är att förändringar sprids snabbare och att mindre fullständiga genomsökning krävs.",
"Copied from elsewhere": "Kopierat från annanstans",
"Copied from original": "Kopierat från original",
"Copyright © 2014-2016 the following Contributors:": "Copyright © 2014-2016 följande bidragare:",
@@ -60,18 +60,19 @@
"Debugging Facilities": "Felsökningsanläggningar",
"Default Folder Path": "Standard mappsökväg",
"Deleted": "Tog bort",
"Deselect All": "Avmarkera alla",
"Device": "Enhet",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Enhet \"{{name}}\" ({{device}} på {{address}}) vill ansluta. Lägg till ny enhet?",
"Device ID": "Enhet-ID",
"Device ID": "Enhets-ID",
"Device Identification": "Enhetens identifikation",
"Device Name": "Enhetsnamn",
"Device rate limits": "Enhetshastighetsgränser",
"Device that last modified the item": "Enhet som senast ändrade objektet",
"Devices": "Enheter",
"Disabled": "Inaktiverad",
"Disabled periodic scanning and disabled watching for changes": "Inaktiverad periodisk skanning och inaktiverad visning av ändringar",
"Disabled periodic scanning and enabled watching for changes": "Inaktiverad periodisk skanning och aktiverad visning av ändringar",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Inaktiverad periodisk skanning och misslyckad att ställa in visning av ändringar, försök igen varje 1m:",
"Disabled periodic scanning and disabled watching for changes": "Inaktiverad periodisk genomsökning och inaktiverad spaning efter ändringar",
"Disabled periodic scanning and enabled watching for changes": "Inaktiverad periodisk genomsökning och aktiverad spaning efter ändringar",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Inaktiverad periodisk genomsökning och misslyckad att ställa in spaning efter ändringar, försök igen varje 1m:",
"Discard": "Kassera",
"Disconnected": "Frånkopplad",
"Discovered": "Upptäckt",
@@ -122,7 +123,7 @@
"Folder Type": "Mapptyp",
"Folders": "Mappar",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "För följande mappar uppstod ett fel när du började bevaka ändringar. Det kommer att omförsökas varje minut, så felen kan försvinna snart. Om de fortsätter, försök att åtgärda det underliggande problemet och fråga om hjälp om du inte kan.",
"Full Rescan Interval (s)": "Fullständig omskanningsintervall(er)",
"Full Rescan Interval (s)": "Fullständig(a) återkommande genomsökningsintervall(er)",
"GUI": "Grafiskt gränssnitt",
"GUI Authentication Password": "Gränssnittets autentiseringslösenord",
"GUI Authentication User": "Gränssnittets autentiseringsanvändare",
@@ -151,11 +152,12 @@
"Keep Versions": "Behåll versioner",
"Largest First": "Största först",
"Last File Received": "Senaste fil mottagen",
"Last Scan": "Senaste skanning",
"Last Scan": "Senaste genomsökning",
"Last seen": "Senast sedd",
"Later": "Senare",
"Latest Change": "Senaste ändring",
"Learn more": "Ta reda på mer",
"Limit": "Gräns",
"Listeners": "Lyssnare",
"Loading data...": "Laddar data...",
"Loading...": "Laddar...",
@@ -204,9 +206,9 @@
"Pause All": "Pausa alla",
"Paused": "Pausad",
"Pending changes": "Väntar på ändringar",
"Periodic scanning at given interval and disabled watching for changes": "Periodisk skanning i givet intervall och inaktiverad visning av ändringar",
"Periodic scanning at given interval and enabled watching for changes": "Periodisk skanning i givet intervall och aktiverad visning av ändringar",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk skanning i givet intervall och misslyckades med att ställa in visning av ändringar, försök igen varje 1m:",
"Periodic scanning at given interval and disabled watching for changes": "Periodisk genomsökning i givet intervall och inaktiverad spaning efter ändringar",
"Periodic scanning at given interval and enabled watching for changes": "Periodisk genomsökning i givet intervall och aktiverad spaning efter ändringar",
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisk genomsökning i givet intervall och misslyckades med att ställa in spaning efter ändringar, försök igen var 1m:",
"Permissions": "Behörigheter",
"Please consult the release notes before performing a major upgrade.": "Läs igenom versionsnyheterna innan den stora uppgraderingen.",
"Please set a GUI Authentication User and Password in the Settings dialog.": "Ställ in ett grafiska gränssnittets användarautentisering och lösenord i inställningsdialogrutan.",
@@ -228,10 +230,10 @@
"Remove Device": "Ta bort enhet",
"Remove Folder": "Ta bort mapp",
"Required identifier for the folder. Must be the same on all cluster devices.": "Krävs identifierare för mappen. Måste vara densamma på alla kluster enheter.",
"Rescan": "Skanna om",
"Rescan All": "Skanna om alla",
"Rescan Interval": "Återskanningsintervall",
"Rescans": "Omskanningar",
"Rescan": "Genomsök igen",
"Rescan All": "Genomsök alla igen",
"Rescan Interval": "Återkommande genomsökningsintervall",
"Rescans": "Återkommande genomsökningar",
"Restart": "Starta om",
"Restart Needed": "Omstart behövs",
"Restarting": "Startar om",
@@ -243,10 +245,11 @@
"Revert Local Changes": "Återställ lokala ändringar",
"Running": "Körs",
"Save": "Spara",
"Scan Time Remaining": "Återstående skanningstid",
"Scanning": "Skannar",
"Scan Time Remaining": "Återstående genomsökningstid",
"Scanning": "Genomsöker",
"See external versioner help for supported templated command line parameters.": "Se hjälp för extern version för stödda mallade kommandoradsparametrar.",
"See external versioning help for supported templated command line parameters.": "Se hjälp för extern version för stödda mallade kommandoradsparametrar.",
"Select All": "Markera alla",
"Select a version": "Välj en version",
"Select latest version": "Välj senaste versionen",
"Select oldest version": "Välj äldsta versionen",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "Засоби відладки",
"Default Folder Path": "Шлях до директорії по замовчанню",
"Deleted": "Видалене",
"Deselect All": "Deselect All",
"Device": "Пристрій",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Пристрій \"{{name}}\" ({{device}} за адресою {{address}}) намагається під’єднатися. Додати новий пристрій?",
"Device ID": "ID пристрою",
@@ -156,6 +157,7 @@
"Later": "Пізніше",
"Latest Change": "Найостанніша зміна",
"Learn more": "Дізнатися більше",
"Limit": "Limit",
"Listeners": "Приймачі (TCP & Relay)",
"Loading data...": "Дані завантажуються...",
"Loading...": "Завантаження...",
@@ -247,6 +249,7 @@
"Scanning": "Сканування",
"See external versioner help for supported templated command line parameters.": "Переглянути допомогу по зовнішньому версіонуванню для підтримуваних шаблонних параметрів командного рядка.",
"See external versioning help for supported templated command line parameters.": "Переглянути допомогу по зовнішньому версіонуванню для підтримуваних шаблонних параметрів командного рядка.",
"Select All": "Select All",
"Select a version": "Обрати версію",
"Select latest version": "Обрати найновішу версію",
"Select oldest version": "Обрати найстарішу версію",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "调试功能",
"Default Folder Path": "默认文件夹路径",
"Deleted": "已删除",
"Deselect All": "取消全选",
"Device": "设备",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "设备 \"{{name}}\"(位于 {{address}} 的 {{device}})请求连接。是否添加新设备?",
"Device ID": "设备 ID",
@@ -156,6 +157,7 @@
"Later": "稍后",
"Latest Change": "最后更改",
"Learn more": "了解更多",
"Limit": "限制",
"Listeners": "侦听程序",
"Loading data...": "正在载入数据…",
"Loading...": "正在载入…",
@@ -247,6 +249,7 @@
"Scanning": "扫描中",
"See external versioner help for supported templated command line parameters.": "有关支持的命令行参数模板,请参阅外部的版本控制器帮助。",
"See external versioning help for supported templated command line parameters.": "有关受支持的模板命令行参数,请参阅外部版本控制帮助。",
"Select All": "全选",
"Select a version": "选择版本",
"Select latest version": "选择最新的版本",
"Select oldest version": "选择最旧的版本",

View File

@@ -60,6 +60,7 @@
"Debugging Facilities": "除錯工具",
"Default Folder Path": "預設資料夾路徑",
"Deleted": "已刪除",
"Deselect All": "Deselect All",
"Device": "裝置",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "裝置 \"{{name}}\" ({{device}} 位於 {{address}}) 想要連線。要增加新裝置嗎?",
"Device ID": "裝置識別碼",
@@ -156,6 +157,7 @@
"Later": "稍後",
"Latest Change": "最近變動",
"Learn more": "瞭解更多",
"Limit": "Limit",
"Listeners": "監聽者",
"Loading data...": "正在載入資料...",
"Loading...": "正在載入...",
@@ -247,6 +249,7 @@
"Scanning": "正在掃描",
"See external versioner help for supported templated command line parameters.": "關於命令列模板參數請參閱外部版本管理說明。",
"See external versioning help for supported templated command line parameters.": "查看關於命令列模板參數請參閱外部版本管理說明。",
"Select All": "Select All",
"Select a version": "選擇一個版本",
"Select latest version": "選擇最新的版本",
"Select oldest version": "選擇最舊的版本",

View File

@@ -1 +1 @@
var langPrettyprint = {"bg":"Bulgarian","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)","eu":"Basque","fi":"Finnish","fr":"French","fr-CA":"French (Canada)","fy":"Western Frisian","hu":"Hungarian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ru":"Russian","sk":"Slovak","sv":"Swedish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-TW":"Chinese (Taiwan)"}
var langPrettyprint = {"bg":"Bulgarian","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)","eu":"Basque","fi":"Finnish","fr":"French","fy":"Western Frisian","hu":"Hungarian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ru":"Russian","sk":"Slovak","sv":"Swedish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-TW":"Chinese (Taiwan)"}

View File

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

View File

@@ -313,6 +313,7 @@
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs">&#9724;</span></span>
<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>
<span ng-switch-when="scan-waiting"><span class="hidden-xs" translate>Waiting to scan</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="stopped"><span class="hidden-xs" translate>Stopped</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="scanning">
<span class="hidden-xs" translate>Scanning</span>
@@ -372,10 +373,10 @@
</span>
</td>
</tr>
<tr ng-if="neededItems(folder.id) > 0">
<tr ng-if="model[folder.id].needTotalItems > 0">
<th><span class="fas fa-fw fa-cloud-download-alt"></span>&nbsp;<span translate>Out of Sync Items</span></th>
<td class="text-right">
<a href="" ng-click="showNeed(folder.id)">{{neededItems(folder.id) | alwaysNumber}} <span translate>items</span>, ~{{model[folder.id].needBytes | binary}}B</a>
<a href="" ng-click="showNeed(folder.id)">{{model[folder.id].needTotalItems | alwaysNumber}} <span translate>items</span>, ~{{model[folder.id].needBytes | binary}}B</a>
</td>
</tr>
<tr ng-if="folderStatus(folder) === 'scanning' && scanRate(folder.id) > 0">
@@ -391,6 +392,12 @@
<a href="" ng-click="showFailed(folder.id)">{{model[folder.id].pullErrors | alwaysNumber | localeNumber}}&nbsp;<span translate>items</span></a>
</td>
</tr>
<tr ng-if="folder.type == 'receiveonly' && canRevert(folder.id)">
<th><span class="fas fa-fw fa-exclamation-circle"></span>&nbsp;<span translate>Locally Changed Items</span></th>
<td class="text-right">
<a href="" ng-click="showLocalChanged(folder.id)">{{model[folder.id].receiveOnlyTotalItems | alwaysNumber}} <span translate>items</span>, ~{{model[folder.id].receiveOnlyBytes | binary}}B</a>
</td>
</tr>
<tr ng-if="folder.type != 'sendreceive'">
<th><span class="fas fa-fw fa-folder"></span>&nbsp;<span translate>Folder Type</span></th>
<td class="text-right">
@@ -551,7 +558,13 @@
<a href="#" class="toggler" ng-click="toggleUnits()">
<span ng-if="!metricRates">{{connectionsTotal.inbps | binary}}B/s</span>
<span ng-if="metricRates">{{connectionsTotal.inbps*8 | metric}}bps</span>
({{connectionsTotal.inBytesTotal | binary}}B)</span>
({{connectionsTotal.inBytesTotal | binary}}B)
<small ng-if="config.options.maxRecvKbps > 0"><br/>
<i class="text-muted"><span translate>Limit</span>:
<span ng-if="!metricRates">{{config.options.maxRecvKbps*1024 | binary}}B/s</span>
<span ng-if="metricRates">{{config.options.maxRecvKbps*1024*8 | metric}}bps</span>
</i>
</small>
</a>
</td>
</tr>
@@ -562,6 +575,12 @@
<span ng-if="!metricRates">{{connectionsTotal.outbps | binary}}B/s</span>
<span ng-if="metricRates">{{connectionsTotal.outbps*8 | metric}}bps</span>
({{connectionsTotal.outBytesTotal | binary}}B)
<small ng-if="config.options.maxSendKbps > 0"><br/>
<i class="text-muted"><span translate>Limit</span>:
<span ng-if="!metricRates">{{config.options.maxSendKbps*1024 | binary}}B/s</span>
<span ng-if="metricRates">{{config.options.maxSendKbps*1024*8 | metric}}bps</span>
</i>
</small>
</a>
</td>
</tr>
@@ -569,9 +588,10 @@
<th><span class="fas fa-fw fa-home"></span>&nbsp;<span translate>Local State (Total)</span></th>
<td class="text-right">
<span tooltip data-original-title="{{localStateTotal.files | alwaysNumber | localeNumber}} {{'files' | translate}}, {{ localStateTotal.directories | alwaysNumber | localeNumber}} {{'directories' | translate}}, ~{{ localStateTotal.bytes | binary}}B">
<span class="far fa-copy"></span>&nbsp;{{localStateTotal.files | alwaysNumber | localeNumber}}&ensp;
<span class="far fa-folder"></span>&nbsp;{{localStateTotal.directories| alwaysNumber | localeNumber}}&ensp;
<span class="far fa-hdd"></span>&nbsp;~{{localStateTotal.bytes | binary}}B
<span class="far fa-copy"></span>&nbsp;{{localStateTotal.files | alwaysNumber | localeNumber}}&ensp;
<span class="far fa-folder"></span>&nbsp;{{localStateTotal.directories| alwaysNumber | localeNumber}}&ensp;
<span class="far fa-hdd"></span>&nbsp;~{{localStateTotal.bytes | binary}}B
</span>
</td>
</tr>
<tr>
@@ -654,7 +674,13 @@
<a href="#" class="toggler" ng-click="toggleUnits()">
<span ng-if="!metricRates">{{connections[deviceCfg.deviceID].inbps | binary}}B/s</span>
<span ng-if="metricRates">{{connections[deviceCfg.deviceID].inbps*8 | metric}}bps</span>
({{connections[deviceCfg.deviceID].inBytesTotal | binary}}B)</span>
({{connections[deviceCfg.deviceID].inBytesTotal | binary}}B)
<small ng-if="deviceCfg.maxRecvKbps > 0"><br/>
<i class="text-muted"><span translate>Limit</span>:
<span ng-if="!metricRates">{{deviceCfg.maxRecvKbps*1024 | binary}}B/s</span>
<span ng-if="metricRates">{{deviceCfg.maxRecvKbps*1024*8 | metric}}bps</span>
</i>
</small>
</a>
</td>
</tr>
@@ -664,7 +690,13 @@
<a href="#" class="toggler" ng-click="toggleUnits()">
<span ng-if="!metricRates">{{connections[deviceCfg.deviceID].outbps | binary}}B/s</span>
<span ng-if="metricRates">{{connections[deviceCfg.deviceID].outbps*8 | metric}}bps</span>
({{connections[deviceCfg.deviceID].outBytesTotal | binary}}B)</span>
({{connections[deviceCfg.deviceID].outBytesTotal | binary}}B)
<small ng-if="deviceCfg.maxSendKbps > 0"><br/>
<i class="text-muted"><span translate>Limit</span>:
<span ng-if="!metricRates">{{deviceCfg.maxSendKbps*1024 | binary}}B/s</span>
<span ng-if="metricRates">{{deviceCfg.maxSendKbps*1024*8 | metric}}bps</span>
</i>
</small>
</a>
</td>
</tr>
@@ -796,6 +828,7 @@
<ng-include src="'syncthing/transfer/neededFilesModalView.html'"></ng-include>
<ng-include src="'syncthing/transfer/failedFilesModalView.html'"></ng-include>
<ng-include src="'syncthing/transfer/remoteNeededFilesModalView.html'"></ng-include>
<ng-include src="'syncthing/transfer/localChangedFilesModalView.html'"></ng-include>
<ng-include src="'syncthing/core/majorUpgradeModalView.html'"></ng-include>
<ng-include src="'syncthing/core/aboutModalView.html'"></ng-include>
<ng-include src="'syncthing/core/discoveryFailuresModalView.html'"></ng-include>

View File

@@ -12,7 +12,7 @@
<p translate>Copyright &copy; 2014-2017 the following Contributors:</p>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Andrew Dunham, Andrew Rabert, Andrey D, Antoine Lamielle, Aranjedeath, Arthur Axel fREW Schmidt, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, Colin Kennedy, Dale Visser, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Han Boetes, Harrison Jones, Heiko Zuerker, Iain Barnett, Ian Johnson, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonathan Cross, Jose Manuel Delicado, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matic Potočnik, Matt Burke, Matteo Ruina, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Tilli, Mike Boone, MikeLund, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Niels Peter Roest, Nils Jakobi, NoLooseEnds, Oyebanji Jacob Mayowa, Pascal Jungblut, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Richard Hartmann, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tomas Cerveny, Tommy Thorn, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, chucic, derekriemer, janost, jaseg, klemens, marco-m, perewa, rubenbe, wangguoliang, xjtdy888, 佛跳墙
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Andrew Dunham, Andrew Rabert, Andrey D, Antoine Lamielle, Aranjedeath, Arthur Axel fREW Schmidt, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, Colin Kennedy, Cromefire_, Dale Visser, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Han Boetes, Harrison Jones, Heiko Zuerker, Iain Barnett, Ian Johnson, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonathan Cross, Jose Manuel Delicado, Jörg Thalheim, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matic Potočnik, Matt Burke, Matteo Ruina, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Tilli, Mike Boone, MikeLund, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Niels Peter Roest, Nils Jakobi, NoLooseEnds, Oyebanji Jacob Mayowa, Pascal Jungblut, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Richard Hartmann, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tomas Cerveny, Tommy Thorn, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, chucic, derekriemer, desbma, janost, jaseg, klemens, marco-m, perewa, rubenbe, wangguoliang, xjtdy888, 佛跳墙
</div>
</div>
<hr/>

View File

@@ -46,6 +46,7 @@ angular.module('syncthing.core')
$scope.neededCurrentPage = 1;
$scope.neededPageSize = 10;
$scope.failed = {};
$scope.localChanged = {};
$scope.scanProgress = {};
$scope.themes = [];
$scope.globalChangeEvents = {};
@@ -672,6 +673,15 @@ angular.module('syncthing.core')
});
};
$scope.refreshLocalChanged = function (page, perpage) {
var url = urlbase + '/db/localchanged?folder=';
url += encodeURIComponent($scope.localChanged.folder);
url += "&page=" + page + "&perpage=" + perpage;
$http.get(url).success(function (data) {
$scope.localChanged = data;
}).error($scope.emitHTTPError);
};
var refreshDeviceStats = debounce(function () {
$http.get(urlbase + "/stats/device").success(function (data) {
$scope.deviceStats = data;
@@ -737,7 +747,7 @@ angular.module('syncthing.core')
if (state === 'error') {
return 'stopped'; // legacy, the state is called "stopped" in the GUI
}
if (state === 'idle' && $scope.neededItems(folderCfg.id) > 0) {
if (state === 'idle' && $scope.model[folderCfg.id].needTotalItems > 0) {
return 'outofsync';
}
if (state === 'scanning') {
@@ -769,22 +779,13 @@ angular.module('syncthing.core')
if (status === 'stopped' || status === 'outofsync' || status === 'error') {
return 'danger';
}
if (status === 'unshared') {
if (status === 'unshared' || status === 'scan-waiting') {
return 'warning';
}
return 'info';
};
$scope.neededItems = function (folderID) {
if (!$scope.model[folderID]) {
return 0
}
return $scope.model[folderID].needFiles + $scope.model[folderID].needDirectories +
$scope.model[folderID].needSymlinks + $scope.model[folderID].needDeletes;
};
$scope.syncPercentage = function (folder) {
if (typeof $scope.model[folder] === 'undefined') {
return 100;
@@ -1371,16 +1372,14 @@ angular.module('syncthing.core')
$('#editDevice').modal();
};
$scope.selectAllFolders = function() {
Object.entries($scope.folders).forEach(entry =>{
let id = entry[1].id;
$scope.selectAllFolders = function () {
angular.forEach($scope.folders, function (id) {
$scope.currentDevice.selectedFolders[id] = true;
});
};
$scope.deSelectAllFolders = function() {
Object.entries($scope.folders).forEach(entry =>{
let id = entry[1].id;
$scope.deSelectAllFolders = function () {
angular.forEach($scope.folders, function (id) {
$scope.currentDevice.selectedFolders[id] = false;
});
};
@@ -2196,6 +2195,14 @@ angular.module('syncthing.core')
$http.post(urlbase + "/db/override?folder=" + encodeURIComponent(folder));
};
$scope.showLocalChanged = function (folder) {
$scope.localChanged.folder = folder;
$scope.localChanged = $scope.refreshLocalChanged(1, 10);
$('#localChanged').modal().one('hidden.bs.modal', function () {
$scope.localChanged = {};
});
};
$scope.revert = function (folder) {
$http.post(urlbase + "/db/revert?folder=" + encodeURIComponent(folder));
};
@@ -2205,11 +2212,7 @@ angular.module('syncthing.core')
if (!f) {
return false;
}
return f.receiveOnlyChangedBytes > 0 ||
f.receiveOnlyChangedDeletes > 0 ||
f.receiveOnlyChangedDirectories > 0 ||
f.receiveOnlyChangedFiles > 0 ||
f.receiveOnlyChangedSymlinks > 0;
return $scope.model[folder].receiveOnlyTotalItems > 0;
};
$scope.advanced = function () {

View File

@@ -112,17 +112,6 @@
<div class="col-md-12">
<label translate>Device rate limits</label>
<div class="row">
<div class="col-md-6">
<div ng-class="{'has-error': deviceEditor.maxSendKbps.$invalid && deviceEditor.maxSendKbps.$dirty}">
<div class="row">
<span class="col-md-8" translate>Outgoing Rate Limit (KiB/s)</span>
<div class="col-md-4">
<input name="maxSendKbps" id="maxSendKbps" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.maxSendKbps" min="0"/>
</div>
</div>
<p class="help-block" ng-if="!deviceEditor.maxSendKbps.$valid && deviceEditor.maxSendKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
</div>
</div>
<div class="col-md-6" ng-class="{'has-error': deviceEditor.maxRecvKbps.$invalid && deviceEditor.maxRecvKbps.$dirty}">
<div class="row">
<span class="col-md-8" translate>Incoming Rate Limit (KiB/s)</span>
@@ -132,6 +121,15 @@
</div>
<p class="help-block" ng-if="!deviceEditor.maxRecvKbps.$valid && deviceEditor.maxRecvKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
</div>
<div class="col-md-6" ng-class="{'has-error': deviceEditor.maxSendKbps.$invalid && deviceEditor.maxSendKbps.$dirty}">
<div class="row">
<span class="col-md-8" translate>Outgoing Rate Limit (KiB/s)</span>
<div class="col-md-4">
<input name="maxSendKbps" id="maxSendKbps" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.maxSendKbps" min="0"/>
</div>
</div>
<p class="help-block" ng-if="!deviceEditor.maxSendKbps.$valid && deviceEditor.maxSendKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,31 @@
<modal id="localChanged" status="info" icon="fas fa-exclamation-circle" heading="{{'Locally Changed Items' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<p translate>
The following items were changed locally.
</p>
<table class="table table-striped">
<thead>
<tr>
<th translate>Path</th>
<th translate>Size</th>
</tr>
</thead>
<tr dir-paginate="file in localChanged.files | itemsPerPage: localChanged.perpage" current-page="localChanged.page" total-items="model[localChanged.folder].receiveOnlyTotalItems" pagination-id="localChanged">
<td>{{file.name}}</td>
<td><span ng-hide="file.type == 'DIRECTORY'">{{file.size | binary}}B</span></td>
</tr>
</table>
<dir-pagination-controls on-page-change="refreshLocalChanged(newPageNumber, localChanged.perpage)" pagination-id="localChanged"></dir-pagination-controls>
<ul class="pagination pull-right">
<li ng-repeat="option in [10, 25, 50]" ng-class="{ active: localChanged.page == option }">
<a href="#" ng-click="refreshLocalChanged(localChanged.page, option)">{{option}}</a>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>
</div>
</modal>

View File

@@ -14,7 +14,7 @@
<table class="table table-striped table-condensed">
<tr dir-paginate="f in needed | itemsPerPage: neededPageSize" current-page="neededCurrentPage" total-items="neededItems(neededFolder)" pagination-id="needed">
<tr dir-paginate="f in needed | itemsPerPage: neededPageSize" current-page="neededCurrentPage" total-items="model[neededFolder].needTotalItems" pagination-id="needed">
<!-- Icon -->
<td class="small-data col-xs-2">

View File

@@ -51,6 +51,7 @@ type OptionsConfiguration struct {
TrafficClass int `xml:"trafficClass" json:"trafficClass"`
DefaultFolderPath string `xml:"defaultFolderPath" json:"defaultFolderPath" default:"~"`
SetLowPriority bool `xml:"setLowPriority" json:"setLowPriority" default:"true"`
MaxConcurrentScans int `xml:"maxConcurrentScans" json:"maxConcurrentScans"`
DeprecatedUPnPEnabled bool `xml:"upnpEnabled,omitempty" json:"-"`
DeprecatedUPnPLeaseM int `xml:"upnpLeaseMinutes,omitempty" json:"-"`

View File

@@ -21,19 +21,44 @@ const (
)
const (
KeyTypeDevice = 0
KeyTypeGlobal = 1
KeyTypeBlock = 2
// KeyTypeDevice <int32 folder ID> <int32 device ID> <file name> = FileInfo
KeyTypeDevice = 0
// KeyTypeGlobal <int32 folder ID> <file name> = VersionList
KeyTypeGlobal = 1
// KeyTypeBlock <int32 folder ID> <32 bytes hash> <§file name> = int32 (block index)
KeyTypeBlock = 2
// KeyTypeDeviceStatistic <device ID as string> <some string> = some value
KeyTypeDeviceStatistic = 3
// KeyTypeFolderStatistic <folder ID as string> <some string> = some value
KeyTypeFolderStatistic = 4
KeyTypeVirtualMtime = 5
KeyTypeFolderIdx = 6
KeyTypeDeviceIdx = 7
KeyTypeIndexID = 8
KeyTypeFolderMeta = 9
KeyTypeMiscData = 10
KeyTypeSequence = 11
KeyTypeNeed = 12
// KeyTypeVirtualMtime <int32 folder ID> <file name> = dbMtime
KeyTypeVirtualMtime = 5
// KeyTypeFolderIdx <int32 id> = string value
KeyTypeFolderIdx = 6
// KeyTypeDeviceIdx <int32 id> = string value
KeyTypeDeviceIdx = 7
// KeyTypeIndexID <int32 device ID> <int32 folder ID> = protocol.IndexID
KeyTypeIndexID = 8
// KeyTypeFolderMeta <int32 folder ID> = CountsSet
KeyTypeFolderMeta = 9
// KeyTypeMiscData <some string> = some value
KeyTypeMiscData = 10
// KeyTypeSequence <int32 folder ID> <int64 sequence number> = KeyTypeDevice key
KeyTypeSequence = 11
// KeyTypeNeed <int32 folder ID> <file name> = <nothing>
KeyTypeNeed = 12
)
type keyer interface {

View File

@@ -45,6 +45,7 @@ type FileIntf interface {
IsIgnored() bool
IsUnsupported() bool
MustRescan() bool
IsReceiveOnlyChanged() bool
IsDirectory() bool
IsSymlink() bool
ShouldConflict() bool

View File

@@ -10,6 +10,7 @@ import (
"bytes"
"fmt"
"os"
"path/filepath"
"sort"
"testing"
"time"
@@ -1334,6 +1335,34 @@ func TestNeedAfterDeviceRemove(t *testing.T) {
}
}
func TestCaseSensitive(t *testing.T) {
// Normal case sensitive lookup should work
ldb := db.OpenMemory()
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
local := []protocol.FileInfo{
{Name: filepath.FromSlash("D1/f1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
{Name: filepath.FromSlash("F1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
{Name: filepath.FromSlash("d1/F1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
{Name: filepath.FromSlash("d1/f1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
{Name: filepath.FromSlash("f1"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
}
replace(s, protocol.LocalDeviceID, local)
gf := globalList(s)
if l := len(gf); l != len(local) {
t.Fatalf("Incorrect len %d != %d for global list", l, len(local))
}
for i := range local {
if gf[i].Name != local[i].Name {
t.Errorf("Incorrect filename;\n%q !=\n%q",
gf[i].Name, local[i].Name)
}
}
}
func replace(fs *db.FileSet, device protocol.DeviceID, files []protocol.FileInfo) {
fs.Drop(device)
fs.Update(device, files)

View File

@@ -142,6 +142,10 @@ func (c Counts) Add(other Counts) Counts {
}
}
func (c Counts) TotalItems() int32 {
return c.Files + c.Directories + c.Symlinks + c.Deleted
}
func (vl VersionList) String() string {
var b bytes.Buffer
var id protocol.DeviceID

View File

@@ -204,7 +204,9 @@ type Logger struct {
nextSubscriptionIDs []int
nextGlobalID int
timeout *time.Timer
mutex sync.Mutex
events chan Event
funcs chan func()
stop chan struct{}
}
type Event struct {
@@ -225,6 +227,13 @@ type Subscription struct {
var Default = NewLogger()
func init() {
// The default logger never stops. To ensure this we nil out the stop
// channel so any attempt to stop it will panic.
Default.stop = nil
go Default.Serve()
}
var (
ErrTimeout = errors.New("timeout")
ErrClosed = errors.New("closed")
@@ -232,8 +241,10 @@ var (
func NewLogger() *Logger {
l := &Logger{
mutex: sync.NewMutex(),
timeout: time.NewTimer(time.Second),
events: make(chan Event, BufferSize),
funcs: make(chan func()),
stop: make(chan struct{}),
}
// Make sure the timer is in the stopped state and hasn't fired anything
// into the channel.
@@ -243,20 +254,52 @@ func NewLogger() *Logger {
return l
}
func (l *Logger) Log(t EventType, data interface{}) {
l.mutex.Lock()
l.nextGlobalID++
dl.Debugln("log", l.nextGlobalID, t, data)
func (l *Logger) Serve() {
loop:
for {
select {
case e := <-l.events:
// Incoming events get sent
l.sendEvent(e)
e := Event{
GlobalID: l.nextGlobalID,
Time: time.Now(),
Type: t,
Data: data,
case fn := <-l.funcs:
// Subscriptions etc are handled here.
fn()
case <-l.stop:
break loop
}
}
// Closing the event channels corresponds to what happens when a
// subscription is unsubscribed; this stops any BufferedSubscription,
// makes Poll() return ErrClosed, etc.
for _, s := range l.subs {
close(s.events)
}
}
func (l *Logger) Stop() {
close(l.stop)
}
func (l *Logger) Log(t EventType, data interface{}) {
l.events <- Event{
Time: time.Now(),
Type: t,
Data: data,
// SubscriptionID and GlobalID are set in sendEvent
}
}
func (l *Logger) sendEvent(e Event) {
l.nextGlobalID++
dl.Debugln("log", l.nextGlobalID, e.Type, e.Data)
e.GlobalID = l.nextGlobalID
for i, s := range l.subs {
if s.mask&t != 0 {
if s.mask&e.Type != 0 {
e.SubscriptionID = l.nextSubscriptionIDs[i]
l.nextSubscriptionIDs[i]++
@@ -278,59 +321,60 @@ func (l *Logger) Log(t EventType, data interface{}) {
}
}
}
l.mutex.Unlock()
}
func (l *Logger) Subscribe(mask EventType) *Subscription {
l.mutex.Lock()
dl.Debugln("subscribe", mask)
res := make(chan *Subscription)
l.funcs <- func() {
dl.Debugln("subscribe", mask)
s := &Subscription{
mask: mask,
events: make(chan Event, BufferSize),
timeout: time.NewTimer(0),
}
s := &Subscription{
mask: mask,
events: make(chan Event, BufferSize),
timeout: time.NewTimer(0),
}
// We need to create the timeout timer in the stopped, non-fired state so
// that Subscription.Poll() can safely reset it and select on the timeout
// channel. This ensures the timer is stopped and the channel drained.
if runningTests {
// Make the behavior stable when running tests to avoid randomly
// varying test coverage. This ensures, in practice if not in
// theory, that the timer fires and we take the true branch of the
// next if.
runtime.Gosched()
}
if !s.timeout.Stop() {
<-s.timeout.C
}
// We need to create the timeout timer in the stopped, non-fired state so
// that Subscription.Poll() can safely reset it and select on the timeout
// channel. This ensures the timer is stopped and the channel drained.
if runningTests {
// Make the behavior stable when running tests to avoid randomly
// varying test coverage. This ensures, in practice if not in
// theory, that the timer fires and we take the true branch of the
// next if.
runtime.Gosched()
}
if !s.timeout.Stop() {
<-s.timeout.C
}
l.subs = append(l.subs, s)
l.nextSubscriptionIDs = append(l.nextSubscriptionIDs, 1)
l.mutex.Unlock()
return s
l.subs = append(l.subs, s)
l.nextSubscriptionIDs = append(l.nextSubscriptionIDs, 1)
res <- s
}
return <-res
}
func (l *Logger) Unsubscribe(s *Subscription) {
l.mutex.Lock()
dl.Debugln("unsubscribe")
for i, ss := range l.subs {
if s == ss {
last := len(l.subs) - 1
l.funcs <- func() {
dl.Debugln("unsubscribe")
for i, ss := range l.subs {
if s == ss {
last := len(l.subs) - 1
l.subs[i] = l.subs[last]
l.subs[last] = nil
l.subs = l.subs[:last]
l.subs[i] = l.subs[last]
l.subs[last] = nil
l.subs = l.subs[:last]
l.nextSubscriptionIDs[i] = l.nextSubscriptionIDs[last]
l.nextSubscriptionIDs[last] = 0
l.nextSubscriptionIDs = l.nextSubscriptionIDs[:last]
l.nextSubscriptionIDs[i] = l.nextSubscriptionIDs[last]
l.nextSubscriptionIDs[last] = 0
l.nextSubscriptionIDs = l.nextSubscriptionIDs[:last]
break
break
}
}
close(s.events)
}
close(s.events)
l.mutex.Unlock()
}
// Poll returns an event from the subscription or an error if the poll times

View File

@@ -9,6 +9,7 @@ package events
import (
"encoding/json"
"fmt"
"sync"
"testing"
"time"
)
@@ -28,6 +29,9 @@ func TestNewLogger(t *testing.T) {
func TestSubscriber(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(0)
defer l.Unsubscribe(s)
if s == nil {
@@ -37,6 +41,9 @@ func TestSubscriber(t *testing.T) {
func TestTimeout(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(0)
defer l.Unsubscribe(s)
_, err := s.Poll(timeout)
@@ -47,6 +54,8 @@ func TestTimeout(t *testing.T) {
func TestEventBeforeSubscribe(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
l.Log(DeviceConnected, "foo")
s := l.Subscribe(0)
@@ -60,6 +69,8 @@ func TestEventBeforeSubscribe(t *testing.T) {
func TestEventAfterSubscribe(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
@@ -85,6 +96,8 @@ func TestEventAfterSubscribe(t *testing.T) {
func TestEventAfterSubscribeIgnoreMask(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(DeviceDisconnected)
defer l.Unsubscribe(s)
@@ -98,6 +111,8 @@ func TestEventAfterSubscribeIgnoreMask(t *testing.T) {
func TestBufferOverflow(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
@@ -121,6 +136,8 @@ func TestBufferOverflow(t *testing.T) {
func TestUnsubscribe(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(AllEvents)
l.Log(DeviceConnected, "foo")
@@ -141,6 +158,8 @@ func TestUnsubscribe(t *testing.T) {
func TestGlobalIDs(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
@@ -171,6 +190,8 @@ func TestGlobalIDs(t *testing.T) {
func TestSubscriptionIDs(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s)
@@ -211,6 +232,8 @@ func TestSubscriptionIDs(t *testing.T) {
func TestBufferedSub(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
@@ -240,6 +263,8 @@ func TestBufferedSub(t *testing.T) {
func BenchmarkBufferedSub(b *testing.B) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
@@ -294,6 +319,8 @@ func BenchmarkBufferedSub(b *testing.B) {
func TestSinceUsesSubscriptionId(t *testing.T) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(DeviceConnected)
defer l.Unsubscribe(s)
@@ -339,3 +366,93 @@ func TestUnmarshalEvent(t *testing.T) {
t.Fatal("Failed to unmarshal event:", err)
}
}
func TestUnsubscribeContention(t *testing.T) {
// Check that we can unsubscribe without blocking the whole system.
const (
listeners = 50
senders = 1000
)
l := NewLogger()
defer l.Stop()
go l.Serve()
// Start listeners. These will poll until the stop channel is closed,
// then exit and unsubscribe.
stopListeners := make(chan struct{})
var listenerWg sync.WaitGroup
listenerWg.Add(listeners)
for i := 0; i < listeners; i++ {
go func() {
defer listenerWg.Done()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
for {
select {
case <-s.C():
case <-stopListeners:
return
}
}
}()
}
// Start senders. These send pointless events until the stop channel is
// closed.
stopSenders := make(chan struct{})
defer close(stopSenders)
var senderWg sync.WaitGroup
senderWg.Add(senders)
for i := 0; i < senders; i++ {
go func() {
defer senderWg.Done()
t := time.NewTicker(time.Millisecond)
for {
select {
case <-t.C:
l.Log(StateChanged, nil)
case <-stopSenders:
return
}
}
}()
}
// Give everything time to start up.
time.Sleep(time.Second)
// Stop the listeners and wait for them to exit. This should happen in a
// reasonable time frame.
t0 := time.Now()
close(stopListeners)
listenerWg.Wait()
if d := time.Since(t0); d > time.Minute {
t.Error("It should not take", d, "to unsubscribe from an event stream")
}
}
func BenchmarkLogEvent(b *testing.B) {
l := NewLogger()
defer l.Stop()
go l.Serve()
s := l.Subscribe(AllEvents)
defer l.Unsubscribe(s)
NewBufferedSubscription(s, 1) // runs in the background
for i := 0; i < b.N; i++ {
l.Log(StateChanged, nil)
}
}

View File

@@ -199,12 +199,11 @@ func NewFilesystem(fsType FilesystemType, uri string) Filesystem {
func IsInternal(file string) bool {
// fs cannot import config, so we hard code .stfolder here (config.DefaultMarkerName)
internals := []string{".stfolder", ".stignore", ".stversions"}
pathSep := string(PathSeparator)
for _, internal := range internals {
if file == internal {
return true
}
if strings.HasPrefix(file, internal+pathSep) {
if IsParent(file, internal) {
return true
}
}

View File

@@ -77,3 +77,15 @@ func WindowsInvalidFilename(name string) bool {
// The path must not contain any disallowed characters
return strings.ContainsAny(name, windowsDisallowedCharacters)
}
func IsParent(path, parent string) bool {
if len(parent) == 0 {
// The empty string is the parent of everything except the empty
// string. (Avoids panic in the next step.)
return len(path) > 0
}
if parent[len(parent)-1] != PathSeparator {
parent += string(PathSeparator)
}
return strings.HasPrefix(path, parent)
}

View File

@@ -336,7 +336,7 @@ func loadParseIncludeFile(filesystem fs.Filesystem, file string, cd ChangeDetect
if filesystem.Type() == fs.FilesystemTypeBasic {
uri := filesystem.URI()
joined := filepath.Join(uri, file)
if !strings.HasPrefix(joined, uri) {
if !fs.IsParent(joined, uri) {
filesystem = fs.NewFilesystem(filesystem.Type(), filepath.Dir(joined))
file = filepath.Base(joined)
}

View File

@@ -6,7 +6,9 @@
package model
import "sync"
import (
"sync"
)
type byteSemaphore struct {
max int
@@ -25,26 +27,44 @@ func newByteSemaphore(max int) *byteSemaphore {
}
func (s *byteSemaphore) take(bytes int) {
s.mut.Lock()
if bytes > s.max {
bytes = s.max
}
s.mut.Lock()
for bytes > s.available {
s.cond.Wait()
if bytes > s.max {
bytes = s.max
}
}
s.available -= bytes
s.mut.Unlock()
}
func (s *byteSemaphore) give(bytes int) {
s.mut.Lock()
if bytes > s.max {
bytes = s.max
}
s.mut.Lock()
if s.available+bytes > s.max {
panic("bug: can never give more than max")
s.available = s.max
} else {
s.available += bytes
}
s.cond.Broadcast()
s.mut.Unlock()
}
func (s *byteSemaphore) setCapacity(cap int) {
s.mut.Lock()
diff := cap - s.max
s.max = cap
s.available += diff
if s.available < 0 {
s.available = 0
} else if s.available > s.max {
s.available = s.max
}
s.available += bytes
s.cond.Broadcast()
s.mut.Unlock()
}

View File

@@ -0,0 +1,113 @@
// Copyright (C) 2018 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package model
import "testing"
func TestZeroByteSempahore(t *testing.T) {
// A semaphore with zero capacity is just a no-op.
s := newByteSemaphore(0)
// None of these should block or panic
s.take(123)
s.take(456)
s.give(1 << 30)
}
func TestByteSempahoreCapChangeUp(t *testing.T) {
// Waiting takes should unblock when the capacity increases
s := newByteSemaphore(100)
s.take(75)
if s.available != 25 {
t.Error("bad state after take")
}
gotit := make(chan struct{})
go func() {
s.take(75)
close(gotit)
}()
s.setCapacity(155)
<-gotit
if s.available != 5 {
t.Error("bad state after both takes")
}
}
func TestByteSempahoreCapChangeDown1(t *testing.T) {
// Things should make sense when capacity is adjusted down
s := newByteSemaphore(100)
s.take(75)
if s.available != 25 {
t.Error("bad state after take")
}
s.setCapacity(90)
if s.available != 15 {
t.Error("bad state after adjust")
}
s.give(75)
if s.available != 90 {
t.Error("bad state after give")
}
}
func TestByteSempahoreCapChangeDown2(t *testing.T) {
// Things should make sense when capacity is adjusted down, different case
s := newByteSemaphore(100)
s.take(75)
if s.available != 25 {
t.Error("bad state after take")
}
s.setCapacity(10)
if s.available != 0 {
t.Error("bad state after adjust")
}
s.give(75)
if s.available != 10 {
t.Error("bad state after give")
}
}
func TestByteSempahoreGiveMore(t *testing.T) {
// We shouldn't end up with more available than we have capacity...
s := newByteSemaphore(100)
s.take(150)
if s.available != 0 {
t.Errorf("bad state after large take")
}
s.give(150)
if s.available != 100 {
t.Errorf("bad state after large take + give")
}
s.take(150)
s.setCapacity(125)
// available was zero before, we're increasing capacity by 25
if s.available != 25 {
t.Errorf("bad state after setcap")
}
s.give(150)
if s.available != 125 {
t.Errorf("bad state after large take + give with adjustment")
}
}

View File

@@ -13,7 +13,6 @@ import (
"math/rand"
"path/filepath"
"sort"
"strings"
"sync/atomic"
"time"
@@ -29,6 +28,9 @@ import (
"github.com/syncthing/syncthing/lib/watchaggregator"
)
// scanLimiter limits the number of concurrent scans. A limit of zero means no limit.
var scanLimiter = newByteSemaphore(0)
var errWatchNotStarted = errors.New("not started")
type folder struct {
@@ -285,6 +287,10 @@ func (f *folder) scanSubdirs(subDirs []string) error {
f.model.fmut.RUnlock()
mtimefs := fset.MtimeFS()
f.setState(FolderScanWaiting)
scanLimiter.take(1)
defer scanLimiter.give(1)
for i := range subDirs {
sub := osutil.NativeFilename(subDirs[i])
@@ -411,7 +417,6 @@ func (f *folder) scanSubdirs(subDirs []string) error {
// ignored files.
var toIgnore []db.FileInfoTruncated
ignoredParent := ""
pathSep := string(fs.PathSeparator)
for _, sub := range subDirs {
var iterError error
@@ -423,7 +428,7 @@ func (f *folder) scanSubdirs(subDirs []string) error {
return false
}
if ignoredParent != "" && !strings.HasPrefix(file.Name, ignoredParent+pathSep) {
if ignoredParent != "" && !fs.IsParent(file.Name, ignoredParent) {
for _, file := range toIgnore {
l.Debugln("marking file as ignored", file)
nf := file.ConvertToIgnoredFileInfo(f.model.id.Short())
@@ -698,11 +703,10 @@ func (f *folder) clearScanErrors(subDirs []string) {
return
}
filtered := f.scanErrors[:0]
pathSep := string(fs.PathSeparator)
outer:
for _, fe := range f.scanErrors {
for _, sub := range subDirs {
if strings.HasPrefix(fe.Path, sub+pathSep) {
if fe.Path == sub || fs.IsParent(fe.Path, sub) {
continue outer
}
}
@@ -735,7 +739,7 @@ func unifySubs(dirs []string, exists func(dir string) bool) []string {
dirs = append(dirs[:i], dirs[i+1:]...)
continue
}
if dir == prev || strings.HasPrefix(dir, prev+string(fs.PathSeparator)) {
if dir == prev || fs.IsParent(dir, prev) {
dirs = append(dirs[:i], dirs[i+1:]...)
continue
}

View File

@@ -446,6 +446,9 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
m := NewModel(defaultCfgWrapper, protocol.LocalDeviceID, "syncthing", "dev", db, nil)
m.AddFolder(defaultFolderConfig)
// Set up our evet subscription early
s := events.Default.Subscribe(events.ItemFinished)
f := setUpSendReceiveFolder(m)
// queue.Done should be called by the finisher routine
@@ -470,15 +473,16 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
// Receive a block at puller, to indicate that at least a single copier
// loop has been performed.
toPull := <-pullChan
// Wait until copier is trying to pass something down to the puller again
time.Sleep(100 * time.Millisecond)
// Close the file
toPull.sharedPullerState.fail("test", os.ErrNotExist)
// Unblock copier
<-pullChan
s := events.Default.Subscribe(events.ItemFinished)
timeout = time.Second
// Close the file, causing errors on further access
toPull.sharedPullerState.fail("test", os.ErrNotExist)
// Unblock copier
go func() {
for range pullChan {
}
}()
select {
case state := <-finisherBufferChan:
// At this point the file should still be registered with both the job
@@ -490,12 +494,13 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
// Pass the file down the real finisher, and give it time to consume
finisherChan <- state
if ev, err := s.Poll(timeout); err != nil {
t0 := time.Now()
if ev, err := s.Poll(time.Minute); err != nil {
t.Fatal("Got error waiting for ItemFinished event:", err)
} else if n := ev.Data.(map[string]interface{})["item"]; n != state.file.Name {
t.Fatal("Got ItemFinished event for wrong file:", n)
}
time.Sleep(100 * time.Millisecond)
t.Log("event took", time.Since(t0))
state.mut.Lock()
stateFd := state.fd
@@ -510,11 +515,15 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
// Doing it again should have no effect
finisherChan <- state
time.Sleep(100 * time.Millisecond)
if _, err := s.Poll(time.Second); err != events.ErrTimeout {
t.Fatal("Expected timeout, not another event", err)
}
if f.model.progressEmitter.lenRegistry() != 0 || f.queue.lenProgress() != 0 || f.queue.lenQueued() != 0 {
t.Fatal("Still registered", f.model.progressEmitter.lenRegistry(), f.queue.lenProgress(), f.queue.lenQueued())
}
case <-time.After(time.Second):
t.Fatal("Didn't get anything to the finisher")
}
@@ -528,6 +537,9 @@ func TestDeregisterOnFailInPull(t *testing.T) {
m := NewModel(defaultCfgWrapper, protocol.LocalDeviceID, "syncthing", "dev", db, nil)
m.AddFolder(defaultFolderConfig)
// Set up our evet subscription early
s := events.Default.Subscribe(events.ItemFinished)
f := setUpSendReceiveFolder(m)
// queue.Done should be called by the finisher routine
@@ -552,7 +564,6 @@ func TestDeregisterOnFailInPull(t *testing.T) {
// Receive at finisher, we should error out as puller has nowhere to pull
// from.
s := events.Default.Subscribe(events.ItemFinished)
timeout = time.Second
select {
case state := <-finisherBufferChan:
@@ -565,12 +576,13 @@ func TestDeregisterOnFailInPull(t *testing.T) {
// Pass the file down the real finisher, and give it time to consume
finisherChan <- state
if ev, err := s.Poll(timeout); err != nil {
t0 := time.Now()
if ev, err := s.Poll(time.Minute); err != nil {
t.Fatal("Got error waiting for ItemFinished event:", err)
} else if n := ev.Data.(map[string]interface{})["item"]; n != state.file.Name {
t.Fatal("Got ItemFinished event for wrong file:", n)
}
time.Sleep(100 * time.Millisecond)
t.Log("event took", time.Since(t0))
state.mut.Lock()
stateFd := state.fd
@@ -585,7 +597,10 @@ func TestDeregisterOnFailInPull(t *testing.T) {
// Doing it again should have no effect
finisherChan <- state
time.Sleep(100 * time.Millisecond)
if _, err := s.Poll(time.Second); err != events.ErrTimeout {
t.Fatal("Expected timeout, not another event", err)
}
if f.model.progressEmitter.lenRegistry() != 0 || f.queue.lenProgress() != 0 || f.queue.lenQueued() != 0 {
t.Fatal("Still registered", f.model.progressEmitter.lenRegistry(), f.queue.lenProgress(), f.queue.lenQueued())
@@ -686,3 +701,45 @@ func TestDiffEmpty(t *testing.T) {
}
}
}
// TestDeleteIgnorePerms checks, that a file gets deleted when the IgnorePerms
// option is true and the permissions do not match between the file on disk and
// in the db.
func TestDeleteIgnorePerms(t *testing.T) {
m := setUpModel(protocol.FileInfo{})
f := setUpSendReceiveFolder(m)
f.IgnorePerms = true
ffs := f.Filesystem()
name := "deleteIgnorePerms"
file, err := ffs.Create(name)
if err != nil {
t.Error(err)
}
defer ffs.Remove(name)
defer file.Close()
stat, err := file.Stat()
if err != nil {
t.Fatal(err)
}
fi, err := scanner.CreateFileInfo(stat, name, ffs)
if err != nil {
t.Fatal(err)
}
ffs.Chmod(name, 0600)
scanChan := make(chan string)
finished := make(chan struct{})
go func() {
err = f.checkToBeDeleted(fi, scanChan)
close(finished)
}()
select {
case <-scanChan:
<-finished
case <-finished:
}
if err != nil {
t.Fatal(err)
}
}

View File

@@ -18,6 +18,7 @@ type folderState int
const (
FolderIdle folderState = iota
FolderScanning
FolderScanWaiting
FolderSyncing
FolderError
)
@@ -28,6 +29,8 @@ func (s folderState) String() string {
return "idle"
case FolderScanning:
return "scanning"
case FolderScanWaiting:
return "scan-waiting"
case FolderSyncing:
return "syncing"
case FolderError:

View File

@@ -170,6 +170,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, clientName, clientVersi
if cfg.Options().ProgressUpdateIntervalS > -1 {
go m.progressEmitter.Serve()
}
scanLimiter.setCapacity(cfg.Options().MaxConcurrentScans)
cfg.Subscribe(m)
return m
@@ -288,7 +289,7 @@ func (m *Model) warnAboutOverwritingProtectedFiles(folder string) {
var filesAtRisk []string
for _, protectedFilePath := range m.protectedFiles {
// check if file is synced in this folder
if !strings.HasPrefix(protectedFilePath, folderLocation) {
if protectedFilePath != folderLocation && !fs.IsParent(protectedFilePath, folderLocation) {
continue
}
@@ -796,12 +797,10 @@ func (m *Model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfo
skip--
return true
}
if get > 0 {
ft := f.(db.FileInfoTruncated)
if _, ok := seen[ft.Name]; !ok {
rest = append(rest, ft)
get--
}
ft := f.(db.FileInfoTruncated)
if _, ok := seen[ft.Name]; !ok {
rest = append(rest, ft)
get--
}
return get > 0
})
@@ -809,6 +808,47 @@ func (m *Model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfo
return progress, queued, rest
}
// LocalChangedFiles returns a paginated list of currently needed files in
// progress, queued, and to be queued on next puller iteration, as well as the
// total number of files currently needed.
func (m *Model) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated {
m.fmut.RLock()
defer m.fmut.RUnlock()
rf, ok := m.folderFiles[folder]
if !ok {
return nil
}
fcfg := m.folderCfgs[folder]
if fcfg.Type != config.FolderTypeReceiveOnly {
return nil
}
if rf.ReceiveOnlyChangedSize().TotalItems() == 0 {
return nil
}
files := make([]db.FileInfoTruncated, 0, perpage)
skip := (page - 1) * perpage
get := perpage
rf.WithHaveTruncated(protocol.LocalDeviceID, func(f db.FileIntf) bool {
if !f.IsReceiveOnlyChanged() {
return true
}
if skip > 0 {
skip--
return true
}
ft := f.(db.FileInfoTruncated)
files = append(files, ft)
get--
return get > 0
})
return files
}
// RemoteNeedFolderFiles returns paginated list of currently needed files in
// progress, queued, and to be queued on next puller iteration, as well as the
// total number of files currently needed.
@@ -832,10 +872,8 @@ func (m *Model) RemoteNeedFolderFiles(device protocol.DeviceID, folder string, p
skip--
return true
}
if get > 0 {
files = append(files, f.(db.FileInfoTruncated))
get--
}
files = append(files, f.(db.FileInfoTruncated))
get--
return get > 0
})
@@ -2586,6 +2624,8 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
}
}
scanLimiter.setCapacity(to.Options.MaxConcurrentScans)
// Some options don't require restart as those components handle it fine
// by themselves. Compare the options structs containing only the
// attributes that require restart and act apprioriately.

View File

@@ -144,14 +144,13 @@ func TestAdler32Variants(t *testing.T) {
windowSize := 128
hf2.Reset()
hf3 := rollingAdler32.New()
hf3.Write(data[:windowSize])
for i := windowSize; i < len(data); i++ {
if i%windowSize == 0 {
// let the reference function catch up
hf2.Reset()
hf2.Write(data[i-windowSize : i])
// verify that they are in sync with the rolling function

View File

@@ -250,7 +250,7 @@ func (w *walker) walkAndHashFiles(ctx context.Context, toHashChan chan<- protoco
return skip
}
// If the parent wasn't ignored already, set this path as the "highest" ignored parent
if info.IsDir() && (ignoredParent == "" || !strings.HasPrefix(path, ignoredParent+string(fs.PathSeparator))) {
if info.IsDir() && (ignoredParent == "" || !fs.IsParent(path, ignoredParent)) {
ignoredParent = path
}
return nil
@@ -295,9 +295,10 @@ func (w *walker) handleItem(ctx context.Context, path string, toHashChan chan<-
return skip
}
oldPath := path
path, err = w.normalizePath(path, info)
if err != nil {
w.handleError(ctx, "normalizing path", path, err, finishedChan)
w.handleError(ctx, "normalizing path", oldPath, err, finishedChan)
return skip
}

View File

@@ -136,10 +136,13 @@ func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg *conf
a.notifyTimer = time.NewTimer(a.notifyDelay)
defer a.notifyTimer.Stop()
inProgress := make(map[string]struct{})
inProgressItemSubscription := events.Default.Subscribe(events.ItemStarted | events.ItemFinished)
defer events.Default.Unsubscribe(inProgressItemSubscription)
cfg.Subscribe(a)
defer cfg.Unsubscribe(a)
inProgress := make(map[string]struct{})
for {
select {
@@ -154,7 +157,6 @@ func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg *conf
case folderCfg := <-a.folderCfgUpdate:
a.updateConfig(folderCfg)
case <-a.ctx.Done():
cfg.Unsubscribe(a)
l.Debugln(a, "Stopped")
return
}

View File

@@ -18,23 +18,24 @@ const (
// It implements the adler32 algorithm https://en.wikipedia.org/wiki/Adler-32
type Adler32 struct {
a, b uint32
n uint32
// window is treated like a circular buffer, where the oldest element
// is indicated by d.oldest
window []byte
oldest int
n uint32
vanilla hash.Hash32
}
// Reset resets the digest to its initial state.
func (d *Adler32) Reset() {
d.window = d.window[:1] // Reset the size but don't reallocate
d.window[0] = 0
d.window = d.window[:0] // Reset the size but don't reallocate
d.oldest = 0
d.a = 1
d.b = 0
d.oldest = 0
d.n = 0
d.vanilla.Reset()
}
// New returns a new Adler32 digest
@@ -42,7 +43,8 @@ func New() *Adler32 {
return &Adler32{
a: 1,
b: 0,
window: make([]byte, 1, rollinghash.DefaultWindowCap),
n: 0,
window: make([]byte, 0, rollinghash.DefaultWindowCap),
oldest: 0,
vanilla: vanilla.New(),
}
@@ -54,30 +56,30 @@ func (d *Adler32) Size() int { return Size }
// BlockSize is 1 byte
func (d *Adler32) BlockSize() int { return 1 }
// Write (re)initializes the rolling window with the input byte slice and
// adds its data to the digest.
func (d *Adler32) Write(p []byte) (int, error) {
// Copy the window, avoiding allocations where possible
l := len(p)
// Write appends data to the rolling window and updates the digest.
func (d *Adler32) Write(data []byte) (int, error) {
l := len(data)
if l == 0 {
l = 1
return 0, nil
}
if len(d.window) != l {
if cap(d.window) >= l {
d.window = d.window[:l]
} else {
d.window = make([]byte, len(p))
}
// Re-arrange the window so that the leftmost element is at index 0
n := len(d.window)
if d.oldest != 0 {
tmp := make([]byte, d.oldest)
copy(tmp, d.window[:d.oldest])
copy(d.window, d.window[d.oldest:])
copy(d.window[n-d.oldest:], tmp)
d.oldest = 0
}
copy(d.window, p)
d.window = append(d.window, data...)
// Piggy-back on the core implementation
d.vanilla.Reset()
d.vanilla.Write(p)
d.vanilla.Write(d.window)
s := d.vanilla.Sum32()
d.a, d.b = s&0xffff, s>>16
d.n = uint32(len(p)) % Mod
return len(d.window), nil
d.n = uint32(len(d.window)) % Mod
return len(data), nil
}
// Sum32 returns the hash as a uint32
@@ -94,6 +96,12 @@ func (d *Adler32) Sum(b []byte) []byte {
// Roll updates the checksum of the window from the entering byte. You
// MUST initialize a window with Write() before calling this method.
func (d *Adler32) Roll(b byte) {
// This check costs 10-15% performance. If we disable it, we crash
// when the window is empty. If we enable it, we are always correct
// (an empty window never changes no matter how much you roll it).
//if len(d.window) == 0 {
// return
//}
// extract the entering/leaving bytes and update the circular buffer.
enter := uint32(b)
leave := uint32(d.window[d.oldest])

View File

@@ -14,9 +14,9 @@ const Size = 4
// Bozo32 is a digest which satisfies the rollinghash.Hash32 interface.
type Bozo32 struct {
a uint32
h uint32
aPowerN uint32
a uint32
aⁿ uint32
value uint32
// window is treated like a circular buffer, where the oldest element
// is indicated by d.oldest
@@ -26,19 +26,19 @@ type Bozo32 struct {
// Reset resets the Hash to its initial state.
func (d *Bozo32) Reset() {
d.h = 0
d.aPowerN = 1
d.window = nil
d.value = 0
d.a = 1
d.oldest = 0
d.window = d.window[:0]
}
func NewFromInt(a uint32) *Bozo32 {
return &Bozo32{
a: a,
h: 0,
aPowerN: 1,
window: make([]byte, 1, rollinghash.DefaultWindowCap),
oldest: 0,
a: a,
value: 0,
a: 1,
window: make([]byte, 0, rollinghash.DefaultWindowCap),
oldest: 0,
}
}
@@ -52,32 +52,37 @@ func (d *Bozo32) Size() int { return Size }
// BlockSize is 1 byte
func (d *Bozo32) BlockSize() int { return 1 }
// Write (re)initializes the rolling window with the input byte slice and
// adds its data to the digest. It never returns an error.
// Write appends data to the rolling window and updates the digest. It
// never returns an error.
func (d *Bozo32) Write(data []byte) (int, error) {
// Copy the window
l := len(data)
if l == 0 {
l = 1
return 0, nil
}
if len(d.window) >= l {
d.window = d.window[:l]
} else {
d.window = make([]byte, l)
// Re-arrange the window so that the leftmost element is at index 0
n := len(d.window)
if d.oldest != 0 {
tmp := make([]byte, d.oldest)
copy(tmp, d.window[:d.oldest])
copy(d.window, d.window[d.oldest:])
copy(d.window[n-d.oldest:], tmp)
d.oldest = 0
}
copy(d.window, data)
d.window = append(d.window, data...)
d.value = 0
d.aⁿ = 1
for _, c := range d.window {
d.h *= d.a
d.h += uint32(c)
d.aPowerN *= d.a
d.value *= d.a
d.value += uint32(c)
d.a *= d.a
}
return len(d.window), nil
return len(data), nil
}
// Sum32 returns the hash as a uint32
func (d *Bozo32) Sum32() uint32 {
return d.h
return d.value
}
// Sum returns the hash as byte slice
@@ -89,6 +94,12 @@ func (d *Bozo32) Sum(b []byte) []byte {
// Roll updates the checksum of the window from the entering byte. You
// MUST initialize a window with Write() before calling this method.
func (d *Bozo32) Roll(c byte) {
// This check costs 10-15% performance. If we disable it, we crash
// when the window is empty. If we enable it, we are always correct
// (an empty window never changes no matter how much you roll it).
//if len(d.window) == 0 {
// return
//}
// extract the entering/leaving bytes and update the circular buffer.
enter := uint32(c)
leave := uint32(d.window[d.oldest])
@@ -99,5 +110,5 @@ func (d *Bozo32) Roll(c byte) {
d.oldest = 0
}
d.h = d.h*d.a + enter - leave*d.aPowerN
d.value = d.value*d.a + enter - leave*d.a
}

View File

@@ -65,7 +65,7 @@ func New() *Buzhash32 {
func NewFromUint32Array(b [256]uint32) *Buzhash32 {
return &Buzhash32{
sum: 0,
window: make([]byte, 1, rollinghash.DefaultWindowCap),
window: make([]byte, 0, rollinghash.DefaultWindowCap),
oldest: 0,
bytehash: b,
}
@@ -77,30 +77,31 @@ func (d *Buzhash32) Size() int { return Size }
// BlockSize is 1 byte
func (d *Buzhash32) BlockSize() int { return 1 }
// Write (re)initializes the rolling window with the input byte slice and
// adds its data to the digest.
// Write appends data to the rolling window and updates the digest.
func (d *Buzhash32) Write(data []byte) (int, error) {
// Copy the window, avoiding allocations where possible
l := len(data)
if l == 0 {
l = 1
return 0, nil
}
if len(d.window) != l {
if cap(d.window) >= l {
d.window = d.window[:l]
} else {
d.window = make([]byte, l)
}
// Re-arrange the window so that the leftmost element is at index 0
n := len(d.window)
if d.oldest != 0 {
tmp := make([]byte, d.oldest)
copy(tmp, d.window[:d.oldest])
copy(d.window, d.window[d.oldest:])
copy(d.window[n-d.oldest:], tmp)
d.oldest = 0
}
copy(d.window, data)
d.window = append(d.window, data...)
d.sum = 0
for _, c := range d.window {
d.sum = d.sum<<1 | d.sum>>31
d.sum ^= d.bytehash[int(c)]
}
d.nRotate = uint(len(d.window)) % 32
d.nRotateComplement = 32 - d.nRotate
return len(d.window), nil
return len(data), nil
}
// Sum32 returns the hash as a uint32
@@ -117,6 +118,13 @@ func (d *Buzhash32) Sum(b []byte) []byte {
// Roll updates the checksum of the window from the entering byte. You
// MUST initialize a window with Write() before calling this method.
func (d *Buzhash32) Roll(c byte) {
// This check costs 10-15% performance. If we disable it, we crash
// when the window is empty. If we enable it, we are always correct
// (an empty window never changes no matter how much you roll it).
//if len(d.window) == 0 {
// return
//}
// extract the entering/leaving bytes and update the circular buffer.
hn := d.bytehash[int(c)]
h0 := d.bytehash[int(d.window[d.oldest])]

View File

@@ -65,7 +65,7 @@ func New() *Buzhash64 {
func NewFromUint64Array(b [256]uint64) *Buzhash64 {
return &Buzhash64{
sum: 0,
window: make([]byte, 1, rollinghash.DefaultWindowCap),
window: make([]byte, 0, rollinghash.DefaultWindowCap),
oldest: 0,
bytehash: b,
}
@@ -77,30 +77,32 @@ func (d *Buzhash64) Size() int { return Size }
// BlockSize is 1 byte
func (d *Buzhash64) BlockSize() int { return 1 }
// Write (re)initializes the rolling window with the input byte slice and
// adds its data to the digest.
// Write appends data to the rolling window and updates the digest. It
// never returns an error.
func (d *Buzhash64) Write(data []byte) (int, error) {
// Copy the window, avoiding allocations where possible
l := len(data)
if l == 0 {
l = 1
return 0, nil
}
if len(d.window) != l {
if cap(d.window) >= l {
d.window = d.window[:l]
} else {
d.window = make([]byte, l)
}
// Re-arrange the window so that the leftmost element is at index 0
n := len(d.window)
if d.oldest != 0 {
tmp := make([]byte, d.oldest)
copy(tmp, d.window[:d.oldest])
copy(d.window, d.window[d.oldest:])
copy(d.window[n-d.oldest:], tmp)
d.oldest = 0
}
copy(d.window, data)
d.window = append(d.window, data...)
d.sum = 0
for _, c := range d.window {
d.sum = d.sum<<1 | d.sum>>63
d.sum ^= d.bytehash[int(c)]
}
d.nRotate = uint(len(d.window)) % 64
d.nRotateComplement = 64 - d.nRotate
return len(d.window), nil
return len(data), nil
}
// Sum64 returns the hash as a uint64
@@ -117,6 +119,13 @@ func (d *Buzhash64) Sum(b []byte) []byte {
// Roll updates the checksum of the window from the entering byte. You
// MUST initialize a window with Write() before calling this method.
func (d *Buzhash64) Roll(c byte) {
// This check costs 10-15% performance. If we disable it, we crash
// when the window is empty. If we enable it, we are always correct
// (an empty window never changes no matter how much you roll it).
//if len(d.window) == 0 {
// return
//}
// extract the entering/leaving bytes and update the circular buffer.
hn := d.bytehash[int(c)]
h0 := d.bytehash[int(d.window[d.oldest])]

View File

@@ -70,8 +70,10 @@ func init() {
cache.entries = make(map[index]*tables)
}
func (d *RabinKarp64) buildTables() {
func (d *RabinKarp64) updateTables() {
windowsize := len(d.window)
pol := d.pol
idx := index{d.pol, windowsize}
cache.Lock()
@@ -82,8 +84,15 @@ func (d *RabinKarp64) buildTables() {
return
}
t = &tables{}
d.tables = buildTables(pol, windowsize)
cache.Lock()
cache.entries[idx] = d.tables
cache.Unlock()
return
}
func buildTables(pol Pol, windowsize int) (t *tables) {
t = &tables{}
// calculate table for sliding out bytes. The byte to slide out is used as
// the index for the table, the value contains the following:
// out_table[b] = Hash(b || 0 || ... || 0)
@@ -99,17 +108,17 @@ func (d *RabinKarp64) buildTables() {
var h Pol
h <<= 8
h |= Pol(b)
h = h.Mod(d.pol)
h = h.Mod(pol)
for i := 0; i < windowsize-1; i++ {
h <<= 8
h |= Pol(0)
h = h.Mod(d.pol)
h = h.Mod(pol)
}
t.out[b] = h
}
// calculate table for reduction mod Polynomial
k := d.pol.Deg()
k := pol.Deg()
for b := 0; b < 256; b++ {
// mod_table[b] = A | B, where A = (b(x) * x^k mod pol) and B = b(x) * x^k
//
@@ -118,13 +127,10 @@ func (d *RabinKarp64) buildTables() {
// two parts: Part A contains the result of the modulus operation, part
// B is used to cancel out the 8 top bits so that one XOR operation is
// enough to reduce modulo Polynomial
t.mod[b] = Pol(uint64(b)<<uint(k)).Mod(d.pol) | (Pol(b) << uint(k))
t.mod[b] = Pol(uint64(b)<<uint(k)).Mod(pol) | (Pol(b) << uint(k))
}
d.tables = t
cache.Lock()
cache.entries[idx] = d.tables
cache.Unlock()
return t
}
// NewFromPol returns a RabinKarp64 digest from a polynomial over GF(2).
@@ -139,6 +145,7 @@ func NewFromPol(p Pol) *RabinKarp64 {
window: make([]byte, 0, rollinghash.DefaultWindowCap),
oldest: 0,
}
res.updateTables()
return res
}
@@ -156,9 +163,9 @@ func New() *RabinKarp64 {
func (d *RabinKarp64) Reset() {
d.tables = nil
d.value = 0
d.window = d.window[:1]
d.window[0] = 0
d.window = d.window[:0]
d.oldest = 0
d.updateTables()
}
// Size is 8 bytes
@@ -167,30 +174,33 @@ func (d *RabinKarp64) Size() int { return Size }
// BlockSize is 1 byte
func (d *RabinKarp64) BlockSize() int { return 1 }
// Write (re)initializes the rolling window with the input byte slice and
// adds its data to the digest. It never returns an error.
// Write appends data to the rolling window and updates the digest.
func (d *RabinKarp64) Write(data []byte) (int, error) {
// Copy the window
l := len(data)
if l == 0 {
l = 1
return 0, nil
}
if len(d.window) >= l {
d.window = d.window[:l]
} else {
d.window = make([]byte, l)
// Re-arrange the window so that the leftmost element is at index 0
n := len(d.window)
if d.oldest != 0 {
tmp := make([]byte, d.oldest)
copy(tmp, d.window[:d.oldest])
copy(d.window, d.window[d.oldest:])
copy(d.window[n-d.oldest:], tmp)
d.oldest = 0
}
copy(d.window, data)
d.window = append(d.window, data...)
d.value = 0
for _, b := range d.window {
d.value <<= 8
d.value |= Pol(b)
d.value = d.value.Mod(d.pol)
}
d.buildTables()
d.updateTables()
return len(d.window), nil
return len(data), nil
}
// Sum64 returns the hash as a uint64
@@ -207,6 +217,12 @@ func (d *RabinKarp64) Sum(b []byte) []byte {
// Roll updates the checksum of the window from the entering byte. You
// MUST initialize a window with Write() before calling this method.
func (d *RabinKarp64) Roll(c byte) {
// This check costs 10-15% performance. If we disable it, we crash
// when the window is empty. If we enable it, we are always correct
// (an empty window never changes no matter how much you roll it).
//if len(d.window) == 0 {
// return
//}
// extract the entering/leaving bytes and update the circular buffer.
enter := c
leave := uint64(d.window[d.oldest])

View File

@@ -3,9 +3,11 @@ package main
import (
"flag"
"fmt"
"hash"
"io"
"log"
"os"
"runtime/pprof"
"time"
"code.cloudfoundry.org/bytefmt"
@@ -33,7 +35,10 @@ func genMasks() (res []uint64) {
return
}
func hash2uint64(s []byte) (res uint64) {
// Gets the hash sum as a uint64
func sum64(h hash.Hash) (res uint64) {
buf := make([]byte, 0, 8)
s := h.Sum(buf)
for _, b := range s {
res <<= 8
res |= uint64(b)
@@ -42,18 +47,27 @@ func hash2uint64(s []byte) (res uint64) {
}
func main() {
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")
dostats := flag.Bool("stats", false, "Do some stats about the rolling sum")
size := flag.String("size", "256M", "How much data to read")
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
fileSize, err := bytefmt.ToBytes(*size)
if err != nil {
log.Fatal(err)
}
bufsize := 16 * MiB
rbuf := make([]byte, bufsize)
hbuf := make([]byte, 0, 8)
buf := make([]byte, bufsize)
t := time.Now()
f, err := os.Open("/dev/urandom")
@@ -66,10 +80,10 @@ func main() {
}
}()
io.ReadFull(f, rbuf)
io.ReadFull(f, buf)
roll := rollsum.New()
roll.Write(rbuf[:64])
roll.Write(buf[:64])
masks := genMasks()
hits := make(map[uint64]uint64)
@@ -97,15 +111,15 @@ func main() {
fmt.Printf(status)
fmt.Printf("\r")
}
_, err := io.ReadFull(f, rbuf)
_, err := io.ReadFull(f, buf)
if err != nil {
panic(err)
}
k = 0
}
roll.Roll(rbuf[k])
roll.Roll(buf[k])
if *dostats {
s := hash2uint64(roll.Sum(hbuf))
s := sum64(roll)
for _, m := range masks {
if s&m == m {
hits[m] += 1

2
vendor/manifest vendored
View File

@@ -94,7 +94,7 @@
"importpath": "github.com/chmduquesne/rollinghash",
"repository": "https://github.com/chmduquesne/rollinghash",
"vcs": "git",
"revision": "abb8cbaf9915e48ee20cae94bcd94221b61707a2",
"revision": "a60f8e7142b536ea61bb5d84014171189eeaaa81",
"branch": "master",
"notests": true
},