Skip to content

Commit e670687

Browse files
committed
ci: check that locize placeholders are the same in all languages
Translators sometimes accidentally translate `{{placeholder}}` variables, like "name"=>"nombre", breaking the translation. This script checks that for each key in English (reference lang), all translations contain the same placeholders.
1 parent d7187c6 commit e670687

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

scripts/check-locize-placeholders.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2024 Shift Crypto AG
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# Generated by ChatGPT-4o.
17+
18+
import os
19+
import json
20+
import re
21+
import sys
22+
23+
def load_json(file_path):
24+
with open(file_path, 'r', encoding='utf-8') as file:
25+
return json.load(file)
26+
27+
def find_placeholders(text):
28+
return re.findall(r'{{\s*[\w]+\s*}}', text)
29+
30+
def check_placeholders(en_placeholders, other_placeholders):
31+
return sorted(en_placeholders) == sorted(other_placeholders)
32+
33+
def extract_placeholders(data, path=''):
34+
placeholders = {}
35+
for key, value in data.items():
36+
current_path = f"{path}.{key}" if path else key
37+
if isinstance(value, dict):
38+
placeholders.update(extract_placeholders(value, current_path))
39+
elif isinstance(value, str):
40+
placeholders[current_path] = find_placeholders(value)
41+
return placeholders
42+
43+
def check_nested_keys(en_data, lang_data, path=''):
44+
issues = []
45+
for key in en_data:
46+
current_path = f"{path}.{key}" if path else key
47+
if key not in lang_data:
48+
#issues.append(f"Missing key '{current_path}' in translation.")
49+
pass
50+
elif isinstance(en_data[key], dict):
51+
if not isinstance(lang_data[key], dict):
52+
issues.append(f"Type mismatch at '{current_path}': expected dict, found {type(lang_data[key])}.")
53+
else:
54+
issues.extend(check_nested_keys(en_data[key], lang_data[key], current_path))
55+
elif isinstance(en_data[key], str):
56+
en_placeholders = find_placeholders(en_data[key])
57+
lang_placeholders = find_placeholders(lang_data[key])
58+
if not check_placeholders(en_placeholders, lang_placeholders):
59+
issues.append(f"Placeholder mismatch at '{current_path}': '{lang_data[key]}' - found {lang_placeholders}; expected {en_placeholders}")
60+
return issues
61+
62+
def main():
63+
base_path = 'frontends/web/src/locales'
64+
en_file_path = os.path.join(base_path, 'en/app.json')
65+
en_data = load_json(en_file_path)
66+
67+
issues_found = False
68+
for lang_dir in os.listdir(base_path):
69+
if lang_dir == 'en':
70+
continue
71+
lang_file_path = os.path.join(base_path, lang_dir, 'app.json')
72+
if not os.path.isfile(lang_file_path):
73+
print(f"Skipping {lang_dir}, no app.json found.")
74+
continue
75+
76+
lang_data = load_json(lang_file_path)
77+
issues = check_nested_keys(en_data, lang_data)
78+
if issues:
79+
issues_found = True
80+
print(f"Issues found in {lang_dir}:")
81+
for issue in issues:
82+
print(f" - {issue}")
83+
84+
if issues_found:
85+
return 1
86+
return 0
87+
88+
if __name__ == "__main__":
89+
sys.exit(main())

scripts/ci.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ if ! locize format frontends/web/src/locales --format json --dry true ; then
3434
echo "i18n files malformatted. Fix with: make locize-fix"
3535
exit 1
3636
fi
37+
38+
./scripts/check-locize-placeholders.py

0 commit comments

Comments
 (0)