i18n 复数形式

  1. 概述
  2. 示例
  3. 精确值
  4. 偏移量
  5. 列表

概述

大多数语言根据计数更改被计数单词的形式。例如,在英语中

You have 1 tree.
You have 2 trees.

其他语言有不同的规则

  • 在法语中,单数形式用于 0 和 1
  • 阿拉伯语除了默认形式外,还有 5 种特殊的复数形式
  • 有些语言根本没有复数形式

GWT 提供了一种方法,可以使用 Messages 接口根据运行时某个事物的计数选择不同的消息,并默认提供数百种语言的复数规则。

示例

首先,一个示例 Messages 接口

@DefaultLocale("en") // not required since this is the default
public interface MyMessages extends Messages {
  @DefaultMessage("There are {0,number} items in your cart.")
  @AlternateMessage({"one", "There is 1 item in your cart."})
  String cartItems(@PluralCount int itemCount);
}

请注意,控制使用哪种复数形式的参数用 @PluralCount 注释标记,并且默认语言(除非使用 @DefaultLocale 指定,为 en)的复数形式在 @AlternateMessage 注释中定义。如果您的默认语言不是英语,您可能会有不同的复数形式集。

假设您已将 添加 enfrar 区域设置到您的模块中。现在您需要为每个区域设置(除了 en,它将从注释中获取)提供翻译。注意:为了清晰起见,我在这些“翻译”中使用了英语 - 您实际上应该使用真正的翻译。

MyMessages_fr.properties

cartItems=There are {0,number} items in your cart.
cartItems[one]=There is {0,number} item in your cart.

请注意,法语中的 "one" 复数形式用于 0 和 1,因此您不能像英语那样在字符串中硬编码计数。

MyMessages_ar.properties

cartItems=There are {0,number} items in your cart.
cartItems[none]=There are no items in your cart.
cartItems[one]=There is one item in your cart.
cartItems[two]=There are two items in your cart.
cartItems[few]=There are {0,number} items in your cart, which are few.
cartItems[many]=There are {0,number} items in your cart, which are many.

GWT 使用的阿拉伯语复数规则是

  • none - 计数为 0
  • one - 计数为 1
  • two - 计数为 2
  • few - 最后两位数字在 03-10 之间
  • many - 最后两位数字在 11-99 之间
  • 对于其他所有内容,使用默认形式,即 101、202 等。

在翻译中表示复数形式的标准仍在不断发展。属性文件没有特别的支持,因此我们发明了 [_plural_form_] 语法来指定它们。希望随着时间的推移,这种情况会得到改善,我们可以支持更标准的方法来获取包含复数形式的翻译消息并将其返回到 GWT 中。

精确值

有时您需要提供特殊的消息,即使语言的语法不需要它。例如,一般来说,说 "You have no messages" 比说 "You have 0 messages" 更好。您可以使用复数形式 "=N" 来指定这一点,例如

public interface MyMessages extends Messages {
  @DefaultMessage("There are {0,number} items in your cart.")
  @AlternateMessage({
      "one", "There is 1 item in your cart.",
      "=0", "Your cart is empty."
  })
  String cartItems(@PluralCount int itemCount);
}

属性文件条目将如下所示

cartItems[\=0]=Your cart is empty.

请注意等号的转义,因为等号在属性文件中将键与值分开。

有关精确值的另一个用法,请参见下一项。

偏移量

在某些情况下,您可能希望在应用复数规则之前修改计数。例如,如果您要说是 "Bob, Joe, and 3 others ate pizza",那么您可能有一个包含 5 个人的列表。您可以专门编写代码减去该值并根据人数选择不同的消息,但这比将所有不同的消息放在一起要容易得多,并且更有可能获得更好的翻译。您可以这样做

public interface MyMessages extends Messages {
  @DefaultMessage("{1}, {2} and {0} others are here.")
  @AlternateMessage({
      "=0", "Nobody is here.",
      "=1", "{1} is  here.",
      "=2", "{1} and {2} are here.",
      "one", "{1}, {2}, and one other are here."
  })
  String peopleHere(@PluralCount @Offset(2) String[] names, String name1,
      String name2);
}

...

String[] names;
alert(peopleHere(names, names.length > 0 ? names[0] : null,
    names.length > 1 ? names[1] : null));

请注意,您可以为 @PluralCount 参数传递一个数组 - 其长度用于计数(java.util.List 实现的工作方式类似)。@Offset 注释表明在查找要使用的正确复数规则之前,应该应用提供的偏移量。但是请注意,精确值匹配在应用偏移量之前进行比较。因此,当计数为 0 时,将选择 "Nobody is here";如果计数为 3,将选择 "{1}, {2}, and one other are here",因为在查找要使用的复数形式之前,会从计数中减去 2。

顺便说一下,我们知道用这种方式传递名称有点笨拙。将来,我们将添加一种从占位符中引用列表/数组中元素的方法,您只需调用 peopleHere(names) 即可。

列表

这与复数略微脱节,但与它相关。GWT 支持格式化列表,使用适合区域设置的分隔符。例如

public interface MyMessages extends Messages {
  @DefaultMessage("Orders {0,list,number} are ready for pickup.")
  @AlternateMessage({
      "=0", "No orders are ready for pickup.",
      "one", "Order {0,list,number} is ready for pickup."
  })
  String ordersReady(@PluralCount List<Integer> orders);
}

格式说明符 {0,list,number} 表示参数 0 将被格式化为列表,每个元素将被格式化为数字。与它不是列表中的元素时一样,可以使用相同的格式选项,因此 {0,list,number:curcode=USD,currency} 也将起作用。如前所述,数组或 java.util.List 实例都可以正常工作,格式化类型的要求与它不是列表时相同。

在英语中,结果将是

  • ordersReady(Arrays.asList()) => "No orders are ready for pickup."
  • ordersReady(Arrays.asList(14)}) => "Order 14 is ready for pickup."
  • ordersReady(Arrays.asList(14, 17)) => "Orders 14 and 17 are ready for pickup."
  • ordersReady(Arrays.asList(14, 17, 21)) => "Orders 14, 17, and 21 are ready for pickup."

请注意,GWT 只了解语言使用的默认列表分隔符,并且虽然您可能希望说 "a, b, or c",但目前还没有方法可以表达这一点。