The code-first approach let´s you specify ordinary, strongly typed Interfaces (a.k.a typealized interfaces) to let you just utilize familiar code artefacts when dealing with i18n.
These typealized interfaces then get picked up by the source-generator, which generates an implementing class that then can be used f.e. in conjuntion with ordinary dependency-injection.
This way, devlepors do not need to leave their natural habitat (the code-editor), which comes with the benefit of automatic code-refactorings, ultimatley optimizing the inner-loop, even during i18n-tasks.
The consuming target project should at least reference a suitable version of Microsoft.Extensions.Localization.Abstractions
-
install TypealizR.CodeFirst via
when targeting versions below
v0.14.*, use TypealizR instead. -
install TypealizR.CodeFirst.Abstractions via
-
Author a
Typealized-Interfacewhich basically is just an ordinaryinterface, marked withCodeFirstTypealizedAttributesomewhere within your project.using TypealizR.CodeFirst.Abstractions; namespace Sample; [CodeFirstTypealized] // <-- this marks the interface as `to-be-typealized` public interface ILocalizables { }
-
Use properties for plain
translatables.using TypealizR.CodeFirst.Abstractions; namespace Sample; [CodeFirstTypealized] public interface ILocalizables { LocalizedString Hello { get; } // <-- simple translatables }
return-type needs to be
LocalizedStringthe generated code uses the property nameHelloas the default-value -
Use methods for type-safe translation of formatted
translatables.using TypealizR.CodeFirst.Abstractions; namespace Sample; [CodeFirstTypealized] public interface ILocalizables { LocalizedString Greet(string userName, string planetName); // <-- formatted translatables }
return-type needs to be
LocalizedStringthe generated code uses the method nameGreetas the default-value -
setup dependency-injection in your startup-code
var services = new ServiceCollection(); services.AddLogging(); //<-- required by `Microsoft.Extensions.Localization` services.AddLocalization(); //<-- required by `Microsoft.Extensions.Localization` services.AddSingleton<ILocalizables, Localizables>(); // <-- `Localizables` gets generated by TypealizR var provider = services.BuildServiceProvider();
-
start using the generated implementation
var codeFirst = provider.GetRequiredService<IStrings>(); Console.WriteLine(codeFirst.Hello); // Hello Console.WriteLine(codeFirst.Greet("Arthur", "🌍 earth")); // Greet Arthur 🌍 earth
-
Utilize structured xml comments to provide custom default-values
using Microsoft.Extensions.Localization; using TypealizR.CodeFirst.Abstractions; namespace ConsoleSTS.CodeFirst; [CodeFirstTypealized] public interface ILocalizables { /// <summary> /// Hello, fellow developer! /// </summary> public LocalizedString Hello { get; } /// <summary> /// Hey <paramref name="userName"/>, welcome to <paramref name="planetName"/> 👍! /// </summary> public LocalizedString Greet(string userName, string planetName); }
var codeFirst = provider.GetRequiredService<IStrings>(); Console.WriteLine(codeFirst.Hello); // Hello, fellow developer! Console.WriteLine(codeFirst.Greet("Arthur", "🌍 earth")); // Hey Arthur, welcome to 🌍 earth 👍!
-
-
install TypealizR.CLI (as local or global tool) via
-
run it on your project
dotnet tr code-first export some/path/to/a.csproj, or alternativelydotnet tr cf ex some/path/to/a.csproj
This will extract the following
resx-file for above sample:
<data name="Hello"> <value>Hello, fellow developer!</value> </data> <data name="Greet"> <value>Hey {0}, welcome to {1} 👍!</value> </data>
executing this will OVERWRITE any existing file at the moment. In the future, TypealizR will be aware of existing files and provide a way to synchronize code with those files, in order to not loose any customizations done within them. Follow the discussion and let´s define together, what workflows would be needed to make code-first-i18n a real game-changer in the future!
In order to also generate comments within resx-files (which may give potential translators some context), leverage the remarks-element within a structured XML-comment:
[CodeFirstTypealized]
public interface ILocalizables
{
/// <summary>
/// Hello, fellow developer!
/// </summary>
/// <remarks>
/// a simple greeting at application startup
/// </remarks>
public LocalizedString Hello { get; }
/// <summary>
/// Hey <paramref name="userName"/>, welcome to <paramref name="planetName"/> 👍!
/// </summary>
/// <remarks>
/// a demo greeting conversation, shown at demo-time ;)
/// </remarks>
public LocalizedString Greet(string userName, string planetName);
}Exporting this, will result in the following resx:
```xml
<data name="Hello">
<value>Hello, fellow developer!</value>
<comment>a simple greeting at application startup</comment>
</data>
<data name="Greet">
<value>Hey {0}, welcome to {1} 👍!</value>
<comment>a demo greeting conversation, shown at demo-time ;)</comment>
</data>
