Skip to content

Simple comments feed#4499

Open
hlbmtc wants to merge 66 commits intomainfrom
feat/4497-simple-comments-feed
Open

Simple comments feed#4499
hlbmtc wants to merge 66 commits intomainfrom
feat/4497-simple-comments-feed

Conversation

@hlbmtc
Copy link
Contributor

@hlbmtc hlbmtc commented Mar 17, 2026

image

closes #4497

Summary by CodeRabbit

  • New Features

    • Dedicated Comments Feed UI with search, relevance sorting, time-window filters (all-time, past week, month, year), bot-exclusion toggle, and new feed navigation.
  • Refactor

    • Voting and “changed my mind” actions moved to service helpers for simpler view logic and consistent updates.
  • Chores

    • Backfilled denormalized comment fields and added time-window support on the backend.
  • Tests

    • Updated comment feed tests to reflect default public/non-deleted behavior.
  • Localization

    • Added translations for comments feed UI in multiple languages.

# Conflicts:
#	comments/migrations/0024_add_denormalized_fields.py
#	comments/models.py
#	comments/services/feed.py
#	comments/views/common.py
@hlbmtc hlbmtc marked this pull request as ready for review March 17, 2026 23:50
@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2026

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-4499-feat-4497-simple-comments-feed-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:feat-4497-simple-comments-feed-905eead
🗄️ PostgreSQL NeonDB branch preview/pr-4499-feat-4497-simple-comments-feed
Redis Fly Redis mtc-redis-pr-4499-feat-4497-simple-comments-feed

Details

  • Commit: 110030db0d0ebec1e48f4eeadcefaeed2d65bc73
  • Branch: feat/4497-simple-comments-feed
  • Fly App: metaculus-pr-4499-feat-4497-simple-comments-feed

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
comments/migrations/0024_add_denormalized_fields.py (1)

79-95: ⚠️ Potential issue | 🟠 Major

Indexes should be created after backfill operations to avoid expensive index maintenance during large UPDATEs.

Running backfill operations after creating indexes—particularly the GIN search index—forces the database to maintain those indexes during the UPDATE operations. Moving the backfill operations before index creation (lines 91–95 before lines 79–90) reduces lock contention and avoids redundant index updates on rows that will be bulk-modified.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comments/migrations/0024_add_denormalized_fields.py` around lines 79 - 95,
The migration creates indexes (migrations.AddIndex for comment_created_at_idx
and GinIndex comment_text_search_vector_idx) before running heavy backfills
(RunPython backfill_vote_score, backfill_cmm_count,
backfill_text_original_search_vector); reorder the operations so the three
RunPython backfill calls run first and the migrations.AddIndex calls (including
the GIN index using django.contrib.postgres.indexes.GinIndex) are applied
afterwards to avoid expensive index maintenance during the bulk UPDATEs.
🧹 Nitpick comments (2)
front_end/src/components/comment_feed/comment_card.tsx (2)

98-100: Remove commented-out code.

Line 99 contains commented-out code (//minHeight: ...). If this is no longer needed, it should be removed to keep the codebase clean. If it's intentionally left for future use, consider adding a comment explaining why.

🧹 Remove dead code
       style={{
         height: !isExpanded && needsExpand ? `${collapsedHeight}px` : "auto",
-        //minHeight: `${collapsedHeight}px`,
       }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/src/components/comment_feed/comment_card.tsx` around lines 98 -
100, Remove the commented-out dead code in the style object inside the
CommentCard component — specifically delete the "//minHeight:
`${collapsedHeight}px`," line near where height is set using isExpanded,
needsExpand and collapsedHeight; if you intended to preserve behavior or
rationale, replace the comment with a short explanatory comment referencing why
minHeight would be needed, otherwise simply remove the commented line to keep
comment_card.tsx clean.

39-39: Consider using consistent naming convention for constants.

The constant DEFAULT_collapsedHeight uses a mixed naming style. Consider using either DEFAULT_COLLAPSED_HEIGHT (UPPER_SNAKE_CASE for constants) or defaultCollapsedHeight (camelCase) for consistency with typical JavaScript conventions.

♻️ Suggested naming
-const DEFAULT_collapsedHeight = 174;
+const DEFAULT_COLLAPSED_HEIGHT = 174;

And update the usage on line 175:

-  collapsedHeight = DEFAULT_collapsedHeight,
+  collapsedHeight = DEFAULT_COLLAPSED_HEIGHT,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/src/components/comment_feed/comment_card.tsx` at line 39, Rename
the constant DEFAULT_collapsedHeight to follow a consistent naming convention
(e.g., DEFAULT_COLLAPSED_HEIGHT or defaultCollapsedHeight) and update all
references in comment_card.tsx (including usage in the CommentCard component and
the reference at line where it's used, e.g., the collapsed height prop/variable
on line ~175) so the identifier matches the new name throughout the file; ensure
imports/exports (if any) are also updated to the new constant name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@comments/migrations/0024_add_denormalized_fields.py`:
- Around line 6-10: The migration callback backfill functions (e.g.
backfill_vote_score and the two other functions invoked via
migrations.RunPython) are using the global django.db.connection; change them to
use the migration's active DB via schema_editor.connection (use the
schema_editor passed into each backfill function) so all cursor/connection
operations become "with schema_editor.connection.cursor() as cursor:" instead of
"with connection.cursor() as cursor:". Update every backfill function referenced
by the RunPython calls to use schema_editor.connection.

