translation using NGettext.Wpf
This commit is contained in:
32
src/NGettext.Wpf/ChangeCultureCommand.cs
Normal file
32
src/NGettext.Wpf/ChangeCultureCommand.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
using System.Globalization;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
public class ChangeCultureCommand : ICommand
|
||||
{
|
||||
public bool CanExecute(object? parameter)
|
||||
{
|
||||
return CultureInfo.GetCultures(CultureTypes.SpecificCultures)
|
||||
.Any(cultureInfo => cultureInfo.Name == (string)parameter);
|
||||
}
|
||||
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
if (CultureTracker is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
CultureTracker.CurrentCulture =
|
||||
CultureInfo.GetCultures(CultureTypes.SpecificCultures)
|
||||
.Single(cultureInfo => cultureInfo.Name == (string)parameter);
|
||||
}
|
||||
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
public static ICultureTracker? CultureTracker { get; set; }
|
||||
}
|
||||
}
|
||||
28
src/NGettext.Wpf/Common/GettextStringFormatConverter.cs
Normal file
28
src/NGettext.Wpf/Common/GettextStringFormatConverter.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace NGettext.Wpf.Common
|
||||
{
|
||||
public class GettextStringFormatConverter : IValueConverter
|
||||
{
|
||||
public string MsgId { get; private set; }
|
||||
|
||||
public GettextStringFormatConverter(string msgId)
|
||||
{
|
||||
this.MsgId = msgId;
|
||||
}
|
||||
|
||||
public static ILocalizer Localizer { get; set; }
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return Localizer.Gettext(MsgId, value);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/NGettext.Wpf/CompositionRoot.cs
Normal file
29
src/NGettext.Wpf/CompositionRoot.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
using NGettext.Wpf.Common;
|
||||
using NGettext.Wpf.EnumTranslation;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
public static class CompositionRoot
|
||||
{
|
||||
public static void Compose(string domainName, CultureInfo cultureInfo, string localeFolder)
|
||||
{
|
||||
var cultureTracker = new CultureTracker();
|
||||
cultureTracker.CurrentCulture = cultureInfo;
|
||||
var localizer = new Localizer(cultureTracker, domainName, localeFolder);
|
||||
ChangeCultureCommand.CultureTracker = cultureTracker;
|
||||
GettextExtension.Localizer = localizer;
|
||||
TrackCurrentCultureBehavior.CultureTracker = cultureTracker;
|
||||
LocalizeEnumConverter.EnumLocalizer = new EnumLocalizer(localizer);
|
||||
Translation.Localizer = localizer;
|
||||
GettextStringFormatConverter.Localizer = localizer;
|
||||
}
|
||||
|
||||
internal static void WriteMissingInitializationErrorMessage()
|
||||
{
|
||||
Console.Error.WriteLine("NGettext.Wpf: NGettext.Wpf.CompositionRoot.Compose() must be called at the entry point of the application for localization to work");
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/NGettext.Wpf/CultureEventArgs.cs
Normal file
16
src/NGettext.Wpf/CultureEventArgs.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
public class CultureEventArgs : EventArgs
|
||||
{
|
||||
public CultureEventArgs(CultureInfo cultureInfo)
|
||||
{
|
||||
if (cultureInfo == null) throw new ArgumentNullException(nameof(cultureInfo));
|
||||
CultureInfo = cultureInfo;
|
||||
}
|
||||
|
||||
public CultureInfo CultureInfo { get; }
|
||||
}
|
||||
}
|
||||
64
src/NGettext.Wpf/CultureTracker.cs
Normal file
64
src/NGettext.Wpf/CultureTracker.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace NGettext.Wpf;
|
||||
|
||||
public interface ICultureTracker
|
||||
{
|
||||
[Obsolete("Use AddWeakCultureObserver() instead. Otherwise the culture tracker (which is probably a singleton) will keep your object alive for longer than it needs to. This method will be removed in 2.x")]
|
||||
event EventHandler<CultureEventArgs> CultureChanged;
|
||||
|
||||
event EventHandler<CultureEventArgs> CultureChanging;
|
||||
|
||||
CultureInfo CurrentCulture { get; set; }
|
||||
|
||||
void AddWeakCultureObserver(IWeakCultureObserver weakCultureObserver);
|
||||
}
|
||||
|
||||
public class CultureTracker : ICultureTracker
|
||||
{
|
||||
private CultureInfo _currentCulture = CultureInfo.CurrentUICulture;
|
||||
private List<WeakReference<IWeakCultureObserver>> _weakObservers = new List<WeakReference<IWeakCultureObserver>>();
|
||||
|
||||
public event EventHandler<CultureEventArgs> CultureChanged;
|
||||
|
||||
public CultureInfo CurrentCulture
|
||||
{
|
||||
get => _currentCulture;
|
||||
set
|
||||
{
|
||||
if (_currentCulture == value)
|
||||
return;
|
||||
|
||||
CultureChanging?.Invoke(this, new CultureEventArgs(value));
|
||||
_currentCulture = value;
|
||||
RaiseCultureChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void RaiseCultureChanged()
|
||||
{
|
||||
var cultureEventArgs = new CultureEventArgs(CurrentCulture);
|
||||
CultureChanged?.Invoke(this, cultureEventArgs);
|
||||
|
||||
var weakObserversStillAlive = new List<WeakReference<IWeakCultureObserver>>();
|
||||
|
||||
foreach (var weakReference in _weakObservers)
|
||||
{
|
||||
if (!weakReference.TryGetTarget(out var observer))
|
||||
continue;
|
||||
|
||||
observer.HandleCultureChanged(this, cultureEventArgs);
|
||||
weakObserversStillAlive.Add(weakReference);
|
||||
}
|
||||
|
||||
_weakObservers = weakObserversStillAlive;
|
||||
}
|
||||
|
||||
public event EventHandler<CultureEventArgs> CultureChanging;
|
||||
|
||||
public void AddWeakCultureObserver(IWeakCultureObserver weakCultureObserver)
|
||||
{
|
||||
_weakObservers.Add(new WeakReference<IWeakCultureObserver>(weakCultureObserver));
|
||||
}
|
||||
}
|
||||
36
src/NGettext.Wpf/EnumTranslation/EnumLocalizer.cs
Normal file
36
src/NGettext.Wpf/EnumTranslation/EnumLocalizer.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NGettext.Wpf.EnumTranslation
|
||||
{
|
||||
public interface IEnumLocalizer
|
||||
{
|
||||
string LocalizeEnum(Enum value);
|
||||
}
|
||||
|
||||
public class EnumLocalizer : IEnumLocalizer
|
||||
{
|
||||
private readonly ILocalizer _localizer;
|
||||
|
||||
public EnumLocalizer(ILocalizer localizer)
|
||||
{
|
||||
_localizer = localizer ?? throw new ArgumentNullException(nameof(localizer));
|
||||
}
|
||||
|
||||
public string LocalizeEnum(Enum value)
|
||||
{
|
||||
var type = value.GetType();
|
||||
var enumMemberName = value.ToString();
|
||||
var msgIdAttribute = (EnumMsgIdAttribute)type.GetMember(enumMemberName).SingleOrDefault()?.GetCustomAttribute(typeof(EnumMsgIdAttribute), true);
|
||||
|
||||
if (msgIdAttribute is null)
|
||||
{
|
||||
Console.Error.WriteLine($"{type}.{enumMemberName} lacks the [MsgId(\"...\")] attribute.");
|
||||
return enumMemberName;
|
||||
}
|
||||
|
||||
return _localizer.Gettext(msgIdAttribute.MsgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/NGettext.Wpf/EnumTranslation/EnumMsgIdAttribute.cs
Normal file
14
src/NGettext.Wpf/EnumTranslation/EnumMsgIdAttribute.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace NGettext.Wpf.EnumTranslation
|
||||
{
|
||||
public class EnumMsgIdAttribute : Attribute
|
||||
{
|
||||
public EnumMsgIdAttribute(string msgId)
|
||||
{
|
||||
MsgId = msgId;
|
||||
}
|
||||
|
||||
public string MsgId { get; }
|
||||
}
|
||||
}
|
||||
55
src/NGettext.Wpf/EnumTranslation/LocalizeEnumConverter.cs
Normal file
55
src/NGettext.Wpf/EnumTranslation/LocalizeEnumConverter.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace NGettext.Wpf.EnumTranslation
|
||||
{
|
||||
public class LocalizeEnumConverter : IValueConverter
|
||||
{
|
||||
private IEnumLocalizer _enumLocalizer;
|
||||
|
||||
public LocalizeEnumConverter()
|
||||
{
|
||||
}
|
||||
|
||||
public LocalizeEnumConverter(IEnumLocalizer enumLocalizer)
|
||||
{
|
||||
_enumLocalizer = enumLocalizer;
|
||||
}
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var enumLocalizer = GetEnumLocalizer();
|
||||
if (enumLocalizer is null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is Enum enumValue)
|
||||
{
|
||||
return enumLocalizer.LocalizeEnum(enumValue);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static IEnumLocalizer EnumLocalizer { get; set; }
|
||||
|
||||
private IEnumLocalizer GetEnumLocalizer()
|
||||
{
|
||||
var result = _enumLocalizer ?? EnumLocalizer;
|
||||
|
||||
if (result is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
96
src/NGettext.Wpf/GettextExtension.cs
Normal file
96
src/NGettext.Wpf/GettextExtension.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
[MarkupExtensionReturnType(typeof(string))]
|
||||
public class GettextExtension : MarkupExtension, IWeakCultureObserver
|
||||
{
|
||||
private DependencyObject _dependencyObject;
|
||||
private DependencyProperty _dependencyProperty;
|
||||
|
||||
[ConstructorArgument("params")] public object[] Params { get; set; }
|
||||
|
||||
[ConstructorArgument("msgId")] public string MsgId { get; set; }
|
||||
|
||||
public GettextExtension(string msgId)
|
||||
{
|
||||
MsgId = msgId;
|
||||
Params = new object[] { };
|
||||
}
|
||||
|
||||
public GettextExtension(string msgId, params object[] @params)
|
||||
{
|
||||
MsgId = msgId;
|
||||
Params = @params;
|
||||
}
|
||||
|
||||
public static ILocalizer Localizer { get; set; }
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
|
||||
if (provideValueTarget.TargetObject is DependencyObject dependencyObject)
|
||||
{
|
||||
_dependencyObject = dependencyObject;
|
||||
if (DesignerProperties.GetIsInDesignMode(_dependencyObject))
|
||||
{
|
||||
return Gettext();
|
||||
}
|
||||
|
||||
AttachToCultureChangedEvent();
|
||||
|
||||
_dependencyProperty = (DependencyProperty)provideValueTarget.TargetProperty;
|
||||
|
||||
KeepGettextExtensionAliveForAsLongAsDependencyObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Console.WriteLine("NGettext.Wpf: Target object of type {0} is not yet implemented", provideValueTarget.TargetObject?.GetType());
|
||||
}
|
||||
|
||||
return Gettext();
|
||||
}
|
||||
|
||||
private string Gettext()
|
||||
{
|
||||
return Params.Any() ? Localizer.Gettext(MsgId, Params) : Localizer.Gettext(MsgId);
|
||||
}
|
||||
|
||||
void KeepGettextExtensionAliveForAsLongAsDependencyObject()
|
||||
{
|
||||
SetGettextExtension(_dependencyObject, this);
|
||||
}
|
||||
|
||||
void AttachToCultureChangedEvent()
|
||||
{
|
||||
if (Localizer is null)
|
||||
{
|
||||
Console.Error.WriteLine("NGettext.WPF.GettextExtension.Localizer not set. Localization is disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
Localizer.CultureTracker.AddWeakCultureObserver(this);
|
||||
}
|
||||
|
||||
public void HandleCultureChanged(ICultureTracker sender, CultureEventArgs eventArgs)
|
||||
{
|
||||
_dependencyObject.SetValue(_dependencyProperty, Gettext());
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty GettextExtensionProperty = DependencyProperty.RegisterAttached(
|
||||
"GettextExtension", typeof(GettextExtension), typeof(GettextExtension), new PropertyMetadata(default(GettextExtension)));
|
||||
|
||||
public static void SetGettextExtension(DependencyObject element, GettextExtension value)
|
||||
{
|
||||
element.SetValue(GettextExtensionProperty, value);
|
||||
}
|
||||
|
||||
public static GettextExtension GetGettextExtension(DependencyObject element)
|
||||
{
|
||||
return (GettextExtension)element.GetValue(GettextExtensionProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/NGettext.Wpf/GettextFormatConverterExtension.cs
Normal file
21
src/NGettext.Wpf/GettextFormatConverterExtension.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Windows.Markup;
|
||||
using NGettext.Wpf.Common;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
public class GettextFormatConverterExtension : MarkupExtension
|
||||
{
|
||||
public GettextFormatConverterExtension(string msgId)
|
||||
{
|
||||
MsgId = msgId;
|
||||
}
|
||||
|
||||
[ConstructorArgument("msgId")] public string MsgId { get; set; }
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return new GettextStringFormatConverter(MsgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/NGettext.Wpf/IWeakCultureObserver.cs
Normal file
7
src/NGettext.Wpf/IWeakCultureObserver.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
public interface IWeakCultureObserver
|
||||
{
|
||||
void HandleCultureChanged(ICultureTracker sender, CultureEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
117
src/NGettext.Wpf/Localizer.cs
Normal file
117
src/NGettext.Wpf/Localizer.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
public interface ILocalizer
|
||||
{
|
||||
ICatalog Catalog { get; }
|
||||
ICatalog GetCatalog(CultureInfo cultureInfo);
|
||||
ICultureTracker CultureTracker { get; }
|
||||
}
|
||||
|
||||
public class Localizer : IDisposable, ILocalizer
|
||||
{
|
||||
string _domainName;
|
||||
string _localeFolder;
|
||||
|
||||
public Localizer(ICultureTracker cultureTracker, string domainName, string localeFolder)
|
||||
{
|
||||
_domainName = domainName;
|
||||
_localeFolder = localeFolder;
|
||||
CultureTracker = cultureTracker;
|
||||
|
||||
if (cultureTracker == null)
|
||||
throw new ArgumentNullException(nameof(cultureTracker));
|
||||
|
||||
cultureTracker.CultureChanging += ResetCatalog;
|
||||
ResetCatalog(cultureTracker.CurrentCulture);
|
||||
}
|
||||
|
||||
void ResetCatalog(object sender, CultureEventArgs e)
|
||||
{
|
||||
ResetCatalog(e.CultureInfo);
|
||||
}
|
||||
|
||||
void ResetCatalog(CultureInfo cultureInfo)
|
||||
{
|
||||
Catalog = GetCatalog(cultureInfo);
|
||||
}
|
||||
|
||||
public ICatalog GetCatalog(CultureInfo cultureInfo) =>
|
||||
new Catalog(_domainName, _localeFolder, cultureInfo);
|
||||
|
||||
public ICatalog Catalog { get; private set; }
|
||||
|
||||
public ICultureTracker CultureTracker { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CultureTracker.CultureChanging -= ResetCatalog;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LocalizerExtensions
|
||||
{
|
||||
internal struct MsgIdWithContext
|
||||
{
|
||||
internal string Context { get; set; }
|
||||
internal string MsgId { get; set; }
|
||||
}
|
||||
|
||||
internal static MsgIdWithContext ConvertToMsgIdWithContext(string msgId)
|
||||
{
|
||||
var result = new MsgIdWithContext { MsgId = msgId };
|
||||
|
||||
if (msgId.Contains("|"))
|
||||
{
|
||||
var pipePosition = msgId.IndexOf('|');
|
||||
result.Context = msgId.Substring(0, pipePosition);
|
||||
result.MsgId = msgId.Substring(pipePosition + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string Gettext(this ILocalizer localizer, string msgId, params object[] values)
|
||||
{
|
||||
if (msgId == null)
|
||||
return "";
|
||||
|
||||
var msgIdWithContext = ConvertToMsgIdWithContext(msgId);
|
||||
|
||||
if (localizer is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
return string.Format(msgIdWithContext.MsgId, values);
|
||||
}
|
||||
|
||||
if (msgIdWithContext.Context != null)
|
||||
{
|
||||
return localizer.Catalog.GetParticularString(msgIdWithContext.Context, msgIdWithContext.MsgId, values);
|
||||
}
|
||||
|
||||
return localizer.Catalog.GetString(msgIdWithContext.MsgId, values);
|
||||
}
|
||||
|
||||
internal static string? Gettext(this ILocalizer localizer, string msgId)
|
||||
{
|
||||
if (msgId is null)
|
||||
return null;
|
||||
|
||||
var msgIdWithContext = ConvertToMsgIdWithContext(msgId);
|
||||
|
||||
if (localizer is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
return msgIdWithContext.MsgId;
|
||||
}
|
||||
|
||||
if (msgIdWithContext.Context != null)
|
||||
return localizer.Catalog.GetParticularString(msgIdWithContext.Context, msgIdWithContext.MsgId);
|
||||
|
||||
return localizer.Catalog.GetString(msgIdWithContext.MsgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/NGettext.Wpf/NGettext.Wpf.csproj
Normal file
16
src/NGettext.Wpf/NGettext.Wpf.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.77" />
|
||||
<PackageReference Include="NGettext" Version="0.6.7" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
24
src/NGettext.Wpf/NGettext.Wpf.nuspec
Normal file
24
src/NGettext.Wpf/NGettext.Wpf.nuspec
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>NGettext.Wpf</id>
|
||||
<version>1.2.6-alpha</version>
|
||||
<title>WPF support for NGettext</title>
|
||||
<authors>Robert Jørgensgaard Engdahl</authors>
|
||||
<owners>Robert Jørgensgaard Engdahl</owners>
|
||||
<license type="expression">LGPL-3.0-or-later</license>
|
||||
<projectUrl>https://github.com/robert-j-engdahl/ngettext-wpf</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Proper internationalization support for WPF (via NGettext). In particular a GetTextMarkupExtension is included, which is what everyone uses anyway.</description>
|
||||
<copyright>Copyright 2017, 2018, 2019 Accuratech ApS</copyright>
|
||||
<tags>gettext wpf ngettext gettextmarkupextension xgettext-xaml</tags>
|
||||
<dependencies>
|
||||
<dependency id="NGettext" version="0.6.3" />
|
||||
<dependency id="Expression.Blend.Sdk" version="1.0.2" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="../XGetText.Xaml/XGetText-Xaml.ps1" target="tools" />
|
||||
<file src="../XGetText.Xaml/Init.ps1" target="tools" />
|
||||
</files>
|
||||
</package>
|
||||
36
src/NGettext.Wpf/Properties/AssemblyInfo.cs
Normal file
36
src/NGettext.Wpf/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("NGettext.Wpf")]
|
||||
[assembly: AssemblyDescription("Proper internationalizations upport for WPF (via NGettext).")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NGettext.Wpf")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017, 2018, 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("4bb0eb35-fe32-4295-bf39-4da1c9b67bb8")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.2.2.0")]
|
||||
[assembly: AssemblyFileVersion("1.2.2.0")]
|
||||
38
src/NGettext.Wpf/Releasing.md
Normal file
38
src/NGettext.Wpf/Releasing.md
Normal file
@@ -0,0 +1,38 @@
|
||||
Releasing
|
||||
=========
|
||||
|
||||
- Update the `.nuspec` file with a new version number.
|
||||
- Build in release mode
|
||||
- Invoke `PM> nuget pack`; no options needed. But it will release the debug version, so make sure your build configuration match! Say `PM> nuget pack -Prop Configuration=Release`.
|
||||
- Sign the created package, for example by
|
||||
```
|
||||
PM> nuget sign .\NGettext.Wpf.1.1.0-alpha.nupkg -Timestamper http://timestamp.digicert.com -CertificateFingerprint 79a047643b02e7b677d5d0a962bc02ac19e63ca8
|
||||
```
|
||||
- Verify signing with
|
||||
```
|
||||
PM> nuget verify -Signatures .\NGettext.Wpf.1.1.0-alpha.nupkg
|
||||
|
||||
Verifying NGettext.Wpf.1.1.0-alpha
|
||||
C:\Git\ngettext-wpf\NGettext.Wpf\.\NGettext.Wpf.1.1.0-alpha.nupkg
|
||||
|
||||
Signature Hash Algorithm: SHA256
|
||||
Signature type: Author
|
||||
Verifying the author primary signature with certificate:
|
||||
Subject Name: CN=ACCURATECH ApS, O=ACCURATECH ApS, L=Holstebro, C=DK, SERIALNUMBER=27635652, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.3=DK
|
||||
SHA1 hash: 79A047643B02E7B677D5D0A962BC02AC19E63CA8
|
||||
SHA256 hash: A66690776D4B00270DAA40F0336E4EE8288D2B2F9F77E6B132B63D18F0F408FF
|
||||
Issued by: CN=DigiCert EV Code Signing CA (SHA2), OU=www.digicert.com, O=DigiCert Inc, C=US
|
||||
Valid from: 06-02-2018 01:00:00 to 09-02-2021 13:00:00
|
||||
|
||||
Timestamp: 19-02-2019 12:32:52
|
||||
|
||||
Verifying author primary signature's timestamp with timestamping service certificate:
|
||||
Subject Name: CN=DigiCert SHA2 Timestamp Responder, O=DigiCert, C=US
|
||||
SHA1 hash: 400191475C98891DEBA104AF47091B5EB6D4CBCB
|
||||
SHA256 hash: FC834D5BFFDE31DBA5B79BF95F573F7953BCBF9156E8525163E828EB92EA8A93
|
||||
Issued by: CN=DigiCert SHA2 Assured ID Timestamping CA, OU=www.digicert.com, O=DigiCert Inc, C=US
|
||||
Valid from: 04-01-2017 01:00:00 to 18-01-2028 01:00:00
|
||||
|
||||
|
||||
Successfully verified package 'NGettext.Wpf.1.1.0-alpha'
|
||||
```
|
||||
47
src/NGettext.Wpf/TrackCurrentCultureBehavior.cs
Normal file
47
src/NGettext.Wpf/TrackCurrentCultureBehavior.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using Microsoft.Xaml.Behaviors;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes sure that the CultureInfo used for all binding operations inside the associated
|
||||
/// FrameworkElement follows the CurrentCulture of the CultureTracker injected to the static
|
||||
/// CultureTracker property.
|
||||
///
|
||||
/// For instance, dates and numbers bound with a culture specific StringFormat will be formatted
|
||||
/// according to the tracked culture and even reformatted on culture changed.
|
||||
/// </summary>
|
||||
public class TrackCurrentCultureBehavior : Behavior<FrameworkElement>, IWeakCultureObserver
|
||||
{
|
||||
public static ICultureTracker CultureTracker { get; set; }
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if (!DesignerProperties.GetIsInDesignMode(AssociatedObject))
|
||||
{
|
||||
if (CultureTracker is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
return;
|
||||
}
|
||||
CultureTracker.AddWeakCultureObserver(this);
|
||||
UpdateAssociatedObjectCulture();
|
||||
}
|
||||
|
||||
base.OnAttached();
|
||||
}
|
||||
|
||||
void UpdateAssociatedObjectCulture()
|
||||
{
|
||||
if (AssociatedObject is null) return;
|
||||
AssociatedObject.Language = XmlLanguage.GetLanguage(CultureTracker.CurrentCulture.IetfLanguageTag);
|
||||
}
|
||||
|
||||
public void HandleCultureChanged(ICultureTracker sender, CultureEventArgs eventArgs)
|
||||
{
|
||||
UpdateAssociatedObjectCulture();
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/NGettext.Wpf/Translation.cs
Normal file
65
src/NGettext.Wpf/Translation.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace NGettext.Wpf
|
||||
{
|
||||
public static class Translation
|
||||
{
|
||||
public static string _(string msgId) => Localizer?.Gettext(msgId) ?? "";
|
||||
|
||||
public static string _(string msgId, params object[] parameters)
|
||||
{
|
||||
return parameters.Any()
|
||||
? Localizer?.Gettext(msgId, parameters) ?? ""
|
||||
: Localizer?.Gettext(msgId) ?? "";
|
||||
}
|
||||
|
||||
public static ILocalizer? Localizer { get; set; }
|
||||
|
||||
public static string Noop(string msgId) => msgId;
|
||||
|
||||
[Obsolete("Use GetPluralString() instead. This method will be removed in 2.x")]
|
||||
public static string PluralGettext(int n, string singularMsgId, string pluralMsgId, params object[] @params)
|
||||
{
|
||||
return GetPluralString(singularMsgId, pluralMsgId, n, @params);
|
||||
}
|
||||
|
||||
public static string GetPluralString(string singularMsgId, string pluralMsgId, int n, params object[] args)
|
||||
{
|
||||
if (Localizer is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
return string.Format(CultureInfo.InvariantCulture, n == 1 ? singularMsgId : pluralMsgId, args);
|
||||
}
|
||||
|
||||
return args.Any()
|
||||
? Localizer.Catalog.GetPluralString(singularMsgId, pluralMsgId, n, args)
|
||||
: Localizer.Catalog.GetPluralString(singularMsgId, pluralMsgId, n);
|
||||
}
|
||||
|
||||
public static string GetParticularPluralString(string context, string text, string pluralText, int n, params object[] args)
|
||||
{
|
||||
if (Localizer is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
return string.Format(CultureInfo.InvariantCulture, n == 1 ? text : pluralText, args);
|
||||
}
|
||||
|
||||
return args.Any()
|
||||
? Localizer.Catalog.GetParticularPluralString(context, text, pluralText, n, args)
|
||||
: Localizer.Catalog.GetParticularPluralString(context, text, pluralText, n);
|
||||
}
|
||||
|
||||
public static string GetParticularString(string context, string text, params object[] args)
|
||||
{
|
||||
if (Localizer is null)
|
||||
{
|
||||
CompositionRoot.WriteMissingInitializationErrorMessage();
|
||||
return (args.Any() ? string.Format(CultureInfo.InvariantCulture, text, args) : text);
|
||||
}
|
||||
return args.Any()
|
||||
? Localizer.Catalog.GetParticularString(context, text, args)
|
||||
: Localizer.Catalog.GetParticularString(context, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/NGettext.Wpf/packages.config
Normal file
6
src/NGettext.Wpf/packages.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Expression.Blend.Sdk" version="1.0.2" targetFramework="net45" />
|
||||
<package id="NGettext" version="0.6.3" targetFramework="net45" />
|
||||
<package id="NuGet.CommandLine" version="4.7.1" targetFramework="net45" developmentDependency="true" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user