Basic GUI translation support.

Conflicts:
	gui/index.html
This commit is contained in:
Jakob Borg 2014-07-20 13:49:26 +02:00
parent bcb5f6f472
commit f692e3ac73
7 changed files with 418 additions and 168 deletions

File diff suppressed because one or more lines are too long

78
cmd/translate/main.go Normal file
View File

@ -0,0 +1,78 @@
package main
import (
"encoding/json"
"log"
"os"
"strings"
"code.google.com/p/go.net/html"
)
var trans = make(map[string]string)
func generalNode(n *html.Node) {
translate := false
if n.Type == html.ElementNode {
for _, a := range n.Attr {
if a.Key == "translate" {
translate = true
break
}
}
} else if n.Type == html.TextNode {
v := strings.TrimSpace(n.Data)
if len(v) > 1 && !(strings.HasPrefix(v, "{{") && strings.HasSuffix(v, "}}")) {
log.Println("Untranslated text node:")
log.Print("\t" + v)
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
if translate {
inTranslate(c)
} else {
generalNode(c)
}
}
}
func inTranslate(n *html.Node) {
if n.Type == html.TextNode {
v := strings.TrimSpace(n.Data)
if _, ok := trans[v]; !ok {
av := strings.Replace(v, "{%", "{{", -1)
av = strings.Replace(av, "%}", "}}", -1)
trans[v] = av
}
} else {
log.Println("translate node with non-text child <")
log.Println(n)
}
if n.FirstChild != nil {
log.Println("translate node has children:")
log.Println(n.Data)
}
}
func main() {
fd, err := os.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
err = json.NewDecoder(fd).Decode(&trans)
if err != nil {
log.Fatal(err)
}
fd.Close()
doc, err := html.Parse(os.Stdin)
if err != nil {
log.Fatal(err)
}
generalNode(doc)
bs, err := json.MarshalIndent(trans, "", " ")
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(bs)
}

1
gui/angular-translate-loader.min.js vendored Normal file
View File

@ -0,0 +1 @@
angular.module("pascalprecht.translate").factory("$translateStaticFilesLoader",["$q","$http",function(a,b){return function(c){if(!c||!c.prefix||!c.suffix)throw new Error("Couldn't load static files, no prefix or suffix specified!");var d=a.defer();return b({url:[c.prefix,c.key,c.suffix].join(""),method:"GET",params:""}).success(function(a){d.resolve(a)}).error(function(){d.reject(c.key)}),d.promise}}]);

6
gui/angular-translate.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -7,15 +7,21 @@
'use strict';
var syncthing = angular.module('syncthing', []);
var syncthing = angular.module('syncthing', ['pascalprecht.translate']);
var urlbase = 'rest';
syncthing.config(function ($httpProvider) {
syncthing.config(function ($httpProvider, $translateProvider) {
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token';
$httpProvider.defaults.xsrfCookieName = 'CSRF-Token';
$translateProvider.useStaticFilesLoader({
prefix: 'lang-',
suffix: '.json'
});
$translateProvider.preferredLanguage('en');
});
syncthing.controller('SyncthingCtrl', function ($scope, $http) {
syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate) {
var prevDate = 0;
var getOK = true;
var restarting = false;
@ -47,31 +53,6 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
'touch': 'asterisk',
}
// Strings before bools look better
$scope.settings = [
{id: 'ListenStr', descr: 'Sync Protocol Listen Addresses', type: 'text'},
{id: 'MaxSendKbps', descr: 'Outgoing Rate Limit (KiB/s)', type: 'number'},
{id: 'RescanIntervalS', descr: 'Rescan Interval (s)', type: 'number'},
{id: 'ReconnectIntervalS', descr: 'Reconnect Interval (s)', type: 'number'},
{id: 'ParallelRequests', descr: 'Max Outstanding Requests', type: 'number'},
{id: 'MaxChangeKbps', descr: 'Max File Change Rate (KiB/s)', type: 'number'},
{id: 'LocalAnnPort', descr: 'Local Discovery Port', type: 'number'},
{id: 'LocalAnnEnabled', descr: 'Local Discovery', type: 'bool'},
{id: 'GlobalAnnEnabled', descr: 'Global Discovery', type: 'bool'},
{id: 'StartBrowser', descr: 'Start Browser', type: 'bool'},
{id: 'UPnPEnabled', descr: 'Enable UPnP', type: 'bool'},
{id: 'UREnabled', descr: 'Anonymous Usage Reporting', type: 'bool'},
];
$scope.guiSettings = [
{id: 'Address', descr: 'GUI Listen Addresses', type: 'text', restart: true},
{id: 'User', descr: 'GUI Authentication User', type: 'text', restart: true},
{id: 'Password', descr: 'GUI Authentication Password', type: 'password', restart: true},
{id: 'UseTLS', descr: 'Use HTTPS for GUI', type: 'bool', restart: true},
{id: 'APIKey', descr: 'API Key', type: 'apikey'},
];
function getSucceeded() {
if (!getOK) {
$scope.init();

View File

@ -97,21 +97,24 @@
<span class="navbar-brand"><img class="logo" src="st-logo-128.png" width="32" height="32" /> Syncthing<small class="hidden-xs"> <span class="text-muted">|</span> {{thisNodeName()}}</small></span>
<ul class="nav navbar-nav navbar-right">
<li ng-if="upgradeInfo.newer">
<button type="button" class="btn navbar-btn btn-default" href="" ng-click="upgrade()"><span class="glyphicon glyphicon-chevron-up"></span>&emsp;Upgrade to {{upgradeInfo.latest}}</button>
<button type="button" class="btn navbar-btn btn-default" href="" ng-click="upgrade()">
<span class="glyphicon glyphicon-chevron-up"></span>&emsp;
<span translate translate-value-version="{{upgradeInfo.latest}}">Upgrade To {%version%}</span>
</button>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit&nbsp;<b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span translate>Edit</spanq&nbsp;<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="" ng-click="addRepo()"><span class="glyphicon glyphicon-hdd"></span>&emsp;Add Repository</a></li>
<li><a href="" ng-click="addNode()"><span class="glyphicon glyphicon-retweet"></span>&emsp;Add Node</a></li>
<li><a href="" ng-click="addRepo()"><span class="glyphicon glyphicon-hdd"></span>&emsp;<span translate>Add Repository</span></a></li>
<li><a href="" ng-click="addNode()"><span class="glyphicon glyphicon-retweet"></span>&emsp;<span translate>Add Node</span></a></li>
<li class="divider"></li>
<li><a href="" ng-click="editSettings()"><span class="glyphicon glyphicon-cog"></span>&emsp;Settings</a></li>
<li><a href="" ng-click="idNode()"><span class="glyphicon glyphicon-qrcode"></span>&emsp;Show ID</a></li>
<li><a href="" ng-click="editSettings()"><span class="glyphicon glyphicon-cog"></span>&emsp;<span translate>Settings</span></a></li>
<li><a href="" ng-click="idNode()"><span class="glyphicon glyphicon-qrcode"></span>&emsp;<span translate>Show ID</span></a></li>
<li class="divider"></li>
<li><a href="" ng-click="shutdown()"><span class="glyphicon glyphicon-off"></span>&emsp;Shutdown</a></li>
<li><a href="" ng-click="restart()"><span class="glyphicon glyphicon-refresh"></span>&emsp;Restart</a></li>
<li><a href="" ng-click="shutdown()"><span class="glyphicon glyphicon-off"></span>&emsp;<span translate>Shutdown</span></a></li>
<li><a href="" ng-click="restart()"><span class="glyphicon glyphicon-refresh"></span>&emsp;<span translate>Restart</span></a></li>
<li class="divider"></li>
<li><a href="" ng-click="about()"><span class="glyphicon glyphicon-heart-empty"></span>&emsp;About</a></li>
<li><a href="" ng-click="about()"><span class="glyphicon glyphicon-heart-empty"></span>&emsp;<span translate>About</span></a></li>
</ul>
</li>
</ul>
@ -125,12 +128,12 @@
<div ng-if="!configInSync" class="row">
<div class="col-md-12">
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title">Restart Needed</h3></div>
<div class="panel-heading"><h3 translate class="panel-title">Restart Needed</h3></div>
<div class="panel-body">
<p>The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.</p>
<p translate>The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.</p>
</div>
<div class="panel-footer">
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="restart()"><span class="glyphicon glyphicon-refresh"></span>&emsp;Restart</button>
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="restart()"><span class="glyphicon glyphicon-refresh"></span>&emsp;<span translate>Restart</span></button>
<div class="clearfix"></div>
</div>
</div>
@ -160,60 +163,60 @@
<table class="table table-condensed table-striped">
<tbody>
<tr>
<th><span class="glyphicon glyphicon-tag"></span>&emsp;Repository ID</th>
<th><span class="glyphicon glyphicon-tag"></span>&emsp;<span translate>Repository ID</span></th>
<td class="text-right">{{repo.ID}}</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-folder-open"></span>&emsp;Folder</th>
<th><span class="glyphicon glyphicon-folder-open"></span>&emsp;<span translate>Folder</span></th>
<td class="text-right">{{repo.Directory}}</td>
</tr>
<tr ng-if="model[repo.ID].invalid">
<th><span class="glyphicon glyphicon-warning-sign"></span>&emsp;Error</th>
<th><span class="glyphicon glyphicon-warning-sign"></span>&emsp;<span translate>Error</span></th>
<td class="text-right">{{model[repo.ID].invalid}}</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-comment"></span>&emsp;Synchronization</th>
<th><span class="glyphicon glyphicon-comment"></span>&emsp;<span translate>Synchronization</span></th>
<td class="text-right">{{repoStatus(repo.ID)}}</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-globe"></span>&emsp;Global Repository</th>
<td class="text-right">{{model[repo.ID].globalFiles | alwaysNumber}} items, {{model[repo.ID].globalBytes | binary}}B</td>
<th><span class="glyphicon glyphicon-globe"></span>&emsp;<span translate>Global Repository</span></th>
<td class="text-right">{{model[repo.ID].globalFiles | alwaysNumber}} <span translate>items</span>, {{model[repo.ID].globalBytes | binary}}B</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-home"></span>&emsp;Local Repository</th>
<td class="text-right">{{model[repo.ID].localFiles | alwaysNumber}} items, {{model[repo.ID].localBytes | binary}}B</td>
<th><span class="glyphicon glyphicon-home"></span>&emsp;<span translate>Local Repository</span></th>
<td class="text-right">{{model[repo.ID].localFiles | alwaysNumber}} <span translate>items</span>, {{model[repo.ID].localBytes | binary}}B</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;Out of Sync</th>
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;<span translate>Out Of Sync</span></th>
<td class="text-right">
<a ng-if="model[repo.ID].needFiles > 0" ng-click="showNeed(repo.ID)" href="">{{model[repo.ID].needFiles | alwaysNumber}} items, {{model[repo.ID].needBytes | binary}}B</a>
<span ng-if="model[repo.ID].needFiles == 0">0 items, 0 B</span>
<a ng-if="model[repo.ID].needFiles > 0" ng-click="showNeed(repo.ID)" href="">{{model[repo.ID].needFiles | alwaysNumber}} <span translate>items</span>, {{model[repo.ID].needBytes | binary}}B</a>
<span ng-if="model[repo.ID].needFiles == 0">0 <span translate>items</span>, 0 B</span>
</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-lock"></span>&emsp;Master Repository</th>
<th><span class="glyphicon glyphicon-lock"></span>&emsp;<span translate>Master Repo</span></th>
<td class="text-right">
<span ng-if="repo.ReadOnly">Yes</span>
<span ng-if="!repo.ReadOnly">No</span>
<span translate ng-if="repo.ReadOnly">Yes</span>
<span translate ng-if="!repo.ReadOnly">No</span>
</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-unchecked"></span>&emsp;Ignore Permissions</th>
<th><span class="glyphicon glyphicon-unchecked"></span>&emsp;<span translate>Ignore Permissions</span></th>
<td class="text-right">
<span ng-if="repo.IgnorePerms">Yes</span>
<span ng-if="!repo.IgnorePerms">No</span>
<span translate ng-if="repo.IgnorePerms">Yes</span>
<span translate ng-if="!repo.IgnorePerms">No</span>
</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-share-alt"></span>&emsp;Shared With</th>
<th><span class="glyphicon glyphicon-share-alt"></span>&emsp;<span translate>Shared With</span></th>
<td class="text-right">{{sharesRepo(repo)}}</td>
</tr>
</tbody>
</table>
</div>
<span class="pull-right">
<a class="btn btn-sm btn-primary" href="" ng-click="editRepo(repo)"><span class="glyphicon glyphicon-pencil"></span>&emsp;Edit</a>
<a class="btn btn-sm btn-danger" ng-if="repo.ReadOnly && model[repo.ID].needFiles > 0" ng-click="override(repo.ID)" href=""><span class="glyphicon glyphicon-upload"></span>&emsp;Override Changes</a>
<a class="btn btn-sm btn-primary" href="" ng-click="editRepo(repo)"><span class="glyphicon glyphicon-pencil"></span>&emsp;<span translate>Edit</span></a>
<a class="btn btn-sm btn-danger" ng-if="repo.ReadOnly && model[repo.ID].needFiles > 0" ng-click="override(repo.ID)" href=""><span class="glyphicon glyphicon-upload"></span>&emsp;<span translate>Override Changes</span></a>
</span>
</div>
</div>
@ -237,36 +240,36 @@
<table class="table table-condensed table-striped">
<tbody>
<tr>
<th><span class="glyphicon glyphicon-th"></span>&emsp;RAM Utilization</th>
<th><span class="glyphicon glyphicon-th"></span>&emsp;<span translate>RAM Utilization</span></th>
<td class="text-right">{{system.sys | binary}}B</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-tasks"></span>&emsp;CPU Utilization</th>
<th><span class="glyphicon glyphicon-tasks"></span>&emsp;<span translate>CPU Utilization</span></th>
<td class="text-right">{{system.cpuPercent | alwaysNumber | natural:1}}%</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;Download Rate</th>
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;<span translate>Download Rate</span></th>
<td class="text-right">{{connections['total'].inbps | metric}}bps ({{connections['total'].InBytesTotal | binary}}B)</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-cloud-upload"></span>&emsp;Upload Rate</th>
<th><span class="glyphicon glyphicon-cloud-upload"></span>&emsp;<span translate>Upload Rate</span></th>
<td class="text-right">{{connections['total'].outbps | metric}}bps ({{connections['total'].OutBytesTotal | binary}}B)</td>
</tr>
<tr ng-if="system.extAnnounceOK != undefined">
<th><span class="glyphicon glyphicon-bullhorn"></span>&emsp;Announce Server</th>
<th><span class="glyphicon glyphicon-bullhorn"></span>&emsp;<span translate>Announce Server</span></th>
<td class="text-right">
<span class="data text-success" ng-if="system.extAnnounceOK">Online</span>
<span class="data text-danger" ng-if="!system.extAnnounceOK">Offline</span>
<span class="data text-success" ng-if="system.extAnnounceOK"><span translate>Online</span></span>
<span class="data text-danger" ng-if="!system.extAnnounceOK"><span translate>Offline</span></span>
</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-tag"></span>&emsp;Version</th>
<th><span class="glyphicon glyphicon-tag"></span>&emsp;<span translate>Version</span></th>
<td class="text-right">{{version}}</td>
</tr>
</tbody>
</table>
</div>
<span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span>&emsp;Edit</a></span>
<span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span>&emsp;<span translate>Edit</span></a></span>
</div>
</div>
</div>
@ -287,29 +290,29 @@
<table class="table table-condensed table-striped">
<tbody>
<tr>
<th><span class="glyphicon glyphicon-link"></span>&emsp;Address</th>
<th><span class="glyphicon glyphicon-link"></span>&emsp;<span translate>Address</span></th>
<td class="text-right">{{nodeAddr(nodeCfg)}}</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-comment"></span>&emsp;Synchronization</th>
<th><span class="glyphicon glyphicon-comment"></span>&emsp;<span translate>Synchronization</span></th>
<td class="text-right">{{nodeStatus(nodeCfg)}}</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;Download Rate</th>
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;<span translate>Download Rate</span></th>
<td class="text-right">{{connections[nodeCfg.NodeID].inbps | metric}}bps ({{connections[nodeCfg.NodeID].InBytesTotal | binary}}B)</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-cloud-upload"></span>&emsp;Upload Rate</th>
<th><span class="glyphicon glyphicon-cloud-upload"></span>&emsp;<span translate>Upload Rate</span></th>
<td class="text-right">{{connections[nodeCfg.NodeID].outbps | metric}}bps ({{connections[nodeCfg.NodeID].OutBytesTotal | binary}}B)</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-tag"></span>&emsp;Version</th>
<th><span class="glyphicon glyphicon-tag"></span>&emsp;<span translate>Version</span></th>
<td class="text-right">{{nodeVer(nodeCfg)}}</td>
</tr>
</tbody>
</table>
</div>
<span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span>&emsp;Edit</a></span>
<span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span>&emsp;<span translate>Edit</span></a></span>
</div>
</div>
</div>
@ -322,12 +325,12 @@
<div ng-if="errorList().length > 0" class="row">
<div class="col-md-12">
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title">Notice</h3></div>
<div class="panel-heading"><h3 class="panel-title"><span translate>Notice</span></h3></div>
<div class="panel-body">
<p ng-repeat="err in errorList()"><small>{{err.Time | date:"H:mm:ss"}}:</small> {{friendlyNodes(err.Error)}}</p>
</div>
<div class="panel-footer">
<button type="button" class="pull-right btn btn-sm btn-default" ng-click="clearErrors()"><span class="glyphicon glyphicon-ok"></span>&emsp;OK</button>
<button type="button" class="pull-right btn btn-sm btn-default" ng-click="clearErrors()"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>OK</span></button>
<div class="clearfix"></div>
</div>
</div>
@ -341,39 +344,38 @@
<nav class="navbar navbar-default navbar-fixed-bottom hidden-xs">
<div class="container">
<ul class="nav navbar-nav">
<li><a class="navbar-link" href="http://discourse.syncthing.net/">Support / Forum</a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/releases">Latest Release</a></li>
<li><a class="navbar-link" href="http://discourse.syncthing.net/category/documentation">Documentation</a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/issues">Bugs</a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing">Source Code</a></li>
<li><a class="navbar-link" href="http://discourse.syncthing.net/"><span translate>Support / Forum</span></a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/releases"><span translate>Latest Release</span></a></li>
<li><a class="navbar-link" href="http://discourse.syncthing.net/category/documentation"><span translate>Documentation</span></a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/issues"><span translate>Bugs</span></a></li>
<li><a class="navbar-link" href="https://github.com/calmh/syncthing"><span translate>Source Code</span></a></li>
</ul>
</div>
</nav>
<!-- Network error modal -->
<modal id="networkError" status="danger" icon="exclamation-sign" title="Connection Error">
<p>
Syncthing seems to be down, or there is a problem with your Internet connection.
Retrying&hellip;
<modal id="networkError" status="danger" icon="exclamation-sign" title="{{'Connection Error' | translate}}">
<p translate>
Syncthing seems to be down, or there is a problem with your Internet connection. Retrying&hellip;
</p>
</modal>
<!-- Restarting modal -->
<modal id="restarting" icon="refresh" title="{{restartingTitle}}" status="info">
<p>{{restartingBody}} Please hold&hellip;</p>
<p>{{restartingBody}} <span translate>Please wait</span>&hellip;</p>
</modal>
<!-- Shutdown modal -->
<modal id="shutdown" icon="off" status="success" title="Shutdown Complete">
<p>Syncthing has been shut down.</p>
<p translate>Syncthing has been shut down.</p>
</modal>
<!-- ID modal -->
<modal id="idqr" large="yes" status="info" close="yes" icon="qrcode" title="Node Identification &mdash; {{nodeName(thisNode())}}">
<modal id="idqr" large="yes" status="info" close="yes" icon="qrcode" title="{{'Node Identification' | translate}} &mdash; {{nodeName(thisNode())}}">
<div class="well well-sm text-monospace text-center">{{myID}}</div>
<img ng-if="myID" class="center-block img-thumbnail" src="qr/{{myID}}"/>
</modal>
@ -384,39 +386,38 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 ng-show="!editingExisting" class="modal-title">Add Node</h4>
<h4 ng-show="editingExisting" class="modal-title">Edit Node</h4>
<h4 translate ng-show="!editingExisting" class="modal-title">Add Node</h4>
<h4 translate ng-show="editingExisting" class="modal-title">Edit Node</h4>
</div>
<div class="modal-body">
<form role="form" name="nodeEditor">
<div class="form-group" ng-class="{'has-error': nodeEditor.nodeID.$invalid && nodeEditor.nodeID.$dirty}">
<label for="nodeID">Node ID</label>
<label translate for="nodeID">Node ID</label>
<input ng-if="!editingExisting" name="nodeID" id="nodeID" class="form-control text-monospace" type="text" ng-model="currentNode.NodeID" required valid-nodeid></input>
<div ng-if="editingExisting" class="well well-sm text-monospace">{{currentNode.NodeID}}</div>
<p class="help-block">
<span ng-if="nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine">The node ID to enter here can be found in the "Edit > Show ID" dialog on the other node. Spaces and dashes are optional (ignored).
<span ng-show="!editingExisting">When adding a new node, keep in mind that <em>this node</em> must be added on the other side too.</span>
</span>
<span ng-if="nodeEditor.nodeID.$error.required && nodeEditor.nodeID.$dirty">The node ID cannot be blank.</span>
<span ng-if="nodeEditor.nodeID.$error.validNodeid && nodeEditor.nodeID.$dirty">The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.</span>
<span translate ng-if="nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine">The node ID to enter here can be found in the "Edit > Show ID" dialog on the other node. Spaces and dashes are optional (ignored).</span>
<span translate ng-show="!editingExisting && (nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine)">When adding a new node, keep in mind that this node must be added on the other side too.</span>
<span translate ng-if="nodeEditor.nodeID.$error.required && nodeEditor.nodeID.$dirty">The node ID cannot be blank.</span>
<span translate ng-if="nodeEditor.nodeID.$error.validNodeid && nodeEditor.nodeID.$dirty">The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.</span>
</p>
</div>
<div class="form-group">
<label for="name">Node Name</label>
<label translate for="name">Node Name</label>
<input placeholder="Home Server" id="name" class="form-control" type="text" ng-model="currentNode.Name"></input>
<p class="help-block">Shown instead of Node ID in the cluster status.</p>
<p translate class="help-block">Shown instead of Node ID in the cluster status.</p>
</div>
<div class="form-group">
<label for="addresses">Addresses</label>
<label translate for="addresses">Addresses</label>
<input placeholder="dynamic" ng-disabled="currentNode.NodeID == myID" id="addresses" class="form-control" type="text" ng-model="currentNode.AddressesStr"></input>
<p class="help-block">Enter comma separated <span class="text-monospace">ip:port</span> addresses or <span class="text-monospace">dynamic</span> to perform automatic discovery of the address.</p>
<p translate class="help-block">Enter comma separated "ip:port" addresses or "dynamic" to perform automatic discovery of the address.</p>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="saveNode()" ng-disabled="nodeEditor.$invalid"><span class="glyphicon glyphicon-ok"></span>&emsp;Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;Close</button>
<button ng-if="editingExisting && !editingSelf" type="button" class="btn btn-danger pull-left" ng-click="deleteNode()"><span class="glyphicon glyphicon-minus"></span>&emsp;Delete</button>
<button type="button" class="btn btn-primary" ng-click="saveNode()" ng-disabled="nodeEditor.$invalid"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Save</span></button>
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;<span translate>Close</span></button>
<button ng-if="editingExisting && !editingSelf" type="button" class="btn btn-danger pull-left" ng-click="deleteNode()"><span class="glyphicon glyphicon-minus"></span>&emsp;<span translate>Delete</span></button>
</div>
</div>
</div>
@ -428,29 +429,29 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 ng-show="!editingExisting" class="modal-title">Add Repository</h4>
<h4 ng-show="editingExisting" class="modal-title">Edit Repository</h4>
<h4 ng-show="!editingExisting" class="modal-title"><span translate>Add Repository</span></h4>
<h4 ng-show="editingExisting" class="modal-title"><span translate>Edit Repository</span></h4>
</div>
<div class="modal-body">
<form role="form" name="repoEditor">
<div class="row">
<div class="col-md-12">
<div class="form-group" ng-class="{'has-error': repoEditor.repoID.$invalid && repoEditor.repoID.$dirty}">
<label for="repoID">Repository ID</label>
<label for="repoID"><span translate>Repository ID</span></label>
<input name="repoID" placeholder="documents" ng-disabled="editingExisting" id="repoID" class="form-control" type="text" ng-model="currentRepo.ID" required unique-repo ng-pattern="/^[a-zA-Z0-9-_.]{1,64}$/"></input>
<p class="help-block">
<span ng-if="repoEditor.repoID.$valid || repoEditor.repoID.$pristine">Short identifier for the repository. Must be the same on all cluster nodes.</span>
<span ng-if="repoEditor.repoID.$error.uniqueRepo">The repository ID must be unique.</span>
<span ng-if="repoEditor.repoID.$error.required && repoEditor.repoID.$dirty">The repository ID cannot be blank.</span>
<span ng-if="repoEditor.repoID.$error.pattern && repoEditor.repoID.$dirty">The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the <code>-_.</code> characters only.</span>
<span translate ng-if="repoEditor.repoID.$valid || repoEditor.repoID.$pristine">Short identifier for the repository. Must be the same on all cluster nodes.</span>
<span translate ng-if="repoEditor.repoID.$error.uniqueRepo">The repository ID must be unique.</span>
<span translate ng-if="repoEditor.repoID.$error.required && repoEditor.repoID.$dirty">The repository ID cannot be blank.</span>
<span translate ng-if="repoEditor.repoID.$error.pattern && repoEditor.repoID.$dirty">The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.</span>
</p>
</div>
<div class="form-group" ng-class="{'has-error': repoEditor.repoPath.$invalid && repoEditor.repoPath.$dirty}">
<label for="repoPath">Repository Path</label>
<label translate for="repoPath">Repository Path</label>
<input name="repoPath" placeholder="~/Documents" id="repoPath" class="form-control" type="text" ng-model="currentRepo.Directory" required></input>
<p class="help-block">
<span ng-if="repoEditor.repoPath.$valid || repoEditor.repoPath.$pristine">Path to the repository on the local computer. Will be created if it does not exist. The tilde character <code>~</code> can be used as a shortcut for <code>{{system.tilde}}</code>.</span>
<span ng-if="repoEditor.repoPath.$error.required && repoEditor.repoPath.$dirty">The repository path cannot be blank.</span>
<span translate ng-if="repoEditor.repoPath.$valid || repoEditor.repoPath.$pristine">Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for</span> <code>{{system.tilde}}</code>.
<span translate ng-if="repoEditor.repoPath.$error.required && repoEditor.repoPath.$dirty">The repository path cannot be blank.</span>
</p>
</div>
</div>
@ -460,59 +461,57 @@
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentRepo.ReadOnly"> Repository Master
<input type="checkbox" ng-model="currentRepo.ReadOnly"> <span translate>Repository Master</span>
</label>
</div>
<p class="help-block">Files are protected from changes made on other nodes, but changes made on <em>this</em> node will be sent to the rest of the cluster.</p>
<p translate class="help-block">Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.</p>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentRepo.IgnorePerms"> Ignore Permissions
<input type="checkbox" ng-model="currentRepo.IgnorePerms"> <span translate>Ignore Permissions</span>
</label>
</div>
<p class="help-block">File permission bits are ignored when looking for changes. Use on FAT filesystems.</p>
<p translate class="help-block">File permission bits are ignored when looking for changes. Use on FAT filesystems.</p>
</div>
<div class="form-group">
<label for="nodes">Share With Nodes</label>
<label translate for="nodes">Share With Nodes</label>
<div class="checkbox" ng-repeat="node in otherNodes()">
<label>
<input type="checkbox" ng-model="currentRepo.selectedNodes[node.NodeID]"> {{nodeName(node)}}
</label>
</div>
<p class="help-block">Select the nodes to share this repository with.</p>
<p translate class="help-block">Select the nodes to share this repository with.</p>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentRepo.simpleFileVersioning"> File Versioning
<input type="checkbox" ng-model="currentRepo.simpleFileVersioning"> <span translate>File Versioning</span>
</label>
</div>
<p class="help-block">Files are moved to date stamped versions in a <code>.stversions</code> folder when replaced or deleted by syncthing.</p>
<p translate class="help-block">Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.</p>
</div>
<div class="form-group" ng-if="currentRepo.simpleFileVersioning" ng-class="{'has-error': repoEditor.simpleKeep.$invalid && repoEditor.simpleKeep.$dirty}">
<label for="simpleKeep">Keep Versions</label>
<label translate for="simpleKeep">Keep Versions</label>
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentRepo.simpleKeep" required min="1"></input>
<p class="help-block">
<span ng-if="repoEditor.simpleKeep.$valid || repoEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
<span ng-if="repoEditor.simpleKeep.$error.required && repoEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
<span ng-if="repoEditor.simpleKeep.$error.min && repoEditor.simpleKeep.$dirty">You must keep at least one version.</span>
<span translate ng-if="repoEditor.simpleKeep.$valid || repoEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
<span translate ng-if="repoEditor.simpleKeep.$error.required && repoEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
<span translate ng-if="repoEditor.simpleKeep.$error.min && repoEditor.simpleKeep.$dirty">You must keep at least one version.</span>
</p>
</div>
</div>
</div>
</form>
<div ng-show="!editingExisting">
When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.
</div>
<div translate ng-show="!editingExisting">When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="saveRepo()" ng-disabled="repoEditor.$invalid"><span class="glyphicon glyphicon-ok"></span>&emsp;Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;Close</button>
<button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteRepo()"><span class="glyphicon glyphicon-minus"></span>&emsp;Delete</button>
<button type="button" class="btn btn-primary" ng-click="saveRepo()" ng-disabled="repoEditor.$invalid"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Save</span></button>
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;<span translate>Close</span></button>
<button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteRepo()"><span class="glyphicon glyphicon-minus"></span>&emsp;<span translate>Delete</span></button>
</div>
</div>
</div>
@ -524,48 +523,117 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Settings</h4>
<h4 translate class="modal-title">Settings</h4>
</div>
<div class="modal-body">
<form role="form">
<div class="row">
<div class="col-md-6">
<div class="form-group" ng-repeat="setting in settings">
<div ng-if="setting.type == 'text' || setting.type == 'number'">
<label for="{{setting.id}}">{{setting.descr}}</label>
<input id="{{setting.id}}" class="form-control" type="{{setting.type}}" ng-model="tmpOptions[setting.id]"></input>
</div>
<div class="checkbox" ng-if="setting.type == 'bool'">
<div class="form-group">
<label translate for="ListenStr">Sync Protocol Listen Addresses</label>
<input id="ListenStr" class="form-control" type="text" ng-model="tmpOptions.ListenStr">
</div>
<div class="form-group">
<label translate for="MaxSendKbps">Outgoing Rate Limit (KiB/s)</label>
<input id="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.MaxSendKbps">
</div>
<div class="form-group">
<label translate for="RescanIntervalS">Rescan Interval (s)</label>
<input id="RescanIntervalS" class="form-control" type="number" ng-model="tmpOptions.RescanIntervalS">
</div>
<div class="form-group">
<label translate for="ReconnectIntervalS">Reconnect Interval (s)</label>
<input id="ReconnectIntervalS" class="form-control" type="number" ng-model="tmpOptions.ReconnectIntervalS">
</div>
<div class="form-group">
<label translate for="ParallelRequests">Max Outstanding Requests</label>
<input id="ParallelRequests" class="form-control" type="number" ng-model="tmpOptions.ParallelRequests">
</div>
<div class="form-group">
<label translate for="MaxChangeKbps">Max File Change Rate (KiB/s)</label>
<input id="MaxChangeKbps" class="form-control" type="number" ng-model="tmpOptions.MaxChangeKbps">
</div>
<div class="form-group">
<div class="checkbox">
<label>
{{setting.descr}} <input id="{{setting.id}}" type="checkbox" ng-model="tmpOptions[setting.id]"></input>
<span translate>Local Discovery</span> <input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.LocalAnnEnabled">
</label>
</div>
</div>
<div class="form-group">
<label translate for="LocalAnnPort">Local Discovery Port</label>
<input ng-disabled="!tmpOptions.LocalAnnEnabled" id="LocalAnnPort" class="form-control" type="number" ng-model="tmpOptions.LocalAnnPort">
</div>
<div class="form-group">
<div class="checkbox">
<label>
<span translate>Global Discovery</span> <input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.GlobalAnnEnabled">
</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<span translate>Enable UPnP</span> <input id="UPnPEnabled" type="checkbox" ng-model="tmpOptions.UPnPEnabled">
</label>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group" ng-repeat="setting in guiSettings">
<div ng-if="setting.type == 'text' || setting.type == 'number' || setting.type == 'password'">
<label for="{{setting.id}}">{{setting.descr}}</label>
<input id="{{setting.id}}" class="form-control" type="{{setting.type}}" ng-model="tmpGUI[setting.id]"></input>
</div>
<div class="checkbox" ng-if="setting.type == 'bool'">
<div class="form-group">
<label translate for="Address">GUI Listen Addresses</label>
<input id="Address" class="form-control" type="text" ng-model="tmpGUI.Address">
</div>
<div class="form-group">
<label translate for="User">GUI Authentication User</label>
<input id="User" class="form-control" type="text" ng-model="tmpGUI.User">
</div>
<div class="form-group">
<label translate for="Password">GUI Authentication Password</label>
<input id="Password" class="form-control" type="password" ng-model="tmpGUI.Password">
</div>
<div class="form-group">
<div class="checkbox">
<label>
{{setting.descr}} <input id="{{setting.id}}" type="checkbox" ng-model="tmpGUI[setting.id]"></input>
<span translate>Use HTTPS for GUI</span> <input id="UseTLS" type="checkbox" ng-model="tmpGUI.UseTLS">
</label>
</div>
<div ng-if="setting.type == 'apikey'">
<label>{{setting.descr}} (<a href="http://discourse.syncthing.net/t/v0-8-14-api-keys/335">Usage</a>)</label>
<div class="well well-sm text-monospace">{{tmpGUI[setting.id] || "-"}}</div>
<button type="button" class="btn btn-sm btn-default" ng-click="setAPIKey(tmpGUI)">Generate</button>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<span translate>Start Browser</span> <input id="StartBrowser" type="checkbox" ng-model="tmpOptions.StartBrowser">
</label>
</div>
</div>
<hr/>
<div class="form-group">
<label><span translate>API Key</span> (<a translate href="http://discourse.syncthing.net/t/v0-8-14-api-keys/335">Usage</a>)</label>
<div class="well well-sm text-monospace">{{tmpGUI.APIKey || "-"}}</div>
<button translate type="button" class="btn btn-sm btn-default" ng-click="setAPIKey(tmpGUI)">Generate</button>
</div>
<hr/>
<div class="form-group">
<div class="checkbox">
<label>
<span translate>Anonymous Usage Reporting</span> <input id="UREnabled" type="checkbox" ng-model="tmpOptions.UREnabled">
</label>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="saveSettings()"><span class="glyphicon glyphicon-ok"></span>&emsp;Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;Close</button>
<button type="button" class="btn btn-primary" ng-click="saveSettings()"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Save</span></button>
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;<span translate>Close</span></button>
</div>
</div>
</div>
@ -577,21 +645,17 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header alert alert-success">
<h4 class="modal-title">Allow Anonymous Usage Reporting?</h4>
<h4 translate class="modal-title">Allow Anonymous Usage Reporting?</h4>
</div>
<div class="modal-body">
<p>
The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.
</p>
<p>
The aggregated statistics are publicly available at <a href="https://data.syncthing.net/">https://data.syncthing.net/</a>.
</p>
<button type="button" class="btn btn-default" ng-show="!reportPreview" ng-click="reportPreview = true">Preview Usage Report</button>
<p translate>The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.</p>
<p translate translate-value-url="https://data.syncthing.net">The aggregated statistics are publicly available at {{url}}</p>
<button translate type="button" class="btn btn-default" ng-show="!reportPreview" ng-click="reportPreview = true">Preview Usage Report</button>
<pre ng-if="reportPreview"><small>{{reportData | json}}</small></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="acceptUR()"><span class="glyphicon glyphicon-ok"></span>&emsp;Yes</button>
<button type="button" class="btn btn-danger" ng-click="declineUR()"><span class="glyphicon glyphicon-remove"></span>&emsp;No</button>
<button type="button" class="btn btn-success" ng-click="acceptUR()"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Yes</span></button>
<button type="button" class="btn btn-danger" ng-click="declineUR()"><span class="glyphicon glyphicon-remove"></span>&emsp;<span translate>No</span></button>
</div>
</div>
</div>
@ -615,7 +679,7 @@
<h1 class="text-center"><img src="st-logo-128.png" style="vertical-align: -16px" width="64" height="64"/> Syncthing<br/><small>{{version}}</small></h1>
<hr/>
<p>Copyright &copy; 2014 <b>Jakob Borg</b> and the following <b>Contributors</b>:</p>
<p translate>Copyright &copy; 2014 Jakob Borg and the following Contributors:</p>
<div class="row">
<div class="col-md-6">
<ul>
@ -638,7 +702,7 @@
</div>
<hr/>
<p>Syncthing includes the following software or portions thereof:
<p translate>Syncthing includes the following software or portions thereof:</p>
<ul>
<li><a href="http://golang.org/">The Go Programming Languange</a>, Copyright &copy; 2012 The Go Authors.</li>
<li><a href="https://bitbucket.org/kardianos/osext">kardianos/osext</a>, Copyright &copy; 2012 Daniel Theophanes.</li>
@ -650,11 +714,12 @@
<li><a href="https://angularjs.org/">AngularJS</a>, Copyright &copy; 2010-2014 Google, Inc.</li>
<li><a href="http://getbootstrap.com/">Bootstrap</a>, Copyright &copy; 2011-2014 Twitter, Inc.</li>
</ul>
</p>
</modal>
<script src="angular.min.js"></script>
<script src="angular-translate.min.js"></script>
<script src="angular-translate-loader.min.js"></script>
<script src="jquery-2.0.3.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<script src="app.js"></script>

104
gui/lang-en.json Normal file
View File

@ -0,0 +1,104 @@
{
"API Key": "API Key",
"About": "About",
"Add Node": "Add Node",
"Add Repository": "Add Repository",
"Address": "Address",
"Addresses": "Addresses",
"Allow Anonymous Usage Reporting?": "Allow Anonymous Usage Reporting?",
"Announce Server": "Announce Server",
"Anonymous Usage Reporting": "Anonymous Usage Reporting",
"Bugs": "Bugs",
"CPU Utilization": "CPU Utilization",
"Close": "Close",
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg and the following Contributors:",
"Delete": "Delete",
"Documentation": "Documentation",
"Download Rate": "Download Rate",
"Edit": "Edit",
"Edit Node": "Edit Node",
"Edit Repository": "Edit Repository",
"Enable UPnP": "Enable UPnP",
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.",
"Error": "Error",
"File Versioning": "File Versioning",
"File permission bits are ignored when looking for changes. Use on FAT filesystems.": "File permission bits are ignored when looking for changes. Use on FAT filesystems.",
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.": "Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.",
"Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.": "Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.",
"Folder": "Folder",
"GUI Authentication Password": "GUI Authentication Password",
"GUI Authentication User": "GUI Authentication User",
"GUI Listen Addresses": "GUI Listen Addresses",
"Generate": "Generate",
"Global Discovery": "Global Discovery",
"Global Repository": "Global Repository",
"Ignore Permissions": "Ignore Permissions",
"Keep Versions": "Keep Versions",
"Latest Release": "Latest Release",
"Local Discovery": "Local Discovery",
"Local Discovery Port": "Local Discovery Port",
"Local Repository": "Local Repository",
"Master Repo": "Master Repo",
"Max File Change Rate (KiB/s)": "Max File Change Rate (KiB/s)",
"Max Outstanding Requests": "Max Outstanding Requests",
"No": "No",
"Node ID": "Node ID",
"Node Name": "Node Name",
"Notice": "Notice",
"OK": "OK",
"Offline": "Offline",
"Online": "Online",
"Out Of Sync": "Out Of Sync",
"Outgoing Rate Limit (KiB/s)": "Outgoing Rate Limit (KiB/s)",
"Override Changes": "Override Changes",
"Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for",
"Please wait": "Please wait",
"Preview Usage Report": "Preview Usage Report",
"RAM Utilization": "RAM Utilization",
"Reconnect Interval (s)": "Reconnect Interval (s)",
"Repository ID": "Repository ID",
"Repository Master": "Repository Master",
"Repository Path": "Repository Path",
"Rescan Interval (s)": "Rescan Interval (s)",
"Restart": "Restart",
"Restart Needed": "Restart Needed",
"Save": "Save",
"Select the nodes to share this repository with.": "Select the nodes to share this repository with.",
"Settings": "Settings",
"Share With Nodes": "Share With Nodes",
"Shared With": "Shared With",
"Short identifier for the repository. Must be the same on all cluster nodes.": "Short identifier for the repository. Must be the same on all cluster nodes.",
"Show ID": "Show ID",
"Shown instead of Node ID in the cluster status.": "Shown instead of Node ID in the cluster status.",
"Shutdown": "Shutdown",
"Source Code": "Source Code",
"Start Browser": "Start Browser",
"Support / Forum": "Support / Forum",
"Sync Protocol Listen Addresses": "Sync Protocol Listen Addresses",
"Synchronization": "Synchronization",
"Syncthing has been shut down.": "Syncthing has been shut down.",
"Syncthing includes the following software or portions thereof:": "Syncthing includes the following software or portions thereof:",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…",
"The aggregated statistics are publicly available at {{url}}": "The aggregated statistics are publicly available at {{url}}",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.",
"The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.": "The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.",
"The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.": "The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.",
"The node ID cannot be blank.": "The node ID cannot be blank.",
"The node ID to enter here can be found in the \"Edit \u003e Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).": "The node ID to enter here can be found in the \"Edit \u003e Show ID\" dialog on the other node. Spaces and dashes are optional (ignored).",
"The number of old versions to keep, per file.": "The number of old versions to keep, per file.",
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
"The repository ID cannot be blank.": "The repository ID cannot be blank.",
"The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.": "The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.",
"The repository ID must be unique.": "The repository ID must be unique.",
"The repository path cannot be blank.": "The repository path cannot be blank.",
"Upgrade To {%version%}": "Upgrade To {{version}}",
"Upload Rate": "Upload Rate",
"Usage": "Usage",
"Use HTTPS for GUI": "Use HTTPS for GUI",
"Version": "Version",
"When adding a new node, keep in mind that this node must be added on the other side too.": "When adding a new node, keep in mind that this node must be added on the other side too.",
"When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.": "When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.",
"Yes": "Yes",
"You must keep at least one version.": "You must keep at least one version.",
"items": "items"
}