ホームページ
2026年2月25日

(第3回)Auto Dictionaries:Umbraco 13 から 17 への冒険 — バックエンド対応とスキーマ生成

Umbraco
パッケージ
C#
NuGet
TypeScript

今、Umbraco 13 はセキュリティアップデートだけを受けられるので、アップデートする時間だ!

次の「LTS」バージョンは Umbraco 17。僕は「LTS」バージョンだけをアップデートしたいので、僕の Umbraco パッケージ Auto Dictionaries を Umbraco 17 にアップデートする。

第1回はここでもっと読める第2回はここでもっと読める、が、この記事だけでも内容は理解できる。

先に、プロジェクトの構造を変えて、フロントエンドのコードを「AngularJS」から「TypeScript」に変えた。

今回で全部終わりだから、やっと新しいAuto Dictionariesのバージョンを公開できる!楽しみにしているよ!

では、行きましょう!


予定

  1. 「Visual Studio 2026」にアップデートする
  2. Webのプロジェクトは Umbraco 17にアップデートする
  3. プロジェクトの構造を変える
  4. フロントエンドのコードは「AngularJS」から「TypeScript」に変える
  5. バックエンドのコードを直す
  6. スキーマジェネレーターを追加する
  7. 日本語の翻訳を追加する


5. バックエンドのコードを直す

やっとバックエンドのコードを直す時間!

よくバックエンドのコードを使って働いているから、楽しみにしていた!

まずはcsprojのファイルでnet8.0からnet10.0を変えて、Umbraco.Cms.Web.Websiteのパッケージを17にアップデートした。

僕はUmbraco.Cms.Web.WebsiteUmbraco.Cms.Web.BackOfficeのパッケージを使っているけど、Umbraco 17にはUmbraco.Cms.Web.BackOfficeのパッケージがない。

そのパッケージはコントローラーのnamespaceがあったために、新しいパッケージをインストールしたほうがいい。そのパッケージの名前はUmbraco.Cms.Api.Managementだ。

Umbraco 17はいろんな変更がある。

例えば、今はツリーを作るために、フロントエンドのコードを使われている。

だから、TreeControllerIManifestFilterを消せる。

それに、UmbracoAuthorizedApiControllerがないから、普通のコントローラーを使わなければいけないので、このAttributeは使ったたほうがいい。

[Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]

LocalizationService

ディクショナリのアイテムを作るために、よくILocalizationServiceを使った。

でも今は、そのサービスがない。

ILocalizationServiceILanguageServiceIDictionaryItemServiceを分けた。

だから、ディクショナリのアイテムを作るための新しい方法がある。

これはディクショナリのアイテムを作るために前に使われていた方法。

var dictionaryItem = _localizationService.CreateDictionaryItemWithIdentity(dictionaryName, GetDictionaryItem(parentId ?? -1)?.Guid);
foreach(var translation in translations) 
{
    _localizationService.AddOrUpdateDictionaryValue(dictionaryItem, translation.Language, translation.TranslatedText);
}

_localizationService.Save(dictionaryItem);

でも今、ディクショナリのアイテムと翻訳を作る方法はちょっと違う。

ディクショナリのアイテムを作るために、この方法を使っている。

var dicTranslations = new List<DictionaryTranslation>();
foreach(var translation in translations)
{
    if (translation.Language == null || string.IsNullOrWhiteSpace(translation.TranslatedText)) 
    {
        continue;
    }

    dicTranslations.Add(new DictionaryTranslation(translation.Language, translation.TranslatedText));
}

if (!dicTranslations.Any()) 
{
    return null;
}

DictionaryItem dictionaryItem = new DictionaryItem(dictionaryName)
{
    ParentId = parent?.Guid ?? null,
    Translations = dicTranslations
};

var attempt = await _dictionaryItemService.CreateAsync(dictionaryItem, userKey);

非同期メソッド

Umbraco 17にはよく非同期のサービスが使われていることに気がついた。

だから、僕はすべてのメソッドを変えなければいけないから、非同期を使っている。

