c# - Pluralità nei messaggi degli utenti




string localization (17)

Internazionalizzazione

Presumo che tu voglia supporto per l'internazionalizzazione, nel qual caso linguaggi diversi hanno schemi diversi per i plurali (ad esempio una forma plurale speciale per 2 di qualcosa, o linguaggi più complicati come il polacco), e non puoi fare affidamento sull'applicazione di alcuni semplici schemi alla tua stringa per risolverlo.

È possibile utilizzare la funzione ngettext GNU Gettext e fornire due messaggi in inglese nel codice sorgente. Gettext fornirà l'infrastruttura per scegliere da altri (potenzialmente più) messaggi quando tradotti in altre lingue. Vedi http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html per una descrizione completa del supporto plurale di GNU gettext.

GNU Gettext è sotto la LGPL. ngettext è denominato GettextResourceManager.GetPluralString nella porta C # di Gettext.

(Se non hai bisogno del supporto per la localizzazione e non vuoi usare subito Gettext, scrivi la tua funzione che lo fa per l'inglese e passa due messaggi completi , in questo modo se hai bisogno di l10n più tardi, puoi aggiungi riscrivendo una singola funzione.)

Molte volte, quando si generano messaggi da mostrare all'utente, il messaggio conterrà un numero di qualcosa di cui voglio informare il cliente.

Darò un esempio: il cliente ha selezionato un numero di articoli da 1 in su e ha fatto clic su delete. Ora voglio dare un messaggio di conferma al cliente, e voglio menzionare il numero di elementi che ha selezionato per minimizzare le possibilità che lui commetta un errore selezionando un gruppo di elementi e facendo clic su Elimina quando vuole solo cancellare uno di loro.

Un modo è rendere il messaggio generico in questo modo:

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

Il "problema" qui è il caso in cui noofitemselected è 1, e dobbiamo scrivere l' articolo e questo invece degli oggetti e loro .

La mia soluzione normale sarà qualcosa di simile

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

Questo diventa piuttosto lungo e abbastanza sgradevole molto velocemente se ci sono molti riferimenti al numero di numeri all'interno del codice, e il messaggio reale diventa difficile da leggere.

Quindi le mie domande sono semplicemente. Ci sono modi migliori per generare messaggi come questo?

MODIFICARE

Vedo che un sacco di persone si è appisolato nel caso in cui ho menzionato che il messaggio dovrebbe essere visualizzato all'interno di una finestra di messaggio, e ha semplicemente dato una risposta su come evitare di usare la finestra del messaggio, e questo è tutto ok .

Ma ricorda che il problema della pluralizzazione si applica anche ai testi in altri punti del programma oltre alle finestre di messaggio. Ad esempio, un'etichetta accanto a una griglia che mostra il numero di linee selezionate nella griglia avrà lo stesso problema per quanto riguarda la pluralizzazione.

Quindi questo si applica fondamentalmente alla maggior parte del testo che viene emesso in qualche modo dai programmi, e quindi la soluzione non è così semplice da cambiare semplicemente il programma per non produrre più testo :)


È possibile evitare completamente il problema formando il messaggio in modo diverso.

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

Che ne dici di scrivere funzioni come

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

.. e usalo quando ti serve?

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

Che ne dici di un modo più generico. Evita la pluralizzazione nella seconda frase:

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

Scopro che farlo in questo modo mette il numero alla fine della linea che è davvero facile da individuare. Questa soluzione funzionerebbe con la stessa logica in qualsiasi lingua.


Dal mio punto di vista, la tua prima soluzione è la più adatta. Perché dico che, nel caso tu abbia bisogno dell'applicazione per supportare più lingue, la seconda opzione può essere scrupolosa. Con il primo approccio è facile localizzare il testo senza troppi sforzi.


Dipendo da quanto è bello un messaggio che vuoi avere. Dal più semplice al più difficile:

  1. Riscrivi il tuo messaggio di errore per evitare la pluralizzazione. Non è bello per il tuo utente, ma più veloce.

  2. Usa un linguaggio più generale ma includi ancora il numero (i).

  3. Usa una "pluralizzazione" e un sistema di innalzamento ala Rails, quindi puoi dire pluralize(5,'bunch') e ottenere 5 bunches . Rails ha un buon modello per questo.

  4. Per l'internazionalizzazione, è necessario considerare cosa fornisce Java. Ciò supporterà un'ampia varietà di lingue, comprese quelle che hanno diverse forme di aggettivi con 2 o 3 elementi. La soluzione "s" è molto centrata sull'inglese.

L'opzione che scegli dipende dagli obiettivi del tuo prodotto. - ndp


Dovrai tradurre la funzione qui sotto da VBA a C #, ma il tuo utilizzo cambierà in:

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

Ho una funzione VBA che uso in MS Access per fare esattamente quello di cui stai parlando. So che verrò fatto a pezzi per aver pubblicato VBA, ma qui va comunque. L'algoritmo dovrebbe essere evidente dai commenti:

'---------------------------------------------------------------------------------------'
' 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

L'uso è stato tagliato un po 'nei commenti del codice sopra, quindi lo ripeterò qui:

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ì, questa soluzione ignora le lingue che non sono inglesi. Se ciò che conta dipende dalle tue esigenze.


Fai come se fosse fatto in World of Warcraft:

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

Il mio approccio generale è scrivere una "funzione singolo / plurale", come questa:

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

Quindi nel corpo del messaggio chiamo questa funzione:

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

