Xử lý số nhiều trong Android là "không"


83

Nếu có nguồn ressource số nhiều sau đây trong string.xml của tôi:

   <plurals name="item_shop">
        <item quantity="zero">No item</item>
        <item quantity="one">One item</item>
        <item quantity="other">%d items</item>
   </plurals>   

Tôi đang hiển thị kết quả cho người dùng bằng cách sử dụng:

textView.setText(getQuantityString(R.plurals.item_shop, quantity, quantity));

Nó hoạt động tốt với 1 trở lên, nhưng nếu số lượng là 0 thì tôi thấy "0 mặt hàng". Giá trị "không" chỉ được hỗ trợ bằng ngôn ngữ Ả Rập như tài liệu đã chỉ ra? Hay tôi đang thiếu cái gì đó?


21
Sự cố này được báo cáo tại đây code.google.com/p/android/issues/detail?id=8287 . Vẫn chưa được sửa: /
OcuS

5
Tôi không nghĩ đó là lỗi: Lưu ý rằng việc lựa chọn được thực hiện dựa trên sự cần thiết về ngữ pháp. Một chuỗi cho số 0 trong tiếng Anh sẽ bị bỏ qua ngay cả khi số lượng là 0, vì 0 không khác về mặt ngữ pháp với 2 hoặc bất kỳ số nào khác ngoại trừ 1 ("zero books", "one book", "two books", v.v. trên)
ByteMe

1
Đáng buồn là không phải tất cả ngôn ngữ là giống hệt nhau để tiếng anh
Armand

2
Tôi nghĩ rằng đây là một lỗi theo nghĩa, ngoài ngữ pháp, 99% số lần tôi cần sử dụng nó, tôi cần chức năng bị thiếu . Vì vậy, vâng, tôi tin rằng đây là một lỗi mà Google có thể sẽ không bao giờ sửa. Do đó, cần có một giải pháp khác. Bạn thấy đấy, các API ở đó để giúp người tiêu dùng hoàn thành công việc chứ không phải để đại diện độc quyền cho các khái niệm trừu tượng từ thế giới thực. Nếu Zero đã được hỗ trợ ngay lập tức, cả hai nhóm (những người cần đúng ngữ pháp và những người không), có thể thoát ra mà không cần phải sử dụng thứ gì đó khác.
Martin Marconcini

Câu trả lời:


90

Phương pháp quốc tế hóa tài nguyên Android khá hạn chế. Tôi đã thành công tốt hơn nhiều khi sử dụng tiêu chuẩn java.text.MessageFormat.

Về cơ bản, tất cả những gì bạn phải làm là sử dụng tài nguyên chuỗi tiêu chuẩn như sau:

<resources>
    <string name="item_shop">{0,choice,0#No items|1#One item|1&lt;{0} items}</string>
</resources>

Sau đó, từ mã tất cả những gì bạn phải làm như sau:

String fmt = getResources().getText(R.string.item_shop).toString();
textView.setText(MessageFormat.format(fmt, amount));

Bạn có thể đọc thêm về các chuỗi định dạng trong javadocs cho MessageFormat


7
Cách giải quyết tốt. Tôi có thể tưởng tượng ngay bây giờ những người dịch các ứng dụng của tôi sẽ đưa vào các tệp của tôi loại lộn xộn nào ... :)
OcuS

1
Làm cách nào để tạo chuỗi định dạng cho các số "vài" (các số kết thúc bằng 2,3,4 nhưng không kết thúc bằng 12,13,14)?
vokilam

3
Chà, thật là xấu xí. Logic như thế này nên đi trong mã, không phải bản dịch. Số nhiều chỉ được cho là giúp giải quyết sự khác biệt về ngôn ngữ cho các con số. Với đề xuất cho "vài", giờ đây bạn đã chuyển thành công logic thành bản dịch và bản dịch thành logic.
DennisK

2
Đó chắc chắn không phải là một cách giải quyết tốt. Chỉ cần thêm một chuỗi cho no_items và một mệnh đề if trước đó. If (items.count == 0) {sử dụng chuỗi} else {sử dụng số nhiều} và sau đó cho phép các dịch đối phó với các vấn đề trong file strings.xml ...
Martin Marconcini

2
@ MartínMarconcini bạn là chính xác ... hơn 3 năm sau :-)
GreyBeardedGeek

47

Từ http://developer.android.com/guide/topics/resources/string-resource.html#Plurals :

