Skip to content

Commit 2c9e710

Browse files
authored
Add hook to check for fuzzy messages (#19)
1 parent 6c96941 commit 2c9e710

6 files changed

Lines changed: 182 additions & 3 deletions

File tree

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.6.1
2+
current_version = 1.7.0
33

44
[bumpversion:file:setup.cfg]
55

.pre-commit-hooks.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
description: Checks for untranslated messages in PO files
1111
files: \.po$
1212
language: python
13+
- id: fuzzy-messages
14+
name: fuzzy-messages
15+
entry: fuzzy-messages-hook
16+
description: Checks for fuzzy messages in PO files
17+
files: \.po$
18+
language: python
1319
- id: remove-django-translators
1420
name: remove-django-translators
1521
entry: lreplace-extracted-comments-hook --django-translators

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ Hooks for pre-commit useful working with PO files.
1111

1212
```yaml
1313
- repo: https://github.com/mondeja/pre-commit-po-hooks
14-
rev: v1.6.1
14+
rev: v1.7.0
1515
hooks:
1616
- id: obsolete-messages
1717
- id: untranslated-messages
18+
- id: fuzzy-messages
1819
- id: remove-django-translators
1920
- id: standard-metadata
2021
- id: max-lines
@@ -37,6 +38,10 @@ Checks for untranslated messages printing their line numbers if found.
3738
to pass this check. Can be defined as a percentage of the messages translated
3839
appending a character `%` at the end of the value.
3940

41+
### **`fuzzy-messages`**
42+
43+
Checks for fuzzy messages printing their line numbers if found.
44+
4045
### **`lreplace-extracted-comments`**
4146

4247
Replaces a matching string at the beginning of extracted comments.

hooks/fuzzy_messages.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Checks for fuzzy messages in PO files.
2+
3+
Returns an error code if a PO file has a fuzzy message.
4+
"""
5+
6+
import argparse
7+
import sys
8+
9+
10+
def check_fuzzy_messages(filenames, quiet=False):
11+
"""Warns about all fuzzy messages found in a set of PO files.
12+
13+
Parameters
14+
----------
15+
16+
filenames : list
17+
Set of file names to check.
18+
19+
quiet : bool, optional
20+
Enabled, don't print output to stderr when an fuzzy message is found.
21+
22+
Returns
23+
-------
24+
25+
int: 0 if no fuzzy messages found, 1 otherwise.
26+
"""
27+
exitcode = 0
28+
for filename in filenames:
29+
with open(filename) as f:
30+
content_lines = f.readlines()
31+
32+
for i, line in enumerate(content_lines):
33+
if line.startswith("#,") and "fuzzy" in line:
34+
exitcode = 1
35+
if not quiet:
36+
sys.stderr.write(f"Found fuzzy message at {filename}:{i + 1}\n")
37+
38+
return exitcode
39+
40+
41+
def main():
42+
parser = argparse.ArgumentParser()
43+
parser.add_argument(
44+
"filenames", nargs="*", help="Filenames to check for fuzzy messages"
45+
)
46+
parser.add_argument("-q", "--quiet", action="store_true", help="Supress output")
47+
args = parser.parse_args()
48+
return check_fuzzy_messages(args.filenames, quiet=args.quiet)
49+
50+
51+
if __name__ == "__main__":
52+
exit(main())

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pre_commit_po_hooks
3-
version = 1.6.1
3+
version = 1.7.0
44
description = pre-commit hooks for PO files
55
long_description = file: README.md
66
long_description_content_type = text/markdown
@@ -32,6 +32,7 @@ exclude =
3232
console_scripts =
3333
obsolete-messages-hook = hooks.obsolete_messages:main
3434
untranslated-messages-hook = hooks.untranslated_messages:main
35+
fuzzy-messages-hook = hooks.fuzzy_messages:main
3536
lreplace-extracted-comments-hook = hooks.lreplace_extracted_comments:main
3637
check-metadata-hook = hooks.check_metadata:main
3738
check-entries-hook = hooks.check_entries:main

tests/test_fuzzy_messages.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""Obsolete messages hook tests."""
2+
3+
import contextlib
4+
import io
5+
import os
6+
import uuid
7+
8+
import pytest
9+
10+
from hooks.fuzzy_messages import check_fuzzy_messages
11+
12+
13+
"""
14+
(
15+
[
16+
(
17+
'#\nmsgid ""\nmsgstr ""\n\n#~ msgid "Obsolete "\n#~ "message"'
18+
'\n#~ msgstr "Mensaje obsoleto"\n'
19+
),
20+
'#\nmsgid ""\nmsgstr ""\n\nmsgid "Hello"\nmsgstr "Hola"\n',
21+
(
22+
'#\nmsgid ""\nmsgstr ""\n\n#~ msgid "Hello"\n#~ msgstr "Hola"\n'
23+
'\nmsgid "Foo"\nmsgstr "Bar"\n'
24+
),
25+
],
26+
2,
27+
1,
28+
[5, 5],
29+
),
30+
(
31+
['#\nmsgid ""\nmsgstr ""\n\nmsgid "Current"\nmsgstr "Actual"\n'],
32+
0,
33+
0,
34+
None,
35+
),
36+
"""
37+
38+
39+
@pytest.mark.parametrize("quiet", (False, True), ids=("quiet=False", "quiet=True"))
40+
@pytest.mark.parametrize(
41+
("contents", "n_printed_errors", "expected_exitcode", "expected_line_numbers"),
42+
(
43+
pytest.param(
44+
[
45+
(
46+
'#\nmsgid ""\nmsgstr ""\n\n'
47+
'#, fuzzy\n#~ msgid "Obsolete"\n#~ msgstr "Obsoleto"\n'
48+
)
49+
],
50+
1,
51+
1,
52+
[5],
53+
id="one-fuzzy-message",
54+
),
55+
pytest.param(
56+
[
57+
(
58+
'#\nmsgid ""\nmsgstr ""\n\n'
59+
'#, fuzzy\n#~ msgid "Obsolete"\n#~ msgstr "Obsoleto"\n'
60+
),
61+
(
62+
'#\nmsgid ""\nmsgstr ""\n\n'
63+
'#, fuzzy\nmsgid "Foo"\nmsgstr "Foo es"\n\n'
64+
'msgid "Bar"\nmsgstr "Bar es"\n'
65+
),
66+
],
67+
2,
68+
1,
69+
[5, 5],
70+
id="two-fuzzy-messages-in-two-files",
71+
),
72+
pytest.param(
73+
['#\nmsgid ""\nmsgstr ""\n\nmsgid "Current"\nmsgstr "Actual"\n'],
74+
0,
75+
0,
76+
None,
77+
id="no-fuzzy-messages",
78+
),
79+
),
80+
)
81+
def test_check_fuzzy_messages(
82+
quiet,
83+
contents,
84+
n_printed_errors,
85+
expected_exitcode,
86+
expected_line_numbers,
87+
tmp_path,
88+
):
89+
filenames = []
90+
for content in contents:
91+
filename = tmp_path / f"{uuid.uuid4().hex[:16]}.po"
92+
93+
with open(filename, "w") as f:
94+
f.write(content)
95+
96+
filenames.append(filename)
97+
98+
stderr = io.StringIO()
99+
with contextlib.redirect_stderr(stderr):
100+
assert check_fuzzy_messages(filenames, quiet=quiet) == expected_exitcode
101+
102+
stderr_lines = stderr.getvalue().splitlines()
103+
if quiet:
104+
n_printed_errors = 0
105+
assert len(stderr_lines) == n_printed_errors
106+
107+
if n_printed_errors:
108+
assert len(stderr_lines) == len(expected_line_numbers)
109+
110+
for i, line in enumerate(stderr_lines):
111+
line_number = int(line.split(":")[-1])
112+
assert line_number == expected_line_numbers[i]
113+
114+
for filename in filenames:
115+
os.remove(filename)

0 commit comments

Comments
 (0)