Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for translatable/readonly for uploaded/downloaded Android Resource Format #12697

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Not yet released.
* :kbd:`?` now displays available :ref:`keyboard`.
* Translation and language view in the project now include basic information about the language and plurals.
* :ref:`bulk-edit` shows a preview of matched strings.
* :ref:`aresource` now supports translatable attribute in its strings.
* Creating component via file upload (Translate document) now supports bilingual files.

**Bug fixes**
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ dependencies = [
"social-auth-app-django>=5.4.1,<6.0.0",
"social-auth-core>=4.5.0,<5.0.0",
"tesserocr>=2.6.1,<2.8.0",
"translate-toolkit>=3.13.1,<3.14",
"translate-toolkit>=3.13.4,<3.14",
"translation-finder>=2.16,<3.0",
"user-agents>=2.0,<2.3",
"weblate-language-data>=2024.6",
Expand Down
13 changes: 12 additions & 1 deletion weblate/formats/ttkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
STATE_APPROVED,
STATE_EMPTY,
STATE_FUZZY,
STATE_READONLY,
STATE_TRANSLATED,
)

Expand Down Expand Up @@ -543,7 +544,7 @@
raise ValueError(
f"Could not parse flags: {self.mainunit.typecomments!r}: {error}"
) from error
flags.remove({"fuzzy"})

Check failure on line 547 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

Argument 1 to "remove" of "Flags" has incompatible type "set[str]"; expected "str | _Element | Flags | tuple[str | tuple[Any, ...]] | None"
return flags.format()

@cached_property
Expand Down Expand Up @@ -1080,6 +1081,16 @@
return False


class AndroidUnit(MonolingualIDUnit):
"""Wrapper unit for Android Resource."""

def set_state(self, state) -> None:
"""Tag unit as translatable/readonly aside from fuzzy and approved flags."""
super().set_state(state)
if state == STATE_READONLY:
self.unit.marktranslatable(False)


class BasePoFormat(TTKitFormat):
loader = pofile
plural_preference = None
Expand Down Expand Up @@ -1139,7 +1150,7 @@
def add_unit(self, unit: TranslationUnit) -> None:
self.store.require_index()
# Check if there is matching obsolete unit
old_unit = self.store.id_index.get(unit.unit.getid())

Check failure on line 1153 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

Item "BaseItem" of "Any | BaseItem" has no attribute "getid"
if old_unit and old_unit.isobsolete():
self.store.removeunit(old_unit)
super().add_unit(unit)
Expand Down Expand Up @@ -1433,7 +1444,7 @@
format_id = "aresource"
loader = ("aresource", "AndroidResourceFile")
monolingual = True
unit_class = MonolingualIDUnit
unit_class = AndroidUnit
new_translation = '<?xml version="1.0" encoding="utf-8"?>\n<resources></resources>'
autoload: tuple[str, ...] = ("strings*.xml", "values*.xml")
language_format = "android"
Expand Down Expand Up @@ -1681,7 +1692,7 @@
"""Return most common file extension for format."""
return "csv"

def parse_store(self, storefile):

Check failure on line 1695 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

Signature of "parse_store" incompatible with supertype "CSVFormat"
"""Parse the store."""
content, filename = self.get_content_and_filename(storefile)

Expand Down Expand Up @@ -1986,7 +1997,7 @@

def save_content(self, handle) -> None:
if self.store.root is None:
self.store.root = self.template_store.store.root

Check failure on line 2000 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

Item "None" of "TranslationFormat | None" has no attribute "store"

Check failure on line 2000 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

Item "BaseStore" of "Any | BaseStore" has no attribute "root"
super().save_content(handle)


Expand Down
1 change: 1 addition & 0 deletions weblate/trans/models/translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.stats = TranslationStats(self)
self.addon_commit_files = []

Check failure on line 180 in weblate/trans/models/translation.py

View workflow job for this annotation

GitHub Actions / mypy

Need type annotation for "addon_commit_files" (hint: "addon_commit_files: list[<type>] = ...")
self.reason = ""
self._invalidate_scheduled = False
self.update_changes = []