In `@comments/services/common.py`:
- Around line 270-303: Both vote_comment and toggle_cmm currently swallow
IntegrityError which hides failures; update them to validate inputs up-front
(e.g., ensure direction is one of the allowed values and enabled is a bool),
remove the silent "except IntegrityError: pass", and instead catch
IntegrityError as e and either log the full error (using logger.exception or
similar) and re-raise the exception so callers see the failure, or raise a clear
custom exception; also ensure toggle_cmm always returns an explicit True/False
on success (no None) and that any failure surfaces by raising the error so API
behavior is deterministic (refer to vote_comment, toggle_cmm,
CommentVote.objects.create, ChangedMyMindEntry.objects.create/delete, and
comment.update_vote_score/update_cmm_count to locate the mutation points).

In `@comments/views/common.py`:
- Around line 228-236: toggle_cmm() returns None for idempotent/no-op toggles so
the view should treat that as success instead of a 400 error; update the branch
that checks result from toggle_cmm to return Response(status=status.HTTP_200_OK)
when result is None (or otherwise return a 200 with a neutral message) and
remove the hardcoded "Already set as changed my mind" 400 path; keep the
existing error/400 behavior only for genuine failures reported differently by
toggle_cmm.

In `@front_end/messages/cs.json`:
- Around line 2075-2091: This block contains untranslated English strings (keys
like commentsFeed, commentsFeedTitle, sortRecent, sortMostUpvoted,
sortMostMindsChanged, sortRelevance, timeWindowAllTime, timeWindowPastWeek,
timeWindowPastMonth, timeWindowPastYear, searchComments, loadMoreComments,
noCommentsFound) and duplicate keys (includeBots and loadMoreComments already
defined earlier); replace each English value with the appropriate Czech
translation for those keys and remove the duplicate key entries so the file only
contains one definition per key (keep the original earlier definitions where
duplicates exist or consolidate into the correct Czech string), ensuring keys
such as commentsFeed, timeWindow, searchComments, noCommentsFound, and the
sort*/timeWindow* labels are localized.

In `@front_end/messages/en.json`:
- Around line 2080-2083: This file contains duplicate translation keys:
includeBots and loadMoreComments are already defined earlier, so the later
entries overwrite existing strings; fix by reusing the original keys or creating
unique, feed-scoped keys (e.g., feed.includeBots or feed.loadMoreComments) and
update any references accordingly; specifically locate the duplicate keys
"includeBots" and "loadMoreComments" in the diff and either remove the
duplicates, point them to the existing keys, or rename them and update the code
that references these keys so no unrelated UI copy is silently changed.

In `@front_end/messages/es.json`:
- Around line 2075-2091: Update the Spanish translations for the listed
localization keys by replacing the English values with the provided Spanish
equivalents: set "commentsFeed" -> "Comentarios", "commentsFeedTitle" -> "Feed
de Comentarios", "sortRecent" -> "Recientes", "sortMostUpvoted" -> "Más
Votados", "sortMostMindsChanged" -> "Más Mentes Cambiadas", "sortRelevance" ->
"Relevancia", "timeWindowAllTime" -> "Todo el Tiempo", "timeWindowPastWeek" ->
"Última Semana", "timeWindowPastMonth" -> "Último Mes", "timeWindowPastYear" ->
"Último Año", "searchComments" -> "Buscar comentarios...", "loadMoreComments" ->
"Cargar Más", and "noCommentsFound" -> "No se encontraron comentarios"; leave
"excludeBots" and "includeBots" unchanged.

