remarks generate example c# string localization pluralize

generate - params comments c#



Pluralidad en los mensajes del usuario (25)

Muchas veces, al generar mensajes para mostrar al usuario, el mensaje contendrá una cantidad de algo sobre lo que quiero informar al cliente.

Daré un ejemplo: el cliente ha seleccionado una cantidad de elementos de 1 en adelante y ha hecho clic en eliminar. Ahora quiero dar un mensaje de confirmación al cliente, y quiero mencionar la cantidad de elementos que ha seleccionado para minimizar la posibilidad de que se equivoque seleccionando un grupo de elementos y haciendo clic en Eliminar cuando solo quiere eliminar uno de ellos.

Una forma es hacer que el mensaje genérico sea así:

int noofitemsselected = SomeFunction(); string message = "You have selected " + noofitemsselected + " item(s). Are you sure you want to delete it/them?";

El "problema" aquí es el caso donde noofitemselected es 1, y tenemos que escribir item y it en lugar de items y ellos .

Mi solución normal será algo como esto

int noofitemsselected = SomeFunction(); string message = "You have selected " + noofitemsselected + " " + (noofitemsselected==1?"item" : "items") + ". Are you sure you want to delete " + (noofitemsselected==1?"it" : "them") + "?";

Esto se vuelve bastante largo y desagradable muy rápido si hay muchas referencias a la pluralidad de números dentro del código, y el mensaje real se vuelve difícil de leer.

Entonces mi pregunta es simple. ¿Hay alguna forma mejor de generar mensajes como este?

EDITAR

Veo que muchas personas se han quedado colgadas en el caso de que mencione que el mensaje debe mostrarse dentro de un cuadro de mensaje, y simplemente ha dado una respuesta sobre cómo evitar el uso del cuadro de mensaje, y eso está bien. .

Pero recuerde que el problema de la pluralización también se aplica a los textos de otros lugares del programa, además de los cuadros de mensaje. Por ejemplo, una etiqueta junto a una cuadrícula que muestra el número de líneas seleccionadas en la cuadrícula tendrá el mismo problema con respecto a la pluralización.

Así que esto se aplica básicamente a la mayoría de los textos que salen de algún modo de los programas, y luego la solución no es tan simple como simplemente cambiar el programa para que no salga más texto :)


Internacionalización

Supongo que quieres soporte de internacionalización, en cuyo caso diferentes idiomas tienen diferentes patrones para plurales (por ejemplo, una forma especial plural para 2 de algo, o idiomas más complicados como polaco), y no puedes confiar en aplicar un patrón simple a tu cadena arreglarlo.

Puede usar la función ngettext GNU Gettext y proporcionar dos mensajes en inglés en su código fuente. Gettext proporcionará la infraestructura para elegir entre otros (potencialmente más) mensajes cuando se traducen a otros idiomas. Ver http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html para una descripción completa del soporte plural de GNU gettext.

GNU Gettext está bajo LGPL. ngettext se llama GettextResourceManager.GetPluralString en el puerto C # de Gettext.

(Si no necesita soporte de localización, y no quiere usar Gettext de inmediato, escriba su propia función que haga esto en inglés, y pase dos mensajes completos , de esa manera si necesita l10n más tarde, puede agregar reescribiendo una sola función).


¿Por qué querrías presentar un mensaje que los usuarios realmente puedan entender? Va en contra de 40 años de historia de programación. Nooooo, estamos pasando algo bueno, no lo estropees con mensajes comprensibles .

(j / k)


¿Qué hay de lo que Java ha tenido durante años: java.text.MessageFormat y ChoiceFormat? Consulte http://download.oracle.com/javase/1.4.2/docs/api/java/text/MessageFormat.html para obtener más información.

MessageFormat form = new MessageFormat("The disk /"{1}/" contains {0}."); form.applyPattern( "There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}."); Object[] testArgs = {new Long(12373), "MyDisk"}; System.out.println(form.format(testArgs)); // output, with different testArgs output: The disk "MyDisk" are no files. output: The disk "MyDisk" is one file. output: The disk "MyDisk" are 1,273 files.

