diff --git a/dist/build-package.ps1 b/dist/build-package.ps1
index c577f9c74..c369b4b97 100644
--- a/dist/build-package.ps1
+++ b/dist/build-package.ps1
@@ -117,7 +117,13 @@ $sln_name = "..\uSync.slnx";
## restore up front with --force-evaluate so the build's implicit restore doesn't run in
## locked mode and fail on NU1403 package content-hash mismatches. --force-evaluate
## re-evaluates against the current cache (updating the lock file) instead of erroring.
-dotnet restore $sln_name --force-evaluate
+##
+## -p:Configuration=$env matters: some projects (uSync.Extend) are excluded from the
+## Debug solution configuration via in
+## uSync.slnx. A restore with no configuration defaults to Debug and silently skips
+## those projects, leaving their lock file stale for the --no-restore build/pack below
+## (which run in $env/Release) - surfacing later as a NU1903 audit failure on pack.
+dotnet restore $sln_name --force-evaluate -p:Configuration=$env
""; "##### Building project"; "--------------------------------"; ""
## --no-restore: we've already restored above, don't let the build kick off a second
diff --git a/docs/perf/batch-save-investigation.md b/docs/perf/batch-save-investigation.md
new file mode 100644
index 000000000..063a7c2b4
--- /dev/null
+++ b/docs/perf/batch-save-investigation.md
@@ -0,0 +1,180 @@
+# Investigation: batched content/media save on import (perf item #1)
+
+**Branch:** `v17/investigate/batch-save`
+**Question:** would routing content/media imports through Umbraco's
+`Save(IEnumerable<…>)` (the batch overload) meaningfully reduce database work,
+given that (a) Umbraco's own batching is limited and (b) notifications may not be
+batched?
+
+**Short answer:** No — not as a safe, general win. In the default configuration
+the only saving would come at the cost of the per-item failure isolation that the
+current default is deliberately designed to provide. In the opt-in "suppressed"
+configuration the transaction and notifications are *already* batched by uSync's
+ambient scope, so the batch overload adds almost nothing. Recommendation: **do not
+wire up bulk `Save` for content/media.** Details and evidence below.
+
+---
+
+## 1. What the batch overload actually does
+
+Decompiled from `Umbraco.Cms.Core.Services.ContentService` (Umbraco 17.3.0,
+`Umbraco.Core.dll`). Media (`MediaService`) is equivalent.
+
+`Save(IContent)` — the per-item path uSync uses today:
+
+```csharp
+using (ICoreScope scope = ScopeProvider.CreateCoreScope())
+{
+ scope.WriteLock(Constants.Locks.ContentTree);
+ if (scope.Notifications.PublishCancelable(new ContentSavingNotification(content, …)))
+ { scope.Complete(); return Cancel; }
+ _documentRepository.Save(content); // 1 row write
+ scope.Notifications.Publish(new ContentSavedNotification(content, …));
+ scope.Notifications.Publish(new ContentTreeChangeNotification(content, RefreshNode, …));
+ Audit(...);
+ scope.Complete(); // 1 transaction commit
+}
+```
+
+`Save(IEnumerable)` — the batch overload:
+
+```csharp
+IContent[] array = contents.ToArray();
+using (ICoreScope scope = ScopeProvider.CreateCoreScope())
+{
+ scope.WriteLock(Constants.Locks.ContentTree);
+ if (scope.Notifications.PublishCancelable(new ContentSavingNotification(array, …))) // ONE, batched
+ { scope.Complete(); return Cancel; }
+ foreach (IContent content in array)
+ _documentRepository.Save(content); // still 1 row write PER item
+ scope.Notifications.Publish(new ContentSavedNotification(array, …)); // ONE, batched
+ scope.Notifications.Publish(new ContentTreeChangeNotification(array, RefreshNode, …));// ONE, batched
+ Audit(...);
+ scope.Complete(); // ONE transaction commit
+}
+```
+
+Key observations:
+
+- **The actual row writes are identical** — `_documentRepository.Save(content)` runs
+ once per item in both. The batch overload does **not** issue a single set-based
+ SQL statement; it loops. So there is **no reduction in the number of INSERT/UPDATE
+ round-trips**.
+- The batch overload's savings are purely **structural**: 1 scope/transaction/commit
+ and 1 write-lock instead of N, and **notifications *are* batched** — `ContentSaving`,
+ `ContentSaved` and `ContentTreeChange` each fire **once with an array** rather than
+ N times. (This corrects the assumption that "notifications aren't batched" — at the
+ `ContentService` level they are, when you use the batch overload.)
+- The batch overload also **skips** the per-item validation that `Save(IContent)`
+ performs: the `PublishedState` guard and the 255-char name-length check. It also
+ doesn't accept a `ContentSchedule`.
+
+So the theoretical benefit of switching is: **N transactions → 1, and N notification
+dispatches → 1.** No change to the number of row writes.
+
+## 2. Does uSync actually pay "N transactions" today? It depends on config.
+
+uSync wraps an import handler run in
+`ICoreScopeProvider.CreateNotificationScope(...)`
+([`ScopeExtensions.cs`](../../uSync.BackOffice/Extensions/ScopeExtensions.cs)):
+
+```csharp
+if (syncConfigService.Settings.DisableNotificationSuppression)
+ return null; // <-- default path
+return scopeProvider.CreateCoreScope(
+ scopedNotificationPublisher: notificationPublisher, // SyncScopedNotificationPublisher
+ autoComplete: true);
+```
+
+`DisableNotificationSuppression` **defaults to `true`** on v16+
+([`uSyncSettings.cs:224`](../../uSync.BackOffice/Configuration/uSyncSettings.cs)),
+so there are two very different runtime shapes:
+
+### Default config — `DisableNotificationSuppression = true`
+
+`CreateNotificationScope` returns **null**. There is **no uSync ambient scope**
+around the import (`SyncService_Handlers.cs`: `scope?.Complete()` is a no-op). Each
+per-item `contentService.Save(item)` therefore opens its **own** root scope →
+its **own** transaction/commit, and fires its notifications **immediately**.
+
+- Here, N items really do mean **N transactions + N notification sets**.
+- The batch overload *would* collapse these to 1 + 1.
+- **BUT** this per-item, non-batched behaviour is a *deliberate design decision*.
+ From the setting's own XML docs:
+
+ > on v16 the default is true, because some of the notifications appear to be
+ > closely coupled to the save/publish process, and if something goes wrong in one
+ > item's import it can cause a cascade of failures across everything that might have
+ > been imported along with it. If the notifications are not suppressed, then if an
+ > item fails to import it doesn't stop other items from being imported.
+
+ Batch-saving reintroduces exactly the failure mode this default exists to avoid:
+ a single bad item (DB constraint, a throwing `Saving`/`Saved` handler, an
+ over-long name that the batch overload no longer validates) rolls back or aborts
+ the **whole batch**, and uSync loses its per-item error attribution (which item
+ failed, with what message). This matches the "batching causes issues" experience.
+
+### Opt-in config — `DisableNotificationSuppression = false`
+
+`CreateNotificationScope` returns a real ambient scope using
+`SyncScopedNotificationPublisher`
+([`SyncScopedNotificationPublisher.cs`](../../uSync.BackOffice/Notifications/SyncScopedNotificationPublisher.cs)).
+
+- **Transaction is already batched.** Umbraco scopes nest; the per-item
+ `contentService.Save` calls become child scopes that share the single ambient
+ transaction, which commits once when uSync completes the outer scope. So the
+ "N transactions" cost is **already gone** without the batch overload.
+- **Notifications are already deferred and grouped.** The scoped publisher collects
+ every notification raised during the import and, at completion, dispatches them
+ grouped by type in one `IEventAggregator.Publish(items)` call per type — or, if
+ `BackgroundNotifications = true`, hands them to the background task queue.
+
+ In this mode, switching to the batch overload only changes "N single-entity
+ `ContentSavedNotification`s, then group-published" into "1 array
+ `ContentSavedNotification`". That is a marginal allocation/dispatch difference,
+ **not** a database saving.
+
+## 3. Why the schema types already use the bulk path — and content doesn't
+
+This asymmetry is intentional. `ContentTypeSerializer`, `MediaTypeSerializer`,
+`MemberTypeSerializer` and `DataTypeSerializer` already override `SaveAsync` and
+honour `SerializerFlags.DoNotSave`, because saving a **doctype/datatype** triggers
+expensive schema changes and full cache/nucache rebuilds — there, batching many into
+one operation is a genuine, large win and the items are few. For **content/media**
+items the per-save cost is dominated by the unavoidable per-row write, and the items
+are many, so the batch overload buys far less while adding the atomicity risk above.
+
+The handler-level bulk hook does exist —
+[`SyncHandlerRoot.ImportAllAsync`](../../uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs)
+calls `serializer.SaveAsync(updates…)` — but only under
+`if (options.Flags.HasFlag(SerializerFlags.DoNotSave))`, and `DoNotSave` is never set
+for the content/media import path. So the plumbing is present but deliberately dormant
+for these types.
+
+## 4. Conclusion & recommendation
+
+| Config | Transactions today | Notifications today | Batch overload benefit | Cost/risk |
+|---|---|---|---|---|
+| **Default** (`DisableNotificationSuppression = true`) | N (one per item) | N, fired immediately | N→1 txn, N→1 notifications | **Loses per-item failure isolation** (the reason this is the default); no reduction in row writes |
+| **Suppressed** (`= false`) | 1 (ambient scope) | Deferred + grouped (opt. background) | ~none (already batched) | Marginal |
+
+**Recommendation: do not wire up bulk `Save(IEnumerable)` for content/media.**
+It delivers no reduction in the dominant cost (per-row writes), its transaction/
+notification batching is either already provided by the suppressed-scope path or
+directly conflicts with the intentional per-item isolation of the default path, and
+it removes per-item error reporting plus two validation checks.
+
+If import throughput on large content sets is the goal, the existing, lower-risk
+levers are configuration, not code:
+
+- **`DisableNotificationSuppression = false`** — collapses the import to a single
+ transaction and defers/groups notifications (this is the "batch" people actually
+ want, done at the scope level rather than the save level).
+- **`BackgroundNotifications = true`** — moves notification processing off the import
+ thread entirely.
+
+If a save-level optimisation is ever revisited, the only shape worth prototyping is a
+**bounded** batch (e.g. save in chunks of N with a per-chunk try/catch that falls back
+to per-item on failure) so the failure blast radius stays small — and it should be
+measured against a real content tree before adoption, since the row-write cost (which
+batching does not change) is expected to dominate.
diff --git a/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs b/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs
index 3f3916481..4fe46a190 100644
--- a/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs
+++ b/uSync.BackOffice/Configuration/uSyncHandlerSettings.cs
@@ -4,6 +4,8 @@
using Umbraco.Extensions;
+using uSync.Core.Extensions;
+
namespace uSync.BackOffice.Configuration;
///
@@ -89,10 +91,10 @@ public static class HandlerSettingsExtensions
///
public static TResult GetSetting(this HandlerSettings settings, string key, TResult defaultValue)
{
- if (settings.Settings != null && settings.Settings.TryGetValue(key, out var value))
+ if (settings.Settings != null && settings.Settings.TryGetValue(key, out var value) && value is not null)
{
- var attempt = value.TryConvertTo();
- if (attempt) return attempt.Result ?? defaultValue;
+ if (value.TryGetValueAs(out var result) && result is not null)
+ return result;
}
return defaultValue;
diff --git a/uSync.BackOffice/Services/ISyncFileService.cs b/uSync.BackOffice/Services/ISyncFileService.cs
index 1cafae4bd..3a56d4353 100644
--- a/uSync.BackOffice/Services/ISyncFileService.cs
+++ b/uSync.BackOffice/Services/ISyncFileService.cs
@@ -136,6 +136,16 @@ public interface ISyncFileService
///
Task LoadXElementAsync(string file);
+ ///
+ /// load just the item key (the Key attribute on the root element) from a file.
+ ///
+ ///
+ /// This streams the file and stops at the root element, so we don't pay the cost
+ /// of parsing the whole document when all we need is the key (e.g. when working out
+ /// which items live in a folder for a 'clean' operation).
+ ///
+ Task LoadKeyFromFileAsync(string file);
+
///
/// merge all the files in the given folders into a single xml node, that can be bulk imported
///
diff --git a/uSync.BackOffice/Services/SyncFileService.cs b/uSync.BackOffice/Services/SyncFileService.cs
index ec556441f..415a962e0 100644
--- a/uSync.BackOffice/Services/SyncFileService.cs
+++ b/uSync.BackOffice/Services/SyncFileService.cs
@@ -211,6 +211,51 @@ public async Task LoadXElementAsync(string file)
}
}
+ private static readonly XmlReaderSettings _keyReaderSettings = new()
+ {
+ CheckCharacters = false,
+ Async = true,
+ IgnoreWhitespace = true,
+ IgnoreComments = true,
+ IgnoreProcessingInstructions = true,
+ DtdProcessing = DtdProcessing.Prohibit,
+ };
+
+ ///
+ public async Task LoadKeyFromFileAsync(string file)
+ {
+ EnsureFileExists(file);
+
+ try
+ {
+ using (var stream = OpenRead(file))
+ {
+ if (stream is null)
+ throw new FileNotFoundException($"Cannot create stream for {file}");
+
+ using (var reader = XmlReader.Create(stream, _keyReaderSettings.Clone()))
+ {
+ // move to the first (root) element and read its Key attribute,
+ // we don't need to read any further into the document.
+ while (await reader.ReadAsync())
+ {
+ if (reader.NodeType != XmlNodeType.Element) continue;
+
+ var key = reader.GetAttribute(global::uSync.Core.uSyncConstants.Xml.Key);
+ return Guid.TryParse(key, out var guid) ? guid : Guid.Empty;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning("Error while reading key from {file} {message}", file, ex.Message);
+ throw new Exception($"Error while reading key from {file}", ex);
+ }
+
+ return Guid.Empty;
+ }
+
///
public async Task SaveFileAsync(string filename, Stream stream)
{
diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs
index b5a7d7f6b..ebe560d8a 100644
--- a/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs
+++ b/uSync.BackOffice/SyncHandlers/SyncHandlerBase.cs
@@ -99,9 +99,11 @@ protected override async Task> CleanFolderAsync(string
private async Task GetCleanParentKeyAsync(string cleanFile)
{
- var node = await syncFileService.LoadXElementAsync(cleanFile);
- if (node.GetKey() == Guid.Empty) return Guid.Empty;
- return (await GetCleanParentAsync(cleanFile))?.Key;
+ // stream the key rather than parsing the whole file, and reuse it for the
+ // parent lookup so we don't read the clean file a second time.
+ var key = await syncFileService.LoadKeyFromFileAsync(cleanFile);
+ if (key == Guid.Empty) return Guid.Empty;
+ return (await GetFromServiceAsync(key))?.Key;
}
///
diff --git a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
index b734ebab2..147ee0a0e 100644
--- a/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
+++ b/uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs
@@ -268,75 +268,79 @@ private HandlerSettings GetDefaultConfig()
public async Task> ImportAllAsync(string[] folders, HandlerSettings config, uSyncImportOptions options)
{
var cacheKey = PrepCaches();
- runtimeCache.ClearByKey(cacheKey);
-
- options.Callbacks?.Update?.Invoke("Calculating import order", 1, 9);
+ try
+ {
+ options.Callbacks?.Update?.Invoke("Calculating import order", 1, 9);
- var items = await GetMergedItemsAsync(folders, new SyncMergeOptions(options.Callbacks?.Update));
+ var items = await GetMergedItemsAsync(folders, new SyncMergeOptions(options.Callbacks?.Update));
- options.Callbacks?.Update?.Invoke($"Processing {items.Count} items", 2, 9);
+ options.Callbacks?.Update?.Invoke($"Processing {items.Count} items", 2, 9);
- // create the update list with items.count space. this is the max size we need this list.
- List actions = new(items.Count);
- List> updates = new(items.Count);
- List cleanMarkers = [];
+ // create the update list with items.count space. this is the max size we need this list.
+ List actions = new(items.Count);
+ List> updates = new(items.Count);
+ List cleanMarkers = [];
- int count = 0;
- int total = items.Count;
+ int count = 0;
+ int total = items.Count;
- options.Callbacks?.SetRange?.Invoke(count, total);
+ options.Callbacks?.SetRange?.Invoke(count, total);
- foreach (var item in items)
- {
- count++;
+ foreach (var item in items)
+ {
+ count++;
- var result = await ImportElementAsync(item.Node, item.FileName, config, options);
- foreach (var attempt in result)
- {
- if (attempt.Success)
+ var result = await ImportElementAsync(item.Node, item.FileName, config, options);
+ foreach (var attempt in result)
{
- if (attempt.Change == ChangeType.Clean)
- {
- cleanMarkers.Add(item.Path);
- }
- else if (attempt.Item is not null && attempt.Item is TObject update)
+ if (attempt.Success)
{
- updates.Add(new ImportedItem(item.Node, update));
+ if (attempt.Change == ChangeType.Clean)
+ {
+ cleanMarkers.Add(item.Path);
+ }
+ else if (attempt.Item is not null && attempt.Item is TObject update)
+ {
+ updates.Add(new ImportedItem(item.Node, update));
+ }
}
- }
- if (attempt.Change != ChangeType.Clean)
- actions.Add(attempt);
+ if (attempt.Change != ChangeType.Clean)
+ actions.Add(attempt);
+ }
}
- }
- // clean up memory we didn't use in the update list.
- updates.TrimExcess();
+ // clean up memory we didn't use in the update list.
+ updates.TrimExcess();
- // bulk save?
- if (updates.Count > 0)
- {
- if (options.Flags.HasFlag(SerializerFlags.DoNotSave))
+ // bulk save?
+ if (updates.Count > 0)
{
- await serializer.SaveAsync(updates.Select(x => x.Item));
- }
+ if (options.Flags.HasFlag(SerializerFlags.DoNotSave))
+ {
+ await serializer.SaveAsync(updates.Select(x => x.Item));
+ }
- await PerformSecondPassImportsAsync(updates, actions, config, options.Callbacks?.Update);
- }
+ await PerformSecondPassImportsAsync(updates, actions, config, options.Callbacks?.Update);
+ }
- if (actions.All(x => x.Success) && cleanMarkers.Count > 0)
- {
- await PerformImportCleanAsync(cleanMarkers, actions, config, options.Callbacks?.Update);
- }
+ if (actions.All(x => x.Success) && cleanMarkers.Count > 0)
+ {
+ await PerformImportCleanAsync(cleanMarkers, actions, config, options.Callbacks?.Update);
+ }
- CleanCaches(cacheKey);
- options.Callbacks?.Update?.Invoke("Done", 3, 3);
+ options.Callbacks?.Update?.Invoke("Done", 3, 3);
- if (logger.IsEnabled(LogLevel.Debug))
- logger.LogDebug("ImportAll: {count} items imported", actions.Count);
+ if (logger.IsEnabled(LogLevel.Debug))
+ logger.LogDebug("ImportAll: {count} items imported", actions.Count);
- return actions;
+ return actions;
+ }
+ finally
+ {
+ CleanCaches(cacheKey);
+ }
}
///
@@ -707,8 +711,8 @@ protected async Task> GetFolderKeysAsync(string folder, bool flat)
foreach (var file in files)
{
- var node = await syncFileService.LoadXElementAsync(file);
- var key = node.GetKey();
+ // we only need the key here, so stream it rather than parsing the whole file.
+ var key = await syncFileService.LoadKeyFromFileAsync(file);
if (key != Guid.Empty)
{
keySet.Add(key);
@@ -729,8 +733,8 @@ protected async Task> GetFolderKeysAsync(string folder, bool flat)
///
protected async Task GetCleanParentAsync(string file)
{
- var node = await syncFileService.LoadXElementAsync(file);
- var key = node.GetKey();
+ // we only need the key to find the parent, so stream it rather than parsing the whole file.
+ var key = await syncFileService.LoadKeyFromFileAsync(file);
if (key == Guid.Empty) return default;
return await GetFromServiceAsync(key);
}
@@ -819,7 +823,19 @@ virtual protected async Task ShouldImportAsync(XElement node, HandlerSetti
/// Export all items to a give folder on the disk
///
virtual public async Task> ExportAllAsync(string[] folders, HandlerSettings settings, SyncUpdateCallback? callback)
- => await ExportAllAsync(default, folders, settings, callback);
+ {
+ // scope the runtime cache (e.g. child-item lookups) to this export run, so the
+ // cache keys stay stable across async thread hops and are cleared when we're done.
+ var cacheKey = BeginCacheScope();
+ try
+ {
+ return await ExportAllAsync(default, folders, settings, callback);
+ }
+ finally
+ {
+ EndCacheScope(cacheKey);
+ }
+ }
///
@@ -1088,28 +1104,33 @@ public async Task> ReportAsync(string[] folders, Handle
List actions = [];
var cacheKey = PrepCaches();
+ try
+ {
+ callback?.Invoke("Calculating order", 1, 3);
- callback?.Invoke("Calculating order", 1, 3);
+ var items = await GetMergedItemsAsync(folders, new SyncMergeOptions(callback));
+ var options = new uSyncImportOptions();
- var items = await GetMergedItemsAsync(folders, new SyncMergeOptions(callback));
- var options = new uSyncImportOptions();
+ int count = 0;
- int count = 0;
+ foreach (var item in items)
+ {
+ count++;
+ callback?.Invoke(Path.GetFileNameWithoutExtension(item.Path), count, items.Count);
+ actions.AddRange(await ReportElementAsync(item.Node, item.FileName, config, options));
+ }
- foreach (var item in items)
+ callback?.Invoke("Validating Report", 2, 3);
+ var validationActions = await ReportMissingParentsAsync([.. actions]);
+ actions.AddRange(ReportDeleteCheck(uSyncConfig.GetWorkingFolder(), validationActions));
+
+ callback?.Invoke($"Done ({this.ItemType})", 3, 3);
+ return actions;
+ }
+ finally
{
- count++;
- callback?.Invoke(Path.GetFileNameWithoutExtension(item.Path), count, items.Count);
- actions.AddRange(await ReportElementAsync(item.Node, item.FileName, config, options));
+ CleanCaches(cacheKey);
}
-
- callback?.Invoke("Validating Report", 2, 3);
- var validationActions = await ReportMissingParentsAsync([.. actions]);
- actions.AddRange(ReportDeleteCheck(uSyncConfig.GetWorkingFolder(), validationActions));
-
- CleanCaches(cacheKey);
- callback?.Invoke($"Done ({this.ItemType})", 3, 3);
- return actions;
}
///
@@ -1600,6 +1621,13 @@ protected string[] GetDefaultHandlerFolders()
///
protected virtual async Task CleanUpAsync(TObject item, string newFile, string folder)
{
+ // when using a flat folder structure with guid file names, an item's file is always
+ // "{key}.{ext}" in the same folder - it never changes name or location - so there can
+ // never be a stale duplicate file to clean up. Skip the (recursive) folder scan, which
+ // otherwise runs on every save/move/delete. (Content/Media make the same check earlier.)
+ if (DefaultConfig.UseFlatStructure && DefaultConfig.GuidNames)
+ return;
+
var physicalFile = syncFileService.GetAbsPath(newFile);
var files = syncFileService.GetFiles(folder, $"*.{this.uSyncConfig.Settings.DefaultExtension}");
@@ -1992,32 +2020,70 @@ protected string GetNameFromFileOrNode(string filename, XElement node)
///
- /// get the key for any caches we might call (thread based cache value)
+ /// per-operation cache scope id.
+ ///
+ ///
+ /// this flows with the async operation (via AsyncLocal) so the runtime-cache keys
+ /// stay stable even when a continuation resumes on a different thread. Previously the
+ /// key was based on the managed thread id, which could change mid-operation across an
+ /// await - defeating the cache (misses) and leaving orphaned entries in the shared
+ /// runtime cache that the end-of-operation cleanup (running on another thread) missed.
+ ///
+ /// when no operation scope is active (ad-hoc lookups) we fall back to the managed
+ /// thread id, preserving the previous behavior for those callers.
+ ///
+ private readonly AsyncLocal _cacheScope = new();
+
+ ///
+ /// get the key for any caches we might call (scoped to the current operation)
///
///
protected string GetCacheKeyBase()
- => $"keyCache_{this.Alias}_{Environment.CurrentManagedThreadId}";
+ => $"keyCache_{this.Alias}_{_cacheScope.Value ?? $"t{Environment.CurrentManagedThreadId}"}";
private string PrepCaches()
{
if (this.serializer is ISyncCachedSerializer cachedSerializer)
cachedSerializer.InitializeCache();
- // make sure the runtime cache is clean.
- var key = GetCacheKeyBase();
-
- // this also clears the folder cache - as its a starts with call.
- runtimeCache.ClearByKey(key);
- return key;
+ return BeginCacheScope();
}
private void CleanCaches(string cacheKey)
{
- runtimeCache.ClearByKey(cacheKey);
+ EndCacheScope(cacheKey);
if (this.serializer is ISyncCachedSerializer cachedSerializer)
cachedSerializer.DisposeCache();
+ }
+ ///
+ /// begin a runtime-cache scope for a single logical operation (import/report/export).
+ ///
+ ///
+ /// the scope id flows with the async operation (see ) so the
+ /// cache keys stay stable across await/thread hops, and the entries can be reliably
+ /// cleared when the operation finishes.
+ ///
+ /// the base cache key for the scope.
+ private string BeginCacheScope()
+ {
+ _cacheScope.Value = Guid.NewGuid().ToString("N");
+
+ // this also clears the folder cache - as its a 'starts with' call.
+ var key = GetCacheKeyBase();
+ runtimeCache.ClearByKey(key);
+ return key;
+ }
+
+ ///
+ /// end the runtime-cache scope started by , clearing the
+ /// entries cached during the operation.
+ ///
+ private void EndCacheScope(string cacheKey)
+ {
+ runtimeCache.ClearByKey(cacheKey);
+ _cacheScope.Value = null;
}
#region roots notifications
diff --git a/uSync.BackOffice/uSyncBackOfficeComposer.cs b/uSync.BackOffice/uSyncBackOfficeComposer.cs
index 20aa8bec2..abdca6e98 100644
--- a/uSync.BackOffice/uSyncBackOfficeComposer.cs
+++ b/uSync.BackOffice/uSyncBackOfficeComposer.cs
@@ -18,10 +18,10 @@ public class uSyncBackOfficeComposer : IComposer
///
public void Compose(IUmbracoBuilder builder)
{
- // uSync core will actually run when their is no back office loaded.
+ // uSync core will actually run when their is no back office loaded.
//if (builder.IsUmbracoBackOfficeEnabled() is false)
// return;
-
+
builder.AdduSync();
}
}
diff --git a/uSync.Backoffice.Management.Client/uSyncManifestReader.cs b/uSync.Backoffice.Management.Client/uSyncManifestReader.cs
index 1472203a1..741ccda2a 100644
--- a/uSync.Backoffice.Management.Client/uSyncManifestReader.cs
+++ b/uSync.Backoffice.Management.Client/uSyncManifestReader.cs
@@ -21,7 +21,7 @@ public void Compose(IUmbracoBuilder builder)
{
if (builder.IsUmbracoBackOfficeEnabled() is false) return;
- // only load this when the backoffice is enabled.
+ // only load this when the backoffice is enabled.
builder.Services.AddSingleton();
builder.Services.AddSingleton();
}
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts b/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts
index 64fb0da1b..aff66fccc 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/components/usync-action-box.ts
@@ -64,7 +64,7 @@ export class uSyncActionBox extends UmbLitElement {
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/da-dk.ts b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/da-dk.ts
new file mode 100644
index 000000000..4b2fce8cc
--- /dev/null
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/da-dk.ts
@@ -0,0 +1,191 @@
+export default {
+ uSync: {
+ section: 'Synkronisering',
+ name: 'uSync',
+ banner: 'uSync alt på én gang',
+
+ migrate: 'Migrer',
+ defaultView: 'Standard',
+ settingsView: 'Indstillinger',
+ addons: 'Tilføjelser',
+
+ groupEverything: 'Alt',
+ groupContent: 'Indhold',
+ groupSettings: 'Indstillinger',
+ groupForms: 'Formularer',
+ groupMedia: 'Medie',
+ groupMembers: 'Medlemmer',
+
+ Report: 'Rapport',
+ Import: 'Importer',
+ Export: 'Eksporter',
+ ImportForce: 'Importer (Tvungen)',
+ ExportClean: 'Eksporter (Ren)',
+ ExportFile: 'Eksporter til fil',
+ ImportFile: 'Importer fra fil',
+
+ noChange: 'Intet har ændret sig',
+ showAll: 'Vis alle elementer',
+
+ detailHeadline: 'Registrerede ændringer',
+ detailHeader: 'Ting der er anderledes',
+
+ runningInBackground:
+ 'uSync kører denne proces i baggrunden. Hvis du navigerer væk fra denne side, vil den fortsætte med at køre.',
+
+ connectionLost:
+ 'Forbindelsen til serveren er gået tabt. Processen vil fortsætte med at køre i baggrunden, men du vil ikke se opdateringer her.',
+
+ importSingle: 'Importer',
+ importSingleWarning:
+ 'Dette vil importere dette element til Umbraco. Hvis dette element har afhængigheder, vil de ikke blive importeret og skal løses manuelt.',
+ importSingleSuccess: 'Elementet er blevet importeret korrekt',
+ importSingleFailed: `
Der opstod en fejl ved import af elementet
%0%`,
+
+ changeAction: 'Handling',
+ changeItem: 'Element',
+ changeDiffrence: 'Forskel',
+ changeCreate: 'Dette element findes ikke i Umbraco og oprettes',
+ noChangesImport: 'Der blev ikke foretaget ændringer i dette element',
+ noChangesReport: 'Ingen ændringer registreret',
+ noChangesDelete: 'Dette element er blevet slettet fra Umbraco',
+
+ importHeader: 'Importer fra fil',
+
+ success: 'Succes',
+ change: 'Ændring',
+ changeType: 'Type',
+ changeName: 'Navn',
+ changeDetail: 'Detalje',
+ changeHeading: 'Resultater',
+ changeCount: '{1}/{0} ændringer',
+ noChangeCount: '0/{0} ændringer',
+
+ legacyInfo: `
uSync har fundet en ældre uSync-mappe på %0%.
+ Det er sandsynligt, at indholdet skal konverteres på en eller anden måde
`,
+
+ legacyObsolete: `
Forældede datatyper
+
%0%
+
Du kan konvertere disse datatyper ved hjælp af
+ uSync.Migrations
+ (I den fulde uSync-udgivelse vil konvertering ske her.)
`,
+
+ legacyCopy: `
Kopiér til uSync/v14
+
Du kan kopiere din %0%-mappe til ~/uSync/v14-mappen og køre en import.
+
Hvis intet skal konverteres, bør alt importeres korrekt.
+
Fjern eller omdøb %0%-mappen for at forhindre denne popup
`,
+
+ hmacMismatch: `
HMAC-uoverensstemmelse
+
Det ser ud til, at indstillingen Imaging:HMAC, der bruges til at generere filerne i uSync-mappen, ikke stemmer overens med den aktuelle indstilling for dette websted.
+
Billeder i RTE-kontrolelementer vil have HMAC-værdien tilføjet til URL-værdien, og uden yderligere konfiguration vises disse billeder muligvis ikke korrekt.
+
Du kan aktivere "HMAC-mapping" i uSync,
+
eller du kan sikre, at HMAC-værdien i indstillingen Imaging:HMAC stemmer overens med den værdi, der bruges til at generere filerne i uSync-mappen
`,
+ formatMismatch:
+ 'Synkroniseringsfilformatversionen stemmer ikke overens med den forventede version. Dette kan indikere et potentielt kompatibilitetsproblem.',
+
+ legacyBanner:
+ 'Dette websted indeholder filer fra en tidligere version af uSync. Se detaljerne under fanen Ældre.',
+
+ legacyCopyTitle: 'Overskriv v%0%-filer',
+ legacyCopyContent:
+ 'Er du sikker på, at du vil overskrive indholdet af mappen %0% med de ældre uSync-mappefilerne?',
+
+ legacyIgnoreTitle: 'Ignorer ældre filer',
+ legacyIgnoreContent:
+ 'Er du sikker på, at du vil ignorere filerne i den ældre uSync-mappe?',
+
+ errorHeader:
+ 'Dette element stødte på en fejl under processen. Detaljerne er nedenfor:',
+
+ uploadIntro: 'Vælg en zip-fil med uSync-filer, som du vil uploade',
+ uploadSuccess: 'Filerne er blevet uploadet og udtrukket til uSync-mappen',
+ uploadError: 'Der opstod en fejl ved upload af filerne',
+
+ ILanguage: 'Sprog',
+ IDictionaryItem: 'Ordbogselementer',
+ IDataType: 'Datatyper',
+ ITemplate: 'Skabeloner',
+ IContentType: 'Indholdstyper',
+ IMediaType: 'Medietyper',
+ IMemberType: 'Medlemstyper',
+ IContent: 'Indhold',
+ IMedia: 'Medie',
+ IDomain: 'Domæner',
+ IWebhook: 'Webhooks',
+ IRelationType: 'Relationstyper',
+ MediaFile: 'Mediefiler',
+ XElement: 'Andet',
+ LanguageHandler: 'Sprog',
+ DictionaryHandler: 'Ordbogselementer',
+ DataTypeHandler: 'Datatyper',
+ TemplateHandler: 'Skabeloner',
+ ContentTypeHandler: 'Indholdstyper',
+ MediaTypeHandler: 'Medietyper',
+ MemberTypeHandler: 'Medlemstyper',
+ ContentHandler: 'Indhold',
+ MediaHandler: 'Medie',
+ RelationTypeHandler: 'Relationstyper',
+ EntityContainer: 'Containere',
+ },
+ USyncSettings: {
+ settings: 'uSync-indstillinger',
+ filesAndFolders: 'Filer og mapper',
+ handlerDefaults: 'Handlerstandarder',
+
+ processingMode: 'Behandlingstilstand',
+ processingModeDesc:
+ 'Hvordan uSync-processen kører, enten i baggrunden eller interaktivt (Normal)',
+
+ importAtStartup: 'Importer ved opstart',
+ importAtStartupDesc: 'Kør en import af filer fra disken, når Umbraco starter',
+
+ exportAtStartup: 'Eksporter ved opstart',
+ exportAtStartupDesc: 'Eksporter Umbraco-indstillingerne, når webstedet starter',
+
+ exportOnSave: 'Eksporter ved gem',
+ exportOnSaveDesc: 'Generer uSync-filer, når elementer gemmes',
+
+ uiEnabledGroups: 'Aktiverede UI-grupper',
+ uiEnabledGroupsDesc: 'Handlergrupper, der kan ses/bruges på dashboardet',
+
+ failOnMissingParent: 'Fejl ved manglende overordnet',
+ failOnMissingParentDesc: 'Fejl ved manglende overordnet element',
+
+ currentHandlerSet: 'Aktuelt sæt',
+
+ handlerSet: 'Standardhandlersæt',
+ handlerSetDesc: 'Det standardhandlersæt, der bruges for webstedet',
+
+ flatStructure: 'Flad struktur',
+ flatStructureDesc: 'Alle elementer af en type gemmes i en flad mappestruktur',
+
+ guidNames: 'Brug GUID som filnavne',
+ guidNamesDesc: 'Brug et elements GUID som filnavn',
+
+ handlerGroups: 'Handlergrupper',
+ handlerGroupsDesc: 'Grupper til at begrænse handlersættet til',
+
+ disabledHandlers: 'Deaktiverede handlere',
+ disabledHandlersDesc: 'Handlere eksplicit deaktiveret for dette handlersæt',
+
+ folders: 'Mapper',
+ foldersDesc:
+ 'Mapper uSync vil søge efter filer i (elementer gemmes normalt i den sidste mappe på listen)',
+
+ rootSite: 'Rodwebsted',
+ rootSiteDesc: 'Er dette websted en rod for andre websteder.',
+
+ rootLocked: 'Rod låst',
+ rootLockedDesc: 'Er ændringer for elementer fra rodwebstedet låst?',
+
+ help: 'Indstillinger styres via filen appsettings.json. Se vores dokumentation',
+
+ bootSettings: 'Indstillinger for første opstart 🥾',
+
+ firstBoot: 'Importer ved første opstart',
+ firstBootDesc: 'Kør importprocessen ved webstedets første opstart',
+
+ firstBootGroup: 'Grupper for første opstart',
+ firstBootGroupDesc: 'De grupper, der køres ved første opstart',
+ },
+};
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/de-de.ts b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/de-de.ts
new file mode 100644
index 000000000..899bc411c
--- /dev/null
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/de-de.ts
@@ -0,0 +1,191 @@
+export default {
+ uSync: {
+ section: 'Synchronisation',
+ name: 'uSync',
+ banner: 'uSync für alles',
+
+ migrate: 'Migrieren',
+ defaultView: 'Standard',
+ settingsView: 'Einstellungen',
+ addons: 'Erweiterungen',
+
+ groupEverything: 'Alles',
+ groupContent: 'Inhalt',
+ groupSettings: 'Einstellungen',
+ groupForms: 'Formulare',
+ groupMedia: 'Medien',
+ groupMembers: 'Mitglieder',
+
+ Report: 'Bericht',
+ Import: 'Importieren',
+ Export: 'Exportieren',
+ ImportForce: 'Importieren (Erzwingen)',
+ ExportClean: 'Exportieren (Bereinigt)',
+ ExportFile: 'In Datei exportieren',
+ ImportFile: 'Aus Datei importieren',
+
+ noChange: 'Es hat sich nichts geändert',
+ showAll: 'Alle Elemente anzeigen',
+
+ detailHeadline: 'Erkannte Änderungen',
+ detailHeader: 'Was sich unterscheidet',
+
+ runningInBackground:
+ 'uSync führt diesen Prozess im Hintergrund aus. Wenn Sie diese Seite verlassen, wird er weiterhin ausgeführt.',
+
+ connectionLost:
+ 'Die Verbindung zum Server wurde unterbrochen. Der Prozess wird im Hintergrund weiter ausgeführt, aber Sie werden hier keine Aktualisierungen sehen.',
+
+ importSingle: 'Importieren',
+ importSingleWarning:
+ 'Dadurch wird dieses Element in Umbraco importiert. Wenn dieses Element Abhängigkeiten hat, werden diese nicht importiert und müssen manuell aufgelöst werden.',
+ importSingleSuccess: 'Das Element wurde erfolgreich importiert',
+ importSingleFailed: `
Beim Importieren des Elements ist ein Fehler aufgetreten
%0%`,
+
+ changeAction: 'Aktion',
+ changeItem: 'Element',
+ changeDiffrence: 'Unterschied',
+ changeCreate: 'Dieses Element existiert nicht in Umbraco und wird erstellt',
+ noChangesImport: 'Es wurden keine Änderungen an diesem Element vorgenommen',
+ noChangesReport: 'Keine Änderungen erkannt',
+ noChangesDelete: 'Dieses Element wurde aus Umbraco gelöscht',
+
+ importHeader: 'Aus Datei importieren',
+
+ success: 'Erfolg',
+ change: 'Änderung',
+ changeType: 'Typ',
+ changeName: 'Name',
+ changeDetail: 'Detail',
+ changeHeading: 'Ergebnisse',
+ changeCount: '{1}/{0} Änderungen',
+ noChangeCount: '0/{0} Änderungen',
+
+ legacyInfo: `
uSync hat einen Legacy-uSync-Ordner unter %0% gefunden.
+ Der Inhalt muss wahrscheinlich konvertiert werden
`,
+
+ legacyObsolete: `
Veraltete Datentypen
+
%0%
+
Sie können diese Datentypen mit
+ uSync.Migrations konvertieren
+ (In der vollständigen uSync-Version wird die Konvertierung hier stattfinden.)
`,
+
+ legacyCopy: `
Nach uSync/v14 kopieren
+
Sie können Ihren %0%-Ordner in den ~/uSync/v14-Ordner kopieren und einen Import ausführen.
+
Wenn nichts konvertiert werden muss, sollte alles korrekt importiert werden.
+
Entfernen oder benennen Sie den %0%-Ordner um, um dieses Popup zu verhindern
`,
+
+ hmacMismatch: `
HMAC-Abweichung
+
Es scheint, dass die Imaging:HMAC-Einstellung, die zum Generieren der Dateien im uSync-Ordner verwendet wurde, nicht mit der aktuellen Einstellung dieser Website übereinstimmt.
+
Bilder in RTE-Steuerelementen haben den HMAC-Wert an die URL angehängt. Ohne zusätzliche Konfiguration werden diese Bilder möglicherweise nicht korrekt dargestellt.
+
Sie können die "HMAC-Zuordnung" in uSync aktivieren,
+
oder Sie können sicherstellen, dass der HMAC-Wert in der Imaging:HMAC-Einstellung mit dem Wert übereinstimmt, der zum Generieren der Dateien im uSync-Ordner verwendet wurde
`,
+ formatMismatch:
+ 'Die Formatversion der Synchronisierungsdatei stimmt nicht mit der erwarteten Version überein. Dies kann auf ein potenzielles Kompatibilitätsproblem hinweisen.',
+
+ legacyBanner:
+ 'Diese Website enthält Dateien einer früheren Version von uSync. Details finden Sie auf der Registerkarte Legacy.',
+
+ legacyCopyTitle: 'v%0%-Dateien überschreiben',
+ legacyCopyContent:
+ 'Möchten Sie den Inhalt des Ordners %0% wirklich mit den Legacy-uSync-Ordnerdateien überschreiben?',
+
+ legacyIgnoreTitle: 'Legacy-Dateien ignorieren',
+ legacyIgnoreContent:
+ 'Möchten Sie die Dateien im Legacy-uSync-Ordner wirklich ignorieren?',
+
+ errorHeader:
+ 'Bei diesem Element ist während des Prozesses ein Fehler aufgetreten. Die Details sind unten aufgeführt:',
+
+ uploadIntro: 'Wählen Sie eine ZIP-Datei mit uSync-Dateien aus, die Sie hochladen möchten',
+ uploadSuccess: 'Die Dateien wurden hochgeladen und in den uSync-Ordner extrahiert',
+ uploadError: 'Beim Hochladen der Dateien ist ein Fehler aufgetreten',
+
+ ILanguage: 'Sprache',
+ IDictionaryItem: 'Wörterbuchelemente',
+ IDataType: 'Datentypen',
+ ITemplate: 'Vorlagen',
+ IContentType: 'Inhaltstypen',
+ IMediaType: 'Medientypen',
+ IMemberType: 'Mitgliedstypen',
+ IContent: 'Inhalt',
+ IMedia: 'Medien',
+ IDomain: 'Domänen',
+ IWebhook: 'Webhooks',
+ IRelationType: 'Beziehungstypen',
+ MediaFile: 'Mediendateien',
+ XElement: 'Sonstiges',
+ LanguageHandler: 'Sprachen',
+ DictionaryHandler: 'Wörterbuchelemente',
+ DataTypeHandler: 'Datentypen',
+ TemplateHandler: 'Vorlagen',
+ ContentTypeHandler: 'Inhaltstypen',
+ MediaTypeHandler: 'Medientypen',
+ MemberTypeHandler: 'Mitgliedstypen',
+ ContentHandler: 'Inhalt',
+ MediaHandler: 'Medien',
+ RelationTypeHandler: 'Beziehungstypen',
+ EntityContainer: 'Container',
+ },
+ USyncSettings: {
+ settings: 'uSync-Einstellungen',
+ filesAndFolders: 'Dateien und Ordner',
+ handlerDefaults: 'Handler-Standardwerte',
+
+ processingMode: 'Verarbeitungsmodus',
+ processingModeDesc:
+ 'Wie der uSync-Prozess ausgeführt wird, entweder im Hintergrund oder interaktiv (Normal)',
+
+ importAtStartup: 'Beim Start importieren',
+ importAtStartupDesc: 'Einen Import von Dateien von der Festplatte beim Start von Umbraco ausführen',
+
+ exportAtStartup: 'Beim Start exportieren',
+ exportAtStartupDesc: 'Die Umbraco-Einstellungen beim Start der Website exportieren',
+
+ exportOnSave: 'Beim Speichern exportieren',
+ exportOnSaveDesc: 'uSync-Dateien generieren, wenn Elemente gespeichert werden',
+
+ uiEnabledGroups: 'UI-aktivierte Gruppen',
+ uiEnabledGroupsDesc: 'Handler-Gruppen, die im Dashboard angezeigt/verwendet werden können',
+
+ failOnMissingParent: 'Fehler bei fehlendem Elternelement',
+ failOnMissingParentDesc: 'Fehler bei fehlendem Elternelement',
+
+ currentHandlerSet: 'Aktuelles Set',
+
+ handlerSet: 'Standard-Handler-Set',
+ handlerSetDesc: 'Das Standard-Handler-Set für die Website',
+
+ flatStructure: 'Flache Struktur',
+ flatStructureDesc: 'Alle Elemente eines Typs werden in einer flachen Ordnerstruktur gespeichert',
+
+ guidNames: 'GUIDs als Dateinamen verwenden',
+ guidNamesDesc: 'Die GUID eines Elements als Dateiname verwenden',
+
+ handlerGroups: 'Handler-Gruppen',
+ handlerGroupsDesc: 'Gruppen zur Einschränkung des Handler-Sets',
+
+ disabledHandlers: 'Deaktivierte Handler',
+ disabledHandlersDesc: 'Handler, die für dieses Handler-Set explizit deaktiviert sind',
+
+ folders: 'Ordner',
+ foldersDesc:
+ 'Ordner, in denen uSync nach Dateien sucht (Elemente werden normalerweise im letzten Ordner der Liste gespeichert)',
+
+ rootSite: 'Root-Website',
+ rootSiteDesc: 'Ist diese Website ein Root für andere Websites.',
+
+ rootLocked: 'Root gesperrt',
+ rootLockedDesc: 'Sind Änderungen an Elementen der Root-Website gesperrt?',
+
+ help: 'Die Einstellungen werden über die Datei appsettings.json gesteuert. Siehe unsere Dokumentation',
+
+ bootSettings: 'Einstellungen für den ersten Start 🥾',
+
+ firstBoot: 'Beim ersten Start importieren',
+ firstBootDesc: 'Den Importprozess beim ersten Start der Website ausführen',
+
+ firstBootGroup: 'Gruppen für den ersten Start',
+ firstBootGroupDesc: 'Die Gruppen, die beim ersten Start ausgeführt werden',
+ },
+};
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/en-us.ts b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/en-us.ts
index a4a9a0ae9..25e83f335 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/en-us.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/en-us.ts
@@ -4,6 +4,18 @@ export default {
name: 'uSync',
banner: 'uSync all the things',
+ migrate: 'Migrate',
+ defaultView: 'Default',
+ settingsView: 'Settings',
+ addons: 'Add-ons',
+
+ groupEverything: 'Everything',
+ groupContent: 'Content',
+ groupSettings: 'Settings',
+ groupForms: 'Forms',
+ groupMedia: 'Media',
+ groupMembers: 'Members',
+
Report: 'Report',
Import: 'Import',
Export: 'Export',
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/es-es.ts b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/es-es.ts
new file mode 100644
index 000000000..23afd411f
--- /dev/null
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/es-es.ts
@@ -0,0 +1,191 @@
+export default {
+ uSync: {
+ section: 'Sincronización',
+ name: 'uSync',
+ banner: 'uSync para todo',
+
+ migrate: 'Migrar',
+ defaultView: 'Predeterminado',
+ settingsView: 'Configuración',
+ addons: 'Complementos',
+
+ groupEverything: 'Todo',
+ groupContent: 'Contenido',
+ groupSettings: 'Configuración',
+ groupForms: 'Formularios',
+ groupMedia: 'Medios',
+ groupMembers: 'Miembros',
+
+ Report: 'Informe',
+ Import: 'Importar',
+ Export: 'Exportar',
+ ImportForce: 'Importar (Forzar)',
+ ExportClean: 'Exportar (Limpio)',
+ ExportFile: 'Exportar a archivo',
+ ImportFile: 'Importar desde archivo',
+
+ noChange: 'Nada ha cambiado',
+ showAll: 'Mostrar todos los elementos',
+
+ detailHeadline: 'Cambios detectados',
+ detailHeader: 'Cosas que son diferentes',
+
+ runningInBackground:
+ 'uSync está ejecutando este proceso en segundo plano. Si navega fuera de esta página, continuará ejecutándose.',
+
+ connectionLost:
+ 'Se ha perdido la conexión con el servidor. El proceso continuará ejecutándose en segundo plano, pero no verá actualizaciones aquí.',
+
+ importSingle: 'Importar',
+ importSingleWarning:
+ 'Esto importará este elemento en Umbraco. Si este elemento tiene dependencias, no serán importadas y deberán resolverse manualmente.',
+ importSingleSuccess: 'El elemento se ha importado correctamente',
+ importSingleFailed: `
Se produjo un error al importar el elemento
%0%`,
+
+ changeAction: 'Acción',
+ changeItem: 'Elemento',
+ changeDiffrence: 'Diferencia',
+ changeCreate: 'Este elemento no existe en Umbraco y está siendo creado',
+ noChangesImport: 'No se realizaron cambios en este elemento',
+ noChangesReport: 'No se detectaron cambios',
+ noChangesDelete: 'Este elemento ha sido eliminado de Umbraco',
+
+ importHeader: 'Importar desde archivo',
+
+ success: 'Éxito',
+ change: 'Cambio',
+ changeType: 'Tipo',
+ changeName: 'Nombre',
+ changeDetail: 'Detalle',
+ changeHeading: 'Resultados',
+ changeCount: '{1}/{0} cambios',
+ noChangeCount: '0/{0} cambios',
+
+ legacyInfo: `
uSync ha encontrado una carpeta uSync heredada en %0%.
+ Es probable que su contenido necesite convertirse de alguna manera
`,
+
+ legacyObsolete: `
Tipos de datos obsoletos
+
%0%
+
Puede convertir estos tipos de datos usando
+ uSync.Migrations
+ (En la versión completa de uSync, la conversión se realizará aquí.)
`,
+
+ legacyCopy: `
Copiar a uSync/v14
+
Puede copiar su carpeta %0% a la carpeta ~/uSync/v14 y ejecutar una importación.
+
Si nada necesita convertirse, todo debería importarse correctamente.
+
Elimine o cambie el nombre de la carpeta %0% para evitar este popup
`,
+
+ hmacMismatch: `
Discrepancia HMAC
+
Parece que la configuración Imaging:HMAC utilizada para generar los archivos en la carpeta uSync no coincide con la configuración actual de este sitio.
+
Las imágenes dentro de los controles RTE tendrán el valor HMAC añadido a la URL, y sin configuración adicional, estas imágenes pueden no mostrarse correctamente.
+
Puede activar el "mapeo HMAC" en uSync,
+
o puede asegurarse de que el valor HMAC en la configuración Imaging:HMAC coincida con el valor utilizado para generar los archivos en la carpeta uSync
`,
+ formatMismatch:
+ 'La versión del formato del archivo de sincronización no coincide con la versión esperada. Esto puede indicar un posible problema de compatibilidad.',
+
+ legacyBanner:
+ 'Este sitio contiene archivos de una versión anterior de uSync. Consulte los detalles en la pestaña Heredado.',
+
+ legacyCopyTitle: 'Sobrescribir archivos v%0%',
+ legacyCopyContent:
+ '¿Está seguro de que desea sobrescribir el contenido de la carpeta %0% con los archivos de la carpeta uSync heredada?',
+
+ legacyIgnoreTitle: 'Ignorar archivos heredados',
+ legacyIgnoreContent:
+ '¿Está seguro de que desea ignorar los archivos en la carpeta uSync heredada?',
+
+ errorHeader:
+ 'Este elemento encontró un error durante el proceso. Los detalles se muestran a continuación:',
+
+ uploadIntro: 'Seleccione un archivo zip que contenga los archivos uSync que desea cargar',
+ uploadSuccess: 'Los archivos han sido cargados y extraídos en la carpeta uSync',
+ uploadError: 'Se produjo un error al cargar los archivos',
+
+ ILanguage: 'Idioma',
+ IDictionaryItem: 'Elementos del diccionario',
+ IDataType: 'Tipos de datos',
+ ITemplate: 'Plantillas',
+ IContentType: 'Tipos de contenido',
+ IMediaType: 'Tipos de medios',
+ IMemberType: 'Tipos de miembros',
+ IContent: 'Contenido',
+ IMedia: 'Medios',
+ IDomain: 'Dominios',
+ IWebhook: 'Webhooks',
+ IRelationType: 'Tipos de relaciones',
+ MediaFile: 'Archivos de medios',
+ XElement: 'Otro',
+ LanguageHandler: 'Idiomas',
+ DictionaryHandler: 'Elementos del diccionario',
+ DataTypeHandler: 'Tipos de datos',
+ TemplateHandler: 'Plantillas',
+ ContentTypeHandler: 'Tipos de contenido',
+ MediaTypeHandler: 'Tipos de medios',
+ MemberTypeHandler: 'Tipos de miembros',
+ ContentHandler: 'Contenido',
+ MediaHandler: 'Medios',
+ RelationTypeHandler: 'Tipos de relaciones',
+ EntityContainer: 'Contenedores',
+ },
+ USyncSettings: {
+ settings: 'Configuración de uSync',
+ filesAndFolders: 'Archivos y carpetas',
+ handlerDefaults: 'Valores predeterminados del controlador',
+
+ processingMode: 'Modo de procesamiento',
+ processingModeDesc:
+ 'Cómo se ejecuta el proceso uSync, ya sea en segundo plano o de forma interactiva (Normal)',
+
+ importAtStartup: 'Importar al inicio',
+ importAtStartupDesc: 'Ejecutar una importación de archivos desde el disco cuando Umbraco se inicia',
+
+ exportAtStartup: 'Exportar al inicio',
+ exportAtStartupDesc: 'Exportar la configuración de Umbraco cuando el sitio se inicia',
+
+ exportOnSave: 'Exportar al guardar',
+ exportOnSaveDesc: 'Generar archivos uSync cuando se guardan elementos',
+
+ uiEnabledGroups: 'Grupos habilitados en la interfaz',
+ uiEnabledGroupsDesc: 'Grupos de controladores que se pueden ver/usar en el panel',
+
+ failOnMissingParent: 'Error si falta el padre',
+ failOnMissingParentDesc: 'Error si falta el elemento padre',
+
+ currentHandlerSet: 'Conjunto actual',
+
+ handlerSet: 'Conjunto de controladores predeterminado',
+ handlerSetDesc: 'El conjunto de controladores predeterminado a usar para el sitio',
+
+ flatStructure: 'Estructura plana',
+ flatStructureDesc: 'Todos los elementos de un tipo se almacenan en una estructura de carpetas plana',
+
+ guidNames: 'Usar GUID como nombres de archivo',
+ guidNamesDesc: 'Usar el GUID de un elemento como nombre de archivo',
+
+ handlerGroups: 'Grupos de controladores',
+ handlerGroupsDesc: 'Grupos para limitar el conjunto de controladores',
+
+ disabledHandlers: 'Controladores deshabilitados',
+ disabledHandlersDesc: 'Controladores explícitamente deshabilitados para este conjunto de controladores',
+
+ folders: 'Carpetas',
+ foldersDesc:
+ 'Carpetas donde uSync buscará archivos (los elementos normalmente se guardan en la última carpeta de la lista)',
+
+ rootSite: 'Sitio raíz',
+ rootSiteDesc: 'Este sitio es una raíz para otros sitios.',
+
+ rootLocked: 'Raíz bloqueada',
+ rootLockedDesc: '¿Están bloqueados los cambios de los elementos que provienen del sitio raíz?',
+
+ help: 'La configuración se controla mediante el archivo appsettings.json. Ver nuestra documentación',
+
+ bootSettings: 'Configuración del primer arranque 🥾',
+
+ firstBoot: 'Importar en el primer arranque',
+ firstBootDesc: 'Ejecutar el proceso de importación en el primer arranque del sitio',
+
+ firstBootGroup: 'Grupos del primer arranque',
+ firstBootGroupDesc: 'Los grupos a ejecutar en el primer arranque',
+ },
+};
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/fr-fr.ts b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/fr-fr.ts
new file mode 100644
index 000000000..0f16f573c
--- /dev/null
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/fr-fr.ts
@@ -0,0 +1,191 @@
+export default {
+ uSync: {
+ section: 'Synchronisation',
+ name: 'uSync',
+ banner: 'uSync pour tout synchroniser',
+
+ migrate: 'Migrer',
+ defaultView: 'Défaut',
+ settingsView: 'Paramètres',
+ addons: 'Extensions',
+
+ groupEverything: 'Tout',
+ groupContent: 'Contenu',
+ groupSettings: 'Paramètres',
+ groupForms: 'Formulaires',
+ groupMedia: 'Médias',
+ groupMembers: 'Membres',
+
+ Report: 'Rapport',
+ Import: 'Importer',
+ Export: 'Exporter',
+ ImportForce: 'Importer (Forcer)',
+ ExportClean: 'Exporter (Propre)',
+ ExportFile: 'Exporter vers fichier',
+ ImportFile: 'Importer depuis fichier',
+
+ noChange: 'Rien n\'a changé',
+ showAll: 'Afficher tous les éléments',
+
+ detailHeadline: 'Modifications détectées',
+ detailHeader: 'Ce qui est différent',
+
+ runningInBackground:
+ 'uSync exécute ce processus en arrière-plan. Si vous quittez cette page, il continuera de fonctionner.',
+
+ connectionLost:
+ 'La connexion au serveur a été perdue. Le processus continuera de s\'exécuter en arrière-plan, mais vous ne verrez pas les mises à jour ici.',
+
+ importSingle: 'Importer',
+ importSingleWarning:
+ 'Cela importera cet élément dans Umbraco. Si cet élément a des dépendances, elles ne seront pas importées et devront être résolues manuellement.',
+ importSingleSuccess: 'L\'élément a été importé avec succès',
+ importSingleFailed: `
Une erreur s'est produite lors de l'importation de l'élément
%0%`,
+
+ changeAction: 'Action',
+ changeItem: 'Élément',
+ changeDiffrence: 'Différence',
+ changeCreate: 'Cet élément n\'existe pas dans Umbraco et est en cours de création',
+ noChangesImport: 'Aucune modification n\'a été apportée à cet élément',
+ noChangesReport: 'Aucune modification détectée',
+ noChangesDelete: 'Cet élément a été supprimé d\'Umbraco',
+
+ importHeader: 'Importer depuis un fichier',
+
+ success: 'Succès',
+ change: 'Modification',
+ changeType: 'Type',
+ changeName: 'Nom',
+ changeDetail: 'Détail',
+ changeHeading: 'Résultats',
+ changeCount: '{1}/{0} modifications',
+ noChangeCount: '0/{0} modifications',
+
+ legacyInfo: `
uSync a trouvé un dossier uSync hérité à %0%.
+ Il est probable que son contenu devra être converti d'une façon ou d'une autre
`,
+
+ legacyObsolete: `
Types de données obsolètes
+
%0%
+
Vous pouvez convertir ces types de données en utilisant
+ uSync.Migrations
+ (Dans la version complète d'uSync, la conversion se fera ici.)
`,
+
+ legacyCopy: `
Copier vers uSync/v14
+
Vous pouvez copier votre dossier %0% vers le dossier ~/uSync/v14 et exécuter une importation.
+
Si rien ne nécessite de conversion, tout devrait s'importer correctement.
+
Supprimez ou renommez le dossier %0% pour éviter cette popup
`,
+
+ hmacMismatch: `
Incompatibilité HMAC
+
Il semble que le paramètre Imaging:HMAC utilisé pour générer les fichiers dans le dossier uSync ne corresponde pas au paramètre actuel de ce site.
+
Les images dans les contrôles RTE auront la valeur HMAC ajoutée à l'URL, et sans configuration supplémentaire, ces images pourraient ne pas s'afficher correctement.
+
Vous pouvez activer le "mappage HMAC" dans uSync,
+
ou vous pouvez vous assurer que la valeur HMAC dans le paramètre Imaging:HMAC correspond à la valeur utilisée pour générer les fichiers dans le dossier uSync
`,
+ formatMismatch:
+ 'La version du format du fichier de synchronisation ne correspond pas à la version attendue. Cela peut indiquer un problème de compatibilité potentiel.',
+
+ legacyBanner:
+ 'Ce site contient des fichiers d\'une version précédente d\'uSync. Consultez les détails dans l\'onglet Héritage.',
+
+ legacyCopyTitle: 'Écraser les fichiers v%0%',
+ legacyCopyContent:
+ 'Êtes-vous sûr de vouloir écraser le contenu du dossier %0% avec les fichiers du dossier uSync hérité ?',
+
+ legacyIgnoreTitle: 'Ignorer les fichiers hérités',
+ legacyIgnoreContent:
+ 'Êtes-vous sûr de vouloir ignorer les fichiers dans le dossier uSync hérité ?',
+
+ errorHeader:
+ 'Cet élément a rencontré une erreur lors du processus. Les détails sont ci-dessous :',
+
+ uploadIntro: 'Sélectionnez un fichier zip contenant les fichiers uSync que vous souhaitez télécharger',
+ uploadSuccess: 'Les fichiers ont été téléchargés et extraits dans le dossier uSync',
+ uploadError: 'Une erreur s\'est produite lors du téléchargement des fichiers',
+
+ ILanguage: 'Langue',
+ IDictionaryItem: 'Éléments du dictionnaire',
+ IDataType: 'Types de données',
+ ITemplate: 'Modèles',
+ IContentType: 'Types de contenu',
+ IMediaType: 'Types de médias',
+ IMemberType: 'Types de membres',
+ IContent: 'Contenu',
+ IMedia: 'Média',
+ IDomain: 'Domaines',
+ IWebhook: 'Webhooks',
+ IRelationType: 'Types de relations',
+ MediaFile: 'Fichiers médias',
+ XElement: 'Autre',
+ LanguageHandler: 'Langues',
+ DictionaryHandler: 'Éléments du dictionnaire',
+ DataTypeHandler: 'Types de données',
+ TemplateHandler: 'Modèles',
+ ContentTypeHandler: 'Types de contenu',
+ MediaTypeHandler: 'Types de médias',
+ MemberTypeHandler: 'Types de membres',
+ ContentHandler: 'Contenu',
+ MediaHandler: 'Média',
+ RelationTypeHandler: 'Types de relations',
+ EntityContainer: 'Conteneurs',
+ },
+ USyncSettings: {
+ settings: 'Paramètres uSync',
+ filesAndFolders: 'Fichiers et dossiers',
+ handlerDefaults: 'Paramètres par défaut des gestionnaires',
+
+ processingMode: 'Mode de traitement',
+ processingModeDesc:
+ 'Comment le processus uSync s\'exécute, soit en arrière-plan, soit de manière interactive (Normal)',
+
+ importAtStartup: 'Importer au démarrage',
+ importAtStartupDesc: 'Exécuter une importation de fichiers depuis le disque au démarrage d\'Umbraco',
+
+ exportAtStartup: 'Exporter au démarrage',
+ exportAtStartupDesc: 'Exporter les paramètres Umbraco au démarrage du site',
+
+ exportOnSave: 'Exporter à l\'enregistrement',
+ exportOnSaveDesc: 'Générer des fichiers uSync lors de l\'enregistrement des éléments',
+
+ uiEnabledGroups: 'Groupes activés dans l\'interface',
+ uiEnabledGroupsDesc: 'Groupes de gestionnaires visibles/utilisables sur le tableau de bord',
+
+ failOnMissingParent: 'Échec si parent manquant',
+ failOnMissingParentDesc: 'Échec si l\'élément parent est manquant',
+
+ currentHandlerSet: 'Ensemble actuel',
+
+ handlerSet: 'Ensemble de gestionnaires par défaut',
+ handlerSetDesc: 'L\'ensemble de gestionnaires par défaut à utiliser pour le site',
+
+ flatStructure: 'Structure plate',
+ flatStructureDesc: 'Tous les éléments d\'un type sont stockés dans une structure de dossiers plate',
+
+ guidNames: 'Utiliser les GUID comme noms de fichiers',
+ guidNamesDesc: 'Utiliser le GUID d\'un élément comme nom de fichier',
+
+ handlerGroups: 'Groupes de gestionnaires',
+ handlerGroupsDesc: 'Groupes pour limiter l\'ensemble de gestionnaires',
+
+ disabledHandlers: 'Gestionnaires désactivés',
+ disabledHandlersDesc: 'Gestionnaires explicitement désactivés pour cet ensemble de gestionnaires',
+
+ folders: 'Dossiers',
+ foldersDesc:
+ 'Dossiers dans lesquels uSync recherchera des fichiers (les éléments sont normalement enregistrés dans le dernier dossier de la liste)',
+
+ rootSite: 'Site racine',
+ rootSiteDesc: 'Ce site est-il une racine pour d\'autres sites.',
+
+ rootLocked: 'Racine verrouillée',
+ rootLockedDesc: 'Les modifications des éléments provenant du site racine sont-elles verrouillées ?',
+
+ help: 'Les paramètres sont contrôlés via le fichier appsettings.json. Voir notre documentation',
+
+ bootSettings: 'Paramètres du premier démarrage 🥾',
+
+ firstBoot: 'Importer au premier démarrage',
+ firstBootDesc: 'Exécuter le processus d\'importation au premier démarrage du site',
+
+ firstBootGroup: 'Groupes du premier démarrage',
+ firstBootGroupDesc: 'Les groupes à exécuter au premier démarrage',
+ },
+};
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/nl-nl.ts b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/nl-nl.ts
new file mode 100644
index 000000000..61133c2fe
--- /dev/null
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/lang/files/nl-nl.ts
@@ -0,0 +1,191 @@
+export default {
+ uSync: {
+ section: 'Synchronisatie',
+ name: 'uSync',
+ banner: 'uSync voor alles',
+
+ migrate: 'Migreren',
+ defaultView: 'Standaard',
+ settingsView: 'Instellingen',
+ addons: 'Add-ons',
+
+ groupEverything: 'Alles',
+ groupContent: 'Inhoud',
+ groupSettings: 'Instellingen',
+ groupForms: 'Formulieren',
+ groupMedia: 'Media',
+ groupMembers: 'Leden',
+
+ Report: 'Rapport',
+ Import: 'Importeren',
+ Export: 'Exporteren',
+ ImportForce: 'Importeren (Forceren)',
+ ExportClean: 'Exporteren (Schoon)',
+ ExportFile: 'Exporteren naar bestand',
+ ImportFile: 'Importeren vanuit bestand',
+
+ noChange: 'Er is niets veranderd',
+ showAll: 'Alle items weergeven',
+
+ detailHeadline: 'Gedetecteerde wijzigingen',
+ detailHeader: 'Wat er anders is',
+
+ runningInBackground:
+ 'uSync voert dit proces op de achtergrond uit. Als u van deze pagina navigeert, blijft het doorlopen.',
+
+ connectionLost:
+ 'De verbinding met de server is verbroken. Het proces blijft op de achtergrond draaien, maar u ziet hier geen updates.',
+
+ importSingle: 'Importeren',
+ importSingleWarning:
+ 'Dit importeert dit item in Umbraco. Als dit item afhankelijkheden heeft, worden deze niet geïmporteerd en moeten handmatig worden opgelost.',
+ importSingleSuccess: 'Het item is succesvol geïmporteerd',
+ importSingleFailed: `
Er is een fout opgetreden bij het importeren van het item
%0%`,
+
+ changeAction: 'Actie',
+ changeItem: 'Item',
+ changeDiffrence: 'Verschil',
+ changeCreate: 'Dit item bestaat niet in Umbraco en wordt aangemaakt',
+ noChangesImport: 'Er zijn geen wijzigingen aangebracht aan dit item',
+ noChangesReport: 'Geen wijzigingen gedetecteerd',
+ noChangesDelete: 'Dit item is verwijderd uit Umbraco',
+
+ importHeader: 'Importeren vanuit bestand',
+
+ success: 'Succes',
+ change: 'Wijziging',
+ changeType: 'Type',
+ changeName: 'Naam',
+ changeDetail: 'Detail',
+ changeHeading: 'Resultaten',
+ changeCount: '{1}/{0} wijzigingen',
+ noChangeCount: '0/{0} wijzigingen',
+
+ legacyInfo: `
uSync heeft een verouderde uSync-map gevonden op %0%.
+ Het is waarschijnlijk dat de inhoud op de een of andere manier geconverteerd moet worden
`,
+
+ legacyObsolete: `
Verouderde gegevenstypen
+
%0%
+
U kunt deze gegevenstypen converteren met
+ uSync.Migrations
+ (In de volledige uSync-release zal de conversie hier plaatsvinden.)
`,
+
+ legacyCopy: `
Kopiëren naar uSync/v14
+
U kunt uw %0%-map kopiëren naar de ~/uSync/v14-map en een import uitvoeren.
+
Als er niets geconverteerd hoeft te worden, zou alles correct moeten importeren.
+
Verwijder of hernoem de %0%-map om deze popup te voorkomen
`,
+
+ hmacMismatch: `
HMAC-mismatch
+
Het lijkt erop dat de Imaging:HMAC-instelling die werd gebruikt om de bestanden in de uSync-map te genereren, niet overeenkomt met de huidige instelling voor deze site.
+
Afbeeldingen in RTE-besturingselementen hebben de HMAC-waarde toegevoegd aan de URL-waarde, en zonder aanvullende configuratie worden deze afbeeldingen mogelijk niet correct weergegeven.
+
U kunt "HMAC-mapping" inschakelen in uSync,
+
of u kunt ervoor zorgen dat de HMAC-waarde in de Imaging:HMAC-instelling overeenkomt met de waarde die werd gebruikt om de bestanden in de uSync-map te genereren
`,
+ formatMismatch:
+ 'De versie van het synchronisatiebestandsformaat komt niet overeen met de verwachte versie. Dit kan wijzen op een mogelijk compatibiliteitsprobleem.',
+
+ legacyBanner:
+ 'Deze site bevat bestanden van een eerdere versie van uSync. Bekijk de details op het tabblad Verouderd.',
+
+ legacyCopyTitle: 'v%0%-bestanden overschrijven',
+ legacyCopyContent:
+ 'Weet u zeker dat u de inhoud van de map %0% wilt overschrijven met de verouderde uSync-mapbestanden?',
+
+ legacyIgnoreTitle: 'Verouderde bestanden negeren',
+ legacyIgnoreContent:
+ 'Weet u zeker dat u de bestanden in de verouderde uSync-map wilt negeren?',
+
+ errorHeader:
+ 'Dit item heeft een fout ondervonden tijdens het proces. De details staan hieronder:',
+
+ uploadIntro: 'Selecteer een zip-bestand met uSync-bestanden die u wilt uploaden',
+ uploadSuccess: 'De bestanden zijn geüpload en uitgepakt naar de uSync-map',
+ uploadError: 'Er is een fout opgetreden bij het uploaden van de bestanden',
+
+ ILanguage: 'Taal',
+ IDictionaryItem: 'Woordenboekitems',
+ IDataType: 'Gegevenstypen',
+ ITemplate: 'Sjablonen',
+ IContentType: 'Inhoudstypen',
+ IMediaType: 'Mediatypen',
+ IMemberType: 'Ledentypen',
+ IContent: 'Inhoud',
+ IMedia: 'Media',
+ IDomain: 'Domeinen',
+ IWebhook: 'Webhooks',
+ IRelationType: 'Relatietypen',
+ MediaFile: 'Mediabestanden',
+ XElement: 'Overig',
+ LanguageHandler: 'Talen',
+ DictionaryHandler: 'Woordenboekitems',
+ DataTypeHandler: 'Gegevenstypen',
+ TemplateHandler: 'Sjablonen',
+ ContentTypeHandler: 'Inhoudstypen',
+ MediaTypeHandler: 'Mediatypen',
+ MemberTypeHandler: 'Ledentypen',
+ ContentHandler: 'Inhoud',
+ MediaHandler: 'Media',
+ RelationTypeHandler: 'Relatietypen',
+ EntityContainer: 'Containers',
+ },
+ USyncSettings: {
+ settings: 'uSync-instellingen',
+ filesAndFolders: 'Bestanden en mappen',
+ handlerDefaults: 'Handler-standaardwaarden',
+
+ processingMode: 'Verwerkingsmodus',
+ processingModeDesc:
+ 'Hoe het uSync-proces wordt uitgevoerd, op de achtergrond of interactief (Normaal)',
+
+ importAtStartup: 'Importeren bij opstarten',
+ importAtStartupDesc: 'Een import van bestanden van de schijf uitvoeren wanneer Umbraco start',
+
+ exportAtStartup: 'Exporteren bij opstarten',
+ exportAtStartupDesc: 'De Umbraco-instellingen exporteren wanneer de site opstart',
+
+ exportOnSave: 'Exporteren bij opslaan',
+ exportOnSaveDesc: 'uSync-bestanden genereren wanneer items worden opgeslagen',
+
+ uiEnabledGroups: 'UI-ingeschakelde groepen',
+ uiEnabledGroupsDesc: 'Handlergroepen die zichtbaar/bruikbaar zijn op het dashboard',
+
+ failOnMissingParent: 'Fout bij ontbrekend bovenliggend item',
+ failOnMissingParentDesc: 'Fout bij ontbrekend bovenliggend item',
+
+ currentHandlerSet: 'Huidige set',
+
+ handlerSet: 'Standaard handlerset',
+ handlerSetDesc: 'De standaard handlerset voor de site',
+
+ flatStructure: 'Platte structuur',
+ flatStructureDesc: 'Alle items van een type worden opgeslagen in een platte mappenstructuur',
+
+ guidNames: 'GUID\'s als bestandsnamen gebruiken',
+ guidNamesDesc: 'De GUID van een item als bestandsnaam gebruiken',
+
+ handlerGroups: 'Handlergroepen',
+ handlerGroupsDesc: 'Groepen om de handlerset te beperken',
+
+ disabledHandlers: 'Uitgeschakelde handlers',
+ disabledHandlersDesc: 'Handlers die expliciet zijn uitgeschakeld voor deze handlerset',
+
+ folders: 'Mappen',
+ foldersDesc:
+ 'Mappen waarin uSync naar bestanden zoekt (items worden normaal opgeslagen in de laatste map in de lijst)',
+
+ rootSite: 'Rootsite',
+ rootSiteDesc: 'Is deze site een root voor andere sites.',
+
+ rootLocked: 'Root vergrendeld',
+ rootLockedDesc: 'Zijn wijzigingen voor items van de rootsite vergrendeld?',
+
+ help: 'Instellingen worden beheerd via het bestand appsettings.json. Zie onze documentatie',
+
+ bootSettings: 'Instellingen voor eerste opstart 🥾',
+
+ firstBoot: 'Importeren bij eerste opstart',
+ firstBootDesc: 'Het importproces uitvoeren bij de eerste opstart van de site',
+
+ firstBootGroup: 'Groepen voor eerste opstart',
+ firstBootGroupDesc: 'De groepen die worden uitgevoerd bij de eerste opstart',
+ },
+};
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/lang/manifest.ts b/uSync.Backoffice.Management.Client/usync-assets/src/lang/manifest.ts
index 756cc2bad..f63e24440 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/lang/manifest.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/lang/manifest.ts
@@ -9,6 +9,56 @@ const localizations: Array = [
},
js: () => import('./files/en-us'),
},
+ {
+ type: 'localization',
+ alias: 'usync.lang.dadk',
+ name: 'Danish',
+ weight: 0,
+ meta: {
+ culture: 'da',
+ },
+ js: () => import('./files/da-dk'),
+ },
+ {
+ type: 'localization',
+ alias: 'usync.lang.frfr',
+ name: 'French',
+ weight: 0,
+ meta: {
+ culture: 'fr',
+ },
+ js: () => import('./files/fr-fr'),
+ },
+ {
+ type: 'localization',
+ alias: 'usync.lang.eses',
+ name: 'Spanish',
+ weight: 0,
+ meta: {
+ culture: 'es',
+ },
+ js: () => import('./files/es-es'),
+ },
+ {
+ type: 'localization',
+ alias: 'usync.lang.dede',
+ name: 'German',
+ weight: 0,
+ meta: {
+ culture: 'de',
+ },
+ js: () => import('./files/de-de'),
+ },
+ {
+ type: 'localization',
+ alias: 'usync.lang.nlnl',
+ name: 'Dutch',
+ weight: 0,
+ meta: {
+ culture: 'nl',
+ },
+ js: () => import('./files/nl-nl'),
+ },
];
export const manifests: UmbExtensionManifest[] = [...localizations];
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/tree/manifest.ts b/uSync.Backoffice.Management.Client/usync-assets/src/tree/manifest.ts
index 750619e46..adf1916f3 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/tree/manifest.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/tree/manifest.ts
@@ -64,7 +64,7 @@ const menuSidebarApp: UmbExtensionManifest = {
name: 'uSync section sidebar menu',
weight: 150,
meta: {
- label: 'Synchronisation',
+ label: '#uSync_section',
menu: menu.alias,
},
conditions: [
diff --git a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/components/usync-action-button.ts b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/components/usync-action-button.ts
index ec76cd68e..9292483e8 100644
--- a/uSync.Backoffice.Management.Client/usync-assets/src/workspace/components/usync-action-button.ts
+++ b/uSync.Backoffice.Management.Client/usync-assets/src/workspace/components/usync-action-button.ts
@@ -46,7 +46,7 @@ export class SyncActionButtonElement extends UmbLitElement {
return html` this.button?.color}
look=${this.button?.look}
state=${ifDefined(this.state)}
@@ -63,7 +63,7 @@ export class SyncActionButtonElement extends UmbLitElement {
const buttons = this.button?.children.map((item: SyncActionButton) => {
return html` this.#onClick(item)}>`;
});
@@ -79,7 +79,7 @@ export class SyncActionButtonElement extends UmbLitElement {
color=${parent?.color}
look=${parent?.look}
@click=${() => this.#onClick(this.button)}>
- ${this.localize.term(`uSync_${this.button?.label}`)}
+ ${this.localize.termOrDefault(`uSync_${this.button?.label}`, this.button?.label ?? '')}
{
render() {
return html`
-
+
${this.renderForm()} ${this.renderResult()}
`;
@@ -38,7 +38,7 @@ export class uSyncImportModalDialog extends UmbModalBaseElement {
renderForm() {
if (this.result !== undefined) return;
- return html` ${this.localize.term('uSync_uploadIntro')}
+ return html` ${this.localize.termOrDefault('uSync_uploadIntro', 'Select a zip file containing uSync files that you want to upload')}
{
return html`${when(
this.result.success,
- () => html`${this.localize.term('uSync_uploadSuccess')}`,
- () => html`${this.localize.term('uSync_uploadError')} ${this.result?.errors}`,
+ () => html`${this.localize.termOrDefault('uSync_uploadSuccess', 'The files have been uploaded and extracted to the uSync folder')}`,
+ () => html`${this.localize.termOrDefault('uSync_uploadError', 'There was an error uploading the files')} ${this.result?.errors}`,
)}