localizationkit 1.0.1__tar.gz → 3.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {localizationkit-1.0.1 → localizationkit-3.0.1}/PKG-INFO +7 -5
- {localizationkit-1.0.1 → localizationkit-3.0.1}/README.md +1 -1
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/__init__.py +7 -8
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/configuration.py +7 -7
- localizationkit-3.0.1/localizationkit/exceptions.py +9 -0
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/localization_types.py +11 -11
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/comment_linebreaks.py +4 -4
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/comment_similarity.py +7 -4
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/duplicate_keys.py +8 -5
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/has_comments.py +12 -6
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/has_value.py +5 -5
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/invalid_tokens.py +10 -5
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/key_length.py +17 -7
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/objectivec_alternative_tokens.py +9 -4
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/placeholder_token_explanation.py +11 -5
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/swift_interpolation.py +9 -4
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/test_case.py +9 -6
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/token_matching.py +7 -4
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/token_position_identifiers.py +17 -7
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/utility_types.py +3 -5
- {localizationkit-1.0.1 → localizationkit-3.0.1}/pyproject.toml +12 -12
- localizationkit-1.0.1/setup.py +0 -30
- {localizationkit-1.0.1 → localizationkit-3.0.1}/LICENSE +0 -0
- {localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/__init__.py +0 -0
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: localizationkit
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: String localization tests
|
|
5
5
|
Home-page: https://github.com/Microsoft/localizationkit
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: localization,strings,tests
|
|
8
8
|
Author: Dale Myers
|
|
9
9
|
Author-email: dalemy@microsoft.com
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.11,<4.0
|
|
11
11
|
Classifier: Development Status :: 3 - Alpha
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
18
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
21
|
Classifier: Topic :: Software Development
|
|
20
22
|
Classifier: Topic :: Software Development :: Testing
|
|
21
23
|
Classifier: Topic :: Utilities
|
|
22
|
-
Requires-Dist: toml (>=0.10.
|
|
24
|
+
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
|
23
25
|
Project-URL: Repository, https://github.com/Microsoft/localizationkit
|
|
24
26
|
Description-Content-Type: text/markdown
|
|
25
27
|
|
|
@@ -98,7 +100,7 @@ for result in results:
|
|
|
98
100
|
print("The following test failed:", result.name)
|
|
99
101
|
print("Failures encountered:")
|
|
100
102
|
for violation in result.violations:
|
|
101
|
-
print(violation)
|
|
103
|
+
print(violation[0]) # The other value in the tuple is the language code
|
|
102
104
|
```
|
|
103
105
|
|
|
104
106
|
### Not running the tests
|
|
@@ -73,7 +73,7 @@ for result in results:
|
|
|
73
73
|
print("The following test failed:", result.name)
|
|
74
74
|
print("Failures encountered:")
|
|
75
75
|
for violation in result.violations:
|
|
76
|
-
print(violation)
|
|
76
|
+
print(violation[0]) # The other value in the tuple is the language code
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
### Not running the tests
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"""Toolkit for validation of localized strings."""
|
|
2
2
|
|
|
3
|
-
import abc
|
|
4
|
-
import difflib
|
|
5
3
|
import inspect
|
|
6
|
-
from typing import
|
|
4
|
+
from typing import Type
|
|
7
5
|
|
|
8
6
|
from localizationkit.configuration import Configuration
|
|
7
|
+
from localizationkit.exceptions import LocalizationKitException
|
|
9
8
|
from localizationkit.localization_types import LocalizedCollection, LocalizedString
|
|
10
9
|
from localizationkit.utility_types import TestResult
|
|
11
10
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
@@ -13,7 +12,7 @@ from localizationkit.tests.test_case import LocalizationTestCase
|
|
|
13
12
|
from localizationkit import tests
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
def _find_tests() ->
|
|
15
|
+
def _find_tests() -> list[Type[LocalizationTestCase]]:
|
|
17
16
|
"""Find all the tests."""
|
|
18
17
|
|
|
19
18
|
test_module_names = [
|
|
@@ -21,8 +20,8 @@ def _find_tests() -> List[Type[LocalizationTestCase]]:
|
|
|
21
20
|
]
|
|
22
21
|
test_modules = [getattr(tests, name) for name in test_module_names]
|
|
23
22
|
|
|
24
|
-
names_seen:
|
|
25
|
-
test_classes:
|
|
23
|
+
names_seen: set[str] = set()
|
|
24
|
+
test_classes: list[Type[LocalizationTestCase]] = []
|
|
26
25
|
|
|
27
26
|
for module in test_modules:
|
|
28
27
|
|
|
@@ -40,7 +39,7 @@ def _find_tests() -> List[Type[LocalizationTestCase]]:
|
|
|
40
39
|
continue
|
|
41
40
|
|
|
42
41
|
if reference.__name__ in names_seen:
|
|
43
|
-
raise
|
|
42
|
+
raise LocalizationKitException(
|
|
44
43
|
"At least 2 classes exist with the same name: " + reference.__name__
|
|
45
44
|
)
|
|
46
45
|
|
|
@@ -49,7 +48,7 @@ def _find_tests() -> List[Type[LocalizationTestCase]]:
|
|
|
49
48
|
return test_classes
|
|
50
49
|
|
|
51
50
|
|
|
52
|
-
def run_tests(configuration: Configuration, collection: LocalizedCollection) ->
|
|
51
|
+
def run_tests(configuration: Configuration, collection: LocalizedCollection) -> list[TestResult]:
|
|
53
52
|
"""Run all tests."""
|
|
54
53
|
|
|
55
54
|
test_classes = _find_tests()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Configuration for localization tests."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, cast
|
|
3
|
+
from typing import Any, cast
|
|
4
4
|
|
|
5
5
|
import toml
|
|
6
6
|
|
|
@@ -8,9 +8,9 @@ import toml
|
|
|
8
8
|
class Configuration:
|
|
9
9
|
"""Holds all the configuration data."""
|
|
10
10
|
|
|
11
|
-
raw_config:
|
|
11
|
+
raw_config: dict[str, Any]
|
|
12
12
|
|
|
13
|
-
def __init__(self, raw_config:
|
|
13
|
+
def __init__(self, raw_config: dict[str, Any]) -> None:
|
|
14
14
|
self.raw_config = raw_config
|
|
15
15
|
|
|
16
16
|
def default_language(self) -> str:
|
|
@@ -22,21 +22,21 @@ class Configuration:
|
|
|
22
22
|
"""
|
|
23
23
|
return self.raw_config.get("default_language", "en")
|
|
24
24
|
|
|
25
|
-
def blacklist(self) ->
|
|
25
|
+
def blacklist(self) -> list[str]:
|
|
26
26
|
"""Return the list of blacklisted tests.
|
|
27
27
|
|
|
28
28
|
:returns: The blacklisted tests
|
|
29
29
|
"""
|
|
30
30
|
return self.raw_config.get("blacklist", [])
|
|
31
31
|
|
|
32
|
-
def opt_in(self) ->
|
|
32
|
+
def opt_in(self) -> list[str]:
|
|
33
33
|
"""Return the list of opted in tests.
|
|
34
34
|
|
|
35
35
|
:returns: The opted in tests
|
|
36
36
|
"""
|
|
37
37
|
return self.raw_config.get("opt_in", [])
|
|
38
38
|
|
|
39
|
-
def get_test_preferences(self, name: str) ->
|
|
39
|
+
def get_test_preferences(self, name: str) -> dict[str, Any]:
|
|
40
40
|
"""Get the preferences for a given test.
|
|
41
41
|
|
|
42
42
|
:param name: The name of the test
|
|
@@ -55,4 +55,4 @@ class Configuration:
|
|
|
55
55
|
"""
|
|
56
56
|
|
|
57
57
|
with open(file_path, encoding="utf-8") as config_file:
|
|
58
|
-
return Configuration(cast(
|
|
58
|
+
return Configuration(cast(dict[str, Any], toml.load(config_file)))
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Exception types."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LocalizationKitException(Exception):
|
|
5
|
+
"""Represents an exception from this library."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DuplicateTestException(LocalizationKitException):
|
|
9
|
+
"""Represents an exception where multiple tests with the same name appear."""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Types for localization."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
-
from typing import ClassVar,
|
|
4
|
+
from typing import ClassVar, Pattern
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class LocalizedString:
|
|
@@ -13,9 +13,9 @@ class LocalizedString:
|
|
|
13
13
|
:param language_code: The language code for the language the string is in
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
_TOKEN_REGEX: ClassVar[
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
_TOKEN_REGEX: ClassVar[str] = (
|
|
17
|
+
r"(%(?:[0-9]+\$)?[0-9]*\.?[0-9]*(?:h|hh|l|ll|q|L|z|t|j){0,2}[dDuUxXoOfFeEgGcCsSaAp@])"
|
|
18
|
+
)
|
|
19
19
|
_TOKEN_PATTERN: ClassVar[Pattern] = re.compile(_TOKEN_REGEX, flags=re.DOTALL)
|
|
20
20
|
|
|
21
21
|
key: str
|
|
@@ -29,14 +29,14 @@ class LocalizedString:
|
|
|
29
29
|
self.comment = comment
|
|
30
30
|
self.language_code = language_code
|
|
31
31
|
|
|
32
|
-
def tokens(self) ->
|
|
32
|
+
def tokens(self) -> list[str]:
|
|
33
33
|
"""Find and return the tokens in the string.
|
|
34
34
|
|
|
35
35
|
:returns: The list of tokens in the string
|
|
36
36
|
"""
|
|
37
37
|
return LocalizedString._TOKEN_PATTERN.findall(self.value)
|
|
38
38
|
|
|
39
|
-
def comment_tokens(self) ->
|
|
39
|
+
def comment_tokens(self) -> list[str]:
|
|
40
40
|
"""Find and return the tokens in the comment string.
|
|
41
41
|
|
|
42
42
|
:returns: The list of tokens in the comment string
|
|
@@ -64,12 +64,12 @@ class LocalizedCollection:
|
|
|
64
64
|
:param localized_strings: The list of localized strings in the collection
|
|
65
65
|
"""
|
|
66
66
|
|
|
67
|
-
localized_strings:
|
|
67
|
+
localized_strings: list[LocalizedString]
|
|
68
68
|
|
|
69
|
-
def __init__(self, localized_strings:
|
|
69
|
+
def __init__(self, localized_strings: list[LocalizedString]) -> None:
|
|
70
70
|
self.localized_strings = localized_strings
|
|
71
71
|
|
|
72
|
-
def strings_for_key(self, key: str) ->
|
|
72
|
+
def strings_for_key(self, key: str) -> list[LocalizedString]:
|
|
73
73
|
"""Return all the strings matching the key
|
|
74
74
|
|
|
75
75
|
i.e. This will have a list of strings with identical keys, but different
|
|
@@ -79,7 +79,7 @@ class LocalizedCollection:
|
|
|
79
79
|
"""
|
|
80
80
|
return [string for string in self.localized_strings if string.key == key]
|
|
81
81
|
|
|
82
|
-
def strings_for_language(self, language_code: str) ->
|
|
82
|
+
def strings_for_language(self, language_code: str) -> list[LocalizedString]:
|
|
83
83
|
"""Return all the strings matching the language code
|
|
84
84
|
|
|
85
85
|
:returns: The list of strings with the matching language code
|
|
@@ -88,7 +88,7 @@ class LocalizedCollection:
|
|
|
88
88
|
string for string in self.localized_strings if string.language_code == language_code
|
|
89
89
|
]
|
|
90
90
|
|
|
91
|
-
def languages(self) ->
|
|
91
|
+
def languages(self) -> list[str]:
|
|
92
92
|
"""Return the list of languages in the collection.
|
|
93
93
|
|
|
94
94
|
:returns: The list of langauges in the collection
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Checks for comments."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,14 +13,14 @@ class CommentLinebreaks(LocalizationTestCase):
|
|
|
13
13
|
return "comment_linebreaks"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {}
|
|
18
18
|
|
|
19
19
|
@classmethod
|
|
20
20
|
def is_opt_in(cls) -> bool:
|
|
21
21
|
return True
|
|
22
22
|
|
|
23
|
-
def run_test(self) ->
|
|
23
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
24
24
|
violations = []
|
|
25
25
|
|
|
26
26
|
for string in self.collection.strings_for_language(self.configuration.default_language()):
|
|
@@ -29,6 +29,6 @@ class CommentLinebreaks(LocalizationTestCase):
|
|
|
29
29
|
continue
|
|
30
30
|
|
|
31
31
|
if "\n" in string.comment or "\r" in string.comment:
|
|
32
|
-
violations.append(f"Comment contains linebreaks: {string}")
|
|
32
|
+
violations.append((f"Comment contains linebreaks: {string}", string.language_code))
|
|
33
33
|
|
|
34
34
|
return violations
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Comment similarity."""
|
|
2
2
|
|
|
3
3
|
import difflib
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
7
7
|
|
|
@@ -14,10 +14,10 @@ class CheckCommentSimilarity(LocalizationTestCase):
|
|
|
14
14
|
return "comment_similarity"
|
|
15
15
|
|
|
16
16
|
@classmethod
|
|
17
|
-
def default_settings(cls) ->
|
|
17
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
18
18
|
return {"maximum_similarity_ratio": 0.5}
|
|
19
19
|
|
|
20
|
-
def run_test(self) ->
|
|
20
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
21
21
|
|
|
22
22
|
maximum_similarity_ratio = self.get_setting("maximum_similarity_ratio")
|
|
23
23
|
|
|
@@ -31,7 +31,10 @@ class CheckCommentSimilarity(LocalizationTestCase):
|
|
|
31
31
|
|
|
32
32
|
if similarity > maximum_similarity_ratio:
|
|
33
33
|
violations.append(
|
|
34
|
-
|
|
34
|
+
(
|
|
35
|
+
f"Value and comment exceeded similarity ratio of {maximum_similarity_ratio}: {string}",
|
|
36
|
+
string.language_code,
|
|
37
|
+
)
|
|
35
38
|
)
|
|
36
39
|
continue
|
|
37
40
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Duplicate keys."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,10 +13,10 @@ class DuplicateKeys(LocalizationTestCase):
|
|
|
13
13
|
return "duplicate_keys"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {"all_languages": False}
|
|
18
18
|
|
|
19
|
-
def run_test(self) ->
|
|
19
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
20
20
|
|
|
21
21
|
violations = []
|
|
22
22
|
|
|
@@ -29,13 +29,16 @@ class DuplicateKeys(LocalizationTestCase):
|
|
|
29
29
|
|
|
30
30
|
for language in languages_to_check:
|
|
31
31
|
|
|
32
|
-
keys:
|
|
32
|
+
keys: set[str] = set()
|
|
33
33
|
|
|
34
34
|
for string in self.collection.strings_for_language(language):
|
|
35
35
|
|
|
36
36
|
if string.key in keys:
|
|
37
37
|
violations.append(
|
|
38
|
-
|
|
38
|
+
(
|
|
39
|
+
f"The key '{string.key}' appears for multiple strings. e.g. {string}",
|
|
40
|
+
string.language_code,
|
|
41
|
+
)
|
|
39
42
|
)
|
|
40
43
|
continue
|
|
41
44
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Checks for comments."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,10 +13,10 @@ class HasComments(LocalizationTestCase):
|
|
|
13
13
|
return "has_comments"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {"minimum_comment_length": 30, "minimum_comment_words": 10}
|
|
18
18
|
|
|
19
|
-
def run_test(self) ->
|
|
19
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
20
20
|
|
|
21
21
|
minimum_comment_length = self.get_setting("minimum_comment_length")
|
|
22
22
|
minimum_comment_words = self.get_setting("minimum_comment_words")
|
|
@@ -26,12 +26,15 @@ class HasComments(LocalizationTestCase):
|
|
|
26
26
|
for string in self.collection.strings_for_language(self.configuration.default_language()):
|
|
27
27
|
|
|
28
28
|
if string.comment is None:
|
|
29
|
-
violations.append(f"Comment was empty: {string}")
|
|
29
|
+
violations.append((f"Comment was empty: {string}", string.language_code))
|
|
30
30
|
continue
|
|
31
31
|
|
|
32
32
|
if minimum_comment_length >= 0 and len(string.comment) < minimum_comment_length:
|
|
33
33
|
violations.append(
|
|
34
|
-
|
|
34
|
+
(
|
|
35
|
+
f"Comment did not meet minimum length of {minimum_comment_length}: {string}",
|
|
36
|
+
string.language_code,
|
|
37
|
+
)
|
|
35
38
|
)
|
|
36
39
|
continue
|
|
37
40
|
|
|
@@ -40,7 +43,10 @@ class HasComments(LocalizationTestCase):
|
|
|
40
43
|
and len(string.comment.split(" ")) < minimum_comment_words
|
|
41
44
|
):
|
|
42
45
|
violations.append(
|
|
43
|
-
|
|
46
|
+
(
|
|
47
|
+
f"Comment did not meet minimum word count of {minimum_comment_words}: {string}",
|
|
48
|
+
string.language_code,
|
|
49
|
+
)
|
|
44
50
|
)
|
|
45
51
|
continue
|
|
46
52
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Checks that the value is set."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,12 +13,12 @@ class HasValue(LocalizationTestCase):
|
|
|
13
13
|
return "has_value"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {"default_language_only": False}
|
|
18
18
|
|
|
19
|
-
def run_test(self) ->
|
|
19
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
20
20
|
|
|
21
|
-
violations:
|
|
21
|
+
violations: list[tuple[str, str]] = []
|
|
22
22
|
|
|
23
23
|
if self.get_setting("default_language_only"):
|
|
24
24
|
test_collection = self.collection.strings_for_language(
|
|
@@ -29,7 +29,7 @@ class HasValue(LocalizationTestCase):
|
|
|
29
29
|
|
|
30
30
|
for string in test_collection:
|
|
31
31
|
if string.value is None or len(string.value) == 0:
|
|
32
|
-
violations.append(f"Value was empty: {string}")
|
|
32
|
+
violations.append((f"Value was empty: {string}", string.language_code))
|
|
33
33
|
continue
|
|
34
34
|
|
|
35
35
|
return violations
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Invalid tokens."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
7
7
|
|
|
@@ -14,20 +14,25 @@ class InvalidTokens(LocalizationTestCase):
|
|
|
14
14
|
return "invalid_tokens"
|
|
15
15
|
|
|
16
16
|
@classmethod
|
|
17
|
-
def default_settings(cls) ->
|
|
17
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
18
18
|
return {}
|
|
19
19
|
|
|
20
|
-
def run_test(self) ->
|
|
20
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
21
21
|
|
|
22
22
|
violations = []
|
|
23
23
|
|
|
24
|
-
invalid_token_pattern = re.compile(r"(%[^@%\.a-
|
|
24
|
+
invalid_token_pattern = re.compile(r"(%[^@%\.a-zA-Z0-9 ]+)", flags=re.DOTALL)
|
|
25
25
|
|
|
26
26
|
for string in self.collection.localized_strings:
|
|
27
27
|
matches = invalid_token_pattern.findall(string.value)
|
|
28
28
|
|
|
29
29
|
# Any matches are a bad thing
|
|
30
30
|
if matches and len(matches) > 0:
|
|
31
|
-
violations.append(
|
|
31
|
+
violations.append(
|
|
32
|
+
(
|
|
33
|
+
f"Translation contains invalid tokens ({matches}): {string}",
|
|
34
|
+
string.language_code,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
32
37
|
|
|
33
38
|
return violations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Checks key length."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,15 +13,15 @@ class KeyLength(LocalizationTestCase):
|
|
|
13
13
|
return "key_length"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {"minimum": -1, "maximum": -1}
|
|
18
18
|
|
|
19
|
-
def run_test(self) ->
|
|
19
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
20
20
|
|
|
21
21
|
minimum_length = self.get_setting("minimum")
|
|
22
22
|
maximum_length = self.get_setting("maximum")
|
|
23
23
|
|
|
24
|
-
violations:
|
|
24
|
+
violations: list[tuple[str, str]] = []
|
|
25
25
|
|
|
26
26
|
# Short circuit for the non-bound case
|
|
27
27
|
if minimum_length < 0 and maximum_length < 0:
|
|
@@ -32,13 +32,23 @@ class KeyLength(LocalizationTestCase):
|
|
|
32
32
|
key = string.key
|
|
33
33
|
|
|
34
34
|
if key is None:
|
|
35
|
-
violations.append(f"Key was empty: {string}")
|
|
35
|
+
violations.append((f"Key was empty: {string}", string.language_code))
|
|
36
36
|
continue
|
|
37
37
|
|
|
38
38
|
if len(key) < minimum_length >= 0:
|
|
39
|
-
violations.append(
|
|
39
|
+
violations.append(
|
|
40
|
+
(
|
|
41
|
+
f"Key is shorter than minimum of {minimum_length}: {string}",
|
|
42
|
+
string.language_code,
|
|
43
|
+
)
|
|
44
|
+
)
|
|
40
45
|
|
|
41
46
|
if len(key) > maximum_length >= 0:
|
|
42
|
-
violations.append(
|
|
47
|
+
violations.append(
|
|
48
|
+
(
|
|
49
|
+
f"Key is longer than maximum of {maximum_length}: {string}",
|
|
50
|
+
string.language_code,
|
|
51
|
+
)
|
|
52
|
+
)
|
|
43
53
|
|
|
44
54
|
return violations
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Objective-C alternative tokens."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
7
7
|
|
|
@@ -19,14 +19,14 @@ class ObjectivecAlternativeTokens(LocalizationTestCase):
|
|
|
19
19
|
return "objectivec_alternative_tokens"
|
|
20
20
|
|
|
21
21
|
@classmethod
|
|
22
|
-
def default_settings(cls) ->
|
|
22
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
23
23
|
return {}
|
|
24
24
|
|
|
25
25
|
@classmethod
|
|
26
26
|
def is_opt_in(cls) -> bool:
|
|
27
27
|
return True
|
|
28
28
|
|
|
29
|
-
def run_test(self) ->
|
|
29
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
30
30
|
|
|
31
31
|
violations = []
|
|
32
32
|
|
|
@@ -35,7 +35,12 @@ class ObjectivecAlternativeTokens(LocalizationTestCase):
|
|
|
35
35
|
for string in self.collection.localized_strings:
|
|
36
36
|
|
|
37
37
|
if pattern.findall(string.value):
|
|
38
|
-
violations.append(
|
|
38
|
+
violations.append(
|
|
39
|
+
(
|
|
40
|
+
f"Objective-C alternative tokens were found in string: {string}",
|
|
41
|
+
string.language_code,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
39
44
|
continue
|
|
40
45
|
|
|
41
46
|
return violations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Placeholder token explanation."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,14 +13,14 @@ class PlaceholderTokenExplanation(LocalizationTestCase):
|
|
|
13
13
|
return "placeholder_token_explanation"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {}
|
|
18
18
|
|
|
19
19
|
@classmethod
|
|
20
20
|
def is_opt_in(cls) -> bool:
|
|
21
21
|
return True
|
|
22
22
|
|
|
23
|
-
def run_test(self) ->
|
|
23
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
24
24
|
|
|
25
25
|
violations = []
|
|
26
26
|
|
|
@@ -33,12 +33,18 @@ class PlaceholderTokenExplanation(LocalizationTestCase):
|
|
|
33
33
|
|
|
34
34
|
if len(extra_in_tokens) != 0:
|
|
35
35
|
violations.append(
|
|
36
|
-
|
|
36
|
+
(
|
|
37
|
+
f"Tokens are not described in the comment ({extra_in_tokens}): {string}",
|
|
38
|
+
string.language_code,
|
|
39
|
+
)
|
|
37
40
|
)
|
|
38
41
|
|
|
39
42
|
if len(extra_in_comments) != 0:
|
|
40
43
|
violations.append(
|
|
41
|
-
|
|
44
|
+
(
|
|
45
|
+
f"Extra tokens appear in the comment ({extra_in_comments}): {string}",
|
|
46
|
+
string.language_code,
|
|
47
|
+
)
|
|
42
48
|
)
|
|
43
49
|
|
|
44
50
|
return violations
|
{localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/swift_interpolation.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Swift interpolation."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
7
7
|
|
|
@@ -14,14 +14,14 @@ class SwiftInterpolation(LocalizationTestCase):
|
|
|
14
14
|
return "swift_interpolation"
|
|
15
15
|
|
|
16
16
|
@classmethod
|
|
17
|
-
def default_settings(cls) ->
|
|
17
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
18
18
|
return {}
|
|
19
19
|
|
|
20
20
|
@classmethod
|
|
21
21
|
def is_opt_in(cls) -> bool:
|
|
22
22
|
return True
|
|
23
23
|
|
|
24
|
-
def run_test(self) ->
|
|
24
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
25
25
|
|
|
26
26
|
violations = []
|
|
27
27
|
|
|
@@ -30,7 +30,12 @@ class SwiftInterpolation(LocalizationTestCase):
|
|
|
30
30
|
for string in self.collection.localized_strings:
|
|
31
31
|
|
|
32
32
|
if pattern.findall(string.value):
|
|
33
|
-
violations.append(
|
|
33
|
+
violations.append(
|
|
34
|
+
(
|
|
35
|
+
f"Swift string interpolation found in string: {string}",
|
|
36
|
+
string.language_code,
|
|
37
|
+
)
|
|
38
|
+
)
|
|
34
39
|
continue
|
|
35
40
|
|
|
36
41
|
return violations
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Base type for localization."""
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from localizationkit.configuration import Configuration
|
|
7
|
+
from localizationkit.exceptions import LocalizationKitException
|
|
7
8
|
from localizationkit.localization_types import LocalizedCollection
|
|
8
9
|
from localizationkit.utility_types import TestResult
|
|
9
10
|
|
|
@@ -18,7 +19,7 @@ class LocalizationTestCase(abc.ABC):
|
|
|
18
19
|
|
|
19
20
|
configuration: Configuration
|
|
20
21
|
collection: LocalizedCollection
|
|
21
|
-
test_settings:
|
|
22
|
+
test_settings: dict[str, Any]
|
|
22
23
|
|
|
23
24
|
def __init__(self, configuration: Configuration, collection: LocalizedCollection):
|
|
24
25
|
self.configuration = configuration
|
|
@@ -45,7 +46,7 @@ class LocalizationTestCase(abc.ABC):
|
|
|
45
46
|
|
|
46
47
|
@classmethod
|
|
47
48
|
@abc.abstractmethod
|
|
48
|
-
def default_settings(cls) ->
|
|
49
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
49
50
|
"""Get the default settings for the test.
|
|
50
51
|
|
|
51
52
|
:returns: The default settings for the test
|
|
@@ -64,7 +65,7 @@ class LocalizationTestCase(abc.ABC):
|
|
|
64
65
|
return False
|
|
65
66
|
|
|
66
67
|
@abc.abstractmethod
|
|
67
|
-
def run_test(self) ->
|
|
68
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
68
69
|
"""Run the test
|
|
69
70
|
|
|
70
71
|
:returns: The list of violations encountered
|
|
@@ -80,11 +81,13 @@ class LocalizationTestCase(abc.ABC):
|
|
|
80
81
|
violations = self.run_test()
|
|
81
82
|
|
|
82
83
|
if violations is None:
|
|
83
|
-
raise
|
|
84
|
+
raise LocalizationKitException(
|
|
85
|
+
f"Failed to get exceptions from test: {self.__class__.name()}"
|
|
86
|
+
)
|
|
84
87
|
|
|
85
88
|
if len(violations):
|
|
86
89
|
return TestResult.failure(self.__class__.name(), violations)
|
|
87
90
|
|
|
88
91
|
return TestResult.success(self.__class__.name())
|
|
89
92
|
except Exception as ex:
|
|
90
|
-
return TestResult.failure(self.__class__.name(), [str(ex)])
|
|
93
|
+
return TestResult.failure(self.__class__.name(), [(str(ex), "unknown")])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Token matching."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,10 +13,10 @@ class TokenMatching(LocalizationTestCase):
|
|
|
13
13
|
return "token_matching"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {"allow_missing_defaults": False}
|
|
18
18
|
|
|
19
|
-
def run_test(self) ->
|
|
19
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
20
20
|
|
|
21
21
|
violations = []
|
|
22
22
|
|
|
@@ -46,7 +46,10 @@ class TokenMatching(LocalizationTestCase):
|
|
|
46
46
|
translation_tokens = sorted(string.tokens())
|
|
47
47
|
if default_tokens != translation_tokens:
|
|
48
48
|
violations.append(
|
|
49
|
-
|
|
49
|
+
(
|
|
50
|
+
f"The tokens for {language_code} do not match the default language: {string.key}",
|
|
51
|
+
string.language_code,
|
|
52
|
+
)
|
|
50
53
|
)
|
|
51
54
|
|
|
52
55
|
return violations
|
{localizationkit-1.0.1 → localizationkit-3.0.1}/localizationkit/tests/token_position_identifiers.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Token position identifiers."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
6
6
|
|
|
@@ -13,10 +13,10 @@ class TokenPositionIdentifiers(LocalizationTestCase):
|
|
|
13
13
|
return "token_position_identifiers"
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
|
-
def default_settings(cls) ->
|
|
16
|
+
def default_settings(cls) -> dict[str, Any]:
|
|
17
17
|
return {"always": False}
|
|
18
18
|
|
|
19
|
-
def run_test(self) ->
|
|
19
|
+
def run_test(self) -> list[tuple[str, str]]:
|
|
20
20
|
|
|
21
21
|
violations = []
|
|
22
22
|
|
|
@@ -34,23 +34,33 @@ class TokenPositionIdentifiers(LocalizationTestCase):
|
|
|
34
34
|
continue
|
|
35
35
|
|
|
36
36
|
# Track the positions we've seen
|
|
37
|
-
positional_tokens:
|
|
37
|
+
positional_tokens: set[int] = set()
|
|
38
38
|
|
|
39
39
|
for token in tokens:
|
|
40
40
|
if "$" not in token:
|
|
41
|
-
violations.append(
|
|
41
|
+
violations.append(
|
|
42
|
+
(
|
|
43
|
+
f"String missing positional tokens: {string}",
|
|
44
|
+
string.language_code,
|
|
45
|
+
)
|
|
46
|
+
)
|
|
42
47
|
continue
|
|
43
48
|
|
|
44
49
|
position = int((token.split("$")[0]).replace("%", ""))
|
|
45
50
|
|
|
46
51
|
if position in positional_tokens:
|
|
47
|
-
violations.append(f"Duplicate token position: {string}")
|
|
52
|
+
violations.append((f"Duplicate token position: {string}", string.language_code))
|
|
48
53
|
continue
|
|
49
54
|
|
|
50
55
|
positional_tokens.add(position)
|
|
51
56
|
|
|
52
57
|
for i in range(1, len(tokens) + 1):
|
|
53
58
|
if i not in positional_tokens:
|
|
54
|
-
violations.append(
|
|
59
|
+
violations.append(
|
|
60
|
+
(
|
|
61
|
+
f"Token position index skipped: {string}",
|
|
62
|
+
string.language_code,
|
|
63
|
+
)
|
|
64
|
+
)
|
|
55
65
|
|
|
56
66
|
return violations
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Utility types for tests."""
|
|
2
2
|
|
|
3
|
-
from typing import List, Optional
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
class TestResult:
|
|
7
5
|
"""A simple result type
|
|
@@ -12,9 +10,9 @@ class TestResult:
|
|
|
12
10
|
|
|
13
11
|
_success: bool
|
|
14
12
|
name: str
|
|
15
|
-
violations:
|
|
13
|
+
violations: list[tuple[str, str]] | None
|
|
16
14
|
|
|
17
|
-
def __init__(self, success: bool, name: str, violations:
|
|
15
|
+
def __init__(self, success: bool, name: str, violations: list[tuple[str, str]] | None):
|
|
18
16
|
self._success = success
|
|
19
17
|
self.name = name
|
|
20
18
|
self.violations = violations
|
|
@@ -37,7 +35,7 @@ class TestResult:
|
|
|
37
35
|
return TestResult(True, name, None)
|
|
38
36
|
|
|
39
37
|
@staticmethod
|
|
40
|
-
def failure(name: str, violations:
|
|
38
|
+
def failure(name: str, violations: list[tuple[str, str]] | None) -> "TestResult":
|
|
41
39
|
"""Create a new failure result.
|
|
42
40
|
|
|
43
41
|
:param name: The name of the test
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "localizationkit"
|
|
3
|
-
version = "
|
|
3
|
+
version = "3.0.1"
|
|
4
4
|
description = "String localization tests"
|
|
5
5
|
|
|
6
6
|
license = "MIT"
|
|
@@ -20,28 +20,28 @@ classifiers = [
|
|
|
20
20
|
'Development Status :: 3 - Alpha',
|
|
21
21
|
'Intended Audience :: Developers',
|
|
22
22
|
'Programming Language :: Python :: 3',
|
|
23
|
-
'Programming Language :: Python :: 3.7',
|
|
24
23
|
'Programming Language :: Python :: 3.8',
|
|
25
24
|
'Programming Language :: Python :: 3.9',
|
|
26
25
|
'Programming Language :: Python :: 3.10',
|
|
26
|
+
'Programming Language :: Python :: 3.11',
|
|
27
27
|
'Topic :: Software Development',
|
|
28
28
|
'Topic :: Software Development :: Testing',
|
|
29
29
|
'Topic :: Utilities'
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[tool.poetry.dependencies]
|
|
33
|
-
python = "^3.
|
|
34
|
-
toml = "^0.10.
|
|
33
|
+
python = "^3.11"
|
|
34
|
+
toml = "^0.10.2"
|
|
35
35
|
|
|
36
36
|
[tool.poetry.dev-dependencies]
|
|
37
|
-
black = "
|
|
38
|
-
mypy = "
|
|
39
|
-
pylint = "
|
|
40
|
-
pytest = "
|
|
41
|
-
pytest-cov = "
|
|
42
|
-
types-toml = "
|
|
37
|
+
black = "25.1.0"
|
|
38
|
+
mypy = "1.15.0"
|
|
39
|
+
pylint = "3.3.4"
|
|
40
|
+
pytest = "^8.1.1"
|
|
41
|
+
pytest-cov = "^4.1.0"
|
|
42
|
+
types-toml = "^0.10.8.7"
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
[build-system]
|
|
46
|
-
requires = ["poetry
|
|
47
|
-
build-backend = "poetry.masonry.api"
|
|
46
|
+
requires = ["poetry-core"]
|
|
47
|
+
build-backend = "poetry.core.masonry.api"
|
localizationkit-1.0.1/setup.py
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from setuptools import setup
|
|
3
|
-
|
|
4
|
-
packages = \
|
|
5
|
-
['localizationkit', 'localizationkit.tests']
|
|
6
|
-
|
|
7
|
-
package_data = \
|
|
8
|
-
{'': ['*']}
|
|
9
|
-
|
|
10
|
-
install_requires = \
|
|
11
|
-
['toml>=0.10.0,<0.11.0']
|
|
12
|
-
|
|
13
|
-
setup_kwargs = {
|
|
14
|
-
'name': 'localizationkit',
|
|
15
|
-
'version': '1.0.1',
|
|
16
|
-
'description': 'String localization tests',
|
|
17
|
-
'long_description': '# localizationkit\n\n`localizationkit` is a toolkit for ensuring that your localized strings are the best that they can be.\n\nIncluded are tests for various things such as:\n\n* Checking that all strings have comments\n* Checking that the comments don\'t just match the value\n* Check that tokens have position specifiers\n* Check that no invalid tokens are included\n\nwith lots more to come. \n\n## Getting started\n\n### Configuration\n\nTo use the library, first off, create a configuration file that is in the TOML format. Here\'s an example:\n\n```toml\ndefault_language = "en"\n\n[has_comments]\nminimum_comment_length = 25\nminimum_comment_words = 8\n\n[token_matching]\nallow_missing_defaults = true\n\n[token_position_identifiers]\nalways = false\n```\n\nThis configuration file sets that `en` is the default language (so this is the language that will be checked for comments, etc. and all tests will run relative to it). Then it sets various settings for each test. Every instance of `[something_here]` specifies that the following settings are for that test. For example, the test `has_comments` will now make sure that not only are there comments, but that they are at least 25 characters in length and 8 words in length. \n\nYou can now load in your configuration:\n\n```python\nfrom localizationkit import Configuration\n\nconfiguration = Configuration.from_file("/path/to/config.toml")\n```\n\n### Localization Collections\n\nNow we need to prepare the strings that will go in. Here\'s how you can create an individual string:\n\n```python\nfrom localizationkit import LocalizedString\n\nmy_string = LocalizedString("My string\'s key", "My string\'s value", "My string\'s comment", "en")\n```\n\nThis creates a single string with a key, value and comment, with its language code set to `en`. Once you\'ve created some more (usually for different languages too), you can bundle them into a collection:\n\n```python\nfrom localizationkit import LocalizedCollection\n\ncollection = LocalizedCollection(list_of_my_strings)\n```\n\n### Running the tests\n\nAt this point, you are ready to run the tests:\n\n```python\nimport localizationkit\n\nresults = localizationkit.run_tests(configuration, collection)\n\nfor result in results:\n if not result.succeeded():\n print("The following test failed:", result.name)\n print("Failures encountered:")\n for violation in result.violations:\n print(violation)\n```\n\n### Not running the tests\n\nSome tests don\'t make sense for everyone. To skip a test you can add the following to your config file at the root:\n\n```toml\nblacklist = ["test_identifier_1", "test_identifier_2"]\n```\n\n# Rule documentation\n\nMost tests have configurable rules. If a rule is not specified, it will use the default instead.\n\nSome tests are opt in only. These will be marked as such.\n\n## Comment Linebreaks\n\nIdentifier: `comment_linebreaks`\nOpt-In: `true`\n\nChecks that comments for strings do not contain linebreaks. Comments which contain linebreaks can interfere with parsing in other tools such as [dotstrings](https://github.com/microsoft/dotstrings).\n\n## Comment Similarity\n\nIdentifier: `comment_similarity`\n\nChecks the similarity between a comment and the string\'s value in the default language. This is achieved via `difflib`\'s `SequenceMatcher`. More details can be found [here](https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher.ratio)\n\n<details>\n <summary>Configuration</summary>\n\n| Parameter | Type | Acceptable Values | Default | Details | \n| --- | --- | --- | --- | --- |\n| `maximum_similarity_ratio` | float | Between 0 and 1 | 0.5 | Set the maximum similarity ratio between the comment and the string value. The higher the value, the more similar they are. The longer the string the more accurate this will be. |\n\n</details>\n\n## Duplicate Keys\n\nIdentifier: `duplicate_keys`\n\nChecks that there are no duplicate keys in the collection.\n\n<details>\n <summary>Configuration</summary>\n\n| Parameter | Type | Acceptable Values | Default | Details | \n| --- | --- | --- | --- | --- |\n| `all_languages` | boolean | `true` or `false` | `false` | Set to `true` to check that every language has unique keys, not just the default language. |\n\n</details>\n\n## Has Comments\n\nIdentifier: `has_comments`\n\nChecks that strings have comments.\n\n_Note: Only languages that have Latin style scripts are really supported for the words check due to splitting on spaces to check._\n\n<details>\n <summary>Configuration</summary>\n\n| Parameter | Type | Acceptable Values | Default | Details | \n| --- | --- | --- | --- | --- |\n| `minimum_comment_length` | int | Any integer | 30 | Set the minimum allowable length for a comment. Set the value to negative to not check. |\n| `minimum_comment_words` | int | Any integer | 10 | Set the minimum allowable number of words for a comment. Set the value to negative to not check. |\n\n</details>\n\n## Has Value\n\nIdentifier: `has_value`\n\nChecks that strings have values. Since any value is enough for some strings, it simply makes sure that the string isn\'t None/null and isn\'t empty.\n\n<details>\n <summary>Configuration</summary>\n\n| Parameter | Type | Acceptable Values | Default | Details | \n| --- | --- | --- | --- | --- |\n| `default_language_only` | boolean | `true` or `false` | `false` | Set to true to only check the default language for missing values. Otherwise all languages will be checked. |\n\n</details>\n\n## Invalid Tokens\n\nIdentifier: `invalid_tokens`\n\nChecks that all format tokens in a string are valid.\n\n_Note: This check is not language specific. It only works very broadly._\n\n## Key Length\n\nIdentifier: `key_length`\n\nChecks the length of the keys.\n\n_Note: By default this test doesn\'t check anything. It needs to have parameters set to positive values to do anything._\n\n<details>\n <summary>Configuration</summary>\n\n| Parameter | Type | Acceptable Values | Default | Details | \n| --- | --- | --- | --- | --- |\n| `minimum` | int | Any integer | -1 | Set the minimum allowable length for a key. Set the value to negative to not check. |\n| `maximum` | int | Any integer | -1 | Set the maximum allowable length for a key. Set the value to negative to not check. |\n\n</details>\n\n## Objective-C Alternative Tokens\n\nIdentifier: `objectivec_alternative_tokens`\nOpt-In: `true`\n\nChecks that strings do not contain Objective-C style alternative position tokens.\n\nObjective-C seems to be allows positional tokens of the form `%1@` rather than `%1$@`. While not illegal, it is preferred that all tokens between languages are consistent so that tools don\'t experience unexpected failures, etc.\n\n## Placeholder token explanation\n\nIdentifier: `placeholder_token_explanation`\nOpt-In: `true`\n\nChecks that if a placeholder is used in a string, the comment explicitly explains what it is replaced with.\n\nPrecondition: Each placeholder in the string and its explanation in comment is expected to follow `token_position_identifiers` rule.\n\n## Swift Interpolation\n\nIdentifier: `swift_interpolation`\nOpt-In: `true`\n\nChecks that strings do not contain Swift style interpolation values since these cannot be localized.\n\n## Token Matching\n\nIdentifier: `token_matching`\n\nChecks that the tokens in a string match across all languages. e.g. If your English string is "Hello %s" but your French string is "Bonjour", this would flag that there is a missing token in the French string.\n\n<details>\n <summary>Configuration</summary>\n\n| Parameter | Type | Acceptable Values | Default | Details | \n| --- | --- | --- | --- | --- |\n| `allow_missing_defaults` | boolean | `true` or `false` | `false` | Due to the way that automated localization works, usually there will be a default language, and then other translations will come in over time. If a translation is deleted, it isn\'t always deleted from all languages immediately. Setting this parameter to true will allow any strings in your non-default language to be ignored if that string is missing from your default language. |\n\n</details>\n\n## Token Position Identifiers\n\nIdentifier: `token_position_identifiers`\n\nCheck that each token has a position specifier with it. e.g. `%s` is not allowed, but `%1$s` is. Tokens can move around in different languages, so position specifiers are extremely important.\n\n<details>\n <summary>Configuration</summary>\n\n| Parameter | Type | Acceptable Values | Default | Details | \n| --- | --- | --- | --- | --- |\n| `always` | boolean | `true` or `false` | `false` | If a string only has a single token, it doesn\'t need a position specifier. Set this to `true` to require it even in those cases.\n\n</details>\n\n# Contributing\n\nThis project welcomes contributions and suggestions. Most contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\n\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n',
|
|
18
|
-
'author': 'Dale Myers',
|
|
19
|
-
'author_email': 'dalemy@microsoft.com',
|
|
20
|
-
'maintainer': None,
|
|
21
|
-
'maintainer_email': None,
|
|
22
|
-
'url': 'https://github.com/Microsoft/localizationkit',
|
|
23
|
-
'packages': packages,
|
|
24
|
-
'package_data': package_data,
|
|
25
|
-
'install_requires': install_requires,
|
|
26
|
-
'python_requires': '>=3.7.2,<4.0.0',
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
setup(**setup_kwargs)
|
|
File without changes
|
|
File without changes
|