localizationkit 3.1.0__tar.gz → 4.0.0__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-4.0.0/.azure/publish.yml +43 -0
- localizationkit-4.0.0/.github/dependabot.yml +19 -0
- localizationkit-4.0.0/.github/workflows/test.yml +78 -0
- localizationkit-4.0.0/.gitignore +13 -0
- localizationkit-4.0.0/.pre-commit-config.yaml +26 -0
- localizationkit-4.0.0/.python-version +1 -0
- localizationkit-4.0.0/.vscode/settings.json +15 -0
- localizationkit-4.0.0/CODE_OF_CONDUCT.md +9 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/PKG-INFO +10 -15
- localizationkit-4.0.0/SECURITY.md +35 -0
- localizationkit-4.0.0/SUPPORT.md +12 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/__init__.py +15 -13
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/configuration.py +4 -5
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/localization_types.py +4 -7
- localizationkit-4.0.0/localizationkit/py.typed +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/comment_linebreaks.py +0 -1
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/duplicate_keys.py +1 -6
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/has_comments.py +1 -5
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/has_value.py +1 -3
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/invalid_tokens.py +9 -2
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/key_length.py +0 -1
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/objectivec_alternative_tokens.py +0 -1
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/swift_interpolation.py +0 -1
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/test_case.py +1 -3
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/token_adjacency.py +12 -14
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/token_matching.py +1 -3
- localizationkit-4.0.0/pyproject.toml +75 -0
- localizationkit-4.0.0/stylecheck.sh +7 -0
- localizationkit-4.0.0/test.sh +5 -0
- localizationkit-4.0.0/tests/__init__.py +0 -0
- localizationkit-4.0.0/tests/config.toml +18 -0
- localizationkit-4.0.0/tests/conftest.py +18 -0
- localizationkit-4.0.0/tests/test_base_calls.py +15 -0
- localizationkit-4.0.0/tests/test_comment_linebreaks.py +44 -0
- localizationkit-4.0.0/tests/test_comment_similarity.py +16 -0
- localizationkit-4.0.0/tests/test_duplicate_keys.py +44 -0
- localizationkit-4.0.0/tests/test_has_comments.py +32 -0
- localizationkit-4.0.0/tests/test_has_value.py +29 -0
- localizationkit-4.0.0/tests/test_invalid_tokens.py +42 -0
- localizationkit-4.0.0/tests/test_key_length.py +35 -0
- localizationkit-4.0.0/tests/test_objectivec_alternative_tokens.py +33 -0
- localizationkit-4.0.0/tests/test_placeholder_token_explanation.py +65 -0
- localizationkit-4.0.0/tests/test_position_identifiers.py +49 -0
- localizationkit-4.0.0/tests/test_swift_interpolation.py +38 -0
- localizationkit-4.0.0/tests/test_token_adjacency.py +48 -0
- localizationkit-4.0.0/tests/test_token_matching.py +70 -0
- localizationkit-4.0.0/uv.lock +621 -0
- localizationkit-3.1.0/pyproject.toml +0 -45
- {localizationkit-3.1.0 → localizationkit-4.0.0}/LICENSE +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/README.md +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/exceptions.py +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/__init__.py +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/comment_similarity.py +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/placeholder_token_explanation.py +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/tests/token_position_identifiers.py +0 -0
- {localizationkit-3.1.0 → localizationkit-4.0.0}/localizationkit/utility_types.py +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
trigger:
|
|
2
|
+
tags:
|
|
3
|
+
include:
|
|
4
|
+
- "*"
|
|
5
|
+
|
|
6
|
+
pool:
|
|
7
|
+
vmImage: "ubuntu-latest"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
- job: "Publish"
|
|
11
|
+
pool:
|
|
12
|
+
vmImage: "ubuntu-latest"
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- task: UsePythonVersion@0
|
|
16
|
+
inputs:
|
|
17
|
+
versionSpec: "3.x"
|
|
18
|
+
|
|
19
|
+
- script: |
|
|
20
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
21
|
+
echo "##vso[task.prependpath]$HOME/.local/bin"
|
|
22
|
+
displayName: "Install uv"
|
|
23
|
+
|
|
24
|
+
- script: |
|
|
25
|
+
# hatch-vcs derives the version from git tags, so full history is required.
|
|
26
|
+
git fetch --tags --unshallow || git fetch --tags
|
|
27
|
+
displayName: "Fetch tags"
|
|
28
|
+
|
|
29
|
+
- script: |
|
|
30
|
+
uv build
|
|
31
|
+
displayName: "Build"
|
|
32
|
+
|
|
33
|
+
- task: EsrpRelease@2
|
|
34
|
+
inputs:
|
|
35
|
+
ConnectedServiceName: "OM-ESRP-Release-Publishing"
|
|
36
|
+
Intent: "PackageDistribution"
|
|
37
|
+
ContentType: "PyPi"
|
|
38
|
+
PackageLocation: "dist"
|
|
39
|
+
Owners: "jizhen@microsoft.com"
|
|
40
|
+
Approvers: "jizhen@microsoft.com"
|
|
41
|
+
ServiceEndpointUrl: "https://api.esrp.microsoft.com"
|
|
42
|
+
MainPublisher: "OutlookMobileiOSESRPPublisher"
|
|
43
|
+
DomainTenantId: "72f988bf-86f1-41af-91ab-2d7cd011db47"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "uv"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
groups:
|
|
8
|
+
python-dependencies:
|
|
9
|
+
patterns:
|
|
10
|
+
- "*"
|
|
11
|
+
|
|
12
|
+
- package-ecosystem: "github-actions"
|
|
13
|
+
directory: "/"
|
|
14
|
+
schedule:
|
|
15
|
+
interval: "weekly"
|
|
16
|
+
groups:
|
|
17
|
+
github-actions:
|
|
18
|
+
patterns:
|
|
19
|
+
- "*"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- master
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
python-version:
|
|
24
|
+
- "3.11"
|
|
25
|
+
- "3.12"
|
|
26
|
+
- "3.13"
|
|
27
|
+
- "3.14"
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Install uv
|
|
33
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
34
|
+
with:
|
|
35
|
+
enable-cache: true
|
|
36
|
+
python-version: ${{ matrix.python-version }}
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: uv sync
|
|
40
|
+
|
|
41
|
+
- name: Lint
|
|
42
|
+
run: |
|
|
43
|
+
uv run ruff check localizationkit tests
|
|
44
|
+
uv run ruff format --check localizationkit tests
|
|
45
|
+
|
|
46
|
+
- name: Type Check
|
|
47
|
+
run: uv run mypy localizationkit/ tests/
|
|
48
|
+
|
|
49
|
+
- name: Test
|
|
50
|
+
run: uv run pytest tests --cov=localizationkit --cov-report xml --doctest-modules --junitxml=junit/test-results.xml
|
|
51
|
+
|
|
52
|
+
- name: Upload coverage report
|
|
53
|
+
if: always()
|
|
54
|
+
uses: actions/upload-artifact@v4
|
|
55
|
+
with:
|
|
56
|
+
name: coverage-${{ matrix.python-version }}
|
|
57
|
+
path: coverage.xml
|
|
58
|
+
|
|
59
|
+
- name: Upload test results
|
|
60
|
+
if: always()
|
|
61
|
+
uses: actions/upload-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: test-results-${{ matrix.python-version }}
|
|
64
|
+
path: junit/test-results.xml
|
|
65
|
+
|
|
66
|
+
test-result:
|
|
67
|
+
name: test-result
|
|
68
|
+
if: always()
|
|
69
|
+
needs: test
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
- name: Verify all matrix jobs passed
|
|
73
|
+
run: |
|
|
74
|
+
if [ "${{ needs.test.result }}" != "success" ]; then
|
|
75
|
+
echo "One or more test matrix jobs failed."
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
echo "All test matrix jobs passed."
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
rev: v0.15.20
|
|
14
|
+
hooks:
|
|
15
|
+
- id: ruff-check
|
|
16
|
+
args: [--fix]
|
|
17
|
+
- id: ruff-format
|
|
18
|
+
|
|
19
|
+
- repo: local
|
|
20
|
+
hooks:
|
|
21
|
+
- id: mypy
|
|
22
|
+
name: mypy
|
|
23
|
+
entry: uv run mypy localizationkit/ tests/
|
|
24
|
+
language: system
|
|
25
|
+
types: [python]
|
|
26
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"python.testing.pytestArgs": [
|
|
3
|
+
"tests"
|
|
4
|
+
],
|
|
5
|
+
"python.testing.unittestEnabled": false,
|
|
6
|
+
"python.testing.pytestEnabled": true,
|
|
7
|
+
"editor.formatOnSave": true,
|
|
8
|
+
"[python]": {
|
|
9
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
10
|
+
"editor.codeActionsOnSave": {
|
|
11
|
+
"source.fixAll.ruff": "explicit",
|
|
12
|
+
"source.organizeImports.ruff": "explicit"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Microsoft Open Source Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
|
4
|
+
|
|
5
|
+
Resources:
|
|
6
|
+
|
|
7
|
+
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
|
8
|
+
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
|
9
|
+
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: localizationkit
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: String localization tests
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Project-URL: homepage, https://github.com/Microsoft/localizationkit
|
|
6
|
+
Project-URL: repository, https://github.com/Microsoft/localizationkit
|
|
7
|
+
Author-email: Dale Myers <dalemy@microsoft.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
7
10
|
Keywords: localization,strings,tests
|
|
8
|
-
|
|
9
|
-
Author-email: dalemy@microsoft.com
|
|
10
|
-
Requires-Python: >=3.11,<4.0
|
|
11
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
13
|
Classifier: Programming Language :: Python :: 3
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
18
|
Classifier: Topic :: Software Development
|
|
22
19
|
Classifier: Topic :: Software Development :: Testing
|
|
23
20
|
Classifier: Topic :: Utilities
|
|
24
|
-
Requires-
|
|
25
|
-
Project-URL: Repository, https://github.com/Microsoft/localizationkit
|
|
21
|
+
Requires-Python: <4.0,>=3.11
|
|
26
22
|
Description-Content-Type: text/markdown
|
|
27
23
|
|
|
28
24
|
# localizationkit
|
|
@@ -299,4 +295,3 @@ provided by the bot. You will only need to do this once across all repos using o
|
|
|
299
295
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
|
300
296
|
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
|
301
297
|
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
|
302
|
-
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.1 BLOCK -->
|
|
2
|
+
|
|
3
|
+
## Security
|
|
4
|
+
|
|
5
|
+
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
|
|
6
|
+
|
|
7
|
+
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below.
|
|
8
|
+
|
|
9
|
+
## Reporting Security Issues
|
|
10
|
+
|
|
11
|
+
**Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155).
|
|
12
|
+
|
|
13
|
+
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
|
14
|
+
|
|
15
|
+
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
|
16
|
+
|
|
17
|
+
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
|
18
|
+
* Full paths of source file(s) related to the manifestation of the issue
|
|
19
|
+
* The location of the affected source code (tag/branch/commit or direct URL)
|
|
20
|
+
* Any special configuration required to reproduce the issue
|
|
21
|
+
* Step-by-step instructions to reproduce the issue
|
|
22
|
+
* Proof-of-concept or exploit code (if possible)
|
|
23
|
+
* Impact of the issue, including how an attacker might exploit the issue
|
|
24
|
+
|
|
25
|
+
This information will help us triage your report more quickly.
|
|
26
|
+
|
|
27
|
+
## Preferred Languages
|
|
28
|
+
|
|
29
|
+
We prefer all communications to be in English.
|
|
30
|
+
|
|
31
|
+
## Policy
|
|
32
|
+
|
|
33
|
+
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
|
34
|
+
|
|
35
|
+
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Support
|
|
2
|
+
|
|
3
|
+
## How to file issues
|
|
4
|
+
|
|
5
|
+
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
|
|
6
|
+
issues before filing new issues to avoid duplicates. For new issues, file your bug or
|
|
7
|
+
feature request as a new Issue.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Microsoft Support Policy
|
|
11
|
+
|
|
12
|
+
Support for this tool is limited to the resources listed above.
|
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
"""Toolkit for validation of localized strings."""
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
-
from typing import Type
|
|
5
4
|
|
|
5
|
+
from localizationkit import tests
|
|
6
6
|
from localizationkit.configuration import Configuration
|
|
7
7
|
from localizationkit.exceptions import LocalizationKitException
|
|
8
8
|
from localizationkit.localization_types import LocalizedCollection, LocalizedString
|
|
9
|
-
from localizationkit.utility_types import TestResult
|
|
10
9
|
from localizationkit.tests.test_case import LocalizationTestCase
|
|
10
|
+
from localizationkit.utility_types import TestResult
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Configuration",
|
|
14
|
+
"LocalizationKitException",
|
|
15
|
+
"LocalizationTestCase",
|
|
16
|
+
"LocalizedCollection",
|
|
17
|
+
"LocalizedString",
|
|
18
|
+
"TestResult",
|
|
19
|
+
"run_tests",
|
|
20
|
+
]
|
|
13
21
|
|
|
14
22
|
|
|
15
|
-
def _find_tests() -> list[
|
|
23
|
+
def _find_tests() -> list[type[LocalizationTestCase]]:
|
|
16
24
|
"""Find all the tests."""
|
|
17
25
|
|
|
18
|
-
test_module_names = [
|
|
19
|
-
module_name for module_name in dir(tests) if not module_name.startswith("__")
|
|
20
|
-
]
|
|
26
|
+
test_module_names = [module_name for module_name in dir(tests) if not module_name.startswith("__")]
|
|
21
27
|
test_modules = [getattr(tests, name) for name in test_module_names]
|
|
22
28
|
|
|
23
29
|
names_seen: set[str] = set()
|
|
24
|
-
test_classes: list[
|
|
30
|
+
test_classes: list[type[LocalizationTestCase]] = []
|
|
25
31
|
|
|
26
32
|
for module in test_modules:
|
|
27
|
-
|
|
28
33
|
for reference_name in dir(module):
|
|
29
|
-
|
|
30
34
|
reference = getattr(module, reference_name)
|
|
31
35
|
|
|
32
36
|
if not inspect.isclass(reference):
|
|
@@ -39,9 +43,7 @@ def _find_tests() -> list[Type[LocalizationTestCase]]:
|
|
|
39
43
|
continue
|
|
40
44
|
|
|
41
45
|
if reference.__name__ in names_seen:
|
|
42
|
-
raise LocalizationKitException(
|
|
43
|
-
"At least 2 classes exist with the same name: " + reference.__name__
|
|
44
|
-
)
|
|
46
|
+
raise LocalizationKitException("At least 2 classes exist with the same name: " + reference.__name__)
|
|
45
47
|
|
|
46
48
|
test_classes.append(reference)
|
|
47
49
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"""Configuration for localization tests."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import toml
|
|
3
|
+
import tomllib
|
|
4
|
+
from typing import Any
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class Configuration:
|
|
@@ -54,5 +53,5 @@ class Configuration:
|
|
|
54
53
|
:returns: The parsed configuration object
|
|
55
54
|
"""
|
|
56
55
|
|
|
57
|
-
with open(file_path,
|
|
58
|
-
return Configuration(
|
|
56
|
+
with open(file_path, "rb") as config_file:
|
|
57
|
+
return Configuration(tomllib.load(config_file))
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Types for localization."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
-
from
|
|
4
|
+
from re import Pattern
|
|
5
|
+
from typing import ClassVar
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class LocalizedString:
|
|
@@ -13,9 +14,7 @@ class LocalizedString:
|
|
|
13
14
|
:param language_code: The language code for the language the string is in
|
|
14
15
|
"""
|
|
15
16
|
|
|
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
|
-
)
|
|
17
|
+
_TOKEN_REGEX: ClassVar[str] = r"(%(?:[0-9]+\$)?[0-9]*\.?[0-9]*(?:h|hh|l|ll|q|L|z|t|j){0,2}[dDuUxXoOfFeEgGcCsSaAp@])"
|
|
19
18
|
_TOKEN_PATTERN: ClassVar[Pattern] = re.compile(_TOKEN_REGEX, flags=re.DOTALL)
|
|
20
19
|
|
|
21
20
|
key: str
|
|
@@ -92,9 +91,7 @@ class LocalizedCollection:
|
|
|
92
91
|
|
|
93
92
|
:returns: The list of strings with the matching language code
|
|
94
93
|
"""
|
|
95
|
-
return [
|
|
96
|
-
string for string in self.localized_strings if string.language_code == language_code
|
|
97
|
-
]
|
|
94
|
+
return [string for string in self.localized_strings if string.language_code == language_code]
|
|
98
95
|
|
|
99
96
|
def languages(self) -> list[str]:
|
|
100
97
|
"""Return the list of languages in the collection.
|
|
File without changes
|
|
@@ -22,17 +22,12 @@ class DuplicateKeys(LocalizationTestCase):
|
|
|
22
22
|
|
|
23
23
|
all_languages = self.get_setting("all_languages")
|
|
24
24
|
|
|
25
|
-
if all_languages
|
|
26
|
-
languages_to_check = self.collection.languages()
|
|
27
|
-
else:
|
|
28
|
-
languages_to_check = [self.configuration.default_language()]
|
|
25
|
+
languages_to_check = self.collection.languages() if all_languages else [self.configuration.default_language()]
|
|
29
26
|
|
|
30
27
|
for language in languages_to_check:
|
|
31
|
-
|
|
32
28
|
keys: set[str] = set()
|
|
33
29
|
|
|
34
30
|
for string in self.collection.strings_for_language(language):
|
|
35
|
-
|
|
36
31
|
if string.key in keys:
|
|
37
32
|
violations.append(
|
|
38
33
|
(
|
|
@@ -24,7 +24,6 @@ class HasComments(LocalizationTestCase):
|
|
|
24
24
|
violations = []
|
|
25
25
|
|
|
26
26
|
for string in self.collection.strings_for_language(self.configuration.default_language()):
|
|
27
|
-
|
|
28
27
|
if string.comment is None:
|
|
29
28
|
violations.append((f"Comment was empty: {string}", string.language_code))
|
|
30
29
|
continue
|
|
@@ -38,10 +37,7 @@ class HasComments(LocalizationTestCase):
|
|
|
38
37
|
)
|
|
39
38
|
continue
|
|
40
39
|
|
|
41
|
-
if (
|
|
42
|
-
minimum_comment_words >= 0
|
|
43
|
-
and len(string.comment.split(" ")) < minimum_comment_words
|
|
44
|
-
):
|
|
40
|
+
if minimum_comment_words >= 0 and len(string.comment.split(" ")) < minimum_comment_words:
|
|
45
41
|
violations.append(
|
|
46
42
|
(
|
|
47
43
|
f"Comment did not meet minimum word count of {minimum_comment_words}: {string}",
|
|
@@ -21,9 +21,7 @@ class HasValue(LocalizationTestCase):
|
|
|
21
21
|
violations: list[tuple[str, str]] = []
|
|
22
22
|
|
|
23
23
|
if self.get_setting("default_language_only"):
|
|
24
|
-
test_collection = self.collection.strings_for_language(
|
|
25
|
-
self.configuration.default_language()
|
|
26
|
-
)
|
|
24
|
+
test_collection = self.collection.strings_for_language(self.configuration.default_language())
|
|
27
25
|
else:
|
|
28
26
|
test_collection = self.collection.localized_strings
|
|
29
27
|
|
|
@@ -21,10 +21,17 @@ class InvalidTokens(LocalizationTestCase):
|
|
|
21
21
|
|
|
22
22
|
violations = []
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# "%%" is the printf escape for a literal percent sign and is always
|
|
25
|
+
# valid, so consume it first in the alternation. The capturing group
|
|
26
|
+
# only matches a "%" that starts something which is *not* a valid
|
|
27
|
+
# conversion (i.e. is followed by a character other than a conversion
|
|
28
|
+
# flag/specifier, "@", "%", ".", alphanumeric, or space). Matching "%%"
|
|
29
|
+
# via the first branch leaves an empty capture group, which is filtered
|
|
30
|
+
# out below, so "50%%)" no longer trips on the trailing "%)".
|
|
31
|
+
invalid_token_pattern = re.compile(r"%%|(%[^@%\.a-zA-Z0-9 ]+)", flags=re.DOTALL)
|
|
25
32
|
|
|
26
33
|
for string in self.collection.localized_strings:
|
|
27
|
-
matches = invalid_token_pattern.findall(string.value)
|
|
34
|
+
matches = [match for match in invalid_token_pattern.findall(string.value) if match]
|
|
28
35
|
|
|
29
36
|
# Any matches are a bad thing
|
|
30
37
|
if matches and len(matches) > 0:
|
|
@@ -33,7 +33,6 @@ class ObjectivecAlternativeTokens(LocalizationTestCase):
|
|
|
33
33
|
pattern = re.compile(r"(%(?:[0-9]+)(?:\.[0-9]+)?[@a-z][a-z]?)", flags=re.DOTALL)
|
|
34
34
|
|
|
35
35
|
for string in self.collection.localized_strings:
|
|
36
|
-
|
|
37
36
|
if pattern.findall(string.value):
|
|
38
37
|
violations.append(
|
|
39
38
|
(
|
|
@@ -81,9 +81,7 @@ class LocalizationTestCase(abc.ABC):
|
|
|
81
81
|
violations = self.run_test()
|
|
82
82
|
|
|
83
83
|
if violations is None:
|
|
84
|
-
raise LocalizationKitException(
|
|
85
|
-
f"Failed to get exceptions from test: {self.__class__.name()}"
|
|
86
|
-
)
|
|
84
|
+
raise LocalizationKitException(f"Failed to get exceptions from test: {self.__class__.name()}")
|
|
87
85
|
|
|
88
86
|
if len(violations):
|
|
89
87
|
return TestResult.failure(self.__class__.name(), violations)
|
|
@@ -40,23 +40,21 @@ class TokenAdjacency(LocalizationTestCase):
|
|
|
40
40
|
|
|
41
41
|
for token, start, end in tokens:
|
|
42
42
|
preceding_char = string.value[start - 1] if start > 0 else None
|
|
43
|
-
if preceding_char:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
string.language_code,
|
|
49
|
-
)
|
|
43
|
+
if preceding_char and preceding_char not in allowed_characters:
|
|
44
|
+
violations.append(
|
|
45
|
+
(
|
|
46
|
+
f"Token '{token}' is directly preceded by '{preceding_char}' in string: {string}",
|
|
47
|
+
string.language_code,
|
|
50
48
|
)
|
|
49
|
+
)
|
|
51
50
|
|
|
52
51
|
succeeding_char = string.value[end] if end < len(string.value) else None
|
|
53
|
-
if succeeding_char:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
string.language_code,
|
|
59
|
-
)
|
|
52
|
+
if succeeding_char and succeeding_char not in allowed_characters:
|
|
53
|
+
violations.append(
|
|
54
|
+
(
|
|
55
|
+
f"Token '{token}' is directly followed by '{succeeding_char}' in string: {string}",
|
|
56
|
+
string.language_code,
|
|
60
57
|
)
|
|
58
|
+
)
|
|
61
59
|
|
|
62
60
|
return violations
|
|
@@ -28,13 +28,11 @@ class TokenMatching(LocalizationTestCase):
|
|
|
28
28
|
default_token_map[string.key] = sorted(string.tokens())
|
|
29
29
|
|
|
30
30
|
for language_code in self.collection.languages():
|
|
31
|
-
|
|
32
31
|
if language_code == self.configuration.default_language():
|
|
33
32
|
continue
|
|
34
33
|
|
|
35
34
|
for string in self.collection.strings_for_language(language_code):
|
|
36
|
-
|
|
37
|
-
default_tokens = default_token_map.get(string.key, None)
|
|
35
|
+
default_tokens = default_token_map.get(string.key)
|
|
38
36
|
|
|
39
37
|
# Sometimes a string can be removed from the default language
|
|
40
38
|
# before it is removed from other languages (due to automation)
|