用户消息中的多元性

107

很多时候,当生成要向用户显示的消息时,该消息将包含一些内容,我希望告知客户。

我举个例子:客户从1个或更多项中选择了许多项目,并单击了删除。现在,我想给客户一个确认消息,并且我想提到他所选的项目数量,以最小化他选择了一堆项目并在只想删除其中一个项目时误点删除的可能性。

一种方法是将通用消息设计如下:

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

这里的“问题”出现在noofitemselected为1的情况下,我们需要写itemit而不是itemsthem

我通常的解决方案是这样的:

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

如果代码中有很多对数字复数的引用,那么生成类似这样的消息就会变得非常冗长和丑陋,并且实际消息会变得难以阅读。

所以我的问题很简单。是否有任何更好的方式来生成这样的消息?

编辑

我看到很多人都被我提到消息框中显示消息这个情境所困扰了,他们只是给出了如何避免使用消息框的答案,这也很好。

但请记住,复数问题也适用于程序中其他位置的文本。例如,显示在显示选定网格中的行数的标签旁边的网格中,将具有与复数化相关的相同问题。

因此,这基本上适用于从程序以某种方式输出的大多数文本,那么解决方案并不像只需更改程序以不再输出文本那么简单 :)


6
我不确定每种语言的复数形式是否像“item(s)”一样容易表达。 - Jens
5
你可能应该考虑本地化,这个问题可能会变得更加严重。 - Alex Brown
4
@Jens:通常它们不会这样做,而且方式也往往不太方便。有些语言例如在复数形式上会区分2-4和5-无限大的情况,并且这一切还要取决于性别。 "自然语言:本地化疯狂!即将登陆您身边的电脑!" - Piskvor left the building
29
对于程序员来说,没有什么比错误的复数形式更能让用户感觉他们懒惰了。 - Greg
4
@Øyvind:你的编辑做得很好。这里似乎有许多“回答者”更关注证明你的问题是错误的,而不是提供正确的解决方案。可以说是反解决方案。 - mwolfe02
显示剩余3条评论
25个回答

4
你可以自动生成复数形式,例如参考复数生成器
有关复数生成规则,请参见维基百科
string msg = "Do you want to delete " + numItems + GetPlural(" item", numItems) + "?";

如果应用程序没有国际化,我喜欢使用这种方法。复数形式会使应用程序更加精细。 - cspolton
1
这真的很难国际化,而且通常在英语中失败,例如:“You have orders + numMice + GetPlural(" mouse",numMice)"。 - James Anderson
@James Anderson:现在,从语法上讲,这并不是最好的例子,对吧;-)不要陷入认为使用GetPural方法是你唯一允许使用的方法的误区。 - cspolton
再加上一些处理更多边角情况的函数,我认为这是一个非常好的解决方案,虽然不太短。 - lalli

4

不妨采用更通用的方式。避免在第二句话中使用复数形式:

要删除的选定项目数量:noofitemsselected。确定吗?

我发现,这样做把数字放在行尾,非常容易发现。这个解决方案适用于任何语言。


“选定要删除的项目:”听起来不太对,这个句子表明了一个名称列表,但是却打印了一个数字。即使改为“要删除的项目数量:”,仍然感觉“items”这个词很别扭。 - lalli
@lalli:是的,措辞不太完美,提供的解决方案都不太完美。我只是建议将其以标签和值的形式呈现。你说得对,应该加上“Number”(当时我正在哄宝宝睡觉,回答有些匆忙)。 - Danosaure

4

国际化

我假设您需要国际化支持,因为不同的语言对于复数有不同的规则(例如,某些数量需要特殊的复数形式,或者像波兰语这样更加复杂的语言),您不能仅仅依靠一些简单的规则来修复字符串。

您可以使用GNU Gettext的ngettext函数,并在源代码中提供两个英文消息。Gettext将提供基础架构,以从翻译成其他语言时选择其他(可能更多)的消息。请参见http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html获取GNU gettext的复数支持的完整描述。

