Skip to content

Conversation

@Hendrik292
Copy link

This PR adds a "bulk accept suggestions" feature that allows users to accept all suggestions from a specific user with a single button click.

Features

  • Backend view (bulk_accept_user_suggestions) handling suggestion acceptance
  • Frontend button appearing next to each suggestion author
  • Shows progress and status ("X accepted", percentage, "Done")
  • Auto-reloads page after completion

Implementation

  • New view: weblate/trans/views/bulk_suggestions.py (91 lines)
  • New JavaScript: weblate/static/js/bulk-accept-suggestions.js (74 lines)
  • New CSS: weblate/static/styles/bulk-accept.css (56 lines)
  • Modified: weblate/urls.py (added route)
  • Modified: weblate/templates/snippets/suggestions.html (added button)

Related

Testing

  • Tested with multiple suggestions from different users
  • Permissions properly checked (requires suggestion.accept)
  • Works for all suggestions from a user across the translation
  • Error handling for failed accepts
  • Button appears only for authenticated users with proper permissions^
Screenshot_73

@Hendrik292 Hendrik292 requested a review from nijel as a code owner January 20, 2026 18:57
Copilot AI review requested due to automatic review settings January 20, 2026 18:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a bulk accept suggestions feature that allows users with appropriate permissions to accept all suggestions from a specific user with a single button click. The feature provides visual feedback showing progress, acceptance count, and percentage, then auto-reloads the page upon completion.

Changes:

  • New backend view for bulk accepting suggestions with permission checking and transaction support
  • Frontend button appearing next to each suggestion author with JavaScript handling for async operations
  • Custom CSS styling for the button and status indicators

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
weblate/trans/views/bulk_suggestions.py New view handling bulk suggestion acceptance with permission checks and error handling
weblate/urls.py Added URL routing for the bulk accept endpoint
weblate/templates/snippets/suggestions.html Added bulk accept button to suggestion display and loaded CSS/JS assets
weblate/static/js/bulk-accept-suggestions.js Client-side logic for AJAX request, progress display, and page reload
weblate/static/styles/bulk-accept.css Styling for the bulk accept button and status indicators

Comment on lines 1 to 130
# Copyright © 2026 Hendrik Leethaus <hendrik@leethaus.de>
#
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations

from typing import TYPE_CHECKING

from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext
from django.views.decorators.http import require_POST

from weblate.auth.models import User
from weblate.trans.models import Suggestion, Translation
from weblate.utils.state import STATE_TRANSLATED

if TYPE_CHECKING:
from weblate.auth.models import AuthenticatedHttpRequest


@require_POST
@login_required
@transaction.atomic
def bulk_accept_user_suggestions(
request: AuthenticatedHttpRequest, project: str, component: str, lang: str
):
"""Accept all suggestions from a specific user for a translation."""
# Get the translation object
translation = get_object_or_404(
Translation.objects.prefetch(),
component__project__slug=project,
component__slug=component,
language__code=lang,
)

# Check permission
if not request.user.has_perm("suggestion.accept", translation):
return JsonResponse(
{"error": gettext("You do not have privilege to accept suggestions!")},
status=403,
)

# Get target username from POST
target_username = request.POST.get("username")
if not target_username:
return JsonResponse({"error": "No username provided"}, status=400)

# Get the target user
try:
target_user = User.objects.get(username=target_username)
except User.DoesNotExist:
return JsonResponse({"error": "User not found"}, status=404)

# Get all suggestions from this user for this translation
suggestions = Suggestion.objects.filter(
unit__translation=translation, user=target_user
).select_related("unit")

total = suggestions.count()
accepted_count = 0
failed_count = 0

# Accept each suggestion
for suggestion in suggestions:
try:
# Check permission for each unit
if not request.user.has_perm("suggestion.accept", suggestion.unit):
failed_count += 1
continue

# Accept the suggestion (uses existing method)
suggestion.accept(request, state=STATE_TRANSLATED)
accepted_count += 1
except Exception:
failed_count += 1
continue

