1using Microsoft.Extensions.Localization;
4using System.ComponentModel;
5using System.Globalization;
6using System.Reflection;
7using System.Runtime.CompilerServices;
16 [AcceptEmptyServiceProvider]
17 [ContentProperty(nameof(Path))]
18 public class LocalizeExtension : IMarkupExtension<BindingBase>, INotifyPropertyChanged, IDisposable
21 private static readonly Dictionary<Type, SortedDictionary<string, bool>> missingStrings = [];
22 private static Timer? timer =
null;
24 private IStringLocalizer? localizer;
25 private bool isDisposed;
29 Log.Terminating += Log_Terminating;
32 private static Task Log_Terminating(
object? sender, EventArgs e)
37 return Task.CompletedTask;
48 return this.localizer;
52 public string Path {
get;
set; } =
".";
53 public BindingMode Mode {
get;
set; } = BindingMode.OneWay;
54 public IValueConverter? Converter {
get;
set; } =
null;
55 public string? ConverterParameter {
get;
set; } =
null;
56 public string? StringFormat {
get;
set; } =
null;
57 public Type? StringResource {
get;
set; } =
null;
62 public BindingBase?
Arg {
get;
set; }
64 public object ProvideValue(IServiceProvider ServiceProvider)
66 return (
this as IMarkupExtension<BindingBase>).ProvideValue(ServiceProvider);
69 BindingBase IMarkupExtension<BindingBase>.ProvideValue(IServiceProvider serviceProvider)
71 Type ResourcesType = this.StringResource ?? typeof(
AppResources);
75 .GetRuntimeProperties()
76 .FirstOrDefault(pi => pi.Name ==
this.Path) is
null)
80 "Localizer[STRINGNOTDEFINED]",
83 this.ConverterParameter,
90 if (this.
Arg is not
null)
92 MultiBinding Multi =
new()
95 Converter = formatConverter,
100 Multi.Bindings.Add(
new Binding(
101 $
"Localizer[{this.Path}]",
104 this.ConverterParameter,
110 Multi.Bindings.Add(this.
Arg);
117 $
"Localizer[{this.Path}]",
120 this.ConverterParameter,
133 lock (missingStrings)
138 if (!missingStrings.TryGetValue(Type, out SortedDictionary<string, bool>? PerId))
141 missingStrings[Type] = PerId;
144 PerId[StringId] =
true;
146 timer =
new Timer(LogAlert,
null, 1000, Timeout.Infinite);
150 private static void LogAlert(
object?
_)
152 StringBuilder sb =
new();
154 sb.AppendLine(
"Missing localized strings:");
157 lock (missingStrings)
159 foreach (KeyValuePair<Type, SortedDictionary<string, bool>> P
in missingStrings)
162 sb.AppendLine(P.Key.FullName);
163 sb.AppendLine(
new string(
'-', (P.Key.FullName?.Length ?? 0) + 3));
166 foreach (
string Key
in P.Value.Keys)
173 missingStrings.Clear();
176 ServiceRef.LogService.LogAlert(sb.ToString());
179 public LocalizeExtension()
181 if (!DesignMode.IsDesignModeEnabled)
182 LocalizationManager.CurrentCultureChanged += this.OnCurrentCultureChanged;
190 private void OnCurrentCultureChanged(
object? sender, CultureInfo culture)
192 PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(nameof(this.
Localizer)));
201 GC.SuppressFinalize(
this);
207 protected virtual void Dispose(
bool disposing)
212 LocalizationManager.CurrentCultureChanged -= this.OnCurrentCultureChanged;
214 this.isDisposed =
true;
217 public event PropertyChangedEventHandler? PropertyChanged;
A strongly-typed resource class, for looking up localized strings, etc.
Access to localized strings.
static void ReportMissingString(string StringId, Type Type)
Reports a missing string
virtual void Dispose(bool disposing)
IDisposable.Dispose
void Dispose()
IDisposable.Dispose
IStringLocalizer Localizer
Localizer instance reference.