Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0564fb1
Add ObjCFormatCheck for Objective-C format strings
anshkr95 Mar 23, 2026
2894743
Update and rename weblate/checks/models.py to weblate/checks/weblate/…
anshkr95 Mar 23, 2026
7252ae1
Add ChecksLoader and WeblateChecksConf classes
anshkr95 Mar 23, 2026
6f90839
Add ObjCFormatCheck to format checks list
anshkr95 Mar 23, 2026
239480c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 23, 2026
3e77511
Delete weblate/checks/weblate/checks/tests/test_format.py
anshkr95 Mar 23, 2026
1087b52
Add ObjCFormatTest for Objective-C format checks
anshkr95 Mar 23, 2026
f4c077b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 23, 2026
f170239
Fix formatting issue in checks/models.py
anshkr95 Mar 23, 2026
5b5e8ec
Add check for Objective-C format strings
anshkr95 Mar 23, 2026
5245314
Document Objective-C format string checks
anshkr95 Mar 23, 2026
f693f69
Fix formatting of Objective-C check description
anshkr95 Mar 23, 2026
a07730a
Apply suggestion from @Copilot
anshkr95 Mar 23, 2026
d2f800a
Add additional format checks in ObjCFormatTest
anshkr95 Mar 23, 2026
939315a
Merge branch 'main' into main
anshkr95 Mar 23, 2026
022f4d5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 23, 2026
be7f7ff
Apply suggestion from @nijel
nijel Mar 24, 2026
f07a9ab
Update format.py
anshkr95 Mar 25, 2026
4ee0727
Update changes.rst
anshkr95 Mar 25, 2026
8812956
Merge branch 'main' into main
anshkr95 Mar 26, 2026
6d4c462
Update Objective-C format check documentation
anshkr95 Mar 26, 2026
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 @@ -15,6 +15,7 @@ Weblate 5.17
* Improved LLM interfaces for better reliability.
* Improved logic for adding monolingual plurals in :doc:`/formats/gettext`.
* Improved error messages in some of the :ref:`api` endpoints.
* Added :ref:`check-objc-format`.
* Improved performance of project and category search result pages with very large match sets.
* :envvar:`WEBLATE_COMMIT_PENDING_HOURS` is now available in Docker container.

Expand Down
19 changes: 19 additions & 0 deletions docs/user/checks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,23 @@ Please file a bug if anything is reported in error.
* :ref:`custom-checks`


.. _check-objc-format:

Objective-C format
------------------

.. versionadded:: 5.17

.. check:: objc-format

Checks whether Objective-C format strings match between source and translation.

For example, ``%@`` for objects, ``%d`` for integers, or positional variants like ``%1$@``.

.. seealso::

`Objective-C String Format Specifiers <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html>`_



Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This really should be in the generated file, but I've just realized that our documentation on this is outdated (see #18724). make -C docs update-docs should update it.

.. include:: /snippets/checks-autogenerated.rst
28 changes: 28 additions & 0 deletions weblate/checks/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@
re.VERBOSE,
)

OBJC_PRINTF_MATCH = re.compile(
r"""
%( # initial %
(?:(?P<ord>\d+)\$)? # variable order, like %1$@
(?P<fullvar>
[ +#'-]* # flags
(?:\d+)? # width
(?:\.\d+)? # precision
(hh|h|l|ll)? # length formatting
(?P<type>[a-zA-Z@%]) # type (%s, %d, %@ etc.)
|)
)""",
re.VERBOSE,
)
# index, width and precision can be '*', in which case their value
# will be read from the next element in the Args array
PASCAL_FORMAT_MATCH = re.compile(
Expand Down Expand Up @@ -349,6 +363,11 @@
name_format_is_position_based,
extract_string_simple,
),
"objc-format": (
OBJC_PRINTF_MATCH,
c_format_is_position_based,
extract_string_simple,
),
}


Expand Down Expand Up @@ -517,11 +536,11 @@
errors: list[StrOrPromise] = []

