From 337f0040011cc98933f98c1b2e1dd680e337c415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 27 Feb 2026 14:20:58 -0500 Subject: [PATCH 001/204] add missing center --- src/views/admin/partials/dashboard/graph.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/admin/partials/dashboard/graph.tpl b/src/views/admin/partials/dashboard/graph.tpl index e6eb91ff29..cc009d12b9 100644 --- a/src/views/admin/partials/dashboard/graph.tpl +++ b/src/views/admin/partials/dashboard/graph.tpl @@ -3,7 +3,7 @@
{graphTitle} -
+
+ + + +
+

+ [[admin/settings/general:screenshot.help]] +

+

From e3ba38f2a230f3a710cc84ba4e8ce0e7d1361467 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Thu, 12 Mar 2026 18:28:58 +0000 Subject: [PATCH 140/204] chore(i18n): fallback strings for new resources: nodebb.admin-settings-general --- public/language/ar/admin/settings/general.json | 4 +++- public/language/az/admin/settings/general.json | 4 +++- public/language/bg/admin/settings/general.json | 4 +++- public/language/bn/admin/settings/general.json | 4 +++- public/language/cs/admin/settings/general.json | 4 +++- public/language/da/admin/settings/general.json | 4 +++- public/language/de/admin/settings/general.json | 4 +++- public/language/el/admin/settings/general.json | 4 +++- public/language/en-US/admin/settings/general.json | 4 +++- public/language/en-x-pirate/admin/settings/general.json | 4 +++- public/language/es/admin/settings/general.json | 4 +++- public/language/et/admin/settings/general.json | 4 +++- public/language/fa-IR/admin/settings/general.json | 4 +++- public/language/fi/admin/settings/general.json | 4 +++- public/language/fr/admin/settings/general.json | 4 +++- public/language/gl/admin/settings/general.json | 4 +++- public/language/he/admin/settings/general.json | 4 +++- public/language/hr/admin/settings/general.json | 4 +++- public/language/hu/admin/settings/general.json | 4 +++- public/language/hy/admin/settings/general.json | 4 +++- public/language/id/admin/settings/general.json | 4 +++- public/language/it/admin/settings/general.json | 4 +++- public/language/ja/admin/settings/general.json | 4 +++- public/language/ko/admin/settings/general.json | 4 +++- public/language/lt/admin/settings/general.json | 4 +++- public/language/lv/admin/settings/general.json | 4 +++- public/language/ms/admin/settings/general.json | 4 +++- public/language/nb/admin/settings/general.json | 4 +++- public/language/nl/admin/settings/general.json | 4 +++- public/language/nn-NO/admin/settings/general.json | 4 +++- public/language/pl/admin/settings/general.json | 4 +++- public/language/pt-BR/admin/settings/general.json | 4 +++- public/language/pt-PT/admin/settings/general.json | 4 +++- public/language/ro/admin/settings/general.json | 4 +++- public/language/ru/admin/settings/general.json | 4 +++- public/language/rw/admin/settings/general.json | 4 +++- public/language/sc/admin/settings/general.json | 4 +++- public/language/sk/admin/settings/general.json | 4 +++- public/language/sl/admin/settings/general.json | 4 +++- public/language/sq-AL/admin/settings/general.json | 4 +++- public/language/sr/admin/settings/general.json | 4 +++- public/language/sv/admin/settings/general.json | 4 +++- public/language/th/admin/settings/general.json | 4 +++- public/language/tr/admin/settings/general.json | 4 +++- public/language/uk/admin/settings/general.json | 4 +++- public/language/ur/admin/settings/general.json | 4 +++- public/language/vi/admin/settings/general.json | 4 +++- public/language/zh-CN/admin/settings/general.json | 4 +++- public/language/zh-TW/admin/settings/general.json | 4 +++- 49 files changed, 147 insertions(+), 49 deletions(-) diff --git a/public/language/ar/admin/settings/general.json b/public/language/ar/admin/settings/general.json index 43e7f5a38c..ad5fba74ae 100644 --- a/public/language/ar/admin/settings/general.json +++ b/public/language/ar/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "الكلمات الدليله للموقع", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "صورة", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "رفع", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/az/admin/settings/general.json b/public/language/az/admin/settings/general.json index 6747b5b431..ec84fda492 100644 --- a/public/language/az/admin/settings/general.json +++ b/public/language/az/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Sayt təsviri", "keywords": "Saytın açar sözləri", "keywords-placeholder": "İcmanızı təsvir edən açar sözlər, vergüllə ayrılmış", - "logo-and-icons": "Saytın loqosu və ikonaları", + "logo-and-icons": "Media & Branding", "logo.image": "Şəkil", "logo.image-placeholder": "Forumun başlığında göstəriləcək loqoya gedən yol", "logo.upload": "Yüklə", @@ -35,6 +35,8 @@ "touch-icon.help": "Tövsiyə olunan ölçü və format: 512x512, yalnız PNG formatı. Əgər toxunma ikonu göstərilməyibsə, NodeBB favikondan istifadə etməyə qayıdacaq.", "maskable-icon": "Maskalana bilən (Ev ekranı) ikonu", "maskable-icon.help": "Tövsiyə olunan ölçü və format: 512x512, yalnız PNG formatı. Əgər maskalana bilən ikona göstərilməyibsə, NodeBB yenidən Touch Icon-a düşəcək.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Çıxış linklər", "outgoing-links.warning-page": "Gedən linklər xəbərdarlıq səhifəsindən istifadə edin", "search": "Axtarış", diff --git a/public/language/bg/admin/settings/general.json b/public/language/bg/admin/settings/general.json index 9c84b5413d..433f5d7ceb 100644 --- a/public/language/bg/admin/settings/general.json +++ b/public/language/bg/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Описание на уеб сайта", "keywords": "Ключови думи на уеб сайта", "keywords-placeholder": "Ключови думи, описващи общността Ви. Трябва да бъдат разделени със запетаи.", - "logo-and-icons": "Лого и иконки на уеб сайта", + "logo-and-icons": "Media & Branding", "logo.image": "Изображение", "logo.image-placeholder": "Път до логото, което да бъде показано в заглавната част на форума", "logo.upload": "Качване", @@ -35,6 +35,8 @@ "touch-icon.help": "Препоръчителен размер и формат: 512x512, само във формат „PNG“. Ако не е посочена иконка за сензорен екран, NodeBB ще използва иконката на уеб сайта.", "maskable-icon": "Маскируема иконка (за начален екран)", "maskable-icon.help": "Препоръчителен размер и формат: 512x512, само във формат „PNG“. Ако не е посочена маскируема иконка, NodeBB ще използва иконката за сензорен екран.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Изходящи връзки", "outgoing-links.warning-page": "Показване на предупредителна страница при щракване върху външни връзки", "search": "Търсене", diff --git a/public/language/bn/admin/settings/general.json b/public/language/bn/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/bn/admin/settings/general.json +++ b/public/language/bn/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/cs/admin/settings/general.json b/public/language/cs/admin/settings/general.json index 3a35e73236..a5dcb3ef1e 100644 --- a/public/language/cs/admin/settings/general.json +++ b/public/language/cs/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Popis stránky", "keywords": "Klíčová slova pro stránky", "keywords-placeholder": "Klíčová slova popisující vaši komunitu, odděleno čárkou", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Obrázek", "logo.image-placeholder": "Cesta k logu, aby mohlo být zobrazeno v hlavičce fóra", "logo.upload": "Nahrát", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Odchozí odkazy", "outgoing-links.warning-page": "Použít stránku s upozorněním při odchozích odkazech", "search": "Search", diff --git a/public/language/da/admin/settings/general.json b/public/language/da/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/da/admin/settings/general.json +++ b/public/language/da/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/de/admin/settings/general.json b/public/language/de/admin/settings/general.json index 975e47f559..278b7fbf52 100644 --- a/public/language/de/admin/settings/general.json +++ b/public/language/de/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Seitenbeschreibung", "keywords": "Forum Schlüsselworte", "keywords-placeholder": "Schlüsselworte, die ihre Community beschreiben, mit Komma getrennt", - "logo-and-icons": "Website-Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Bild", "logo.image-placeholder": "Pfad zu einem Logo, welches im Header des Forums angezeigt werden soll", "logo.upload": "Hochladen", @@ -35,6 +35,8 @@ "touch-icon.help": "Empfohlene Größe und Format: 512x512, nur PNG-Format. Wenn kein Touch-Symbol angegeben wird, verwendet NodeBB wieder das Favicon.", "maskable-icon": "Maskierbares (Start-Bildschirm) Symbol", "maskable-icon.help": "Empfohlene Größe und Format: 512x512, nur PNG-Format. Wenn kein maskierbares Icon angegeben wird, greift NodeBB auf das Touch-Symbol zurück.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Ausgehende Links", "outgoing-links.warning-page": "Warnseite für ausgehende links verwenden", "search": "Suche", diff --git a/public/language/el/admin/settings/general.json b/public/language/el/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/el/admin/settings/general.json +++ b/public/language/el/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/en-US/admin/settings/general.json b/public/language/en-US/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/en-US/admin/settings/general.json +++ b/public/language/en-US/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/en-x-pirate/admin/settings/general.json b/public/language/en-x-pirate/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/en-x-pirate/admin/settings/general.json +++ b/public/language/en-x-pirate/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/es/admin/settings/general.json b/public/language/es/admin/settings/general.json index 09a6c8295f..e76f554aae 100644 --- a/public/language/es/admin/settings/general.json +++ b/public/language/es/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Palabras Clave (keywords) del Sitio", "keywords-placeholder": "Palabras Clave (keywords) que describen tu comunidad, separadas por comas", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Imagen", "logo.image-placeholder": "Ruta al logo que se mostrará en la cabecera del foro", "logo.upload": "Subir", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Enlaces a sitios externos", "outgoing-links.warning-page": "Usar Página de Advertencia para Enlaces a Sitios Externos", "search": "Search", diff --git a/public/language/et/admin/settings/general.json b/public/language/et/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/et/admin/settings/general.json +++ b/public/language/et/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/fa-IR/admin/settings/general.json b/public/language/fa-IR/admin/settings/general.json index e8b37f08e5..c401841a15 100644 --- a/public/language/fa-IR/admin/settings/general.json +++ b/public/language/fa-IR/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "جستجو", diff --git a/public/language/fi/admin/settings/general.json b/public/language/fi/admin/settings/general.json index 3f71acedf1..dc341d2e0c 100644 --- a/public/language/fi/admin/settings/general.json +++ b/public/language/fi/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Sivuston avainsanat", "keywords-placeholder": "Yhteisöäsi kuvaavat avainsanat pilkuin eroteltuina.", - "logo-and-icons": "Sivuston logo ja kuvakkeet", + "logo-and-icons": "Media & Branding", "logo.image": "Kuva", "logo.image-placeholder": "Foorumin otsakkeessa näytettävän logon sijainti.", "logo.upload": "Lataa", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Peitetttävä (aloitussivun) kuvake", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/fr/admin/settings/general.json b/public/language/fr/admin/settings/general.json index 6da6d17130..2122df070a 100644 --- a/public/language/fr/admin/settings/general.json +++ b/public/language/fr/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Description du site", "keywords": "Mots-clés du site", "keywords-placeholder": "Mots-clés décrivant votre communauté, séparés par des virgules", - "logo-and-icons": "Logo & Icônes du site", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Chemin vers un logo à afficher dans l'en-tête du forum", "logo.upload": "Téléverser", @@ -35,6 +35,8 @@ "touch-icon.help": "Taille et format recommandés : 512x512, format PNG uniquement. Si aucune icône d'accueil n'est spécifiée, le favicon NodeBB sera visible.", "maskable-icon": "Icône masquable (écran d'accueil)", "maskable-icon.help": "Taille et format recommandés : 512x512, format PNG uniquement. Si aucune icône masquable n'est spécifiée, le favicon NodeBB sera visible.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Liens sortants", "outgoing-links.warning-page": "Utiliser la page d'avertissement pour liens sortants", "search": "Rechercher", diff --git a/public/language/gl/admin/settings/general.json b/public/language/gl/admin/settings/general.json index 23438c4828..7a25eb12a4 100644 --- a/public/language/gl/admin/settings/general.json +++ b/public/language/gl/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/he/admin/settings/general.json b/public/language/he/admin/settings/general.json index 3d0cfbd4ba..1ab09d5bd4 100644 --- a/public/language/he/admin/settings/general.json +++ b/public/language/he/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "תיאור האתר", "keywords": "מילות מפתח של האתר", "keywords-placeholder": "מילות מפתח המתארות את הקהילה שלך, מופרדות באמצעות פסיקים", - "logo-and-icons": "לוגו אתר ואייקונים", + "logo-and-icons": "Media & Branding", "logo.image": "תמונה", "logo.image-placeholder": "נתב ללוגו שיראה בכותרת הפורום", "logo.upload": "העלאה", @@ -35,6 +35,8 @@ "touch-icon.help": "סמליל דף אינטרנט מופיע כאשר מישהו מסמן את דף האינטרנט שלך או מוסיף את דף האינטרנט שלך למסך הבית שלו, גודל ותבנית מומלצים: 512x512, תבנית PNG בלבד. אם לא הוגדר סמליל דף אינטרנט, NodeBB יחזור להשתמש בסמליל הפבאייקון.", "maskable-icon": "סמליל הניתן להסוואה (במסך הבית)", "maskable-icon.help": "סמליל הניתן להסוואה מופיע בדף הבית של הסוללרי, זהו תמונה אטומה עם מעט ריפוד שהיישום דף הבית שלך יוכל לחתוך אחר כך לצורה ולגודל הרצוי. עדיף לא להסתמך על צורה מסוימת, מכיוון שהצורה שנבחרה בסופו של דבר יכולה להשתנות לפי סוגי מסך בית ופלטפורמה. גודל ותבנית מומלצים: 512x512, תבנית PNG בלבד. אם לא הוגדר אייקון הניתן להסוואה, NodeBB יחזור להשתמש בסמליל דף האינטרנט.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "קישורים חיצוניים", "outgoing-links.warning-page": "שימוש בדף האזהרה לקישורים יוצאים", "search": "חיפוש", diff --git a/public/language/hr/admin/settings/general.json b/public/language/hr/admin/settings/general.json index d21861149a..72f42c022c 100644 --- a/public/language/hr/admin/settings/general.json +++ b/public/language/hr/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Ključne riječi", "keywords-placeholder": "Ključne riječi koje opisuju Vašu zajednicu, odvojeni zarezom", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Slika", "logo.image-placeholder": "Putanja logotipa za zaglavlje foruma", "logo.upload": "Učitaj", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Odlazne poveznice", "outgoing-links.warning-page": "Koristi upozorenje za odlazne poveznice", "search": "Search", diff --git a/public/language/hu/admin/settings/general.json b/public/language/hu/admin/settings/general.json index 29839979b5..eb71f531b7 100644 --- a/public/language/hu/admin/settings/general.json +++ b/public/language/hu/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Weboldal kulcsszavak", "keywords-placeholder": "A közösségedet leíró kulcsszavak, vesszővel elválasztva", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Kép", "logo.image-placeholder": "A logó elérési útvonala, amit a fórum fejlécében fogunk megjeleníteni", "logo.upload": "Feltöltés", @@ -35,6 +35,8 @@ "touch-icon.help": "Ajánlott méret és formátum: 512x512, csak PNG formátum. Ha nincs beállítva, a NodeBB a favicon-t fogja használni.", "maskable-icon": "Maszkolható (főképernyő) ikon", "maskable-icon.help": "Ajánlott méret és formátum: 512x512, csak PNG formátum. Ha nincs beállítva, a NodeBB a favicon-t fogja használni", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Kimenő linkek", "outgoing-links.warning-page": "Kimenő link figyelmeztető oldal használata", "search": "Keresés", diff --git a/public/language/hy/admin/settings/general.json b/public/language/hy/admin/settings/general.json index ba5240156d..d42a299463 100644 --- a/public/language/hy/admin/settings/general.json +++ b/public/language/hy/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Կայքի հիմնաբառեր", "keywords-placeholder": "Ձեր համայնքը նկարագրող հիմնաբառեր՝ բաժանված ստորակետերով", - "logo-and-icons": "Կայքի Լոգո և պատկերանշաններ", + "logo-and-icons": "Media & Branding", "logo.image": "Նկար ", "logo.image-placeholder": "Ճանապարհ դեպի լոգո՝ ֆորումի վերնագրում ցուցադրելու համար", "logo.upload": "Վերբեռնել", @@ -35,6 +35,8 @@ "touch-icon.help": "Առաջարկվող չափը և ձևաչափը՝ 512x512, միայն PNG ձևաչափ: Եթե որևէ հպման պատկերակ նշված չէ, NodeBB-ը կվերադառնա ֆավիկոնի օգտագործմանը:", "maskable-icon": "Դիմակելի (հիմնական էկրան) պատկերակ", "maskable-icon.help": "Առաջարկվող չափը և ձևաչափը՝ 512x512, միայն PNG ձևաչափ: Եթե ոչ մի դիմակավոր պատկերակ նշված չէ, NodeBB-ը կվերադառնա Touch Icon-ին:", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Ելքային հղումներ", "outgoing-links.warning-page": "Օգտագործեք ելքային հղումների նախազգուշացման էջը", "search": "Որոնում", diff --git a/public/language/id/admin/settings/general.json b/public/language/id/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/id/admin/settings/general.json +++ b/public/language/id/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/it/admin/settings/general.json b/public/language/it/admin/settings/general.json index 6b1fea08ae..ebb00fe63f 100644 --- a/public/language/it/admin/settings/general.json +++ b/public/language/it/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Descrizione sito", "keywords": "Parole chiave del sito", "keywords-placeholder": "Parole chiave che descrivono la vostra comunità, separate da virgole", - "logo-and-icons": "Logo e icone del sito", + "logo-and-icons": "Media & Branding", "logo.image": "Immagine", "logo.image-placeholder": "Percorso del logo da visualizzare sull'intestazione del forum", "logo.upload": "Carica", @@ -35,6 +35,8 @@ "touch-icon.help": "Dimensioni e formato consigliati: 512x512, solo formato PNG. Se non è specificata alcuna icona touch, NodeBB tornerà a utilizzare la favicon.", "maskable-icon": "Icona Mascherabile (Schermata Iniziale)", "maskable-icon.help": "Dimensioni e formato consigliati: 512x512, solo formato PNG. Se non è specificata alcuna icona mascherabile, NodeBB tornerà a utilizzare l'Icona Touch.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Link in uscita", "outgoing-links.warning-page": "Usa pagina di avviso per i link in uscita", "search": "Cerca", diff --git a/public/language/ja/admin/settings/general.json b/public/language/ja/admin/settings/general.json index 2d79a2bc35..f3f623f970 100644 --- a/public/language/ja/admin/settings/general.json +++ b/public/language/ja/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "サイトのキーワード", "keywords-placeholder": "あなたのコミュニティを記述するキーワード、カンマ区切り", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "画像", "logo.image-placeholder": "フォーラムのヘッダーに表示するロゴのパス", "logo.upload": "アップロード", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "外部サイトへのリンク", "outgoing-links.warning-page": "送信リンクの警告ページを使用", "search": "Search", diff --git a/public/language/ko/admin/settings/general.json b/public/language/ko/admin/settings/general.json index 0756494d50..a2f98a64bf 100644 --- a/public/language/ko/admin/settings/general.json +++ b/public/language/ko/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "사이트 키워드", "keywords-placeholder": "커뮤니티를 설명하는 키워드, 쉼표로 구분", - "logo-and-icons": "사이트 로고 & 아이콘", + "logo-and-icons": "Media & Branding", "logo.image": "이미지", "logo.image-placeholder": "포럼 헤더에 표시할 로고의 경로", "logo.upload": "업로드", @@ -35,6 +35,8 @@ "touch-icon.help": "권장 크기 및 형식: 512x512, PNG 형식만. 터치 아이콘을 지정하지 않은 경우 NodeBB는 파비콘을 사용합니다.", "maskable-icon": "Maskable (홈 화면) 아이콘", "maskable-icon.help": "권장 크기 및 형식: 512x512, PNG 형식만. 마스크 가능 아이콘을 지정하지 않은 경우 NodeBB는 터치 아이콘을 사용합니다.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "외부로 나가는 링크", "outgoing-links.warning-page": "외부 링크 경고 페이지 사용", "search": "검색", diff --git a/public/language/lt/admin/settings/general.json b/public/language/lt/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/lt/admin/settings/general.json +++ b/public/language/lt/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/lv/admin/settings/general.json b/public/language/lv/admin/settings/general.json index fabfbb824e..2d306b6508 100644 --- a/public/language/lv/admin/settings/general.json +++ b/public/language/lv/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Foruma atslēgvārdi", "keywords-placeholder": "Atslēgvārdi, kas apraksta forumu, atdalīti ar komatu", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Bilde", "logo.image-placeholder": "Ceļš uz logo, ko parādītu foruma galvenē", "logo.upload": "Augšupielādēt", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Izejošās saites", "outgoing-links.warning-page": "Lietot izejošo saišu brīdinājuma lapu", "search": "Search", diff --git a/public/language/ms/admin/settings/general.json b/public/language/ms/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/ms/admin/settings/general.json +++ b/public/language/ms/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/nb/admin/settings/general.json b/public/language/nb/admin/settings/general.json index 17d717b787..15d189e51f 100644 --- a/public/language/nb/admin/settings/general.json +++ b/public/language/nb/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Nøkkelord for nettstedet", "keywords-placeholder": "Nøkkelord som beskriver fellesskapet ditt, kommaseparert", - "logo-and-icons": "Logo og ikoner for nettstedet", + "logo-and-icons": "Media & Branding", "logo.image": "Bilde", "logo.image-placeholder": "Sti til et logo som vises i forumets topptekst", "logo.upload": "Last opp", @@ -35,6 +35,8 @@ "touch-icon.help": "Anbefalt størrelse og format: 512x512, kun PNG-format. Hvis ingen berøringsikon er spesifisert, brukes favicon som reserve.", "maskable-icon": "Maskerbart (Hjem-skjerm) ikon", "maskable-icon.help": "Anbefalt størrelse og format: 512x512, kun PNG-format. Hvis ingen maskerbart ikon er spesifisert, brukes berøringsikon som reserve.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Utgående lenker", "outgoing-links.warning-page": "Bruk varslingsside for utgående lenker", "search": "Søk", diff --git a/public/language/nl/admin/settings/general.json b/public/language/nl/admin/settings/general.json index a5828eaa84..4a450b7aff 100644 --- a/public/language/nl/admin/settings/general.json +++ b/public/language/nl/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Trefwoorden", "keywords-placeholder": "Trefwoorden die uw community beschrijven, kommagescheiden", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Afbeelding", "logo.image-placeholder": "Pad naar een logo om te tonen op de forum header", "logo.upload": "Uploaden", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Uitgaande links", "outgoing-links.warning-page": "Gebruik waarschuwingspagina voor uitgaande links", "search": "Search", diff --git a/public/language/nn-NO/admin/settings/general.json b/public/language/nn-NO/admin/settings/general.json index 2157dd08fb..c32ff58019 100644 --- a/public/language/nn-NO/admin/settings/general.json +++ b/public/language/nn-NO/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Nøkkelord", "keywords-placeholder": "Skriv inn nøkkelord, skilde med komma", - "logo-and-icons": "Logo og ikon", + "logo-and-icons": "Media & Branding", "logo.image": "Bilet-URL for logo", "logo.image-placeholder": "https://din-nettstad.no/logo.png", "logo.upload": "Last opp logo", @@ -35,6 +35,8 @@ "touch-icon.help": "Bruk touch-ikonet for å vise på mobile einingar.", "maskable-icon": "Maskerbart ikon", "maskable-icon.help": "Maskerbare ikon vert brukt for å tilpasse webappen til ulike skjermstorleikar.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Utgåande lenkjer", "outgoing-links.warning-page": "Advarselside for utgåande lenkjer", "search": "Søk", diff --git a/public/language/pl/admin/settings/general.json b/public/language/pl/admin/settings/general.json index ed26bb21bf..083be75a77 100644 --- a/public/language/pl/admin/settings/general.json +++ b/public/language/pl/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Opis strony", "keywords": "Słowa kluczowe strony", "keywords-placeholder": "Słowa kluczowe opisujące społeczność, oddzielone przecinkami", - "logo-and-icons": "Logo i ikony strony", + "logo-and-icons": "Media & Branding", "logo.image": "Obraz", "logo.image-placeholder": "Ścieżka do logo, które ma być wyświetlane w nagłówku forum", "logo.upload": "Prześlij", @@ -35,6 +35,8 @@ "touch-icon.help": "Rekomendowana wielkość: 512x512, tylko format PNG. Jeśli nie ustalono ikony dotykowej, użyta zostanie favikona.", "maskable-icon": "Ikona ekranu głównego", "maskable-icon.help": "Rekomendowana wielkość: 512x512, tylko format PNG. Jeśli nie ustalono tej ikony, użyta zostanie ikona dotykowa.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Odnośniki wychodzące", "outgoing-links.warning-page": "Używaj strony ostrzegawczej o odnośnikach wychodzących", "search": "Szukaj", diff --git a/public/language/pt-BR/admin/settings/general.json b/public/language/pt-BR/admin/settings/general.json index d59c71f9f5..c96b724a56 100644 --- a/public/language/pt-BR/admin/settings/general.json +++ b/public/language/pt-BR/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Palavras-chave do Site", "keywords-placeholder": "Palavras-chave descrevendo sua comunidade, separadas por vírgula", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Imagem", "logo.image-placeholder": "Caminho de URL do logotipo para mostrar no cabeçalho do fórum", "logo.upload": "Enviar", @@ -35,6 +35,8 @@ "touch-icon.help": "Tamanho e formato recomendados: 512x512, somente formato PNG. Se nenhum ícone para touch for especificado, o NodeBB usará o seu próprio favicon.", "maskable-icon": "Ícone Mascarável (de Tela Inicial)", "maskable-icon.help": "Tamanho e formato recomendados: 512x512, somente formato PNG. Se nenhum ícone mascarável for especificado, o NodeBB usará o seu próprio Ícone para Touch.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Links Externos", "outgoing-links.warning-page": "Habilitar Página de Aviso de Links Externos", "search": "Search", diff --git a/public/language/pt-PT/admin/settings/general.json b/public/language/pt-PT/admin/settings/general.json index 8e7e464640..394753a971 100644 --- a/public/language/pt-PT/admin/settings/general.json +++ b/public/language/pt-PT/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Palavras-chave do Site", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Imagem", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Enviar", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Links Externos", "outgoing-links.warning-page": "Utilizar a página de aviso para links externos", "search": "Search", diff --git a/public/language/ro/admin/settings/general.json b/public/language/ro/admin/settings/general.json index ebebb10fa5..3d4dd933d3 100644 --- a/public/language/ro/admin/settings/general.json +++ b/public/language/ro/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Descrierea site-ului", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/ru/admin/settings/general.json b/public/language/ru/admin/settings/general.json index 21aceec945..cfeaca388e 100644 --- a/public/language/ru/admin/settings/general.json +++ b/public/language/ru/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Ключевые слова для сайта", "keywords-placeholder": "Укажите через запятую ключевые слова, описывающие ваше сообщество", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Логотип в шапке сайта", "logo.image-placeholder": "Путь к файлу логотипа ", "logo.upload": "Загрузить", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Внешние ссылки", "outgoing-links.warning-page": "Предупреждать, когда пользователь переходит по внешним ссылкам", "search": "Поиск", diff --git a/public/language/rw/admin/settings/general.json b/public/language/rw/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/rw/admin/settings/general.json +++ b/public/language/rw/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/sc/admin/settings/general.json b/public/language/sc/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/sc/admin/settings/general.json +++ b/public/language/sc/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/sk/admin/settings/general.json b/public/language/sk/admin/settings/general.json index 2dfddf133e..2223015b5a 100644 --- a/public/language/sk/admin/settings/general.json +++ b/public/language/sk/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Kľúčové slová pre stránky", "keywords-placeholder": "Kľúčové slová popisujúce Vašu komunitu, oddelené čiarkou", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Obrázok", "logo.image-placeholder": "Cesta k logu, aby mohlo byť zobrazené v hlavičke fóra", "logo.upload": "Nahrať", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Odchádzajúce odkazy", "outgoing-links.warning-page": "Použiť stránku s upozornením pri odchádzajúcich odkazoch", "search": "Search", diff --git a/public/language/sl/admin/settings/general.json b/public/language/sl/admin/settings/general.json index ea11d557c7..fec7da1605 100644 --- a/public/language/sl/admin/settings/general.json +++ b/public/language/sl/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Ključne besede spletnega mesta", "keywords-placeholder": "Ključne besede, ki opisujejo vašo skupnost, ločene z vejicami", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Slika", "logo.image-placeholder": "Pot do logotipa za prikaz v glavi foruma", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Odhodne povezave", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Išči", diff --git a/public/language/sq-AL/admin/settings/general.json b/public/language/sq-AL/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/sq-AL/admin/settings/general.json +++ b/public/language/sq-AL/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/sr/admin/settings/general.json b/public/language/sr/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/sr/admin/settings/general.json +++ b/public/language/sr/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/sv/admin/settings/general.json b/public/language/sv/admin/settings/general.json index d56c819745..0ee921d831 100644 --- a/public/language/sv/admin/settings/general.json +++ b/public/language/sv/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/th/admin/settings/general.json b/public/language/th/admin/settings/general.json index a0f6cc7655..85299636dd 100644 --- a/public/language/th/admin/settings/general.json +++ b/public/language/th/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Outgoing Links", "outgoing-links.warning-page": "Use Outgoing Links Warning Page", "search": "Search", diff --git a/public/language/tr/admin/settings/general.json b/public/language/tr/admin/settings/general.json index e553bcdf41..27df764c91 100644 --- a/public/language/tr/admin/settings/general.json +++ b/public/language/tr/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Site Anahtar Kelimeler", "keywords-placeholder": "Topluluğunuzu tanımlayan anahtar kelimeler, virgülle-ayrılmış", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Görsel", "logo.image-placeholder": "Forum başlığında görüntülenecek bir logo yolu", "logo.upload": "Yükle", @@ -35,6 +35,8 @@ "touch-icon.help": "Önerilen Boyut: 512x512. Önerilen format: PNG. Simge belirtilmezse varsayılan olarak favicon kullanılır.", "maskable-icon": "Maskelenebilir (Ana Ekran) Simgesi", "maskable-icon.help": "Önerilen boyut ve format: 512x512, PNG formatı. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Harici Bağlantılar", "outgoing-links.warning-page": "Dışarı giden bağlantılar için uyarı sayfası kullan", "search": "Arama", diff --git a/public/language/uk/admin/settings/general.json b/public/language/uk/admin/settings/general.json index 96962dfe01..76f2cfabe9 100644 --- a/public/language/uk/admin/settings/general.json +++ b/public/language/uk/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "Ключові слова сайту", "keywords-placeholder": "Ключові слова, що описують вашу спільноту, розділені комами", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "Зображення", "logo.image-placeholder": "Шлях до логотипу для відображення в шапці форуму", "logo.upload": "Завантажити", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Зовнішні посилання", "outgoing-links.warning-page": "Використовувати сторінку попередження про зовнішній перехід", "search": "Search", diff --git a/public/language/ur/admin/settings/general.json b/public/language/ur/admin/settings/general.json index 51e14ab67f..b2594add22 100644 --- a/public/language/ur/admin/settings/general.json +++ b/public/language/ur/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "ویب سائٹ کی تفصیل", "keywords": "ویب سائٹ کے کلیدی الفاظ", "keywords-placeholder": "آپ کی کمیونٹی کی وضاحت کرنے والے کلیدی الفاظ، کوموں سے الگ کیے گئے۔", - "logo-and-icons": "ویب سائٹ کا لوگو اور آئیکنز", + "logo-and-icons": "Media & Branding", "logo.image": "تصویر", "logo.image-placeholder": "فورم کے ہیڈر میں دکھائے جانے والے لوگو کا پاتھ", "logo.upload": "اپ لوڈ", @@ -35,6 +35,8 @@ "touch-icon.help": "تجویز کردہ سائز اور فارمیٹ: 512x512، صرف PNG فارمیٹ میں۔ اگر ٹچ آئیکن بیان نہیں کیا گیا تو NodeBB ویب سائٹ کے فیویکن کا استعمال کرے گا۔", "maskable-icon": "ماسک ایبل آئیکن (ہوم اسکرین کے لیے)", "maskable-icon.help": "تجویز کردہ سائز اور فارمیٹ: 512x512، صرف PNG فارمیٹ میں۔ اگر ماسک ایبل آئیکن بیان نہیں کیا گیا تو NodeBB ٹچ آئیکن کا استعمال کرے گا۔", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "باہر جانے والے لنکس", "outgoing-links.warning-page": "باہری لنکس پر کلک کرنے پر تنبیہی صفحہ دکھائیں", "search": "تلاش", diff --git a/public/language/vi/admin/settings/general.json b/public/language/vi/admin/settings/general.json index a45f6a551f..5907a11dc6 100644 --- a/public/language/vi/admin/settings/general.json +++ b/public/language/vi/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Mô Tả Trang", "keywords": "Từ Khóa Trang", "keywords-placeholder": "Các từ khóa mô tả cộng đồng của bạn, được phân tách bằng dấu phẩy", - "logo-and-icons": "Lô-gô & Biểu Tượng Trang", + "logo-and-icons": "Media & Branding", "logo.image": "Ảnh", "logo.image-placeholder": "Đường dẫn đến biểu trưng để hiển thị phần đầu diễn đàn", "logo.upload": "Tải lên", @@ -35,6 +35,8 @@ "touch-icon.help": "Kích cỡ và định dạng được đề xuất: 512x512, chỉ định dạng PNG. Nếu không có biểu tượng cảm ứng nào, NodeBB sẽ quay trở lại sử dụng favicon.", "maskable-icon": "Biểu tượng có thể che được (Màn Hình Trang Chủ)", "maskable-icon.help": "Kích thước và định dạng nên là: 512x512, chỉ định dạng PNG. Nếu không có biểu tượng có thể che được nào được chỉ định, NodeBB sẽ trở lại Biểu Tượng Chạm.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "Liên Kết Đi", "outgoing-links.warning-page": "Sử Dụng Trang Cảnh Báo Liên Kết Đi", "search": "Tìm kiếm", diff --git a/public/language/zh-CN/admin/settings/general.json b/public/language/zh-CN/admin/settings/general.json index cd6cc33bac..489ebf012b 100644 --- a/public/language/zh-CN/admin/settings/general.json +++ b/public/language/zh-CN/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "站点描述", "keywords": "站点关键字", "keywords-placeholder": "描述您的社区的关键字(以逗号分隔)", - "logo-and-icons": "网站徽标与图标", + "logo-and-icons": "Media & Branding", "logo.image": "图像", "logo.image-placeholder": "要在论坛标题上显示的 Logo 的路径", "logo.upload": "上传", @@ -35,6 +35,8 @@ "touch-icon.help": "推荐的尺寸和格式:512x512,仅限PNG格式。如果没有指定触摸图标,NodeBB将回退到站点图标。", "maskable-icon": "可遮蔽(主屏)图标", "maskable-icon.help": "推荐的尺寸和格式:512x512,仅限PNG格式。如果没有指定可遮蔽图标,NodeBB将回退到触摸图标。", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "站外链接", "outgoing-links.warning-page": "使用站外链接警告页", "search": "搜索", diff --git a/public/language/zh-TW/admin/settings/general.json b/public/language/zh-TW/admin/settings/general.json index 992143a90b..a438f177ee 100644 --- a/public/language/zh-TW/admin/settings/general.json +++ b/public/language/zh-TW/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Site Description", "keywords": "網站關鍵字", "keywords-placeholder": "描述您的社區的關鍵字,以逗號分隔", - "logo-and-icons": "Site Logo & Icons", + "logo-and-icons": "Media & Branding", "logo.image": "圖檔", "logo.image-placeholder": "要在論壇標題上顯示的 Logo 的路徑", "logo.upload": "上傳", @@ -35,6 +35,8 @@ "touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.", "maskable-icon": "Maskable (Homescreen) Icon", "maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.", + "screenshot": "Screenshot", + "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", "outgoing-links": "站外連結", "outgoing-links.warning-page": "使用站外連結警告頁", "search": "Search", From 09c54127de83d326e7ef6c2398da6cf0025a6ee2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 12 Mar 2026 14:31:43 -0400 Subject: [PATCH 141/204] fix: screenshot fallback --- public/images/screenshot-default.png | Bin 0 -> 42804 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/images/screenshot-default.png diff --git a/public/images/screenshot-default.png b/public/images/screenshot-default.png new file mode 100644 index 0000000000000000000000000000000000000000..cd231ecb2a64f3e3136f45426ea18554ac6a4350 GIT binary patch literal 42804 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 Svmkj7_%S$dqEo7UIrwjN8u(5C literal 0 HcmV?d00001 From 4fef1b4f9d68f479b9258c08390d1053fa14907e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Mar 2026 15:50:33 -0400 Subject: [PATCH 142/204] increase analytics 404 --- src/meta/errors.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/meta/errors.js b/src/meta/errors.js index bae26c2acc..7b623fe5f0 100644 --- a/src/meta/errors.js +++ b/src/meta/errors.js @@ -74,8 +74,10 @@ Errors.log404 = function (route) { if (!route) { return; } - route = route.slice(0, 512).replace(/\/$/, ''); // remove trailing slashes + analytics.increment('errors:404'); + + route = route.slice(0, 512).replace(/\/$/, ''); // remove trailing slashes const segments = route.split('/'); const containsUUID = segments.some((segment) => { const cleanSegment = segment.split('.')[0]; @@ -84,7 +86,7 @@ Errors.log404 = function (route) { if (containsUUID) { return; } - analytics.increment('errors:404'); + counters[route] = counters[route] || 0; counters[route] += 1; }; From 6147a4d0821f601d600b2d59b1653c6433866113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Mar 2026 22:02:03 -0400 Subject: [PATCH 143/204] fix: merged chat notifications if all the messages are from the same user usernames.length ends up being 1 so need to use default translation string and not -dual/-triple/-multiple variants --- src/notifications.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/notifications.js b/src/notifications.js index d06108e8ca..e38ccd215f 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -519,7 +519,9 @@ Notifications.merge = async function (notifications) { differentiators.forEach((differentiator) => { function typeFromLength(items) { - if (items.length === 2) { + if (items.length <= 1) { + return ''; + } else if (items.length === 2) { return 'dual'; } else if (items.length === 3) { return 'triple'; @@ -553,7 +555,7 @@ Notifications.merge = async function (notifications) { const type = typeFromLength(usernames); const isMultiple = type === 'multiple'; const txArgs = [ - `${mergeId}-${type}`, + `${mergeId}${type ? '-type' : ''}`, ...usernames.slice(0, usernames.length <= 3 ? 3 : 2), ...(isMultiple ? [usernames.length - 2] : []), notifObj.roomIcon, @@ -574,7 +576,7 @@ Notifications.merge = async function (notifications) { const type = typeFromLength(usernames); const isMultiple = type === 'multiple'; const txArgs = [ - `${mergeId}-${type}`, + `${mergeId}${type ? '-type' : ''}`, ...usernames.slice(0, usernames.length <= 3 ? 3 : 2), ...(isMultiple ? [usernames.length - 2] : []), utils.decodeHTMLEntities(notifObj.topicTitle || ''), From 40fecd01f35d3f972ec36f09470522026ca6d0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Mar 2026 22:04:48 -0400 Subject: [PATCH 144/204] fix: type --- src/notifications.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/notifications.js b/src/notifications.js index e38ccd215f..0a108ff03b 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -555,7 +555,7 @@ Notifications.merge = async function (notifications) { const type = typeFromLength(usernames); const isMultiple = type === 'multiple'; const txArgs = [ - `${mergeId}${type ? '-type' : ''}`, + `${mergeId}${type ? `-${type}` : ''}`, ...usernames.slice(0, usernames.length <= 3 ? 3 : 2), ...(isMultiple ? [usernames.length - 2] : []), notifObj.roomIcon, @@ -576,7 +576,7 @@ Notifications.merge = async function (notifications) { const type = typeFromLength(usernames); const isMultiple = type === 'multiple'; const txArgs = [ - `${mergeId}${type ? '-type' : ''}`, + `${mergeId}${type ? `-${type}` : ''}`, ...usernames.slice(0, usernames.length <= 3 ? 3 : 2), ...(isMultiple ? [usernames.length - 2] : []), utils.decodeHTMLEntities(notifObj.topicTitle || ''), From f2bca332621541b22562a7a3c1681a53a770a4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Mar 2026 22:13:38 -0400 Subject: [PATCH 145/204] perf: move out nconf.get and isClientScript regex --- src/controllers/404.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/404.js b/src/controllers/404.js index 412676c2e7..1688b80a7a 100644 --- a/src/controllers/404.js +++ b/src/controllers/404.js @@ -10,6 +10,9 @@ const activitypub = require('../activitypub'); const middleware = require('../middleware'); const helpers = require('../middleware/helpers'); +const relativePath = nconf.get('relative_path'); +const isClientScript = new RegExp(`^${relativePath}\\/assets\\/src\\/.+\\.js(\\?v=\\w+)?$`); + const error404Icons = [ 'fa-hippo', 'fa-cat', 'fa-otter', 'fa-dog', 'fa-cow', 'fa-fish', @@ -17,9 +20,6 @@ const error404Icons = [ ]; exports.handle404 = helpers.try(async (req, res) => { - const relativePath = nconf.get('relative_path'); - const isClientScript = new RegExp(`^${relativePath}\\/assets\\/src\\/.+\\.js(\\?v=\\w+)?$`); - if (plugins.hooks.hasListeners('action:meta.override404')) { return plugins.hooks.fire('action:meta.override404', { req: req, From 59dd22ca2e8bb00dd8ec57ab4d96af5347cb4f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Mar 2026 22:04:48 -0400 Subject: [PATCH 146/204] fix: type --- src/notifications.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/notifications.js b/src/notifications.js index fd0d01ce55..ef7e813a95 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -549,7 +549,7 @@ Notifications.merge = async function (notifications) { const type = typeFromLength(usernames); const isMultiple = type === 'multiple'; const txArgs = [ - `${mergeId}-${type}`, + `${mergeId}${type ? `-${type}` : ''}`, ...usernames.slice(0, usernames.length <= 3 ? 3 : 2), ...(isMultiple ? [usernames.length - 2] : []), notifObj.roomIcon, @@ -570,7 +570,7 @@ Notifications.merge = async function (notifications) { const type = typeFromLength(usernames); const isMultiple = type === 'multiple'; const txArgs = [ - `${mergeId}-${type}`, + `${mergeId}${type ? `-${type}` : ''}`, ...usernames.slice(0, usernames.length <= 3 ? 3 : 2), ...(isMultiple ? [usernames.length - 2] : []), utils.decodeHTMLEntities(notifObj.topicTitle || ''), From 4d55ee0a55d95ecc5e41339d4f5c64550c08e4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Mar 2026 22:13:38 -0400 Subject: [PATCH 147/204] perf: move out nconf.get and isClientScript regex --- src/controllers/404.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/controllers/404.js b/src/controllers/404.js index bed1a085e3..03407e084c 100644 --- a/src/controllers/404.js +++ b/src/controllers/404.js @@ -9,12 +9,17 @@ const plugins = require('../plugins'); const activitypub = require('../activitypub'); const middleware = require('../middleware'); const helpers = require('../middleware/helpers'); -const { secureRandom } = require('../utils'); + +const relativePath = nconf.get('relative_path'); +const isClientScript = new RegExp(`^${relativePath}\\/assets\\/src\\/.+\\.js(\\?v=\\w+)?$`); + +const error404Icons = [ + 'fa-hippo', 'fa-cat', 'fa-otter', + 'fa-dog', 'fa-cow', 'fa-fish', + 'fa-dragon', 'fa-horse', 'fa-dove', +]; exports.handle404 = helpers.try(async (req, res) => { - const relativePath = nconf.get('relative_path'); - const isClientScript = new RegExp(`^${relativePath}\\/assets\\/src\\/.+\\.js(\\?v=\\w+)?$`); - if (plugins.hooks.hasListeners('action:meta.override404')) { return plugins.hooks.fire('action:meta.override404', { req: req, @@ -62,16 +67,12 @@ exports.send404 = helpers.try(async (req, res) => { bodyClass: helpers.buildBodyClass(req, res), }); } - const icons = [ - 'fa-hippo', 'fa-cat', 'fa-otter', - 'fa-dog', 'fa-cow', 'fa-fish', - 'fa-dragon', 'fa-horse', 'fa-dove', - ]; + await middleware.buildHeaderAsync(req, res); res.render('404', { path: validator.escape(path), title: '[[global:404.title]]', bodyClass: helpers.buildBodyClass(req, res), - icon: icons[secureRandom(0, icons.length - 1)], + icon: error404Icons[Math.floor(Math.random() * error404Icons.length)], }); }); From 26bb60effcc485cd72eaa55088486ff9b280d11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 12 Mar 2026 22:02:03 -0400 Subject: [PATCH 148/204] fix: merged chat notifications if all the messages are from the same user usernames.length ends up being 1 so need to use default translation string and not -dual/-triple/-multiple variants --- src/notifications.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/notifications.js b/src/notifications.js index ef7e813a95..197ea656cc 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -515,7 +515,9 @@ Notifications.merge = async function (notifications) { differentiators.forEach((differentiator) => { function typeFromLength(items) { - if (items.length === 2) { + if (items.length <= 1) { + return ''; + } else if (items.length === 2) { return 'dual'; } else if (items.length === 3) { return 'triple'; From f1d311454177b357d8b78c010ca34f66fbb9515b Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Fri, 13 Mar 2026 09:07:41 +0000 Subject: [PATCH 149/204] Latest translations and fallbacks --- public/language/bg/admin/manage/users.json | 2 +- public/language/bg/admin/settings/general.json | 6 +++--- public/language/de/admin/manage/users.json | 2 +- public/language/it/admin/advanced/jobs.json | 2 +- public/language/it/admin/manage/users.json | 2 +- public/language/vi/admin/advanced/jobs.json | 6 +++--- public/language/vi/admin/manage/privileges.json | 2 +- public/language/vi/admin/manage/users.json | 2 +- public/language/vi/admin/menu.json | 2 +- public/language/vi/admin/settings/activitypub.json | 4 ++-- public/language/vi/aria.json | 2 +- public/language/vi/error.json | 2 +- public/language/vi/world.json | 4 ++-- public/language/zh-CN/admin/advanced/jobs.json | 2 +- public/language/zh-CN/admin/manage/users.json | 2 +- 15 files changed, 21 insertions(+), 21 deletions(-) diff --git a/public/language/bg/admin/manage/users.json b/public/language/bg/admin/manage/users.json index 898aa08316..b68fa15b10 100644 --- a/public/language/bg/admin/manage/users.json +++ b/public/language/bg/admin/manage/users.json @@ -40,7 +40,7 @@ "250-per-page": "250 на страница", "500-per-page": "500 на страница", - "search.help": "Use "*" to make partial searches, for example "*query"", + "search.help": "Използвайте "*" за частични търсения. например: "*дума"", "search.uid": "По потребителски идентификатор", "search.uid-placeholder": "Въведете потребителски идентификатор, който да потърсите", "search.username": "По име на потребител", diff --git a/public/language/bg/admin/settings/general.json b/public/language/bg/admin/settings/general.json index 433f5d7ceb..74b12dd3e2 100644 --- a/public/language/bg/admin/settings/general.json +++ b/public/language/bg/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Описание на уеб сайта", "keywords": "Ключови думи на уеб сайта", "keywords-placeholder": "Ключови думи, описващи общността Ви. Трябва да бъдат разделени със запетаи.", - "logo-and-icons": "Media & Branding", + "logo-and-icons": "Медийни материали", "logo.image": "Изображение", "logo.image-placeholder": "Път до логото, което да бъде показано в заглавната част на форума", "logo.upload": "Качване", @@ -35,8 +35,8 @@ "touch-icon.help": "Препоръчителен размер и формат: 512x512, само във формат „PNG“. Ако не е посочена иконка за сензорен екран, NodeBB ще използва иконката на уеб сайта.", "maskable-icon": "Маскируема иконка (за начален екран)", "maskable-icon.help": "Препоръчителен размер и формат: 512x512, само във формат „PNG“. Ако не е посочена маскируема иконка, NodeBB ще използва иконката за сензорен екран.", - "screenshot": "Screenshot", - "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", + "screenshot": "Екранна снимка", + "screenshot.help": "Препоръчителен размер и формат: между 320px и 3480px, само JPG и PNG. Ако няма зададена екранна снимка, NodeBB ще използва такава по подразбиране.", "outgoing-links": "Изходящи връзки", "outgoing-links.warning-page": "Показване на предупредителна страница при щракване върху външни връзки", "search": "Търсене", diff --git a/public/language/de/admin/manage/users.json b/public/language/de/admin/manage/users.json index acf5de647c..a3965798a8 100644 --- a/public/language/de/admin/manage/users.json +++ b/public/language/de/admin/manage/users.json @@ -40,7 +40,7 @@ "250-per-page": "250 pro Seite", "500-per-page": "500 pro Seite", - "search.help": "Use "*" to make partial searches, for example "*query"", + "search.help": "Benutz "*" um nach Teilen von Wörtern zu suchen, zum Beispiel "*anfrage"", "search.uid": "Nach Benutzer-ID", "search.uid-placeholder": "Gib eine Benutzer-ID ein um danach zu suchen", "search.username": "Nach Nutzernamen", diff --git a/public/language/it/admin/advanced/jobs.json b/public/language/it/admin/advanced/jobs.json index 6ff20a0785..110381511f 100644 --- a/public/language/it/admin/advanced/jobs.json +++ b/public/language/it/admin/advanced/jobs.json @@ -5,5 +5,5 @@ "next-run": "Prossima esecuzione", "last-duration": "Ultima durata", "running": "In esecuzione", - "active": "Active" + "active": "Attivo" } \ No newline at end of file diff --git a/public/language/it/admin/manage/users.json b/public/language/it/admin/manage/users.json index f657537395..3c00762d4f 100644 --- a/public/language/it/admin/manage/users.json +++ b/public/language/it/admin/manage/users.json @@ -40,7 +40,7 @@ "250-per-page": "250 per pagina", "500-per-page": "500 per pagina", - "search.help": "Use "*" to make partial searches, for example "*query"", + "search.help": "Usa "*" per effettuare ricerche parziali, ad esempio "*richiesta"", "search.uid": "Da ID Utente", "search.uid-placeholder": "Inserisci l'ID utente da cercare", "search.username": "Da Nome Utente", diff --git a/public/language/vi/admin/advanced/jobs.json b/public/language/vi/admin/advanced/jobs.json index 896b07930a..7cd625a139 100644 --- a/public/language/vi/admin/advanced/jobs.json +++ b/public/language/vi/admin/advanced/jobs.json @@ -1,7 +1,7 @@ { - "jobs": "Jobs", - "job-name": "Job Name", - "schedule": "Schedule", + "jobs": "Công việc", + "job-name": "Tên Công Việc", + "schedule": "Lên lịch", "next-run": "Next Run", "last-duration": "Last Duration", "running": "Running", diff --git a/public/language/vi/admin/manage/privileges.json b/public/language/vi/admin/manage/privileges.json index 6b56cdc2d5..7183f47ff4 100644 --- a/public/language/vi/admin/manage/privileges.json +++ b/public/language/vi/admin/manage/privileges.json @@ -29,7 +29,7 @@ "access-topics": "Truy Cập Chủ Đề", "create-topics": "Tạo Chủ Đề", "reply-to-topics": "Trả Lời Chủ Đề", - "crosspost-topics": "Cross-post Topics", + "crosspost-topics": "Chủ Đề Đăng Chéo", "schedule-topics": "Lên Lịch Chủ Đề", "tag-topics": "Gắn Thẻ Chủ Đề", "edit-posts": "Chỉnh Sửa Bài Đăng", diff --git a/public/language/vi/admin/manage/users.json b/public/language/vi/admin/manage/users.json index 17cd5611b2..349d33d288 100644 --- a/public/language/vi/admin/manage/users.json +++ b/public/language/vi/admin/manage/users.json @@ -40,7 +40,7 @@ "250-per-page": "250 mỗi trang", "500-per-page": "500 mỗi trang", - "search.help": "Use "*" to make partial searches, for example "*query"", + "search.help": "Dùng "*" thực hiện tìm kiếm một phần, ví dụ "*query"", "search.uid": "Bởi ID Người Dùng", "search.uid-placeholder": "Nhập ID người dùng để tìm", "search.username": "Theo Tên Người Dùng", diff --git a/public/language/vi/admin/menu.json b/public/language/vi/admin/menu.json index 0bf7c1c273..bf999805fb 100644 --- a/public/language/vi/admin/menu.json +++ b/public/language/vi/admin/menu.json @@ -78,7 +78,7 @@ "advanced/logs": "Nhật ký", "advanced/errors": "Lỗi", "advanced/cache": "Bộ nhớ đệm", - "advanced/jobs": "Jobs", + "advanced/jobs": "Công việc", "development/logger": "Ghi nhật ký", "development/info": "Thông tin", diff --git a/public/language/vi/admin/settings/activitypub.json b/public/language/vi/admin/settings/activitypub.json index 7dff454a67..ed7b3b8643 100644 --- a/public/language/vi/admin/settings/activitypub.json +++ b/public/language/vi/admin/settings/activitypub.json @@ -49,6 +49,6 @@ "content.outgoing": "Ra ngoài", "content.summary-limit": "Số lượng ký tự sau đó bản tóm tắt sẽ được tạo.", "content.summary-limit-help": "Khi nội dung được phân phối vượt quá số lượng ký tự này, tóm tắt được tạo ra, bao gồm tất cả các câu hoàn chỉnh trước giới hạn này. (Mặc định: 500)", - "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string": "Dấu phân cách Ghi chú/Bài đăng", + "content.break-string-help": "Dấu phân cách này có thể được người dùng thành thạo chèn thủ công khi soạn thảo chủ đề mới. Nó hướng dẫn NodeBB sử dụng nội dung cho đến thời điểm đó như một phần của tóm tắt. Nếu chuỗi này không được sử dụng, thì phương án dự phòng dựa trên số lượng ký tự sẽ được áp dụng. (Mặc định: [...])" } \ No newline at end of file diff --git a/public/language/vi/aria.json b/public/language/vi/aria.json index b82f396907..71fa3f06de 100644 --- a/public/language/vi/aria.json +++ b/public/language/vi/aria.json @@ -6,5 +6,5 @@ "user-watched-tags": "Thẻ người dùng đã xem", "delete-upload-button": "Xóa nút tải lên", "group-page-link-for": "Liên kết trang nhóm cho %1", - "show-crossposts": "Show Cross-posts" + "show-crossposts": "Hiển thị Bài Đăng Chéo" } \ No newline at end of file diff --git a/public/language/vi/error.json b/public/language/vi/error.json index bf230db126..1d4e90a668 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -1,6 +1,6 @@ { "invalid-data": "Dữ Liệu Không Hợp Lệ", - "invalid-config-field-value": "Invalid value for config field \"%1\": %2", + "invalid-config-field-value": "Giá trị không hợp lệ cho trường cấu hình \"%1\": %2", "invalid-json": "JSON không hợp lệ", "wrong-parameter-type": "Giá trị của loại %3 được mong đợi cho thuộc tính `%1`, nhưng thay vào đó, %2 đã được nhận", "required-parameters-missing": "Các thông số bắt buộc bị thiếu trong lệnh gọi API này: %1", diff --git a/public/language/vi/world.json b/public/language/vi/world.json index 337b188306..77e8e377d4 100644 --- a/public/language/vi/world.json +++ b/public/language/vi/world.json @@ -22,6 +22,6 @@ "onboard.how": "Trong thời gian chờ đợi, bạn có thể nhấp vào các nút tắt ở trên cùng để xem diễn đàn này biết thêm những gì và bắt đầu khám phá một số nội dung mới!", "category-search": "Tìm danh mục...", - "see-more": "See more", - "see-less": "See less" + "see-more": "Xem nhiều hơn", + "see-less": "Xem ít hơn" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/advanced/jobs.json b/public/language/zh-CN/admin/advanced/jobs.json index 9a145abf51..7159567c23 100644 --- a/public/language/zh-CN/admin/advanced/jobs.json +++ b/public/language/zh-CN/admin/advanced/jobs.json @@ -5,5 +5,5 @@ "next-run": "下次执行", "last-duration": "上次持续时间", "running": "执行中", - "active": "Active" + "active": "启用" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/manage/users.json b/public/language/zh-CN/admin/manage/users.json index 5acff6becc..d62f412fc7 100644 --- a/public/language/zh-CN/admin/manage/users.json +++ b/public/language/zh-CN/admin/manage/users.json @@ -40,7 +40,7 @@ "250-per-page": "每页250", "500-per-page": "每页500", - "search.help": "Use "*" to make partial searches, for example "*query"", + "search.help": "使用 "*" 进行部分搜索,例如 "*query"", "search.uid": "通过用户 ID", "search.uid-placeholder": "输入用户 ID 以搜索", "search.username": "通过用户名", From 38a1da4609c0ba2409efd2fc89e577953cd16b3a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 12:09:25 -0400 Subject: [PATCH 150/204] fix: imagesLoaded integration for handleBack in world.js --- public/src/client/world.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/public/src/client/world.js b/public/src/client/world.js index ddecba0ead..ca9fda9187 100644 --- a/public/src/client/world.js +++ b/public/src/client/world.js @@ -3,10 +3,10 @@ define('forum/world', [ 'forum/infinitescroll', 'search', 'sort', 'hooks', 'alerts', 'api', 'bootbox', 'helpers', 'forum/category/tools', - 'translator', 'quickreply', 'handleBack', + 'translator', 'quickreply', 'handleBack', 'imagesloaded', ], function (infinitescroll, search, sort, hooks, alerts, api, bootbox, helpers, categoryTools, - translator, quickreply, handleBack) { + translator, quickreply, handleBack, imagesLoaded) { const World = {}; World.init = function () { @@ -52,11 +52,13 @@ define('forum/world', [ app.parseAndTranslate(ajaxify.data.template.name, 'posts', payload, function (html) { const listEl = document.getElementById('world-feed'); $(listEl).append(html); - html.find('.timeago').timeago(); - handleImages(); - handleShowMoreButtons(); - callback(); - handleBackCb(); + imagesLoaded(listEl, () => { + html.find('.timeago').timeago(); + handleImages(); + handleShowMoreButtons(); + callback(); + handleBackCb(); + }); }); }); }, { container: '#world-feed' }); From e2131d1d2e1c6f14cb8867ac7e22840da3f4c63f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 12:41:55 -0400 Subject: [PATCH 151/204] refactor: /world sorting logic to always use topics/sorted logic - New params for getSortedTopics (includeRemote, followingOnly) - Ability to show latest (followers only) or latest (all), ?all query param to discriminate - World now always shows posts from the local forum - Popular sort will be followers-only + local --- public/language/en-GB/world.json | 3 +- src/controllers/activitypub/topics.js | 30 +++++++++----------- src/topics/sorted.js | 40 +++++++++++++++++---------- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/public/language/en-GB/world.json b/public/language/en-GB/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/en-GB/world.json +++ b/public/language/en-GB/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/src/controllers/activitypub/topics.js b/src/controllers/activitypub/topics.js index 6aaae596af..702893cac8 100644 --- a/src/controllers/activitypub/topics.js +++ b/src/controllers/activitypub/topics.js @@ -2,7 +2,6 @@ const _ = require('lodash'); -const db = require('../../database'); const meta = require('../../meta'); const user = require('../../user'); const topics = require('../../topics'); @@ -20,8 +19,8 @@ controller.list = async function (req, res) { const { topicsPerPage } = await user.getSettings(req.uid); let { page, after } = req.query; page = parseInt(page, 10) || 1; - let start = Math.max(0, (page - 1) * topicsPerPage); - let stop = start + topicsPerPage - 1; + const start = Math.max(0, (page - 1) * topicsPerPage); + const stop = start + topicsPerPage - 1; const [userSettings, userPrivileges] = await Promise.all([ user.getSettings(req.uid), @@ -50,27 +49,24 @@ controller.list = async function (req, res) { if (req.query.sort === 'popular') { cidQuery = { ...cidQuery, - cids: ['-1'], sort: 'posts', term: req.query.term || 'day', + includeRemote: true, + followingOnly: !req.query.all || !parseInt(req.query.all, 10), }; delete cidQuery.cid; ({ tids, topicCount } = await topics.getSortedTopics(cidQuery)); tids = tids.slice(start, stop !== -1 ? stop + 1 : undefined); } else { - if (after) { - // Update start/stop with values inferred from `after` - const set = await categories.buildTopicsSortedSet(cidQuery); - const index = await db.sortedSetRevRank(set, decodeURIComponent(after)); - if (index && start - index < 1) { - const count = stop - start; - start = index + 1; - stop = start + count; - } - } - - tids = await categories.getTopicIds({ ...cidQuery, start, stop }); - topicCount = await categories.getTopicCount(cidQuery); + cidQuery = { + ...cidQuery, + term: req.query.term, + includeRemote: true, + followingOnly: !req.query.all || !parseInt(req.query.all, 10), + }; + delete cidQuery.cid; + ({ tids, topicCount } = await topics.getSortedTopics(cidQuery)); + tids = tids.slice(start, stop !== -1 ? stop + 1 : undefined); } data.topicCount = topicCount; diff --git a/src/topics/sorted.js b/src/topics/sorted.js index 1d4da1f772..6756fbfe38 100644 --- a/src/topics/sorted.js +++ b/src/topics/sorted.js @@ -46,7 +46,8 @@ module.exports = function (Topics) { let tids; if (params.term !== 'alltime') { if (params.sort === 'posts') { - tids = await getTidsWithMostPostsInTerm(params.cids, params.uid, params.term); + const { cids, uid, term, includeRemote, followingOnly } = params; + tids = await getTidsWithMostPostsInTerm({ cids, uid, term, includeRemote, followingOnly }); } else { const cids = await getCids(params.cids, params.uid); tids = await Topics.getLatestTidsFromSet( @@ -74,17 +75,18 @@ module.exports = function (Topics) { } async function getInbox(tids, params) { - if (!Array.isArray(params.cids) || !params.cids.includes('-1')) { + if (!params.includeRemote) { return tids; } let inbox; + const set = params.followingOnly ? `uid:${params.uid}:inbox` : 'cid:-1:tids'; if (params.term !== 'alltime') { const method = params.sort === 'old' ? 'getSortedSetRangeByScore' : 'getSortedSetRevRangeByScore'; inbox = await db[method]( - `uid:${params.uid}:inbox`, + set, 0, 1000, '+inf', @@ -94,7 +96,7 @@ module.exports = function (Topics) { const method = params.sort === 'old' ? 'getSortedSetRange' : 'getSortedSetRevRange'; - inbox = await db[method](`uid:${params.uid}:inbox`, 0, meta.config.recentMaxTopics - 1); + inbox = await db[method](set, 0, meta.config.recentMaxTopics - 1); } return _.uniq(tids.concat(inbox)); @@ -115,33 +117,41 @@ module.exports = function (Topics) { return 'topics:recent'; } - async function getCids(cids, uid) { + async function getCids(cids, uid, includeRemote) { if (Array.isArray(cids)) { cids = await privileges.categories.filterCids('topics:read', cids, uid); } else { cids = await categories.getCidsByPrivilege('categories:cid', uid, 'topics:read'); cids = cids.filter(cid => cid !== -1); } + + if (includeRemote) { + const remoteCids = await db.getObjectValues('handle:cid'); + cids = [-1, ...cids, ...remoteCids]; + } return cids; } - async function getTidsWithMostPostsInTerm(cids, uid, term) { - cids = await getCids(cids, uid); - const pids = await db.getSortedSetRevRangeByScore( - cids.map(cid => `cid:${cid}:pids`), + async function getTidsWithMostPostsInTerm({ cids, uid, term, includeRemote, followingOnly }) { + cids = await getCids(cids, uid, includeRemote); + const sets = cids.map(cid => `cid:${cid}:tids`); + if (followingOnly && sets.includes('cid:-1:tids')) { + sets.splice(sets.indexOf('cid:-1:tids'), 1, `uid:${uid}:inbox`); + } + const tids = await db.getSortedSetRevRangeByScore( + sets, 0, 1000, '+inf', Date.now() - Topics.getSinceFromTerm(term) ); - const postObjs = await db.getObjectsFields(pids.map(pid => `post:${pid}`), ['tid']); const tidToCount = {}; - postObjs.forEach((post) => { - tidToCount[post.tid] = tidToCount[post.tid] || 0; - tidToCount[post.tid] += 1; + tids.forEach((tid) => { + tidToCount[tid] = tidToCount[tid] || 0; + tidToCount[tid] += 1; }); - return _.uniq(postObjs.map(post => String(post.tid))) + return _.uniq(tids) .sort((t1, t2) => tidToCount[t2] - tidToCount[t1]); } @@ -200,7 +210,7 @@ module.exports = function (Topics) { } async function sortTids(tids, params) { - if (params.term === 'alltime' && !params.cids && !params.tags.length && params.filter !== 'watched' && !params.floatPinned) { + if (params.term === 'alltime' && !params.cids && !params.tags.length && params.filter !== 'watched' && !params.floatPinned && !params.includeRemote) { return tids; } From 702f7c6251323fbf5525e6350c2610c4ba972115 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Fri, 13 Mar 2026 16:48:21 +0000 Subject: [PATCH 152/204] chore(i18n): fallback strings for new resources: nodebb.world --- public/language/ar/world.json | 3 ++- public/language/az/world.json | 3 ++- public/language/bg/world.json | 3 ++- public/language/bn/world.json | 3 ++- public/language/cs/world.json | 3 ++- public/language/da/world.json | 3 ++- public/language/de/world.json | 3 ++- public/language/el/world.json | 3 ++- public/language/en-US/world.json | 3 ++- public/language/en-x-pirate/world.json | 3 ++- public/language/es/world.json | 3 ++- public/language/et/world.json | 3 ++- public/language/fa-IR/world.json | 3 ++- public/language/fi/world.json | 3 ++- public/language/fr/world.json | 3 ++- public/language/gl/world.json | 3 ++- public/language/he/world.json | 3 ++- public/language/hr/world.json | 3 ++- public/language/hu/world.json | 3 ++- public/language/hy/world.json | 3 ++- public/language/id/world.json | 3 ++- public/language/it/world.json | 3 ++- public/language/ja/world.json | 3 ++- public/language/ko/world.json | 3 ++- public/language/lt/world.json | 3 ++- public/language/lv/world.json | 3 ++- public/language/ms/world.json | 3 ++- public/language/nb/world.json | 3 ++- public/language/nl/world.json | 3 ++- public/language/nn-NO/world.json | 3 ++- public/language/pl/world.json | 3 ++- public/language/pt-BR/world.json | 3 ++- public/language/pt-PT/world.json | 3 ++- public/language/ro/world.json | 3 ++- public/language/ru/world.json | 3 ++- public/language/rw/world.json | 3 ++- public/language/sc/world.json | 3 ++- public/language/sk/world.json | 3 ++- public/language/sl/world.json | 3 ++- public/language/sq-AL/world.json | 3 ++- public/language/sr/world.json | 3 ++- public/language/sv/world.json | 3 ++- public/language/th/world.json | 3 ++- public/language/tr/world.json | 3 ++- public/language/uk/world.json | 3 ++- public/language/ur/world.json | 3 ++- public/language/vi/world.json | 3 ++- public/language/zh-CN/world.json | 3 ++- public/language/zh-TW/world.json | 3 ++- 49 files changed, 98 insertions(+), 49 deletions(-) diff --git a/public/language/ar/world.json b/public/language/ar/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/ar/world.json +++ b/public/language/ar/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/az/world.json b/public/language/az/world.json index bf503c1e9e..f4101247d6 100644 --- a/public/language/az/world.json +++ b/public/language/az/world.json @@ -1,6 +1,7 @@ { "name": "Dünya", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/bg/world.json b/public/language/bg/world.json index cec323447a..0f78ca89f7 100644 --- a/public/language/bg/world.json +++ b/public/language/bg/world.json @@ -1,6 +1,7 @@ { "name": "Свят", - "latest": "Най-нови", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Популярни (за деня)", "popular-week": "Популярни (за седмицата)", "popular-month": "Популярни (за месеца)", diff --git a/public/language/bn/world.json b/public/language/bn/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/bn/world.json +++ b/public/language/bn/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/cs/world.json b/public/language/cs/world.json index 03b928bc9e..5d64fc0acd 100644 --- a/public/language/cs/world.json +++ b/public/language/cs/world.json @@ -1,6 +1,7 @@ { "name": "Svět", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/da/world.json b/public/language/da/world.json index 854bd7f7fa..ed22d19f06 100644 --- a/public/language/da/world.json +++ b/public/language/da/world.json @@ -1,6 +1,7 @@ { "name": "Verden", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/de/world.json b/public/language/de/world.json index aae1add79b..95e16612b9 100644 --- a/public/language/de/world.json +++ b/public/language/de/world.json @@ -1,6 +1,7 @@ { "name": "Welt", - "latest": "Letzte", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Beliebt (Tag)", "popular-week": "Beliebt (Woche)", "popular-month": "Beliebt (Monat)", diff --git a/public/language/el/world.json b/public/language/el/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/el/world.json +++ b/public/language/el/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/en-US/world.json b/public/language/en-US/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/en-US/world.json +++ b/public/language/en-US/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/en-x-pirate/world.json b/public/language/en-x-pirate/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/en-x-pirate/world.json +++ b/public/language/en-x-pirate/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/es/world.json b/public/language/es/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/es/world.json +++ b/public/language/es/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/et/world.json b/public/language/et/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/et/world.json +++ b/public/language/et/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/fa-IR/world.json b/public/language/fa-IR/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/fa-IR/world.json +++ b/public/language/fa-IR/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/fi/world.json b/public/language/fi/world.json index 013c363443..87d193efa9 100644 --- a/public/language/fi/world.json +++ b/public/language/fi/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/fr/world.json b/public/language/fr/world.json index b419024fc9..fb839a8ce8 100644 --- a/public/language/fr/world.json +++ b/public/language/fr/world.json @@ -1,6 +1,7 @@ { "name": "Monde", - "latest": "Dernier", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Populaires (Jour)", "popular-week": "Populaires (Semaine)", "popular-month": "Populaires (Mois)", diff --git a/public/language/gl/world.json b/public/language/gl/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/gl/world.json +++ b/public/language/gl/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/he/world.json b/public/language/he/world.json index df588a6375..9235d136c3 100644 --- a/public/language/he/world.json +++ b/public/language/he/world.json @@ -1,6 +1,7 @@ { "name": "עולם", - "latest": "אחרונים", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "פופולרי (יומי)", "popular-week": "פופולרי (שבועי)", "popular-month": "פופולרי (חודשי)", diff --git a/public/language/hr/world.json b/public/language/hr/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/hr/world.json +++ b/public/language/hr/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/hu/world.json b/public/language/hu/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/hu/world.json +++ b/public/language/hu/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/hy/world.json b/public/language/hy/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/hy/world.json +++ b/public/language/hy/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/id/world.json b/public/language/id/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/id/world.json +++ b/public/language/id/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/it/world.json b/public/language/it/world.json index 7cf33e04dd..859fdbedcd 100644 --- a/public/language/it/world.json +++ b/public/language/it/world.json @@ -1,6 +1,7 @@ { "name": "Mondo", - "latest": "Ultimo", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popolare (Giorno)", "popular-week": "Popolare (Settimana)", "popular-month": "Popolare (Mese)", diff --git a/public/language/ja/world.json b/public/language/ja/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/ja/world.json +++ b/public/language/ja/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/ko/world.json b/public/language/ko/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/ko/world.json +++ b/public/language/ko/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/lt/world.json b/public/language/lt/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/lt/world.json +++ b/public/language/lt/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/lv/world.json b/public/language/lv/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/lv/world.json +++ b/public/language/lv/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/ms/world.json b/public/language/ms/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/ms/world.json +++ b/public/language/ms/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/nb/world.json b/public/language/nb/world.json index 0b31cac31a..bd39706af9 100644 --- a/public/language/nb/world.json +++ b/public/language/nb/world.json @@ -1,6 +1,7 @@ { "name": "Verden", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/nl/world.json b/public/language/nl/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/nl/world.json +++ b/public/language/nl/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/nn-NO/world.json b/public/language/nn-NO/world.json index 5b7d186b92..45ae2a8b3e 100644 --- a/public/language/nn-NO/world.json +++ b/public/language/nn-NO/world.json @@ -1,6 +1,7 @@ { "name": "Verda", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/pl/world.json b/public/language/pl/world.json index e2c8e8c1f3..62f0daf537 100644 --- a/public/language/pl/world.json +++ b/public/language/pl/world.json @@ -1,6 +1,7 @@ { "name": "Świat", - "latest": "Najnowsze", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popularne (Dziś)", "popular-week": "Popularne (W Tygodniu)", "popular-month": "Popularne (W Miesiącu)", diff --git a/public/language/pt-BR/world.json b/public/language/pt-BR/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/pt-BR/world.json +++ b/public/language/pt-BR/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/pt-PT/world.json b/public/language/pt-PT/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/pt-PT/world.json +++ b/public/language/pt-PT/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/ro/world.json b/public/language/ro/world.json index 1527ca76f5..dc182ccab2 100644 --- a/public/language/ro/world.json +++ b/public/language/ro/world.json @@ -1,6 +1,7 @@ { "name": "Lumea", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/ru/world.json b/public/language/ru/world.json index 46950ee50b..e83f4ee497 100644 --- a/public/language/ru/world.json +++ b/public/language/ru/world.json @@ -1,6 +1,7 @@ { "name": "Мир", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/rw/world.json b/public/language/rw/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/rw/world.json +++ b/public/language/rw/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/sc/world.json b/public/language/sc/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/sc/world.json +++ b/public/language/sc/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/sk/world.json b/public/language/sk/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/sk/world.json +++ b/public/language/sk/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/sl/world.json b/public/language/sl/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/sl/world.json +++ b/public/language/sl/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/sq-AL/world.json b/public/language/sq-AL/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/sq-AL/world.json +++ b/public/language/sq-AL/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/sr/world.json b/public/language/sr/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/sr/world.json +++ b/public/language/sr/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/sv/world.json b/public/language/sv/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/sv/world.json +++ b/public/language/sv/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/th/world.json b/public/language/th/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/th/world.json +++ b/public/language/th/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/tr/world.json b/public/language/tr/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/tr/world.json +++ b/public/language/tr/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/uk/world.json b/public/language/uk/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/uk/world.json +++ b/public/language/uk/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/ur/world.json b/public/language/ur/world.json index ed5793dce1..df3cc4f334 100644 --- a/public/language/ur/world.json +++ b/public/language/ur/world.json @@ -1,6 +1,7 @@ { "name": "جهان", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", diff --git a/public/language/vi/world.json b/public/language/vi/world.json index 77e8e377d4..7e8d1181c8 100644 --- a/public/language/vi/world.json +++ b/public/language/vi/world.json @@ -1,6 +1,7 @@ { "name": "Thế giới", - "latest": "Mới nhất", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Phổ biến (Ngày)", "popular-week": "Phổ biến (Tuần)", "popular-month": "Phổ biến (Tháng)", diff --git a/public/language/zh-CN/world.json b/public/language/zh-CN/world.json index 94a04f3224..db61c5c92d 100644 --- a/public/language/zh-CN/world.json +++ b/public/language/zh-CN/world.json @@ -1,6 +1,7 @@ { "name": "世界", - "latest": "最新", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "热门(按天)", "popular-week": "热门(按周)", "popular-month": "热门(按月)", diff --git a/public/language/zh-TW/world.json b/public/language/zh-TW/world.json index 427135352d..58e6526fbb 100644 --- a/public/language/zh-TW/world.json +++ b/public/language/zh-TW/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest", + "latest": "Latest (Following)", + "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", "popular-month": "Popular (Month)", From 1af835641e65fa813e99ec47bff5b4aef19f1c09 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 13:19:29 -0400 Subject: [PATCH 153/204] fix: restored popular calculation behaviour that was broken by e2131d1d2e1c6f14cb8867ac7e22840da3f4c63f, removed followingOnly arg passing for popular --- src/topics/sorted.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/topics/sorted.js b/src/topics/sorted.js index 6756fbfe38..a37d249cb0 100644 --- a/src/topics/sorted.js +++ b/src/topics/sorted.js @@ -46,8 +46,8 @@ module.exports = function (Topics) { let tids; if (params.term !== 'alltime') { if (params.sort === 'posts') { - const { cids, uid, term, includeRemote, followingOnly } = params; - tids = await getTidsWithMostPostsInTerm({ cids, uid, term, includeRemote, followingOnly }); + const { cids, uid, term, includeRemote } = params; + tids = await getTidsWithMostPostsInTerm({ cids, uid, term, includeRemote }); } else { const cids = await getCids(params.cids, params.uid); tids = await Topics.getLatestTidsFromSet( @@ -132,26 +132,24 @@ module.exports = function (Topics) { return cids; } - async function getTidsWithMostPostsInTerm({ cids, uid, term, includeRemote, followingOnly }) { + async function getTidsWithMostPostsInTerm({ cids, uid, term, includeRemote }) { cids = await getCids(cids, uid, includeRemote); - const sets = cids.map(cid => `cid:${cid}:tids`); - if (followingOnly && sets.includes('cid:-1:tids')) { - sets.splice(sets.indexOf('cid:-1:tids'), 1, `uid:${uid}:inbox`); - } - const tids = await db.getSortedSetRevRangeByScore( + const sets = cids.map(cid => `cid:${cid}:pids`); + const pids = await db.getSortedSetRevRangeByScore( sets, 0, 1000, '+inf', Date.now() - Topics.getSinceFromTerm(term) ); + const postObjs = await db.getObjectsFields(pids.map(pid => `post:${pid}`), ['tid']); const tidToCount = {}; - tids.forEach((tid) => { - tidToCount[tid] = tidToCount[tid] || 0; - tidToCount[tid] += 1; + postObjs.forEach((post) => { + tidToCount[post.tid] = tidToCount[post.tid] || 0; + tidToCount[post.tid] += 1; }); - return _.uniq(tids) + return _.uniq(postObjs.map(post => String(post.tid))) .sort((t1, t2) => tidToCount[t2] - tidToCount[t1]); } From 10859455801682469082fab7158ac5d048110ba9 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 13:21:14 -0400 Subject: [PATCH 154/204] fix: bump harmony --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index c7250d637c..507f5c5a8d 100644 --- a/install/package.json +++ b/install/package.json @@ -108,7 +108,7 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.52", + "nodebb-theme-harmony": "2.2.53", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", "nodebb-theme-persona": "14.2.28", From ff1e1b9200c118795d9ff1bd1cd8b8f51107756c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 13:25:32 -0400 Subject: [PATCH 155/204] test: exclude uploadScreenshot from routeMap parsing test --- test/controllers-admin.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/controllers-admin.js b/test/controllers-admin.js index 154fec69a8..c7498eefee 100644 --- a/test/controllers-admin.js +++ b/test/controllers-admin.js @@ -629,6 +629,7 @@ describe('Admin Controllers', () => { 'uploadfavicon', 'uploadTouchIcon', 'uploadMaskableIcon', + 'uploadScreenshot', 'uploadlogo', 'uploadOgImage', 'uploadDefaultAvatar', @@ -643,7 +644,7 @@ describe('Admin Controllers', () => { await privileges.admin.give([privileges.admin.routeMap[route]], uid); ({ response: res } = await request.get(`${nconf.get('url')}/api/admin/${route}`, requestOpts)); - assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.statusCode, 200, `${route} returned ${res.statusCode} instead of 200`); await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); } From 1aa5ca88522c062ab3c522723185243993d226bb Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 13:52:19 -0400 Subject: [PATCH 156/204] fix: restore guest access to /world, default to latest(all) --- public/src/client/world.js | 23 +++++++++++++++-------- src/controllers/activitypub/topics.js | 4 ++++ src/routes/activitypub.js | 1 - 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/public/src/client/world.js b/public/src/client/world.js index ca9fda9187..b22813162b 100644 --- a/public/src/client/world.js +++ b/public/src/client/world.js @@ -36,14 +36,21 @@ define('forum/world', [ const sortOptionsEl = document.getElementById('sort-options'); if (sortLabelEl && sortOptionsEl) { const params = new URLSearchParams(window.location.search); - if (params.get('sort') === 'popular') { - translator.translate(`[[world:popular-${params.get('term')}]]`, function (translated) { - sortLabelEl.innerText = translated; - }); - } else { - translator.translate('[[world:latest]]', function (translated) { - sortLabelEl.innerText = translated; - }); + switch(params.get('sort')) { + case 'popular': { + translator.translate(`[[world:popular-${params.get('term')}]]`, function (translated) { + sortLabelEl.innerText = translated; + }); + break; + } + + default: { + console.log('here!'); + translator.translate(`[[world:latest${params.get('all') === '1' ? '-all' : ''}]]`, function (translated) { + sortLabelEl.innerText = translated; + }); + break; + } } } diff --git a/src/controllers/activitypub/topics.js b/src/controllers/activitypub/topics.js index 702893cac8..8785fecc14 100644 --- a/src/controllers/activitypub/topics.js +++ b/src/controllers/activitypub/topics.js @@ -16,6 +16,10 @@ const helpers = require('../helpers'); const controller = module.exports; controller.list = async function (req, res) { + if (!req.uid && !req.query.sort && !req.query.all) { + return helpers.redirect(res, '/world?all=1', false); + } + const { topicsPerPage } = await user.getSettings(req.uid); let { page, after } = req.query; page = parseInt(page, 10) || 1; diff --git a/src/routes/activitypub.js b/src/routes/activitypub.js index 00d8b1a125..6c13fea365 100644 --- a/src/routes/activitypub.js +++ b/src/routes/activitypub.js @@ -6,7 +6,6 @@ module.exports = function (app, middleware, controllers) { helpers.setupPageRoute(app, '/world', [ middleware.activitypub.enabled, middleware.activitypub.pageview, - middleware.ensureLoggedIn, ], controllers.activitypub.topics.list); helpers.setupPageRoute(app, '/ap', [ middleware.activitypub.enabled, From 58da90361f8bab5adc91b0a1aa82e59b87686e50 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 13:57:34 -0400 Subject: [PATCH 157/204] fix: debug log --- public/src/client/world.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/src/client/world.js b/public/src/client/world.js index b22813162b..6e63b85a23 100644 --- a/public/src/client/world.js +++ b/public/src/client/world.js @@ -45,7 +45,6 @@ define('forum/world', [ } default: { - console.log('here!'); translator.translate(`[[world:latest${params.get('all') === '1' ? '-all' : ''}]]`, function (translated) { sortLabelEl.innerText = translated; }); From d1e1a0082d9555cca3e453890932ca01f0e85156 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 14:42:48 -0400 Subject: [PATCH 158/204] fix: long-press support for topicSelect, #14045 --- public/src/client/category/tools.js | 9 ++++-- public/src/modules/topicSelect.js | 46 +++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/public/src/client/category/tools.js b/public/src/client/category/tools.js index 58a257f293..bd15ff459b 100644 --- a/public/src/client/category/tools.js +++ b/public/src/client/category/tools.js @@ -9,11 +9,16 @@ define('forum/category/tools', [ 'api', 'bootbox', 'alerts', -], function (topicSelect, threadTools, components, api, bootbox, alerts) { + 'bootstrap', +], function (topicSelect, threadTools, components, api, bootbox, alerts, bootstrap) { const CategoryTools = {}; CategoryTools.init = function (containerEl) { - topicSelect.init(updateDropdownOptions, containerEl); + topicSelect.init(updateDropdownOptions, () => { + const toggleEl = document.querySelector('.thread-tools button'); + const dropdown = new bootstrap.Dropdown(toggleEl); + dropdown.show(); + }, containerEl); handlePinnedTopicSort(); diff --git a/public/src/modules/topicSelect.js b/public/src/modules/topicSelect.js index 50d38c4ba7..54cffc4465 100644 --- a/public/src/modules/topicSelect.js +++ b/public/src/modules/topicSelect.js @@ -7,13 +7,14 @@ define('topicSelect', ['components'], function (components) { let topicsContainer; - TopicSelect.init = function (onSelect, containerEl) { + TopicSelect.init = function (onSelect, onLongPress, containerEl) { topicsContainer = containerEl || $('[component="category"]'); topicsContainer.on('selectstart', '[component="topic/select"]', function (ev) { ev.preventDefault(); }); - topicsContainer.on('click', '[component="topic/select"]', function (ev) { + let isLongPress = false; + const click = function (ev) { const select = $(this); const topicEl = select.parents('[component="category/topic"]'); if (ev.shiftKey) { @@ -28,6 +29,47 @@ define('topicSelect', ['components'], function (components) { if (typeof onSelect === 'function') { onSelect(); } + }; + topicsContainer.on('click', '[component="topic/select"]', function (ev) { + if (isLongPress) { + ev.preventDefault(); + ev.stopImmediatePropagation(); + return false; + } + + click.call(this, ev); + }); + + // Long press + let longPressTimeout; + const start = function (ev) { + if (ev.type === 'touchstart') { + ev.preventDefault(); + } + isLongPress = false; + longPressTimeout = setTimeout(() => { + isLongPress = true; + click.call(this, ev); + if (navigator.vibrate) { + navigator.vibrate(50); + } + const topicEl = this.closest('[component="category/topic"]'); + if (topicEl.classList.contains('selected')) { + onLongPress(); + } + }, 500); + }; + const cancel = () => { + clearTimeout(longPressTimeout); + }; + topicsContainer.on('mousedown', '[component="topic/select"]', start); + topicsContainer.on('touchstart', '[component="topic/select"]', start); + topicsContainer.on('mouseup', '[component="topic/select"]', cancel); + topicsContainer.on('mouseleave', '[component="topic/select"]', cancel); + topicsContainer.on('touchend', '[component="topic/select"]', cancel); + topicsContainer.on('touchcancel', '[component="topic/select"]', cancel); + topicsContainer.on('contextmenu', (e) => { + e.preventDefault(); }); }; From 533ae69c460e40e3ed43f8a94fb3829513865bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20U=C5=9Fakl=C4=B1?= Date: Fri, 13 Mar 2026 18:42:50 -0400 Subject: [PATCH 159/204] feat: allow 3 profile pics (#14092) * feat: allow 3 profile pics * test: fix notification test * test: fix user picture test * test: relative_path fixes * fix: relative_path getting saved in when updating profile pic --- public/src/modules/accounts/picture.js | 65 +++----- src/api/users.js | 11 +- src/socket.io/user/picture.js | 19 ++- .../4.10.0/user-profile-pictures-zset.js | 23 +++ src/user/data.js | 3 +- src/user/delete.js | 1 + src/user/picture.js | 150 +++++++++++------- src/views/modals/change-picture.tpl | 54 +++---- test/notifications.js | 3 +- test/user.js | 3 +- 10 files changed, 194 insertions(+), 138 deletions(-) create mode 100644 src/upgrades/4.10.0/user-profile-pictures-zset.js diff --git a/public/src/modules/accounts/picture.js b/public/src/modules/accounts/picture.js index 0bec1a24a9..651ce7deaa 100644 --- a/public/src/modules/accounts/picture.js +++ b/public/src/modules/accounts/picture.js @@ -27,7 +27,12 @@ define('accounts/picture', [ icon: { text: ajaxify.data['icon:text'], bgColor: ajaxify.data['icon:bgColor'] }, defaultAvatar: ajaxify.data.defaultAvatar, allowProfileImageUploads: ajaxify.data.allowProfileImageUploads, - iconBackgrounds: ajaxify.data.iconBackgrounds, + iconBackgrounds: ajaxify.data.iconBackgrounds.map((color) => { + return { + color, + selected: color === ajaxify.data['icon:bgColor'], + }; + }), user: { uid: ajaxify.data.uid, username: ajaxify.data.username, @@ -55,9 +60,8 @@ define('accounts/picture', [ }, }); - modal.on('shown.bs.modal', updateImages); - modal.on('click', '.list-group-item', function selectImageType() { - modal.find('.list-group-item').removeClass('active'); + modal.on('click', '[component="profile/picture/button"]', function selectImageType() { + modal.find('[component="profile/picture/button"]').removeClass('active'); $(this).addClass('active'); }); @@ -69,34 +73,17 @@ define('accounts/picture', [ handleImageUpload(modal); - function updateImages() { - // Check to see which one is the active picture - if (!ajaxify.data.picture) { - modal.find('[data-type="default"]').addClass('active'); - } else { - modal.find('.list-group-item img').each(function () { - if (this.getAttribute('src') === ajaxify.data.picture) { - $(this).parents('.list-group-item').addClass('active'); - } - }); - } - - // Update avatar background colour - const iconbgEl = modal.find(`[data-bg-color="${ajaxify.data['icon:bgColor']}"]`); - if (iconbgEl.length) { - iconbgEl.addClass('selected'); - } else { - modal.find('[data-bg-color="transparent"]').addClass('selected'); - } - } - function saveSelection() { - const type = modal.find('.list-group-item.active').attr('data-type'); + const activeBtn = modal.find('[component="profile/picture/button"].active'); + const type = activeBtn.attr('data-type'); + const picture = activeBtn.find('img').attr('src'); const iconBgColor = modal.find('[data-bg-color].selected').attr('data-bg-color') || 'transparent'; - changeUserPicture(type, iconBgColor).then(() => { + api.put(`/users/${ajaxify.data.theirid}/picture`, { + type, picture, iconBgColor, + }).then(() => { Picture.updateHeader( - type === 'default' ? '' : modal.find('.list-group-item.active img').attr('src'), + type === 'default' ? '' : picture, iconBgColor ); ajaxify.refresh(); @@ -158,13 +145,6 @@ define('accounts/picture', [ } } - function onRemoveComplete() { - if (ajaxify.data.uploadedpicture === ajaxify.data.picture) { - ajaxify.refresh(); - Picture.updateHeader(); - } - } - modal.find('[data-action="upload"]').on('click', function () { modal.modal('hide'); @@ -217,21 +197,24 @@ define('accounts/picture', [ }); modal.find('[data-action="remove-uploaded"]').on('click', function () { + const removeBtn = $(this); + const removePicture = removeBtn.attr('data-url'); socket.emit('user.removeUploadedPicture', { uid: ajaxify.data.theirid, + picture: removePicture, }, function (err) { - modal.modal('hide'); if (err) { return alerts.error(err); } - onRemoveComplete(); + removeBtn.parent().remove(); + if (removePicture === ajaxify.data.picture) { + modal.modal('hide'); + ajaxify.refresh(); + Picture.updateHeader(); + } }); }); } - function changeUserPicture(type, bgColor) { - return api.put(`/users/${ajaxify.data.theirid}/picture`, { type, bgColor }); - } - return Picture; }); diff --git a/src/api/users.js b/src/api/users.js index 369a692cc0..b962968882 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -2,7 +2,7 @@ const path = require('path'); const fs = require('fs').promises; - +const nconf = require('nconf'); const validator = require('validator'); const winston = require('winston'); @@ -627,7 +627,14 @@ usersAPI.changePicture = async (caller, data) => { if (type === 'default') { picture = ''; } else if (type === 'uploaded') { - picture = await user.getUserField(data.uid, 'uploadedpicture'); + const cleanPath = data.picture.replace(new RegExp(`^${nconf.get('relative_path')}`), ''); + const isUserPicture = await user.isUserUploadedPicture(data.uid, cleanPath); + if (isUserPicture) { + await user.setUserField(data.uid, 'uploadedpicture', cleanPath); + picture = cleanPath; + } else { + picture = ''; + } } else if (type === 'external' && url) { picture = validator.escape(url); } else { diff --git a/src/socket.io/user/picture.js b/src/socket.io/user/picture.js index 828dca61f8..d63f28e61c 100644 --- a/src/socket.io/user/picture.js +++ b/src/socket.io/user/picture.js @@ -1,5 +1,9 @@ 'use strict'; +const validator = require('validator'); +const nconf = require('nconf'); + +const db = require('../../database'); const user = require('../../user'); const plugins = require('../../plugins'); @@ -10,7 +14,7 @@ module.exports = function (SocketUser) { } await user.isAdminOrSelf(socket.uid, data.uid); // 'keepAllUserImages' is ignored, since there is explicit user intent - const userData = await user.removeProfileImage(data.uid); + const userData = await user.removeProfileImage(data.uid, data.picture); plugins.hooks.fire('action:user.removeUploadedPicture', { callerUid: socket.uid, uid: data.uid, @@ -23,27 +27,29 @@ module.exports = function (SocketUser) { throw new Error('[[error:invalid-data]]'); } - const [list, userObj] = await Promise.all([ + const [list, userObj, userPictures] = await Promise.all([ plugins.hooks.fire('filter:user.listPictures', { uid: data.uid, pictures: [], }), user.getUserData(data.uid), + db.getSortedSetRevRange(`uid:${data.uid}:profile:pictures`, 0, 2), ]); - if (userObj.uploadedpicture) { + userPictures.forEach((picture) => { list.pictures.push({ type: 'uploaded', - url: userObj.uploadedpicture, + url: `${nconf.get('relative_path')}${picture}`, text: '[[user:uploaded-picture]]', }); - } + }); // Normalize list into "user object" format list.pictures = list.pictures.map(({ type, url, text }) => ({ type, username: text, - picture: url, + picture: validator.escape(String(url)), + selected: url === userObj.picture, })); list.pictures.unshift({ @@ -51,6 +57,7 @@ module.exports = function (SocketUser) { 'icon:text': userObj['icon:text'], 'icon:bgColor': userObj['icon:bgColor'], username: '[[user:default-picture]]', + selected: !userObj.picture, }); return list.pictures; diff --git a/src/upgrades/4.10.0/user-profile-pictures-zset.js b/src/upgrades/4.10.0/user-profile-pictures-zset.js new file mode 100644 index 0000000000..98ce8f9e4f --- /dev/null +++ b/src/upgrades/4.10.0/user-profile-pictures-zset.js @@ -0,0 +1,23 @@ +'use strict'; + +const db = require('../../database'); +const batch = require('../../batch'); + +module.exports = { + name: 'Add uid::profile:pictures zset', + timestamp: Date.UTC(2026, 2, 13), + method: async function () { + const { progress } = this; + await batch.processSortedSet('users:joindate', async (uids) => { + const userData = await db.getObjects(uids.map(uid => `user:${uid}`)); + const now = Date.now(); + const bulkAdd = userData.filter(u => u && u.uploadedpicture) + .map(u => ([`uid:${u.uid}:profile:pictures`, now, u.uploadedpicture])); + await db.sortedSetAddBulk(bulkAdd); + progress.incr(uids.length); + }, { + batch: 500, + progress, + }); + }, +}; diff --git a/src/user/data.js b/src/user/data.js index d45e4a6259..fbe9fd75da 100644 --- a/src/user/data.js +++ b/src/user/data.js @@ -376,7 +376,8 @@ module.exports = function (User) { const _iconBackgrounds = [ '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#009688', '#1b5e20', '#33691e', '#827717', '#e65100', '#ff5722', - '#795548', '#607d8b', + '#795548', '#607d8b', '#00bcd4', '#ffc107', '#8bc34a', '#9e9e9e', + '#004d40', '#ad1457', ]; const data = await plugins.hooks.fire('filter:user.iconBackgrounds', { iconBackgrounds: _iconBackgrounds }); diff --git a/src/user/delete.js b/src/user/delete.js index 9deecd001b..3de2127ed0 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -134,6 +134,7 @@ module.exports = function (User) { `uid:${uid}:flag:pids`, `uid:${uid}:sessions`, `uid:${uid}:shares`, + `uid:${uid}:profile:images`, `invitation:uid:${uid}`, ]; diff --git a/src/user/picture.js b/src/user/picture.js index 9e7eaf6d00..db135b463f 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -52,7 +52,10 @@ module.exports = function (User) { const filename = `${data.uid}-profilecover-${Date.now()}${extension}`; const uploadData = await image.uploadImage(filename, `profile/uid-${data.uid}`, picture); - await deleteCurrentPicture(data.uid, 'cover:url'); + if (!meta.config['profile:keepAllUserImages']) { + await deletePicture(data.uid, 'cover:url'); + } + await User.setUserField(data.uid, 'cover:url', uploadData.url); if (data.position) { @@ -87,30 +90,11 @@ module.exports = function (User) { throw new Error('[[error:invalid-image-extension]]'); } - const normalizedPath = await convertToPNG(userPhoto.path); - const isNormalized = userPhoto.path !== normalizedPath; - - await image.resizeImage({ - path: normalizedPath, - type: isNormalized ? 'image/png' : userPhoto.type, - width: meta.config.profileImageDimension, - height: meta.config.profileImageDimension, + return await storeUserUploadedPicture(data.callerUid, data.uid, { + path: userPhoto.path, + type: userPhoto.type, + extension, }); - - const filename = generateProfileImageFilename(data.uid, extension); - const uploadedImage = await image.uploadImage(filename, `profile/uid-${data.uid}`, { - uid: data.uid, - path: normalizedPath, - name: 'profileAvatar', - }); - - await deleteCurrentPicture(data.uid, 'uploadedpicture'); - await User.updateProfile(data.callerUid, { - uid: data.uid, - uploadedpicture: uploadedImage.url, - picture: uploadedImage.url, - }, ['uploadedpicture', 'picture']); - return uploadedImage; }; // uploads image data in base64 as profile picture @@ -133,40 +117,67 @@ module.exports = function (User) { } picture.path = await image.writeImageDataToTempFile(data.imageData); - const normalizedPath = await convertToPNG(picture.path); - const isNormalized = picture.path !== normalizedPath; - picture.path = normalizedPath; - await image.resizeImage({ + + return await storeUserUploadedPicture(data.callerUid, data.uid, { path: picture.path, - type: isNormalized ? 'image/png' : type, - width: meta.config.profileImageDimension, - height: meta.config.profileImageDimension, + type, + extension, }); - - const filename = generateProfileImageFilename(data.uid, extension); - const uploadedImage = await image.uploadImage(filename, `profile/uid-${data.uid}`, picture); - - await deleteCurrentPicture(data.uid, 'uploadedpicture'); - await User.updateProfile(data.callerUid, { - uid: data.uid, - uploadedpicture: uploadedImage.url, - picture: uploadedImage.url, - }, ['uploadedpicture', 'picture']); - return uploadedImage; } finally { await file.delete(picture.path); } }; - async function deleteCurrentPicture(uid, field) { - if (meta.config['profile:keepAllUserImages']) { - return; + async function storeUserUploadedPicture(callerUid, updateUid, picture) { + const { type, extension } = picture; + const normalizedPath = await convertToPNG(picture.path); + const isNormalized = picture.path !== normalizedPath; + + await image.resizeImage({ + path: normalizedPath, + type: isNormalized ? 'image/png' : type, + width: meta.config.profileImageDimension, + height: meta.config.profileImageDimension, + }); + + const filename = generateProfileImageFilename(updateUid, extension); + const uploadedImage = await image.uploadImage(filename, `profile/uid-${updateUid}`, { + uid: updateUid, + path: picture.path, + name: 'profileAvatar', + }); + + await User.updateProfile(callerUid, { + uid: updateUid, + uploadedpicture: uploadedImage.url, + picture: uploadedImage.url, + }, ['uploadedpicture', 'picture']); + + const zsetKey = `uid:${updateUid}:profile:pictures`; + + if (!meta.config['profile:keepAllUserImages']) { + // if we are not keeping all images, only keep most recent 3 + const imagesToKeep = 3; + const previousImages = await db.getSortedSetRevRangeWithScores(zsetKey, 0, -1); + const toDeleteImages = previousImages.filter((imagePath, index) => index >= imagesToKeep - 1) + .map(image => image.value); + const toRemove = [ + ...toDeleteImages.map(imagePath => ([zsetKey, imagePath])), + ]; + + await db.sortedSetRemoveBulk(toRemove); + toDeleteImages.forEach((imagePath) => { + if (imagePath && !imagePath.startsWith('http')) { + file.delete(imagePath); + } + }); } - await deletePicture(uid, field); + await db.sortedSetAdd(zsetKey, Date.now(), uploadedImage.url); + return { url: uploadedImage.url }; } async function deletePicture(uid, field) { - const uploadPath = await getPicturePath(uid, field); + const uploadPath = await getPicturePathFromUserField(uid, field); if (uploadPath) { await file.delete(uploadPath); } @@ -207,31 +218,56 @@ module.exports = function (User) { await db.deleteObjectFields(`user:${data.uid}`, ['cover:url', 'cover:position']); }; - User.removeProfileImage = async function (uid) { + // this function expects a path without nconf.get('relative_path) prepended + User.isUserUploadedPicture = async (uid, picture) => { + return await db.isSortedSetMember(`uid:${uid}:profile:pictures`, picture); + }; + + User.removeProfileImage = async function (uid, picture) { const userData = await User.getUserFields(uid, ['uploadedpicture', 'picture']); - await deletePicture(uid, 'uploadedpicture'); - await User.setUserFields(uid, { - uploadedpicture: '', - // if current picture is uploaded picture, reset to user icon - picture: userData.uploadedpicture === userData.picture ? '' : userData.picture, - }); + if (!picture) { + picture = userData.uploadedpicture; + } + // picture has relative_path prepended, db entries don't have it, so remove it + const cleanPath = picture.replace(new RegExp(`^${nconf.get('relative_path')}`), ''); + const isUserPicture = await User.isUserUploadedPicture(uid, cleanPath); + if (isUserPicture) { + const path = getPicturePath(uid, picture); + await Promise.all([ + path && !path.startsWith('http') ? file.delete(path) : null, + db.sortedSetRemove(`uid:${uid}:profile:pictures`, cleanPath), + ]); + if (picture === userData.picture) { + // if deleting current uploaded picture, reset to user icon + await User.setUserFields(uid, { + uploadedpicture: '', + picture: '', + }); + } + } + return userData; }; User.getLocalCoverPath = async function (uid) { - return getPicturePath(uid, 'cover:url'); + return await getPicturePathFromUserField(uid, 'cover:url'); }; User.getLocalAvatarPath = async function (uid) { - return getPicturePath(uid, 'uploadedpicture'); + return await getPicturePathFromUserField(uid, 'uploadedpicture'); }; - async function getPicturePath(uid, field) { + async function getPicturePathFromUserField(uid, field) { const value = await User.getUserField(uid, field); + return getPicturePath(uid, value); + } + + function getPicturePath(uid, value) { if (!value || !value.startsWith(`${nconf.get('relative_path')}/assets/uploads/profile/uid-${uid}`)) { return false; } const filename = value.split('/').pop(); return path.join(nconf.get('upload_path'), `profile/uid-${uid}`, filename); } + }; diff --git a/src/views/modals/change-picture.tpl b/src/views/modals/change-picture.tpl index f0c16744dc..390f4e95d4 100644 --- a/src/views/modals/change-picture.tpl +++ b/src/views/modals/change-picture.tpl @@ -1,43 +1,39 @@ +
+ + +
From aeb530433fc9449b02b2042dc811f1788cb170c5 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Mon, 16 Mar 2026 19:35:14 +0000 Subject: [PATCH 176/204] chore(i18n): fallback strings for new resources: nodebb.admin-settings-activitypub --- public/language/ar/admin/settings/activitypub.json | 3 ++- public/language/az/admin/settings/activitypub.json | 3 ++- public/language/bg/admin/settings/activitypub.json | 3 ++- public/language/bn/admin/settings/activitypub.json | 3 ++- public/language/cs/admin/settings/activitypub.json | 3 ++- public/language/da/admin/settings/activitypub.json | 3 ++- public/language/de/admin/settings/activitypub.json | 3 ++- public/language/el/admin/settings/activitypub.json | 3 ++- public/language/en-US/admin/settings/activitypub.json | 3 ++- public/language/en-x-pirate/admin/settings/activitypub.json | 3 ++- public/language/es/admin/settings/activitypub.json | 3 ++- public/language/et/admin/settings/activitypub.json | 3 ++- public/language/fa-IR/admin/settings/activitypub.json | 3 ++- public/language/fi/admin/settings/activitypub.json | 3 ++- public/language/fr/admin/settings/activitypub.json | 3 ++- public/language/gl/admin/settings/activitypub.json | 3 ++- public/language/he/admin/settings/activitypub.json | 3 ++- public/language/hr/admin/settings/activitypub.json | 3 ++- public/language/hu/admin/settings/activitypub.json | 3 ++- public/language/hy/admin/settings/activitypub.json | 3 ++- public/language/id/admin/settings/activitypub.json | 3 ++- public/language/it/admin/settings/activitypub.json | 3 ++- public/language/ja/admin/settings/activitypub.json | 3 ++- public/language/ko/admin/settings/activitypub.json | 3 ++- public/language/lt/admin/settings/activitypub.json | 3 ++- public/language/lv/admin/settings/activitypub.json | 3 ++- public/language/ms/admin/settings/activitypub.json | 3 ++- public/language/nb/admin/settings/activitypub.json | 3 ++- public/language/nl/admin/settings/activitypub.json | 3 ++- public/language/nn-NO/admin/settings/activitypub.json | 3 ++- public/language/pl/admin/settings/activitypub.json | 3 ++- public/language/pt-BR/admin/settings/activitypub.json | 3 ++- public/language/pt-PT/admin/settings/activitypub.json | 3 ++- public/language/ro/admin/settings/activitypub.json | 3 ++- public/language/ru/admin/settings/activitypub.json | 3 ++- public/language/rw/admin/settings/activitypub.json | 3 ++- public/language/sc/admin/settings/activitypub.json | 3 ++- public/language/sk/admin/settings/activitypub.json | 3 ++- public/language/sl/admin/settings/activitypub.json | 3 ++- public/language/sq-AL/admin/settings/activitypub.json | 3 ++- public/language/sr/admin/settings/activitypub.json | 3 ++- public/language/sv/admin/settings/activitypub.json | 3 ++- public/language/th/admin/settings/activitypub.json | 3 ++- public/language/tr/admin/settings/activitypub.json | 3 ++- public/language/uk/admin/settings/activitypub.json | 3 ++- public/language/ur/admin/settings/activitypub.json | 3 ++- public/language/vi/admin/settings/activitypub.json | 3 ++- public/language/zh-CN/admin/settings/activitypub.json | 3 ++- public/language/zh-TW/admin/settings/activitypub.json | 3 ++- 49 files changed, 98 insertions(+), 49 deletions(-) diff --git a/public/language/ar/admin/settings/activitypub.json b/public/language/ar/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/ar/admin/settings/activitypub.json +++ b/public/language/ar/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/az/admin/settings/activitypub.json b/public/language/az/admin/settings/activitypub.json index c528187a81..d68d9dbdaf 100644 --- a/public/language/az/admin/settings/activitypub.json +++ b/public/language/az/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/bg/admin/settings/activitypub.json b/public/language/bg/admin/settings/activitypub.json index d2e19f1aa2..4923a53f7f 100644 --- a/public/language/bg/admin/settings/activitypub.json +++ b/public/language/bg/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Брой знаци, след които се създава обобщение", "content.summary-limit-help": "Когато дадено съдържание е федерирано в посока извън този форум и превишава този брой знаци, ще се създаде обобщение, което се състои от всички завършени изречения до ограничението. (по подразбиране: 500)", "content.break-string": "Разделител на съдържанието", - "content.break-string-help": "Този разделител може да бъде поставен ръчно от потребителите с по-високи правомощия, когато създават нови теми. Той казва на NodeBB да използва съдържанието до така посоченото място като част от обобщението. Ако този разделител не бъде използван, се прилага зададеният брой на знаците. (по подразбиране: [...])" + "content.break-string-help": "Този разделител може да бъде поставен ръчно от потребителите с по-високи правомощия, когато създават нови теми. Той казва на NodeBB да използва съдържанието до така посоченото място като част от обобщението. Ако този разделител не бъде използван, се прилага зададеният брой на знаците. (по подразбиране: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/bn/admin/settings/activitypub.json b/public/language/bn/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/bn/admin/settings/activitypub.json +++ b/public/language/bn/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/cs/admin/settings/activitypub.json b/public/language/cs/admin/settings/activitypub.json index 111cdfa472..7cbf3281c2 100644 --- a/public/language/cs/admin/settings/activitypub.json +++ b/public/language/cs/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/da/admin/settings/activitypub.json b/public/language/da/admin/settings/activitypub.json index f5726b8b58..49819c963e 100644 --- a/public/language/da/admin/settings/activitypub.json +++ b/public/language/da/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/de/admin/settings/activitypub.json b/public/language/de/admin/settings/activitypub.json index ca3af062c9..bc119ed32b 100644 --- a/public/language/de/admin/settings/activitypub.json +++ b/public/language/de/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Anzahl der Zeichen, nach der eine Zusammenfassung erstellt wird", "content.summary-limit-help": "Wenn Inhalte rausgeschickt werden, die diese Zeichenanzahl überschreiten, wird eine Zusammenfassung erstellt, die alle vollständigen Sätze vor dieser Grenze enthält. (Standard: 500)", "content.break-string": "Notiz/Artikel-Trennzeichen", - "content.break-string-help": "\nDieses Trennzeichen kann von Power-Usern beim Erstellen neuer Themen manuell eingefügt werden. Es sagt NodeBB, dass der Inhalt bis zu dieser Stelle als Teil der Zusammenfassungverwendet werden soll. Wenn diese Zeichenfolge nicht benutzt wird, greift die Standardregel für die Zeichenanzahl. (Standard: [...])" + "content.break-string-help": "\nDieses Trennzeichen kann von Power-Usern beim Erstellen neuer Themen manuell eingefügt werden. Es sagt NodeBB, dass der Inhalt bis zu dieser Stelle als Teil der Zusammenfassungverwendet werden soll. Wenn diese Zeichenfolge nicht benutzt wird, greift die Standardregel für die Zeichenanzahl. (Standard: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/el/admin/settings/activitypub.json b/public/language/el/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/el/admin/settings/activitypub.json +++ b/public/language/el/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/en-US/admin/settings/activitypub.json b/public/language/en-US/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/en-US/admin/settings/activitypub.json +++ b/public/language/en-US/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/en-x-pirate/admin/settings/activitypub.json b/public/language/en-x-pirate/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/en-x-pirate/admin/settings/activitypub.json +++ b/public/language/en-x-pirate/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/es/admin/settings/activitypub.json b/public/language/es/admin/settings/activitypub.json index 7dca33aad3..033001a12f 100644 --- a/public/language/es/admin/settings/activitypub.json +++ b/public/language/es/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/et/admin/settings/activitypub.json b/public/language/et/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/et/admin/settings/activitypub.json +++ b/public/language/et/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/fa-IR/admin/settings/activitypub.json b/public/language/fa-IR/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/fa-IR/admin/settings/activitypub.json +++ b/public/language/fa-IR/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/fi/admin/settings/activitypub.json b/public/language/fi/admin/settings/activitypub.json index c43363e641..678232c912 100644 --- a/public/language/fi/admin/settings/activitypub.json +++ b/public/language/fi/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/fr/admin/settings/activitypub.json b/public/language/fr/admin/settings/activitypub.json index 566f2e7e00..2fde051d4e 100644 --- a/public/language/fr/admin/settings/activitypub.json +++ b/public/language/fr/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Nombre de caractères déclenchant la génération d’un résumé", "content.summary-limit-help": "Lorsque le contenu fédéré dépasse ce nombre de caractères, un résumé est généré à partir de toutes les phrases complètes situées avant cette limite. (Par défaut : 500)", "content.break-string": "Séparateur note/article", - "content.break-string-help": "Ce séparateur peut être inséré manuellement par les utilisateurs avancés lors de la création de nouveaux sujets. Il indique à NodeBB d’utiliser le contenu jusqu’à cet emplacement comme résumé. Si cette chaîne n’est pas utilisée, la limite de caractères par défaut s’applique. (Par défaut : [...])" + "content.break-string-help": "Ce séparateur peut être inséré manuellement par les utilisateurs avancés lors de la création de nouveaux sujets. Il indique à NodeBB d’utiliser le contenu jusqu’à cet emplacement comme résumé. Si cette chaîne n’est pas utilisée, la limite de caractères par défaut s’applique. (Par défaut : [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/gl/admin/settings/activitypub.json b/public/language/gl/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/gl/admin/settings/activitypub.json +++ b/public/language/gl/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/he/admin/settings/activitypub.json b/public/language/he/admin/settings/activitypub.json index a2add15547..cf39104619 100644 --- a/public/language/he/admin/settings/activitypub.json +++ b/public/language/he/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "מכסת תווים שמעבר לה ייווצר תקציר", "content.summary-limit-help": "כאשר תוכן המופץ החוצה חורג ממכסת תווים זו, ייווצר תקצירהמורכב מכל המשפטים השלמים שלפני המגבלה. (ברירת מחדל: 500)", "content.break-string": "מפריד הערת סיכום/מאמר", - "content.break-string-help": "משתמשים מתקדמים יכולים להזין את המפריד הזה ידנית בעת כתיבת נושאים חדשים. הוא מורה ל-NodeBB להשתמש בתוכן שעד לנקודה זו כחלק מה- תקציר . אם לא נעשה שימוש במחרוזת זו, תופעל חלופת ספירת התווים. (ברירת מחדל: [...] )" + "content.break-string-help": "משתמשים מתקדמים יכולים להזין את המפריד הזה ידנית בעת כתיבת נושאים חדשים. הוא מורה ל-NodeBB להשתמש בתוכן שעד לנקודה זו כחלק מה- תקציר . אם לא נעשה שימוש במחרוזת זו, תופעל חלופת ספירת התווים. (ברירת מחדל: [...] )", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/hr/admin/settings/activitypub.json b/public/language/hr/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/hr/admin/settings/activitypub.json +++ b/public/language/hr/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/hu/admin/settings/activitypub.json b/public/language/hu/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/hu/admin/settings/activitypub.json +++ b/public/language/hu/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/hy/admin/settings/activitypub.json b/public/language/hy/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/hy/admin/settings/activitypub.json +++ b/public/language/hy/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/id/admin/settings/activitypub.json b/public/language/id/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/id/admin/settings/activitypub.json +++ b/public/language/id/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/it/admin/settings/activitypub.json b/public/language/it/admin/settings/activitypub.json index 792edca984..320400e725 100644 --- a/public/language/it/admin/settings/activitypub.json +++ b/public/language/it/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Numero di caratteri dopo il quale sarà generato un riepilogo", "content.summary-limit-help": "Quando il contenuto federato supera questo limite di caratteri, sarà generato un riepilogo che comprende tutte le frasi complete precedenti a tale limite. (Predefinito: 500)\n ", "content.break-string": "Delimitatore di nota/articolo", - "content.break-string-help": "Questo delimitatore può essere inserito manualmente dagli utenti esperti durante la composizione di nuove discussioni. Indica a NodeBB di usare il contenuto fino a quel punto come parte del riepilogo. Se questa stringa non viene usata, si applica il conteggio dei caratteri di riserva. (Predefinito: [...])" + "content.break-string-help": "Questo delimitatore può essere inserito manualmente dagli utenti esperti durante la composizione di nuove discussioni. Indica a NodeBB di usare il contenuto fino a quel punto come parte del riepilogo. Se questa stringa non viene usata, si applica il conteggio dei caratteri di riserva. (Predefinito: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/ja/admin/settings/activitypub.json b/public/language/ja/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/ja/admin/settings/activitypub.json +++ b/public/language/ja/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/ko/admin/settings/activitypub.json b/public/language/ko/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/ko/admin/settings/activitypub.json +++ b/public/language/ko/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/lt/admin/settings/activitypub.json b/public/language/lt/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/lt/admin/settings/activitypub.json +++ b/public/language/lt/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/lv/admin/settings/activitypub.json b/public/language/lv/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/lv/admin/settings/activitypub.json +++ b/public/language/lv/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/ms/admin/settings/activitypub.json b/public/language/ms/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/ms/admin/settings/activitypub.json +++ b/public/language/ms/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/nb/admin/settings/activitypub.json b/public/language/nb/admin/settings/activitypub.json index 312603a4ff..63597bafc7 100644 --- a/public/language/nb/admin/settings/activitypub.json +++ b/public/language/nb/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/nl/admin/settings/activitypub.json b/public/language/nl/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/nl/admin/settings/activitypub.json +++ b/public/language/nl/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/nn-NO/admin/settings/activitypub.json b/public/language/nn-NO/admin/settings/activitypub.json index 016130ebb9..c573484437 100644 --- a/public/language/nn-NO/admin/settings/activitypub.json +++ b/public/language/nn-NO/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/pl/admin/settings/activitypub.json b/public/language/pl/admin/settings/activitypub.json index 746b544e8c..66f6baa799 100644 --- a/public/language/pl/admin/settings/activitypub.json +++ b/public/language/pl/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/pt-BR/admin/settings/activitypub.json b/public/language/pt-BR/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/pt-BR/admin/settings/activitypub.json +++ b/public/language/pt-BR/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/pt-PT/admin/settings/activitypub.json b/public/language/pt-PT/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/pt-PT/admin/settings/activitypub.json +++ b/public/language/pt-PT/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/ro/admin/settings/activitypub.json b/public/language/ro/admin/settings/activitypub.json index 79dfbd9e30..f4d9a3d629 100644 --- a/public/language/ro/admin/settings/activitypub.json +++ b/public/language/ro/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/ru/admin/settings/activitypub.json b/public/language/ru/admin/settings/activitypub.json index be8f21ea1a..11d3ce79b5 100644 --- a/public/language/ru/admin/settings/activitypub.json +++ b/public/language/ru/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/rw/admin/settings/activitypub.json b/public/language/rw/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/rw/admin/settings/activitypub.json +++ b/public/language/rw/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/sc/admin/settings/activitypub.json b/public/language/sc/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/sc/admin/settings/activitypub.json +++ b/public/language/sc/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/sk/admin/settings/activitypub.json b/public/language/sk/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/sk/admin/settings/activitypub.json +++ b/public/language/sk/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/sl/admin/settings/activitypub.json b/public/language/sl/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/sl/admin/settings/activitypub.json +++ b/public/language/sl/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/sq-AL/admin/settings/activitypub.json b/public/language/sq-AL/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/sq-AL/admin/settings/activitypub.json +++ b/public/language/sq-AL/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/sr/admin/settings/activitypub.json b/public/language/sr/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/sr/admin/settings/activitypub.json +++ b/public/language/sr/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/sv/admin/settings/activitypub.json b/public/language/sv/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/sv/admin/settings/activitypub.json +++ b/public/language/sv/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/th/admin/settings/activitypub.json b/public/language/th/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/th/admin/settings/activitypub.json +++ b/public/language/th/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/tr/admin/settings/activitypub.json b/public/language/tr/admin/settings/activitypub.json index a4be3b7879..707c485bf2 100644 --- a/public/language/tr/admin/settings/activitypub.json +++ b/public/language/tr/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/uk/admin/settings/activitypub.json b/public/language/uk/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/uk/admin/settings/activitypub.json +++ b/public/language/uk/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/ur/admin/settings/activitypub.json b/public/language/ur/admin/settings/activitypub.json index 34869aa058..fea9adb3a6 100644 --- a/public/language/ur/admin/settings/activitypub.json +++ b/public/language/ur/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/vi/admin/settings/activitypub.json b/public/language/vi/admin/settings/activitypub.json index 4343357b7d..e1f3cd8804 100644 --- a/public/language/vi/admin/settings/activitypub.json +++ b/public/language/vi/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Số lượng ký tự sau đó bản tóm tắt sẽ được tạo.", "content.summary-limit-help": "Khi nội dung được phân phối vượt quá số lượng ký tự này, tóm tắt được tạo ra, bao gồm tất cả các câu hoàn chỉnh trước giới hạn này. (Mặc định: 500)", "content.break-string": "Dấu phân cách Ghi chú/Bài đăng", - "content.break-string-help": "Dấu phân cách này có thể được người dùng thành thạo chèn thủ công khi soạn thảo chủ đề mới. Nó hướng dẫn NodeBB sử dụng nội dung cho đến thời điểm đó như một phần của tóm tắt. Nếu chuỗi này không được sử dụng, thì phương án dự phòng dựa trên số lượng ký tự sẽ được áp dụng. (Mặc định: [...])" + "content.break-string-help": "Dấu phân cách này có thể được người dùng thành thạo chèn thủ công khi soạn thảo chủ đề mới. Nó hướng dẫn NodeBB sử dụng nội dung cho đến thời điểm đó như một phần của tóm tắt. Nếu chuỗi này không được sử dụng, thì phương án dự phòng dựa trên số lượng ký tự sẽ được áp dụng. (Mặc định: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/settings/activitypub.json b/public/language/zh-CN/admin/settings/activitypub.json index 24d31ff887..75308dab20 100644 --- a/public/language/zh-CN/admin/settings/activitypub.json +++ b/public/language/zh-CN/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "生成摘要后的字符计数", "content.summary-limit-help": "当联合输出内容超过此字符限制时,将生成 摘要 ,包含该限制前的所有完整句子。(默认值:500)", "content.break-string": "注释/文章分隔符", - "content.break-string-help": "此分隔符可由高级用户在撰写新主题时手动插入。它指示 NodeBB 将该分隔符之前的内容作为摘要的一部分。若未使用此字符串,则采用字符计数备用方案。(默认值:[...])" + "content.break-string-help": "此分隔符可由高级用户在撰写新主题时手动插入。它指示 NodeBB 将该分隔符之前的内容作为摘要的一部分。若未使用此字符串,则采用字符计数备用方案。(默认值:[...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/language/zh-TW/admin/settings/activitypub.json b/public/language/zh-TW/admin/settings/activitypub.json index 9718851b7d..3a6e0f137a 100644 --- a/public/language/zh-TW/admin/settings/activitypub.json +++ b/public/language/zh-TW/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file From 779a372fc5b3ab56cb464cdfe5d9b0e276cc1c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 16 Mar 2026 20:31:24 -0400 Subject: [PATCH 177/204] test: add missing selectedCategory to world.yaml fix url of plugins that are missing it in plugin.json and look for repository.url for backup --- public/openapi/read/world.yaml | 3 +++ src/plugins/data.js | 1 + 2 files changed, 4 insertions(+) diff --git a/public/openapi/read/world.yaml b/public/openapi/read/world.yaml index 450d49e362..d2cbe0b332 100644 --- a/public/openapi/read/world.yaml +++ b/public/openapi/read/world.yaml @@ -31,6 +31,9 @@ get: properties: bookmarks: type: number + selectedCategory: + $ref: ../components/schemas/CategoryObject.yaml#/CategoryObject + nullable: true showThumbs: type: boolean showTopicTools: diff --git a/src/plugins/data.js b/src/plugins/data.js index ba6e319e78..0078fa34b8 100644 --- a/src/plugins/data.js +++ b/src/plugins/data.js @@ -55,6 +55,7 @@ Data.loadPluginInfo = async function (pluginPath) { pluginData.description = packageData.description; pluginData.version = packageData.version; pluginData.repository = packageData.repository; + pluginData.url = pluginData.url || pluginData?.repository?.url || ''; pluginData.nbbpm = packageData.nbbpm; pluginData.path = pluginPath; } catch (err) { From 19bb37cae8f80307b17ae3eac3a2b75362610949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 16 Mar 2026 20:39:37 -0400 Subject: [PATCH 178/204] lint: remove unused --- public/src/client/header/notifications.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/src/client/header/notifications.js b/public/src/client/header/notifications.js index 6d833ccbd6..71c50ea726 100644 --- a/public/src/client/header/notifications.js +++ b/public/src/client/header/notifications.js @@ -5,7 +5,6 @@ define('forum/header/notifications', function () { notifications.prepareDOM = function () { const notifTrigger = $('[component="notifications"] [data-bs-toggle="dropdown"]'); - const listEl = document.querySelector('[component="notifications/list"]'); notifTrigger.on('show.bs.dropdown', async (ev) => { const notifications = await app.require('notifications'); From d458eadb6628998ae5ced5cba53677a8f61967b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 16 Mar 2026 21:07:44 -0400 Subject: [PATCH 179/204] add active class to selected filter show no-notifs if there are no unreads --- install/package.json | 2 +- public/src/client/header/notifications.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index ee1a0c15d6..fa702393fb 100644 --- a/install/package.json +++ b/install/package.json @@ -108,7 +108,7 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.58", + "nodebb-theme-harmony": "2.2.59", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", "nodebb-theme-persona": "14.2.32", diff --git a/public/src/client/header/notifications.js b/public/src/client/header/notifications.js index 71c50ea726..94a23c9ecd 100644 --- a/public/src/client/header/notifications.js +++ b/public/src/client/header/notifications.js @@ -24,7 +24,8 @@ define('forum/header/notifications', function () { dropdownEl.on('click', '[data-filter]', (e) => { const filter = e.target.getAttribute('data-filter'); - + dropdownEl.find('[data-filter]').removeClass('active'); + e.target.classList.add('active'); if (filter === 'unread') { listEl.get(0).querySelectorAll('[data-nid]:not(.unread)').forEach((e) => { e.classList.toggle('hidden', true); @@ -34,6 +35,8 @@ define('forum/header/notifications', function () { e.classList.toggle('hidden', false); }); } + const visibleNotifCount = dropdownEl.find('[data-nid]:not(.hidden)').length; + dropdownEl.find('.no-notifs').toggleClass('hidden', visibleNotifCount !== 0); }); }); From d5f4a3702067d0e6f176e71fde5c287df875a39b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 21:11:13 -0400 Subject: [PATCH 180/204] fix(deps): update dependency tough-cookie to v6.0.1 (#14097) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index fa702393fb..0238672e62 100644 --- a/install/package.json +++ b/install/package.json @@ -149,7 +149,7 @@ "timeago": "1.6.7", "tinycon": "0.6.8", "toobusy-js": "0.5.1", - "tough-cookie": "6.0.0", + "tough-cookie": "6.0.1", "undici": "^7.10.0", "validator": "13.15.26", "webpack": "5.105.4", From 73b023b430d8dcf5aaa92647286009e100f89315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 17 Mar 2026 10:42:35 -0400 Subject: [PATCH 181/204] chore: up composer --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 4a9cfa33a8..3fad170317 100644 --- a/install/package.json +++ b/install/package.json @@ -97,7 +97,7 @@ "multer": "2.1.1", "nconf": "0.13.0", "nodebb-plugin-2factor": "7.6.1", - "nodebb-plugin-composer-default": "10.3.27", + "nodebb-plugin-composer-default": "10.3.28", "nodebb-plugin-dbsearch": "6.4.1", "nodebb-plugin-emoji": "6.0.6", "nodebb-plugin-emoji-android": "4.1.1", From 3825c755e9bafed066aa305a641b47b6cddfb929 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:58:20 -0400 Subject: [PATCH 182/204] chore(deps): update dependency jsdom to v29 (#14100) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 4d576dc99e..ee7cbba54c 100644 --- a/install/package.json +++ b/install/package.json @@ -172,7 +172,7 @@ "grunt": "1.6.1", "grunt-contrib-watch": "1.1.0", "husky": "8.0.3", - "jsdom": "28.1.0", + "jsdom": "29.0.0", "lint-staged": "16.3.3", "mocha": "11.7.5", "mocha-lcov-reporter": "1.3.0", From 3765fb37f0407ca4138a67e14e8091becdcb5814 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:58:40 -0400 Subject: [PATCH 183/204] fix(deps): update dependency lru-cache to v11.2.7 (#14096) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index ee7cbba54c..6019d38cdc 100644 --- a/install/package.json +++ b/install/package.json @@ -89,7 +89,7 @@ "jsonwebtoken": "9.0.3", "lodash": "4.17.23", "logrotate-stream": "0.2.9", - "lru-cache": "11.2.6", + "lru-cache": "11.2.7", "mime": "3.0.0", "mkdirp": "3.0.1", "mongodb": "7.1.0", From c26bfddfe180e66a36ccafac7a0b9ec1ac5ad235 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:58:55 -0400 Subject: [PATCH 184/204] fix(deps): update dependency esbuild to v0.27.4 (#14090) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 6019d38cdc..2060ba6afd 100644 --- a/install/package.json +++ b/install/package.json @@ -67,7 +67,7 @@ "csrf-sync": "4.2.1", "daemon": "1.1.0", "diff": "8.0.3", - "esbuild": "0.27.3", + "esbuild": "0.27.4", "express": "4.22.1", "express-session": "1.19.0", "express-useragent": "2.1.0", From 06c3b88b1bb5590d333440403b57338022f22f2b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:59:51 -0400 Subject: [PATCH 185/204] chore(deps): update commitlint monorepo to v20.5.0 (#14098) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 2060ba6afd..ddf847714e 100644 --- a/install/package.json +++ b/install/package.json @@ -162,8 +162,8 @@ }, "devDependencies": { "@apidevtools/swagger-parser": "10.1.0", - "@commitlint/cli": "20.4.4", - "@commitlint/config-angular": "20.4.4", + "@commitlint/cli": "20.5.0", + "@commitlint/config-angular": "20.5.0", "coveralls": "3.1.1", "@eslint/js": "10.0.1", "@stylistic/eslint-plugin": "5.10.0", From 902533db0659866fda51428a99320b8a6ab6e4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 17 Mar 2026 11:56:31 -0400 Subject: [PATCH 186/204] fix: delete cid::privilegeMask on category.purge --- src/categories/delete.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/categories/delete.js b/src/categories/delete.js index 5f96e32676..3575107336 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -52,6 +52,7 @@ module.exports = function (Categories) { `cid:${cid}:uid:watch:state`, `cid:${cid}:children`, `cid:${cid}:tag:whitelist`, + `cid:${cid}:privilegeMask`, `${utils.isNumber(cid) ? 'category' : 'categoryRemote'}:${cid}`, ]); const privilegeList = await privileges.categories.getPrivilegeList(); From 27b0fbe68593a16ea8ebe7f9a4ed2dbcb2b70b62 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 17 Mar 2026 12:06:43 -0400 Subject: [PATCH 187/204] fix: only show category selector on quickreply on /world --- install/package.json | 4 ++-- public/src/modules/quickreply.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/install/package.json b/install/package.json index ddf847714e..42f19561a3 100644 --- a/install/package.json +++ b/install/package.json @@ -108,10 +108,10 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.59", + "nodebb-theme-harmony": "2.2.60", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", - "nodebb-theme-persona": "14.2.32", + "nodebb-theme-persona": "14.2.33", "nodebb-widget-essentials": "7.0.43", "nodemailer": "8.0.2", "nprogress": "0.2.0", diff --git a/public/src/modules/quickreply.js b/public/src/modules/quickreply.js index f081c59588..3a9573fb7b 100644 --- a/public/src/modules/quickreply.js +++ b/public/src/modules/quickreply.js @@ -19,7 +19,7 @@ define('quickreply', [ return; } - if ($('[component="topic/quickreply/container"] [component="category-selector"]')) { + if (opts?.body?.cid && $('[component="topic/quickreply/container"] [component="category-selector"]')) { categorySelector.init($('[component="category-selector"]'), { privilege: 'topics:create', selectedCategory: ajaxify.data.selectedCategory, @@ -28,6 +28,7 @@ define('quickreply', [ opts.body.cid = category.cid; }, }); + $('[component="topic/quickreply/container"] [component="topic/quickreply/category-selector"').removeClass('hidden'); } const qrDraftId = ajaxify.data.tid ? `qr:draft:tid:${ajaxify.data.tid}` : `qr:draft:cid:${opts?.body?.cid || -1}`; From 35c03e5c8e71221975a14773504cddf1a2f77308 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 17 Mar 2026 12:11:24 -0400 Subject: [PATCH 188/204] fix: issue where initial quickcreate post wouldn't go to the right cid --- public/src/client/world.js | 2 +- src/controllers/api.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/public/src/client/world.js b/public/src/client/world.js index 61492a5d9f..ec2c4ef497 100644 --- a/public/src/client/world.js +++ b/public/src/client/world.js @@ -14,7 +14,7 @@ define('forum/world', [ quickreply.init({ route: '/topics', body: { - cid: ajaxify.data.cid, + cid: config.activitypub.worldDefaultCid || ajaxify.data.cid, }, }); diff --git a/src/controllers/api.js b/src/controllers/api.js index 4fd83dcd5b..ba49ccb29c 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -103,6 +103,7 @@ apiController.loadConfig = async function (req) { }, activitypub: { probe: meta.config.activitypubEnabled && meta.config.activitypubProbe, + worldDefaultCid: meta.config.activitypubWorldDefaultCid, }, }; From 168b17e828f7d7c5dc8d553402e145243e872504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 17 Mar 2026 12:11:28 -0400 Subject: [PATCH 189/204] chore: up mentions --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 3fad170317..8b4c8802f2 100644 --- a/install/package.json +++ b/install/package.json @@ -103,7 +103,7 @@ "nodebb-plugin-emoji-android": "4.1.1", "nodebb-plugin-link-preview": "2.2.3", "nodebb-plugin-markdown": "13.2.4", - "nodebb-plugin-mentions": "4.8.17", + "nodebb-plugin-mentions": "4.8.18", "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.6", "nodebb-rewards-essentials": "1.0.2", From 4a01d55f6ad76c1a83e1818770fca21d20c09c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 17 Mar 2026 12:29:22 -0400 Subject: [PATCH 190/204] chore: up develop --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index f32497d288..1215fbb396 100644 --- a/install/package.json +++ b/install/package.json @@ -108,7 +108,7 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.60", + "nodebb-theme-harmony": "2.2.61", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", "nodebb-theme-persona": "14.2.33", From efaf8eb996da1220d944e4472f3f28c5a1f57688 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 17 Mar 2026 12:42:45 -0400 Subject: [PATCH 191/204] fix: schema fix for new api config value --- public/openapi/read/config.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/openapi/read/config.yaml b/public/openapi/read/config.yaml index 16d2043667..273cf56076 100644 --- a/public/openapi/read/config.yaml +++ b/public/openapi/read/config.yaml @@ -184,4 +184,6 @@ get: type: object properties: probe: - type: number \ No newline at end of file + type: number + worldDefaultCid: + type: string \ No newline at end of file From 7c65471ba41f3d79c91ccea5e386aee27bb88e1c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 17 Mar 2026 12:51:14 -0400 Subject: [PATCH 192/204] fix: close notif drawer on item click, fix crash in module --- public/src/modules/notifications.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index 70dc54e3bb..599644848e 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -43,13 +43,13 @@ define('notifications', [ app.parseAndTranslate('partials/notifications_list', { notifications }, function (html) { notifList.html(html); notifList.off('click').on('click', '[component="notifications/item/link"]', function (ev) { - const notifEl = $(this); + const notifEl = $(this).parents('[data-nid]'); if (scrollToPostIndexIfOnPage(notifEl)) { ev.stopPropagation(); ev.preventDefault(); - if (triggerEl) { - triggerEl.dropdown('toggle'); - } + } + if (triggerEl) { + triggerEl.dropdown('toggle'); } const unread = notifEl.hasClass('unread'); @@ -61,6 +61,7 @@ define('notifications', [ }); components.get('notifications').on('click', '.mark-all-read', () => { Notifications.markAllRead(); + triggerEl.dropdown('toggle'); }); components.get('notifications').on('click', `[href="${config.relative_path}/notifications"]`, () => { triggerEl.dropdown('toggle'); From bea46026389ac5127cca7bc31b3de3d602a31d25 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:11:50 -0400 Subject: [PATCH 193/204] chore(deps): update dependency lint-staged to v16.4.0 (#14099) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 1215fbb396..abb4fd3946 100644 --- a/install/package.json +++ b/install/package.json @@ -173,7 +173,7 @@ "grunt-contrib-watch": "1.1.0", "husky": "8.0.3", "jsdom": "29.0.0", - "lint-staged": "16.3.3", + "lint-staged": "16.4.0", "mocha": "11.7.5", "mocha-lcov-reporter": "1.3.0", "mockdate": "3.0.5", From 483a63c67b5919f6ef56daaecad77d9f0ce7c79d Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Wed, 18 Mar 2026 09:07:37 +0000 Subject: [PATCH 194/204] Latest translations and fallbacks --- public/language/bg/admin/settings/activitypub.json | 2 +- public/language/bg/admin/settings/post.json | 4 ++-- public/language/bg/login.json | 2 +- public/language/bg/world.json | 14 +++++++------- public/language/de/admin/settings/activitypub.json | 2 +- public/language/de/world.json | 12 ++++++------ 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/public/language/bg/admin/settings/activitypub.json b/public/language/bg/admin/settings/activitypub.json index 4923a53f7f..089529287a 100644 --- a/public/language/bg/admin/settings/activitypub.json +++ b/public/language/bg/admin/settings/activitypub.json @@ -51,5 +51,5 @@ "content.summary-limit-help": "Когато дадено съдържание е федерирано в посока извън този форум и превишава този брой знаци, ще се създаде обобщение, което се състои от всички завършени изречения до ограничението. (по подразбиране: 500)", "content.break-string": "Разделител на съдържанието", "content.break-string-help": "Този разделител може да бъде поставен ръчно от потребителите с по-високи правомощия, когато създават нови теми. Той казва на NodeBB да използва съдържанието до така посоченото място като част от обобщението. Ако този разделител не бъде използван, се прилага зададеният брой на знаците. (по подразбиране: [...])", - "content.world-default-cid": "Default category ID for "World" page composer" + "content.world-default-cid": "Идентификатор на категория по подразбиране при съставяне на страница в „Света“" } \ No newline at end of file diff --git a/public/language/bg/admin/settings/post.json b/public/language/bg/admin/settings/post.json index f39ea34f3c..d937bcddd2 100644 --- a/public/language/bg/admin/settings/post.json +++ b/public/language/bg/admin/settings/post.json @@ -29,7 +29,7 @@ "restrictions.stale-help": "Ако дадена тема е определена като „стара“, то потребителите, които се опитат да пишат в нея, ще получат предупредително съобщение. (0 = изключено)", "timestamp": "Време", "timestamp.cut-off": "Използване на дата след (в брой дни)", - "timestamp.cut-off-help": "Датите и времената ще бъдат показвани относително (напр. „преди 3 часа“ или „преди 5 дни“), и преведени на множество\n\\t\\t\\t\\t\\tезици. След определено време, този текст ще започне да показва самите дата и час, според езика на потребителя\n\\t\\t\\t\\t\\t(напр. „5 ноември 2016 15:30“).
(По подразбиране: 30, тоест един месец). Ако зададете 0, винаги ще се изписват дати, а ако оставите полето празно, времето ще бъде винаги относително.", + "timestamp.cut-off-help": "Датите и времената ще бъдат показвани относително (напр. „преди 3 часа“ или „преди 5 дни“), и преведени на множество\n\t\t\t\t\tезици. След определено време, този текст ще започне да показва самите дата и час, според езика на потребителя\n\t\t\t\t\t(напр. „5 ноември 2016 15:30“).
(По подразбиране: 30, тоест един месец). Ако зададете 0, винаги ще се изписват дати, а ако оставите полето празно, времето ще бъде винаги относително.", "timestamp.necro-threshold": "Мъртва граница (в дни)", "timestamp.necro-threshold-help": "Между публикациите ще бъде показано съобщение, ако времето между тях е по-дълго от мъртвата граница. (По подразбиране: 7, или една седмица). Задайте 0 за изключване.
", "timestamp.topic-views-interval": "Интервал за увеличаване на броя на преглеждания на темите (в минути)", @@ -51,7 +51,7 @@ "signature.hide-duplicates": "Скриване на дублираните подписи в темите", "signature.max-length": "Максимална дължина на подписите", "composer": "Настройки за съставянето", - "composer-help": "Следващите настройки определят функционалностите и/или вида на елемента за съставяне на\n\\t\\t\\t\\tпубликация, който се използва от потребителите, когато те създават нови теми или отговорят в съществуващи.", + "composer-help": "Следващите настройки определят функционалностите и/или вида на елемента за съставяне на\n\t\t\t\tпубликация, който се използва от потребителите, когато те създават нови теми или отговорят в съществуващи.", "composer.show-help": "Показване на раздела „Помощ“", "composer.enable-plugin-help": "Позволяване на добавките да добавят съдържание в раздела за помощ", "composer.custom-help": "Персонализиран текст за помощ", diff --git a/public/language/bg/login.json b/public/language/bg/login.json index 4914291c5c..1dd0452064 100644 --- a/public/language/bg/login.json +++ b/public/language/bg/login.json @@ -6,7 +6,7 @@ "alternative-logins": "Други начини за вписване", "failed-login-attempt": "Неуспешно вписване", "login-successful": "Вие влязохте успешно!", - "dont-have-account": "Нямате регистрация?", + "dont-have-account": "Нямате акаунт?", "logged-out-due-to-inactivity": "Вие излязохте автоматично от администраторския контролен панел, поради бездействие.", "caps-lock-enabled": "Главните букви са включени" } \ No newline at end of file diff --git a/public/language/bg/world.json b/public/language/bg/world.json index 6e332f3fe8..6ca101eab0 100644 --- a/public/language/bg/world.json +++ b/public/language/bg/world.json @@ -1,8 +1,8 @@ { "name": "Свят", - "latest": "Latest", - "latest-local": "Latest (Local)", - "latest-all": "Последни (всички)", + "latest": "Най-нови", + "latest-local": "Най-нови (локални)", + "latest-all": "Най-нови (всички)", "popular-day": "Популярни (за деня)", "popular-week": "Популярни (за седмицата)", "popular-month": "Популярни (за месеца)", @@ -18,10 +18,10 @@ "help.federating": "По същия начин, ако потребители извън този форум започнат да следват Вас, тогава Вашите публикации ще започнат да се появяват в техните приложения и уеб сайтове.", "help.next-generation": "Това е новото поколение социална мрежа. Започнете да допринасяте още днес!", - "onboard.title": "A world of content at your fingertips…", - "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", - "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", - "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", + "onboard.title": "Свят изпълнен със съдържание, на една ръка разстояние…", + "onboard.what": "Гледайте на това като на глобална опашка за откриване на съдържание. Тук се събират интересни дискусии от различни места в мрежата и от други общности – на едно място.", + "onboard.why": "Можете да разглеждате препоръчаните популярни теми, но най-добрият начин да ползвате тази функционалност, е да я нагласите по свой вкус. Ако си създадете акаунт, ще можете да следвате определени хора или теми – така ще филтрирате шума и ще виждате само това, което Ви интересува.", + "onboard.how": "Готови? Създайте си акаунт и започнете да следвате другите, да получавате известия когато хората Ви отговарят, както и да запазвате любимите си открития.", "category-search": "Търсене на категория…", "see-more": "Вижте повече", diff --git a/public/language/de/admin/settings/activitypub.json b/public/language/de/admin/settings/activitypub.json index bc119ed32b..98ea96fe66 100644 --- a/public/language/de/admin/settings/activitypub.json +++ b/public/language/de/admin/settings/activitypub.json @@ -51,5 +51,5 @@ "content.summary-limit-help": "Wenn Inhalte rausgeschickt werden, die diese Zeichenanzahl überschreiten, wird eine Zusammenfassung erstellt, die alle vollständigen Sätze vor dieser Grenze enthält. (Standard: 500)", "content.break-string": "Notiz/Artikel-Trennzeichen", "content.break-string-help": "\nDieses Trennzeichen kann von Power-Usern beim Erstellen neuer Themen manuell eingefügt werden. Es sagt NodeBB, dass der Inhalt bis zu dieser Stelle als Teil der Zusammenfassungverwendet werden soll. Wenn diese Zeichenfolge nicht benutzt wird, greift die Standardregel für die Zeichenanzahl. (Standard: [...])", - "content.world-default-cid": "Default category ID for "World" page composer" + "content.world-default-cid": "Standard-Kategorie-ID für den "Welt" Seiten-Editor" } \ No newline at end of file diff --git a/public/language/de/world.json b/public/language/de/world.json index f03c9cd773..ba57a0e92d 100644 --- a/public/language/de/world.json +++ b/public/language/de/world.json @@ -1,7 +1,7 @@ { "name": "Welt", - "latest": "Latest", - "latest-local": "Latest (Local)", + "latest": "Neueste", + "latest-local": "Neueste (Lokal)", "latest-all": "Neueste (Alle)", "popular-day": "Beliebt (Tag)", "popular-week": "Beliebt (Woche)", @@ -18,10 +18,10 @@ "help.federating": "Wenn Leute außerhalb dieses Forums anfangen, dirzu folgen, werden deine Beiträge auch in diesen Apps und auf diesen Websites angezeigt.", "help.next-generation": "Das ist die nächste Generation der sozialen Medien, leg noch heute los!", - "onboard.title": "A world of content at your fingertips…", - "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", - "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", - "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", + "onboard.title": "Eine Welt voller Inhalte, immer griffbereit…", + "onboard.what": "Betrachte das als deinen weltweiten Entdeckungs-Feed. Hier findest du interessante Diskussionen aus dem gesamten Internet und anderen Communities – alles an einem Ort.", + "onboard.why": "Du kannst sehen, was gerade im Trend liegt, am besten nutzt du diesen Feed, indem du ihn ganz nach deinen Wünschen gestaltest. Wenn du ein Konto erstellst, kannst du bestimmten Creators und Themen folgen, um den Ballast auszusortieren und nur das zu sehen, was dir wichtig ist.", + "onboard.how": "Bist du bereit, loszulegen? Erstelle ein Konto, um anderen zu folgen, Benachrichtigungen zu erhalten, wenn dir ein Mensch antwortet, und deine Lieblingsfunde zu speichern.", "category-search": "Such eine Kategorie...", "see-more": "Mehr sehen", From 11b98583b17bba11eb12608c325caae5d630d992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 18 Mar 2026 08:48:46 -0400 Subject: [PATCH 195/204] check triggerEl in callbacks --- public/src/modules/notifications.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index 599644848e..1e68cb724a 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -48,9 +48,8 @@ define('notifications', [ ev.stopPropagation(); ev.preventDefault(); } - if (triggerEl) { - triggerEl.dropdown('toggle'); - } + + triggerEl?.dropdown('toggle'); const unread = notifEl.hasClass('unread'); if (!unread) { @@ -61,10 +60,10 @@ define('notifications', [ }); components.get('notifications').on('click', '.mark-all-read', () => { Notifications.markAllRead(); - triggerEl.dropdown('toggle'); + triggerEl?.dropdown('toggle'); }); components.get('notifications').on('click', `[href="${config.relative_path}/notifications"]`, () => { - triggerEl.dropdown('toggle'); + triggerEl?.dropdown('toggle'); }); Notifications.handleUnreadButton(notifList); From 7e2c7db3b216605dea9d96f6e09c903767147273 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Mar 2026 09:10:10 -0400 Subject: [PATCH 196/204] fix: schema fix for new api config value --- public/openapi/read/admin/config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/openapi/read/admin/config.yaml b/public/openapi/read/admin/config.yaml index a58405fb84..044c0b87a1 100644 --- a/public/openapi/read/admin/config.yaml +++ b/public/openapi/read/admin/config.yaml @@ -153,6 +153,8 @@ get: properties: probe: type: number + worldDefaultCid: + type: string acpLang: type: string openOutgoingLinksInNewTab: From 895997b2b873aea1b320173ec3620d110195e8d2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Mar 2026 09:58:23 -0400 Subject: [PATCH 197/204] docs: wrong type for worldDefaultCid --- public/openapi/read/admin/config.yaml | 2 +- public/openapi/read/config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/openapi/read/admin/config.yaml b/public/openapi/read/admin/config.yaml index 044c0b87a1..457e63fcc9 100644 --- a/public/openapi/read/admin/config.yaml +++ b/public/openapi/read/admin/config.yaml @@ -154,7 +154,7 @@ get: probe: type: number worldDefaultCid: - type: string + type: number acpLang: type: string openOutgoingLinksInNewTab: diff --git a/public/openapi/read/config.yaml b/public/openapi/read/config.yaml index 273cf56076..f4ae1dfe87 100644 --- a/public/openapi/read/config.yaml +++ b/public/openapi/read/config.yaml @@ -186,4 +186,4 @@ get: probe: type: number worldDefaultCid: - type: string \ No newline at end of file + type: number \ No newline at end of file From 58d3aa77cd0f4383a699433461e24dacdb6887ef Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Mar 2026 10:28:54 -0400 Subject: [PATCH 198/204] feat: add /world as a potential home page route --- src/controllers/helpers.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index 7101170357..3f635a4a80 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -423,6 +423,10 @@ helpers.getHomePageRoutes = async function (uid) { route: 'categories', name: 'Categories', }, + { + route: 'world', + name: 'World', + }, { route: 'unread', name: 'Unread', From cc606677cb76356d3270dec0badf7efe9c109488 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Mar 2026 11:09:08 -0400 Subject: [PATCH 199/204] fix: cold load redirect should only affect guests --- src/controllers/accounts/profile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/accounts/profile.js b/src/controllers/accounts/profile.js index 21f6e6a191..93be1d2b4e 100644 --- a/src/controllers/accounts/profile.js +++ b/src/controllers/accounts/profile.js @@ -24,7 +24,7 @@ profileController.get = async function (req, res, next) { return next(); } - if (meta.config.activitypubEnabled && !res.locals.isAPI && !utils.isNumber(userData.uid)) { + if (!req.loggedIn && meta.config.activitypubEnabled && !res.locals.isAPI && !utils.isNumber(userData.uid)) { return helpers.redirect(res, `/outgoing?url=${encodeURIComponent(userData.uid)}`); } From 44e78e4775204ae00b0176932eb9bd239b276f34 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 18 Mar 2026 12:13:50 -0400 Subject: [PATCH 200/204] fix: sync follow counts on local and remote follows, #14105 --- src/activitypub/helpers.js | 2 +- src/activitypub/inbox.js | 19 +++++++++-------- src/controllers/activitypub/index.js | 12 ++++------- src/user/follow.js | 31 +++++++++++++++++++++------- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js index a6dae62caf..3b6e7549aa 100644 --- a/src/activitypub/helpers.js +++ b/src/activitypub/helpers.js @@ -473,7 +473,7 @@ Helpers.generateCollection = async ({ set, method, count, page, perPage, url }) } else if (set) { method = method.bind(null, set); } - count = count || await db.sortedSetCard(set); + count = count ?? await db.sortedSetCard(set); const pageCount = Math.max(1, Math.ceil(count / perPage)); let items = []; let paginate = true; diff --git a/src/activitypub/inbox.js b/src/activitypub/inbox.js index d0ee3976a4..cd14579362 100644 --- a/src/activitypub/inbox.js +++ b/src/activitypub/inbox.js @@ -517,11 +517,12 @@ inbox.follow = async (req) => { } const now = Date.now(); - await db.sortedSetAdd(`followersRemote:${id}`, now, actor); - await db.sortedSetAdd(`followingRemote:${actor}`, now, id); // for following backreference (actor pruning) - - const followerRemoteCount = await db.sortedSetCard(`followersRemote:${id}`); - await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); + await Promise.all([ + db.sortedSetAdd(`followersRemote:${id}`, now, actor), + db.sortedSetAdd(`followingRemote:${actor}`, now, id), // for following backreference (actor pruning) + user.syncFollowCounts(id, false, true), + user.syncFollowCounts(actor, true, false), + ]); activitypub.actors._followerCache.del(id); await user.onFollow(actor, id); @@ -600,8 +601,8 @@ inbox.accept = async (req) => { db.sortedSetAdd(`followingRemote:${id}`, timestamp, actor), db.sortedSetAdd(`followersRemote:${actor}`, timestamp, id), // for followers backreference and notes assertion checking ]); - const followingRemoteCount = await db.sortedSetCard(`followingRemote:${id}`); - await user.setUserField(id, 'followingRemoteCount', followingRemoteCount); + await user.syncFollowCounts(id, true, false); + await user.syncFollowCounts(actor, false, true); } else if (localType === 'category') { if (!await db.isSortedSetMember(`followRequests:cid.${id}`, actor)) { if (await db.isSortedSetMember(`cid:${id}:following`, actor)) return; // already following @@ -649,9 +650,9 @@ inbox.undo = async (req) => { await Promise.all([ db.sortedSetRemove(`followersRemote:${id}`, actor), db.sortedSetRemove(`followingRemote:${actor}`, id), + user.syncFollowCounts(id, false, true), + user.syncFollowCounts(actor, true, false), ]); - const followerRemoteCount = await db.sortedSetCard(`followerRemote:${id}`); - await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); notifications.rescind(`follow:${id}:uid:${actor}`); activitypub.actors._followerCache.del(id); break; diff --git a/src/controllers/activitypub/index.js b/src/controllers/activitypub/index.js index 086e90933b..b7eaa334c8 100644 --- a/src/controllers/activitypub/index.js +++ b/src/controllers/activitypub/index.js @@ -66,10 +66,8 @@ Controller.fetch = async (req, res, next) => { }; Controller.getFollowing = async (req, res) => { - const { followingCount, followingRemoteCount } = await user.getUserFields(req.params.uid, ['followingCount', 'followingRemoteCount']); - const totalItems = parseInt(followingCount || 0, 10) + parseInt(followingRemoteCount || 0, 10); - - const count = totalItems; + const followingCount = await user.getUserField(req.params.uid, 'followingCount'); + const count = parseInt(followingCount, 10); const collection = await activitypub.helpers.generateCollection({ method: user.getFollowing.bind(null, req.params.uid), count, @@ -92,10 +90,8 @@ Controller.getFollowing = async (req, res) => { }; Controller.getFollowers = async (req, res) => { - const { followerCount, followerRemoteCount } = await user.getUserFields(req.params.uid, ['followerCount', 'followerRemoteCount']); - const totalItems = parseInt(followerCount || 0, 10) + parseInt(followerRemoteCount || 0, 10); - - const count = totalItems; + const followerCount = await user.getUserField(req.params.uid, 'followerCount'); + const count = parseInt(followerCount, 10); const collection = await activitypub.helpers.generateCollection({ method: user.getFollowers.bind(null, req.params.uid), count, diff --git a/src/user/follow.js b/src/user/follow.js index 7e561d07e5..d14251d38c 100644 --- a/src/user/follow.js +++ b/src/user/follow.js @@ -58,15 +58,32 @@ module.exports = function (User) { ]); } - const [followingCount, followingRemoteCount, followerCount, followerRemoteCount] = await db.sortedSetsCard([ - `following:${uid}`, `followingRemote:${uid}`, `followers:${theiruid}`, `followersRemote:${theiruid}`, - ]); - await Promise.all([ - User.setUserField(uid, 'followingCount', followingCount + followingRemoteCount), - User.setUserField(theiruid, 'followerCount', followerCount + followerRemoteCount), - ]); + User.syncFollowCounts(uid, true, false); + User.syncFollowCounts(theiruid, false, true); } + User.syncFollowCounts = async function (uid, following, followers) { + const sets = []; + const property = []; + if (following) { + property.push('followingCount'); + sets.push(`following:${uid}`, `followingRemote:${uid}`); + } + if (followers) { + property.push('followerCount'); + sets.push(`followers:${uid}`, `followersRemote:${uid}`); + }; + + const values = await db.sortedSetsCard(sets); + const payload = property.reduce((payload, cur, idx) => { + const sum = values[idx * 2] + values[(idx * 2) + 1]; + payload[cur] = sum; + return payload; + }, {}); + + await User.setUserFields(uid, payload); + }; + User.getFollowing = async function (uid, start, stop) { return await getFollow(uid, 'following', start, stop); }; From fa7c1a522bc98d6fab15b9dc3c5308c1de196e67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:50:13 -0400 Subject: [PATCH 201/204] fix(deps): update dependency nodemailer to v8.0.3 (#14104) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index abb4fd3946..9243ba4190 100644 --- a/install/package.json +++ b/install/package.json @@ -113,7 +113,7 @@ "nodebb-theme-peace": "2.2.57", "nodebb-theme-persona": "14.2.33", "nodebb-widget-essentials": "7.0.43", - "nodemailer": "8.0.2", + "nodemailer": "8.0.3", "nprogress": "0.2.0", "passport": "0.7.0", "passport-http-bearer": "1.0.1", From f51e1b2a28eaf5a7defb56bc9b3c5011044fb448 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 10:32:57 -0400 Subject: [PATCH 202/204] fix(deps): update dependency cronstrue to v3.14.0 (#14107) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 9243ba4190..c31cbf4889 100644 --- a/install/package.json +++ b/install/package.json @@ -62,7 +62,7 @@ "connect-redis": "9.0.0", "cookie-parser": "1.4.7", "cron": "4.4.0", - "cronstrue": "3.13.0", + "cronstrue": "3.14.0", "cropperjs": "1.6.2", "csrf-sync": "4.2.1", "daemon": "1.1.0", From ebe709da89b356f5f4bfdace77e46d451c286580 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 19 Mar 2026 11:36:28 -0400 Subject: [PATCH 203/204] fix: call syncfollowcounts on unfollow as well --- src/activitypub/out.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/activitypub/out.js b/src/activitypub/out.js index 45d902fb05..5242e6df6d 100644 --- a/src/activitypub/out.js +++ b/src/activitypub/out.js @@ -471,7 +471,8 @@ Out.undo.follow = enabledCheck(async (type, id, actor) => { db.sortedSetRemove(`followingRemote:${id}`, actor), db.sortedSetRemove(`followRequests:uid.${id}`, actor), db.sortedSetRemove(`followersRemote:${actor}`, id), - db.decrObjectField(`user:${id}`, 'followingRemoteCount'), + user.syncFollowCounts(id, true, false), + user.syncFollowCounts(actor, false, true), ]); } else if (type === 'cid') { await Promise.all([ From 8ca34e74bd7715315f0cd76943e1044e9e0a134f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 19 Mar 2026 12:02:26 -0400 Subject: [PATCH 204/204] fix: improve idempotency of ap test --- test/activitypub/actors.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/activitypub/actors.js b/test/activitypub/actors.js index e659713de3..e185984c1b 100644 --- a/test/activitypub/actors.js +++ b/test/activitypub/actors.js @@ -737,6 +737,10 @@ describe('Pruning', () => { }); describe('Users', () => { + before(async function () { + this.current = await activitypub.actors.prune(); + }); + it('should do nothing if the user is newer than the prune cutoff', async () => { const { id: uid } = helpers.mocks.person(); await activitypub.actors.assert([uid]); @@ -762,7 +766,7 @@ describe('Pruning', () => { assert(result.counts.deleted >= 1); }); - it('should do nothing if the user has some content (e.g. a topic)', async () => { + it('should do nothing if the user has some content (e.g. a topic)', async function () { const { cid } = await categories.create({ name: utils.generateUUID() }); const { id: uid } = helpers.mocks.person(); const { id, note } = helpers.mocks.note({ @@ -776,7 +780,7 @@ describe('Pruning', () => { const result = await activitypub.actors.prune(); assert.strictEqual(result.counts.deleted, 0); - assert.strictEqual(result.counts.preserved, 1); + assert.strictEqual(result.counts.preserved, this.current.counts.preserved + 1); assert.strictEqual(result.counts.missing, 0); }); });
-
- {{{each pictures}}} - - {{{end}}} +
+ {{{ each pictures }}} +
+ + +
+ {{{ end }}}
-
+
+
[[user:avatar-background-colour]]
+
+ + {{{ each iconBackgrounds }}} + + {{{ end }}} +
+
{{{ if allowProfileImageUploads }}} - {{{ end }}} - - {{{ if uploaded }}} - - {{{ end }}}
-
- -
- -

[[user:avatar-background-colour]]

-
- - {{{ each iconBackgrounds }}} - -{{{ end }}}
\ No newline at end of file diff --git a/test/notifications.js b/test/notifications.js index 33d03fadcf..9596fd8112 100644 --- a/test/notifications.js +++ b/test/notifications.js @@ -75,7 +75,8 @@ describe('Notifications', () => { const notifData = await notifications.get(nid); assert.strictEqual(notifData.icon, undefined); assert.strictEqual(notifData.user['icon:text'], 'I'); - assert.strictEqual(notifData.user['icon:bgColor'], '#3f51b5'); + assert(notifData.user['icon:bgColor'].length === 7 && + notifData.user['icon:bgColor'].startsWith('#')); }); it('should return null if pid is same and importance is lower', (done) => { diff --git a/test/user.js b/test/user.js index 6ecb92814f..3eb85d83ed 100644 --- a/test/user.js +++ b/test/user.js @@ -1028,7 +1028,8 @@ describe('User', () => { it('should set user picture to uploaded', async () => { await User.setUserField(uid, 'uploadedpicture', '/test'); - await apiUser.changePicture({ uid: uid }, { type: 'uploaded', uid: uid }); + await db.sortedSetAdd(`uid:${uid}:profile:pictures`, Date.now(), '/test'); + await apiUser.changePicture({ uid: uid }, { type: 'uploaded', picture: '/test', uid: uid }); const picture = await User.getUserField(uid, 'picture'); assert.equal(picture, `${nconf.get('relative_path')}/test`); }); From 6c3daebbce0879abfc3f2ba4dfcf4f3899789502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 13 Mar 2026 19:53:35 -0400 Subject: [PATCH 160/204] update fix_username_zsets upgrade script to show progress while deleting --- src/upgrades/3.2.0/fix_username_zsets.js | 25 +++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/upgrades/3.2.0/fix_username_zsets.js b/src/upgrades/3.2.0/fix_username_zsets.js index 22ab35c152..01c2663a04 100644 --- a/src/upgrades/3.2.0/fix_username_zsets.js +++ b/src/upgrades/3.2.0/fix_username_zsets.js @@ -11,9 +11,28 @@ module.exports = { method: async function () { const { progress } = this; - await db.deleteAll(['username:uid', 'username:sorted']); + const [userNameUid, usernameSorted, usersJoindate] = await db.sortedSetsCard([ + 'username:uid', 'username:sorted', 'users:joindate', + ]); + progress.total = userNameUid + usernameSorted + usersJoindate; + + await batch.processSortedSet('username:uid', async (usernames) => { + await db.sortedSetRemove('username:uid', usernames); + progress.incr(usernames.length); + }, { + batch: 500, + alwaysStartAt: 0, + }); + + await batch.processSortedSet('username:sorted', async (usernames) => { + await db.sortedSetRemove('username:sorted', usernames); + progress.incr(usernames.length); + }, { + batch: 500, + alwaysStartAt: 0, + }); + await batch.processSortedSet('users:joindate', async (uids) => { - progress.incr(uids.length); const usersData = await db.getObjects(uids.map(uid => `user:${uid}`)); const bulkAdd = []; usersData.forEach((userData) => { @@ -23,9 +42,9 @@ module.exports = { } }); await db.sortedSetAddBulk(bulkAdd); + progress.incr(uids.length); }, { batch: 500, - progress: progress, }); }, }; From 698758d994b165bc44ff79c602b0eae3a0d5f357 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Sat, 14 Mar 2026 09:07:38 +0000 Subject: [PATCH 161/204] Latest translations and fallbacks --- public/language/de/admin/settings/general.json | 4 ++-- public/language/de/world.json | 4 ++-- public/language/vi/admin/manage/uploads.json | 2 +- .../language/vi/admin/settings/activitypub.json | 2 +- public/language/vi/admin/settings/general.json | 4 ++-- public/language/vi/error.json | 2 +- public/language/vi/global.json | 2 +- public/language/vi/modules.json | 4 ++-- public/language/vi/reset_password.json | 2 +- public/language/vi/search.json | 4 ++-- public/language/vi/topic.json | 2 +- public/language/vi/world.json | 4 ++-- .../language/zh-CN/admin/settings/general.json | 16 ++++++++-------- public/language/zh-CN/world.json | 4 ++-- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/public/language/de/admin/settings/general.json b/public/language/de/admin/settings/general.json index 278b7fbf52..4f6a5e7ef6 100644 --- a/public/language/de/admin/settings/general.json +++ b/public/language/de/admin/settings/general.json @@ -18,7 +18,7 @@ "description": "Seitenbeschreibung", "keywords": "Forum Schlüsselworte", "keywords-placeholder": "Schlüsselworte, die ihre Community beschreiben, mit Komma getrennt", - "logo-and-icons": "Media & Branding", + "logo-and-icons": "Medien & Branding", "logo.image": "Bild", "logo.image-placeholder": "Pfad zu einem Logo, welches im Header des Forums angezeigt werden soll", "logo.upload": "Hochladen", @@ -36,7 +36,7 @@ "maskable-icon": "Maskierbares (Start-Bildschirm) Symbol", "maskable-icon.help": "Empfohlene Größe und Format: 512x512, nur PNG-Format. Wenn kein maskierbares Icon angegeben wird, greift NodeBB auf das Touch-Symbol zurück.", "screenshot": "Screenshot", - "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", + "screenshot.help": "Empfohlene Größe und Format: zwischen 320px und 3480px, nur JPG- und PNG-Format. Wenn kein Screenshot angegeben wird, greift NodeBB auf einen Standard-Screenshot zurück", "outgoing-links": "Ausgehende Links", "outgoing-links.warning-page": "Warnseite für ausgehende links verwenden", "search": "Suche", diff --git a/public/language/de/world.json b/public/language/de/world.json index 95e16612b9..55a1fbfb24 100644 --- a/public/language/de/world.json +++ b/public/language/de/world.json @@ -1,7 +1,7 @@ { "name": "Welt", - "latest": "Latest (Following)", - "latest-all": "Latest (All)", + "latest": "Neueste (Gefolgt)", + "latest-all": "Neueste (Alle)", "popular-day": "Beliebt (Tag)", "popular-week": "Beliebt (Woche)", "popular-month": "Beliebt (Monat)", diff --git a/public/language/vi/admin/manage/uploads.json b/public/language/vi/admin/manage/uploads.json index 32f4d447e3..39e2308e7c 100644 --- a/public/language/vi/admin/manage/uploads.json +++ b/public/language/vi/admin/manage/uploads.json @@ -2,7 +2,7 @@ "manage-uploads": "Quản Lý Tải Lên", "upload-file": "Tải Lên Tệp", "filename": "Tên Tệp", - "usage": "Dùng Bài Đăng", + "usage": "Bài Đăng Sử Dụng", "orphaned": "Đơn độc", "size/filecount": "Kích cỡ/ Số tệp", "confirm-delete": "Bạn có chắc muốn xóa tệp này không?", diff --git a/public/language/vi/admin/settings/activitypub.json b/public/language/vi/admin/settings/activitypub.json index ed7b3b8643..4343357b7d 100644 --- a/public/language/vi/admin/settings/activitypub.json +++ b/public/language/vi/admin/settings/activitypub.json @@ -42,7 +42,7 @@ "server-filtering": "Lọc", "count": "NodeBB này hiện đã biết về %1 máy chủ", - "server.filter-help": "Chỉ định các máy chủ mà bạn muốn cấm liên kết với NodeBB của mình. Ngoài ra, bạn có thể chọn tham gia có chọn lọc cho phép liên kết có chọn lọc với các máy chủ cụ thể. Cả hai tùy chọn đều được hỗ trợ, mặc dù chúng loại trừ lẫn nhau.", + "server.filter-help": "Chỉ ra các máy chủ mà bạn muốn cấm liên kết với NodeBB của mình. Ngoài ra, bạn có thể chọn tham gia có chọn lọc cho phép liên kết có chọn lọc với các máy chủ cụ thể. Cả hai tùy chọn đều được hỗ trợ, mặc dù chúng loại trừ lẫn nhau.", "server.filter-help-hostname": "Chỉ nhập tên máy chủ bên dưới (vd: example.org), tách nhau bằng ngắt dòng.", "server.filter-allow-list": "Dùng nó làm Danh Sách Cho Phép Thay Thế", diff --git a/public/language/vi/admin/settings/general.json b/public/language/vi/admin/settings/general.json index 5907a11dc6..bfbdbde36a 100644 --- a/public/language/vi/admin/settings/general.json +++ b/public/language/vi/admin/settings/general.json @@ -35,8 +35,8 @@ "touch-icon.help": "Kích cỡ và định dạng được đề xuất: 512x512, chỉ định dạng PNG. Nếu không có biểu tượng cảm ứng nào, NodeBB sẽ quay trở lại sử dụng favicon.", "maskable-icon": "Biểu tượng có thể che được (Màn Hình Trang Chủ)", "maskable-icon.help": "Kích thước và định dạng nên là: 512x512, chỉ định dạng PNG. Nếu không có biểu tượng có thể che được nào được chỉ định, NodeBB sẽ trở lại Biểu Tượng Chạm.", - "screenshot": "Screenshot", - "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", + "screenshot": "Ảnh chụp màn hình", + "screenshot.help": "Kích thước và định dạng được đề xuất: từ 320px đến 3480px, chỉ định dạng JPG và PNG. Nếu không chỉ định ảnh chụp màn hình, NodeBB sẽ sử dụng ảnh chụp màn hình mặc định.", "outgoing-links": "Liên Kết Đi", "outgoing-links.warning-page": "Sử Dụng Trang Cảnh Báo Liên Kết Đi", "search": "Tìm kiếm", diff --git a/public/language/vi/error.json b/public/language/vi/error.json index 1d4e90a668..76ab019087 100644 --- a/public/language/vi/error.json +++ b/public/language/vi/error.json @@ -228,7 +228,7 @@ "invalid-session-text": "Có vẻ như phiên đăng nhập của bạn không còn hoạt động. Vui lòng làm mới trang này.", "session-mismatch": "‎Phiên Không Khớp‎", "session-mismatch-text": "Có vẻ như phiên đăng nhập của bạn không còn khớp với máy chủ. Vui lòng làm mới trang này.", - "no-topics-selected": "Không chọn chủ đề!", + "no-topics-selected": "Chưa chọn chủ đề!", "cant-move-to-same-topic": "Bạn không thể đưa bài đăng vào cùng chủ đề!", "cant-move-topic-to-same-category": "Không thể di chuyển chủ đề đến cùng danh mục!", "cant-move-topic-to-from-remote-categories": "Bạn không thể di chuyển chủ đề vào hoặc ra khỏi các danh mục từ xa; hãy cân nhắc đăng chéo bài viết thay thế.", diff --git a/public/language/vi/global.json b/public/language/vi/global.json index f26ee302c1..f9cc9bcada 100644 --- a/public/language/vi/global.json +++ b/public/language/vi/global.json @@ -49,7 +49,7 @@ "header.account": "Tài khoản", "header.navigation": "Điều hướng", "header.manage": "Quản lý", - "header.drafts": "Bản thảo", + "header.drafts": "Bản nháp", "header.world": "Thế giới", "notifications.loading": "Đang Tải Thông Báo", "chats.loading": "Đang Tải Trò Chuyện", diff --git a/public/language/vi/modules.json b/public/language/vi/modules.json index 6834051f81..912ef6c505 100644 --- a/public/language/vi/modules.json +++ b/public/language/vi/modules.json @@ -113,8 +113,8 @@ "composer.cancel-scheduling": "Hủy Lịch Trình", "composer.change-schedule-date": "Đổi Ngày", "composer.set-schedule-date": "Đặt Ngày", - "composer.discard-all-drafts": "Hủy tất cả bản nháp", - "composer.no-drafts": "Bạn không có bản nháp nào", + "composer.discard-all-drafts": "Loại bỏ tất cả bản nháp", + "composer.no-drafts": "Bạn không có bản nháp", "composer.discard-draft-confirm": "Bạn có muốn hủy bản nháp này không?", "composer.remote-pid-editing": "Sửa bài đăng từ xa", "composer.remote-pid-content-immutable": "Nội dung của bài viết từ xa không thể được chỉnh sửa. Tuy nhiên, bạn có thể thay đổi tiêu đề và gắn thẻ chủ đề.", diff --git a/public/language/vi/reset_password.json b/public/language/vi/reset_password.json index a8cfb41fc3..261cec9f0d 100644 --- a/public/language/vi/reset_password.json +++ b/public/language/vi/reset_password.json @@ -12,7 +12,7 @@ "enter-email-address": "Nhập địa chỉ Email", "password-reset-sent": "Nếu có địa chỉ cụ thể ứng với tài khoản người dùng hiện có, một email đặt lại mật khẩu đã được gửi. Xin lưu ý chỉ có một email được gửi mỗi phút.", "invalid-email": "Email không đúng/không tồn tại!", - "password-too-short": "Mật khẩu bạn nhập quá ngắn, vui lòng chọn một mật khẩu khác.", + "password-too-short": "Mật khẩu đã nhập quá ngắn, vui lòng chọn một mật khẩu khác.", "passwords-do-not-match": "Hai mật khẩu bạn nhập không khớp với nhau.", "password-expired": "Mật khẩu của bạn đã hết hạn, vui lòng chọn một mật khẩu mới." } \ No newline at end of file diff --git a/public/language/vi/search.json b/public/language/vi/search.json index 43b468535f..a71c3441bb 100644 --- a/public/language/vi/search.json +++ b/public/language/vi/search.json @@ -103,8 +103,8 @@ "search-preferences-saved": "Đã lưu tùy chọn tìm kiếm", "search-preferences-cleared": "Đã xóa tùy chọn tìm kiếm", "show-results-as": "Hiện thị kết quả theo", - "show-results-as-topics": "Hiển thị kết quả dưới dạng chủ đề", - "show-results-as-posts": "Hiển thị kết quả dưới dạng bài viết", + "show-results-as-topics": "Kết quả dạng chủ đề", + "show-results-as-posts": "Kết quả dạng bài đăng", "see-more-results": "Xem thêm kết quả (%1)", "search-in-category": "Tìm kiếm trong \"%1\"" } \ No newline at end of file diff --git a/public/language/vi/topic.json b/public/language/vi/topic.json index 76b1583950..5e687d910d 100644 --- a/public/language/vi/topic.json +++ b/public/language/vi/topic.json @@ -168,7 +168,7 @@ "move-topic-instruction": "Chọn danh mục nhắm đến và sau đó nhấp vào di chuyển", "change-owner-instruction": "Bấm vào bài viết bạn muốn chỉ định cho người dùng khác", "manage-editors-instruction": "Quản lý những người dùng có thể chỉnh sửa bài đăng này bên dưới.", - "crossposts.instructions": "Chọn một hoặc nhiều danh mục để đăng chéo bài viết. Bài viết sẽ được truy cập từ danh mục gốc và tất cả các danh mục được đăng chéo.", + "crossposts.instructions": "Chọn một hoặc nhiều danh mục để đăng chéo bài viết. Chủ đề sẽ được truy cập từ danh mục gốc và tất cả các danh mục được đăng chéo.", "crossposts.listing": "Chủ đề này đã được đăng tải chéo lên các chuyên mục nội bộ sau:", "crossposts.none": "Chủ đề này chưa được đăng tải chéo lên bất kỳ chuyên mục nào khác.", "composer.title-placeholder": "Nhập tiêu đề chủ đề của bạn tại đây...", diff --git a/public/language/vi/world.json b/public/language/vi/world.json index 7e8d1181c8..4d86569784 100644 --- a/public/language/vi/world.json +++ b/public/language/vi/world.json @@ -1,7 +1,7 @@ { "name": "Thế giới", - "latest": "Latest (Following)", - "latest-all": "Latest (All)", + "latest": "Mới nhất (Tiếp theo)", + "latest-all": "Mới nhất (Tất cả)", "popular-day": "Phổ biến (Ngày)", "popular-week": "Phổ biến (Tuần)", "popular-month": "Phổ biến (Tháng)", diff --git a/public/language/zh-CN/admin/settings/general.json b/public/language/zh-CN/admin/settings/general.json index 489ebf012b..a6b4916b8e 100644 --- a/public/language/zh-CN/admin/settings/general.json +++ b/public/language/zh-CN/admin/settings/general.json @@ -7,7 +7,7 @@ "title.short-placeholder": "如果没有指定短标题,将会使用站点标题", "title.url": "标题链接地址", "title.url-placeholder": "网站标题链接", - "title.url-help": "当标题被点击时,将向用户发送该地址。如果留空,用户将跳转到论坛索引页面。注意:这不是在电子邮件中使用的外部URL,这由config.json中的url属性设置。", + "title.url-help": "点击标题时,将用户重定向至此地址。若留空,用户将被重定向至论坛首页。注意:此处并非用于电子邮件等场景的外部网址。该网址由 config.json 文件中的 url 属性设定。", "title.name": "您的社区名称", "title.show-in-header": "在顶部显示站点标题", "browser-title": "浏览器标题", @@ -18,7 +18,7 @@ "description": "站点描述", "keywords": "站点关键字", "keywords-placeholder": "描述您的社区的关键字(以逗号分隔)", - "logo-and-icons": "Media & Branding", + "logo-and-icons": "媒体与品牌建设", "logo.image": "图像", "logo.image-placeholder": "要在论坛标题上显示的 Logo 的路径", "logo.upload": "上传", @@ -35,8 +35,8 @@ "touch-icon.help": "推荐的尺寸和格式:512x512,仅限PNG格式。如果没有指定触摸图标,NodeBB将回退到站点图标。", "maskable-icon": "可遮蔽(主屏)图标", "maskable-icon.help": "推荐的尺寸和格式:512x512,仅限PNG格式。如果没有指定可遮蔽图标,NodeBB将回退到触摸图标。", - "screenshot": "Screenshot", - "screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot", + "screenshot": "屏幕截图", + "screenshot.help": "推荐尺寸和格式:320px 至 3480px 之间,仅限 JPG 和 PNG 格式。如果未指定截图,NodeBB 将使用通用截图作为替代", "outgoing-links": "站外链接", "outgoing-links.warning-page": "使用站外链接警告页", "search": "搜索", @@ -49,17 +49,17 @@ "background-color": "背景色", "background-color-help": "当网站安装为 PWA 时用于启动屏幕背景的颜色", "undo-timeout": "撤销超时", - "undo-timeout-help": "部分操作,例如移动主题,将允许版主在一定时间内撤销其操作。设置为 0 可完全禁用撤消。", + "undo-timeout-help": "某些操作(例如移动主题)允许版主在一定时间内撤销该操作。将该值设为 0 可完全禁用撤销功能。", "topic-tools": "主题工具", "home-page": "主页", "home-page-route": "主页路由", - "home-page-description": "选择用户导航到论坛根 URL 时显示的页面。", + "home-page-description": "选择用户访问论坛根 URL 时显示的页面。", "custom-route": "自定义路由", "allow-user-home-pages": "允许用户主页", "home-page-title": "首页标题(默认“Home”)", "default-language": "默认语言", "auto-detect": "自动检测游客的语言设置", - "default-language-help": "默认语言会决定所有用户的语言设定。
单一用户可以各自在帐户设置中覆盖此项设定。", + "default-language-help": "默认语言决定了所有访问您论坛的用户的语言设置。
用户可以在其账号设置页面中覆盖默认语言。", "post-sharing": "帖子分享", - "info-plugins-additional": "插件可以增加可选的用于分享帖子的网络。" + "info-plugins-additional": "插件可以添加额外的社交网络,用于分享帖子。" } \ No newline at end of file diff --git a/public/language/zh-CN/world.json b/public/language/zh-CN/world.json index db61c5c92d..cae52d19af 100644 --- a/public/language/zh-CN/world.json +++ b/public/language/zh-CN/world.json @@ -1,7 +1,7 @@ { "name": "世界", - "latest": "Latest (Following)", - "latest-all": "Latest (All)", + "latest": "最新(关注)", + "latest-all": "最新(全部)", "popular-day": "热门(按天)", "popular-week": "热门(按周)", "popular-month": "热门(按月)", From 09de6fb9ae95762ebadc9f22f88091c3c4dc7882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 14 Mar 2026 17:39:43 -0400 Subject: [PATCH 162/204] perf: switch to set, remove parseFloat in redis add test to cover float --- src/database/redis/sorted.js | 8 ++++---- test/database/sorted.js | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/database/redis/sorted.js b/src/database/redis/sorted.js index 5b9652e6e0..8433133f15 100644 --- a/src/database/redis/sorted.js +++ b/src/database/redis/sorted.js @@ -301,7 +301,7 @@ module.exports = function (module) { const returnData = []; let done; - const seen = Object.create(null); + const seen = new Set(); do { /* eslint-disable no-await-in-loop */ const res = await module.client.zScan(params.key, cursor, { MATCH: params.match, COUNT: 5000 }); @@ -310,11 +310,11 @@ module.exports = function (module) { for (let i = 0; i < res.members.length; i ++) { const item = res.members[i]; - if (!seen[item.value]) { - seen[item.value] = 1; + if (!seen.has(item.value)) { + seen.add(item.value); if (params.withScores) { - returnData.push({ value: item.value, score: parseFloat(item.score) }); + returnData.push({ value: item.value, score: item.score }); } else { returnData.push(item.value); } diff --git a/test/database/sorted.js b/test/database/sorted.js index a375b2ec48..fde2bafb0b 100644 --- a/test/database/sorted.js +++ b/test/database/sorted.js @@ -93,6 +93,22 @@ NUMERIC)-- WsPn&query[cid]=-1&parentCid=0&selectedCids[]=-1&privilege=topics:rea }); assert.strictEqual(data.length, 0); }); + + it('should handle floating point scores', async () => { + await db.sortedSetAdd('scanzset6', [1.5, 2.5, 3.5, 4.5, 5.5, 6.5], ['aaab{', 'bbbb', 'bbcb', 'ddb', 'dddd', 'adb']); + const data = await db.getSortedSetScan({ + key: 'scanzset6', + match: '*b', + withScores: true, + }); + data.sort((a, b) => b.score - a.score); + assert.deepStrictEqual(data, [ + { value: 'adb', score: 6.5 }, + { value: 'ddb', score: 4.5 }, + { value: 'bbcb', score: 3.5 }, + { value: 'bbbb', score: 2.5 }, + ]); + }); }); describe('sortedSetAdd()', () => { From 5f2b6b8ec63eefde018390e30540ee4728b88d44 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Sun, 15 Mar 2026 09:07:40 +0000 Subject: [PATCH 163/204] Latest translations and fallbacks --- public/language/bg/world.json | 4 ++-- public/language/zh-CN/admin/settings/user.json | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/public/language/bg/world.json b/public/language/bg/world.json index 0f78ca89f7..4eb44f7d03 100644 --- a/public/language/bg/world.json +++ b/public/language/bg/world.json @@ -1,7 +1,7 @@ { "name": "Свят", - "latest": "Latest (Following)", - "latest-all": "Latest (All)", + "latest": "Последни (следвани)", + "latest-all": "Последни (всички)", "popular-day": "Популярни (за деня)", "popular-week": "Популярни (за седмицата)", "popular-month": "Популярни (за месеца)", diff --git a/public/language/zh-CN/admin/settings/user.json b/public/language/zh-CN/admin/settings/user.json index ff2acbaa24..7858667fff 100644 --- a/public/language/zh-CN/admin/settings/user.json +++ b/public/language/zh-CN/admin/settings/user.json @@ -6,20 +6,20 @@ "allow-login-with.username-email": "用户名或者邮箱", "allow-login-with.username": "仅限用户名", "account-settings": "用户设置", - "gdpr-enabled": "启用通用数据保护条例(GDPR)许可的个人信息收集", - "gdpr-enabled-help": "当启用时,所有的新注册用户需要明确同意允许数据采集和在通用数据保护协议(GDPR)保护下的使用。注意:开启GDPR不一定要之前已经存在的用户同意。在这之前,您需要去安装GDPR插件。", + "gdpr-enabled": "启用《通用数据保护条例》(GDPR)的同意收集功能", + "gdpr-enabled-help": "启用此功能后,所有新注册用户都必须根据《通用数据保护条例》(GDPR)明确同意数据收集和使用。注意:启用GDPR不会强制现有用户提供同意。若要实现此功能,您需要安装GDPR插件。", "disable-username-changes": "禁用修改用户名", "disable-email-changes": "禁用修改邮箱", "disable-password-changes": "禁用修改密码", - "allow-account-deletion": "允许消除帐号", + "allow-account-deletion": "允许删除账号", "hide-fullname": "隐藏用户的全名", "hide-email": "隐藏用户的电子邮箱", - "show-fullname-as-displayname": "如果可以,把用户的全名作为他们的显示名称。", + "show-fullname-as-displayname": "如果可用,则显示用户的全名作为其显示名称", "themes": "主题", - "disable-user-skins": "阻止用户选择自定义皮肤", + "disable-user-skins": "禁止用户选择自定义皮肤", "account-protection": "帐号保护", "admin-relogin-duration": "管理员无操作自动退出时长 (分钟)", - "admin-relogin-duration-help": "访问管理面板一段时间后需要重新登录以保证管理面板的安全,设置为0以禁用。", + "admin-relogin-duration-help": "在管理员区域停留一定时间后,系统将要求重新登录;将该值设为 0 可禁用此功能", "login-attempts": "每小时尝试登录次数", "login-attempts-help": "如果用户的尝试登录次数超过此界限,该帐号将会被被锁定预设的时间。", "lockout-duration": "帐户锁定时间(分钟)", From ec4e87ff84531b4f461cc3e92b8ec61ed488c4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 15 Mar 2026 14:23:01 -0400 Subject: [PATCH 164/204] chore: up harmony change group description to a textarea --- install/package.json | 2 +- src/views/admin/manage/group.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 507f5c5a8d..9eaf413ac3 100644 --- a/install/package.json +++ b/install/package.json @@ -108,7 +108,7 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.53", + "nodebb-theme-harmony": "2.2.54", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", "nodebb-theme-persona": "14.2.28", diff --git a/src/views/admin/manage/group.tpl b/src/views/admin/manage/group.tpl index f08142547c..775bd5f77f 100644 --- a/src/views/admin/manage/group.tpl +++ b/src/views/admin/manage/group.tpl @@ -44,7 +44,7 @@
- +
From 47f59dcf8ca6ac34d446d934a67e79d1c1ee4b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 15 Mar 2026 15:18:17 -0400 Subject: [PATCH 165/204] fix profile image update in header after changes --- public/src/modules/accounts/picture.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/src/modules/accounts/picture.js b/public/src/modules/accounts/picture.js index 651ce7deaa..7ddd4cebb7 100644 --- a/public/src/modules/accounts/picture.js +++ b/public/src/modules/accounts/picture.js @@ -108,7 +108,9 @@ define('accounts/picture', [ const headerIconEl = $(`[component="header/avatar"] [component="avatar/icon"]`); if (picture) { - if (!headerPictureEl.length && headerIconEl.length) { + if (headerPictureEl.length) { + headerPictureEl.attr('src', picture); + } else if (headerIconEl.length) { const img = $(''); $(headerIconEl[0].attributes).each(function () { img.attr(this.nodeName, this.nodeValue); From 0124b50a28cf684ebcb80bb15c6e1a90b18e4aa9 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Mon, 16 Mar 2026 09:07:39 +0000 Subject: [PATCH 166/204] Latest translations and fallbacks --- public/language/ru/topic.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/public/language/ru/topic.json b/public/language/ru/topic.json index 8020fa99fd..cdbd32efc5 100644 --- a/public/language/ru/topic.json +++ b/public/language/ru/topic.json @@ -15,7 +15,7 @@ "replies-to-this-post": "%1 ответов", "one-reply-to-this-post": "1 ответ", "last-reply-time": "Последний ответ", - "reply-options": "Reply options", + "reply-options": "Варианты ответа", "reply-as-topic": "Ответить, создав новую тему", "guest-login-reply": "Авторизуйтесь, чтобы ответить", "login-to-view": "Авторизуйтесь, чтобы просмотреть", @@ -36,7 +36,7 @@ "pinned": "Прикреплена", "pinned-with-expiry": "Закреплен до %1", "scheduled": "Запланировано", - "deleted": "Deleted", + "deleted": "Удалён", "moved": "Перенесена", "moved-from": "Перенесено с %1", "copy-code": "Copy Code", @@ -145,7 +145,7 @@ "loading-more-posts": "Загружаем больше сообщений", "move-topic": "Перенести тему", "move-topics": "Перенести темы", - "crosspost-topic": "Cross-post Topic", + "crosspost-topic": "Дублировать тему", "move-post": "Перенести сообщение", "post-moved": "Сообщение перенесено!", "fork-topic": "Создать дополнительную ветвь дискуссии", @@ -168,7 +168,7 @@ "move-topic-instruction": "Select the target category and then click move", "change-owner-instruction": "Нажмите на сообщения, которые вы хотите присвоить другому пользователю", "manage-editors-instruction": "Manage the users who can edit this post below.", - "crossposts.instructions": "Select one or more categories to cross-post to. Topic(s) will be accessible from the original category and all cross-posted categories.", + "crossposts.instructions": "Укажите дополнительные категории для этой темы. Она будет видна во всех выбранных разделах, исходная категория сохранится.", "crossposts.listing": "This topic has been cross-posted to the following local categories:", "crossposts.none": "This topic has not been cross-posted to any additional categories", "composer.title-placeholder": "Введите название темы...", @@ -228,14 +228,14 @@ "post-quick-create": "Quick post", "navigator.index": "Сообщений %1 от %2", "navigator.unread": "%1 непрочитано", - "upvote-post": "Upvote post", - "downvote-post": "Downvote post", + "upvote-post": "Полезно", + "downvote-post": "Не полезно", "post-tools": "Post tools", "unread-posts-link": "Unread posts link", "thumb-image": "Topic thumbnail image", - "announcers": "Shares", - "announcers-x": "Shares (%1)", - "guest-cta.title": "Hello! It looks like you're interested in this conversation, but you don't have an account yet.", - "guest-cta.message": "Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.", - "guest-cta.closing": "With your input, this post could be even better 💗" + "announcers": "Поделиться", + "announcers-x": "Поделиться (%1)", + "guest-cta.title": "Здравствуйте! Похоже, вам интересна эта беседа, но у вас пока нет учетной записи.", + "guest-cta.message": "Вы устали просматривать одни и те же посты каждый раз, когда заходите на сайт? После регистрации, вам не придётся искать обсуждения в которых вы принимали участие, настройте уведомления о новых сообщениях так как вам это удобно (по электронной почте или уведомлением). У вас появится возможность сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.", + "guest-cta.closing": "С вашими комментариями этот пост может стать ещё лучше 💗" } \ No newline at end of file From 2eb0964d3a58eaf00e1d9b7d3cfd1ecd77f74f0a Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Mar 2026 11:08:53 -0400 Subject: [PATCH 167/204] fix: restrict contextmenu preventDefault to the checkbox only --- public/src/modules/topicSelect.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/public/src/modules/topicSelect.js b/public/src/modules/topicSelect.js index 54cffc4465..70a53f25a0 100644 --- a/public/src/modules/topicSelect.js +++ b/public/src/modules/topicSelect.js @@ -43,9 +43,6 @@ define('topicSelect', ['components'], function (components) { // Long press let longPressTimeout; const start = function (ev) { - if (ev.type === 'touchstart') { - ev.preventDefault(); - } isLongPress = false; longPressTimeout = setTimeout(() => { isLongPress = true; @@ -68,7 +65,7 @@ define('topicSelect', ['components'], function (components) { topicsContainer.on('mouseleave', '[component="topic/select"]', cancel); topicsContainer.on('touchend', '[component="topic/select"]', cancel); topicsContainer.on('touchcancel', '[component="topic/select"]', cancel); - topicsContainer.on('contextmenu', (e) => { + topicsContainer.on('contextmenu', '[component="topic/select"]', (e) => { e.preventDefault(); }); }; From 6c01a5d84f59c3ed80a7da9a9094e9b7cc943003 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Mar 2026 12:33:47 -0400 Subject: [PATCH 168/204] feat: #14094, notification drawer UX improvements --- install/package.json | 4 ++-- public/src/client/header/notifications.js | 18 +++++++++++++++++- public/src/modules/notifications.js | 6 ++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/install/package.json b/install/package.json index 9eaf413ac3..6631108574 100644 --- a/install/package.json +++ b/install/package.json @@ -108,10 +108,10 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.54", + "nodebb-theme-harmony": "2.2.55", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", - "nodebb-theme-persona": "14.2.28", + "nodebb-theme-persona": "14.2.29", "nodebb-widget-essentials": "7.0.43", "nodemailer": "8.0.2", "nprogress": "0.2.0", diff --git a/public/src/client/header/notifications.js b/public/src/client/header/notifications.js index fc402b8d96..6d833ccbd6 100644 --- a/public/src/client/header/notifications.js +++ b/public/src/client/header/notifications.js @@ -5,6 +5,7 @@ define('forum/header/notifications', function () { notifications.prepareDOM = function () { const notifTrigger = $('[component="notifications"] [data-bs-toggle="dropdown"]'); + const listEl = document.querySelector('[component="notifications/list"]'); notifTrigger.on('show.bs.dropdown', async (ev) => { const notifications = await app.require('notifications'); @@ -15,11 +16,26 @@ define('forum/header/notifications', function () { notifTrigger.each((index, el) => { const triggerEl = $(el); const dropdownEl = triggerEl.parent().find('.dropdown-menu'); + const listEl = dropdownEl.find('[component="notifications/list"]'); if (dropdownEl.hasClass('show')) { app.require('notifications').then((notifications) => { - notifications.loadNotifications(triggerEl, dropdownEl.find('[component="notifications/list"]')); + notifications.loadNotifications(triggerEl, listEl); }); } + + dropdownEl.on('click', '[data-filter]', (e) => { + const filter = e.target.getAttribute('data-filter'); + + if (filter === 'unread') { + listEl.get(0).querySelectorAll('[data-nid]:not(.unread)').forEach((e) => { + e.classList.toggle('hidden', true); + }); + } else { + listEl.get(0).querySelectorAll('[data-nid]').forEach((e) => { + e.classList.toggle('hidden', false); + }); + } + }); }); socket.removeListener('event:new_notification', onNewNotification); diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index f4806eafdf..70dc54e3bb 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -42,7 +42,7 @@ define('notifications', [ hooks.fire('filter:notifications.load', { notifications: notifs }).then(({ notifications }) => { app.parseAndTranslate('partials/notifications_list', { notifications }, function (html) { notifList.html(html); - notifList.off('click').on('click', '[data-nid]', function (ev) { + notifList.off('click').on('click', '[component="notifications/item/link"]', function (ev) { const notifEl = $(this); if (scrollToPostIndexIfOnPage(notifEl)) { ev.stopPropagation(); @@ -62,6 +62,9 @@ define('notifications', [ components.get('notifications').on('click', '.mark-all-read', () => { Notifications.markAllRead(); }); + components.get('notifications').on('click', `[href="${config.relative_path}/notifications"]`, () => { + triggerEl.dropdown('toggle'); + }); Notifications.handleUnreadButton(notifList); @@ -86,7 +89,6 @@ define('notifications', [ $this.find('.unread').toggleClass('hidden', unread); $this.find('.read').toggleClass('hidden', !unread); }); - return false; }); }; From 44e65b8d73f058bf3d11b8dc3bef5036292e321f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Mar 2026 13:34:04 -0400 Subject: [PATCH 169/204] feat: ability to show only local posts in /world --- install/package.json | 4 +- public/language/en-GB/world.json | 3 +- public/src/client/world.js | 8 +++- src/controllers/activitypub/topics.js | 60 +++++++++++++++------------ 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/install/package.json b/install/package.json index 6631108574..17da5d468c 100644 --- a/install/package.json +++ b/install/package.json @@ -108,10 +108,10 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.55", + "nodebb-theme-harmony": "2.2.56", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", - "nodebb-theme-persona": "14.2.29", + "nodebb-theme-persona": "14.2.30", "nodebb-widget-essentials": "7.0.43", "nodemailer": "8.0.2", "nprogress": "0.2.0", diff --git a/public/language/en-GB/world.json b/public/language/en-GB/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/en-GB/world.json +++ b/public/language/en-GB/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/src/client/world.js b/public/src/client/world.js index 6e63b85a23..61492a5d9f 100644 --- a/public/src/client/world.js +++ b/public/src/client/world.js @@ -45,7 +45,13 @@ define('forum/world', [ } default: { - translator.translate(`[[world:latest${params.get('all') === '1' ? '-all' : ''}]]`, function (translated) { + let suffix = ''; + if (params.get('all') === '1') { + suffix = '-all'; + } else if (params.get('local') === '1') { + suffix = '-local'; + } + translator.translate(`[[world:latest${suffix}]]`, function (translated) { sortLabelEl.innerText = translated; }); break; diff --git a/src/controllers/activitypub/topics.js b/src/controllers/activitypub/topics.js index 8785fecc14..d45b187761 100644 --- a/src/controllers/activitypub/topics.js +++ b/src/controllers/activitypub/topics.js @@ -16,8 +16,8 @@ const helpers = require('../helpers'); const controller = module.exports; controller.list = async function (req, res) { - if (!req.uid && !req.query.sort && !req.query.all) { - return helpers.redirect(res, '/world?all=1', false); + if (!req.uid && !req.query.sort && !req.query.local) { + return helpers.redirect(res, '/world?local=1', false); } const { topicsPerPage } = await user.getSettings(req.uid); @@ -50,12 +50,14 @@ controller.list = async function (req, res) { let tids; let topicCount; + let { local } = req.query; + local = parseInt(local, 10) === 1; if (req.query.sort === 'popular') { cidQuery = { ...cidQuery, sort: 'posts', term: req.query.term || 'day', - includeRemote: true, + includeRemote: !local, followingOnly: !req.query.all || !parseInt(req.query.all, 10), }; delete cidQuery.cid; @@ -65,7 +67,7 @@ controller.list = async function (req, res) { cidQuery = { ...cidQuery, term: req.query.term, - includeRemote: true, + includeRemote: !local, followingOnly: !req.query.all || !parseInt(req.query.all, 10), }; delete cidQuery.cid; @@ -110,29 +112,33 @@ controller.list = async function (req, res) { data.showSelect = true; // Tracked/watched categories - let cids = await user.getCategoriesByStates(req.uid, [ - categories.watchStates.tracking, categories.watchStates.watching, - ]); - cids = cids.filter(cid => !utils.isNumber(cid)); - const [categoryData, watchState] = await Promise.all([ - categories.getCategories(cids), - categories.getWatchState(cids, req.uid), - ]); - data.categories = categories.getTree(categoryData, 0); - await Promise.all([ - categories.getRecentTopicReplies(categoryData, req.uid, req.query), - categories.setUnread(data.categories, cids, req.uid), - ]); - data.categories.forEach((category, idx) => { - if (category) { - helpers.trimChildren(category); - helpers.setCategoryTeaser(category); - category.isWatched = watchState[idx] === categories.watchStates.watching; - category.isTracked = watchState[idx] === categories.watchStates.tracking; - category.isNotWatched = watchState[idx] === categories.watchStates.notwatching; - category.isIgnored = watchState[idx] === categories.watchStates.ignoring; - } - }); + if (req.uid) { + let cids = await user.getCategoriesByStates(req.uid, [ + categories.watchStates.tracking, categories.watchStates.watching, + ]); + cids = cids.filter(cid => !utils.isNumber(cid)); + const [categoryData, watchState] = await Promise.all([ + categories.getCategories(cids), + categories.getWatchState(cids, req.uid), + ]); + data.categories = categories.getTree(categoryData, 0); + await Promise.all([ + categories.getRecentTopicReplies(categoryData, req.uid, req.query), + categories.setUnread(data.categories, cids, req.uid), + ]); + data.categories.forEach((category, idx) => { + if (category) { + helpers.trimChildren(category); + helpers.setCategoryTeaser(category); + category.isWatched = watchState[idx] === categories.watchStates.watching; + category.isTracked = watchState[idx] === categories.watchStates.tracking; + category.isNotWatched = watchState[idx] === categories.watchStates.notwatching; + category.isIgnored = watchState[idx] === categories.watchStates.ignoring; + } + }); + } else { + data.categories = []; + } data.title = translator.escape(data.name); data.breadcrumbs = helpers.buildBreadcrumbs([]); From 61414fa4fbe901909a259f6b3e3fc90ef608f0d3 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Mon, 16 Mar 2026 17:34:32 +0000 Subject: [PATCH 170/204] chore(i18n): fallback strings for new resources: nodebb.world --- public/language/ar/world.json | 3 ++- public/language/az/world.json | 3 ++- public/language/bg/world.json | 3 ++- public/language/bn/world.json | 3 ++- public/language/cs/world.json | 3 ++- public/language/da/world.json | 3 ++- public/language/de/world.json | 3 ++- public/language/el/world.json | 3 ++- public/language/en-US/world.json | 3 ++- public/language/en-x-pirate/world.json | 3 ++- public/language/es/world.json | 3 ++- public/language/et/world.json | 3 ++- public/language/fa-IR/world.json | 3 ++- public/language/fi/world.json | 3 ++- public/language/fr/world.json | 3 ++- public/language/gl/world.json | 3 ++- public/language/he/world.json | 3 ++- public/language/hr/world.json | 3 ++- public/language/hu/world.json | 3 ++- public/language/hy/world.json | 3 ++- public/language/id/world.json | 3 ++- public/language/it/world.json | 3 ++- public/language/ja/world.json | 3 ++- public/language/ko/world.json | 3 ++- public/language/lt/world.json | 3 ++- public/language/lv/world.json | 3 ++- public/language/ms/world.json | 3 ++- public/language/nb/world.json | 3 ++- public/language/nl/world.json | 3 ++- public/language/nn-NO/world.json | 3 ++- public/language/pl/world.json | 3 ++- public/language/pt-BR/world.json | 3 ++- public/language/pt-PT/world.json | 3 ++- public/language/ro/world.json | 3 ++- public/language/ru/world.json | 3 ++- public/language/rw/world.json | 3 ++- public/language/sc/world.json | 3 ++- public/language/sk/world.json | 3 ++- public/language/sl/world.json | 3 ++- public/language/sq-AL/world.json | 3 ++- public/language/sr/world.json | 3 ++- public/language/sv/world.json | 3 ++- public/language/th/world.json | 3 ++- public/language/tr/world.json | 3 ++- public/language/uk/world.json | 3 ++- public/language/ur/world.json | 3 ++- public/language/vi/world.json | 3 ++- public/language/zh-CN/world.json | 3 ++- public/language/zh-TW/world.json | 3 ++- 49 files changed, 98 insertions(+), 49 deletions(-) diff --git a/public/language/ar/world.json b/public/language/ar/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/ar/world.json +++ b/public/language/ar/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/az/world.json b/public/language/az/world.json index f4101247d6..2197d73e9f 100644 --- a/public/language/az/world.json +++ b/public/language/az/world.json @@ -1,6 +1,7 @@ { "name": "Dünya", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/bg/world.json b/public/language/bg/world.json index 4eb44f7d03..fc44b81ec8 100644 --- a/public/language/bg/world.json +++ b/public/language/bg/world.json @@ -1,6 +1,7 @@ { "name": "Свят", - "latest": "Последни (следвани)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Последни (всички)", "popular-day": "Популярни (за деня)", "popular-week": "Популярни (за седмицата)", diff --git a/public/language/bn/world.json b/public/language/bn/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/bn/world.json +++ b/public/language/bn/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/cs/world.json b/public/language/cs/world.json index 5d64fc0acd..c5c4a0e9e6 100644 --- a/public/language/cs/world.json +++ b/public/language/cs/world.json @@ -1,6 +1,7 @@ { "name": "Svět", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/da/world.json b/public/language/da/world.json index ed22d19f06..699c95f0c0 100644 --- a/public/language/da/world.json +++ b/public/language/da/world.json @@ -1,6 +1,7 @@ { "name": "Verden", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/de/world.json b/public/language/de/world.json index 55a1fbfb24..f450566099 100644 --- a/public/language/de/world.json +++ b/public/language/de/world.json @@ -1,6 +1,7 @@ { "name": "Welt", - "latest": "Neueste (Gefolgt)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Neueste (Alle)", "popular-day": "Beliebt (Tag)", "popular-week": "Beliebt (Woche)", diff --git a/public/language/el/world.json b/public/language/el/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/el/world.json +++ b/public/language/el/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/en-US/world.json b/public/language/en-US/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/en-US/world.json +++ b/public/language/en-US/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/en-x-pirate/world.json b/public/language/en-x-pirate/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/en-x-pirate/world.json +++ b/public/language/en-x-pirate/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/es/world.json b/public/language/es/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/es/world.json +++ b/public/language/es/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/et/world.json b/public/language/et/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/et/world.json +++ b/public/language/et/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/fa-IR/world.json b/public/language/fa-IR/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/fa-IR/world.json +++ b/public/language/fa-IR/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/fi/world.json b/public/language/fi/world.json index 87d193efa9..cbf5526761 100644 --- a/public/language/fi/world.json +++ b/public/language/fi/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/fr/world.json b/public/language/fr/world.json index fb839a8ce8..e4975a78c3 100644 --- a/public/language/fr/world.json +++ b/public/language/fr/world.json @@ -1,6 +1,7 @@ { "name": "Monde", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Populaires (Jour)", "popular-week": "Populaires (Semaine)", diff --git a/public/language/gl/world.json b/public/language/gl/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/gl/world.json +++ b/public/language/gl/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/he/world.json b/public/language/he/world.json index 9235d136c3..1c7ba7a867 100644 --- a/public/language/he/world.json +++ b/public/language/he/world.json @@ -1,6 +1,7 @@ { "name": "עולם", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "פופולרי (יומי)", "popular-week": "פופולרי (שבועי)", diff --git a/public/language/hr/world.json b/public/language/hr/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/hr/world.json +++ b/public/language/hr/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/hu/world.json b/public/language/hu/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/hu/world.json +++ b/public/language/hu/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/hy/world.json b/public/language/hy/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/hy/world.json +++ b/public/language/hy/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/id/world.json b/public/language/id/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/id/world.json +++ b/public/language/id/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/it/world.json b/public/language/it/world.json index 859fdbedcd..103ab1daad 100644 --- a/public/language/it/world.json +++ b/public/language/it/world.json @@ -1,6 +1,7 @@ { "name": "Mondo", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popolare (Giorno)", "popular-week": "Popolare (Settimana)", diff --git a/public/language/ja/world.json b/public/language/ja/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/ja/world.json +++ b/public/language/ja/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/ko/world.json b/public/language/ko/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/ko/world.json +++ b/public/language/ko/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/lt/world.json b/public/language/lt/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/lt/world.json +++ b/public/language/lt/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/lv/world.json b/public/language/lv/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/lv/world.json +++ b/public/language/lv/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/ms/world.json b/public/language/ms/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/ms/world.json +++ b/public/language/ms/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/nb/world.json b/public/language/nb/world.json index bd39706af9..d0c3e5e232 100644 --- a/public/language/nb/world.json +++ b/public/language/nb/world.json @@ -1,6 +1,7 @@ { "name": "Verden", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/nl/world.json b/public/language/nl/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/nl/world.json +++ b/public/language/nl/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/nn-NO/world.json b/public/language/nn-NO/world.json index 45ae2a8b3e..3a43c31a8b 100644 --- a/public/language/nn-NO/world.json +++ b/public/language/nn-NO/world.json @@ -1,6 +1,7 @@ { "name": "Verda", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/pl/world.json b/public/language/pl/world.json index 62f0daf537..1b37dc05eb 100644 --- a/public/language/pl/world.json +++ b/public/language/pl/world.json @@ -1,6 +1,7 @@ { "name": "Świat", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popularne (Dziś)", "popular-week": "Popularne (W Tygodniu)", diff --git a/public/language/pt-BR/world.json b/public/language/pt-BR/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/pt-BR/world.json +++ b/public/language/pt-BR/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/pt-PT/world.json b/public/language/pt-PT/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/pt-PT/world.json +++ b/public/language/pt-PT/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/ro/world.json b/public/language/ro/world.json index dc182ccab2..7938c5ebeb 100644 --- a/public/language/ro/world.json +++ b/public/language/ro/world.json @@ -1,6 +1,7 @@ { "name": "Lumea", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/ru/world.json b/public/language/ru/world.json index e83f4ee497..e3b375a865 100644 --- a/public/language/ru/world.json +++ b/public/language/ru/world.json @@ -1,6 +1,7 @@ { "name": "Мир", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/rw/world.json b/public/language/rw/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/rw/world.json +++ b/public/language/rw/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/sc/world.json b/public/language/sc/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/sc/world.json +++ b/public/language/sc/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/sk/world.json b/public/language/sk/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/sk/world.json +++ b/public/language/sk/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/sl/world.json b/public/language/sl/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/sl/world.json +++ b/public/language/sl/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/sq-AL/world.json b/public/language/sq-AL/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/sq-AL/world.json +++ b/public/language/sq-AL/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/sr/world.json b/public/language/sr/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/sr/world.json +++ b/public/language/sr/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/sv/world.json b/public/language/sv/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/sv/world.json +++ b/public/language/sv/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/th/world.json b/public/language/th/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/th/world.json +++ b/public/language/th/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/tr/world.json b/public/language/tr/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/tr/world.json +++ b/public/language/tr/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/uk/world.json b/public/language/uk/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/uk/world.json +++ b/public/language/uk/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/ur/world.json b/public/language/ur/world.json index df3cc4f334..80727acc33 100644 --- a/public/language/ur/world.json +++ b/public/language/ur/world.json @@ -1,6 +1,7 @@ { "name": "جهان", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", diff --git a/public/language/vi/world.json b/public/language/vi/world.json index 4d86569784..a883e532dd 100644 --- a/public/language/vi/world.json +++ b/public/language/vi/world.json @@ -1,6 +1,7 @@ { "name": "Thế giới", - "latest": "Mới nhất (Tiếp theo)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Mới nhất (Tất cả)", "popular-day": "Phổ biến (Ngày)", "popular-week": "Phổ biến (Tuần)", diff --git a/public/language/zh-CN/world.json b/public/language/zh-CN/world.json index cae52d19af..e43b0fb8e7 100644 --- a/public/language/zh-CN/world.json +++ b/public/language/zh-CN/world.json @@ -1,6 +1,7 @@ { "name": "世界", - "latest": "最新(关注)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "最新(全部)", "popular-day": "热门(按天)", "popular-week": "热门(按周)", diff --git a/public/language/zh-TW/world.json b/public/language/zh-TW/world.json index 58e6526fbb..af21f98948 100644 --- a/public/language/zh-TW/world.json +++ b/public/language/zh-TW/world.json @@ -1,6 +1,7 @@ { "name": "World", - "latest": "Latest (Following)", + "latest": "Latest", + "latest-local": "Latest (Local)", "latest-all": "Latest (All)", "popular-day": "Popular (Day)", "popular-week": "Popular (Week)", From 67a93da507b62d3a2347ba51393a559a524d628c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Mar 2026 13:50:39 -0400 Subject: [PATCH 171/204] fix: add back 'after' query param handling in /world that was removed accidentally --- src/controllers/activitypub/topics.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/controllers/activitypub/topics.js b/src/controllers/activitypub/topics.js index d45b187761..85659fc466 100644 --- a/src/controllers/activitypub/topics.js +++ b/src/controllers/activitypub/topics.js @@ -23,8 +23,8 @@ controller.list = async function (req, res) { const { topicsPerPage } = await user.getSettings(req.uid); let { page, after } = req.query; page = parseInt(page, 10) || 1; - const start = Math.max(0, (page - 1) * topicsPerPage); - const stop = start + topicsPerPage - 1; + let start = Math.max(0, (page - 1) * topicsPerPage); + let stop = start + topicsPerPage - 1; const [userSettings, userPrivileges] = await Promise.all([ user.getSettings(req.uid), @@ -72,6 +72,16 @@ controller.list = async function (req, res) { }; delete cidQuery.cid; ({ tids, topicCount } = await topics.getSortedTopics(cidQuery)); + + if (after) { + // Update start/stop with values inferred from `after` + const index = tids.indexOf(utils.isNumber(after) ? parseInt(after, 10) : after); + if (index && start - index < 1) { + const count = stop - start; + start = index + 1; + stop = start + count; + } + } tids = tids.slice(start, stop !== -1 ? stop + 1 : undefined); } data.topicCount = topicCount; From 53286625107c1c4cfbbc66e4608ef01b9fd51994 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Mar 2026 14:55:31 -0400 Subject: [PATCH 172/204] fix: removing topic tools/checkbox from /world for guests, reword guest CTA in /world --- public/language/en-GB/world.json | 8 ++++---- src/controllers/activitypub/topics.js | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/public/language/en-GB/world.json b/public/language/en-GB/world.json index af21f98948..2057e13d00 100644 --- a/public/language/en-GB/world.json +++ b/public/language/en-GB/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/src/controllers/activitypub/topics.js b/src/controllers/activitypub/topics.js index 85659fc466..775d738305 100644 --- a/src/controllers/activitypub/topics.js +++ b/src/controllers/activitypub/topics.js @@ -26,9 +26,10 @@ controller.list = async function (req, res) { let start = Math.max(0, (page - 1) * topicsPerPage); let stop = start + topicsPerPage - 1; - const [userSettings, userPrivileges] = await Promise.all([ + const [userSettings, userPrivileges, isAdminOrGlobalMod] = await Promise.all([ user.getSettings(req.uid), privileges.categories.get('-1', req.uid), + user.isAdminOrGlobalMod(req.uid), ]); const targetUid = await user.getUidByUserslug(req.query.author); let cidQuery = { @@ -118,8 +119,8 @@ controller.list = async function (req, res) { }); data.showThumbs = req.loggedIn || meta.config.privateUploads !== 1; data.posts = postData; - data.showTopicTools = true; - data.showSelect = true; + data.showTopicTools = isAdminOrGlobalMod; + data.showSelect = isAdminOrGlobalMod; // Tracked/watched categories if (req.uid) { From 3aa8d5baddcba6e6e26046fb17a02608a9a15b3b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Mar 2026 14:59:37 -0400 Subject: [PATCH 173/204] fix: bump themes --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 17da5d468c..55d3247aba 100644 --- a/install/package.json +++ b/install/package.json @@ -108,10 +108,10 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.56", + "nodebb-theme-harmony": "2.2.57", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", - "nodebb-theme-persona": "14.2.30", + "nodebb-theme-persona": "14.2.31", "nodebb-widget-essentials": "7.0.43", "nodemailer": "8.0.2", "nprogress": "0.2.0", From e9063a63e7aa73c1e7c8c3f1fca087a4bbaeffdf Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Mon, 16 Mar 2026 19:00:04 +0000 Subject: [PATCH 174/204] chore(i18n): fallback strings for new resources: nodebb.world --- public/language/ar/world.json | 8 ++++---- public/language/az/world.json | 8 ++++---- public/language/bg/world.json | 8 ++++---- public/language/bn/world.json | 8 ++++---- public/language/cs/world.json | 8 ++++---- public/language/da/world.json | 8 ++++---- public/language/de/world.json | 8 ++++---- public/language/el/world.json | 8 ++++---- public/language/en-US/world.json | 8 ++++---- public/language/en-x-pirate/world.json | 8 ++++---- public/language/es/world.json | 8 ++++---- public/language/et/world.json | 8 ++++---- public/language/fa-IR/world.json | 8 ++++---- public/language/fi/world.json | 8 ++++---- public/language/fr/world.json | 8 ++++---- public/language/gl/world.json | 8 ++++---- public/language/he/world.json | 8 ++++---- public/language/hr/world.json | 8 ++++---- public/language/hu/world.json | 8 ++++---- public/language/hy/world.json | 8 ++++---- public/language/id/world.json | 8 ++++---- public/language/it/world.json | 8 ++++---- public/language/ja/world.json | 8 ++++---- public/language/ko/world.json | 8 ++++---- public/language/lt/world.json | 8 ++++---- public/language/lv/world.json | 8 ++++---- public/language/ms/world.json | 8 ++++---- public/language/nb/world.json | 8 ++++---- public/language/nl/world.json | 8 ++++---- public/language/nn-NO/world.json | 8 ++++---- public/language/pl/world.json | 8 ++++---- public/language/pt-BR/world.json | 8 ++++---- public/language/pt-PT/world.json | 8 ++++---- public/language/ro/world.json | 8 ++++---- public/language/ru/world.json | 8 ++++---- public/language/rw/world.json | 8 ++++---- public/language/sc/world.json | 8 ++++---- public/language/sk/world.json | 8 ++++---- public/language/sl/world.json | 8 ++++---- public/language/sq-AL/world.json | 8 ++++---- public/language/sr/world.json | 8 ++++---- public/language/sv/world.json | 8 ++++---- public/language/th/world.json | 8 ++++---- public/language/tr/world.json | 8 ++++---- public/language/uk/world.json | 8 ++++---- public/language/ur/world.json | 8 ++++---- public/language/vi/world.json | 8 ++++---- public/language/zh-CN/world.json | 8 ++++---- public/language/zh-TW/world.json | 8 ++++---- 49 files changed, 196 insertions(+), 196 deletions(-) diff --git a/public/language/ar/world.json b/public/language/ar/world.json index af21f98948..2057e13d00 100644 --- a/public/language/ar/world.json +++ b/public/language/ar/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/az/world.json b/public/language/az/world.json index 2197d73e9f..65e4bface8 100644 --- a/public/language/az/world.json +++ b/public/language/az/world.json @@ -18,10 +18,10 @@ "help.federating": "Eyni şəkildə, bu forumdan kənar istifadəçilər sizi izləməyə başlayarsa, o zaman yazılarınız həmin proqramlarda və vebsaytlarda da görünməyə başlayacaq.", "help.next-generation": "Bu, sosial medianın yeni nəslidir, bu gün töhfə verməyə başlayın!", - "onboard.title": "Sizin fediverse pəncərəniz...", - "onboard.what": "Bu, yalnız bu forumdan kənarda tapılan məzmundan ibarət sizin fərdiləşdirilmiş kateqoriyanızdır. Bu səhifədə nəyinsə görünüb-göstərilməməsi onları izlədiyinizdən və ya həmin postun izlədiyiniz biri tərəfindən paylaşılıb-paylaşılmamasından asılıdır.", - "onboard.why": "Bu forumdan kənarda gedən çox şey var və bunların heç də hamısı maraqlarınıza uyğun deyil. Buna görə də insanları izləmək, kimdənsə daha çox görmək istədiyinizi bildirməyin ən yaxşı yoludur.", - "onboard.how": "Bu arada, bu forumun daha nələr haqqında bildiyini görmək üçün yuxarıdakı qısayol düymələrinə klikləyə və bəzi yeni məzmunlar kəşf etməyə başlaya bilərsiniz!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/bg/world.json b/public/language/bg/world.json index fc44b81ec8..6e332f3fe8 100644 --- a/public/language/bg/world.json +++ b/public/language/bg/world.json @@ -18,10 +18,10 @@ "help.federating": "По същия начин, ако потребители извън този форум започнат да следват Вас, тогава Вашите публикации ще започнат да се появяват в техните приложения и уеб сайтове.", "help.next-generation": "Това е новото поколение социална мрежа. Започнете да допринасяте още днес!", - "onboard.title": "Вашият прозорец към федивселената…", - "onboard.what": "Това е Вашата персонализирана категория съставена само от съдържание извън този форум. Тук се появяват неща от хора, които следвате, както и такива споделени от тях.", - "onboard.why": "Много неща се случват извън този форум, и не всичко отговаря на Вашите интереси. Затова следването на конкретни хора е най-добрият начин да покажете, че искате да виждате повече от тях.", - "onboard.how": "Междувременно можете да използвате бутоните в горната част, за да видите до какво има достъп този форум. Така може да започнете да откривате ново съдържание!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Търсене на категория…", "see-more": "Вижте повече", diff --git a/public/language/bn/world.json b/public/language/bn/world.json index af21f98948..2057e13d00 100644 --- a/public/language/bn/world.json +++ b/public/language/bn/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/cs/world.json b/public/language/cs/world.json index c5c4a0e9e6..c2ccc649f9 100644 --- a/public/language/cs/world.json +++ b/public/language/cs/world.json @@ -18,10 +18,10 @@ "help.federating": "Stejně tak, pokud vás začnou sledovat uživatelé mimo toto fórum, vaše příspěvky se začnou zobrazovat i v těchto aplikacích a na webových stránkách.", "help.next-generation": "Toto je nová generace sociálních sítí – začněte přispívat ještě dnes!", - "onboard.title": "Vaše okno do fediverse…", - "onboard.what": "Toto je vaše personalizovaná kategorie složená pouze z obsahu nalezeného mimo toto fórum. To, zda se něco zobrazí na této stránce, závisí na tom, zda tyto zdroje sledujete, nebo zda byl příspěvek sdílen někým, koho sledujete.", - "onboard.why": "Mimo toto fórum se děje spousta věcí, a ne všechno je relevantní pro vaše zájmy. Proto je sledování lidí nejlepší způsob, jak naznačit, že chcete vidět více obsahu od konkrétní osoby.", - "onboard.how": "Mezitím můžete kliknout na tlačítka zkratek nahoře, abyste viděli o čem dalším toto fórum ví, a začít objevovat nový obsah!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/da/world.json b/public/language/da/world.json index 699c95f0c0..3637ccc7b0 100644 --- a/public/language/da/world.json +++ b/public/language/da/world.json @@ -18,10 +18,10 @@ "help.federating": "Ligeså, hvis brugere udefra dette fourm begynder at følge dig, så vil dine opslag begynde at dukke op på deres programmer og hjemmesider også.", "help.next-generation": "Dette er den næste generation af sociale medier, bliv en del af det i dag!", - "onboard.title": "Dit vindue til fødiverset...", - "onboard.what": "Dette er din personaliserede kategori som består kun af indhold fra udefra dette forum. Om noget dukker op her eller ej afhænger af om følger personen der lavede indlægget, eller om du følger nogen der har fremhævet indlægget.", - "onboard.why": "Der foregår en masse udenfor dette forum, og ikke det hele er relevant for dine interesser. At følge folk er derfor den bedste måde at signalere at du gerne vil se mere fra dem.", - "onboard.how": "I mellemtiden kan du klikke på genvejs-knapperne i toppen for at se, hvad forummet kender til og begynd at opdage nyt indhold!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/de/world.json b/public/language/de/world.json index f450566099..f03c9cd773 100644 --- a/public/language/de/world.json +++ b/public/language/de/world.json @@ -18,10 +18,10 @@ "help.federating": "Wenn Leute außerhalb dieses Forums anfangen, dirzu folgen, werden deine Beiträge auch in diesen Apps und auf diesen Websites angezeigt.", "help.next-generation": "Das ist die nächste Generation der sozialen Medien, leg noch heute los!", - "onboard.title": "Dein Fenster zum Fediversum...", - "onboard.what": "Das ist deine persönliche Kategorie, die nur aus Inhalten außerhalb dieses Forums besteht. Ob etwas auf dieser Seite angezeigt wird, hängt davon ab, ob du diesen Inhalten folgst oder ob der Beitrag von jemandem geteilt wurde, dem du folgst.", - "onboard.why": "Außerhalb dieses Forums passiert echt viel, und nicht alles ist für dich interessant. Deshalb ist das Folgen von Leuten die beste Möglichkeit, um zu zeigen, dass du mehr von jemandem sehen willst.", - "onboard.how": "In der Zwischenzeit kannst du auf die Shortcut-Buttons oben klicken, um zu sehen, was es sonst noch in diesem Forum gibt, und neue Inhalte entdecken!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Such eine Kategorie...", "see-more": "Mehr sehen", diff --git a/public/language/el/world.json b/public/language/el/world.json index af21f98948..2057e13d00 100644 --- a/public/language/el/world.json +++ b/public/language/el/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/en-US/world.json b/public/language/en-US/world.json index af21f98948..2057e13d00 100644 --- a/public/language/en-US/world.json +++ b/public/language/en-US/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/en-x-pirate/world.json b/public/language/en-x-pirate/world.json index af21f98948..2057e13d00 100644 --- a/public/language/en-x-pirate/world.json +++ b/public/language/en-x-pirate/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/es/world.json b/public/language/es/world.json index af21f98948..2057e13d00 100644 --- a/public/language/es/world.json +++ b/public/language/es/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/et/world.json b/public/language/et/world.json index af21f98948..2057e13d00 100644 --- a/public/language/et/world.json +++ b/public/language/et/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/fa-IR/world.json b/public/language/fa-IR/world.json index af21f98948..2057e13d00 100644 --- a/public/language/fa-IR/world.json +++ b/public/language/fa-IR/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/fi/world.json b/public/language/fi/world.json index cbf5526761..d08b09de6f 100644 --- a/public/language/fi/world.json +++ b/public/language/fi/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/fr/world.json b/public/language/fr/world.json index e4975a78c3..d8467a263e 100644 --- a/public/language/fr/world.json +++ b/public/language/fr/world.json @@ -18,10 +18,10 @@ "help.federating": "De même, si des utilisateurs extérieurs à ce forum commencent à vous suivre, vos publications commenceront à apparaître sur ces applications et sites web également.", "help.next-generation": "C'est la prochaine génération des réseaux sociaux, commencez à contribuer dès aujourd'hui !", - "onboard.title": "Votre fenêtre sur le fediverse...", - "onboard.what": "Voici votre catégorie personnalisée, composée uniquement de contenu trouvé en dehors de ce forum. La présence de contenu sur cette page dépend de si vous suivez la personne, ou si ce post a été partagé par quelqu'un que vous suivez.", - "onboard.why": "Il se passe beaucoup de choses en dehors de ce forum, et tout n'est pas nécessairement pertinent pour vos intérêts. C'est pourquoi suivre des personnes est le meilleur moyen de signaler que vous souhaitez voir plus de contenu de leur part.", - "onboard.how": "En attendant, vous pouvez cliquer sur les boutons de raccourci en haut pour voir ce que ce forum connaît d'autre et commencer à découvrir de nouveaux contenus !", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Trouver une catégorie...", "see-more": "Voir plus", diff --git a/public/language/gl/world.json b/public/language/gl/world.json index af21f98948..2057e13d00 100644 --- a/public/language/gl/world.json +++ b/public/language/gl/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/he/world.json b/public/language/he/world.json index 1c7ba7a867..ace8b5cad4 100644 --- a/public/language/he/world.json +++ b/public/language/he/world.json @@ -18,10 +18,10 @@ "help.federating": "באופן דומה, אם משתמשים מחוץ לפורום זה מתחילים לעקוב אחריכם, אז הפוסטים שלכם יתחילו להופיע גם באפליקציות ובאתרים אלה.", "help.next-generation": "זהו הדור הבא של המדיה החברתית, התחלו לתרום עוד היום!", - "onboard.title": "החלון שלכם אל ה-fediverse...", - "onboard.what": "זוהי הקטגוריה המותאמת אישית שלכם המורכבת רק מתוכן שנמצא מחוץ לפורום זה. אם משהו מופיע בדף הזה תלוי אם אתם עוקבים אחריו, או אם הפוסט הזה שותף על ידי מישהו שאתם עוקבים אחריו.", - "onboard.why": "יש הרבה דברים שקורים מחוץ לפורום הזה, ולא כל זה רלוונטי לתחומי העניין שלכם. לכן מעקב אחר אנשים הוא הדרך הטובה ביותר לאותת שאתם רוצים לראות יותר ממישהו אחר.", - "onboard.how": "בינתיים, תוכלו ללחוץ על כפתורי הקיצור בחלק העליון כדי לראות על מה עוד הפורום הזה יודע, ולהתחיל לגלות תוכן חדש!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "מצא קטגוריה...", "see-more": "ראה עוד", diff --git a/public/language/hr/world.json b/public/language/hr/world.json index af21f98948..2057e13d00 100644 --- a/public/language/hr/world.json +++ b/public/language/hr/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/hu/world.json b/public/language/hu/world.json index af21f98948..2057e13d00 100644 --- a/public/language/hu/world.json +++ b/public/language/hu/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/hy/world.json b/public/language/hy/world.json index af21f98948..2057e13d00 100644 --- a/public/language/hy/world.json +++ b/public/language/hy/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/id/world.json b/public/language/id/world.json index af21f98948..2057e13d00 100644 --- a/public/language/id/world.json +++ b/public/language/id/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/it/world.json b/public/language/it/world.json index 103ab1daad..3af26b4563 100644 --- a/public/language/it/world.json +++ b/public/language/it/world.json @@ -18,10 +18,10 @@ "help.federating": "Allo stesso modo, se gli utenti esterni a questo forum iniziano a seguirti, i tuoi post inizieranno ad apparire anche su quelle app e quei siti web.", "help.next-generation": "Questa è la prossima generazione di social media, inizia a contribuire oggi!", - "onboard.title": "La tua finestra sul fediverso...", - "onboard.what": "Questa è la tua categoria personalizzata composta solo da contenuti trovati al di fuori di questo forum. La presenza di qualcosa in questa pagina dipende dal fatto che tu la segua o che il post sia stato condiviso da qualcuno che segui.", - "onboard.why": "Ci sono molte cose che accadono al di fuori di questo forum e non tutte sono rilevanti per i tuoi interessi. Ecco perché seguire le persone è il modo migliore per segnalare che vuoi vedere di più da qualcuno.", - "onboard.how": "Nel frattempo, puoi cliccare sui pulsanti di scelta rapida in alto per vedere cos'altro conosce questo forum e iniziare a scoprire nuovi contenuti!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Trova una categoria...", "see-more": "Vedi di più", diff --git a/public/language/ja/world.json b/public/language/ja/world.json index af21f98948..2057e13d00 100644 --- a/public/language/ja/world.json +++ b/public/language/ja/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/ko/world.json b/public/language/ko/world.json index af21f98948..2057e13d00 100644 --- a/public/language/ko/world.json +++ b/public/language/ko/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/lt/world.json b/public/language/lt/world.json index af21f98948..2057e13d00 100644 --- a/public/language/lt/world.json +++ b/public/language/lt/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/lv/world.json b/public/language/lv/world.json index af21f98948..2057e13d00 100644 --- a/public/language/lv/world.json +++ b/public/language/lv/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/ms/world.json b/public/language/ms/world.json index af21f98948..2057e13d00 100644 --- a/public/language/ms/world.json +++ b/public/language/ms/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/nb/world.json b/public/language/nb/world.json index d0c3e5e232..62c2f9ddde 100644 --- a/public/language/nb/world.json +++ b/public/language/nb/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "Dette er neste generasjon sosiale medier, begynn å bidra i dag!", - "onboard.title": "Ditt vindu til fødiverset...", - "onboard.what": "Dette er din personlige kategori, som kun består av innhold funnet utenfor dette forumet. Om noe vises på denne siden, avhenger av om du følger dem, eller om innlegget ble delt av noen du følger.", - "onboard.why": "Det skjer mye utenfor dette forumet, og ikke alt er relevant for dine interesser. Derfor er det å følge folk den beste måten å vise at du vil se mer fra noen.", - "onboard.how": "I mellomtiden kan du klikke på snarveisknappene øverst for å se hva annet dette forumet inneholder, og begynne å oppdage nytt innhold!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/nl/world.json b/public/language/nl/world.json index af21f98948..2057e13d00 100644 --- a/public/language/nl/world.json +++ b/public/language/nl/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/nn-NO/world.json b/public/language/nn-NO/world.json index 3a43c31a8b..f402724f70 100644 --- a/public/language/nn-NO/world.json +++ b/public/language/nn-NO/world.json @@ -18,10 +18,10 @@ "help.federating": "På same måte, dersom brukarar frå utsida av dette forumet begynner å følge deg, vil innlegga dine òg begynne å visast på desse appane og nettsidene.", "help.next-generation": "Dette er den neste generasjonen av sosiale medium, begynn å bidra i dag!", - "onboard.title": "Ditt vindauge til fødiverset...", - "onboard.what": "Dette er din personlege kategori som berre består av innhald funne utanfor dette forumet. Om noko blir vist på denne sida, avheng av om du følger dei, eller om innlegget blei delt av nokon du følger.", - "onboard.why": "Det skjer mykje utanfor dette forumet, og ikkje alt er relevant for interessene dine. Difor er det å følge folk den beste måten å signalisere at du ønskjer å sjå meir frå nokon.", - "onboard.how": "I mellomtida kan du klikke på snarvegsknappane øvst for å sjå kva anna dette forumet inneheld, og begynne å oppdage nytt innhald!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/pl/world.json b/public/language/pl/world.json index 1b37dc05eb..f2bfa77c65 100644 --- a/public/language/pl/world.json +++ b/public/language/pl/world.json @@ -18,10 +18,10 @@ "help.federating": "O ile użytkownicy spoza tego forum zaczną Cię śledzić, to w efekcie Twoje wpisy pojawią się na zewnętrznych stronach i w aplikacjach.", "help.next-generation": "To są media społecznościowe kolejnej generacji, dołącz już dzisiaj!", - "onboard.title": "Twoje okno na fediverse...", - "onboard.what": "Zawartość tej kategorii jest dostowana do Twoich poczynań i zawiera jedynie dane spoza tego forum. Aby się coś tu pojawiło trzeba zacząć śledzić zdalne źródła informacji.", - "onboard.why": "Mnóstwo rzeczy dzieje się poza tym forum ale niekoniecznie zgodnych z Twoimi zainteresowaniami. Dlatego śledzenie konkretnych użytkowników jest dobrą metodą aby uzyskać więcej zawartości przez nich dodawanych.", - "onboard.how": "W międzyczasie możesz użyć przycisków skrótów na górze aby przekonać się jak forum jest powiązane a okaże się, że zapewnia wiele dodatkowych treści!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Znajdź kategorię...", "see-more": "See more", diff --git a/public/language/pt-BR/world.json b/public/language/pt-BR/world.json index af21f98948..2057e13d00 100644 --- a/public/language/pt-BR/world.json +++ b/public/language/pt-BR/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/pt-PT/world.json b/public/language/pt-PT/world.json index af21f98948..2057e13d00 100644 --- a/public/language/pt-PT/world.json +++ b/public/language/pt-PT/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/ro/world.json b/public/language/ro/world.json index 7938c5ebeb..69f9b8e5d0 100644 --- a/public/language/ro/world.json +++ b/public/language/ro/world.json @@ -18,10 +18,10 @@ "help.federating": "De asemenea, dacă utilizatori din afara acestui forum încep să te urmărească, atunci postările tale vor începe să apară și pe acele aplicații și site-uri web.", "help.next-generation": "Aceasta este următoarea generație de social media, începe să contribui chiar azi!", - "onboard.title": "Fereastra ta către fedivers...", - "onboard.what": "Aceasta este categoria ta personalizată, formată doar din conținut găsit în afara acestui forum. Afișarea unui element pe această pagină depinde de dacă îl urmărești sau dacă postarea respectivă a fost distribuită de cineva pe care îl urmărești.", - "onboard.why": "Se întâmplă multe lucruri în afara acestui forum și nu toate sunt relevante pentru interesele tale. De aceea, urmărirea oamenilor este cea mai bună modalitate de a semnala că vrei să vezi mai multe de la cineva.", - "onboard.how": "Între timp, puteți da clic pe butoanele de comandă rapidă din partea de sus pentru a vedea ce mai știe acest forum și pentru a începe să descoperiți conținut nou!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/ru/world.json b/public/language/ru/world.json index e3b375a865..56688fcbda 100644 --- a/public/language/ru/world.json +++ b/public/language/ru/world.json @@ -18,10 +18,10 @@ "help.federating": "Аналогично, если пользователи за пределами этого форума начнут подписываться на вас, то ваши сообщения также начнут появляться в этих приложениях и на этих сайтах.", "help.next-generation": "Это новое поколение социальных сетей, начните вносить свой вклад уже сегодня!", - "onboard.title": "Ваше окно в мир fediverse...", - "onboard.what": "Это ваша персонализированная категория, состоящая только из контента, найденного за пределами этого форума. Появится ли что-то на этой странице, зависит от того, подписаны ли вы на них, или же этот пост был опубликован кем-то, на кого вы подписаны.", - "onboard.why": "За пределами этого форума происходит много всего, и не все из этого соответствует вашим интересам. Вот почему подписка на людей — лучший способ подать сигнал о том, что вы хотите видеть больше от кого-то.", - "onboard.how": "А пока вы можете нажать на кнопки быстрого доступа вверху, чтобы узнать, что еще известно на этом форуме, и начать открывать для себя новый контент!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/rw/world.json b/public/language/rw/world.json index af21f98948..2057e13d00 100644 --- a/public/language/rw/world.json +++ b/public/language/rw/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/sc/world.json b/public/language/sc/world.json index af21f98948..2057e13d00 100644 --- a/public/language/sc/world.json +++ b/public/language/sc/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/sk/world.json b/public/language/sk/world.json index af21f98948..2057e13d00 100644 --- a/public/language/sk/world.json +++ b/public/language/sk/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/sl/world.json b/public/language/sl/world.json index af21f98948..2057e13d00 100644 --- a/public/language/sl/world.json +++ b/public/language/sl/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/sq-AL/world.json b/public/language/sq-AL/world.json index af21f98948..2057e13d00 100644 --- a/public/language/sq-AL/world.json +++ b/public/language/sq-AL/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/sr/world.json b/public/language/sr/world.json index af21f98948..2057e13d00 100644 --- a/public/language/sr/world.json +++ b/public/language/sr/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/sv/world.json b/public/language/sv/world.json index af21f98948..2057e13d00 100644 --- a/public/language/sv/world.json +++ b/public/language/sv/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/th/world.json b/public/language/th/world.json index af21f98948..2057e13d00 100644 --- a/public/language/th/world.json +++ b/public/language/th/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/tr/world.json b/public/language/tr/world.json index af21f98948..2057e13d00 100644 --- a/public/language/tr/world.json +++ b/public/language/tr/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/uk/world.json b/public/language/uk/world.json index af21f98948..2057e13d00 100644 --- a/public/language/uk/world.json +++ b/public/language/uk/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/ur/world.json b/public/language/ur/world.json index 80727acc33..62b8f7d916 100644 --- a/public/language/ur/world.json +++ b/public/language/ur/world.json @@ -18,10 +18,10 @@ "help.federating": "اسی طرح، اگر اس فورم سے باہر کے صارفین آپ کو فالو کرنا شروع کر دیں، تو آپ کی پوسٹس ان کے ایپلیکیشنز اور ویب سائٹس پر ظاہر ہونا شروع ہو جائیں گی۔", "help.next-generation": "یہ سوشل نیٹ ورک کی نئی نسل ہے۔ آج ہی سے حصہ ڈالنا شروع کریں!", - "onboard.title": "فیڈی ورس کی طرف آپ کی کھڑکی…", - "onboard.what": "یہ آپ کی ذاتی نوعیت کی کیٹیگری ہے جو صرف اس فورم سے باہر کے مواد پر مشتمل ہے۔ یہاں وہ چیزیں ظاہر ہوتی ہیں جو آپ کے فالو کیے ہوئے لوگوں نے بنائیں یا شیئر کیں۔", - "onboard.why": "اس فورم سے باہر بہت کچھ ہو رہا ہے، اور ہر چیز آپ کے مفادات سے مطابقت نہیں رکھتی۔ اس لیے مخصوص لوگوں کو فالو کرنا یہ ظاہر کرنے کا بہترین طریقہ ہے کہ آپ ان سے مزید دیکھنا چاہتے ہیں۔", - "onboard.how": "اس دوران، آپ اس فورم کے قابل رسائی مواد کو دیکھنے کے لیے اوپر کے بٹن استعمال کر سکتے ہیں۔ اس طرح آپ نئے مواد کی دریافت شروع کر سکتے ہیں!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", diff --git a/public/language/vi/world.json b/public/language/vi/world.json index a883e532dd..77a23d73a5 100644 --- a/public/language/vi/world.json +++ b/public/language/vi/world.json @@ -18,10 +18,10 @@ "help.federating": "Tương tự như vậy, nếu người dùng bên ngoài diễn đàn này bắt đầu theo dõi bạn, thì bài đăng của bạn cũng sẽ bắt đầu xuất hiện trên các ứng dụng và trang web đó.", "help.next-generation": "Đây là thế hệ mạng xã hội kế tiếp, hãy bắt đầu đóng góp ngay hôm nay!", - "onboard.title": "Cửa sổ của bạn đến với liên đoàn...", - "onboard.what": "Đây là danh mục được cá nhân hóa của bạn chỉ bao gồm nội dung được tìm thấy bên ngoài diễn đàn này. Việc nội dung nào đó có hiển thị trên trang này hay không tùy thuộc vào việc bạn có theo dõi họ hay không hoặc liệu bài đăng đó có được chia sẻ bởi người mà bạn theo dõi hay không.", - "onboard.why": "Có rất nhiều điều diễn ra bên ngoài diễn đàn này và không phải tất cả đều phù hợp với sở thích của bạn. Đó là lý do tại sao theo dõi mọi người là cách tốt nhất để báo hiệu rằng bạn muốn biết thêm thông tin từ ai đó.", - "onboard.how": "Trong thời gian chờ đợi, bạn có thể nhấp vào các nút tắt ở trên cùng để xem diễn đàn này biết thêm những gì và bắt đầu khám phá một số nội dung mới!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Tìm danh mục...", "see-more": "Xem nhiều hơn", diff --git a/public/language/zh-CN/world.json b/public/language/zh-CN/world.json index e43b0fb8e7..4e2f056e85 100644 --- a/public/language/zh-CN/world.json +++ b/public/language/zh-CN/world.json @@ -18,10 +18,10 @@ "help.federating": "同样,如果本论坛以外的用户开始关注 ,那么您的帖子也会开始出现在这些应用程序和网站上。", "help.next-generation": "这是新一代的社交媒体,从今天开始,贡献力量吧!", - "onboard.title": "您通往联邦宇宙的窗口...", - "onboard.what": "这是您的个性化版块,只包含本论坛以外的内容。内容是否显示在本页取决于您是否关注他们,或者该帖子是否由您关注的人分享。", - "onboard.why": "论坛之外的事情很多,而且并非所有事情都与您的兴趣相关。因此,关注他人是表明您想从某人那里了解更多信息的最佳方式。", - "onboard.how": "在此期间,您可以点击顶部的快捷按钮,了解本论坛的其他内容,并开始发现一些新内容!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "查找版块...", "see-more": "显示更多", diff --git a/public/language/zh-TW/world.json b/public/language/zh-TW/world.json index af21f98948..2057e13d00 100644 --- a/public/language/zh-TW/world.json +++ b/public/language/zh-TW/world.json @@ -18,10 +18,10 @@ "help.federating": "Likewise, if users from outside of this forum start following you, then your posts will start appearing on those apps and websites as well.", "help.next-generation": "This is the next generation of social media, start contributing today!", - "onboard.title": "Your window to the fediverse...", - "onboard.what": "This is your personalized category made up of only content found outside of this forum. Whether something shows up in this page depends on whether you follow them, or whether that post was shared by someone you follow.", - "onboard.why": "There's a lot that goes on outside of this forum, and not all of it is relevant to your interests. That's why following people is the best way to signal that you want to see more from someone.", - "onboard.how": "In the meantime, you can click on the shortcut buttons at the top to see what else this forum knows about, and start discovering some new content!", + "onboard.title": "A world of content at your fingertips…", + "onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.", + "onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.", + "onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.", "category-search": "Find a category...", "see-more": "See more", From 2f5021e54730a835da442850357f4e6eb17479ed Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 16 Mar 2026 15:34:08 -0400 Subject: [PATCH 175/204] feat: add category selector to /world quick composer --- install/data/defaults.json | 3 ++- install/package.json | 4 ++-- .../en-GB/admin/settings/activitypub.json | 3 ++- public/src/modules/quickreply.js | 15 ++++++++++++++- src/controllers/activitypub/topics.js | 4 +++- src/views/admin/federation/content.tpl | 4 ++++ 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/install/data/defaults.json b/install/data/defaults.json index 2636fa875c..a6b25a2732 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -208,5 +208,6 @@ "activitypubUserPruneDays": 7, "activitypubFilter": 0, "activitypubSummaryLimit": 500, - "activitypubBreakString": "[...]" + "activitypubBreakString": "[...]", + "activitypubWorldDefaultCid": -1 } diff --git a/install/package.json b/install/package.json index 55d3247aba..ee1a0c15d6 100644 --- a/install/package.json +++ b/install/package.json @@ -108,10 +108,10 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.57", + "nodebb-theme-harmony": "2.2.58", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", - "nodebb-theme-persona": "14.2.31", + "nodebb-theme-persona": "14.2.32", "nodebb-widget-essentials": "7.0.43", "nodemailer": "8.0.2", "nprogress": "0.2.0", diff --git a/public/language/en-GB/admin/settings/activitypub.json b/public/language/en-GB/admin/settings/activitypub.json index 0486870db1..64508849aa 100644 --- a/public/language/en-GB/admin/settings/activitypub.json +++ b/public/language/en-GB/admin/settings/activitypub.json @@ -50,5 +50,6 @@ "content.summary-limit": "Character count after which a summary is generated", "content.summary-limit-help": "When content is federated out that exceeds this character count, a summary is generated, comprising of all complete sentences prior to this limit. (Default: 500)", "content.break-string": "Note/Article Delimiter", - "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])" + "content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the summary. If this string is not used, then the character count fallback applies. (Default: [...])", + "content.world-default-cid": "Default category ID for "World" page composer" } \ No newline at end of file diff --git a/public/src/modules/quickreply.js b/public/src/modules/quickreply.js index 9172ca3769..f081c59588 100644 --- a/public/src/modules/quickreply.js +++ b/public/src/modules/quickreply.js @@ -3,9 +3,11 @@ define('quickreply', [ 'components', 'autocomplete', 'api', 'alerts', 'uploadHelpers', 'mousetrap', 'storage', 'hooks', + 'categorySelector', ], function ( components, autocomplete, api, - alerts, uploadHelpers, mousetrap, storage, hooks + alerts, uploadHelpers, mousetrap, storage, hooks, + categorySelector, ) { const QuickReply = { _autocomplete: null, @@ -17,6 +19,17 @@ define('quickreply', [ return; } + if ($('[component="topic/quickreply/container"] [component="category-selector"]')) { + categorySelector.init($('[component="category-selector"]'), { + privilege: 'topics:create', + selectedCategory: ajaxify.data.selectedCategory, + onSelect: function (category) { + opts.body = opts.body || {}; + opts.body.cid = category.cid; + }, + }); + } + const qrDraftId = ajaxify.data.tid ? `qr:draft:tid:${ajaxify.data.tid}` : `qr:draft:cid:${opts?.body?.cid || -1}`; const data = { element: element, diff --git a/src/controllers/activitypub/topics.js b/src/controllers/activitypub/topics.js index 775d738305..26008e416c 100644 --- a/src/controllers/activitypub/topics.js +++ b/src/controllers/activitypub/topics.js @@ -26,10 +26,11 @@ controller.list = async function (req, res) { let start = Math.max(0, (page - 1) * topicsPerPage); let stop = start + topicsPerPage - 1; - const [userSettings, userPrivileges, isAdminOrGlobalMod] = await Promise.all([ + const [userSettings, userPrivileges, isAdminOrGlobalMod, selectedCategory] = await Promise.all([ user.getSettings(req.uid), privileges.categories.get('-1', req.uid), user.isAdminOrGlobalMod(req.uid), + categories.getCategoryData(meta.config.activitypubWorldDefaultCid), ]); const targetUid = await user.getUidByUserslug(req.query.author); let cidQuery = { @@ -48,6 +49,7 @@ controller.list = async function (req, res) { delete data.children; data.sort = req.query.sort; data.privileges = userPrivileges; + data.selectedCategory = selectedCategory; let tids; let topicCount; diff --git a/src/views/admin/federation/content.tpl b/src/views/admin/federation/content.tpl index 3ee43fcf21..be584f6de2 100644 --- a/src/views/admin/federation/content.tpl +++ b/src/views/admin/federation/content.tpl @@ -20,6 +20,10 @@ [[admin/settings/activitypub:content.break-string-help]]