C#6.0string插值本地化

C#6.0有一个string插入 – 一个很好的function来格式化string,如:

var name = "John"; WriteLine($"My name is {name}"); 

该示例转换为

  var name = "John"; WriteLine(String.Format("My name is {0}", name)); 

从本地化的angular度来看,存储string要好得多:

 "My name is {name} {middlename} {surname}" 

比在String.Format表示法:

 "My name is {0} {1} {2}" 

如何使用.NET本地化的string插值? 是否会有一种方法把$“…”资源文件? 或者应该将string存储为“… {name}”并以某种方式进行插入?

插值string将花括号之间的块评估为C#expression式(例如{expression}{1 + 1}{person.FirstName} )。

这意味着插入string中的expression式必须在当前上下文中引用名称。

例如这个语句不会编译:

 var nameFormat = $"My name is {name}"; // Cannot use *name* // before it is declared var name = "Fred"; WriteLine(nameFormat); 

同理:

 class Program { const string interpolated = $"{firstName}"; // Name *firstName* does not exist // in the current context static void Main(string[] args) { var firstName = "fred"; Console.WriteLine(interpolated); Console.ReadKey(); } } 

回答你的问题:

框架没有提供当前机制来在运行时评估插值string。 因此,您不能存储string,并且可以快速插入。

存在处理string运行时插值的库。

根据Roslyn codeplex站点上的这个讨论 ,string插值可能不会与资源文件兼容(重点是我的):

string插值可能比String.Format或串联更整齐,也更易于debugging…

 Dim y = $"Robot {name} reporting {coolant.name} levels are {coolant.level} {reactor.name} levels are {reactor.level}" 

但是,这个例子很腥。 大多数专业程序员不会在代码中编写面向用户的string。 相反,他们将这些string存储在资源(.resw,.resx或.xlf)由于本地化的原因。 所以这里没有太多的用于string插值。

如果格式string不在您的C#源代码中,C#6.0string插值将不会帮助您。 在这种情况下,你将不得不使用其他的解决scheme,比如这个库 。

正如在以前的答案中已经说过的那样:你目前不能在运行时(例如从资源文件)加载格式化string进行string插值,因为它在编译时使用。

如果你不关心编译时function,只想拥有命名的占位符,你可以使用像这样的扩展方法:

 public static string StringFormat(this string input, Dictionary<string, object> elements) { int i = 0; var values = new object[elements.Count]; foreach (var elem in elements) { input = Regex.Replace(input, "{" + Regex.Escape(elem.Key) + "(?<format>[^}]+)?}", "{" + i + "${format}}"); values[i++] = elem.Value; } return string.Format(input, values); } 

请注意,您不能在这里使用{i+1}这样的内联expression式,并且这不是具有最佳性能的代码。

你可以使用这个你从资源文件加载的字典或内联像这样:

 var txt = "Hello {name} on {day:yyyy-MM-dd}!".StringFormat(new Dictionary<string, object> { ["name"] = "Joe", ["day"] = DateTime.Now, }); 

插值string不能从它们的(variables) 范围重构出来,因为它们使用了embedded的variables

重定位string文字部分的唯一方法是将作用域绑定variables作为parameter passing给其他位置,并用特殊的占位符标记它们在string中的位置。 然而,这个解决scheme已经被“发明”了,

string.Format("literal with placeholers", parameters);

或一些高级库(插值运行时),但使用非常相同的概念(传递参数)。

然后,你可以重构出"literal with placeholers"的资源。

假设你的问题是关于如何本地化你的源代码中插入的string,而不是如何处理插入的string资源…

给出示例代码:

 var name = "John"; var middlename = "W"; var surname = "Bloggs"; var text = $"My name is {name} {middlename} {surname}"; Console.WriteLine(text); 

输出显然是:

 My name is John W Bloggs 

现在更改文本分配来取代翻译:

 var text = Translate($"My name is {name} {middlename} {surname}"); 

Translate是这样实现的:

 public static string Translate(FormattableString text) { return string.Format(GetTranslation(text.Format), text.GetArguments()); } private static string GetTranslation(string text) { return text; // actually use gettext or whatever } 

您需要提供您自己的GetTranslation实现; 它会收到一个像"My name is {0} {1} {2}"的string,并且应该使用GetText或者资源或者类似的词来定位和返回合适的翻译,或者只是返回原始参数来跳过翻译。

您仍然需要为翻译人员logging参数号码的含义; 原始代码string中使用的文本在运行时不存在。

例如,如果在这种情况下GetTranslation返回"{2}. {0} {2}, {1}. Don't wear it out." (嘿,本地化不仅仅是语言!)那么完整程序的输出将是:

 Bloggs. John Bloggs, W. Don't wear it out. 

说到这一点,使用这种翻译风格很容易发展,实际上很难翻译,因为string埋在代码中,只在运行时出现。 除非你有一个可以静态探索你的代码并提取所有可翻译的string的工具(不必在运行时点击代码path),你最好使用更传统的resx文件,因为它们固有地给你一个文本表被翻译。

string插值很难与本地化相结合,因为编译器倾向于将其转换为不支持本地化的string.Format(...) 。 但是,有一个技巧可以将本地化和string插值结合起来; 这篇文章的结尾附近有描述。