En tu caso, quieres algo algo más simple:

MessageFormat form = new MessageFormat("Are you sure you want to delete {0,choice,1#one item,1<{0,number.integer} files}?");

La ventaja de este enfoque es que funciona bien con los paquetes de i18n, y puede proporcionar traducciones correctamente para los idiomas (como el japonés) que no tienen ningún concepto de palabras en plural o singular.


¿Qué tal escribir función como

string GetOutputMessage(int count, string oneItemMsg, string multiItemMsg) { return string.Format("{0} {1}", count, count > 1 ? multiItemMsg : oneItemMsg); }

.. y usarlo cuando lo necesites?

string message = "You have selected " + GetOutputMessage(noofitemsselected,"item","items") + ". Are you sure you want to delete it/them?";


¿Qué tal solo?

string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?"

De esta forma, eliminas las dificultades de acuerdo con el número, y terminas con un mensaje de error aún más corto, más al punto para el usuario como una bonificación. Todos sabemos que los usuarios no leen los mensajes de error de todos modos . Cuanto más cortos sean, más probabilidades tendrán de mirar el texto.

O, armados con este conocimiento de que los usuarios no leen los mensajes de error, podrían abordarlo de otra manera. Omita por completo el mensaje de confirmación y solo proporcione una función de deshacer que funcione, independientemente de lo que se eliminó. La mayoría de los usuarios ya están acostumbrados a deshacer una operación cuando notan que no era lo que querían, y es probable que encuentren este comportamiento más natural que tener que lidiar con otra ventana emergente molesta.


¿Qué tal una forma más genérica? Evitar la pluralización en la segunda oración:

Number of selected items to be deleted: noofitemsselected. Are you sure?

Descubrí que hacerlo de esta manera pone el número al final de la línea que es realmente fácil de detectar. Esta solución funcionaría con la misma lógica en cualquier idioma.


Depende de cuán agradable sea el mensaje que quieres tener. De lo más fácil a lo más difícil:

  1. Vuelva a escribir su mensaje de error para evitar la pluralización. No es tan agradable para su usuario, pero es más rápido.

  2. Use un lenguaje más general pero aún incluya los números.

  3. Utilice un sistema de "pluralización" y de inflector ala Rails, para que pueda decir pluralize(5,''bunch'') y obtener 5 bunches . Rails tiene un buen patrón para esto.

  4. Para la internacionalización, necesita ver lo que proporciona Java. Eso admitirá una amplia variedad de idiomas, incluidos los que tienen diferentes formas de adjetivos con 2 o 3 elementos. La solución "s" está muy centrada en inglés.

La opción que elija depende de los objetivos de su producto. - ndp


Desde mi punto de vista, su primera solución es la más adecuada. Por qué digo que es así, en caso de que necesite que la aplicación sea compatible con varios idiomas, la segunda opción puede ser minuciosa. Con el primer enfoque, es fácil localizar el texto sin mucho esfuerzo.


Hazlo como está hecho en World of Warcraft:

BILLING_NAG_WARNING = "Your play time expires in %d |4minute:minutes;";


Lo primero que sugeriría es: use string.Format . Eso te permite hacer algo como esto:

int numOfItems = GetNumOfItems(); string msgTemplate; msgTemplate = numOfItems == 1 ? "You selected only {0} item." : "Wow, you selected {0} items!"; string msg = string.Format(msgTemplate, numOfItems);

Además, en las aplicaciones WPF, he visto sistemas en los que una cadena de recursos estaría delimitada por tuberías para tener dos mensajes: un mensaje singular y un mensaje plural (o un mensaje cero / único / muchos, incluso). Un convertidor personalizado podría usarse para analizar este recurso y usar la cadena relevante (formateada), por lo que su Xaml es algo como esto:

<TextBlock Text="{Binding numOfItems, Converter={StaticResource c:NumericMessageFormatter}, ConverterParameter={StaticResource s:SuitableMessageTemplate}}" />


