Skip to content

Commit 7213fe7

Browse files
committed
feat(add-ons): improved LINGUAS detection
Gracefully handle situation when POT file is placed differently than PO and LINGUAS files.
1 parent 58ca708 commit 7213fe7

3 files changed

Lines changed: 47 additions & 17 deletions

File tree

docs/changes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Weblate 2026.7
1010
* Management interface access control is now more fine-grained with dedicated site-wide permissions.
1111
* Default commit and merge request message templates now use Conventional Commits, and settings forms can restore installation defaults for individual message templates.
1212
* Documented :ref:`legal` customizations and added options to hide legal pages or disable document numbering.
13+
* :ref:`addon-weblate.gettext.linguas` better detects ``LINGUAS`` file presence.
1314

1415
.. rubric:: Bug fixes
1516

weblate/addons/gettext.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
)
6262
POT_BLANK_COPYRIGHT_RE = re.compile(r"^# Copyright \(C\)(?: [0-9– -]*)?$")
6363
if TYPE_CHECKING:
64-
from collections.abc import Callable, Generator
64+
from collections.abc import Callable, Generator, Iterator
6565

6666
from weblate.addons.base import CompatDict
6767
from weblate.formats.ttkit import PoFormat
@@ -139,22 +139,28 @@ class UpdateLinguasAddon(GettextBaseAddon):
139139
)
140140

141141
@staticmethod
142-
def get_linguas_path(component: Component) -> str:
143-
base = component.get_new_base_filename()
144-
if not base:
145-
base = os.path.join(
146-
component.full_path, component.filemask.replace("*", "x")
147-
)
148-
return os.path.join(os.path.dirname(base), "LINGUAS")
142+
def get_linguas_paths(component: Component) -> Iterator[str]:
143+
# First look at the POT file location
144+
try:
145+
base = component.get_new_base_filename()
146+
except ValidationError:
147+
base = None
148+
if base:
149+
yield os.path.join(os.path.dirname(base), "LINGUAS")
150+
# Fallback to PO file location
151+
base = os.path.join(component.full_path, component.filemask.replace("*", "x"))
152+
yield os.path.join(os.path.dirname(base), "LINGUAS")
149153

150154
@classmethod
151155
def get_validated_linguas_path(cls, component: Component) -> str | None:
152-
try:
153-
path = cls.get_linguas_path(component)
154-
component.check_file_is_valid(path)
155-
except ValidationError:
156-
return None
157-
return path
156+
for path in cls.get_linguas_paths(component):
157+
try:
158+
component.check_file_is_valid(path)
159+
except ValidationError:
160+
continue
161+
if os.path.exists(path):
162+
return path
163+
return None
158164

159165
@classmethod
160166
def can_install(
@@ -171,7 +177,7 @@ def can_install(
171177
if component is None:
172178
return True
173179
path = cls.get_validated_linguas_path(component)
174-
return path is not None and os.path.exists(path)
180+
return path is not None
175181

176182
@staticmethod
177183
def update_linguas(lines: list[str], codes: set[str]) -> tuple[bool, list[str]]:

weblate/addons/tests.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,24 @@ def test_update_linguas(self) -> None:
936936
self.assertIn("LINGUAS", commit)
937937
self.assertIn("\n+cs de it", commit)
938938

939+
def test_update_linguas_uses_po_path_when_new_base_elsewhere(self) -> None:
940+
translation = self.get_translation()
941+
pot_path = Path(self.component.full_path) / "pot" / "hello.pot"
942+
pot_path.parent.mkdir()
943+
source_path = cast("str", self.component.get_new_base_filename())
944+
shutil.copy(source_path, pot_path)
945+
self.component.new_base = "pot/hello.pot"
946+
self.component.save(update_fields=["new_base"])
947+
948+
self.assertTrue(UpdateLinguasAddon.can_install(component=self.component))
949+
addon = UpdateLinguasAddon.create(component=self.component)
950+
commit = self.component.repository.show(self.component.repository.last_revision)
951+
self.assertIn("po/LINGUAS", commit)
952+
self.assertIn("\n+cs\n", commit)
953+
954+
addon.post_add(translation)
955+
self.assertEqual(translation.addon_commit_files, [])
956+
939957
def test_update_linguas_rejects_symlink(self) -> None:
940958
translation = self.get_translation()
941959
addon = UpdateLinguasAddon.create(component=translation.component)
@@ -960,7 +978,7 @@ def test_update_linguas_rejects_symlink(self) -> None:
960978
Path(handle.name).read_text(encoding="utf-8"), "outside repository\n"
961979
)
962980

963-
def test_update_linguas_invalid_new_base_returns_false(self) -> None:
981+
def test_update_linguas_invalid_new_base_uses_po_path(self) -> None:
964982
translation = self.get_translation()
965983
addon = UpdateLinguasAddon.create(component=self.component)
966984

@@ -972,7 +990,12 @@ def test_update_linguas_invalid_new_base_returns_false(self) -> None:
972990
os.unlink(new_base_path)
973991
os.symlink(handle.name, new_base_path)
974992

975-
self.assertFalse(UpdateLinguasAddon.can_install(component=self.component))
993+
linguas_path = os.path.join(self.component.full_path, "po", "LINGUAS")
994+
self.assertEqual(
995+
UpdateLinguasAddon.get_validated_linguas_path(self.component),
996+
linguas_path,
997+
)
998+
self.assertTrue(UpdateLinguasAddon.can_install(component=self.component))
976999
addon.post_add(translation)
9771000
self.assertEqual(translation.addon_commit_files, [])
9781001

0 commit comments

Comments
 (0)