diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json
index 067e085364..35e852c086 100644
--- a/services/web/frontend/extracted-translations.json
+++ b/services/web/frontend/extracted-translations.json
@@ -117,6 +117,7 @@
"after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "",
"aggregate_changed": "",
"aggregate_to": "",
+ "agpl_licence": "",
"agree": "",
"agree_with_the_terms": "",
"ai_assist_in_overleaf_is_included_via_writefull_groups": "",
@@ -228,6 +229,7 @@
"breadcrumbs": "",
"browser": "",
"build_collection_of_most_used_references": "",
+ "built_on": "",
"bullet_list": "",
"buy_licenses": "",
"buy_more_licenses": "",
@@ -1909,6 +1911,7 @@
"sort_by": "",
"sort_by_x": "",
"sort_projects": "",
+ "source_code": "",
"speak": "",
"speech_input_not_available": "",
"spellcheck": "",
diff --git a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx
index 57a9505610..f3759217d2 100644
--- a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx
+++ b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx
@@ -99,9 +99,11 @@ const ProjectCard = memo(function ProjectCard({
}: {
project: Project
}) {
+ const { t } = useTranslation()
const { tags } = useProjectListContext()
const variant = getFormatVariant(project.compiler, project.quartoFlavor)
- const ownerName = getOwnerName(project)
+ const rawOwner = getOwnerName(project)
+ const ownerName = rawOwner === 'You' ? t('you') : rawOwner
const date = fromNowDate(project.lastUpdated)
const initial = project.name.charAt(0).toUpperCase() || '?'
const projectTags = tags
diff --git a/services/web/frontend/js/shared/components/footer/thin-footer.tsx b/services/web/frontend/js/shared/components/footer/thin-footer.tsx
index 8d44310238..414c9d3ad1 100644
--- a/services/web/frontend/js/shared/components/footer/thin-footer.tsx
+++ b/services/web/frontend/js/shared/components/footer/thin-footer.tsx
@@ -5,6 +5,7 @@ import type {
import OLRow from '@/shared/components/ol/ol-row'
import LanguagePicker from '@/shared/components/language-picker'
import React from 'react'
+import { useTranslation } from 'react-i18next'
function FooterItemLi({
text,
@@ -41,6 +42,7 @@ function Separator() {
}
function ThinFooter({ subdomainLang, leftItems, rightItems }: FooterMetadata) {
+ const { t } = useTranslation()
const showLanguagePicker = Boolean(
subdomainLang && Object.keys(subdomainLang).length > 1
)
@@ -62,7 +64,7 @@ function ThinFooter({ subdomainLang, leftItems, rightItems }: FooterMetadata) {
- Built on{' '}
+ {t('built_on')}{' '}
- AGPL licence
+ {t('agpl_licence')}
@@ -100,7 +102,7 @@ function ThinFooter({ subdomainLang, leftItems, rightItems }: FooterMetadata) {
target="_blank"
rel="noopener noreferrer"
>
- Source code
+ {t('source_code')}
{rightItems?.map(item => (
diff --git a/services/web/frontend/js/utils/dates.ts b/services/web/frontend/js/utils/dates.ts
index b4dd84f822..000c24bd40 100644
--- a/services/web/frontend/js/utils/dates.ts
+++ b/services/web/frontend/js/utils/dates.ts
@@ -1,4 +1,14 @@
import moment from 'moment'
+import getMeta from '@/utils/meta'
+
+// Set moment's display locale to match the app language so that
+// fromNow() returns "il y a 2 jours" rather than "2 days ago", etc.
+const _lang = getMeta('ol-i18n')?.currentLangCode ?? 'en'
+if (_lang !== 'en') {
+ import(`moment/locale/${_lang}`)
+ .then(() => { moment.locale(_lang) })
+ .catch(() => { /* fall back to English */ })
+}
export function formatDate(date: moment.MomentInput, format?: string) {
if (!date) return 'N/A'
diff --git a/services/web/locales/de.json b/services/web/locales/de.json
index a9559c5784..f5b6da741b 100644
--- a/services/web/locales/de.json
+++ b/services/web/locales/de.json
@@ -146,6 +146,7 @@
"after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "Danach berechnen wir dir jährlich am __date__ __totalAmount__ (__subtotalAmount__ + __taxAmount__ Steuern), falls du nicht kündigst.",
"aggregate_changed": "Geändert",
"aggregate_to": "zu",
+ "agpl_licence": "AGPL-Lizenz",
"agree": "Zustimmen",
"agree_with_the_terms": "Ich stimme den Verso Bedingungen zu",
"ai_allowance": "KI-Zulage",
@@ -311,6 +312,7 @@
"browser": "Browser",
"build_collection_of_most_used_references": "Erstellen Sie in der Bibliothek eine Sammlung Ihrer am häufigsten verwendeten Referenzen, damit Sie sie problemlos zu jedem Projekt hinzufügen können.",
"built_in": "Eigener",
+ "built_on": "Basiert auf",
"bullet_list": "Bullet-Liste",
"buy_add_on": "Add-on kaufen",
"buy_licenses": "Lizenzen kaufen",
@@ -2495,6 +2497,7 @@
"sort_by_x": "Nach __x__ sortieren",
"sort_projects": "Projekte sortieren",
"source": "Quelldateien",
+ "source_code": "Quellcode",
"speak": "Sprich",
"speech_input_not_available": "Die Spracheingabe ist in diesem Browser noch nicht verfügbar",
"spellcheck": "Rechtschreibprüfung",
diff --git a/services/web/locales/en.json b/services/web/locales/en.json
index 48f4a9cd85..9d7751afcf 100644
--- a/services/web/locales/en.json
+++ b/services/web/locales/en.json
@@ -143,6 +143,7 @@
"after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "After that, we’ll bill you __totalAmount__ (__subtotalAmount__ + __taxAmount__ tax) annually on __date__, unless you cancel.",
"aggregate_changed": "Changed",
"aggregate_to": "to",
+ "agpl_licence": "AGPL licence",
"agree": "Agree",
"agree_with_the_terms": "I agree with the Verso terms",
"ai_allowance": "AI allowance",
@@ -305,6 +306,7 @@
"browser": "Browser",
"build_collection_of_most_used_references": "Build a collection of your most-used references in the Library, so you can easily add them to any project.",
"built_in": "Built-In",
+ "built_on": "Built on",
"bullet_list": "Bullet list",
"buy_add_on": "Buy add-on",
"buy_licenses": "Buy licenses",
@@ -2474,6 +2476,7 @@
"sort_by_x": "Sort by __x__",
"sort_projects": "Sort projects",
"source": "Source",
+ "source_code": "Source code",
"speak": "Speak",
"speech_input_not_available": "Speech input is not yet available in this browser",
"spellcheck": "Spellcheck",
diff --git a/services/web/locales/es.json b/services/web/locales/es.json
index 3c507bf6ab..6abbf0984c 100644
--- a/services/web/locales/es.json
+++ b/services/web/locales/es.json
@@ -143,6 +143,7 @@
"after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "Después de eso, le facturaremos __totalAmount__ (__subtotalAmount__ + __taxAmount__ impuestos) anualmente el __date__, a menos que cancele.",
"aggregate_changed": "Cambiado",
"aggregate_to": "a",
+ "agpl_licence": "Licencia AGPL",
"agree": "De acuerdo",
"agree_with_the_terms": "Estoy de acuerdo con los términos Verso",
"ai_allowance": "subsidio de IA",
@@ -306,6 +307,7 @@
"browser": "Navegador",
"build_collection_of_most_used_references": "Cree una colección de sus referencias más utilizadas en la Biblioteca, para que pueda agregarlas fácilmente a cualquier proyecto.",
"built_in": "Integrado",
+ "built_on": "Desarrollado con",
"bullet_list": "lista de viñetas",
"buy_add_on": "Comprar complemento",
"buy_licenses": "Comprar licencias",
@@ -2475,6 +2477,7 @@
"sort_by_x": "Ordenar por __x__",
"sort_projects": "Ordenar proyectos",
"source": "Fuente",
+ "source_code": "Código fuente",
"speak": "hablar",
"speech_input_not_available": "La entrada de voz aún no está disponible en este navegador",
"spellcheck": "corrector ortográfico",
diff --git a/services/web/locales/fr.json b/services/web/locales/fr.json
index c27b6c9019..6d355b346d 100644
--- a/services/web/locales/fr.json
+++ b/services/web/locales/fr.json
@@ -143,6 +143,7 @@
"after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "Après cela, nous vous facturerons __totalAmount__ (__subtotalAmount__ + __taxAmount__ taxe) annuellement le __date__, sauf si vous annulez.",
"aggregate_changed": "Modification de",
"aggregate_to": "en",
+ "agpl_licence": "Licence AGPL",
"agree": "D'accord",
"agree_with_the_terms": "J'accepte les conditions d'utilisation de Verso",
"ai_allowance": "Allocation IA",
@@ -306,6 +307,7 @@
"browser": "Navigateur",
"build_collection_of_most_used_references": "Créez une collection de vos références les plus utilisées dans la bibliothèque, afin de pouvoir les ajouter facilement à n'importe quel projet.",
"built_in": "Intégré",
+ "built_on": "Construit sur",
"bullet_list": "Liste à puces",
"buy_add_on": "Acheter un module complémentaire",
"buy_licenses": "Acheter des licences",
@@ -2479,6 +2481,7 @@
"sort_by_x": "Trier par __x__",
"sort_projects": "Trier les projets",
"source": "Code source",
+ "source_code": "Code source",
"speak": "Parler",
"speech_input_not_available": "La saisie vocale n'est pas encore disponible dans ce navigateur",
"spellcheck": "Vérification orthographique",
diff --git a/services/web/locales/it.json b/services/web/locales/it.json
index a0ab6914ed..41674df02a 100644
--- a/services/web/locales/it.json
+++ b/services/web/locales/it.json
@@ -143,6 +143,7 @@
"after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "Successivamente, ti addebiteremo __totalAmount__ (__subtotalAmount__ + __taxAmount__ tasse) annualmente il __date__, a meno che tu non annulli l'abbonamento.",
"aggregate_changed": "Cambiato",
"aggregate_to": "a",
+ "agpl_licence": "Licenza AGPL",
"agree": "D'accordo",
"agree_with_the_terms": "Sono d'accordo con i termini di Verso",
"ai_allowance": "Indennità AI",
@@ -305,6 +306,7 @@
"browser": "Navigatore",
"build_collection_of_most_used_references": "Crea una raccolta dei riferimenti più utilizzati nella Libreria, in modo da poterli aggiungere facilmente a qualsiasi progetto.",
"built_in": "Built-In",
+ "built_on": "Basato su",
"bullet_list": "Elenco puntato",
"buy_add_on": "Acquista il componente aggiuntivo",
"buy_licenses": "Acquista licenze",
@@ -2474,6 +2476,7 @@
"sort_by_x": "Ordina per __x__",
"sort_projects": "Ordina progetti",
"source": "Sorgente",
+ "source_code": "Codice sorgente",
"speak": "Parla",
"speech_input_not_available": "L'input vocale non è ancora disponibile in questo browser",
"spellcheck": "Controllo ortografico",