interpoladas entender ejemplos cadenas c# .net c#-6.0

entender - string c# ejemplos



LocalizaciĆ³n de interpolaciĆ³n de cadenas C#6.0 (9)

Asumiendo que su pregunta es más acerca de cómo localizar cadenas interpoladas en su código fuente, y no cómo manejar recursos de cadenas interpoladas ...

Dado el código de ejemplo:

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

La salida es obviamente:

My name is John W Bloggs

Ahora cambie la asignación de texto para buscar una traducción en su lugar:

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

Translate se implementa de esta manera:

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 proporcionar su propia implementación de GetTranslation ; recibirá una cadena como "My name is {0} {1} {2}" y debe usar GetText o recursos o similares para localizar y devolver una traducción adecuada para esto, o simplemente devolver el parámetro original para omitir la traducción.

Aún necesitará documentar para sus traductores lo que significan los números de parámetros; El texto utilizado en la cadena de código original no existe en tiempo de ejecución.

Si, por ejemplo, en este caso, GetTranslation devolvió "{2}. {0} {2}, {1}. Don''t wear it out." GetTranslation "{2}. {0} {2}, {1}. Don''t wear it out." (¡oye, la localización no se trata solo del lenguaje!), entonces el resultado del programa completo sería:

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

Dicho esto, aunque usar este estilo de traducción es fácil de desarrollar, es difícil traducirlo, ya que las cadenas están enterradas en el código y solo aparecen en tiempo de ejecución. A menos que tenga una herramienta que pueda explorar estáticamente su código y extraer todas las cadenas traducibles (sin tener que presionar esa ruta de código en tiempo de ejecución), es mejor que use archivos resx más tradicionales, ya que inherentemente le dan una tabla de texto para ser traducido

C # 6.0 tiene una interpolación de cadenas , una buena característica para formatear cadenas como:

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

El ejemplo se convierte a

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

Desde el punto de vista de la localización, es mucho mejor almacenar cadenas como:

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

que en String.Notación de formato:

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

¿Cómo usar la interpolación de cadenas para la localización .NET? ¿Habrá una manera de poner $ "..." en los archivos de recursos? ¿O las cadenas deben almacenarse como "... {nombre}" y de alguna manera interpolarse al vuelo?

PD: Esta pregunta NO trata sobre "cómo hacer una cadena. Extensión de formato" (hay MUCHAS bibliotecas, respuestas SO, etc.). Esta pregunta es sobre algo como la extensión de Roslyn para la "interpolación de cadenas" en el contexto de "localización" (ambos son términos en vocabulario MS .NET), o el uso dinámico como lo propuso Dylan.


Como ya se dijo en las respuestas anteriores: actualmente no puede cargar la cadena de formato en tiempo de ejecución (por ejemplo, desde archivos de recursos) para la interpolación de cadenas porque se usa en tiempo de compilación.

Si no le importa la función de tiempo de compilación y solo desea tener marcadores de posición con nombre, puede usar algo como este método de extensión:

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); }

Tenga en cuenta que no puede tener expresiones en línea como {i+1} aquí y que este no es un código con el mejor rendimiento.

Puede usar esto con un diccionario que cargue desde archivos de recursos o en línea de esta manera:

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


De acuerdo con esta discusión en el sitio codeplex de Roslyn, la interpolación de cadenas probablemente no será compatible con los archivos de recursos (énfasis mío):

La interpolación de cadenas podría ser más ordenada y más fácil de depurar que String.Format o concatenación ...

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

Sin embargo, este ejemplo es sospechoso. La mayoría de los programadores profesionales no escribirán cadenas orientadas al usuario en el código. En cambio, almacenarán esas cadenas en recursos (.resw, .resx o .xlf) por razones de localización. Por lo tanto, aquí no parece tener mucha utilidad la interpolación de cadenas.


La interpolación de cadenas C # 6.0 no lo ayudará si la cadena de formato no está en su código fuente C #. En ese caso, tendrá que usar alguna otra solución, como esta biblioteca .


La interpolación de cadenas es difícil de combinar con la localización porque el compilador prefiere traducirla a string.Format(...) , que no admite la localización. Sin embargo, hay un truco que hace posible combinar la localización y la interpolación de cadenas; se describe cerca del final de este artículo .

Normalmente, la interpolación de cadenas se traduce en string.Format , cuyo comportamiento no se puede personalizar. Sin embargo, de la misma manera que los métodos lambda a veces se convierten en árboles de expresión, el compilador cambiará de string.Format a FormattableStringFactory.Create (un método .NET 4.6) si el método de destino acepta un objeto System.FormattableString .