return JsonResponse(
{
"success": True,
"accepted": accepted_count,
"failed": failed_count,
"total": total,
"message": gettext(
"Accepted %(count)d of %(total)d suggestions from %(user)s"
)
% {
"count": accepted_count,
"total": total,
"user": target_username,
},
}
)
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

This PR adds a new user-facing feature (bulk accept suggestions) but does not include any documentation updates. According to the coding guidelines, user-visible changes should be documented with an entry in the changelog (docs/changes.rst) and potentially in the user documentation (e.g., docs/user/translating.rst which already documents suggestions). Users and administrators should be informed about this new capability.

Copilot uses AI. Check for mistakes.
Copy link
Member

@nijel nijel left a comment

Choose a reason for hiding this comment

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

I didn't look at the frontend code, @KarenKonou please comment on that.

@nijel nijel requested a review from KarenKonou January 20, 2026 19:21
@Hendrik292
Copy link
Author

I just noticed that an error message is now appearing after my fix. I'm working on getting it working again and meeting your requirements. (This is my first PR)

root added 2 commits January 21, 2026 00:45
- Use object_path URL pattern for categorized components
- Add BulkAcceptForm with UserField validation
- Remove exception swallowing in suggestion loop
- Use ngettext for proper pluralization
- Wrap all error messages with gettext
- Use CSS theme variables instead of hardcoded colors
- Add ARIA attributes for accessibility
- Update comment style to lowercase
- Remove German comment
- Add 'from None' to ValidationError
@Hendrik292 Hendrik292 force-pushed the feature/bulk-accept-suggestions branch from 7393aae to 2d6ffaa Compare January 20, 2026 23:57
@Hendrik292
Copy link
Author

Hi @nijel, I've spent the last few hours trying to fix all the mistakes to meet your requirements. Please let me know if I've forgotten anything or done anything wrong. (P.S. This is my first PR)

@devimarj
Copy link

I'm actually using a similar feature with conditions i/e not accepting multiple suggestions from a translation and so

image

@Hendrik292
Copy link
Author

Hi @devimarj,

Interesting that we both worked on this! Your addon approach with conditions looks useful for automated workflows.

My implementation is simpler, just a UI button for manual bulk accept. I oriented it on the bookmarklet/extension concept that was created for Anna's Archive.

Maybe both approaches could complement each other? Let's see what @nijel thinks about which direction fits better!

@nijel
Copy link
Member

nijel commented Jan 21, 2026

Having the button on the action bar, like shown on screenshots from @devimarj, seems to fit better into the existing user interface (even though the action bar has its problems, like #8259).

@Hendrik292
Copy link
Author

@nijel Thanks for the feedback! I'll work on moving the button to the action bar as you suggested.

@Hendrik292
Copy link
Author

Hendrik292 commented Jan 21, 2026

Hi @nijel
I've moved the bulk accept button to the action bar as requested.
The button is now positioned at the leftmost side, next to the other accept buttons.
Let me know if this looks good to you.

image

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

root and others added 3 commits January 22, 2026 00:10
- Remove nested @transaction.atomic decorator to avoid rollback issues
- Fix path type annotation: list[str] | tuple[str, ...] instead of str
- Add comprehensive unit tests covering all endpoints and edge cases
- Internationalize all JavaScript strings with gettext/ngettext
- Add null check for suggestion.user in template (anonymous users)
- Fix button rendering: use ifchanged instead of forloop.first
- Add type='button' attribute and translate button title
- Add try-except around suggestion.accept() for better error handling
@Hendrik292
Copy link
Author

Addressed all Copilot feedback. Added tests, fixed type annotations, i18n, and template logic. All checks passing.

@KarenKonou
Copy link
Contributor

Hello! I'm looking at the frontend code and will also do some local testing, will have a response soon

@Hendrik292
Copy link
Author

Hi, I had Copilot review the code in my repository again and it suggested a few things. Should I post the changes here or are you already satisfied?

- Replace deprecated sr-only class with visually-hidden (Bootstrap 5)
- Add account-check icon to improve UX distinction from accept-and-approve action
@Hendrik292
Copy link
Author

@KarenKonou @nijel Fixed

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.

4 participants