Plurality in user messages

前端 未结 25 1506
没有蜡笔的小新
没有蜡笔的小新 2021-01-30 01:53

Many times, when generating messages to show to the user, the message will contain a number of something that I want to inform the customer about.

I\'ll give a

相关标签:
25条回答
  • 2021-01-30 02:43

    My general approach is to write a "single/plural function", like this:

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

    Then in the body of the message I call this function:

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

    This isn't a whole lot better, but at least it, (a) puts the decision in a function and so unclutters the code a little, and (b) is flexible enough to handle irregular plurals. i.e. it's easy enough to say noun(n, "child", "children") and the like.

    Of course this only works for English, but the concept is readily extensible to languages with more complex endings.

    It occurs to me that you could make the last parameter optional for the easy case:

    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;
    }
    
    0 讨论(0)
  • 2021-01-30 02:43

    From my point of view, your first solution is the most suited one. Why I say that is, in case you need the application to support multiple languages, the second option can be painstaking. With the fist approach it is easy to localize the text without much effort.

    0 讨论(0)
  • 2021-01-30 02:44

    You can sidestep the issue entirely by phrasing the message differently.

    string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";
    
    0 讨论(0)
  • 2021-01-30 02:46

    You'll have to translate the function below from VBA to C#, but your usage would change to:

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

    I have a VBA function that I use in MS Access to do exactly what you are talking about. I know I'll get hacked to pieces for posting VBA, but here goes anyway. The algorithm should be apparent from the comments:

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

    The usage got cut off a bit in the code comments above, so I'll repeat it here:

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

    Yes, this solution ignores languages that are not English. Whether that matters depends on your requirements.

    0 讨论(0)
  • 2021-01-30 02:47

    The first thing I'd suggest is: use string.Format. That allows you to do something like this:

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

    Further, in WPF apps, I've seen systems where a resource string would be pipe-delimited to have two messages: a singular and a plural message (or a zero/single/many message, even). A custom converter could then be used to parse this resource and use the relevant (formatted) string, so your Xaml is something like this:

    <TextBlock Text="{Binding numOfItems, Converter={StaticResource c:NumericMessageFormatter}, ConverterParameter={StaticResource s:SuitableMessageTemplate}}" />
    
    0 讨论(0)
  • 2021-01-30 02:47

    You could go for a more generic message like 'Are you sure you want to delete the selected item(s)'.

    0 讨论(0)
提交回复
热议问题