# Merge plurals
results: MissingExtraDict = defaultdict(list)

Check failure on line 539 in weblate/checks/format.py

View workflow job for this annotation

GitHub Actions / mypy

Incompatible types in assignment (expression has type "defaultdict[Never, list[Never]]", variable has type "MissingExtraDict")
for result in checks:
if result:
for key, value in result.items():
results[key].extend(value)

Check failure on line 543 in weblate/checks/format.py

View workflow job for this annotation

GitHub Actions / mypy

TypedDict key must be a string literal; expected one of ("missing", "extra", "errors")
if results:
errors.extend(self.format_result(results))
if errors:
Expand Down Expand Up @@ -608,6 +627,15 @@
description = gettext_lazy("C format string does not match source.")


class ObjCFormatCheck(BasePrintfCheck):
"""Check for Objective-C format string."""

check_id = "objc_format"
name = gettext_lazy("Objective-C format")
description = gettext_lazy("Objective-C format string does not match source.")
Comment on lines +630 to +635
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

No tests are added for the new Objective-C placeholder behavior (e.g. %@ and %1$@) even though this module already has a dedicated format-check test suite. Please add unit tests covering matching, missing/extra placeholders, and positional reordering for objc-format.

Copilot uses AI. Check for mistakes.
Comment on lines +630 to +635
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

As implemented, ObjCFormatCheck will only run when the objc-format flag is present on a unit. If the intent is to resolve #18339 for iOS .strings / .stringsdict by default, wire this flag into the relevant file format(s) via their check_flags so users don’t have to set it manually.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

version_added is missing here.

version_added = "5.17"


class PerlBraceFormatCheck(BaseFormatCheck):
"""Check for Perl brace format string."""

Expand Down
1 change: 1 addition & 0 deletions weblate/checks/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class WeblateChecksConf(AppConf):
"weblate.checks.format.PythonBraceFormatCheck",
"weblate.checks.format.PHPFormatCheck",
"weblate.checks.format.CFormatCheck",
"weblate.checks.format.ObjCFormatCheck",
"weblate.checks.format.PerlFormatCheck",
"weblate.checks.format.PerlBraceFormatCheck",
"weblate.checks.format.JavaScriptFormatCheck",
Expand Down
19 changes: 18 additions & 1 deletion weblate/checks/tests/test_format_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from weblate.checks.models import Check
from weblate.checks.qt import QtFormatCheck, QtPluralCheck
from weblate.checks.ruby import RubyFormatCheck
from weblate.checks.tests.test_checks import CheckTestCase, MockUnit
from weblate.checks.tests.test_checks import BaseFormatTest, CheckTestCase, MockUnit

Check failure on line 37 in weblate/checks/tests/test_format_checks.py

View workflow job for this annotation

GitHub Actions / mypy

Module "weblate.checks.tests.test_checks" has no attribute "BaseFormatTest"; maybe "BaseFormatCheck"?
from weblate.lang.models import Language
from weblate.trans.models import Component, Project, Translation, Unit
from weblate.trans.tests.test_views import FixtureTestCase
Expand Down Expand Up @@ -1917,3 +1917,20 @@
["Recognition {}% ({}/{})"], MockUnit(flags="python-brace-format")
)
)


class ObjCFormatTest(BaseFormatTest):
CHECK = "objc_format"

def test_format(self):

self.check_match("Hello %@", "Hello %@")
self.check_match("Order %1$@ %2$d", "Order %1$@ %2$d")
self.check_match("Value is %1$.2f", "Value is %1$.2f")

self.check_match("%1$@ %2$@", "%2$@ %1$@")

self.check_fail("Hello %@", "Hello %s")
self.check_fail("Total: %d", "Total: %@")
self.check_fail("Missing %@", "Missing")
self.check_fail("Hello", "Hello %@")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This really should use existing attributes logic for the basic testing. Adding other tests is okay, but using existing fine-grained tests is better starting point.

Loading