Auto Dictionariesには、すべてのビューをループするから、すべてのディクショナリのアイテムを取得しなければいけない。

それに、その翻訳済みのチェックをするために、すべての言語も取得しなければいけない。

前に、その値はコンストラクタで取得した。(_languageCount_dictionaryItems)

private readonly int _languageCount;
private readonly List<DictionaryModel> _dictionaryItems;

public AutoDictionariesService(ILocalizationService localizationService)
{
    _dictionaryItems = GetAllDictionaryItems();
    languageCount = localizationService.GetAllLanguages().Count();
}

でも今、そのメソッドは非同期だから、ほかの方法を使わなければいけない。

だから、Lazyパターンを使う!Lazyパターンをぜんぜん使わないから、とても面白くなると思う!

まず、変数は型付きLazy<T>を追加する。

private readonly Lazy<Task<int>> _languageCount;
private Lazy<Task<List<DictionaryModel>>> _allDictionaryItems;

public AutoDictionariesService(ILanguageService languageService, IDictionaryItemService dictionaryItemService)
{
    _allDictionaryItems = new Lazy<Task<List<DictionaryModel>>>(GetAllDictionaryItems);
    _languageCount = new Lazy<Task<int>>(async () => {
        var languages = await _languageService.GetAllAsync();
        return languages.Count();
    });
}

次に、GETメソッドを追加する。

private Task<int> GetLanguageCountAsync()
{
     return _languageCount.Value;
}

public Task<List<DictionaryModel>> LazyAllDictionaryItems()
{
      return _allDictionaryItems.Value;
}

public void RefreshGetAllDictionaryItems()
{ 
      _allDictionaryItems = new Lazy<Task<List<DictionaryModel>>>(GetAllDictionaryItems);
}

private async Task<List<DictionaryModel>> GetAllDictionaryItems()
{
      return await _dictionaryItemService.GetAtRootAsync();
}

始めにGetAllDictionaryItems()メソッドを1回しか使わない。それからLazyAllDictionaryItems()メソッドを使う。

GetDictionaryItemFromStaticContent(await LazyAllDictionaryItems(), staticContent.Key)

ディクショナリのアイテムがアップデートされたら、 その値はもう一度すべてを取得しなければいけない。

そのために、INotificationHandler<DictionaryItemSavedNotification>を作ったから、RefreshGetAllDictionaryItems()メソッドを取得できる。

Regex

今、使っている「Regex」は静的コンテンツを探した時、よくまちがった静的コンテンツを見つけてしまったので、「Regex」を変えたい。

その変更について詳しい説明を書きたいので、後でその記事を書こうと思っている。

ステップ5は終わった!


6. スキーマ・ジェネレーターを追加する

今はappsettings.jsonでいろんな設定があるので、その設定をappsettings.jsonに追加するもっと簡単にしたい。

だから、appsettings-schema.auto-dictionaries.jsonのジェネレーターを作る!

ケビン・ジャンップUmbracoの解決策をよく見た。

同じ解決策を使う。(その解決策はここでよく見た)

僕にとって、一番難しいと思うことについて話す。

その解決策を作った。

でも今、毎回AutoDictionaries.SchemaGeneratorのプロジェクトをビルドすると、appsettings-schema.auto-dictionaries.jsonのファイルが作られるけど、それはほしくない。

だから、.csprojにこれで追加する。

<Target Name="GenerateSchema"
    BeforeTargets="BeforeBuild"
    Condition="'$(Configuration)' == 'Release'">
    <Message Text="Generating AutoDictionaries schema file..."
        Importance="high" />
    <Exec Command='dotnet run --project "$(MSBuildThisFileDirectory)\..\AutoDictionaries.SchemaGenerator\AutoDictionaries.SchemaGenerator.csproj" -- -o "$(MSBuildThisFileDirectory)appsettings-schema.auto-dictionaries.json"' />
</Target>

今、毎回リリースにビルドすると、新しいファイルが作られるようになってほしい。

