Skip to content
This repository has been archived by the owner on Oct 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #119 from ErikApption/collect-missing-tojson
Browse files Browse the repository at this point in the history
Collect missing to json
  • Loading branch information
AlexTeixeira authored Dec 11, 2021
2 parents 3e9caae + b61751e commit 26590d8
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public enum MissingTranslationLogBehavior
{
Ignore,
LogConsoleError
LogConsoleError,
CollectToJSON
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class JsonLocalizationOptions : LocalizationOptions
private const char PLURAL_SEPARATOR = '|';
private const string DEFAULT_RESOURCES = "Resources";
private const string DEFAULT_CULTURE = "en-US";
public const string DEFAULT_MISSING_TRANSLATIONS = "MissingTranslations.json";

public new string ResourcesPath { get; set; } = DEFAULT_RESOURCES;
/// We cache all values to memory to avoid loading files for each request, this parameter defines the time after which the cache is refreshed.
Expand Down Expand Up @@ -120,5 +121,10 @@ public string FileEncodingName {
/// Define JSON Files management. See documentation for more information
/// </summary>
public LocalizationMode LocalizationMode { get; set; } = LocalizationMode.Basic;

/// <summary>
/// Local file name where the missing translation JSON values can be written. See documentation for more information
/// </summary>
public string MissingTranslationsOutputFile { get; internal set; } = DEFAULT_MISSING_TRANSLATIONS;
}
}
62 changes: 61 additions & 1 deletion Askmethat.Aspnet.JsonLocalizer/Localizer/JsonStringLocalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
using Askmethat.Aspnet.JsonLocalizer.JsonOptions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Newtonsoft.Json;

namespace Askmethat.Aspnet.JsonLocalizer.Localizer
{
internal class JsonStringLocalizer : JsonStringLocalizerBase, IJsonStringLocalizer
internal class JsonStringLocalizer : JsonStringLocalizerBase, IJsonStringLocalizer, IDisposable
{

#if NETCORE
Expand All @@ -24,6 +25,7 @@ public JsonStringLocalizer(IOptions<JsonLocalizationOptions> localizationOptions
= null) : base(localizationOptions, baseName)
{
_env = env;
_missingTranslations = localizationOptions.Value.MissingTranslationsOutputFile;
resourcesRelativePath = GetJsonRelativePath(_localizationOptions.Value.ResourcesPath);
}
#elif BLAZORASM
Expand All @@ -33,6 +35,7 @@ public JsonStringLocalizer(IOptions<JsonLocalizationOptions> localizationOptions
= null) : base(localizationOptions, baseName)
{
_env = env;
//_missingValuesJSON not viable on WASM
resourcesRelativePath = GetJsonRelativePath(_localizationOptions.Value.ResourcesPath);
}
#else
Expand All @@ -43,10 +46,14 @@ public JsonStringLocalizer(IOptions<JsonLocalizationOptions> localizationOptions
string baseName = null) : base(localizationOptions, baseName)
{
_env = env;
_missingTranslations = localizationOptions.Value.MissingTranslationsOutputFile;
resourcesRelativePath = GetJsonRelativePath(_localizationOptions.Value.ResourcesPath);
}
#endif

private IDictionary<string, string> _missingJsonValues = null;
private bool _disposedValue;
private string _missingTranslations = null;

public LocalizedString this[string name]
{
Expand Down Expand Up @@ -209,6 +216,19 @@ private string GetString(string name, bool shouldTryDefaultCulture = true)
Console.Error.WriteLine($"{name} does not contain any translation");
}

// Notify the user that a translation was not found for the current string
// only if logging is defined in options.MissingTranslationLogBehavior
if (_localizationOptions.Value.MissingTranslationLogBehavior ==
MissingTranslationLogBehavior.CollectToJSON)
{
if (_missingJsonValues is null)
_missingJsonValues = new Dictionary<string, string>();
if (_missingJsonValues.TryAdd(name, name))
{
Console.Error.WriteLine($"'{name}' added to missing values");
}
}

return null;
}

Expand Down Expand Up @@ -276,5 +296,45 @@ public void ReloadMemCache(IEnumerable<CultureInfo> reloadCulturesToCache = null
}
}

protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (!string.IsNullOrWhiteSpace(_missingTranslations))
{
try
{
// save missing values
var json = JsonConvert.SerializeObject(_missingJsonValues);
Console.Error.WriteLine($"Writing missing translations to {Path.GetFullPath(_missingTranslations)}");
File.WriteAllText(_missingTranslations, json);
} catch (Exception)
{
Console.Error.WriteLine($"Cannot write missing translations to {Path.GetFullPath(_missingTranslations)}");
}
}
}

// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
_disposedValue = true;
}
}

// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
// ~JsonStringLocalizer()
// {
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
// Dispose(disposing: false);
// }

public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ services.AddJsonLocalization(options => {
- **PluralSeparator** : *_default value: |*. Seperator used to get singular or pluralized version of localization. More information in *Pluralization*
- **MissingTranslationLogBehavior** : *_default value: LogConsoleError*. Define the logging mode
- **LocalizationMode** : *_default value: Basic*. Define the localization mode for the Json file. Currently Basic and I18n. More information in *LocalizationMode*
- **MissingTranslationsOutputFile** : This enables to specify in which file the missing translations will be written when `MissingTranslationLogBehavior = MissingTranslationLogBehavior.CollectToJSON`, defaults to `MissingTranslations.json`

### Search patterns when UseBaseName = true

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Askmethat.Aspnet.JsonLocalizer.Localizer;
using Askmethat.Aspnet.JsonLocalizer.Test.Helpers;
using Microsoft.Extensions.Localization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections;
using System.Globalization;
using System.Linq;
using Askmethat.Aspnet.JsonLocalizer.JsonOptions;
using LocalizedString = Microsoft.Extensions.Localization.LocalizedString;
using System.IO;

namespace Askmethat.Aspnet.JsonLocalizer.Test.Localizer
{
[TestClass]
public class MissingTranslationTest
{
private JsonStringLocalizer localizer = null;
public void InitLocalizer(string cultureString)
{
SetCurrentCulture(cultureString);

localizer = JsonStringLocalizerHelperFactory.Create(new JsonLocalizationOptions()
{
DefaultCulture = new CultureInfo("en-AU"),
MissingTranslationLogBehavior = Extensions.MissingTranslationLogBehavior.CollectToJSON,
SupportedCultureInfos = new System.Collections.Generic.HashSet<CultureInfo>()
{
new CultureInfo("fr"),
new CultureInfo("en"),
new CultureInfo("zh-CN"),
new CultureInfo("en-AU")
},
ResourcesPath = $"{AppContext.BaseDirectory}/i18nFallback",
LocalizationMode = LocalizationMode.I18n
});
}

[TestMethod]
public void Should_Track_Colored_NotFound()
{
var defaultJsonFile = JsonLocalizationOptions.DEFAULT_MISSING_TRANSLATIONS;
if (File.Exists(defaultJsonFile))
File.Delete(defaultJsonFile);
InitLocalizer("en-AU");
var result = localizer.GetString("Colored",false);
Assert.IsTrue(result.ResourceNotFound);
localizer.Dispose();
Assert.IsTrue(File.Exists(defaultJsonFile));
}

/// <summary>
/// LocalizedString doesn't implement the IComparer interface required by CollectionAssert.AreEqual(), so providing one here
/// </summary>
private class LocalizedStringComparer : IComparer
{
public int Compare(object x, object y)
{
LocalizedString lsX = (LocalizedString)x;
LocalizedString lsY = (LocalizedString)y;
if (ReferenceEquals(lsX, lsY))
{
return 0;
}
if (lsX.Name == lsY.Name && lsX.Value == lsY.Value && lsX.ResourceNotFound == lsY.ResourceNotFound)
{
return 0;
}
int result = StringComparer.CurrentCulture.Compare(lsX.Name, lsY.Name);
if (result != 0)
{
return result;
}
result = StringComparer.CurrentCulture.Compare(lsX.Value, lsY.Value);
return result != 0 ? result : lsX.ResourceNotFound.CompareTo(lsY.ResourceNotFound);
}
}

private void SetCurrentCulture(string cultureName)
=> SetCurrentCulture(new CultureInfo(cultureName));

private void SetCurrentCulture(CultureInfo cultureInfo)
=> CultureInfo.CurrentUICulture = cultureInfo;
}
}

0 comments on commit 26590d8

Please sign in to comment.