<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paul McGann</title>
    <description>The latest articles on DEV Community by Paul McGann (@paulmcgann).</description>
    <link>https://dev.to/paulmcgann</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F414619%2Fad6e7e4d-3f0e-4e08-bfd5-058fc5df0fac.png</url>
      <title>DEV Community: Paul McGann</title>
      <link>https://dev.to/paulmcgann</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulmcgann"/>
    <language>en</language>
    <item>
      <title>Configuring Optimizely Azure Blob Storage</title>
      <dc:creator>Paul McGann</dc:creator>
      <pubDate>Tue, 25 Apr 2023 00:07:46 +0000</pubDate>
      <link>https://dev.to/paulmcgann/configuring-optimizely-azure-blob-storage-1lfa</link>
      <guid>https://dev.to/paulmcgann/configuring-optimizely-azure-blob-storage-1lfa</guid>
      <description>&lt;p&gt;Working in web development you are more than likely will come across a conversation at some point around &lt;a href="https://dev.to/paulmcgann/optimizely-dxp-image-optimization-2ip"&gt;image optimization&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In the world of &lt;a href="https://world.optimizely.com/"&gt;Optimizely&lt;/a&gt;, there are a number of tools / libraries to help with image optimization some of these include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cloudflare - Used when working within the &lt;a href="https://world.optimizely.com/products/#dxp"&gt;Optimizely DXP&lt;/a&gt;, has great built in features for automatically optimizing images.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.tourl"&gt;Responsive images&lt;/a&gt; using imageresizer, for the older .net framework versions of Optimizely. Requires a bit more effort however the output can make a real difference to the sites performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Responsive images CMS 12 using &lt;a href="https://hacksbyme.net/2021/10/18/picturerenderer-for-optimizely-cms-12/"&gt;picturerenderer&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these solutions help with image optimization, however none of these will work if we have configured our blob storage setup incorrectly.&lt;/p&gt;

&lt;p&gt;In the post I will focus on the last point &lt;a href="https://world.optimizely.com/products/#contentmanagement"&gt;Optimizely CMS 12&lt;/a&gt; and configuring azure blobstorage provider.&lt;/p&gt;

&lt;p&gt;The first thing you need to do it add a connectionstring to your appsettings.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"EPiServerAzureBlobs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DefaultEndpointsProtocol=https;AccountName=your_account_name;AccountKey=your_account_key;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next in the your Startup.cs or where your configure services, you can now configure the azure blobstorage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAzureBlobProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"EPiServerAzureBlobs"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your_mediafolder"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the basic steps needed to configure the azure blobstorage. However we also should configure some image optimization.&lt;/p&gt;

&lt;p&gt;There are a number of discussions around the web on how best to perform this and it is still in discussion as we read. One approach that is being used in the Optimizely world, is to use the follow package &lt;a href="https://github.com/vnbaaij/Baaijte.Optimizely.ImageSharp.Web"&gt;Baaijte.Optimizely.ImageSharp.Web&lt;/a&gt;, this builds upon the SixLabors.ImageSharp library already present in the Optimizely system.&lt;/p&gt;

&lt;p&gt;To configure the Baaijte.Optimizely.ImageSharp.Web library perform the following:&lt;/p&gt;

&lt;p&gt;Add the following to the configure service in your Startup.cs, as early as possible in the configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddImageSharp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AzureBlobStorageCacheOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"EPiServerAzureBlobs"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your_mediafolder"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ClearProviders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BlobImageProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetCache&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AzureBlobStorageCache&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note as part of this you will also need to install the 'SixLabors.ImageSharp.Web.Providers.Azure' package also.&lt;/p&gt;

&lt;p&gt;Next you will need to configure the app by adding the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseBaaijteOptimizelyImageSharp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With all the configuration in place now you should now be able to render images from azure blobstorage and also be able to resize them appropriately.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Please feel free to comment and share your thoughts.&lt;/p&gt;