Check failure on line 183 in weblate/trans/models/translation.py

View workflow job for this annotation

GitHub Actions / mypy

Need type annotation for "update_changes" (hint: "update_changes: list[<type>] = ...")
# Project backup integration
self.original_id = -1

Expand Down Expand Up @@ -321,9 +321,9 @@
except KeyError:
newunit = Unit(translation=self, id_hash=id_hash, state=-1)
# Avoid fetching empty list of checks from the database
newunit.all_checks = []

Check failure on line 324 in weblate/trans/models/translation.py

View workflow job for this annotation

GitHub Actions / mypy

Incompatible types in assignment (expression has type "list[Never]", variable has type "QuerySet[Check, Check]")
# Avoid fetching empty list of variants
newunit._prefetched_objects_cache = { # noqa: SLF001

Check failure on line 326 in weblate/trans/models/translation.py

View workflow job for this annotation

GitHub Actions / mypy

"Unit" has no attribute "_prefetched_objects_cache"
"defined_variants": Variant.objects.none()
}
is_new = True
Expand Down Expand Up @@ -384,7 +384,7 @@
self.component.check_template_valid()

# List of updated units (used for cleanup and duplicates detection)
updated = {}

Check failure on line 387 in weblate/trans/models/translation.py

View workflow job for this annotation

GitHub Actions / mypy

Need type annotation for "updated" (hint: "updated: dict[<type>, <type>] = ...")

try:
store = self.store
Expand Down Expand Up @@ -1213,6 +1213,7 @@
split_plural(unit.source),
split_plural(unit.target) if not self.is_source else [],
is_batch_update=True,
state=STATE_READONLY if unit.is_readonly() else None,
)
existing.add(idkey)
accepted += 1
Expand Down
6 changes: 6 additions & 0 deletions weblate/trans/tests/data/strings-with-readonly.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="string_one">String One</string>
<string name="string_two" translatable="false">String Two</string>
<string name="string_three">String Three</string>
</resources>
39 changes: 39 additions & 0 deletions weblate/trans/tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from weblate.trans.models import Change, ComponentList
from weblate.trans.tests.test_views import ViewTestCase
from weblate.trans.tests.utils import get_test_file
from weblate.utils.state import STATE_READONLY

TEST_PO = get_test_file("cs.po")
TEST_CSV = get_test_file("cs.csv")
Expand All @@ -29,6 +30,7 @@
TEST_MO = get_test_file("cs.mo")
TEST_XLIFF = get_test_file("cs.poxliff")
TEST_ANDROID = get_test_file("strings-cs.xml")
TEST_ANDROID_READONLY = get_test_file("strings-with-readonly.xml")
TEST_XLSX = get_test_file("cs.xlsx")
TEST_TBX = get_test_file("terms.tbx")

Expand Down Expand Up @@ -380,6 +382,43 @@ def test_replace(self) -> None:
translation.change_set.filter(action=Change.ACTION_REPLACE_UPLOAD).exists()
)

def test_readonly_upload_download(self) -> None:
"""Test upload and download with a file containing a non-translatable string."""
project = self.component.project
component = self.create_android(name="Component", project=project)
self.user.is_superuser = True
self.user.save()
with open(TEST_ANDROID_READONLY, "rb") as handle:
response = self.client.post(
reverse(
"upload",
kwargs={"path": component.source_translation.get_url_path()},
),
{
"file": handle,
"method": "replace",
"author_name": self.user.full_name,
"author_email": self.user.email,
},
follow=True,
)
messages = list(response.context["messages"])
self.assertIn("updated: 3", messages[0].message)
unit = component.source_translation.unit_set.get(context="string_two")
self.assertEqual(unit.state, STATE_READONLY)

response = self.client.get(
reverse(
"download",
kwargs={"path": component.source_translation.get_url_path()},
),
follow=True,
)
self.assertIn(
'name="string_two" translatable="false"',
response.getvalue().decode("utf-8"),
)


class CSVImportTest(ViewTestCase):
test_file = TEST_CSV
Expand Down
Loading