diff --git a/quickshell/translations/en.json b/quickshell/translations/en.json index 066df9fb..fbc79f21 100644 --- a/quickshell/translations/en.json +++ b/quickshell/translations/en.json @@ -6773,6 +6773,12 @@ "reference": "Modules/Settings/DesktopWidgetBrowser.qml:459", "comment": "" }, + { + "term": "No widgets added. Click \"Add Widget\" to get started.", + "context": "No widgets added. Click \"Add Widget\" to get started.", + "reference": "Modules/Settings/DesktopWidgetsTab.qml:603", + "comment": "" + }, { "term": "No widgets match your search", "context": "No widgets match your search", diff --git a/quickshell/translations/extract_translations.py b/quickshell/translations/extract_translations.py index 0a100af7..64753a6b 100755 --- a/quickshell/translations/extract_translations.py +++ b/quickshell/translations/extract_translations.py @@ -1,44 +1,74 @@ #!/usr/bin/env python3 -import os +import ast import re import json from pathlib import Path from collections import defaultdict + +def decode_string_literal(content, quote): + try: + return ast.literal_eval(f"{quote}{content}{quote}") + except (ValueError, SyntaxError): + return content + + +def spans_overlap(a, b): + return a[0] < b[1] and b[0] < a[1] + + def extract_qstr_strings(root_dir): translations = defaultdict(lambda: {'contexts': set(), 'occurrences': []}) - qstr_pattern_double = re.compile(r'qsTr\("([^"]+)"\)') - qstr_pattern_single = re.compile(r"qsTr\('([^']+)'\)") - i18n_pattern_with_context_double = re.compile(r'I18n\.tr\("([^"]+)"\s*,\s*"([^"]+)"\)') - i18n_pattern_with_context_single = re.compile(r"I18n\.tr\('([^']+)'\s*,\s*'([^']+)'\)") - i18n_pattern_simple_double = re.compile(r'I18n\.tr\("([^"]+)"\)') - i18n_pattern_simple_single = re.compile(r"I18n\.tr\('([^']+)'\)") + qstr_patterns = [ + (re.compile(r'qsTr\(\s*"((?:\\.|[^"\\])*)"\s*\)'), '"'), + (re.compile(r"qsTr\(\s*'((?:\\.|[^'\\])*)'\s*\)"), "'") + ] + i18n_context_patterns = [ + ( + re.compile(r'I18n\.tr\(\s*"((?:\\.|[^"\\])*)"\s*,\s*"((?:\\.|[^"\\])*)"\s*\)'), + '"' + ), + ( + re.compile(r"I18n\.tr\(\s*'((?:\\.|[^'\\])*)'\s*,\s*'((?:\\.|[^'\\])*)'\s*\)"), + "'" + ) + ] + i18n_simple_patterns = [ + (re.compile(r'I18n\.tr\(\s*"((?:\\.|[^"\\])*)"\s*\)'), '"'), + (re.compile(r"I18n\.tr\(\s*'((?:\\.|[^'\\])*)'\s*\)"), "'") + ] for qml_file in Path(root_dir).rglob('*.qml'): relative_path = qml_file.relative_to(root_dir) with open(qml_file, 'r', encoding='utf-8') as f: for line_num, line in enumerate(f, 1): - qstr_matches = qstr_pattern_double.findall(line) + qstr_pattern_single.findall(line) - for match in qstr_matches: - translations[match]['occurrences'].append({ - 'file': str(relative_path), - 'line': line_num - }) + for pattern, quote in qstr_patterns: + for match in pattern.finditer(line): + term = decode_string_literal(match.group(1), quote) + translations[term]['occurrences'].append({ + 'file': str(relative_path), + 'line': line_num + }) - i18n_with_context = i18n_pattern_with_context_double.findall(line) + i18n_pattern_with_context_single.findall(line) - for term, context in i18n_with_context: - translations[term]['contexts'].add(context) - translations[term]['occurrences'].append({ - 'file': str(relative_path), - 'line': line_num - }) + context_spans = [] + for pattern, quote in i18n_context_patterns: + for match in pattern.finditer(line): + term = decode_string_literal(match.group(1), quote) + context = decode_string_literal(match.group(2), quote) + translations[term]['contexts'].add(context) + translations[term]['occurrences'].append({ + 'file': str(relative_path), + 'line': line_num + }) + context_spans.append(match.span()) - has_context = i18n_pattern_with_context_double.search(line) or i18n_pattern_with_context_single.search(line) - if not has_context: - i18n_simple = i18n_pattern_simple_double.findall(line) + i18n_pattern_simple_single.findall(line) - for match in i18n_simple: - translations[match]['occurrences'].append({ + for pattern, quote in i18n_simple_patterns: + for match in pattern.finditer(line): + if any(spans_overlap(match.span(), span) for span in context_spans): + continue + term = decode_string_literal(match.group(1), quote) + translations[term]['occurrences'].append({ 'file': str(relative_path), 'line': line_num }) diff --git a/quickshell/translations/poexports/pt.json b/quickshell/translations/poexports/pt.json index 366c233b..e908dfcb 100644 --- a/quickshell/translations/poexports/pt.json +++ b/quickshell/translations/poexports/pt.json @@ -3231,6 +3231,9 @@ "No variants created. Click Add to create a new monitor widget.": { "No variants created. Click Add to create a new monitor widget.": "Nenhuma variante criada. Clique Adicionar para criar um novo widget de monitor." }, + "No widgets added. Click \"Add Widget\" to get started.": { + "No widgets added. Click \"Add Widget\" to get started.": "Nenhum widget adicionado. Clique em \"Adicionar widget\" para começar." + }, "No widgets available": { "No widgets available": "" }, @@ -3521,7 +3524,7 @@ "Make sure KDE Connect or Valent is running on your other devices": "" }, "Phone Connect no devices status | bluetooth status": { - "No devices": "" + "No devices": "Nenhum dispositivo" }, "Phone Connect pairing action": { "Device paired": "", @@ -5297,7 +5300,7 @@ "Charging": "", "Discharging": "", "Empty": "", - "Fully Charged": "", + "Fully Charged": "Totalmente carregada", "No Battery": "", "Pending Charge": "", "Pending Discharge": "", @@ -5306,13 +5309,15 @@ "bluetooth status": { "Bluetooth": "", "Connected Device": "", - "Enabled": "", + "Disabled": "Desativado", + "Enabled": "Ativado", "No adapter": "", "No adapters": "", + "No devices": "Nenhum dispositivo", "Off": "" }, "bluetooth status | lock screen notification mode option": { - "Disabled": "" + "Disabled": "Desativado" }, "border color": { "Color": "" @@ -5364,16 +5369,16 @@ "Select Wallpaper": "Selecionar Papel de Parede" }, "date format option": { - "Custom...": "", - "Day Date": "", - "Day Month Date": "", - "Full Day & Month": "", - "Full with Year": "", - "ISO Date": "", - "Month Date": "", - "Numeric (D/M)": "", - "Numeric (M/D)": "", - "System Default": "" + "Custom...": "Personalizado...", + "Day Date": "Dia e data", + "Day Month Date": "Dia, mês e data", + "Full Day & Month": "Dia completo e mês", + "Full with Year": "Completo com ano", + "ISO Date": "Data ISO", + "Month Date": "Mês e data", + "Numeric (D/M)": "Numérico (D/M)", + "Numeric (M/D)": "Numérico (M/D)", + "System Default": "Padrão do sistema" }, "days": { "days": "dias" @@ -5406,8 +5411,8 @@ "dms/outputs config exists but is not included in your compositor config. Display changes won't persist.": "" }, "dock indicator style option": { - "Circle": "", - "Line": "" + "Circle": "Círculo", + "Line": "Linha" }, "dock position option": { "Bottom": "", @@ -5662,15 +5667,15 @@ "loginctl not available - lock integration requires DMS socket connection": "loginctl não disponível - integração com bloqueio requer conexão de socket DMS" }, "matugen color scheme option": { - "Content": "", - "Expressive": "", - "Fidelity": "", - "Fruit Salad": "", - "Monochrome": "", - "Neutral": "", - "Rainbow": "", - "Tonal Spot": "", - "Vibrant": "" + "Content": "Conteúdo", + "Expressive": "Expressivo", + "Fidelity": "Fidelidade", + "Fruit Salad": "Salada de frutas", + "Monochrome": "Monocromático", + "Neutral": "Neutro", + "Rainbow": "Arco-íris", + "Tonal Spot": "Ponto tonal", + "Vibrant": "Vibrante" }, "matugen error": { "matugen not found - install matugen package for dynamic theming": "" @@ -5682,9 +5687,9 @@ "Matugen Missing": "" }, "media scroll wheel option": { - "Change Song": "", - "Change Volume": "", - "Nothing": "" + "Change Song": "Trocar música", + "Change Volume": "Alterar volume", + "Nothing": "Nada" }, "minutes": { "minutes": "minutos" @@ -5792,9 +5797,9 @@ "Prioritize performance": "" }, "power profile option": { - "Balanced": "", - "Performance": "", - "Power Saver": "" + "Balanced": "Equilibrado", + "Performance": "Desempenho", + "Power Saver": "Economia de energia" }, "primary color": { "Primary": "" @@ -5818,14 +5823,14 @@ "Color theme from DMS registry": "" }, "screen position option": { - "Bottom Center": "", - "Bottom Left": "", - "Bottom Right": "", - "Left Center": "", - "Right Center": "", - "Top Center": "", - "Top Left": "", - "Top Right": "" + "Bottom Center": "Inferior central", + "Bottom Left": "Inferior esquerdo", + "Bottom Right": "Inferior direito", + "Left Center": "Centro esquerdo", + "Right Center": "Centro direito", + "Top Center": "Superior central", + "Top Left": "Superior esquerdo", + "Top Right": "Superior direito" }, "secondary color": { "Secondary": "" @@ -5872,10 +5877,10 @@ "Install color themes from the DMS theme registry": "" }, "theme category option": { - "Auto": "", - "Browse": "", - "Custom": "", - "Generic": "" + "Auto": "Automático", + "Browse": "Navegar", + "Custom": "Personalizado", + "Generic": "Genérico" }, "theme installation confirmation": { "Install theme '%1' from the DMS registry?": "" @@ -5924,13 +5929,13 @@ "Wallpaper Error": "" }, "wallpaper fill mode": { - "Fill": "", - "Fit": "", - "Pad": "", - "Stretch": "", - "Tile": "", - "Tile H": "", - "Tile V": "" + "Fill": "Preencher", + "Fit": "Ajustar", + "Pad": "Centralizar", + "Stretch": "Esticar", + "Tile": "Lado a lado", + "Tile H": "Lado a lado (H)", + "Tile V": "Lado a lado (V)" }, "wallpaper processing error": { "Wallpaper processing failed": "" @@ -5945,15 +5950,15 @@ "External Wallpaper Management": "Gerenciamento Externo de Papéis de Parede" }, "wallpaper transition option": { - "Disc": "", - "Fade": "", - "Iris Bloom": "", - "None": "", - "Pixelate": "", - "Portal": "", - "Random": "", - "Stripes": "", - "Wipe": "" + "Disc": "Disco", + "Fade": "Desvanecer", + "Iris Bloom": "Íris em expansão", + "None": "Nenhum", + "Pixelate": "Pixelizar", + "Portal": "Portal", + "Random": "Aleatório", + "Stripes": "Listras", + "Wipe": "Deslizar" }, "weather feels like temperature": { "Feels Like %1°": "" diff --git a/quickshell/translations/template.json b/quickshell/translations/template.json index a8362a9e..c2b3edf4 100644 --- a/quickshell/translations/template.json +++ b/quickshell/translations/template.json @@ -7902,6 +7902,13 @@ "reference": "", "comment": "" }, + { + "term": "No widgets added. Click \"Add Widget\" to get started.", + "translation": "", + "context": "", + "reference": "", + "comment": "" + }, { "term": "No widgets match your search", "translation": "",