Lưu ý rằng lựa chọn được thực hiện dựa trên sự cần thiết về ngữ pháp. Một chuỗi cho số 0 trong tiếng Anh sẽ bị bỏ qua ngay cả khi số lượng là 0, vì 0 không khác về mặt ngữ pháp với 2 hoặc bất kỳ số nào khác ngoại trừ 1 ("zero books", "one book", "two books", v.v. trên). Cũng đừng nhầm lẫn bởi thực tế rằng, có hai âm thanh như nó chỉ có thể áp dụng cho số lượng 2: một ngôn ngữ có thể yêu cầu rằng 2, 12, 102 (v.v.) đều được đối xử như nhau nhưng khác với ngôn ngữ khác số lượng. Dựa vào người phiên dịch của bạn để biết ngôn ngữ của họ thực sự nhấn mạnh những điểm khác biệt nào.

Tóm lại, 'số không' chỉ được sử dụng cho một số ngôn ngữ nhất định (tương tự đối với 'hai' 'một số', v.v.) vì các ngôn ngữ khác không có cách chia đặc biệt và do đó trường 'không' được coi là không cần thiết


4
"Một chuỗi cho số 0 trong tiếng Anh sẽ bị bỏ qua ngay cả khi số lượng là 0, vì 0 không khác về mặt ngữ pháp với 2" ... thế còn "Tôi không có trái cây" so với "Tôi có một quả táo và một quả cam" thì sao?
Jeffrey Blattman

4
Có rất nhiều cách khác nhau để từ 0 vs 2. Nhưng ý tưởng đằng sau những tài nguyên này là sử dụng chúng với một con số. Đại loại như: Tôi có 0 quả táo. Tôi có 1 quả táo. Tôi có 2 quả táo.
ByteMe

4
tệ quá. họ không nên cố gắng đưa ra các giả định về ngôn ngữ cụ thể. mọi người tôi biết đó là số nhiều được sử dụng đã bị nhầm lẫn bởi điều này.
Jeffrey Blattman

2
@racle2k: Tin nhắn không được dịch theo danh mục, vì vậy bất kỳ danh mục hoặc tin nhắn nào bạn có bằng một ngôn ngữ đều không ảnh hưởng đến ngôn ngữ khác. Vì vậy, nếu bạn có thông báo lớp 'không' bằng tiếng Anh chỉ cho số 0 và một ngôn ngữ khác đang sử dụng lớp 'không' cho số đếm 10.100, điều đó không quan trọng vì bạn không dịch thông báo lớp '0'. Mặt khác: nếu bạn tạo một tài nguyên thư riêng cho số 0 bằng tiếng Anh, thì bạn đang tạo ra vấn đề về quy trình làm việc cho người dịch vì trường hợp đó đã được bao phủ bởi thông báo lớp 'không' ban đầu trong ngôn ngữ của họ.
Basel Shishani

2
Đúng là việc bật danh mục "không" trong tiếng Anh như một tính năng tiện lợi có thể được thực hiện, nhưng nó không "sạch" và chỉ khuyến khích hành vi xấu. Bạn có thể có ý tưởng đặt văn bản khuyến khích người dùng thêm các mục vào chuỗi "không" - sau đó có thể không dịch được. Nói một cách chính xác, ví dụ của OP ("Không có mục") đã cho thấy vấn đề đó, bởi vì người dịch có thể muốn cụm từ viết hoa bằng 0 bằng cách sử dụng các từ. Đó không phải là vấn đề về quy trình làm việc nếu bạn coi "% s item" là phiên bản số ít của "% s items" và "No items" là một thứ hoàn toàn khác về mặt ngôn ngữ.
magic2k

15

Đây là một giải pháp mà tôi đang sử dụng để xử lý sự cố này mà không cần chuyển sang MessageFormat.

Đầu tiên, tôi trích xuất chuỗi "số không" thành tài nguyên chuỗi của riêng nó.

<string name="x_items_zero">No items.</string>
<plurals name="x_items">
    <!-- NOTE: This "zero" value is never accessed but is kept here to show the intended usage of the "zero" string -->
    <item quantity="zero">@string/x_items_zero</item>
    <item quantity="one">One item.</item>
    <item quantity="other">%d items.</item>
</plurals>

Sau đó, tôi có một số phương pháp tiện lợi trong ResourcesUtil của riêng tôi

public static String getQuantityStringZero(Resources resources, int resId, int zeroResId, int quantity) {
    if (quantity == 0) {
        return resources.getString(zeroResId);
    } else {
        return resources.getQuantityString(resId, quantity, quantity);
    }
}

