Skip to content

Commit a80f9d0

Browse files
author
Sébastien Geiser
committed
Add LocFieldAttribute and LocPropertyAttribute to specify custom Loc Instance in Fody packages
Add LocFieldAttribute and LocPropertyAttribute to specify custom Loc Instance in Fody packages
1 parent c9f13f8 commit a80f9d0

File tree

8 files changed

+206
-2
lines changed

8 files changed

+206
-2
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Diagnostics;
2+
using System.Windows;
3+
4+
namespace CodingSeb.Localization.AssemblyToProcess
5+
{
6+
[LocField(nameof(customLoc))]
7+
public class LocalizedWithFodyAndCustomLocFieldClass : NotifyPropertyChangedBase
8+
{
9+
[Localize]
10+
public string TestProperty => customLoc.Translate("TestLabel");
11+
12+
[Localize(nameof(TextIdInAttribute))]
13+
public string TextIdInAttribute { get; set; }
14+
15+
private Loc customLoc = new Loc();
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Diagnostics;
2+
using System.Windows;
3+
4+
namespace CodingSeb.Localization.AssemblyToProcess
5+
{
6+
[LocPropertyAttribute(nameof(CustomLoc))]
7+
public class LocalizedWithFodyAndCustomLocPropertyClass : NotifyPropertyChangedBase
8+
{
9+
[Localize]
10+
public string TestProperty => CustomLoc.Translate("TestLabel");
11+
12+
[Localize(nameof(TextIdInAttribute))]
13+
public string TextIdInAttribute { get; set; }
14+
15+
public Loc CustomLoc { get; set; } = new Loc();
16+
}
17+
}

CodingSeb.Localization.FodyAddin.Fody/ModuleWeaver.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ private void SubscribeToLanguageChangedInConstructors(TypeDefinition typeDefinit
125125
&& methodReference.DeclaringType == typeDefinition);
126126
}).ToList();
127127