Questo non è molto meglio, ma almeno, (a) mette la decisione in una funzione e quindi disimpegna un po 'il codice, e (b) è abbastanza flessibile da gestire i plurali irregolari. cioè è abbastanza facile dire sostantivo (n, "bambino", "figli") e simili.

Naturalmente questo funziona solo per l'inglese, ma il concetto è facilmente estensibile alle lingue con terminazioni più complesse.

Mi viene in mente che potresti rendere l'ultimo parametro opzionale per il caso facile:

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

La prima cosa che suggerirei è: usare string.Format . Questo ti permette di fare qualcosa del genere:

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

Inoltre, nelle app WPF, ho visto sistemi in cui una stringa di risorse sarebbe delimitata da pipe per avere due messaggi: un messaggio singolare e plurale (o un messaggio zero / singolo / molti, pari). Quindi è possibile utilizzare un convertitore personalizzato per analizzare questa risorsa e utilizzare la stringa rilevante (formattata), quindi il tuo Xaml è qualcosa del genere:

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

Per il primo problema, intendo Pluralize, puoi usare Inflector .

E per il secondo, puoi usare un'estensione per la rappresentazione delle stringhe con un nome come ToPronounString.


Per l'inglese, molte risposte sopra. Per altri linguaggi è più difficile, poiché i plurali dipendono dal genere del nome e dalla parola che termina. Alcuni esempi in francese:

Maschile regolare:

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

Regolare femminile

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

Maschile irregolare (finisce con "s")

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

Lo stesso problema esiste nella maggior parte delle lingue latine e peggiora in tedesco o russo, dove ci sono 3 generi (macrogeno, femminile e neutro).

Avrai bisogno di fare attenzione se il tuo obiettivo è di gestire più che solo l'inglese.


Potresti scegliere un messaggio più generico come "Sei sicuro di voler eliminare gli elementi selezionati".


Puoi evitare tutto questo disordinato pluralismo semplicemente eliminando gli elementi senza alcun messaggio e dando all'utente una funzionalità di annullamento davvero buona. Gli utenti non leggono mai nulla . Dovresti comunque costruire una buona funzione di annullamento come parte del tuo programma.

In realtà ottieni 2 vantaggi quando crei una funzione di annullamento completa. Il primo vantaggio semplifica la vita dell'utente consentendogli di invertire gli errori e ridurre al minimo la lettura. Il secondo vantaggio è che la tua app riflette la vita reale consentendo l'inversione del flusso di lavoro non banale (non solo errori).

Una volta ho scritto un'app senza usare una singola finestra di dialogo o un messaggio di conferma. Ci sono voluti alcuni pensieri seri ed è stato molto più difficile da implementare rispetto all'utilizzo di messaggi di tipo conferma. Ma il risultato finale è stato piuttosto piacevole da usare in base ai suoi utenti finali.


Se c'è sempre qualche possibilità, non importa quanto piccola, che questa app debba essere tradotta in altre lingue, allora entrambi sono sbagliati. Il modo corretto per farlo è:

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

Questo perché lingue diverse gestiscono diversamente la pluralità. Alcuni come il malese non hanno nemmeno plurali sintattici, quindi le stringhe sarebbero generalmente identiche. Separare le due stringhe facilita il supporto di altre lingue in seguito.

Altrimenti, se questa app è pensata per essere consumata dal grande pubblico e dovrebbe essere di facile utilizzo, è preferibile il secondo metodo. Scusa ma non so davvero un modo più breve per farlo.

Se questa app è pensata per essere consumata solo internamente dalla tua azienda, allora fai la scorciatoia "item(s)" cosa. Non devi davvero impressionare nessuno quando scrivi codice enterprisy. Ma sconsiglio di farlo per l'app pubblicamente consumata perché questo dà l'impressione che il programmatore sia pigro e quindi riduce la sua opinione sulla qualità dell'app. Fidati di me, piccole cose come questa.


Un approccio che non ho visto menzionato sarebbe l'uso di un tag di sostituzione / selezione (ad es. Qualcosa come "Stai per schiacciare {0} [? I ({0} = 1): / cactus / cactus /]". (in altre parole, avere un'espressione di formato specificare la sostituzione in base all'argomento se l'argomento zero, preso come numero intero, è uguale a 1). Ho visto tali tag utilizzati nei giorni precedenti .net; Non sono a conoscenza di alcun standard per loro in .net, né conosco il modo migliore per formattarli.


Vorrei pensare fuori dagli schemi per un minuto, tutti i suggerimenti qui sono o pluralizzano (e preoccupati di più di 1 livello di pluralizzazione, genere, ecc.) O non lo usano affatto e forniscono un bel annullamento.

Andrei in modo non linguale e userei code visive per questo. Ad esempio, immagina un'app Iphone per selezionare gli elementi pulendo il dito. prima di eliminarli usando il pulsante di cancellazione principale, "scuoterà" gli elementi selezionati e mostrerà una casella con un punto interrogativo con i pulsanti V (ok) o X (cancella) ...

Oppure, nel mondo 3D di Kinekt / Move / Wii - immagina di selezionare i file, sposta la tua mano sul pulsante di cancellazione e ti viene detto di muovere la mano sopra la testa per confermare (usando gli stessi simboli visivi che ho citato prima. di chiederti di cancellare 3 file? ti mostrerà 3 file con una X rossa passante per metà trasparente e ti dirò di fare qualcosa per confermare.





pluralize