</description>
      <category>optimizely</category>
      <category>blobstorage</category>
      <category>dotnetcore</category>
      <category>episerver</category>
    </item>
    <item>
      <title>Optimizely Docker Development Images</title>
      <dc:creator>Paul McGann</dc:creator>
      <pubDate>Mon, 24 Apr 2023 13:37:59 +0000</pubDate>
      <link>https://dev.to/paulmcgann/optimizely-docker-development-images-k66</link>
      <guid>https://dev.to/paulmcgann/optimizely-docker-development-images-k66</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Since I have being developing I have always used a local sql server instance to create and restore databases for development. Having a local instance is great as you have full control over the sql server.&lt;/p&gt;

&lt;p&gt;This leads to some issues when it comes to development however these being:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We have a full sql server on our development machines, if your not familiar with sql server and configuration you might give it too much power and it will use up all the resources on your machine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restoring large databases can take some time. On a project I work on regularly I tend to need to setup and restore a database if switching branches, etc. This process each time I restore takes anywhere from 5-10 mins, do this 4-6 times a day and you are losing 20-60 mins a day.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue I was having is that the scripts I was running required me to have specific database names and every time this ran it would delete and re-create the database. &lt;/p&gt;

&lt;p&gt;Solution&lt;/p&gt;

&lt;p&gt;What I would like is the ability to easily setup a new instance depending upon the branch I am working upon and easily discard when done.&lt;/p&gt;

&lt;p&gt;This lead me to investigate using docker to create an image of the server and then depending on how I wished to use it, I could spin up a new server almost instantly.&lt;/p&gt;

&lt;p&gt;The steps I needed to perform in order to achieve this are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a base container of the sql server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect to the server and restore the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create an image from this container.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating a base container
&lt;/h2&gt;

&lt;p&gt;There are lost out tutorials out there that will help with getting the base container setup. To create a pretty standard sql server instance you can run the following (Please note to change the password):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Password123" --name "mssql-server" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This docker command will create a base container using the sql server image from docker hub and configure the sa password. It will also set the sql server port to 1433.&lt;/p&gt;

&lt;p&gt;The command can take some time to run the first time as it needs to download the image before running the container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the sever and restoring the database
&lt;/h2&gt;

&lt;p&gt;Once it completes you should have a sql server container up and running in docker. You can try connecting to the server on 'localhost' with the sa password details.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: If you already have a sql server instance running on your development machine you will first need to stop the instance to use 'localhost'. Open 'services' and stop the 'sql server'.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Oxf6fC6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/csvvveowzj7j5u345n7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Oxf6fC6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/csvvveowzj7j5u345n7t.png" alt="Windows services" width="653" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once connected you can restore a bacpac to the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create and image from the container.
&lt;/h2&gt;

&lt;p&gt;Having followed the above you should now have a sql server instance running in a docker container with your databases configured.&lt;/p&gt;

&lt;p&gt;As you can see if we where to even do this each time we wanted a new instance it would take some time.&lt;/p&gt;

&lt;p&gt;To solve this we can capture an image of the container and tag it so that we can reuse the image as many times as we wish.&lt;/p&gt;

&lt;p&gt;To create an image of the container run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker commit new-container new-image:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run a new instance we can now run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --name "new-server" -p 1433:1433 -d new-image:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully we now have 2 instances running of the same sql instance.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You will need to stop 1 of the containers to connect using localhost. You can introduce a network and assign a specific ip address to the container if you wish to run multiple container at once. As I am working on a branch at a time usually this was not a requirement.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This has helped me with improving my development process. However I also see this being useful for creating demo environments.&lt;/p&gt;

&lt;p&gt;Optimizely comes with the built in command you can run which is 'Initialize-EPiDatabase' you can use this to setup a fresh Optimizely database and then run 'Update-EPiDatabase' to ensure it is up-to-date.&lt;/p&gt;

&lt;p&gt;Next steps could also be setup up a private container registry using azure container registry and then sharing you images with your team so they too can benefit from your images, or maybe even they could implement a 1 click deploy to setup a demo / training site to use with customers.&lt;/p&gt;

&lt;p&gt;Hopefully this has shown you how easy it is to get started using docker containers in development.&lt;/p&gt;

&lt;p&gt;If you have any recommendations for improvement please drop a comment below, I am just dipping my toes in the water so any info people can provide it much appreciated.&lt;/p&gt;

