yubioath-flutter/check_strings.py

134 lines
3.8 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
# Copyright (C) 2023 Yubico.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import os
import sys
non_words = (":",)
errors = []
def check_duplicate_keys(pairs):
seen = set()
for d in [k for k, v in pairs if k in seen or seen.add(k)]:
errors.append(f"Duplicate key: {d}")
return dict(pairs)
def check_duplicate_values(strings):
seen = {}
for k, v in strings.items():
if isinstance(v, str):
if v in seen:
errors.append(
f"Duplicate value in key: {k} (originally in {seen[v]}): {v}"
)
else:
seen[v] = k
def check_prefixes(k, v, s_max_words, s_max_len, p_ending_chars, q_ending_chars):
errs = []
if k.startswith("s_"):
if len(v) > s_max_len:
errs.append(f"Too long ({len(v)} chars)")
n_words = len([w for w in v.split() if w not in non_words])
if n_words > s_max_words:
errs.append(f"Too many words ({n_words})")
2023-03-02 16:20:47 +03:00
if k.startswith("l_") or k.startswith("s_"):
if v.endswith("."):
errs.append("Ends with '.'")
if ". " in v:
errs.append("Spans multiple sentences")
elif k.startswith("p_"):
if p_ending_chars and not any(v.endswith(p) for p in p_ending_chars):
errs.append("Doesn't end in punctuation")
elif k.startswith("q_"):
if q_ending_chars and not any(v.endswith(q) for q in q_ending_chars):
errs.append("Doesn't end in question mark.")
return errs
def check_misc(k, v):
errs = []
if "..." in v:
errs.append("'...' should be replaced with '\\u2026'")
2023-03-02 16:20:47 +03:00
if v[0].upper() != v[0]:
errs.append("Starts with lowercase letter")
return errs
def check_keys_exist_in_reference(reference_strings, checked_strings):
errs = []
for key in checked_strings.keys():
if key not in reference_strings:
errs.append(f"Invalid key: {key}")
return errs
def lint_strings(strings, rules):
for k, v in strings.items():
if v is None:
continue
errs = []
errs.extend(
check_prefixes(
k,
v,
rules.get("s_max_words", 4),
rules.get("s_max_length", 32),
rules.get("p_ending_chars", ".!"),
rules.get("q_ending_chars", "?"),
)
)
errs.extend(check_misc(k, v))
if errs:
errors.append(f'Errors in {k}: "{v}"')
errors.extend([f" {e}" for e in errs])
if len(sys.argv) != 2:
print("USAGE: check_strings.py <ARB_FILE>")
sys.exit(1)
target = sys.argv[1]
2023-11-30 17:15:59 +03:00
with open(target, encoding="utf-8") as f:
values = json.load(f, object_pairs_hook=check_duplicate_keys)
strings = {k: v for k, v in values.items() if not k.startswith("@")}
2023-03-02 16:20:47 +03:00
print(target, f"- checking {len(strings)} strings")
lint_strings(strings, values.get("@_lint_rules", {}))
2023-03-02 16:20:47 +03:00
check_duplicate_values(strings)
2023-11-30 17:15:59 +03:00
with open(os.path.join(os.path.dirname(target), "app_en.arb"), encoding="utf-8") as f:
reference_values = json.load(f)
errors.extend(check_keys_exist_in_reference(reference_values, values))
if errors:
2023-03-02 16:20:47 +03:00
print()
print(target, "HAS ERRORS:")
for e in errors:
print(e)
2023-03-02 16:20:47 +03:00
print()
sys.exit(1)
2023-03-02 16:20:47 +03:00
print(target, "OK")