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插值结合起来; 这篇文章的结尾附近有描述。