</description>
      <category>optimizely</category>
      <category>docker</category>
      <category>devops</category>
      <category>episerver</category>
    </item>
    <item>
      <title>Optimizely HttpErrors</title>
      <dc:creator>Paul McGann</dc:creator>
      <pubDate>Wed, 24 Aug 2022 16:48:00 +0000</pubDate>
      <link>https://dev.to/paulmcgann/optimizely-httperrors-2pdb</link>
      <guid>https://dev.to/paulmcgann/optimizely-httperrors-2pdb</guid>
      <description>&lt;p&gt;I was recently investigating an issue reported by our team during development that they were unable to create certain blocks in our environments.&lt;/p&gt;

&lt;p&gt;This reminded me of a Optimizely support article &lt;a href="https://support.optimizely.com/hc/en-us/articles/360002910312-409-conflict-Creating-page-block?_gl=1*1mb1k8b*_ga*MTk2NjI0ODYyNi4xNjU5MDIyOTkw*_ga_C7SLJ6HMJ5*MTY2MTM1ODk4Ni4xMy4wLjE2NjEzNTg5ODYuNjAuMC4w"&gt;'409 conflict Creating page/block'&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The suggestions listed on the site suggested to completely stop using the existingResponse="Replace" attribute upon HttpErrors section and use an alternative method.&lt;/p&gt;

&lt;p&gt;Rather then implement this I decide to handle the HttpErrors differently only upon the scenario where we needed to.&lt;/p&gt;

&lt;p&gt;To do this I decided to use the web.config location element to specify for a location e.g. 'episerver/cms' how I would like to handle the HttpErrors using snippet below.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;location path="episerver/cms"&amp;gt;&lt;br&gt;
        &amp;lt;system.webServer&amp;gt;&lt;br&gt;
            &amp;lt;httpErrors errorMode="Custom" existingResponse="PassThrough" /&amp;gt;&lt;br&gt;
        &amp;lt;/system.webServer&amp;gt;&lt;br&gt;
    &amp;lt;/location&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now on the front-end of the site we can use HttpErrors as we wish, and within the admin we can handle this differently and receive back the original HTTP response.&lt;/p&gt;

</description>
      <category>episerver</category>
      <category>httperrors</category>
      <category>webconfig</category>
      <category>optimizely</category>
    </item>
    <item>
      <title>Optimizely DXP Image Optimization</title>
      <dc:creator>Paul McGann</dc:creator>
      <pubDate>Tue, 16 Nov 2021 21:04:51 +0000</pubDate>
      <link>https://dev.to/paulmcgann/optimizely-dxp-image-optimization-2ip</link>
      <guid>https://dev.to/paulmcgann/optimizely-dxp-image-optimization-2ip</guid>
      <description>&lt;p&gt;Optimizely DXP is a cloud-first approach to customer engagement including high availability and performance, easy connectivity with other cloud services and existing systems, ability to manage spikes in customer demand, and a platform that is ready to seamlessly adopt the latest technology updates. &lt;/p&gt;

&lt;p&gt;Optimizely DXP also includes a Content Delivery Network provided by Cloudflare for performance and security optimization.&lt;/p&gt;

&lt;p&gt;I want to focus upon the optimization side of Cloudflare, more specifically image optimization.&lt;/p&gt;

&lt;p&gt;Cloudflare has 3 products to help with image optimization, Cloudflare Image, Cloudflare Image Resizing, Cloudflare Polish. For the purpose of this post I will focus on Cloudflare Polish as this is enabled by default in Optimizely DXP, you can read more about the other products &lt;a href="https://developers.cloudflare.com/images/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cloudflare polish automatically optimizes images upon your site whenever is is fetched from you site.&lt;/p&gt;

