i18n fails

Move %1$d messages to spam folder

A rather interesting plural string handling case.

Date Published

Vendor

Mozilla 2024 logoMozilla
From
English (United States)
English (United States)
Confirm move to spam folder
Do you really want to move this message to the spam folder?
To
Japanese
日本語
迷惑メールフォルダーに移動するか確認してください
本当にメッセージ %1$d 通を迷惑メールフォルダに移動しますか?

Do you really want to move %1$d messages to the spam folder?

确认移动到垃圾邮件文件夹

是否确定要移动 %1$d 封邮件到垃圾邮件文件夹?

An interesting issue where the engineers incorrectly assuming that all languages have distinct “singular” and “plural” forms. This “Move � messages to spam folder” message appears in two different places in the source code. One of them guarantees that the number of messages is always one. In this particular place, the engineer had the source code as follows (comment added):

1val message = resources.getQuantityString(
2 R.plurals.dialog_confirm_spam_message, // string ID,
3 1 // number to determine which string variant to use
4 // (omitted) number to fit in placeholder
5)

This is likely due to how the engineer sees the string from their content writer, and assumes that there won’t be any placeholder when there’s only one email.

Singular: Do you really want to move this message to the spam folder?
Plural: Do you really want to move � messages to the spam folder?

Notice how the singular string reads “this message,” not “1 message” or “� message”.

While the majority of languages have a distinct singular form compared to other non-singular forms, there are a number of languages that do not have plurality. Furthermore, some languages use the “singular” form not just on one, but also two, three, five, and a lot other numbers (See CLDR Language Plural Rules). In these languages, the number has to appear as a placeholder in the string variant for one-count.

When there is a placeholder in the string, but no value is provided to fit the placeholder, usually the i18n framework would just show the placeholder as is, which is why we are seeing this bug here.

The fix is simple, just fit the placeholder with 1 when generating the string. It would work with the strings that have a placeholder, and for those who don’t, nothing would change.

1val message = resources.getQuantityString(
2 R.plurals.dialog_confirm_spam_message, // string ID,
3 1, // number to determine which string variant to use
4 1 // number to fit in placeholder, if any
5)