In `@front_end/messages/pt.json`:
- Around line 2073-2089: The new comments-feed block contains duplicate keys and
untranslated English text—remove or replace the duplicated keys so the existing
Portuguese entries are not overridden: ensure "loadMoreComments" keeps the
earlier Portuguese value "Carregar mais comentários" (do not reintroduce the
English "Load More"), translate "includeBots" to Portuguese (matching "Excluir
Bots"/"Incluir Bots" style), and translate the timeWindow options
("timeWindowAllTime", "timeWindowPastWeek", "timeWindowPastMonth",
"timeWindowPastYear", "searchComments", "noCommentsFound", "commentsFeed",
"commentsFeedTitle", "sortRecent", "sortMostUpvoted", "sortMostMindsChanged",
"sortRelevance", "bots", "timeWindow", "excludeBots") into Portuguese or remove
duplicates so the file contains a single Portuguese version of each key.

In `@front_end/messages/zh-TW.json`:
- Around line 2072-2088: This block introduces untranslated English strings and
duplicate keys that override earlier entries; search the zh-TW file for the keys
includeBots and loadMoreComments (and any of the Comments Feed keys like
commentsFeed, commentsFeedTitle, sortRecent, sortMostUpvoted, timeWindowAllTime,
timeWindowPastWeek, timeWindowPastMonth, timeWindowPastYear, excludeBots,
searchComments, noCommentsFound), remove the duplicate definitions so each key
appears only once, replace the English literals with proper Traditional Chinese
translations for the Comments Feed texts, and if you need to keep different
variants merge them into a single canonical key (update only the existing key
occurrences rather than adding new ones).

In `@front_end/messages/zh.json`:
- Around line 2077-2093: This block introduces duplicate keys and leaves many
strings untranslated; remove the duplicate entries for includeBots and
loadMoreComments in this snippet (keep the earlier translations "包括機器人" and
"加載更多評論" defined elsewhere) and translate the remaining English keys to Chinese:
commentsFeed -> "評論", commentsFeedTitle -> "評論動態", sortRecent -> "最近",
sortMostUpvoted -> "最多讚", sortMostMindsChanged -> "最多改變觀點", sortRelevance ->
"相關性", timeWindowAllTime -> "所有時間", timeWindowPastWeek -> "過去一週",
timeWindowPastMonth -> "過去一個月", timeWindowPastYear -> "過去一年", searchComments ->
"搜索評論...", noCommentsFound -> "未找到評論". Ensure only one entry per i18n key
remains (remove the English duplicates) so the zh locale is consistent.

---

Outside diff comments:
In `@comments/migrations/0024_add_denormalized_fields.py`:
- Around line 79-95: The migration creates indexes (migrations.AddIndex for
comment_created_at_idx and GinIndex comment_text_search_vector_idx) before
running heavy backfills (RunPython backfill_vote_score, backfill_cmm_count,
backfill_text_original_search_vector); reorder the operations so the three
RunPython backfill calls run first and the migrations.AddIndex calls (including
the GIN index using django.contrib.postgres.indexes.GinIndex) are applied
afterwards to avoid expensive index maintenance during the bulk UPDATEs.

---

Nitpick comments:
In `@front_end/src/components/comment_feed/comment_card.tsx`:
- Around line 98-100: Remove the commented-out dead code in the style object
inside the CommentCard component — specifically delete the "//minHeight:
`${collapsedHeight}px`," line near where height is set using isExpanded,
needsExpand and collapsedHeight; if you intended to preserve behavior or
rationale, replace the comment with a short explanatory comment referencing why
minHeight would be needed, otherwise simply remove the commented line to keep
comment_card.tsx clean.
- Line 39: Rename the constant DEFAULT_collapsedHeight to follow a consistent
naming convention (e.g., DEFAULT_COLLAPSED_HEIGHT or defaultCollapsedHeight) and
update all references in comment_card.tsx (including usage in the CommentCard
component and the reference at line where it's used, e.g., the collapsed height
prop/variable on line ~175) so the identifier matches the new name throughout
the file; ensure imports/exports (if any) are also updated to the new constant
name.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: da90f0e2-7b41-4a15-b00b-88c22627d6bf

📥 Commits

Reviewing files that changed from the base of the PR and between 7004695 and 3965cec.

📒 Files selected for processing (23)
  • comments/constants.py
  • comments/migrations/0024_add_denormalized_fields.py
  • comments/models.py
  • comments/serializers/common.py
  • comments/services/common.py
  • comments/services/feed.py
  • comments/views/common.py
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/components/headers/hooks/useNavbarLinks.tsx
  • front_end/src/app/(main)/questions/hooks/use_feed.tsx
  • front_end/src/app/(main)/questions/page.tsx
  • front_end/src/components/comment_feed/comment_card.tsx
  • front_end/src/components/comment_feed/comment_feed_card.tsx
  • front_end/src/components/comment_feed/comment_feed_content.tsx
  • front_end/src/components/weekly_top_comments_feed/components/highlighted_comment_card.tsx
  • front_end/src/constants/posts_feed.ts
  • front_end/src/services/api/comments/comments.shared.ts
  • tests/unit/test_comments/test_views.py

Comment on lines +6 to +10
from django.db import connection, migrations, models


def backfill_vote_score(apps, schema_editor):
with connection.cursor() as cursor:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify migration callback DB access pattern.
rg -n "from django.db import connection|connection.cursor\\(|schema_editor.connection.cursor\\(" comments/migrations/0024_add_denormalized_fields.py

Repository: Metaculus/metaculus

Length of output: 246


🏁 Script executed:

cat -n comments/migrations/0024_add_denormalized_fields.py

Repository: Metaculus/metaculus

Length of output: 4348


Use schema_editor.connection instead of global django.db.connection in migration callbacks.

RunPython should use the migration's active DB alias; global connection can target the wrong DB in multi-database setups. This migration calls three backfill functions via migrations.RunPython() (lines 91–95), all of which use the incorrect pattern on lines 10, 25, and 40.

Suggested fix
-from django.db import connection, migrations, models
+from django.db import migrations, models
@@
 def backfill_vote_score(apps, schema_editor):
-    with connection.cursor() as cursor:
+    with schema_editor.connection.cursor() as cursor:
@@
 def backfill_cmm_count(apps, schema_editor):
-    with connection.cursor() as cursor:
+    with schema_editor.connection.cursor() as cursor:
@@
 def backfill_text_original_search_vector(apps, schema_editor):
-    with connection.cursor() as cursor:
+    with schema_editor.connection.cursor() as cursor:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comments/migrations/0024_add_denormalized_fields.py` around lines 6 - 10, The
migration callback backfill functions (e.g. backfill_vote_score and the two
other functions invoked via migrations.RunPython) are using the global
django.db.connection; change them to use the migration's active DB via
schema_editor.connection (use the schema_editor passed into each backfill
function) so all cursor/connection operations become "with
schema_editor.connection.cursor() as cursor:" instead of "with
connection.cursor() as cursor:". Update every backfill function referenced by
the RunPython calls to use schema_editor.connection.

Comment on lines +270 to +303
def vote_comment(comment: Comment, user: User, direction: int | None) -> int:
try:
with transaction.atomic():
CommentVote.objects.filter(user=user, comment=comment).delete()

if direction:
CommentVote.objects.create(
user=user, comment=comment, direction=direction
)
except IntegrityError:
pass

return comment.update_vote_score()


def toggle_cmm(comment: Comment, user: User, enabled: bool) -> bool | None:
"""Returns True if created, False if deleted, None if no-op."""
try:
with transaction.atomic():
cmm = ChangedMyMindEntry.objects.filter(user=user, comment=comment)

if not enabled and cmm.exists():
cmm.delete()
comment.update_cmm_count()
return False

if enabled and not cmm.exists():
ChangedMyMindEntry.objects.create(user=user, comment=comment)
comment.update_cmm_count()
return True
except IntegrityError:
pass


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not silently swallow IntegrityError in vote/CMM mutation paths.

Current except IntegrityError: pass hides failed writes and makes API outcomes ambiguous (None/stale score without surfacing an error).

Safer pattern: validate inputs + deterministic writes + explicit failure
 def vote_comment(comment: Comment, user: User, direction: int | None) -> int:
-    try:
-        with transaction.atomic():
-            CommentVote.objects.filter(user=user, comment=comment).delete()
-
-            if direction:
-                CommentVote.objects.create(
-                    user=user, comment=comment, direction=direction
-                )
-    except IntegrityError:
-        pass
+    if direction not in (None, -1, 1):
+        raise ValidationError("direction must be one of: -1, 1, null")
+
+    with transaction.atomic():
+        if direction is None:
+            CommentVote.objects.filter(user=user, comment=comment).delete()
+        else:
+            CommentVote.objects.update_or_create(
+                user=user,
+                comment=comment,
+                defaults={"direction": direction},
+            )

     return comment.update_vote_score()


 def toggle_cmm(comment: Comment, user: User, enabled: bool) -> bool | None:
     """Returns True if created, False if deleted, None if no-op."""
-    try:
-        with transaction.atomic():
-            cmm = ChangedMyMindEntry.objects.filter(user=user, comment=comment)
-
-            if not enabled and cmm.exists():
-                cmm.delete()
-                comment.update_cmm_count()
-                return False
-
-            if enabled and not cmm.exists():
-                ChangedMyMindEntry.objects.create(user=user, comment=comment)
-                comment.update_cmm_count()
-                return True
-    except IntegrityError:
-        pass
+    with transaction.atomic():
+        if enabled:
+            _, created = ChangedMyMindEntry.objects.get_or_create(
+                user=user, comment=comment
+            )
+            if created:
+                comment.update_cmm_count()
+                return True
+            return None
+
+        deleted, _ = ChangedMyMindEntry.objects.filter(
+            user=user, comment=comment
+        ).delete()
+        if deleted:
+            comment.update_cmm_count()
+            return False
+        return None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comments/services/common.py` around lines 270 - 303, Both vote_comment and
toggle_cmm currently swallow IntegrityError which hides failures; update them to
validate inputs up-front (e.g., ensure direction is one of the allowed values
and enabled is a bool), remove the silent "except IntegrityError: pass", and
instead catch IntegrityError as e and either log the full error (using
logger.exception or similar) and re-raise the exception so callers see the
failure, or raise a clear custom exception; also ensure toggle_cmm always
returns an explicit True/False on success (no None) and that any failure
surfaces by raising the error so API behavior is deterministic (refer to
vote_comment, toggle_cmm, CommentVote.objects.create,
ChangedMyMindEntry.objects.create/delete, and
comment.update_vote_score/update_cmm_count to locate the mutation points).

Comment on lines +228 to +236
result = toggle_cmm(comment, request.user, enabled)

if not cmm.exists():
ChangedMyMindEntry.objects.create(user=user, comment=comment)
comment.update_cmm_count()
return Response(status=status.HTTP_200_OK)
if result is None:
return Response(
{"error": "Already set as changed my mind"},
status=status.HTTP_400_BAD_REQUEST,
)

return Response(
{"error": "Already set as changed my mind"},
status=status.HTTP_400_BAD_REQUEST,
)
return Response(status=status.HTTP_200_OK)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep CMM toggles idempotent.

toggle_cmm() uses None for “no change”, so mapping that to a 400 makes duplicate clicks/retries fail even when the requested state is already applied. It also sends the wrong message when enabled=false. Treat no-ops as success, or have the service distinguish real errors from idempotent replays.

💡 Minimal fix
-    result = toggle_cmm(comment, request.user, enabled)
-
-    if result is None:
-        return Response(
-            {"error": "Already set as changed my mind"},
-            status=status.HTTP_400_BAD_REQUEST,
-        )
+    toggle_cmm(comment, request.user, enabled)
 
     return Response(status=status.HTTP_200_OK)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result = toggle_cmm(comment, request.user, enabled)
if not cmm.exists():
ChangedMyMindEntry.objects.create(user=user, comment=comment)
comment.update_cmm_count()
return Response(status=status.HTTP_200_OK)
if result is None:
return Response(
{"error": "Already set as changed my mind"},
status=status.HTTP_400_BAD_REQUEST,
)
return Response(
{"error": "Already set as changed my mind"},
status=status.HTTP_400_BAD_REQUEST,
)
return Response(status=status.HTTP_200_OK)
toggle_cmm(comment, request.user, enabled)
return Response(status=status.HTTP_200_OK)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comments/views/common.py` around lines 228 - 236, toggle_cmm() returns None
for idempotent/no-op toggles so the view should treat that as success instead of
a 400 error; update the branch that checks result from toggle_cmm to return
Response(status=status.HTTP_200_OK) when result is None (or otherwise return a
200 with a neutral message) and remove the hardcoded "Already set as changed my
mind" 400 path; keep the existing error/400 behavior only for genuine failures
reported differently by toggle_cmm.

Comment on lines +2075 to +2091
"commentsFeed": "Comments",
"commentsFeedTitle": "Comments Feed",
"sortRecent": "Recent",
"sortMostUpvoted": "Most Upvoted",
"sortMostMindsChanged": "Most Minds Changed",
"sortRelevance": "Relevance",
"timeWindow": "Časové období",
"bots": "Boti",
"timeWindowAllTime": "All Time",
"timeWindowPastWeek": "Past Week",
"timeWindowPastMonth": "Past Month",
"timeWindowPastYear": "Past Year",
"excludeBots": "Vyloučit boty",
"includeBots": "Zahrnout boty",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
"noCommentsFound": "No comments found"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Please localize these new Czech strings and remove duplicate key definitions.

commentsFeed*, sort labels, time-window labels, search/load/no-results are still English. Also includeBots (already at Line 390) and loadMoreComments (already at Line 583) are duplicated, which can silently override earlier values.

Suggested Czech values for this block
-  "commentsFeed": "Comments",
-  "commentsFeedTitle": "Comments Feed",
-  "sortRecent": "Recent",
-  "sortMostUpvoted": "Most Upvoted",
-  "sortMostMindsChanged": "Most Minds Changed",
+  "commentsFeed": "Komentáře",
+  "commentsFeedTitle": "Tok komentářů",
+  "sortRecent": "Nejnovější",
+  "sortMostUpvoted": "Nejvíce hlasů",
+  "sortMostMindsChanged": "Nejvíce změn názoru",
   "sortRelevance": "Relevance",
   "timeWindow": "Časové období",
   "bots": "Boti",
-  "timeWindowAllTime": "All Time",
-  "timeWindowPastWeek": "Past Week",
-  "timeWindowPastMonth": "Past Month",
-  "timeWindowPastYear": "Past Year",
+  "timeWindowAllTime": "Celé období",
+  "timeWindowPastWeek": "Poslední týden",
+  "timeWindowPastMonth": "Poslední měsíc",
+  "timeWindowPastYear": "Poslední rok",
   "excludeBots": "Vyloučit boty",
   "includeBots": "Zahrnout boty",
-  "searchComments": "Search comments...",
-  "loadMoreComments": "Load More",
-  "noCommentsFound": "No comments found"
+  "searchComments": "Hledat komentáře...",
+  "loadMoreComments": "Načíst další",
+  "noCommentsFound": "Žádné komentáře nenalezeny"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/cs.json` around lines 2075 - 2091, This block contains
untranslated English strings (keys like commentsFeed, commentsFeedTitle,
sortRecent, sortMostUpvoted, sortMostMindsChanged, sortRelevance,
timeWindowAllTime, timeWindowPastWeek, timeWindowPastMonth, timeWindowPastYear,
searchComments, loadMoreComments, noCommentsFound) and duplicate keys
(includeBots and loadMoreComments already defined earlier); replace each English
value with the appropriate Czech translation for those keys and remove the
duplicate key entries so the file only contains one definition per key (keep the
original earlier definitions where duplicates exist or consolidate into the
correct Czech string), ensuring keys such as commentsFeed, timeWindow,
searchComments, noCommentsFound, and the sort*/timeWindow* labels are localized.

Comment on lines +2080 to +2083
"excludeBots": "Exclude Bots",
"includeBots": "Include Bots",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid duplicate translation keys.

includeBots is already defined earlier in this file, and loadMoreComments already exists with different copy. The later entry silently wins, so this can change unrelated UI and make locale drift hard to spot. Please reuse the existing keys or give the feed-specific strings unique names.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/en.json` around lines 2080 - 2083, This file contains
duplicate translation keys: includeBots and loadMoreComments are already defined
earlier, so the later entries overwrite existing strings; fix by reusing the
original keys or creating unique, feed-scoped keys (e.g., feed.includeBots or
feed.loadMoreComments) and update any references accordingly; specifically
locate the duplicate keys "includeBots" and "loadMoreComments" in the diff and
either remove the duplicates, point them to the existing keys, or rename them
and update the code that references these keys so no unrelated UI copy is
silently changed.

Comment on lines +2075 to +2091
"commentsFeed": "Comments",
"commentsFeedTitle": "Comments Feed",
"sortRecent": "Recent",
"sortMostUpvoted": "Most Upvoted",
"sortMostMindsChanged": "Most Minds Changed",
"sortRelevance": "Relevance",
"timeWindow": "Período de tiempo",
"bots": "Bots",
"timeWindowAllTime": "All Time",
"timeWindowPastWeek": "Past Week",
"timeWindowPastMonth": "Past Month",
"timeWindowPastYear": "Past Year",
"excludeBots": "Excluir Bots",
"includeBots": "Incluir Bots",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
"noCommentsFound": "No comments found"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Several new keys are not translated to Spanish.

This is the Spanish localization file, but many of the new keys have English values instead of Spanish translations:

Key Current (English) Should be (Spanish)
commentsFeed "Comments" "Comentarios"
commentsFeedTitle "Comments Feed" "Feed de Comentarios"
sortRecent "Recent" "Recientes"
sortMostUpvoted "Most Upvoted" "Más Votados"
sortMostMindsChanged "Most Minds Changed" "Más Mentes Cambiadas"
sortRelevance "Relevance" "Relevancia"
timeWindowAllTime "All Time" "Todo el Tiempo"
timeWindowPastWeek "Past Week" "Última Semana"
timeWindowPastMonth "Past Month" "Último Mes"
timeWindowPastYear "Past Year" "Último Año"
searchComments "Search comments..." "Buscar comentarios..."
loadMoreComments "Load More" "Cargar Más"
noCommentsFound "No comments found" "No se encontraron comentarios"

Note: excludeBots and includeBots are correctly translated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/es.json` around lines 2075 - 2091, Update the Spanish
translations for the listed localization keys by replacing the English values
with the provided Spanish equivalents: set "commentsFeed" -> "Comentarios",
"commentsFeedTitle" -> "Feed de Comentarios", "sortRecent" -> "Recientes",
"sortMostUpvoted" -> "Más Votados", "sortMostMindsChanged" -> "Más Mentes
Cambiadas", "sortRelevance" -> "Relevancia", "timeWindowAllTime" -> "Todo el
Tiempo", "timeWindowPastWeek" -> "Última Semana", "timeWindowPastMonth" ->
"Último Mes", "timeWindowPastYear" -> "Último Año", "searchComments" -> "Buscar
comentarios...", "loadMoreComments" -> "Cargar Más", and "noCommentsFound" ->
"No se encontraron comentarios"; leave "excludeBots" and "includeBots"
unchanged.

Comment on lines +2073 to +2089
"commentsFeed": "Comments",
"commentsFeedTitle": "Comments Feed",
"sortRecent": "Recent",
"sortMostUpvoted": "Most Upvoted",
"sortMostMindsChanged": "Most Minds Changed",
"sortRelevance": "Relevance",
"timeWindow": "Período de tempo",
"bots": "Bots",
"timeWindowAllTime": "All Time",
"timeWindowPastWeek": "Past Week",
"timeWindowPastMonth": "Past Month",
"timeWindowPastYear": "Past Year",
"excludeBots": "Excluir Bots",
"includeBots": "Incluir Bots",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
"noCommentsFound": "No comments found"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import json
from pathlib import Path

path = Path("front_end/messages/pt.json")
dups = []

def hook(pairs):
    seen = {}
    for k, v in pairs:
        if k in seen:
            dups.append(k)
        seen[k] = v
    return dict(pairs)

json.loads(path.read_text(encoding="utf-8"), object_pairs_hook=hook)
print("Duplicate keys:", sorted(set(dups)))
PY

Repository: Metaculus/metaculus

Length of output: 114


🏁 Script executed:

# First, let's find where includeBots and loadMoreComments appear in the file
rg -n "includeBots|loadMoreComments" front_end/messages/pt.json

Repository: Metaculus/metaculus

Length of output: 660


🏁 Script executed:

# Also check the overall structure around the reported lines to understand context
sed -n '2050,2100p' front_end/messages/pt.json | cat -n

Repository: Metaculus/metaculus

Length of output: 2051


Fix duplicate keys and untranslated English strings in pt locale.

Line 2086 (includeBots) and line 2088 (loadMoreComments) redefine keys that already exist earlier in the file. At line 656, loadMoreComments has the proper Portuguese translation "Carregar mais comentários", but the duplicate at line 2088 overrides it with English "Load More", causing JSON to load the wrong language. The new comments-feed section (lines 2074–2090) is entirely in English while the rest of the file is Portuguese, creating a mixed-language UI.

Suggested patch
-  "commentsFeed": "Comments",
-  "commentsFeedTitle": "Comments Feed",
-  "sortRecent": "Recent",
-  "sortMostUpvoted": "Most Upvoted",
-  "sortMostMindsChanged": "Most Minds Changed",
-  "sortRelevance": "Relevance",
+  "commentsFeed": "Comentários",
+  "commentsFeedTitle": "Feed de comentários",
+  "sortRecent": "Recentes",
+  "sortMostUpvoted": "Mais votados",
+  "sortMostMindsChanged": "Mais mudanças de ideia",
+  "sortRelevance": "Relevância",
   "timeWindow": "Período de tempo",
   "bots": "Bots",
-  "timeWindowAllTime": "All Time",
-  "timeWindowPastWeek": "Past Week",
-  "timeWindowPastMonth": "Past Month",
-  "timeWindowPastYear": "Past Year",
+  "timeWindowAllTime": "Todo o período",
+  "timeWindowPastWeek": "Última semana",
+  "timeWindowPastMonth": "Último mês",
+  "timeWindowPastYear": "Último ano",
   "excludeBots": "Excluir Bots",
-  "includeBots": "Incluir Bots",
-  "searchComments": "Search comments...",
-  "loadMoreComments": "Load More",
-  "noCommentsFound": "No comments found"
+  "searchComments": "Pesquisar comentários...",
+  "noCommentsFound": "Nenhum comentário encontrado"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"commentsFeed": "Comments",
"commentsFeedTitle": "Comments Feed",
"sortRecent": "Recent",
"sortMostUpvoted": "Most Upvoted",
"sortMostMindsChanged": "Most Minds Changed",
"sortRelevance": "Relevance",
"timeWindow": "Período de tempo",
"bots": "Bots",
"timeWindowAllTime": "All Time",
"timeWindowPastWeek": "Past Week",
"timeWindowPastMonth": "Past Month",
"timeWindowPastYear": "Past Year",
"excludeBots": "Excluir Bots",
"includeBots": "Incluir Bots",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
"noCommentsFound": "No comments found"
"commentsFeed": "Comentários",
"commentsFeedTitle": "Feed de comentários",
"sortRecent": "Recentes",
"sortMostUpvoted": "Mais votados",
"sortMostMindsChanged": "Mais mudanças de ideia",
"sortRelevance": "Relevância",
"timeWindow": "Período de tempo",
"bots": "Bots",
"timeWindowAllTime": "Todo o período",
"timeWindowPastWeek": "Última semana",
"timeWindowPastMonth": "Último mês",
"timeWindowPastYear": "Último ano",
"excludeBots": "Excluir Bots",
"searchComments": "Pesquisar comentários...",
"noCommentsFound": "Nenhum comentário encontrado"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/pt.json` around lines 2073 - 2089, The new comments-feed
block contains duplicate keys and untranslated English text—remove or replace
the duplicated keys so the existing Portuguese entries are not overridden:
ensure "loadMoreComments" keeps the earlier Portuguese value "Carregar mais
comentários" (do not reintroduce the English "Load More"), translate
"includeBots" to Portuguese (matching "Excluir Bots"/"Incluir Bots" style), and
translate the timeWindow options ("timeWindowAllTime", "timeWindowPastWeek",
"timeWindowPastMonth", "timeWindowPastYear", "searchComments",
"noCommentsFound", "commentsFeed", "commentsFeedTitle", "sortRecent",
"sortMostUpvoted", "sortMostMindsChanged", "sortRelevance", "bots",
"timeWindow", "excludeBots") into Portuguese or remove duplicates so the file
contains a single Portuguese version of each key.

Comment on lines +2072 to +2088
"commentsFeed": "Comments",
"commentsFeedTitle": "Comments Feed",
"sortRecent": "Recent",
"sortMostUpvoted": "Most Upvoted",
"sortMostMindsChanged": "Most Minds Changed",
"sortRelevance": "Relevance",
"timeWindow": "時間範圍",
"bots": "機器人",
"timeWindowAllTime": "All Time",
"timeWindowPastWeek": "Past Week",
"timeWindowPastMonth": "Past Month",
"timeWindowPastYear": "Past Year",
"excludeBots": "排除機器人",
"includeBots": "包含機器人",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
"noCommentsFound": "No comments found"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid duplicate keys and untranslated literals in the zh-TW locale block.

This block adds English text to a Traditional Chinese file, and also redefines existing keys: includeBots (already at Line 450) and loadMoreComments (already at Line 705). Duplicate keys can silently override prior values and cause regressions outside Comments Feed.

Suggested patch for this block (and keep only one definition per key in the file)
-  "commentsFeed": "Comments",
-  "commentsFeedTitle": "Comments Feed",
-  "sortRecent": "Recent",
-  "sortMostUpvoted": "Most Upvoted",
-  "sortMostMindsChanged": "Most Minds Changed",
-  "sortRelevance": "Relevance",
+  "commentsFeed": "評論",
+  "commentsFeedTitle": "評論動態",
+  "sortRecent": "最新",
+  "sortMostUpvoted": "最多讚",
+  "sortMostMindsChanged": "最多改變想法",
+  "sortRelevance": "相關性",
   "timeWindow": "時間範圍",
   "bots": "機器人",
-  "timeWindowAllTime": "All Time",
-  "timeWindowPastWeek": "Past Week",
-  "timeWindowPastMonth": "Past Month",
-  "timeWindowPastYear": "Past Year",
+  "timeWindowAllTime": "所有時間",
+  "timeWindowPastWeek": "過去一週",
+  "timeWindowPastMonth": "過去一個月",
+  "timeWindowPastYear": "過去一年",
   "excludeBots": "排除機器人",
   "includeBots": "包含機器人",
-  "searchComments": "Search comments...",
-  "loadMoreComments": "Load More",
-  "noCommentsFound": "No comments found"
+  "searchComments": "搜尋評論...",
+  "loadMoreComments": "載入更多評論",
+  "noCommentsFound": "找不到評論"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/zh-TW.json` around lines 2072 - 2088, This block
introduces untranslated English strings and duplicate keys that override earlier
entries; search the zh-TW file for the keys includeBots and loadMoreComments
(and any of the Comments Feed keys like commentsFeed, commentsFeedTitle,
sortRecent, sortMostUpvoted, timeWindowAllTime, timeWindowPastWeek,
timeWindowPastMonth, timeWindowPastYear, excludeBots, searchComments,
noCommentsFound), remove the duplicate definitions so each key appears only
once, replace the English literals with proper Traditional Chinese translations
for the Comments Feed texts, and if you need to keep different variants merge
them into a single canonical key (update only the existing key occurrences
rather than adding new ones).

Comment on lines +2077 to +2093
"commentsFeed": "Comments",
"commentsFeedTitle": "Comments Feed",
"sortRecent": "Recent",
"sortMostUpvoted": "Most Upvoted",
"sortMostMindsChanged": "Most Minds Changed",
"sortRelevance": "Relevance",
"timeWindow": "时间范围",
"bots": "机器人",
"timeWindowAllTime": "All Time",
"timeWindowPastWeek": "Past Week",
"timeWindowPastMonth": "Past Month",
"timeWindowPastYear": "Past Year",
"excludeBots": "排除机器人",
"includeBots": "包含机器人",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
"noCommentsFound": "No comments found"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import json
from pathlib import Path

path = Path("front_end/messages/zh.json")
dups = []

def hook(pairs):
    seen = {}
    for k, v in pairs:
        if k in seen:
            dups.append(k)
        seen[k] = v
    return dict(pairs)

json.loads(path.read_text(encoding="utf-8"), object_pairs_hook=hook)
print("Duplicate keys:", sorted(set(dups)))
PY

Repository: Metaculus/metaculus

Length of output: 114


🏁 Script executed:

#!/bin/bash
# First, let's see the file size and examine the structure
wc -l front_end/messages/zh.json

# Find all occurrences of the duplicate keys
echo "=== Searching for 'includeBots' ==="
rg -n "includeBots" front_end/messages/zh.json

echo "=== Searching for 'loadMoreComments' ==="
rg -n "loadMoreComments" front_end/messages/zh.json

# Extract the context around lines 2077-2093
echo "=== Lines 2077-2093 context ==="
sed -n '2077,2093p' front_end/messages/zh.json

Repository: Metaculus/metaculus

Length of output: 1137


Remove duplicate i18n keys and translate remaining English strings in the zh locale.

The file contains duplicate keys at lines 2090 (includeBots) and 2092 (loadMoreComments) that override earlier entries:

  • Line 395 has "includeBots": "包括機器人" but line 2090 overrides it with "包含机器人"
  • Line 581 has "loadMoreComments": "加載更多評論" but line 2092 overrides it with English "Load More"

Additionally, the following keys in lines 2077-2089 remain untranslated in English: commentsFeed, commentsFeedTitle, sortRecent, sortMostUpvoted, sortMostMindsChanged, sortRelevance, timeWindowAllTime, timeWindowPastWeek, timeWindowPastMonth, timeWindowPastYear, searchComments, and noCommentsFound. This creates mixed-language UI in the Chinese locale.

Suggested fix
-  "commentsFeed": "Comments",
-  "commentsFeedTitle": "Comments Feed",
-  "sortRecent": "Recent",
-  "sortMostUpvoted": "Most Upvoted",
-  "sortMostMindsChanged": "Most Minds Changed",
-  "sortRelevance": "Relevance",
+  "commentsFeed": "评论",
+  "commentsFeedTitle": "评论动态",
+  "sortRecent": "最新",
+  "sortMostUpvoted": "最多赞同",
+  "sortMostMindsChanged": "最多改主意",
+  "sortRelevance": "相关性",
   "timeWindow": "时间范围",
   "bots": "机器人",
-  "timeWindowAllTime": "All Time",
-  "timeWindowPastWeek": "Past Week",
-  "timeWindowPastMonth": "Past Month",
-  "timeWindowPastYear": "Past Year",
+  "timeWindowAllTime": "全部时间",
+  "timeWindowPastWeek": "过去一周",
+  "timeWindowPastMonth": "过去一个月",
+  "timeWindowPastYear": "过去一年",
   "excludeBots": "排除机器人",
-  "includeBots": "包含机器人",
-  "searchComments": "Search comments...",
-  "loadMoreComments": "Load More",
-  "noCommentsFound": "No comments found"
+  "searchComments": "搜索评论...",
+  "noCommentsFound": "未找到评论"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"commentsFeed": "Comments",
"commentsFeedTitle": "Comments Feed",
"sortRecent": "Recent",
"sortMostUpvoted": "Most Upvoted",
"sortMostMindsChanged": "Most Minds Changed",
"sortRelevance": "Relevance",
"timeWindow": "时间范围",
"bots": "机器人",
"timeWindowAllTime": "All Time",
"timeWindowPastWeek": "Past Week",
"timeWindowPastMonth": "Past Month",
"timeWindowPastYear": "Past Year",
"excludeBots": "排除机器人",
"includeBots": "包含机器人",
"searchComments": "Search comments...",
"loadMoreComments": "Load More",
"noCommentsFound": "No comments found"
"commentsFeed": "评论",
"commentsFeedTitle": "评论动态",
"sortRecent": "最新",
"sortMostUpvoted": "最多赞同",
"sortMostMindsChanged": "最多改主意",
"sortRelevance": "相关性",
"timeWindow": "时间范围",
"bots": "机器人",
"timeWindowAllTime": "全部时间",
"timeWindowPastWeek": "过去一周",
"timeWindowPastMonth": "过去一个月",
"timeWindowPastYear": "过去一年",
"excludeBots": "排除机器人",
"searchComments": "搜索评论...",
"noCommentsFound": "未找到评论"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/zh.json` around lines 2077 - 2093, This block introduces
duplicate keys and leaves many strings untranslated; remove the duplicate
entries for includeBots and loadMoreComments in this snippet (keep the earlier
translations "包括機器人" and "加載更多評論" defined elsewhere) and translate the remaining
English keys to Chinese: commentsFeed -> "評論", commentsFeedTitle -> "評論動態",
sortRecent -> "最近", sortMostUpvoted -> "最多讚", sortMostMindsChanged -> "最多改變觀點",
sortRelevance -> "相關性", timeWindowAllTime -> "所有時間", timeWindowPastWeek ->
"過去一週", timeWindowPastMonth -> "過去一個月", timeWindowPastYear -> "過去一年",
searchComments -> "搜索評論...", noCommentsFound -> "未找到評論". Ensure only one entry
per i18n key remains (remove the English duplicates) so the zh locale is
consistent.

Copy link
Contributor

@ncarazon ncarazon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, don't see other problems besides what's been highlighted by coderabbit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Simple comment feed

2 participants