128+
string locPropertyName = typeDefinition.CustomAttributes.FirstOrDefault(attribute => attribute.AttributeType.Name.Equals("LocPropertyAttribute"))?.ConstructorArguments[0].Value?.ToString();
129+
var locProperty = typeDefinition.Properties.FirstOrDefault(property => property.Name.Equals(locPropertyName) && property.PropertyType.FullName.Equals(locType.FullName));
130+
131+
string locFieldName = typeDefinition.CustomAttributes.FirstOrDefault(attribute => attribute.AttributeType.Name.Equals("LocFieldAttribute"))?.ConstructorArguments[0].Value?.ToString();
132+
var locField = typeDefinition.Fields.FirstOrDefault(field => field.Name.Equals(locFieldName) && field.FieldType.FullName.Equals(locType.FullName));
133+
128134
exclusiveAlwaysCalledConstructors.ForEach(constructor =>
129135
{
130136
var instructions = constructor.Body.Instructions;
@@ -135,7 +141,22 @@ private void SubscribeToLanguageChangedInConstructors(TypeDefinition typeDefinit
135141
}
136142

137143
instructions.Add(Instruction.Create(OpCodes.Nop));
138-
instructions.Add(Instruction.Create(OpCodes.Call, locGetInstance));
144+
145+
if (locProperty != null)
146+
{
147+
instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
148+
instructions.Add(Instruction.Create(OpCodes.Call, locProperty.GetMethod));
149+
}
150+
else if (locField != null)
151+
{
152+
instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
153+
instructions.Add(Instruction.Create(OpCodes.Ldfld, locField));
154+
}
155+
else
156+
{
157+
instructions.Add(Instruction.Create(OpCodes.Call, locGetInstance));
158+
}
159+
139160
instructions.Add(Instruction.Create(OpCodes.Ldstr, "CurrentLanguageChanged"));
140161
instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
141162
instructions.Add(Instruction.Create(OpCodes.Ldftn, currentLanguageChangedMethod));

CodingSeb.Localization.FodyAddin.Tests/WeaverTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,105 @@ void NotifyPropertyChanged_PropertyChanged(object sender, PropertyChangedEventAr
6363

6464
notifyPropertyChanged.PropertyChanged -= NotifyPropertyChanged_PropertyChanged;
6565
}
66+
67+
[Fact]
68+
public void ValidateCustomInstanceLocPropertyAttribute()
69+
{
70+
var type = testResult.Assembly.GetType("CodingSeb.Localization.AssemblyToProcess.LocalizedWithFodyAndCustomLocPropertyClass");
71+
72+
FieldInfo propertyNamesField = type.GetField("__localizedPropertyNames__", BindingFlags.NonPublic | BindingFlags.Static);
73+
MethodInfo languageChangedMethod = type.GetMethod("__CurrentLanguageChanged__", BindingFlags.NonPublic | BindingFlags.Instance);
74+
75+
Assert.NotNull(propertyNamesField);
76+
Assert.NotNull(languageChangedMethod);
77+
78+
var instance = (dynamic)Activator.CreateInstance(type, true);
79+
80+
List<string> listOfPropertyNames = propertyNamesField.GetValue(instance) as List<string>;
81+
82+
Assert.NotNull(listOfPropertyNames);
83+
Assert.Contains("TestProperty", listOfPropertyNames);
84+
85+
INotifyPropertyChanged notifyPropertyChanged = instance as INotifyPropertyChanged;
86+
87+
List<string> propertyNames = new List<string>();
88+
89+
void NotifyPropertyChanged_PropertyChanged(object sender, PropertyChangedEventArgs e)
90+
{
91+
propertyNames.Add(e.PropertyName);
92+
}
93+
94+
notifyPropertyChanged.PropertyChanged += NotifyPropertyChanged_PropertyChanged;
95+
96+
Loc customLoc = type.GetProperty("CustomLoc").GetValue(instance) as Loc;
97+
98+
Assert.NotNull(customLoc);
99+
100+
languageChangedMethod.Invoke(instance, new object[] { customLoc, new CurrentLanguageChangedEventArgs("en", "fr") });
101+
102+
Assert.Contains("TestProperty", propertyNames);
103+
Assert.Contains("TextIdInAttribute", propertyNames);
104+
105+
propertyNames.Clear();
106+
107+
Assert.Empty(propertyNames);
108+
109+
customLoc.CurrentLanguage = "es";
110+
111+
Assert.Contains("TestProperty", propertyNames);
112+
Assert.Contains("TextIdInAttribute", propertyNames);
113+
114+
notifyPropertyChanged.PropertyChanged -= NotifyPropertyChanged_PropertyChanged;
115+
}
116+
117+
[Fact]
118+
public void ValidateCustomInstanceLocFieldAttribute()
119+
{
120+
var type = testResult.Assembly.GetType("CodingSeb.Localization.AssemblyToProcess.LocalizedWithFodyAndCustomLocFieldClass");
121+
122+
FieldInfo propertyNamesField = type.GetField("__localizedPropertyNames__", BindingFlags.NonPublic | BindingFlags.Static);
123+
MethodInfo languageChangedMethod = type.GetMethod("__CurrentLanguageChanged__", BindingFlags.NonPublic | BindingFlags.Instance);
124+
125+
Assert.NotNull(propertyNamesField);
126+
Assert.NotNull(languageChangedMethod);
127+
128+
var instance = (dynamic)Activator.CreateInstance(type, true);
129+
130+
List<string> listOfPropertyNames = propertyNamesField.GetValue(instance) as List<string>;
131+
132+
Assert.NotNull(listOfPropertyNames);
133+
Assert.Contains("TestProperty", listOfPropertyNames);
134+
135+
INotifyPropertyChanged notifyPropertyChanged = instance as INotifyPropertyChanged;
136+
137+
List<string> propertyNames = new List<string>();
138+
139+
void NotifyPropertyChanged_PropertyChanged(object sender, PropertyChangedEventArgs e)
140+
{
141+
propertyNames.Add(e.PropertyName);
142+
}
143+
144+
notifyPropertyChanged.PropertyChanged += NotifyPropertyChanged_PropertyChanged;
145+
146+
Loc customLoc = type.GetField("customLoc", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(instance) as Loc;
147+
148+
Assert.NotNull(customLoc);
149+
150+
languageChangedMethod.Invoke(instance, new object[] { customLoc, new CurrentLanguageChangedEventArgs("en", "fr") });
151+
152+
Assert.Contains("TestProperty", propertyNames);
153+
Assert.Contains("TextIdInAttribute", propertyNames);
154+
155+
propertyNames.Clear();
156+
157+
Assert.Empty(propertyNames);
158+
159+
customLoc.CurrentLanguage = "es";
160+
161+
Assert.Contains("TestProperty", propertyNames);
162+
Assert.Contains("TextIdInAttribute", propertyNames);
163+
164+
notifyPropertyChanged.PropertyChanged -= NotifyPropertyChanged_PropertyChanged;
165+
}
66166
}
67167
}

CodingSeb.Localization.FodyAddin/GlobalSuppressions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@
77

88
[assembly: SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.LocalizeAttribute.#ctor(System.String)")]
99
[assembly: SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.PropertyChangedTriggerMethodNameForLocalization.#ctor(System.String)")]
10+
[assembly: SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.LocPropertyAttribute.#ctor(System.String)")]
11+
[assembly: SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.LocField.#ctor(System.String)")]
1012
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.PropertyChangedTriggerMethodNameForLocalization.#ctor(System.String)")]
1113
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.LocalizeAttribute.#ctor(System.String)")]
14+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.LocPropertyAttribute.#ctor(System.String)")]
15+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.Localization.LocField.#ctor(System.String)")]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace CodingSeb.Localization
6+
{
7+
/// <summary>
8+
/// To specify the name of a field defined in the class that has this attribute that return a custom <see cref="Loc"/> instance
9+
/// for multi-users mode
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Class)]
12+
public class LocFieldAttribute : Attribute
13+
{
14+
/// <summary>
15+
/// To specify the name of a field defined in the class that has this attribute that return a custom <see cref="Loc"/> instance
16+
/// for multi-users mode
17+
/// </summary>
18+
/// <param name="fieldName">the name of the field that get the custom <see cref="Loc"/> instance</param>
19+
public LocFieldAttribute(string fieldName)
20+
{}
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace CodingSeb.Localization
6+
{
7+
/// <summary>
8+
/// To specify the name of a property defined in the class that has this attribute that return a custom <see cref="Loc"/> instance
9+
/// for multi-users mode
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Class)]
12+
public class LocPropertyAttribute : Attribute
13+
{
14+
/// <summary>
15+
/// To specify the name of a property defined in the class that has this attribute that return a custom <see cref="Loc"/> instance
16+
/// for multi-users mode
17+
/// </summary>
18+
/// <param name="propertyName">the name of the property that get the custom <see cref="Loc"/> instance</param>
19+
public LocPropertyAttribute(string propertyName)
20+
{}
21+
}
22+
}

CodingSeb.Localization/CodingSeb.Localization.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
* Loc.MissingTranslations is now static
2121
* Add Loc.GetInstance Property of type Func&lt;Loc&gt; to redefine how to resolve the Loc.Instance for Loc.Tr
2222
* JsonMissingTranslationsLogger and YamlMissingTranslationsLogger method EnableLogFor(Loc) and DisableLocFor(Loc) are now EnableLog() and DisableLog()
23-
* Can now Binding a custom instance of Loc in Tr xaml markup With Property LocInstanceBinding </PackageReleaseNotes>
23+
* Can now Binding a custom instance of Loc in Tr xaml markup With Property LocInstanceBinding
24+
* Add LocFieldAttribute and LocPropertyAttribute to specify custom Loc Instance in Fody packages</PackageReleaseNotes>
2425
<Version>1.2.0</Version>
2526
</PropertyGroup>
2627

0 commit comments

Comments
 (0)