May 29, 2016
visibility 6462
star star star star star star
(6 votes)

Missing translations in XML translation files

Most Episerver sites involve working with language files. These files are sadly often neglected during the development phase and give both developers and editors a headache when launch date closes in on the project.

There is no silver bullet to magically solve the challenges of translation, but I will here give a small contribution on how to gain insight on missing translation texts.

The Episerver Localization Service provides [Missing text….] output to spot missing translations, but often these texts are hidden I e-mail templates, validation texts or other, not so visible, places.

 

The image below shows a Spanish version of Alloy where translation fallback have been turned off. There are several [Missing text ‘key’ for Spanish] displayed.

image

 

In the Alloy sample site the translation files are mostly located in the Views.xml file in the Resources directory and this file is referred in the web.config file

   1: <episerver.framework>
   2: ....
   3:     <localization fallbackBehavior="Echo, MissingMessage">
   4:         <providers>
   5:             <add virtualPath="~/Resources/LanguageFiles" name="languageFiles" type="EPiServer.Framework.Localization.XmlResources.FileXmlLocalizationProvider, EPiServer.Framework" />        
   6:         </providers>
   7:     </localization>
   8: </episerver.framework>

 

It is possible to have several providers added to the list of providers. The localization service will return the first value that matches the passed key. If one provider returnes null, the key is passed on to the next provider.

If all providers return ‘null’, the missing text is returned.

 

To log missing translations, you can simply add a provider to log keys that have no previous provider.

   1: <localization fallbackBehavior="Echo, MissingMessage">
   2:   <providers>
   3:     <add virtualPath="~/Resources/LanguageFiles" name="languageFiles" type="EPiServer.Framework.Localization.XmlResources.FileXmlLocalizationProvider, EPiServer.Framework" />
   4:     <add name="missing_keys" type="Alloy.Business.Localization.MissingKeyLoggerLocalizationProvider, Alloy"/>
   5:   </providers>
   6: </localization>

 

Result:

image

 

The provider

The code shows a simplified version of the attached code file.

The basic outline of the functionality is to create a dictionary with missing keys (language key + culture). Every 10 minutes the missing keys are written to a local file on the server.  To remove duplicates that might be registered due to restarting the site etc., I recommend using Excel or some other tool Winking smile

 
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Globalization;
   4: using System.IO;
   5: using System.Linq;
   6: using EPiServer.DataAbstraction;
   7: using EPiServer.Framework.Localization;
   8: using EPiServer.ServiceLocation;
   9:  
  10:  
  11: namespace Alloy.Business.Localization
  12: {
  13:     public class MissingKeyLoggerLocalizationProvider : LocalizationProvider
  14:     {
  15:         Dictionary<string, DateTime> languageKeys = new Dictionary<string, DateTime>();
  16:         private DateTime _latestLogDateTime = DateTime.MinValue;
  17:  
  18:         public override string GetString(string originalKey, string[] normalizedKey, CultureInfo culture)
  19:         {
  20:             //Concatenate key and culture ot create a unique key for dictionary
  21:             string keyWithLanguage = originalKey + ";" + culture.Name;
  22:             if (!languageKeys.ContainsKey(keyWithLanguage))
  23:             {
  24:                 languageKeys.Add(keyWithLanguage, DateTime.Now);
  25:             }
  26:  
  27:             //Log all currently found keys
  28:             TimeSpan timeSinceLog = (DateTime.Now - _latestLogDateTime);
  29:             if (timeSinceLog > new TimeSpan(0, 0, 10, 0))
  30:             {
  31:                 LogDictionary();
  32:             }
  33:             return null; //return null to pass on to other providers
  34:         }
  35:  
  36:         private void LogDictionary()
  37:         {
  38:             IEnumerable<string> keyInfo = languageKeys.Keys.Select(x => $"Date:{languageKeys[x]};Key:{x}");
  39:  
  40:             string missingKeyLogPath = $@"c:\Temp\MissingKeys_{Name}.log";
  41:  
  42:             File.WriteAllLines(missingKeyLogPath, keyInfo);
  43:             _latestLogDateTime = DateTime.Now;
  44:         }
  45:  
  46:         public override IEnumerable<ResourceItem> GetAllStrings(string originalKey, string[] normalizedKey, CultureInfo culture)
  47:         {
  48:             return new List<ResourceItem>();
  49:         }
  50:  
  51:         public override IEnumerable<CultureInfo> AvailableLanguages
  52:         {
  53:             get { return ServiceLocator.Current.GetInstance<ILanguageBranchRepository>().ListEnabled().Select(x => x.Culture); }
  54:         }
  55:     }
  56: }

 

MissingKeyLoggerLocalizationProvider.cs

May 29, 2016

Comments

Daniel Ovaska
Daniel Ovaska May 30, 2016 09:10 AM

For older versions, a common mistake was that developer forgot to copy the language_xxx.xml files (especially when upgrading site) which results in some "Missing text..." all over the place. 
Nice post!

Vincent
Vincent May 30, 2016 12:14 PM

Nice post. 

Per Nergård (MVP)
Per Nergård (MVP) May 30, 2016 09:33 PM

Nice!

error Please login to comment.
Latest blogs
The Silent Success: When Your Optimizely SaaS CMS Config Push Succeeds with "0" Changes

  Picture this frustratingly common scenario in headless, code-first development with Optimizely SaaS CMS: You’ve defined a brilliant new element,...

Vipin Banka | Jul 13, 2026

Architecting an Enterprise-Grade Development Pipeline in Optimizely SaaS CMS

Most enterprise teams show up to Optimizely SaaS CMS with a clear roadmap for their release pipeline: DEV → QA → Stage → Prod. Four logical...

Vipin Banka | Jul 12, 2026

Bynder DAM Connector for Optimizely SaaS CMS: Improved Metadata Property Synchronization

While working with the Bynder DAM Connector for Optimizely SaaS CMS , one of the key areas I explored was how Bynder asset metadata is synchronized...

Vipin Banka | Jul 11, 2026

Optimizely DXP: Every Supported Culture, One Searchable Page

Quick one for anyone building multi-language sites on Optimizely DXP. I put together a reference tool listing all 806 supported cultures. More...

Adnan Zameer | Jul 10, 2026 |

A day in the life of an Optimizely OMVP: London Meetup 2026

On 2nd July 2026 the Optimizely London Developer Meetup returned to The Lightwell, and the running theme across the evening was less about individu...

Graham Carr | Jul 10, 2026

Optimizely’s Summer ’26 Roadmap: The CMS Is Starting to Look Less Like a Publishing Tool and More Like Marketing Infrastructure

Optimizely’s Summer ’26 Product Roadmap event was not just a list of product updates. At least, that is not the part I found most interesting. The...

Augusto Davalos | Jul 9, 2026