Documentation

Custom property editors

Support site-specific editors with safe configuration mappings or a persisted-value adapter in code.

This documentation applies to Omni for Umbraco 17 and 18. Both version lines have the same functionality and configuration. Choose the package version for your Umbraco site.

Choose the smallest safe extension

Omni extends support at the persisted-value boundary. Choose an approach based on what the editor actually stores—not how it renders in the backoffice.

Map simple values in configuration

Use PlainText only when the editor stores text directly. Use RichText only when it stores HTML compatible with Umbraco's rich-text parser.

{
  "DiploTranslatorOmni": {
    "ContentTranslation": {
      "PropertyEditorMappings": [
        { "EditorAlias": "Acme.Headline", "Adapter": "PlainText" },
        { "EditorAlias": "Acme.FormattedIntroduction", "Adapter": "RichText" }
      ]
    }
  }
}

Only PlainText and RichText are accepted. Unknown mappings, complex JSON and unsafe values remain unsupported.

Implement a code adapter for complex values

For structured persisted values, implement IContentTranslationPropertyEditorAdapter. An adapter identifies the editors it handles, extracts stable text-unit IDs, patches only those units into a cloned value and validates that protected structure remains unchanged.

using Diplo.Translator.Omni.Services;

public sealed class CalloutTranslationAdapter
    : IContentTranslationPropertyEditorAdapter
{
    public bool CanHandle(ContentTranslationEditorAdapterContext context)
        => context.EditorAlias == "Acme.Callout";

    public ContentTranslationEditorExtraction Extract(
        ContentTranslationEditorAdapterContext context,
        string sourceValue) { /* return stable text units */ }

    public ContentTranslationEditorPatch Patch(
        ContentTranslationEditorAdapterContext context,
        string sourceValue,
        IReadOnlyDictionary<string, string> translations,
        IReadOnlyCollection<ContentTranslationEditorUnit> units)
        { /* patch a clone of the persisted value */ }

    public ContentTranslationEditorValidation Validate(
        ContentTranslationEditorAdapterContext context,
        string originalValue,
        string patchedValue)
        { /* verify protected structure is unchanged */ }
}

The outline shows the contract; production implementations must parse defensively and return extraction, patch and validation failures rather than accepting uncertain output.

Register the adapter

Register the implementation from an Umbraco composer:

using Diplo.Translator.Omni;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;

public sealed class TranslationComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
        => builder.AddContentTranslationEditorAdapter<CalloutTranslationAdapter>();
}

Safety behaviour

  • Configuration mappings do not override built-in handling. Code adapters may disambiguate a specific editor UI using Umbraco.Plain.String; match both EditorAlias and PropertyEditorUiAlias so unrelated string editors are unaffected. Omni's recognised Ph Heading handling takes precedence.
  • Adapter exceptions and validation failures fail closed.
  • Duplicate or unknown unit IDs and null patch results are rejected.
  • Use stable unit IDs so Omni can track source changes reliably.
  • Adapters registered through the helper are singletons. Keep them thread-safe and do not retain per-request state or scoped services.

The interface and value types are in Diplo.Translator.Omni.Services. The registration helper is in Diplo.Translator.Omni. The outline is illustrative and requires real method bodies before it will compile.

Diplo Translator Omni

Ready to translate your first draft?

Bring AI translation into the Umbraco backoffice — and keep the final say with your editors.

Try it on your own Umbraco site: five single-page draft translation runs, with no time limit. AI provider usage costs are separate.