Repository: WeblateOrg/weblate

Issue:
Unclear error message on replacing source language's translation file through API
### Describe the issue

Using Weblate v4.17, I am trying to fully replace the translation file for the source language of my translation component through Weblate's API. This works fine for other languages, but errors out for the source language with the error message "You can not overwrite existing approved translations".

I'm confused as to why this is the case through the API, since uploading a file through the web portal with "Replace existing translation file" works without a hitch.

For reference, here is a code snippet of the request:

```
axios({
    url: `${process.env.REACT_APP_WEBLATE_URL}api/translations/${process.env.REACT_APP_WEBLATE_PATH}${language}/file/`,
    method: "POST",
    data: {
      file: fs.createReadStream(filePath),
      email: "my@email.com",
      author: "Author name",
      conflicts: "replace-approved",
      method: "replace",
    },
    headers: {
      "Content-Type": "multipart/form-data",
      Authorization: `Token ${process.env.REACT_APP_WEBLATE_TOKEN}`,
    },
  })
```

I have searched extensively on the docs, google and the issues on GitHub, but found no mentions about this issue.

### I already tried

- [X] I've read and searched [the documentation](https://docs.weblate.org/).
- [X] I've searched for similar filed issues in this repository.

### Steps to reproduce the behavior

1. Add a new translation component
2. Make a POST request to `/api/translations/(string: project)/(string: component)/(string: language)/file/` for the source language
3. Error - "You can not overwrite existing approved translations"

### Expected behavior

Translation file successfully replace

### Screenshots

_No response_

### Exception traceback

_No response_

### How do you run Weblate?

Other

### Weblate versions

_No response_

### Weblate deploy checks

_No response_

### Additional context

_No response_


Discussion:
Most likely source strings review are not enabled and thus approved state is not applicable. The error message could be definitely more clear in this case.

Resolving patch:
diff --git a/weblate/api/serializers.py b/weblate/api/serializers.py
--- a/weblate/api/serializers.py
+++ b/weblate/api/serializers.py
@@ -869,12 +869,10 @@ def check_perms(self, user, obj):
             raise serializers.ValidationError(
                 {"conflicts": "You can not overwrite existing translations."}
             )
-        if data["conflicts"] == "replace-approved" and not user.has_perm(
-            "unit.review", obj
+        if data["conflicts"] == "replace-approved" and not (
+            denied := user.has_perm("unit.review", obj)
         ):
-            raise serializers.ValidationError(
-                {"conflicts": "You can not overwrite existing approved translations."}
-            )
+            raise serializers.ValidationError({"conflicts": denied.reason})
 
         if data["method"] == "source" and not obj.is_source:
             raise serializers.ValidationError(
diff --git a/weblate/auth/permissions.py b/weblate/auth/permissions.py
--- a/weblate/auth/permissions.py
+++ b/weblate/auth/permissions.py
@@ -120,7 +120,7 @@ def check_ignore_check(user, permission, check):
     return check_permission(user, permission, check.unit.translation)
 
 
-def check_can_edit(user, permission, obj, is_vote=False):
+def check_can_edit(user, permission, obj, is_vote=False):  # noqa: C901
     translation = component = None
 
     if isinstance(obj, Translation):
@@ -165,6 +165,10 @@ def check_can_edit(user, permission, obj, is_vote=False):
         if not user.is_authenticated:
             # Signing in might help, but user still might need additional privileges
             return Denied(gettext("Sign in to save the translation."))
+        if permission == "unit.review":
+            return Denied(
+                gettext("Insufficient privileges for approving translations.")
+            )
         return Denied(gettext("Insufficient privileges for saving translations."))
 
     # Special check for source strings (templates)


Test patch: