From 96d4211dd7c186a864abee1c1a39ce3fbe41ed26 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Thu, 31 Oct 2024 20:53:20 +0100 Subject: [PATCH] Add ADR for "entry(s)" (#12127) * Replace "entry(ies)" by "entry(s)" * Add ADR-0040 * Fix typo * Update 0040-use-one-form-for-singular-and-plural.md Refine Option 1 --- docs/code-howtos/localization.md | 3 +- ...40-use-one-form-for-singular-and-plural.md | 95 +++++++++++++++++++ src/main/java/org/jabref/gui/LibraryTab.java | 6 +- .../externalfiles/DownloadFullTextAction.java | 2 +- src/main/resources/l10n/JabRef_en.properties | 6 +- 5 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 docs/decisions/0040-use-one-form-for-singular-and-plural.md diff --git a/docs/code-howtos/localization.md b/docs/code-howtos/localization.md index 568ad369529..7ef71486d2f 100644 --- a/docs/code-howtos/localization.md +++ b/docs/code-howtos/localization.md @@ -45,8 +45,9 @@ To write a localized string in FXML file, prepend it with `%`, like in this code ## General hints * Use the String you want to localize directly, do not use members or local variables: `Localization.lang("Translate me");` instead of `Localization.lang(someVariable)` (possibly in the form `someVariable = Localization.lang("Translate me")` -* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entries.", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entries.");` +* Use `%x`-variables where appropriate: `Localization.lang("Exported %0 entry(s).", number)` instead of `Localization.lang("Exported ") + number + Localization.lang(" entry(s).");` * Use a full stop/period (".") to end full sentences +* For pluralization, use a combined form. E.g., `Localization.lang("checked %0 entry(s)")`. ## Checking for correctness diff --git a/docs/decisions/0040-use-one-form-for-singular-and-plural.md b/docs/decisions/0040-use-one-form-for-singular-and-plural.md new file mode 100644 index 00000000000..d09007a5458 --- /dev/null +++ b/docs/decisions/0040-use-one-form-for-singular-and-plural.md @@ -0,0 +1,95 @@ +--- +nav_order: 40 +parent: Decision Records +--- + + +# Use one language string for pluralization localization + +## Context and Problem Statement + +For user-facing messages, sometimes, it needs to be counted: E.g., 1 entry updated, 2 entries updated, etc. + +In some languages, there is not only "one" and "more than one", but other forms: + +* zero → “لم نزرع أي شجرة حتى الآن” +* one → “لقد زرعنا شجرة ١ حتى الآن” +* two → “لقد زرعنا شجرتين ٢ حتى الآن” +* few → “لقد زرعنا ٣ شجرات حتى الآن” +* many → “لقد زرعنا ١١ شجرة حتى الآن” +* other → “لقد زرعنا ١٠٠ شجرة حتى الآن” + +(Example is from [Pluralization: A Guide to Localizing Plurals](https://phrase.com/blog/posts/pluralization/)) + +How to localize pluralization? + +## Decision Drivers + +* Good English language +* Good localization to other languages + +## Considered Options + +* Use one language string for pluralization (no explicit pluralization) +* Use singular and plural +* Handling of multiple forms + +## Decision Outcome + +Chosen option: "Use one form only (no explicit pluralization)", because it is the most easiest to handle in the code. + +## Pros and Cons of the Options + +### Use one language string for pluralization (no explicit pluralization) + +Example: + +- `Imported 0 entry(s)` +- `Imported 1 entry(s)` +- `Imported 12 entry(s)` + +There are sub alternatives here: + +* `Imported %0 entry(ies)`. +* `Number of entries imported: %0` (always use "other" plural form) + +These arguments are for the general case of using a single text for all kinds of numbers: + +* Good, because easy to handle in the code +* Bad, because reads strange in English UI + +### Use singular and plural + +Example: + +- `Imported 0 entries` +- `Imported 1 entry` +- `Imported 12 entries` + +* Good, because reads well in English +* Bad, because all localizations need to take an `if` check for the count +* Bad, because Arabic not localized properly + +### Handling of multiple forms + +Example: + +- `Imported 0 entries` +- `Imported 1 entry` +- `Imported 12 entries` + +Code: `Localization.lang("Imported %0 entries", "Imported %0 entry.", "Imported %0 entries.", "Imported %0 entries.", "Imported %0 entries.", "Imported %0 entries.", count)` + +* Good, because reads well in English +* Bad, because sophisticated localization handling is required +* Bad, because no Java library for handling pluralization is known +* Bad, because Arabic not localized properly + +## More Information + +- [Language Plural Rules](https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html) +- [Unicode CLDR Project's Plural Rules](https://cldr.unicode.org/index/cldr-spec/plural-rules) +- [Implementation in Mozilla Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) +- [SX discussion on plural forms](https://english.stackexchange.com/a/90283/66058) + + diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 49c417623b9..f7579709894 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -902,7 +902,7 @@ public void insertEntries(final List entries) { public void copyEntry() { int entriesCopied = doCopyEntry(getSelectedEntries()); if (entriesCopied >= 0) { - dialogService.notify(Localization.lang("Copied %0 entry(ies)", entriesCopied)); + dialogService.notify(Localization.lang("Copied %0 entry(s)", entriesCopied)); } else { dialogService.notify(Localization.lang("Copy failed", entriesCopied)); } @@ -966,7 +966,7 @@ public void cutEntry() { int entriesDeleted = doDeleteEntry(StandardActions.CUT, mainTable.getSelectedEntries()); if (entriesCopied == entriesDeleted) { - dialogService.notify(Localization.lang("Cut %0 entry(ies)", entriesCopied)); + dialogService.notify(Localization.lang("Cut %0 entry(s)", entriesCopied)); } else { dialogService.notify(Localization.lang("Cut failed", entriesCopied)); undoManager.undo(); @@ -979,7 +979,7 @@ public void cutEntry() { */ public void deleteEntry() { int entriesDeleted = doDeleteEntry(StandardActions.DELETE_ENTRY, mainTable.getSelectedEntries()); - dialogService.notify(Localization.lang("Deleted %0 entry(ies)", entriesDeleted)); + dialogService.notify(Localization.lang("Deleted %0 entry(s)", entriesDeleted)); } public void deleteEntry(BibEntry entry) { diff --git a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java index e476883b7f5..48d321b1d81 100644 --- a/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java +++ b/src/main/java/org/jabref/gui/externalfiles/DownloadFullTextAction.java @@ -25,7 +25,7 @@ import org.slf4j.LoggerFactory; /** - * Try to download fulltext PDF for selected entry(ies) by following URL or DOI link. + * Try to download fulltext PDF for selected entry(s) by following URL or DOI link. */ public class DownloadFullTextAction extends SimpleCommand { diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 8a8de5c18eb..2a4ab3bef81 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2769,9 +2769,9 @@ Pushing\ citations\ to\ TeXShop\ is\ only\ possible\ on\ macOS\!=Pushing citatio Single\ instance=Single instance -Copied\ %0\ entry(ies)=Copied %0 entry(ies) -Cut\ %0\ entry(ies)=Cut %0 entry(ies) -Deleted\ %0\ entry(ies)=Deleted %0 entry(ies) +Copied\ %0\ entry(s)=Copied %0 entry(s) +Cut\ %0\ entry(s)=Cut %0 entry(s) +Deleted\ %0\ entry(s)=Deleted %0 entry(s) Enable\ Journal\ Information\ Fetching?=Enable Journal Information Fetching? Would\ you\ like\ to\ enable\ fetching\ of\ journal\ information?\ This\ can\ be\ changed\ later\ in\ %0\ >\ %1.=Would you like to enable fetching of journal information? This can be changed later in %0 > %1.