次に、そのファイルを「NuGet」に追加したい。

.targetsのファイルを作って、これを追加する

<Project>
    <ItemGroup>
        <UmbracoJsonSchemaFiles Include="$(MSBuildThisFileDirectory)..\content\appsettings-schema.auto-dictionaries.json"
            Weight="-80" />
    </ItemGroup>
</Project>

そして、.csprojに変える。

重要!

これは、ちょっと問題になった!

この方法をためしてみたけど、たくさんの問題があった。

例えば、いろんなファイルは「NuGet」にパッケージされなかった。

<PropertyGroup>
     <EnableDefaultContentItems>false</EnableDefaultContentItems>
</PropertyGroup>
<ItemGroup>
        <Content Include="buildTransitive\**" PackagePath="buildTransitive" />
        <Content Include="appsettings-schema.auto-dictionaries.json" PackagePath="" />
</ItemGroup>

いろんな方法試すためしてみたけど、この方法をやっと上手くいった!

<ItemGroup>
    <None Include="buildTransitive\**\*.*" Pack="True" PackagePath="buildTransitive" />
    <None Include="$(MSBuildThisFileDirectory)appsettings-schema.auto-dictionaries.json" Pack="True" PackagePath="content" />
</ItemGroup>

やっと!今は「NuGet」のパッケージにappsettings-schema.auto-dictionaries.jsonがある。

ステップ6は終わった!


7. 日本語の翻訳を追加する

もっとプログラミングの日本語の言葉を習いたいから、最後のステップとして、日本語の翻訳を追加する!

ここで全部の訳をよく見た

読んで、かいぜんてんを見つけたら、プルリクエストをしてください!

翻訳のファイル

これはちょっと複雑だけど、頑張って説明をする!

まずは翻訳のファイルを作らなければいけない。

ENとEN-GB、JAとJA-JPがあるけど、どれを使ったほうがいいだろう?

でもなんで2つある?

例えば、EN-GBでは「Colour」でENでは「Color」。

EN-GBの言語を選ぶと、EN-GBの翻訳が使われるけど、そのファイルがなかったら、ENのファイルが使れる。

だから、ENとJAを使っている。

どうやって翻訳をする

Umbraco 17では、2つLocalizeをする方法がある

コードでは、これを使える。

this.localize.term("autoDictionaries_generate_and_translate")

そして、HTMLではこれを使える。

<umb-localize key="autoDictionaries_generate_and_translate"></umb-localize>

全てのプロジェクトは静的コンテンツを探して、変える。

重要!

翻訳を追加したら、1階層だけができる。

このブログ記事を書く前に、このUmbracoのドキュメントがなかった。でも、僕は追加した。(ドキュメント)

これはダメだ。

export default {
    autoDictionaries: {
        content: {
            overview: "",
        }
    }
};

これをしなければいけない。

export default {
    autoDictionaries: {
        content_overview: ""
    }
};

全部終わった!


感想

やっと終わった!この冒険は長くて難しかったけど、とても楽しくて、多くのことを習ったと思う!

あまりTypeScriptを使わないので、この新しいBackofficeはちょっとふくざつ。

それに、あまりBackofficeのドキュメントがないから、この冒険はもっと難しくなったと思う。

具体的にこういうことを学んだ、

  • Webのプロジェクトのデータベースを扱い方
  • TypeScriptの活用
  • Umbraco 17で動的アイテム付きツリーを作る方法
  • Lazyパターンの活用
  • 翻訳のファイルの操作方法

次のブログ記事は「Umbraco 17で動的アイテム付きツリーを作る方法」!


リンク


パーツたち

  1. (第1回)Auto Dictionaries:Umbraco 13 から 17 への冒険
  2. (第2回)プロジェクト構造と TypeScript への移行準備
  3. (第3回)Auto Dictionaries:バックエンド対応とスキーマ生成
ヨハネス・ランツ

ヨハネス・ランツ

ウェブ開発者・経験8年

採用可能

お問い合わせ