C # 6.0 문자열 보간 지역화
C # 6.0에는 문자열 보간 기능이 있습니다. 다음과 같은 문자열 형식을 지정할 수있는 좋은 기능입니다.
var name = "John";
WriteLine($"My name is {name}");
예제는 다음으로 변환됩니다.
var name = "John";
WriteLine(String.Format("My name is {0}", name));
지역화 관점에서 다음과 같은 문자열을 저장하는 것이 훨씬 좋습니다.
"My name is {name} {middlename} {surname}"
String.Format 표기법보다
"My name is {0} {1} {2}"
.NET 지역화에 문자열 보간을 사용하는 방법은 무엇입니까? $ "..."를 리소스 파일에 넣는 방법이 있습니까? 아니면 문자열을 "... {name}"처럼 저장하고 어떻게 든 즉시 보간해야합니까?
추신 :이 질문은 "string.FormatIt 확장을 만드는 방법"에 관한 것이 아닙니다 (많은 라이브러리, SO 답변 등이 있습니다). 이 질문은 "현지화"컨텍스트에서 "문자열 보간"에 대한 Roslyn 확장 (둘 다 MS .NET 어휘의 용어 임) 또는 Dylan이 제안한 동적 사용에 관한 것입니다.
Microsoft.CodeAnalysis.CSharp.Scripting 패키지를 사용 하면이 작업을 수행 할 수 있습니다.
동적 개체가 사용되는 아래에 데이터를 저장할 개체를 만들어야합니다. 필요한 모든 속성을 사용하여 특정 클래스를 만들 수도 있습니다. 여기 에 설명 된 클래스에서 동적 개체를 래핑하는 이유 .
public class DynamicData
{
public dynamic Data { get; } = new ExpandoObject();
}
그런 다음 아래와 같이 사용할 수 있습니다.
var options = ScriptOptions.Default
.AddReferences(
typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).GetTypeInfo().Assembly,
typeof(System.Runtime.CompilerServices.DynamicAttribute).GetTypeInfo().Assembly);
var globals = new DynamicData();
globals.Data.Name = "John";
globals.Data.MiddleName = "James";
globals.Data.Surname = "Jamison";
var text = "My name is {Data.Name} {Data.MiddleName} {Data.Surname}";
var result = await CSharpScript.EvaluateAsync<string>($"$\"{text}\"", options, globals);
이것은 코드 조각을 컴파일하고 실행하므로 진정한 C # 문자열 보간입니다. 실제로 런타임에 코드를 컴파일하고 실행하므로 성능을 고려해야합니다. 이 성능 저하를 피하려면 CSharpScript.Create를 사용하여 코드를 컴파일하고 캐시 할 수 있습니다.
보간 된 문자열이 C # 1과 같은 식의 중괄호 블럭을 평가한다 (예를 들어 {expression}, {1 + 1}, {person.FirstName}).
이는 보간 된 문자열의 표현식이 현재 컨텍스트의 이름을 참조해야 함을 의미합니다.
예를 들어 다음 문은 컴파일되지 않습니다.
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();
}
}
질문에 답하려면 :
런타임에 보간 된 문자열을 평가하기 위해 프레임 워크에서 제공하는 현재 메커니즘이 없습니다. 따라서 상자에서 즉시 문자열을 저장하고 보간 할 수 없습니다.
문자열의 런타임 보간을 처리하는 라이브러리가 있습니다.
Roslyn codeplex 사이트에 대한 이 토론 에 따르면 문자열 보간은 리소스 파일과 호환되지 않을 가능성이 높습니다 (강조).
문자열 보간은 String.Format 또는 concatenation보다 깔끔하고 디버깅하기 쉽습니다.
Dim y = $"Robot {name} reporting
{coolant.name} levels are {coolant.level}
{reactor.name} levels are {reactor.level}"
그러나이 예는 비린내입니다. 대부분의 전문 프로그래머는 코드에 사용자 용 문자열을 작성하지 않습니다. 대신 지역화를 위해 리소스 (.resw, .resx 또는 .xlf)에 해당 문자열을 저장합니다. 따라서 여기에서는 문자열 보간을 많이 사용하지 않는 것 같습니다.
이전 답변에서 이미 말했듯이 : 현재 컴파일 타임에 사용되기 때문에 문자열 보간을 위해 런타임에 (예 : 리소스 파일에서) 형식 문자열을로드 할 수 없습니다.
컴파일 시간 기능에 신경 쓰지 않고 이름이 지정된 자리 표시자를 원하면 다음 확장 메서드와 같은 것을 사용할 수 있습니다.
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}여기와 같은 인라인 표현식을 사용할 수 없으며 이것이 최상의 성능을 제공하는 코드가 아님을 유의하십시오.
리소스 파일에서로드하거나 다음과 같이 인라인으로로드하는 사전과 함께 사용할 수 있습니다.
var txt = "Hello {name} on {day:yyyy-MM-dd}!".StringFormat(new Dictionary<string, object>
{
["name"] = "Joe",
["day"] = DateTime.Now,
});
형식 문자열이 C # 소스 코드에없는 경우 C # 6.0 문자열 보간은 도움이되지 않습니다. 이 경우이 라이브러리 와 같은 다른 솔루션을 사용해야 합니다 .
귀하의 질문이 보간 된 문자열 리소스를 처리하는 방법이 아니라 소스 코드에서 보간 된 문자열을 지역화하는 방법에 관한 것이라고 가정합니다.
예제 코드가 주어지면 :
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}"GetText 또는 리소스 또는 이와 유사한 것을 사용하여 이에 적합한 번역을 찾아 반환하거나 원래 매개 변수를 반환하여 번역을 건너 뛰어야합니다.
You will still need to document for your translators what the parameter numbers mean; the text used in the original code string doesn't exist at runtime.
If, for example, in this case GetTranslation returned "{2}. {0} {2}, {1}. Don't wear it out." (hey, localisation is not just about language!) then the output of the full program would be:
Bloggs. John Bloggs, W. Don't wear it out.
Having said this, while using this style of translation is easy to develop, it's hard to actually translate, since the strings are buried in the code and only surface at runtime. Unless you have a tool that can statically explore your code and extract all the translatable strings (without having to hit that code path at runtime), you're better off using more traditional resx files, since they inherently give you a table of text to be translated.
If we use interpolation then we are thinking in terms of methods, not constants. In that case we could define our translations as methods:
public abstract class InterpolatedText
{
public abstract string GreetingWithName(string firstName, string lastName);
}
public class InterpolatedTextEnglish : InterpolatedText
{
public override string GreetingWithName(string firstName, string lastName) =>
$"Hello, my name is {firstName} {lastName}.";
}
We can then load an implementation of InterpolatedText for a specific culture. This also provides a way to implement fallback, as one implementation can inherit from another. If English is the default language and other implementations inherit from it, there will at least be something to display until a translation is provided.
This seems a bit unorthodox, but offers some benefits:
Primarily, the string used for interpolation is always stored in a strongly-typed method with clearly-specified arguments.
Given this: "Hello, my name is {0} {1}" can we determine that the placeholders represent first name and last name in that order? There will always be a method which matches values to placeholders, but there's less room for confusion when the interpolated string is stored with its arguments.
Similarly, if we store our translation strings in one place and use them in another, it becomes possible to modify them in a way that breaks the code using them. We can add {2} to a string which will be used elsewhere, and that code will fail at runtime.
Using string interpolation this is impossible. If our translation string doesn't match the available arguments it won't even compile.
There are drawbacks, although I see difficulty in maintaining any solution.
The greatest is portability. If your translation is coded in C# and you switch, it's not the easiest thing to export all of your translations.
It also means that if you wish to farm out translations to different individuals (unless you have one person who speaks everything) then the translators must modify code. It's easy code, but code nonetheless.
String interpolation is difficult to combine with localization because the compiler prefers to translate it to string.Format(...), which does not support localization. However, there is a trick that makes it possible to combine localization and string interpolation; it is described near the end of this article.
Normally string interpolation is translated to
string.Format, whose behavior cannot be customized. However, in much the same way as lambda methods sometimes become expression trees, the compiler will switch fromstring.FormattoFormattableStringFactory.Create(a .NET 4.6 method) if the target method accepts aSystem.FormattableStringobject.The problem is, the compiler prefers to call
string.Formatif possible, so if there were an overload ofLocalized()that acceptedFormattableString, it would not work with string interpolation because the C# compiler would simply ignore it [because there is an overload that accepts a plain string]. Actually, it's worse than that: the compiler also refuses to useFormattableStringwhen calling an extension method.It can work if you use a non-extension method. For example:
static class Loca { public static string lize(this FormattableString message) { return message.Format.Localized(message.GetArguments()); } }Then you can use it like this:
public class Program { public static void Main(string[] args) { Localize.UseResourceManager(Resources.ResourceManager); var name = "Dave"; Console.WriteLine(Loca.lize($"Hello, {name}")); } }It's important to realize that the compiler converts the
$"..."string into an old-fashioned format string. So in this example,Loca.lizeactually receives"Hello, {0}"as the format string, not"Hello, {name}".
Interpolated strings can not refactored out from their (variable) scope because of using of the embedded variables in them.
The only way to relocate the string literal part is passing the scope bound variables as parameter to an other location, and mark their position in the string with special placeholders. However this solution is already "invented" and out there:
string.Format("literal with placeholers", parameters);
or some of advanced library (interpolating runtime), but using the very same concept (passing parameters).
Then you can refactor out the "literal with placeholers" to a resource.
참고URL : https://stackoverflow.com/questions/29068194/c6-0-string-interpolation-localization
'Program Club' 카테고리의 다른 글
| 데이터 가져 오기시 Firestore 성능 저하 문제 (0) | 2020.11.18 |
|---|---|
| 'git merge'는 세부적으로 어떻게 작동합니까? (0) | 2020.11.18 |
| vim 일반 모드에서 g 키 사용 (0) | 2020.11.18 |
| DynamoDB에서 쿼리 또는 스캔을 사용하여 결과를 주문할 수 있습니까? (0) | 2020.11.18 |
| Xcode 6 Save for Enterprise Deployment가 더 이상 ipa에 대한 plist를 생성하지 않습니까? (0) | 2020.11.18 |