El problema es que el compilador prefiere llamar a string.Format si es posible, por lo que si hubiera una sobrecarga de Localized() que aceptara FormattableString , no funcionaría con la interpolación de cadenas porque el compilador de C # simplemente lo ignoraría [porque hay una sobrecarga que acepta una cadena simple]. En realidad, es peor que eso: el compilador también se niega a usar FormattableString al llamar a un método de extensión.

Puede funcionar si usa un método sin extensión. Por ejemplo:

static class Loca { public static string lize(this FormattableString message) { return message.Format.Localized(message.GetArguments()); } }

Entonces puedes usarlo así:

public class Program { public static void Main(string[] args) { Localize.UseResourceManager(Resources.ResourceManager); var name = "Dave"; Console.WriteLine(Loca.lize($"Hello, {name}")); } }

Es importante darse cuenta de que el compilador convierte la cadena $"..." en una cadena de formato antigua. Entonces, en este ejemplo, Loca.lize realmente recibe "Hello, {0}" como la cadena de formato, no "Hello, {name}" .


Las cadenas interpoladas no se pueden refactorizar desde su alcance (variable) debido al uso de las variables incrustadas en ellas.

La única forma de reubicar la parte literal de la cadena es pasar las variables vinculadas al alcance como parámetro a otra ubicación y marcar su posición en la cadena con marcadores de posición especiales. Sin embargo, esta solución ya está "inventada" y por ahí:

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

o algunos de la biblioteca avanzada (tiempo de ejecución interpolado), pero usando el mismo concepto (parámetros de paso).

Luego puede refactorizar el "literal with placeholers" a un recurso.


Si usamos la interpolación, entonces estamos pensando en términos de métodos, no de constantes. En ese caso podríamos definir nuestras traducciones como métodos:

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}."; }

Luego podemos cargar una implementación de InterpolatedText para una cultura específica. Esto también proporciona una manera de implementar el respaldo, ya que una implementación puede heredar de otra. Si el inglés es el idioma predeterminado y otras implementaciones heredan de él, al menos habrá algo que mostrar hasta que se proporcione una traducción.

Esto parece un poco poco ortodoxo, pero ofrece algunos beneficios:

Principalmente, la cadena utilizada para la interpolación siempre se almacena en un método fuertemente tipado con argumentos claramente especificados.

Dado esto: "Hello, my name is {0} {1}" ¿podemos determinar que los marcadores de posición representan el nombre y el apellido en ese orden? Siempre habrá un método que haga coincidir los valores con los marcadores de posición, pero hay menos espacio para la confusión cuando la cadena interpolada se almacena con sus argumentos.

Del mismo modo, si almacenamos nuestras cadenas de traducción en un lugar y las usamos en otro, es posible modificarlas de una manera que rompa el código al usarlas. Podemos agregar {2} a una cadena que se usará en otro lugar, y ese código fallará en tiempo de ejecución.

Usar la interpolación de cadenas es imposible. Si nuestra cadena de traducción no coincide con los argumentos disponibles, ni siquiera se compilará.

Hay inconvenientes, aunque veo dificultades para mantener cualquier solución.

Lo mejor es la portabilidad. Si su traducción está codificada en C # y cambia, no es lo más fácil exportar todas sus traducciones.

También significa que si desea agilizar las traducciones a diferentes personas (a menos que tenga una persona que hable todo), los traductores deben modificar el código. Es un código fácil, pero no obstante, el código.


Una cadena interpolada evalúa el bloque entre las llaves como una expresión de C # (por ejemplo, {expression} , {1 + 1} , {person.FirstName} ).

Esto significa que las expresiones en una cadena interpolada deben hacer referencia a nombres en el contexto actual.

Por ejemplo, esta declaración no compilará:

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

Similar:

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(); } }

Para responder tu pregunta:

No existe un mecanismo actual provisto por el marco para evaluar cadenas interpoladas en tiempo de ejecución. Por lo tanto, no puede almacenar cadenas e interpolar sobre la marcha fuera de la caja.

Existen bibliotecas que manejan la interpolación de cadenas en tiempo de ejecución.


Usando el paquete Microsoft.CodeAnalysis.CSharp.Scripting puede lograr esto.

Deberá crear un objeto para almacenar los datos, debajo se utiliza un objeto dinámico. También puede crear una clase específica con todas las propiedades requeridas. La razón para envolver el objeto dinámico en una clase se describe here .

public class DynamicData { public dynamic Data { get; } = new ExpandoObject(); }

Luego puede usarlo como se muestra a continuación.

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);

Esto es compilar el fragmento de código y ejecutarlo, por lo que es una verdadera interpolación de cadenas C #. Aunque tendrá que tener en cuenta el rendimiento de esto, ya que en realidad está compilando y ejecutando su código en tiempo de ejecución. Para evitar este golpe de rendimiento si pudiera usar CSharpScript.Create para compilar y almacenar en caché el código.