public static String getQuantityStringZero(Resources resources, int resId, int zeroResId, int quantity, Object... formatArgs) {
    if (quantity == 0) {
        return resources.getString(zeroResId);
    } else {
        return resources.getQuantityString(resId, quantity, formatArgs);
    }
}

Bây giờ bất cứ lúc nào tôi muốn sử dụng một chuỗi cụ thể cho số lượng 0, tôi gọi:

String pluralString = ResourcesUtil.getQuantityStringZero(
     getContext().getResources(),
     R.plural.x_items,
     R.string.x_items_zero,
     quantity
);

Tôi ước có điều gì đó tốt hơn nhưng điều này ít nhất cũng hoàn thành công việc trong khi vẫn giữ cho tài nguyên chuỗi XML dễ đọc.


Sử dụng điều này, sẽ không bao giờ thực sự được sử dụng "<item amount =" zero ">"?
nhà phát triển android

4
Đúng '<item amount = "zero">' sẽ không được sử dụng. Tôi có nó ở đó vì tôi nghĩ nó giúp làm rõ mục đích sử dụng của giá trị đó cho số không.
Justin Fiedler

Cũng nghĩ vậy. Nó có thể giúp người dịch hiểu được những gì đang xảy ra.
nhà phát triển android

13

Android đang sử dụng hệ thống số nhiều CLDR và ​​đây không phải là cách nó hoạt động (vì vậy đừng mong đợi điều này sẽ thay đổi).

Hệ thống được mô tả ở đây:

http://cldr.unicode.org/index/cldr-spec/plural-rules

Tóm lại, điều quan trọng là phải hiểu rằng "một" không có nghĩa là số 1. Thay vào đó, các từ khóa này là các danh mục và các số cụ thể n thuộc từng danh mục được xác định bởi các quy tắc trong cơ sở dữ liệu CLDR:

http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html

Mặc dù dường như không có ngôn ngữ nào sử dụng "0" cho bất kỳ ngôn ngữ nào khác 0, nhưng vẫn có những ngôn ngữ gán 0 cho "một". Chắc chắn có rất nhiều trường hợp "hai" chứa các số khác không chỉ 2.

Nếu Android cho phép bạn làm những gì bạn dự định, các ứng dụng của bạn sẽ không thể được dịch đúng cách sang bất kỳ ngôn ngữ nào có quy tắc số nhiều phức tạp hơn.


5
Hãy cẩn thận khi đăng bản sao và dán câu trả lời mẫu / nguyên văn cho nhiều câu hỏi, những câu hỏi này có xu hướng bị cộng đồng gắn cờ là "spam". Nếu bạn đang làm điều này thì điều đó thường có nghĩa là các câu hỏi trùng lặp vì vậy hãy gắn cờ cho chúng như vậy thay vào đó: stackoverflow.com/questions/8473816
Kev

3

Tôi đã viết một tiện ích mở rộng Kotlin để xử lý tất cả các tình huống mà tôi có thể nghĩ đến.

Tôi có zeroResId tùy chọn, vì vậy đôi khi chúng tôi muốn xử lý số 0 bằng cách hiển thị "Không có Mục", thay vì "0 Mục".

Tiếng Anh coi số không về mặt ngữ pháp là số nhiều.

Việc lựa chọn chuỗi nào để sử dụng chỉ được thực hiện dựa trên sự cần thiết về ngữ pháp.

Trong tiếng Anh, một chuỗi cho số 0 bị bỏ qua ngay cả khi số lượng là 0, bởi vì số 0 không khác về mặt ngữ pháp với 2 hoặc bất kỳ số nào khác ngoại trừ 1 ("không cuốn sách", "một cuốn sách", "hai cuốn sách", v.v. trên).

https://developer.android.com/guide/topics/resources/string-resource.html#Plurals

fun Context.getQuantityStringZero(
    quantity: Int, 
    pluralResId: Int, 
    zeroResId: Int? = null
): String {
    return if (zeroResId != null && quantity == 0) {
        resources.getString(zeroResId)
    } else {
        resources.getQuantityString(pluralResId, quantity, quantity)
    }
}

2

Nếu bạn đang sử dụng liên kết dữ liệu, bạn có thể giải quyết vấn đề này bằng một số thứ như:

<TextView ... 
android:text="@{collection.size() > 0 ? @plurals/plural_str(collection.size(), collection.size()) : @string/zero_str}"/>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.