Skip to content

Commit f4a3980

Browse files
committed
Add hook to define a minimum number or percentage of translation in files
1 parent 416f4bf commit f4a3980

7 files changed

Lines changed: 106 additions & 11 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.4.0
2+
current_version = 1.5.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
@@ -46,3 +46,9 @@
4646
description: Checks that each one of your PO files don't contain more than X messages
4747
files: \.po$
4848
language: python
49+
- id: min-translated
50+
name: min-translated
51+
entry: untranslated-messages-hook --min
52+
description: Checks that each one of your PO files has at least a number or a parcentage of messages translated
53+
files: \.po$
54+
language: python

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ 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.4.0
14+
rev: v1.5.0
1515
hooks:
1616
- id: obsolete-messages
1717
- id: untranslated-messages
@@ -29,6 +29,12 @@ Checks for obsolete messages printing their line numbers if found.
2929

3030
Checks for untranslated messages printing their line numbers if found.
3131

32+
#### Parameters
33+
34+
- `-m/--min`: Minimum number of messages that must be translated in each file
35+
to pass this check. Can be defined as a percentage of the messages translated
36+
appending a character `%` at the end of the value.
37+
3238
### **`lreplace-extracted-comments`**
3339

3440
Replaces a matching string at the beginning of extracted comments.
@@ -112,6 +118,23 @@ first argument:
112118

113119
- Maximum number of messages allowed for each PO file.
114120

121+
### **`min-translated`**
122+
123+
Define a minimum number of files that must be translated in order to pass.
124+
Pass a float or a value ending with `%` character if you wan to compare
125+
against the percentage of translated files:
126+
127+
```yaml
128+
- id: min-translated
129+
args:
130+
- "95%"
131+
```
132+
133+
#### Parameters
134+
135+
- Minimum number or percentage of messages which must be translated in each
136+
PO file.
137+
115138

116139
[pypi-link]: https://pypi.org/project/pre-commit-po-hooks
117140
[pypi-version-badge-link]: https://img.shields.io/pypi/v/pre-commit-po-hooks

hooks/check_entries.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ def main():
7373
return maximum_number_of_messages(
7474
args.filenames, args.max_messages, quiet=args.quiet
7575
)
76+
else:
77+
parser.print_help()
78+
return 1
7679

7780

7881
if __name__ == "__main__":

hooks/untranslated_messages.py

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sys
88

99

10-
def check_untranslated_messages(filenames, quiet=False):
10+
def check_untranslated_messages(filenames, min_=None, quiet=False):
1111
"""Warns about all unstranslated messages found in a set of PO files.
1212
1313
Parameters
@@ -24,6 +24,8 @@ def check_untranslated_messages(filenames, quiet=False):
2424
2525
int: 0 if no untranslated messages found, 1 otherwise.
2626
"""
27+
min_string = str(min_ if min_ is not None else 0)
28+
2729
exitcode = 0
2830
for filename in filenames:
2931
with open(filename) as f:
@@ -32,17 +34,50 @@ def check_untranslated_messages(filenames, quiet=False):
3234
if len(content_lines) > 4: # skip first empty message
3335
content_lines = content_lines[4:]
3436

37+
untranslated_messages, total_messages = 0, -1
38+
3539
for i, line in enumerate(content_lines):
3640
next_i = i + 1
3741