&lt;p&gt;Whenever you make the first request to retrieve and image you will notice within the headers the 'cf-cache-status' will be set to 'MISS'. Cloudflare is telling us it tried to retrieve the image from cache, however it did not find it and so requested it from the origin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bq4k8Frd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6maq87yai5g399j4tqar.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bq4k8Frd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6maq87yai5g399j4tqar.PNG" alt="Cloudflare CF-Cache-Status MISS" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you were to wait a few seconds and refresh the image you will notice that the 'cf-cache-status' is now 'HIT'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5bCWwBQI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8mfhvfo6uqhmh2ni2siq.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5bCWwBQI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8mfhvfo6uqhmh2ni2siq.PNG" alt="Cloudflare CF-Cache-Status HIT" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To confirm that Polish has been applied we can check the 'cf-bgj' header returns the image quality setting 'imgq'. We can also check the 'cf-polished' header to confirm Polish status and returns the image quality setting (qual) and original size (origSize). &lt;/p&gt;

&lt;p&gt;The above headers can confirm that polish is working working, however we can do better and ensure that images are being served in next generation format. To do this we can review the 'content-type' header and ensure this is returning 'image/webp', comparing this to the 'cf-polished' original format (origFmt) we can see the original format of the image was png and has been converted to webp.&lt;/p&gt;

&lt;p&gt;While Cloudflare Polish automatically optimizes images, it will only optimize the image if the webp image is smaller than the original image.&lt;/p&gt;

&lt;p&gt;If we review the below 'cf-polished' header below. We can see the status is set to 'webp_bigger' this is informing us that the image optimization on this image would provide no benefit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jnzisLTm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9lq4jzcyb0cou8shqorc.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jnzisLTm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9lq4jzcyb0cou8shqorc.PNG" alt="No Compression" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully someone else finds this information useful. If you have any questions please drop a comment below.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Working with Optimizely Projects</title>
      <dc:creator>Paul McGann</dc:creator>
      <pubDate>Fri, 05 Nov 2021 11:38:32 +0000</pubDate>
      <link>https://dev.to/paulmcgann/working-with-optimizely-projects-2ojn</link>
      <guid>https://dev.to/paulmcgann/working-with-optimizely-projects-2ojn</guid>
      <description>&lt;p&gt;Optimizely has a featured called &lt;a href="https://world.optimizely.com/documentation/developer-guides/CMS/projects/"&gt;'Projects'&lt;/a&gt;, which allows editors to collaborate on content before scheduling to publish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;p&gt;While work on a project that imports hundreds, of pieces of content. These pieces of content are added to a project for the team to review and mark the items as ready to publish.&lt;/p&gt;

&lt;p&gt;You can see the problem here in that with hundreds of pieces of content to review, marking each content piece as ready to publish could take some time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;To over come the problem we decided to create a scheduled job that would loop over all items in a project and mark them as ready to publish.&lt;/p&gt;

&lt;p&gt;To retrieve the projects and their project items we can get an instance of the 'ProjectRepository' into our scheduled job.&lt;/p&gt;

&lt;p&gt;Once we have the project and its items we can then loop over the project items and use the 'IContentChangeManager' interface to update the status of the content items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_projectRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;List&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;projectItemIds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_projectRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;projectItemId&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;projectItemIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ProjectItem&lt;/span&gt; &lt;span class="n"&gt;projectItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetProjectItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectItemId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectItem&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CommitResult&lt;/span&gt; &lt;span class="n"&gt;commitResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_contentChangeManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SaveAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CheckIn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ProjectItem&lt;/span&gt; &lt;span class="nf"&gt;GetProjectItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;projectItemId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_projectRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectItemId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I just wanted to share this piece of code with anyone who was trying to achieve something similiar.&lt;/p&gt;

</description>
      <category>optimizely</category>
      <category>projects</category>
      <category>csharp</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Translate Categories in Episerver using DbLocalizationProvider</title>
      <dc:creator>Paul McGann</dc:creator>
      <pubDate>Thu, 21 Jan 2021 20:52:53 +0000</pubDate>
      <link>https://dev.to/paulmcgann/translate-categories-in-episerver-using-dblocalizationprovider-3c9d</link>
      <guid>https://dev.to/paulmcgann/translate-categories-in-episerver-using-dblocalizationprovider-3c9d</guid>
      <description>&lt;p&gt;While doing some investigation into localization with Episerver I came across a useful library that helps content editors easily translate resources that are normally stored in xml, by storing them within a database.&lt;/p&gt;

