【问题标题】:Is there a way to avoid using hard-coded resw keys with the windows store app ResourceLoader?有没有办法避免在 Windows 商店应用程序 ResourceLoader 中使用硬编码的 resw 键?
【发布时间】:2013-07-08 13:30:47
【问题描述】:
有没有办法避免对 Windows 商店应用程序中的 resw 文件中包含的全球化字符串使用硬编码键?即
// I have to use a hard-coded string key, which may violate the
// DRY principle to gain access to the translation from C#:
var translation =
Windows.ApplicationModel.Resources.ResourceLoader.GetString("MyStringKey/Text")
【问题讨论】:
标签:
c#
windows-store-apps
translation
t4
globalization
【解决方案1】:
您可以使用下面的 T4 模板自动生成围绕您的 resw 文件的 C# 包装器。这意味着您无需在代码中乱扔魔术字符串资源键来访问 C# 的翻译 - 然后您的所有键都知道在编译时有效,而不是在运行时有效。模板生成如下代码:
internal static class Translation
{
public static ResourceLoader ResourceLoader { get; private set; }
static Translation()
{
ResourceLoader = new ResourceLoader();
}
public static string MyFirstString
{
get { return ResourceLoader.GetString("MyFirstString/Text"); }
}
}
这是由以下 T4 模板生成的:
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.CSharp" #>
<#@ import namespace="EnvDTE" #>
<#@ output extension=".cs" #>
<#
DTE env = GetVSEnvironment();
var inputFilePath = @"en-GB\Resources.resw";
var provider = new CSharpCodeProvider();
string className = CreateClassName(provider);
SetCurrentDirectory();
if (File.Exists(inputFilePath)) {
#>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a resource generator.
// </auto-generated>
//------------------------------------------------------------------------------
using Windows.ApplicationModel.Resources;
namespace <#= GetNamespace() #> {
internal static class <#= className #> {
public static ResourceLoader ResourceLoader { get; private set; }
static <#= className #>() {
ResourceLoader = new ResourceLoader();
}
<#
foreach (string name in GetResourceKeys(inputFilePath)) {
#>
public static string <#= provider.CreateEscapedIdentifier(name.Replace(".Text", String.Empty).Replace(".Content", String.Empty).Replace(".FlowDirection", string.Empty)) #> {
get { return ResourceLoader.GetString("<#= name.Replace(".Text", "/Text").Replace(".Content", "/Content").Replace(".FlowDirection", "/FlowDirection") #>"); }
}
<#
}
#>
}
}
<#
} else {
throw new FileNotFoundException(String.Format("Unable to find Resource file: {0}", inputFilePath));
}
#>
<#+
private DTE GetVSEnvironment() {
DTE env = null;
var provider = Host as IServiceProvider;
if (provider != null) {
env = provider.GetService(typeof(DTE)) as DTE;
}
if (env == null) {
throw new InvalidOperationException("Template must be executed from Visual Studio");
}
return env;
}
private void SetCurrentDirectory() {
Directory.SetCurrentDirectory(Host.ResolvePath(""));
}
private string CreateClassName(CSharpCodeProvider provider) {
string name = Path.GetFileNameWithoutExtension(Host.TemplateFile);
return provider.CreateEscapedIdentifier(name);
}
private string GetNamespace() {
return Host.ResolveParameterValue("directiveId", "namespaceDirectiveProcessor", "namespaceHint");
}
private static IEnumerable<string> GetResourceKeys(string filePath) {
XDocument doc = XDocument.Load(filePath);
var rootElement = doc.Root.Elements().SingleOrDefault(x => x.Name == "root");
if (rootElement == null)
{
throw new InvalidOperationException("No root element was found");
}
var dataElements = rootElement.Elements("data").ToList();
var resourceKeys = dataElements.Select(e => e.Attribute("name").Value).ToList();
return resourceKeys;
}
#>