Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [L10NSharp.Windows.Forms] Removed emailForSubmissions parameter (8th parameter) from LocalizationManagerWinforms.Create. Since the localization dialog was jettisoned, it no longer makes sense to store this information on the localization manager.
- [L10NSharp] Replaced the .NET 8.0 target with .NET Standard 2.0 for broader compatibility.
- [L10NSharp] `GetDynamicString`, `GetDynamicStringOrEnglish`, and `GetString` now return the English fallback text immediately when called with a null, empty, or whitespace string ID, rather than attempting a cache lookup or write.

- [L10NSharp] Enabled nullable reference types on the core project with full CS86xx warning cleanup across all layers; `ILocalizedStringCache` and `ILocalizationManagerInternal` gained nullable annotations that may require source updates for NRT consumers.

### Fixed

- [L10NSharp.Windows.Forms] Restored project-local Resources support for `FallbackLanguagesDlgBase` button images (`Move`, `Move_up`, and `Move_down`).
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ See full changelog at https://github.com/sillsdev/l10nsharp/blob/master/CHANGELO
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<LangVersion>8.0</LangVersion>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<!-- Without this line some projects fail to build on TC with "error : SourceRoot items
Expand Down
2 changes: 1 addition & 1 deletion src/L10NSharp.Tests/LocalizationManagerTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace L10NSharp.Tests
{
public abstract class LocalizationManagerTestsBase<T> where T : IDocument
public abstract class LocalizationManagerTestsBase<T> where T : class, IDocument
{
protected const string AppId = "test";
protected const string AppName = "unit test";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void RegisterComponentForLocalizing(LocalizingInfoWinforms info,
Action<ILocalizationManagerInternalWinforms, LocalizingInfoWinforms> successAction);
}

internal interface ILocalizationManagerInternalWinforms<T> : ILocalizationManagerInternalWinforms, ILocalizationManagerInternal<T>
internal interface ILocalizationManagerInternalWinforms<T> : ILocalizationManagerInternalWinforms, ILocalizationManagerInternal<T> where T : class
{
new ILocalizedStringCacheWinforms<T> StringCache { get; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace L10NSharp.Windows.Forms
{
internal interface ILocalizedStringCacheWinforms<T> : L10NSharp.ILocalizedStringCache<T>
internal interface ILocalizedStringCacheWinforms<T> : L10NSharp.ILocalizedStringCache<T> where T : class
{
List<LocTreeNode<T>> LeafNodeList { get; }
Keys GetShortcutKeys(string langId, string id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace L10NSharp.Windows.Forms
{
internal class LocalizationManagerInternalWinforms<T> : LocalizationManagerInternal<T>
internal class LocalizationManagerInternalWinforms<T> : LocalizationManagerInternal<T> where T : class
{

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace L10NSharp.Windows.Forms.UIComponents
{
public class FallbackLanguagesDlg<T> : FallbackLanguagesDlgBase
public class FallbackLanguagesDlg<T> : FallbackLanguagesDlgBase where T : class
{
public FallbackLanguagesDlg()
{
Expand Down
2 changes: 1 addition & 1 deletion src/L10NSharp.Windows.Forms/UIComponents/LocTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace L10NSharp.Windows.Forms.UIComponents
{
/// ----------------------------------------------------------------------------------------
internal class LocTreeNode<T> : TreeNode
internal class LocTreeNode<T> : TreeNode where T : class
{
internal string Group { get; set; }
internal string Id { get; private set; }
Expand Down
2 changes: 1 addition & 1 deletion src/L10NSharp.Windows.Forms/UIComponents/NodeComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace L10NSharp.Windows.Forms.UIComponents
{
/// ----------------------------------------------------------------------------------------
internal class NodeComparer<T> : IComparer<LocTreeNode<T>>
internal class NodeComparer<T> : IComparer<LocTreeNode<T>> where T : class
{
internal enum SortField
{
Expand Down
6 changes: 3 additions & 3 deletions src/L10NSharp/CodeReader/ILReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ namespace L10NSharp.CodeReader
internal class ILInstruction
{
public readonly OpCode opCode;
public readonly object operand;
public readonly object? operand;

/// ------------------------------------------------------------------------------------
public ILInstruction(OpCode opCode, object operand)
public ILInstruction(OpCode opCode, object? operand)
{
this.opCode = opCode;
this.operand = operand;
Expand Down Expand Up @@ -94,7 +94,7 @@ ILInstruction Next()
opCode = s_TwoByteOpCodes[code];
}

object operand = null;
object? operand = null;

switch (opCode.OperandType)
{
Expand Down
65 changes: 34 additions & 31 deletions src/L10NSharp/CodeReader/StringExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ namespace L10NSharp.CodeReader
/// ------------------------------------------------------------------------------------
public class StringExtractor<T>
{
private MethodInfo[] _getStringMethodOverloads;
private List<LocalizingInfo> _getStringCallsInfo;
private Dictionary<string, LocalizingInfo> _extenderInfo;
private List<ILInstruction> _instructions;
private MethodInfo[] _getStringMethodOverloads = Array.Empty<MethodInfo>();
private List<LocalizingInfo> _getStringCallsInfo = new List<LocalizingInfo>();
private Dictionary<string, LocalizingInfo> _extenderInfo = new Dictionary<string, LocalizingInfo>();
private List<ILInstruction> _instructions = new List<ILInstruction>();
private readonly HashSet<string> _scannedTypes = new HashSet<string>();

public List<string> ExtractionExceptions = new List<string>();
Expand All @@ -34,16 +34,16 @@ public IReadOnlyList<LocalizingInfo> DoExtractingWork(

/// ------------------------------------------------------------------------------------
public IReadOnlyList<LocalizingInfo> DoExtractingWork(
IEnumerable<MethodInfo> additionalLocalizationMethods,
string[] namespaceBeginnings, BackgroundWorker worker)
IEnumerable<MethodInfo>? additionalLocalizationMethods,
string[] namespaceBeginnings, BackgroundWorker? worker)
{
_getStringMethodOverloads = typeof(LocalizationManager)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "GetString" || m.Name=="Localize")
.Union(typeof(L10NStringExtensions)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Localize"))
.Union(additionalLocalizationMethods ?? new MethodInfo[0])
.Union(additionalLocalizationMethods ?? Array.Empty<MethodInfo>())
.ToArray();

_getStringCallsInfo = new List<LocalizingInfo>();
Expand Down Expand Up @@ -193,7 +193,7 @@ private IEnumerable<Type> GetTypesToScan(ICollection<string> namespaceBeginnings

foreach (var type in typesInAssembly
.Where(t => t != null && !typesToScan.Contains(t))
.Where(type => namespaceBeginnings.Count == 0 || namespaceBeginnings.Any(nsb => type.FullName.StartsWith(nsb))))
.Where(type => namespaceBeginnings.Count == 0 || namespaceBeginnings.Any(nsb => type.FullName?.StartsWith(nsb) ?? false)))
{
typesToScan.Add(type);
}
Expand All @@ -208,13 +208,13 @@ private class LocInfoDistinctComparer : IEqualityComparer<LocalizingInfo>
/// ------------------------------------------------------------------------------------
public bool Equals(LocalizingInfo x, LocalizingInfo y)
{
return x.Id.Equals(y.Id, StringComparison.Ordinal);
return string.Equals(x.Id, y.Id, StringComparison.Ordinal);
}

/// ------------------------------------------------------------------------------------
public int GetHashCode(LocalizingInfo obj)
{
return obj.Id.GetHashCode();
return obj.Id?.GetHashCode() ?? 0;
}
}

Expand All @@ -224,7 +224,7 @@ public int GetHashCode(LocalizingInfo obj)
/// If this is set, no other loaded assemblies are scanned.
/// </summary>
/// ------------------------------------------------------------------------------------
public Assembly[] ExternalAssembliesToScan;
public Assembly[]? ExternalAssembliesToScan;

/// ------------------------------------------------------------------------------------
/// <summary>
Expand Down Expand Up @@ -300,7 +300,6 @@ private void FindLocalizedStringsInType(Type type)
#endif
try
{
_instructions = new List<ILInstruction>(new ILReader<T>(method));

var methodCallsInMethod = GetMethodCalls(method);
foreach (var getStringOverload in _getStringMethodOverloads)
Expand Down Expand Up @@ -328,6 +327,7 @@ e is FileLoadException || // Installed version is different from the one referen
/// ------------------------------------------------------------------------------------
private List<Tuple<int, MethodBase>> GetMethodCalls(MethodBase caller)
{
_instructions = new List<ILInstruction>(new ILReader<T>(caller));
var methodCalls = new List<Tuple<int, MethodBase>>();
var module = caller.Module;

Expand All @@ -336,16 +336,16 @@ private List<Tuple<int, MethodBase>> GetMethodCalls(MethodBase caller)
if (_instructions[i].opCode != OpCodes.Call)
continue;

Type[] genericMethodArguments = null;
var genericTypeArguments = caller.DeclaringType.GetGenericArguments();
Type[]? genericMethodArguments = null;
var genericTypeArguments = caller.DeclaringType!.GetGenericArguments();

if (!caller.IsConstructor && !caller.Name.Equals(".cctor"))
genericMethodArguments = caller.GetGenericArguments();

try
{
methodCalls.Add(new Tuple<int, MethodBase>(i,
module.ResolveMethod((int)_instructions[i].operand,
module.ResolveMethod((int)_instructions[i].operand!,
genericTypeArguments, genericMethodArguments)));
}
catch (FileNotFoundException e1)
Expand Down Expand Up @@ -388,7 +388,7 @@ private void FindGetStringCalls(MethodBase caller,
/// This is special because, as an extension method the 1st parameter in "hello".Localize("myapp.greeting") will be the string ("hello") itself,
/// which is backwards from the convention used in the GetString(id, theString, etc.)
/// </summary>
private LocalizingInfo GetInfoForCallToLocalizeExtension(Module module, int instrIndex, int paramsInMethodCall)
private LocalizingInfo? GetInfoForCallToLocalizeExtension(Module module, int instrIndex, int paramsInMethodCall)
{
var parameters = GetParameters(module, instrIndex, paramsInMethodCall);

Expand All @@ -399,7 +399,7 @@ private LocalizingInfo GetInfoForCallToLocalizeExtension(Module module, int inst

var id = String.IsNullOrEmpty(parameters[1]) ? parameters[0] : parameters[1];

var locInfo = new LocalizingInfo(id) { Text = parameters[0] };
var locInfo = new LocalizingInfo(id!) { Text = parameters[0] };

//end part that differs from GetInfoForCallToLocalizationMethod

Expand All @@ -417,14 +417,14 @@ private LocalizingInfo GetInfoForCallToLocalizeExtension(Module module, int inst
return locInfo;
}

private string[] GetParameters(Module module, int instrIndex, int paramsInMethodCall)
private string?[] GetParameters(Module module, int instrIndex, int paramsInMethodCall)
{
int parameterIndex = paramsInMethodCall - 1;
var parameters = new string[paramsInMethodCall];
var parameters = new string?[paramsInMethodCall];
for (int i = 1; ; i++)
{
if (_instructions[instrIndex - i].opCode == OpCodes.Ldstr)
parameters[parameterIndex--] = module.ResolveString((int) _instructions[instrIndex - i].operand);
parameters[parameterIndex--] = module.ResolveString((int)_instructions[instrIndex - i].operand!);
else if (_instructions[instrIndex - i].opCode != OpCodes.Call)
{
parameterIndex--;
Expand All @@ -439,15 +439,15 @@ private string[] GetParameters(Module module, int instrIndex, int paramsInMethod
}

/// ------------------------------------------------------------------------------------
private LocalizingInfo GetInfoForCallToGetStringMethod(Module module,
private LocalizingInfo? GetInfoForCallToGetStringMethod(Module module,
int instrIndex, int paramsInMethodCall)
{
var parameters = GetParameters(module, instrIndex, paramsInMethodCall);

if (parameters[0] == null || parameters[1] == null)
return null;

var locInfo = new LocalizingInfo(parameters[0]) { Text = parameters[1] };
var locInfo = new LocalizingInfo(parameters[0]!) { Text = parameters[1]! };

if (paramsInMethodCall >= 3 && parameters[2] != null)
locInfo.Comment = parameters[2];
Expand All @@ -470,11 +470,11 @@ private void FindExtenderCalls(MethodBase caller)

for (int i = 1; i < _instructions.Count; i++)
{
string text = null;
string? text;

if (_instructions[i].opCode == OpCodes.Ldstr)
{
text = module.ResolveString((int)_instructions[i].operand);
text = module.ResolveString((int)_instructions[i].operand!);
if (text.StartsWith(LocalizationManager.kL10NPrefix))
{
var locInfo = GetLocInfoForField(caller.ReflectedType.Name, text);
Expand All @@ -494,18 +494,18 @@ private void FindExtenderCalls(MethodBase caller)
if (_instructions[i - 1].opCode == OpCodes.Ldnull)
continue;

Type[] genericMethodArguments = null;
var genericTypeArguments = caller.DeclaringType.GetGenericArguments();
Type[]? genericMethodArguments = null;
var genericTypeArguments = caller.DeclaringType!.GetGenericArguments();

if (!caller.IsConstructor && !caller.Name.Equals(".cctor"))
genericMethodArguments = caller.GetGenericArguments();

string fldName = null;
string? fldName;

MethodBase mi = null;
MethodBase? mi;
try
{
mi = module.ResolveMethod((int)_instructions[i].operand,
mi = module.ResolveMethod((int)_instructions[i].operand!,
genericTypeArguments, genericMethodArguments);

}
Expand All @@ -516,6 +516,9 @@ private void FindExtenderCalls(MethodBase caller)
continue;
}

if (mi == null)
continue;

if (mi.Name.Equals("SetLocalizationPriority", StringComparison.Ordinal))
{
var priority = (LocalizationPriority)(_instructions[i - 1].opCode.Value - 22);
Expand All @@ -525,7 +528,7 @@ private void FindExtenderCalls(MethodBase caller)
}

text = (i > 1 && _instructions[i - 1].opCode == OpCodes.Ldstr ?
module.ResolveString((int)_instructions[i - 1].operand) : null);
module.ResolveString((int)_instructions[i - 1].operand!) : null);

if (text == null)
continue;
Expand Down Expand Up @@ -557,7 +560,7 @@ private void FindExtenderCalls(MethodBase caller)
private string GetFieldName(Module module, ILInstruction instruction)
{
if (instruction.opCode == OpCodes.Ldfld)
return module.ResolveField((int)instruction.operand).Name;
return module.ResolveField((int)instruction.operand!).Name;

return (instruction.opCode == OpCodes.Ldarg_0 ? "Form" : "throwaway");
}
Expand Down
2 changes: 1 addition & 1 deletion src/L10NSharp/ILocalizationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@ string GetLocalizedString(IComponent component, string id, string defaultText,
/// Gets the localized text for the specified id.
/// </summary>
/// ------------------------------------------------------------------------------------
string GetLocalizedString(string id, string defaultText);
string GetLocalizedString(string id, string? defaultText);
}
}
8 changes: 4 additions & 4 deletions src/L10NSharp/ILocalizationManagerInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ internal interface ILocalizationManagerInternal: ILocalizationManager
{
Dictionary<IComponent, string> ComponentCache { get; }

string GetStringFromStringCache(string uiLangId, string id);
string? GetStringFromStringCache(string uiLangId, string id);

void SaveIfDirty(ICollection<string> langIdsToForceCreate);
string GetPathForLanguage(string langId, bool getCustomPathEvenIfNonexistent);
void SaveIfDirty(ICollection<string>? langIdsToForceCreate);
string? GetPathForLanguage(string langId, bool getCustomPathEvenIfNonexistent);
void HandleUiLanguageChange();
}

internal interface ILocalizationManagerInternal<T>: ILocalizationManagerInternal
internal interface ILocalizationManagerInternal<T> : ILocalizationManagerInternal where T : class
{
ILocalizedStringCache<T> StringCache { get; }
/// <summary>
Expand Down
19 changes: 10 additions & 9 deletions src/L10NSharp/ILocalizedStringCache.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace L10NSharp
{
internal interface ILocalizedStringCache<T>
internal interface ILocalizedStringCache<T> where T : class
{
bool TryGetDocument(string langId, out T doc);
bool TryGetDocument(string langId, [NotNullWhen(true)] out T? doc);
IEnumerable<string> AvailableLangKeys { get; }
string GetString(string langId, string id);
string GetString(string langId, string id, bool formatForDisplay);
string GetToolTipText(string langId, string id);
string GetToolTipText(string langId, string id, bool formatForDisplay);
string GetShortcutKeysText(string langId, string id);
string GetComment(string id);
string GetValueForExactLangAndId(string langId, string id, bool formatForDisplay);
string? GetString(string langId, string id);
string? GetString(string langId, string id, bool formatForDisplay);
string? GetToolTipText(string langId, string id);
string? GetToolTipText(string langId, string id, bool formatForDisplay);
string? GetShortcutKeysText(string langId, string id);
string? GetComment(string id);
string? GetValueForExactLangAndId(string langId, string id, bool formatForDisplay);

void UpdateLocalizedInfo(LocalizingInfo locInfo);

Expand Down
Loading
Loading