&lt;p&gt;One of the areas that I investigated was translating the built in categories within Episerver. This is possible to do using a strongly-typed &lt;a href="https://github.com/valdisiljuconoks/localization-provider-epi/blob/master/docs/working-with-resources-epi.md#translating-episerver-categories"&gt;category definition&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;LocalizedCategory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SampleCategory&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SampleCategory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This is sample cat. from code"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may be suitable in some cases, however in some scenarios where the categories already exist this may not be the best course of action.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/valdisiljuconoks/localization-provider-epi/blob/master/README.md"&gt;DbLocalizationProvider&lt;/a&gt; has a way to &lt;a href="https://github.com/valdisiljuconoks/LocalizationProvider/blob/master/docs/sync-net.md#register-manually-resources"&gt;manually create resources&lt;/a&gt;. Using this I set about creating a resource manually for each category.&lt;/p&gt;

&lt;p&gt;Each resource needs a unique resource key, the translation /value and the language the translation is for.&lt;/p&gt;

&lt;p&gt;In order to create each resource I used a stored procedure that would iterate over the categories and build out a unique resource key, whilst assigning the value and language.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DbLocalizationProvider.Sync&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EPiServer.DataAbstraction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EPiServer.PlugIn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EPiServer.Scheduler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Globalization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LangTransaltion.Business.Localization&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ScheduledPlugIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DisplayName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Category Localization Scheduled Job"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategoryLocalizationScheduledJob&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ScheduledJobBase&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;CategoryRepository&lt;/span&gt; &lt;span class="n"&gt;_categoryRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ISynchronizer&lt;/span&gt; &lt;span class="n"&gt;_synchronizer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;_stopSignaled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_categoryCounterTotal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ManualResource&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_allCategories&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CategoryLocalizationScheduledJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CategoryRepository&lt;/span&gt; &lt;span class="n"&gt;categoryRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ISynchronizer&lt;/span&gt; &lt;span class="n"&gt;synchronizer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;IsStoppable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;_categoryRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;categoryRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;_synchronizer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;synchronizer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_stopSignaled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Called when a scheduled job executes&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;A status message to be stored in the database log and visible from admin mode&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;OnStatusChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Starting execution of &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;_allCategories&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ManualResource&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_categoryRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetRoot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nf"&gt;IterateCategories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"Categories.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;_synchronizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterManually&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_allCategories&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_categoryCounterTotal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; categories checked"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Iterates over categories build a unique key&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="currentParent"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="path"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;IterateCategories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="n"&gt;currentParent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentParent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_stopSignaled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="n"&gt;_categoryCounterTotal&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;

                &lt;span class="nf"&gt;OnStatusChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Working on category &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, child of: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;currentParent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="n"&gt;_allCategories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ManualResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

                &lt;span class="nf"&gt;IterateCategories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the scheduled job the new resources will be added to the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iMGsxJj0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/knqhifult9weu0oghnht.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iMGsxJj0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/knqhifult9weu0oghnht.PNG" alt="DbLocalizationProvider" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final piece of the puzzle was to retrieve the resource key whenever we use the category on the site. In order to do this I created an extension method to return the resource key based on the current category.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EPiServer.DataAbstraction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LangTransaltion.Helpers&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategoryHelper&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Gets the category resource key.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="category"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetResourceKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;categoryList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class="nf"&gt;GetParentCategories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categoryList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;categoryList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reverse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resourceKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Categories.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categoryList&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resourceKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Gets the parent categories, starting a from a root category.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="currentCategory"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="categoryList"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetParentCategories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="n"&gt;currentCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;categoryList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;categoryList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parent&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="nf"&gt;GetParentCategories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categoryList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you are using a category and wish to retrieve the translation you can simply use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;DbLocalizationProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LocalizationProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetResourceKey&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would like to thank the &lt;a href="https://twitter.com/tech_fellow"&gt;Valdis Iljuconoks&lt;/a&gt; for helping me out and answering some questions I had along the way.&lt;/p&gt;

</description>
      <category>episerver</category>
      <category>localization</category>
      <category>translation</category>
      <category>optimizely</category>
    </item>
  </channel>
</rss>