Me gustaría ir sin codificar el mensaje, pero proporcionar dos mensajes en un archivo de recursos separado. Me gusta

string DELETE_SINGLE = "You have selected {0} item. Are you sure you want to delete it?"; string DELETE_MULTI = "You have selected {0} items. Are you sure you want to delete them?";

y luego alimentarlos en String.Format like

if(noofitemsselected == 1) messageTemplate = MessageResources.DELETE_SINGLE; else messageTemplate = MessageResources.DELETE_MULTI; string message = String.Format(messageTemplate, noofitemsselected)

Creo que este enfoque es más fácil de localizar y mantener. Todos los mensajes de IU se encontrarán en una única ubicación.


Me hizo exactamente esta misma pregunta ayer un miembro de nuestro equipo.

Dado que apareció de nuevo aquí en , pensé que el universo me decía que tuviera un golpe para producir una solución decente.

Rápidamente armé algo y de ninguna manera es perfecto, sin embargo, podría ser útil o provocar una discusión / desarrollo.

Este código se basa en la idea de que puede haber 3 mensajes. Uno para cero artículos, uno para uno y uno para más de un artículo que siguen la siguiente estructura:

singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural

Creé una clase interna para probar con el fin de imitar la clase de recursos. No he probado esto usando un archivo de recursos real todavía, así que aún no veo el resultado completo.

