[web] Migrate manual plurals to i18next _plural convention (#33989)

* Add tests on plurals

* Update `collabs_per_proj` and its pluralisations

* Update `n_user` and its pluralisations

* Update `showing_x_results` and its pluralisations

* Update `show_x_more_projects` and its pluralisations

* `bin/run web npm run extract-translations`

* Populate `_plural` keys in non-en locales

For 2-form languages (da, de, es, fi, fr, it, nl, no, pt, sv, tr), copy
the existing bare-key value into the new `_plural` sibling to prevent
i18next from falling back to English for count!=1.

Also remove orphan singular keys (`collabs_per_proj_single`,
`showing_1_result*`) left over from the previous commits.

Bare-key values remain in their original plural form pending translator
review — count=1 will still render the plural form in non-en until
translators flip those to singular. Multi-form (cs, pl, ru) and
single-form (ja, ko, zh-CN, zh-TW) locales are unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Flip non-en bare-key values to singular form

Per review, the i18next v3 plural convention uses the bare key for count=1
(singular) and `_plural` for count!=1. The non-en bare-key values were
left as the original plural form by the previous commit so the `_plural`
siblings could be copied from them; this commit flips the bare values to
the singular form per language.

Languages where singular and plural noun forms coincide (Finnish, Swedish,
Turkish) are unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Apply suggestions from code review

Co-authored-by: Olzhas Askar <olzhas.askar@gmail.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Olzhas Askar <olzhas.askar@gmail.com>
GitOrigin-RevId: 628513ca792c2dcce023247e52b7320e2741cc54
This commit is contained in:
Antoine Clausse
2026-06-01 12:44:32 +02:00
committed by Copybot
parent 58884231c1
commit 0e4fe4090a
24 changed files with 104 additions and 71 deletions
@@ -321,9 +321,9 @@
"collaborator_chat": "",
"collabs_per_proj": "",
"collabs_per_proj_multiple": "",
"collabs_per_proj_plural": "",
"collabs_per_proj_plus": "",
"collabs_per_proj_single": "",
"collabs_per_proj_single_plus": "",
"collabs_per_proj_plus_plural": "",
"collapse": "",
"column_width": "",
"column_width_is_custom_click_to_resize": "",
@@ -1815,13 +1815,14 @@
"show_outline": "",
"show_version_history": "",
"show_x_more_projects": "",
"showing_1_result": "",
"showing_1_result_of_total": "",
"show_x_more_projects_plural": "",
"showing_pdf_preview_with_inverted_colors": "",
"showing_x_out_of_n_projects": "",
"showing_x_out_of_n_users": "",
"showing_x_results": "",
"showing_x_results_of_total": "",
"showing_x_results_of_total_plural": "",
"showing_x_results_plural": "",
"sidebar": "",
"sign_up": "",
"simple_search_mode": "",
@@ -191,9 +191,7 @@ export function CompileTimeoutPaywallModal({
<strong>{t('compile_timeout_modal_intro')}</strong>
</p>
<div className="compile-time-paywall-feature-list">
<ListItem>
{t('collabs_per_proj', { collabcount: 10 })}
</ListItem>
<ListItem>{t('collabs_per_proj', { count: 10 })}</ListItem>
<ListItem>{t('track_changes')}</ListItem>
<ListItem>{t('github_integration')}</ListItem>
<ListItem>{t('and_much_more')}</ListItem>
@@ -21,7 +21,7 @@ export default function LoadMore() {
className="project-list-load-more-button"
onClick={() => loadMoreProjects()}
>
{t('show_x_more_projects', { x: loadMoreCount })}
{t('show_x_more_projects', { count: loadMoreCount })}
</OLButton>
</>
) : null}
@@ -38,13 +38,7 @@ function GroupPlanCollaboratorCount({ planCode }: { planCode: string }) {
const { t } = useTranslation()
if (planCode === 'collaborator') {
return (
<>
{t('collabs_per_proj', {
collabcount: 10,
})}
</>
)
return <>{t('collabs_per_proj', { count: 10 })}</>
} else if (planCode === 'professional') {
return <>{t('unlimited_collabs')}</>
}
@@ -107,7 +107,7 @@ export function UpgradePrompt({
{t('24x_more_compile_time')}
</IconListItem>
<IconListItem icon="group_add">
{t('collabs_per_proj', { collabcount: 10 })}
{t('collabs_per_proj', { count: 10 })}
</IconListItem>
<IconListItem icon="history">
{t('unlimited_document_history')}
@@ -145,7 +145,7 @@ export function UpgradePrompt({
{t('basic_compile_time')}
</IconListItem>
<IconListItem icon="person">
{t('collabs_per_proj_single', { collabcount: 1 })}
{t('collabs_per_proj', { count: 1 })}
</IconListItem>
<IconListItem icon="history_off">
{t('limited_document_history')}
+2 -1
View File
@@ -43,7 +43,8 @@
"close": "Zavřít",
"collaboration": "Spolupráce",
"collaborator": "Collaborator",
"collabs_per_proj": "__collabcount__ spolupracovníků na projektu",
"collabs_per_proj": "__count__ spolupracovník na projektu",
"collabs_per_proj_plural": "__count__ spolupracovníků na projektu",
"comment": "Komentář",
"common": "Běžné",
"compiler": "Kompilátor",
+10 -8
View File
@@ -268,8 +268,8 @@
"collaboration": "Samarbejde",
"collaborator": "Samarbejdspartner",
"collabratec_account_not_registered": "IEEE Collabratec™ konto er ikke registeret. Forbind til Overleaf from IEEE Collabratec™ eller log ind med en anden konto.",
"collabs_per_proj": "__collabcount__ samarbejdspartnere per projekt",
"collabs_per_proj_single": "__collabcount__ samarbejdspartnere per projekt",
"collabs_per_proj": "__count__ samarbejdspartner per projekt",
"collabs_per_proj_plural": "__count__ samarbejdspartnere per projekt",
"collapse": "Fold sammen",
"column_width": "Kolonnebredde",
"column_width_is_custom_click_to_resize": "Kolonnebredden er brugerdefineret. Klik for at ændre",
@@ -1100,7 +1100,8 @@
"n_more_updates_above_plural": "__count__ flere opdateringer ovenover",
"n_more_updates_below": "__count__ mere opdatering nedenunder",
"n_more_updates_below_plural": "__count__ flere opdateringer nedenunder",
"n_users": "__userCount__ brugere",
"n_users": "__count__ bruger",
"n_users_plural": "__count__ brugere",
"name": "Navn",
"name_usage_explanation": "Dit navn vil blive vist til dine samarbejdspartnere (så de ved hvem de arbejder sammen med).",
"native": "Indbygget",
@@ -1643,12 +1644,13 @@
"show_local_file_contents": "Vis lokalt filindhold",
"show_more": "vis mere",
"show_outline": "Vis disposition",
"show_x_more_projects": "Vis __x__ flere projekter",
"showing_1_result": "Viser 1 resultat",
"showing_1_result_of_total": "Viser 1 resultat ud af __total__",
"show_x_more_projects": "Vis __count__ mere projekt",
"show_x_more_projects_plural": "Vis __count__ flere projekter",
"showing_x_out_of_n_projects": "Viser __x__ af __n__ projekter.",
"showing_x_results": "Viser __x__ resultater",
"showing_x_results_of_total": "Viser __x__ resultater ud af __total__",
"showing_x_results": "Viser __count__ resultat",
"showing_x_results_of_total": "Viser __count__ resultat ud af __total__",
"showing_x_results_of_total_plural": "Viser __count__ resultater ud af __total__",
"showing_x_results_plural": "Viser __count__ resultater",
"sign_up": "Opret konto",
"sign_up_for_free": "Opret gratis konto",
"sign_up_for_free_account": "Opret gratis konto og modtag regelmæssige opdateringer",
+8 -7
View File
@@ -328,8 +328,8 @@
"collaboration": "Zusammenarbeit",
"collaborator": "Mitarbeiter",
"collabratec_account_not_registered": "IEEE-Collabratec™-Konto nicht registriert. Bitte verbinde dich mit Overleaf von IEEE Collabratec™ oder melde dich mit einem anderen Konto an.",
"collabs_per_proj": "__collabcount__ Mitarbeiter pro Projekt",
"collabs_per_proj_single": "__collabcount__ Mitarbeiter pro Projekt",
"collabs_per_proj": "__count__ Mitarbeiter pro Projekt",
"collabs_per_proj_plural": "__count__ Mitarbeiter pro Projekt",
"collapse": "Einklappen",
"comment": "Kommentar",
"comment_only": "Nur Kommentare",
@@ -1360,12 +1360,13 @@
"show_less": "Weniger anzeigen",
"show_live_equation_previews_while_typing": "Live-Gleichungsvorschau beim Tippen anzeigen",
"show_outline": "Dateigliederung anzeigen",
"show_x_more_projects": "__x__ weitere Projekte anzeigen",
"showing_1_result": "1 Ergebnis wird angezeigt",
"showing_1_result_of_total": "Zeige 1 Ergebnis von __total__",
"show_x_more_projects": "__count__ weiteres Projekt anzeigen",
"show_x_more_projects_plural": "__count__ weitere Projekte anzeigen",
"showing_x_out_of_n_projects": "Es werden __x__ von __n__ Projekten angezeigt.",
"showing_x_results": "Es werden __x__ Ergebnisse angezeigt",
"showing_x_results_of_total": "Es werden __x__ Ergebnisse von __total__ angezeigt",
"showing_x_results": "Es wird __count__ Ergebnis angezeigt",
"showing_x_results_of_total": "Es wird __count__ Ergebnis von __total__ angezeigt",
"showing_x_results_of_total_plural": "Es werden __count__ Ergebnisse von __total__ angezeigt",
"showing_x_results_plural": "Es werden __count__ Ergebnisse angezeigt",
"single_sign_on_sso": "Single Sign-On (SSO)",
"site_description": "Ein einfach bedienbarer Online-LaTeX-Editor. Keine Installation notwendig, Zusammenarbeit in Echtzeit, Versionskontrolle, Hunderte von LaTeX-Vorlagen und mehr",
"skip_to_content": "Zum Inhalt springen",
+12 -11
View File
@@ -423,11 +423,11 @@
"collaborators_per_project": "Collaborators per project",
"collaborators_per_project_explanation": "The number of editors and reviewers you can invite to collaborate on a project with you. The limit is per project and you can invite different people to different projects.",
"collabratec_account_not_registered": "IEEE Collabratec™ account not registered. Please connect to Overleaf from IEEE Collabratec™ or log in with a different account.",
"collabs_per_proj": "__collabcount__ collaborators per project",
"collabs_per_proj": "__count__ collaborator per project",
"collabs_per_proj_multiple": "Multiple collaborators per project",
"collabs_per_proj_plus": "__collabcount__ collaborators per project, plus:",
"collabs_per_proj_single": "__collabcount__ collaborator per project",
"collabs_per_proj_single_plus": "__collabcount__ collaborator per project, plus:",
"collabs_per_proj_plural": "__count__ collaborators per project",
"collabs_per_proj_plus": "__count__ collaborator per project, plus:",
"collabs_per_proj_plus_plural": "__count__ collaborators per project, plus:",
"collapse": "Collapse",
"column_width": "Column width",
"column_width_is_custom_click_to_resize": "Column width is custom. Click to resize",
@@ -1611,7 +1611,8 @@
"n_more_updates_above_plural": "__count__ more updates above",
"n_more_updates_below": "__count__ more update below",
"n_more_updates_below_plural": "__count__ more updates below",
"n_users": "__userCount__ users",
"n_users": "__count__ user",
"n_users_plural": "__count__ users",
"name": "Name",
"name_usage_explanation": "Your name will be displayed to your collaborators (so they know who theyre working with).",
"native": "Native",
@@ -1727,7 +1728,6 @@
"one_free_collab": "One free collaborator",
"one_per_project": "1 per project",
"one_step_away_from_professional_features": "You are one step away from accessing <0>Overleaf premium features</0>!",
"one_user": "1 user",
"ongoing_experiments": "Ongoing experiments",
"online_latex_editor": "Online LaTeX Editor",
"only_add_people_who_dont_yet_have_access": "Only add people who dont yet have access.",
@@ -2360,14 +2360,15 @@
"show_more": "show more",
"show_outline": "Show File outline",
"show_version_history": "Show version history",
"show_x_more_projects": "Show __x__ more projects",
"showing_1_result": "Showing 1 result",
"showing_1_result_of_total": "Showing 1 result of __total__",
"show_x_more_projects": "Show __count__ more project",
"show_x_more_projects_plural": "Show __count__ more projects",
"showing_pdf_preview_with_inverted_colors": "Showing PDF preview with inverted colors",
"showing_x_out_of_n_projects": "Showing __x__ out of __n__ projects.",
"showing_x_out_of_n_users": "Showing __x__ out of __n__ users",
"showing_x_results": "Showing __x__ results",
"showing_x_results_of_total": "Showing __x__ results of __total__",
"showing_x_results": "Showing __count__ result",
"showing_x_results_of_total": "Showing __count__ result of __total__",
"showing_x_results_of_total_plural": "Showing __count__ results of __total__",
"showing_x_results_plural": "Showing __count__ results",
"sidebar": "Sidebar",
"sign_up": "Sign up",
"sign_up_for_free": "Sign up for free",
+2 -1
View File
@@ -218,7 +218,8 @@
"cn": "Chino (simplificado)",
"collaboration": "Colaboración",
"collaborator": "Colaborador",
"collabs_per_proj": "__collabcount__ colaboradores por proyecto",
"collabs_per_proj": "__count__ colaborador por proyecto",
"collabs_per_proj_plural": "__count__ colaboradores por proyecto",
"comment": "Comentar",
"common": "Común",
"compile_error_entry_description": "Un error ha impedido la compilación de este proyecto",
+2 -1
View File
@@ -45,7 +45,8 @@
"cn": "Kiina (Yksinkertainen)",
"collaboration": "Yhteistyö",
"collaborator": "Työtoveri",
"collabs_per_proj": "__collabcount__ työtoveria per projekti",
"collabs_per_proj": "__count__ työtoveri per projekti",
"collabs_per_proj_plural": "__count__ työtoveria per projekti",
"comment": "Kommentoi",
"common": "Yleisiä",
"compiler": "Kääntäjä",
+6 -6
View File
@@ -221,8 +221,8 @@
"collaboration": "Collaboration",
"collaborator": "Collaborateur·rice",
"collabratec_account_not_registered": "Pas de compte IEEE Collabratec™ enregistré. Veuillez vous connecter à Overleaf via IEEE Collabratec™ ou bien vous connecter avec un compte différent.",
"collabs_per_proj": "__collabcount__ collaborateur·rice·s par projet",
"collabs_per_proj_single": "__collabcount__ collaborateurs par projet",
"collabs_per_proj": "__count__ collaborateur·rice par projet",
"collabs_per_proj_plural": "__count__ collaborateur·rice·s par projet",
"collapse": "Replier",
"column_width": "Largeur de colonne",
"column_width_is_custom_click_to_resize": "La largeur des colonnes est personnalisée. Cliquez pour redimensionner",
@@ -984,11 +984,11 @@
"sharelatex_beta_program": "Programme de bêta __appName__",
"show_less": "voir moins",
"show_outline": "Afficher la structure du fichier",
"showing_1_result": "Affiche 1 résultat",
"showing_1_result_of_total": "Affiche 1 résultat sur __total__",
"showing_x_out_of_n_projects": "Affiche __x__ sur __n__ projets.",
"showing_x_results": "Affiche __x__ résultats",
"showing_x_results_of_total": "Affiche __x__ résultats sur __total__",
"showing_x_results": "Affiche __count__ résultat",
"showing_x_results_of_total": "Affiche __count__ résultat sur __total__",
"showing_x_results_of_total_plural": "Affiche __count__ résultats sur __total__",
"showing_x_results_plural": "Affiche __count__ résultats",
"sign_up": "Sinscrire",
"site_description": "Un éditeur LaTeX en ligne facile à utiliser. Pas dinstallation, collaboration en temps réel, gestion des versions, des centaines de modèles de documents LaTeX, et plus encore.",
"skip_to_content": "Aller au contenu",
+2 -1
View File
@@ -56,7 +56,8 @@
"cn": "Cinese (Semplificato)",
"collaboration": "Collaborazione",
"collaborator": "Collaboratore",
"collabs_per_proj": "__collabcount__ collaboratori per progetto",
"collabs_per_proj": "__count__ collaboratore per progetto",
"collabs_per_proj_plural": "__count__ collaboratori per progetto",
"comment": "Commento",
"common": "Comune",
"compile_larger_projects": "Compila progetti più grandi",
+1 -1
View File
@@ -75,7 +75,7 @@
"cn": "中国語(簡体字)",
"collaboration": "コラボレーション",
"collaborator": "共同編集者",
"collabs_per_proj": "プロジェクトあたりの __collabcount__ 共同編集者",
"collabs_per_proj": "プロジェクトあたりの __count__ 共同編集者",
"comment": "コメント",
"common": "共通",
"compile_larger_projects": "大きなプロジェクトをコンパイル",
+1 -1
View File
@@ -91,7 +91,7 @@
"code_check_failed_explanation": "자동 컴파일 실행 전에 에러를 수정해야합니다.",
"collaboration": "콜라보레이션",
"collaborator": "콜라보레이터",
"collabs_per_proj": "프로젝트 당 __collabcount__명까지 공유 가능",
"collabs_per_proj": "프로젝트 당 __count__명까지 공유 가능",
"comment": "댓글",
"common": "일반",
"compile_larger_projects": "큰 프로젝트 컴파일",
+2 -1
View File
@@ -91,7 +91,8 @@
"cn": "Chinees (vereenvoudigd)",
"collaboration": "Samenwerking",
"collaborator": "Bijdrager",
"collabs_per_proj": "__collabcount__ bijdragers per project",
"collabs_per_proj": "__count__ bijdrager per project",
"collabs_per_proj_plural": "__count__ bijdragers per project",
"comment": "Reageren",
"common": "Veelvoorkomend",
"compact": "Compact",
+2 -1
View File
@@ -57,7 +57,8 @@
"cn": "Kinesisk (Forenklet)",
"collaboration": "Samarbeid",
"collaborator": "Samarbeidspartner",
"collabs_per_proj": "__collabcount__ samarbeidspartnere per prosjekt",
"collabs_per_proj": "__count__ samarbeidspartner per prosjekt",
"collabs_per_proj_plural": "__count__ samarbeidspartnere per prosjekt",
"comment": "Kommenter",
"common": "Vanilige",
"compile_larger_projects": "Kompiler Større Prosjekter",
+2 -1
View File
@@ -30,7 +30,8 @@
"clearing": "Czyszczenie",
"click_here_to_view_sl_in_lng": "Kliknij tutaj, żeby używać __appName__ w <0>__lngName__</0>m",
"collaborator": "Współpracownik",
"collabs_per_proj": "__collabcount__ współpracowników na projekt",
"collabs_per_proj": "__count__ współpracownik na projekt",
"collabs_per_proj_plural": "__count__ współpracowników na projekt",
"comment": "Skomentuj",
"common": "Wspólne",
"compiler": "Kompilator",
+2 -1
View File
@@ -106,7 +106,8 @@
"code_check_failed_explanation": "Seu código contém erros que precisam ser corrigidos antes de rodar a auto-compilação",
"collaboration": "Colaboração",
"collaborator": "Colaborador",
"collabs_per_proj": "__collabcount__ colaboradores por projeto",
"collabs_per_proj": "__count__ colaborador por projeto",
"collabs_per_proj_plural": "__count__ colaboradores por projeto",
"comment": "Comentário",
"common": "Comum",
"compact": "Compacto",
+1 -1
View File
@@ -88,7 +88,7 @@
"cn": "Китайский (упрощённый)",
"collaboration": "Совместная разработка",
"collaborator": "Совместная работа",
"collabs_per_proj": "Максимальное число соавторов на проект: __collabcount__",
"collabs_per_proj": "Максимальное число соавторов на проект: __count__",
"comment": "Комментарии",
"common": "Общие",
"compile_larger_projects": "Компиляция больших проектов",
+2 -2
View File
@@ -139,8 +139,8 @@
"collaborate_online_and_offline": "Samarbeta online och offline, med ditt eget arbetsflöde",
"collaboration": "Samarbete",
"collaborator": "Samarbetare",
"collabs_per_proj": "__collabcount__ samarbetare per projekt",
"collabs_per_proj_single": "__collabcount__ medarbetare per projekt",
"collabs_per_proj": "__count__ samarbetare per projekt",
"collabs_per_proj_plural": "__count__ samarbetare per projekt",
"collapse": "Kontrahera",
"comment": "Kommentar",
"common": "Vanliga",
+2 -1
View File
@@ -60,7 +60,8 @@
"cn": "Çince (Basitleştirilmiş)",
"collaboration": "İş birliği",
"collaborator": "İş ortağı",
"collabs_per_proj": "her bir proje için __collabcount__ iş ortağı",
"collabs_per_proj": "her bir proje için __count__ iş ortağı",
"collabs_per_proj_plural": "her bir proje için __count__ iş ortağı",
"comment": "Yorumlar",
"common": "Belirli",
"compiler": "Derleyici",
+5 -8
View File
@@ -311,8 +311,7 @@
"collaborator": "合作者",
"collaborator_chat": "协作者聊天",
"collabratec_account_not_registered": "未注册 IEEE Collabratec™ 帐户。请从IEEE Collabratec™连接到Overleaf 或者使用其他帐户登录。",
"collabs_per_proj": "每个项目 __collabcount__ 个合作者",
"collabs_per_proj_single": "__collabcount__ 个合作者每个项目",
"collabs_per_proj": "每个项目 __count__ 个合作者",
"collapse": "合上",
"column_width": "列宽",
"column_width_is_custom_click_to_resize": "列宽为默认值,单击以调整大小",
@@ -1266,7 +1265,7 @@
"n_more_updates_above_plural": "__count__处更新在上方",
"n_more_updates_below": "__count__处更新在下方",
"n_more_updates_below_plural": "__count__处更新在下方",
"n_users": "__userCount__ 个用户",
"n_users": "__count__ 个用户",
"name": "名字",
"name_usage_explanation": "您的名字将显示给您的合作者(以便他们知道正在与谁合作)。",
"native": "本机",
@@ -1874,12 +1873,10 @@
"show_more": "显示更多",
"show_outline": "显示文件大纲",
"show_version_history": "显示版本历史记录",
"show_x_more_projects": "再显示 __x__ 个项目",
"showing_1_result": "显示 1 个结果",
"showing_1_result_of_total": "显示 1 个结果(共计 __total__ ",
"show_x_more_projects": "再显示 __count__ 个项目",
"showing_x_out_of_n_projects": "显示 __x__ 个项目(共 __n__ 个)",
"showing_x_results": "显示 __x__ 结果",
"showing_x_results_of_total": "显示 __x__ 个结果(共计__total__ ",
"showing_x_results": "显示 __count__ 结果",
"showing_x_results_of_total": "显示 __count__ 个结果(共计__total__ ",
"sign_up": "注册",
"sign_up_for_free": "免费注册",
"sign_up_for_free_account": "注册免费帐户并接收定期更新",
@@ -144,4 +144,34 @@ describe('i18n', function () {
.should('have.text', "<i>T</i>&<b>e</b>'s<code>t</code>ing")
})
})
describe('plurals', function () {
// See https://www.i18next.com/misc/json-format#i-18-next-json-v3
it('picks singular for count=1', function () {
const Test = () => {
const { t } = useTranslation()
return <div>{t('project_search_file_count', { count: 1 })}</div>
}
cy.mount(<Test />)
cy.findByText('in 1 file')
})
it('picks plural for count>1', function () {
const Test = () => {
const { t } = useTranslation()
return <div>{t('project_search_file_count', { count: 5 })}</div>
}
cy.mount(<Test />)
cy.findByText('in 5 files')
})
it('picks plural for count=0', function () {
const Test = () => {
const { t } = useTranslation()
return <div>{t('project_search_file_count', { count: 0 })}</div>
}
cy.mount(<Test />)
cy.findByText('in 0 files')
})
})
})