GNU Gettext属于LGPL许可证。在Gettext的C#端口中,ngettext命名为GettextResourceManager.GetPluralString

(如果您不需要本地化支持,并且暂时不想使用Gettext,则可以编写自己的函数来处理英文,然后将两个完整的消息传递给它,这样如果您以后需要本地化,可以通过重写单个函数来添加。)


4

我的通用方法是编写一个“单数/复数函数”,如下所示:

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

然后在邮件正文中调用此函数:

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

这并不完美,但至少它(a)将决策放在一个函数中,因此代码更加清晰,(b)足够灵活,可以处理不规则的复数形式。例如:很容易说出名词(n, "child", "children")等类似的形式。
当然,这仅适用于英语,但这个概念可以轻松扩展到具有更复杂结尾的语言。
我想到了一个简单的情况下可以使最后一个参数变成可选项的方法:
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;
}

1
我喜欢你最后一部分的做法,即在名词复数化的默认操作中使用默认值来“简化”代码。 - Øyvind Bråthen

3

对于第一个问题,即pluralize(复数形式),您可以使用Inflector

而对于第二个问题,您可以使用字符串表示扩展,例如ToPronounString。


3

昨天我们团队的一个成员问了我这个完全相同的问题。

既然在StackOverflow上又出现了这个问题,我想宇宙告诉我要尝试制作一个不错的解决方案。

我快速地写了一些代码,虽然并不完美,但可能会有用或引发一些讨论/开发。

此代码基于以下结构,可以有3个消息。零项、一项和多项,分别遵循以下结构:

singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural

我创建了一个内部类来测试,以模拟资源类。我还没有使用实际的资源文件进行测试,所以我还没有看到完整的结果。

这是代码(目前我已经包含了一些泛型,在我知道我可以简单地将第三个参数指定为类型时,我也可以将第二个参数合并为更好的东西,但当我有空闲时间时我会回来处理这个问题。

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

测试资源类:

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

同时我也拥有快速执行方法

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

如果你想让这个完整一些,你还应该为 singlePropertyName_All 添加一个消息,以使它更加明显。 - Danosaure

3
如何编写类似的函数?
string GetOutputMessage(int count, string oneItemMsg, string multiItemMsg)
{
 return string.Format("{0} {1}", count, count > 1 ? multiItemMsg : oneItemMsg);
}

...并在需要时使用它吗?

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

3
你没有考虑到这点,哈哈。你有一个函数来解决句子中的多个对象吗?然后你却把"它/它们"放在了句子结尾,哈哈。 - Barkermn01
你只需要把整个句子分成两条信息传递到这个函数中。 - Ken Bloom
这只是一个例子。我意识到发送整个句子的变体更加正确。 - Pavel Morshenyuk

2

从我的角度来看,你的第一个解决方案是最合适的。我这样说的原因是,如果你需要应用程序支持多种语言,第二个选项可能会很繁琐。使用第一种方法可以轻松本地化文本,不需要太多的努力。


2
您可以选择一个更加通用的消息,比如“您确定要删除所选的项目吗?”。

3
这个方法确实可行,但我认为提问者展示即将删除的项目数量的想法很好。不止一次,我试图从我的桌面删除一个文件,却意外地删除了更多的文件。 - Cody Gray

2

这取决于您希望拥有多好的消息。从最简单到最难:

  1. 重新编写错误消息以避免复数形式。对用户来说不太友好,但速度更快。

  2. 使用更通用的语言,但仍包括数字。

  3. 使用类似Rails的“复数”和词形变化系统,这样您就可以使用pluralize(5,'bunch')并获得5 bunches。Rails为此提供了很好的模式。

  4. 对于国际化,您需要查看Java提供的内容。它将支持各种语言,包括那些具有2或3个项目的形容词不同形式的语言。 “s”解决方案非常依赖英语。

您选择哪个选项取决于您的产品目标。- ndp


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接