Aquí está el código (actualmente he incluido algunos genéricos donde sé que podría haber especificado el tercer parámetro simplemente como Tipo y también el segundo parámetro es una cadena, creo que hay una manera de combinar estos dos parámetros en algo mejor, pero yo '' Regresaré a eso cuando tenga un momento libre.

public static string GetMessage<T>(int count, string resourceSingularName, T resourceType) where T : Type { var resourcePluralName = resourceSingularName + "_Plural"; var resourceZeroName = resourceSingularName + "_Zero"; string resource = string.Empty; if(count == 0) { resource = resourceZeroName; } else{ resource = (count <= 1)? resourceSingularName : resourcePluralName; } var x = resourceType.GetProperty(resource).GetValue(Activator.CreateInstance(resourceType),null); return x.ToString(); }

Clase de recursos de prueba:

internal class TestMessenger { public string Tester{get{ return "Hello World of one";}} public string Tester_Zero{get{ return "Hello no world";}} public string Tester_Plural{get{ return "Hello Worlds";}} }

y mi método rápido de ejecución

void Main() { var message = GetMessage(56, "Tester",typeof(TestMessenger)); message.Dump(); }


Mi enfoque general es escribir una "función única / plural", como esta:

public static string noun(int n, string single, string plural) { if (n==1) return single; else return plural; }

Luego, en el cuerpo del mensaje, llamo a esta función:

string message="Congratulations! You have won "+n+" "+noun(n, "foobar", "foobars")+"!";

Esto no es mucho mejor, pero al menos, (a) pone la decisión en una función y así despeja el código un poco, y (b) es lo suficientemente flexible como para manejar plurales irregulares. es decir, es fácil decir sustantivo (n, "niño", "niños") y similares.

Por supuesto, esto solo funciona para el inglés, pero el concepto es fácilmente extensible a idiomas con terminaciones más complejas.

Se me ocurre que puede hacer que el último parámetro sea opcional para el caso fácil:

public static string noun(int n, string single, string plural=null) { if (n==1) return single; else if (plural==null) return single+"s"; else return plural; }


Para el primer problema, me refiero a Pluralizar, puedes usar Inflector .

Y para el segundo, puede usar una extensión de representación de cadena con un nombre como ToPronounString.


Para inglés, muchas respuestas arriba. Para otros idiomas es más difícil, ya que los plurales dependen del género del sustantivo y de la palabra que termina. Algunos ejemplos en francés:

Regular masculino:

Vous avez choisi 1 compte. Voulez-vous vraiment le supprimer. Vous avez choisi 2 comptes. Voulez-vous vraiment les supprimer.

Regular femenino

Vous avez choisi 1 table. Voulez-vous vraiment la supprimer. Vous avez choisi 2 tables. Voulez-vous vraiment les supprimer.

Irregular masculino (termina con ''s'')

Vous avez choisi 1 pays. Voulez-vous vraiment le supprimer. Vous avez choisi 2 pays. Voulez-vous vraiment les supprimer?

El mismo problema existe en la mayoría de los idiomas latinos y empeora en alemán o ruso, donde hay 3 géneros (maculine, femenino y neutro).

Tendrá que tener cuidado si su objetivo es manejar algo más que inglés.


Para poder tener mensajes pluralizados que sean posibles de localizar correctamente, mi opinión es que sería prudente crear primero una capa de indirección entre el número y un mensaje.

Por ejemplo, use una constante de algún tipo para especificar qué mensaje desea mostrar. Obtenga el mensaje usando alguna función que oculte los detalles de la implementación.

get_message(DELETE_WARNING, quantity)

A continuación, cree un diccionario que contenga los posibles mensajes y variaciones, y haga que las variaciones sepan cuándo se deben usar.

DELETE_WARNING = { 1: ''Are you sure you want to delete %s item'', >1: ''Are you sure you want to delete %s items'' >5: ''My language has special plural above five, do you wish to delete it?'' }

Ahora puede encontrar la clave que corresponde a la quantity e interpolar el valor de la quantity con ese mensaje.

Este ejemplo simplificado e ingenuo, pero realmente no veo otra manera sensata de hacerlo y poder brindar un buen soporte para L10N e I18N.


Pensaría de inmediato por un minuto, todas las sugerencias aquí son: haga la pluralización (y preocúpese por más de 1 nivel de pluralización, género, etc.) o no la use para nada y proporcione un buen deshacer.

Iría de la manera no lingual y usaría colas visuales para eso. por ejemplo, imagina una aplicación de Iphone para seleccionar elementos al limpiar tu dedo. antes de eliminarlos con el botón de eliminación maestra, "sacudirá" los elementos seleccionados y le mostrará un cuadro de interrogación titulado cuadro con los botones V (ok) o X (cancelar) ...

O bien, en el mundo 3D de Kinekt / Move / Wii: imagine seleccionar los archivos, mueva la mano hasta el botón Eliminar y diga que mueva la mano por encima de la cabeza para confirmar (utilizando los mismos símbolos visuales que mencioné anteriormente, por ejemplo, de pedirle que elimine 3 archivos, le mostrará 3 archivos con una X roja semicircular transparente y le indicará que haga algo para confirmar.


Puede buscar un mensaje más genérico como ''¿Está seguro de que desea eliminar los elementos seleccionados''?


Puede eludir el problema por completo al redactar el mensaje de manera diferente.

string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";


Puede evitar toda esta pluralidad desordenada simplemente borrando los elementos sin ningún mensaje y dándole al usuario una facilidad de Deshacer realmente buena. Los usuarios nunca leen nada . Deberías construir una buena instalación de Deshacer como parte de tu programa de todos modos.

En realidad, obtienes 2 beneficios cuando creas una instalación completa de Deshacer. El primer beneficio facilita la vida del usuario, ya que le permite revertir los errores y minimizar la lectura. El segundo beneficio es que su aplicación refleja la vida real al permitir la reversión de un flujo de trabajo no trivial (no solo errores).

Una vez escribí una aplicación sin usar un solo cuadro de diálogo o mensaje de confirmación. Tomó algunas reflexiones serias y fue significativamente más difícil de implementar que el uso de mensajes de confirmación. Pero el resultado final fue bastante agradable de usar de acuerdo con sus usuarios finales.


Puedes generar el plural automáticamente, ver por ej. generador plural

Para ver las reglas de generación en plural, ver wikipedia

string msg = "Do you want to delete " + numItems + GetPlural(" item", numItems) + "?";


Se pone un poco más corto con

string message = "Are you sure you want to delete " + noofitemsselected + " item" + (noofitemsselected>1 ? "s" : "") + "?";


Si existe alguna posibilidad, por pequeña que sea, de que esta aplicación deba traducirse a otros idiomas, ambas están equivocadas. La forma correcta de hacer esto es:

string message = ( noofitemsselected==1 ? "You have selected " + noofitemsselected + " item. Are you sure you want to delete it?": "You have selected " + noofitemsselected + " items. Are you sure you want to delete them?" );

Esto se debe a que diferentes lenguajes manejan la pluralidad de manera diferente. Algunos como el malayo ni siquiera tienen plurales sintácticos, por lo que las cuerdas generalmente serían idénticas. Al separar las dos cadenas, es más fácil admitir otros idiomas más adelante.

De lo contrario, si esta aplicación está destinada a ser consumida por el público en general y se supone que es fácil de usar, entonces el segundo método es preferible. Lo siento, pero realmente no conozco una forma más corta de hacer esto.

Si esta aplicación está destinada a ser consumida internamente por su empresa, entonces haga lo de "item(s)" acceso directo. En realidad no tiene que impresionar a nadie al escribir el código emprendedor. Pero desaconsejaría hacer esto para la aplicación de consumo público porque da la impresión de que el programador es flojo y, por lo tanto, disminuye su opinión sobre la calidad de la aplicación. Confía en mí, cosas pequeñas como esta cuestión.


Tendrá que traducir la función siguiente de VBA a C #, pero su uso cambiaría a:

int noofitemsselected = SomeFunction(); string message = Pluralize("You have selected # item[s]. Are you sure you want to delete [it/them]?", noofitemsselected);

Tengo una función de VBA que uso en MS Access para hacer exactamente lo que estás hablando. Sé que voy a ser pirateado para publicar VBA, pero aquí va de todos modos. El algoritmo debería ser evidente a partir de los comentarios:

''---------------------------------------------------------------------------------------'' '' Procedure : Pluralize'' '' Purpose : Formats an English phrase to make verbs agree in number.'' '' Usage : Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."'' '' Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each."'' '' Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."'' ''---------------------------------------------------------------------------------------'' '''' Function Pluralize(Text As String, Num As Variant, Optional NumToken As String = "#") Const OpeningBracket = "/[" Const ClosingBracket = "/]" Const DividingSlash = "/" Const CharGroup = "([^/]]*)" ''Group of 0 or more characters not equal to closing bracket'' Dim IsPlural As Boolean, Msg As String, Pattern As String On Error GoTo Err_Pluralize If IsNumeric(Num) Then IsPlural = (Num <> 1) End If Msg = Text ''Replace the number token with the actual number'' Msg = Replace(Msg, NumToken, Num) ''Replace [y/ies] style references'' Pattern = OpeningBracket & CharGroup & DividingSlash & CharGroup & ClosingBracket Msg = RegExReplace(Pattern, Msg, "$" & IIf(IsPlural, 2, 1)) ''Replace [s] style references'' Pattern = OpeningBracket & CharGroup & ClosingBracket Msg = RegExReplace(Pattern, Msg, IIf(IsPlural, "$1", "")) ''Return the modified message'' Pluralize = Msg End Function Function RegExReplace(SearchPattern As String, _ TextToSearch As String, _ ReplacePattern As String) As String Dim RE As Object Set RE = CreateObject("vbscript.regexp") With RE .MultiLine = False .Global = True .IgnoreCase = False .Pattern = SearchPattern End With RegExReplace = RE.Replace(TextToSearch, ReplacePattern) End Function

El uso se cortó un poco en los comentarios del código anterior, por lo que lo repetiré aquí:

Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each." Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each." Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."

Sí, esta solución ignora idiomas que no son inglés. Si eso importa depende de sus requisitos.


Un enfoque que no he visto mencionado sería el uso de una etiqueta de sustitución / selección (por ejemplo, algo así como "Estás a punto de aplastar {0} [? I ({0} = 1): / cactus / cacti /]". (en otras palabras, tenga una expresión de formato especifique la sustitución en función de si el argumento cero, tomado como un entero, es igual a 1). He visto tales etiquetas usadas en los días anteriores a .net; no conozco ninguna estándar para ellos en .net, ni sé la mejor manera de formatearlos.