openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

108 lines
3.8 KiB

#!/usr/bin/env python3
import json
import os
import requests
import xml.etree.ElementTree as ET
from openpilot.common.basedir import BASEDIR
from openpilot.selfdrive.ui.update_translations import LANGUAGES_FILE, TRANSLATIONS_DIR
BADGE_HEIGHT = 20 + 8
SHIELDS_URL = "https://img.shields.io/badge"
def parse_po_file(file_path):
"""
Parse a .po file and count total and unfinished translations.
Returns: (total_translations, unfinished_translations)
"""
with open(file_path) as f:
content = f.read()
total_translations = 0
unfinished_translations = 0
# Split into entries (separated by blank lines)
entries = content.split('\n\n')
for entry in entries:
# Skip header entry (contains Project-Id-Version)
if 'Project-Id-Version' in entry:
continue
# Check if this entry has a msgid (translation entry)
# After skipping header, any entry with msgid " is a translation
# (both msgid "content" and msgid "" for multiline contain msgid ")
if 'msgid "' not in entry:
continue
total_translations += 1
# Check if msgstr is empty (unfinished translation)
if 'msgstr ""' in entry:
# Check if there are continuation lines with content after msgstr ""
lines = entry.split('\n')
msgstr_idx = None
for i, line in enumerate(lines):
if line.strip().startswith('msgstr ""'):
msgstr_idx = i
break
if msgstr_idx is not None:
# Check if any continuation lines have content
has_content = False
for line in lines[msgstr_idx + 1:]:
stripped = line.strip()
# Continuation line with content
if stripped.startswith('"') and len(stripped) > 2:
has_content = True
break
# End of entry
if stripped.startswith(('msgid', '#')) or not stripped:
break
if not has_content:
unfinished_translations += 1
return (total_translations, unfinished_translations)
if __name__ == "__main__":
with open(LANGUAGES_FILE) as f:
translation_files = json.load(f)
badge_svg = []
max_badge_width = 0 # keep track of max width to set parent element
for idx, (name, file) in enumerate(translation_files.items()):
po_file_path = os.path.join(str(TRANSLATIONS_DIR), f"app_{file}.po")
total_translations, unfinished_translations = parse_po_file(po_file_path)
percent_finished = int(100 - (unfinished_translations / total_translations * 100.)) if total_translations > 0 else 0
color = f"rgb{(94, 188, 0) if percent_finished == 100 else (248, 255, 50) if percent_finished > 90 else (204, 55, 27)}"
# Download badge
badge_label = f"LANGUAGE {name}"
badge_message = f"{percent_finished}% complete"
if unfinished_translations != 0:
badge_message += f" ({unfinished_translations} unfinished)"
r = requests.get(f"{SHIELDS_URL}/{badge_label}-{badge_message}-{color}", timeout=10)
assert r.status_code == 200, "Error downloading badge"
content_svg = r.content.decode("utf-8")
xml = ET.fromstring(content_svg)
assert "width" in xml.attrib
max_badge_width = max(max_badge_width, int(xml.attrib["width"]))
# Make tag ids in each badge unique to combine them into one svg
for tag in ("r", "s"):
content_svg = content_svg.replace(f'id="{tag}"', f'id="{tag}{idx}"')
content_svg = content_svg.replace(f'"url(#{tag})"', f'"url(#{tag}{idx})"')
badge_svg.extend([f'<g transform="translate(0, {idx * BADGE_HEIGHT})">', content_svg, "</g>"])
badge_svg.insert(0, '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
f'height="{len(translation_files) * BADGE_HEIGHT}" width="{max_badge_width}">')
badge_svg.append("</svg>")
with open(os.path.join(BASEDIR, "translation_badge.svg"), "w") as badge_f:
badge_f.write("\n".join(badge_svg))