42+
if line.startswith('msgid "'):
43+
total_messages += 1
44+
3845
if line.startswith('msgstr ""') and (
3946
next_i == len(content_lines) or (not content_lines[next_i].strip())
4047
):
48+
exitcode = 1
49+
untranslated_messages += 1
50+
if not quiet and min_ is None:
51+
sys.stderr.write(f"Untranslated message at {filename}:{i + 5}\n")
52+
53+
if min_ is not None:
54+
_is_percent = False
55+
if min_string[-1] == "%":
56+
min_float = total_messages / 100 * float(min_string[:-1])
57+
_is_percent = True
58+
else:
59+
min_float = float(min)
60+
61+
translated_messages = total_messages - untranslated_messages
62+
if min_float > translated_messages:
4163
exitcode = 1
4264
if not quiet:
43-
sys.stderr.write(
44-
f"Found untranslated message at {filename}:{i + 5}\n"
45-
)
65+
if _is_percent:
66+
translation_percent = max(
67+
100,
68+
translated_messages / max(1, total_messages) * 100,
69+
)
70+
sys.stderr.write(
71+
"Lower percent of translation"
72+
f" ({round(translation_percent, 3)}) than minimum"
73+
f" required ({min_string}) at file {filename}\n"
74+
)
75+
else:
76+
sys.stderr.write(
77+
"Lower number of messages translated"
78+
f" ({translated_messages}) than required"
79+
f" ({min_string}) at file {filename}\n"
80+
)
4681

4782
return exitcode
4883

@@ -52,9 +87,28 @@ def main():
5287
parser.add_argument(
5388
"filenames", nargs="*", help="Filenames to check for untranslated messages"
5489
)
90+
parser.add_argument(
91+
"-m",
92+
"--min",
93+
type=str,
94+
metavar="N/N%",
95+
required=False,
96+
default=None,
97+
dest="min",
98+
help=(
99+
"Minimum messages translated in each PO file to be considered valid."
100+
" You can pass either a float number optionally ending in a character"
101+
" % to indicate that is a percentage of the total of translated"
102+
" entries in each PO file."
103+
),
104+
)
55105
parser.add_argument("-q", "--quiet", action="store_true", help="Supress output")
56106
args = parser.parse_args()
57-
return check_untranslated_messages(args.filenames, quiet=args.quiet)
107+
return check_untranslated_messages(
108+
args.filenames,
109+
min_=args.min,
110+
quiet=args.quiet,
111+
)
58112

59113

60114
if __name__ == "__main__":

setup.cfg

Lines changed: 1 addition & 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.4.0
3+
version = 1.5.0
44
description = pre-commit hooks for PO files
55
long_description = file: README.md
66
long_description_content_type = text/markdown

tests/test_untranslated_messages.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from hooks.untranslated_messages import check_untranslated_messages
1111

1212

13+
@pytest.mark.parametrize("min_", ("100%", None), ids=("min_=100%", "min=None"))
1314
@pytest.mark.parametrize("quiet", (False, True), ids=("quiet=False", "quiet=True"))
1415
@pytest.mark.parametrize(
1516
("contents", "n_printed_errors", "expected_exitcode", "expected_line_numbers"),
@@ -47,6 +48,7 @@ def test_check_untranslated_messages(
4748
n_printed_errors,
4849
expected_exitcode,
4950
expected_line_numbers,
51+
min_,
5052
tmp_path,
5153
):
5254
filenames = []
@@ -60,7 +62,10 @@ def test_check_untranslated_messages(
6062

6163
stderr = io.StringIO()
6264
with contextlib.redirect_stderr(stderr):
63-
assert check_untranslated_messages(filenames, quiet=quiet) == expected_exitcode
65+
assert (
66+
check_untranslated_messages(filenames, min_=min_, quiet=quiet)
67+
== expected_exitcode
68+
)
6469

6570
stderr_lines = stderr.getvalue().splitlines()
6671
if quiet:
@@ -71,8 +76,12 @@ def test_check_untranslated_messages(
7176
assert len(stderr_lines) == len(expected_line_numbers)
7277

7378
for i, line in enumerate(stderr_lines):
74-
line_number = int(line.split(":")[-1])
75-
assert line_number == expected_line_numbers[i]
79+
try:
80+
line_number = int(line.split(":")[-1])
81+
except ValueError:
82+
continue
83+
else:
84+
assert line_number == expected_line_numbers[i]
7685

7786
for filename in filenames:
7887
os.remove(filename)

0 commit comments

Comments
 (0)