client_v2: fall back empty locale values to base language

Weblate writes an empty string for every untranslated key. Paraglide
compiles a present-but-empty value into a message function that returns
"", so the site rendered a blank instead of falling back to English. A
key that is absent from a locale, by contrast, is re-exported from the
base locale and falls back correctly.

Add scripts/strip-empty-locales.mjs to delete empty/whitespace-only
leaves from the non-base locales/*/common.json before paraglide compiles
them, turning "present-but-empty" into "absent". Idempotent, base en
untouched, key order and formatting preserved. Wired as predev/prebuild
so builds stay correct if empties reappear from a later Weblate sync;
deliberately not a lint/CI gate, since empties are a benign expected
input to be neutralised, not forbidden.

Cleans 125 existing empties across 10 locales.

Claude-Session: https://claude.ai/code/session_014Cwk5Yyze9yPvkYkUtMAP4
This commit is contained in:
Donkie committed 2026-07-20 16:32:04 +02:00
1 parent 5b5e07e2c1
commit a34d52a597
12 files changed
+117 -125

No files matched your search

-57
View File
@@ -10,7 +10,6 @@
"buttons.clearFilters": "Εκκαθάριση Φίλτρων",
"buttons.clone": "Αντιγραφή",
"buttons.confirm": "Είστε σίγουρος;",
"buttons.continue": "",
"buttons.create": "Δημιουργία",
"buttons.delete": "Διαγραφή",
"buttons.edit": "Τροποποίηση",
@@ -36,7 +35,6 @@
"documentTitle.filament.show": "#{{id}} Προβολή Νήματος | Spoolman",
"documentTitle.help.list": "Βοήθεια | Spoolman",
"documentTitle.home.list": "Αρχική | Spoolman",
"documentTitle.locations.list": "",
"documentTitle.spool.clone": "#{{id}} Αντιγραφή Καρουλιού| Spoolman",
"documentTitle.spool.create": "Δημιουργία Καρουλιού| Spoolman",
"documentTitle.spool.edit": "#{{id}} Τροποποίηση Καρουλιού| Spoolman",
@@ -50,22 +48,17 @@
"documentTitle.vendor.show": "#{{id}} Προβολή Προμηθευτή | Spoolman",
"filament.buttons.addSpool": "Προσθήκη Καρουλιού",
"filament.fields.articleNumber": "Κωδικός Προϊόντος",
"filament.fields.coaxial": "",
"filament.fields.colorHex": "Χρώμα",
"filament.fields.comment": "Σχόλια",
"filament.fields.density": "Πυκνότητα",
"filament.fields.diameter": "Διάμετρος",
"filament.fields.externalId": "",
"filament.fields.id": "Α/Α",
"filament.fields.longitudinal": "",
"filament.fields.material": "Υλικό",
"filament.fields.multiColor": "",
"filament.fields.name": "Όνομα",
"filament.fields.price": "Τιμή",
"filament.fields.registered": "Ημερομηνία Εγγραφής",
"filament.fields.settingsBedTemp": "Θερμοκρασία Κρεβατιού",
"filament.fields.settingsExtruderTemp": "Θερμοκρασία Μύτης",
"filament.fields.singleColor": "",
"filament.fields.spoolWeight": "Βάρος Καρουλιού",
"filament.fields.spools": "Προβολή Καρουλιών",
"filament.fields.vendor": "Προμηθευτής",
@@ -73,15 +66,12 @@
"filament.fields.weight": "Βάρος",
"filament.fieldsHelp.articleNumber": "Π.χ. EAN, UPC, κλπ.",
"filament.fieldsHelp.material": "Π.χ. PLA, ABS, PETG, κλπ.",
"filament.fieldsHelp.multiColorDirection": "",
"filament.fieldsHelp.name": "Όνομα νήματος, για να διακρίνει αυτόν τον τύπο νήματος μεταξύ άλλων από τον ίδιο προμηθευτή. Θα πρέπει να περιέχει το χρώμα για παράδειγμα.",
"filament.fieldsHelp.price": "Τιμή ενός πλήρους καρουλιού.",
"filament.fieldsHelp.spoolWeight": "Το βάρος ενός άδειου καρουλιού. Χρησιμοποιείται για τον προσδιορισμό του μετρημένου βάρους ενός καρουλιού.",
"filament.fieldsHelp.weight": "Το βάρος του νήματος ενός πλήρους καρουλιού (καθαρό βάρος). Αυτό δεν πρέπει να περιλαμβάνει το βάρος του ίδιου του καρουλιού, μόνο το βάρος του νήματος. Είναι αυτό που συνήθως αναγράφεται στη συσκευασία.",
"filament.filament": "Νήματα",
"filament.form.filamentUpdated": "Αυτό το νήμα έχει ενημερωθεί από κάποιον/κάτι άλλο από τότε που ανοίξατε αυτήν τη σελίδα. Η αποθήκευση θα αντικαταστήσει αυτές τις αλλαγές!",
"filament.form.importExternal": "",
"filament.form.importExternalDescription": "",
"filament.titles.clone": "Αντιγραφή Νήματος",
"filament.titles.create": "Δημιουργία Νήματος",
"filament.titles.edit": "Επεξεργασία Νήματος",
@@ -93,15 +83,9 @@
"help.resources.filament": "Επωνυμίες νημάτων. Περιλαμβάνει ιδιότητες όπως όνομα, υλικό, χρώμα, διάμετρος και άλλα.",
"help.resources.spool": "Μεμονωμένα καρούλια ενός συγκεκριμένου νήματος.",
"help.resources.vendor": "Οι εταιρείες που φτιάχνουν το νήμα.",
"home.description": "",
"home.home": "Αρχική",
"home.welcome": "",
"kofi": "Φιλοδώρημα στο Ko-fi",
"loading": "Φόρτωση",
"locations.locations": "",
"locations.newLocation": "",
"locations.noLocation": "",
"locations.noLocationsHelp": "",
"no": "Όχι",
"notifications.createError": "Προέκυψε σφάλμα κατά τη δημιουργία του {{resource}} (status code: {{statusCode}})",
"notifications.createSuccess": "Δημιουργήθηκε επιτυχώς {{resource}}",
@@ -115,29 +99,22 @@
"notifications.success": "Επιτυχής",
"notifications.undoable": "Έχετε {{seconds}} δευτερόλεπτα για να αναιρέσετε.",
"notifications.validationError": "Κείμενο σφάλματος που εμφανίζεται εάν η φόρμα είχε προβλήματα κατά την υποβολή {{error}}",
"printing.generic.addSettings": "",
"printing.generic.borders.border": "Περιθώρια",
"printing.generic.borders.grid": "Πλέγμα",
"printing.generic.borders.none": "Κανένα",
"printing.generic.columns": "Στήλες",
"printing.generic.contentSettings": "Ρυθμίσεις Περιεχομένου",
"printing.generic.customSize": "Προσαρμοσμένο",
"printing.generic.defaultSettings": "",
"printing.generic.deleteSettings": "",
"printing.generic.deleteSettingsConfirm": "",
"printing.generic.description": "Τροποποιήστε τις παρακάτω ρυθμίσεις για να αποκτήσετε την επιθυμητή διάταξη εκτύπωσης. Λάβετε υπόψη ότι οι εκτυπωτές και το λειτουργικό σας σύστημα ενδέχεται να εφαρμόζουν τα δικά τους περιθώρια και την δική τους μεγέθυνση, επομένως ίσως χρειαστεί να εκτελέσετε κάποιες δοκιμές πριν είναι όλα σωστά. Δοκιμάστε τις ρυθμίσεις σε ένα κομμάτι κανονικού χαρτιού πριν εκτυπώσετε στις πραγματικές ετικέτες.",
"printing.generic.dimensions": "Διαστάσεις",
"printing.generic.duplicateSettings": "",
"printing.generic.helpMargin": "Τα περιθώρια θα πρέπει να διαμορφωθούν ώστε να ταιριάζουν με το χαρτί ετικετών που χρησιμοποιείτε αλλά και τον εκτυπωτή σας. Η αλλαγή αυτών των περιθωρίων θα επηρεάσει το μέγεθος ολόκληρου του πλέγματος.",
"printing.generic.helpPrinterMargin": "Το ασφαλές περιθώριο πρέπει να ρυθμιστεί στο πόσο κοντά στην άκρη του χαρτιού μπορεί να εκτυπώσει ο εκτυπωτής σας. Η αλλαγή αυτών δεν θα επηρεάσει ολόκληρο το πλέγμα.",
"printing.generic.horizontalSpacing": "Οριζόντιο Διάστημα",
"printing.generic.itemCopies": "",
"printing.generic.layoutSettings": "Ρυθμίσεις Διάταξης",
"printing.generic.marginBottom": "Κάτω Περιθώριο",
"printing.generic.marginLeft": "Αριστερό περιθώριο",
"printing.generic.marginRight": "Δεξιό Περιθώριο",
"printing.generic.marginTop": "Επάνω Περιθώριο",
"printing.generic.newSetting": "",
"printing.generic.paperSize": "Μέγεθος Χαρτιού",
"printing.generic.previewScale": "Προηγούμενη Κλίμακα",
"printing.generic.print": "Τύπωσε",
@@ -146,10 +123,6 @@
"printing.generic.printerMarginRight": "Ασφαλές Περιθώριο Δεξιά",
"printing.generic.printerMarginTop": "Ασφαλές Περιθώριο Επάνω",
"printing.generic.rows": "Σειρές",
"printing.generic.saveAsImage": "",
"printing.generic.saveSetting": "",
"printing.generic.settings": "",
"printing.generic.settingsName": "",
"printing.generic.showBorder": "Εμφάνιση Περιθωρίου",
"printing.generic.skipItems": "Παράληψη Αντικειμένων",
"printing.generic.title": "Εκτύπωση",
@@ -161,10 +134,6 @@
"printing.qrcode.showContent": "Εκτύπωση Πληροφοριών",
"printing.qrcode.showFilamentComment": "Σχόλια Νήματος",
"printing.qrcode.showLotNr": "Αριθμός Παρτίδας",
"printing.qrcode.showQRCode": "",
"printing.qrcode.showQRCodeMode.no": "",
"printing.qrcode.showQRCodeMode.simple": "",
"printing.qrcode.showQRCodeMode.withIcon": "",
"printing.qrcode.showSpoolComment": "Σχόλια Καρουλιού",
"printing.qrcode.showSpoolWeight": "Βάρος Καρουλιού",
"printing.qrcode.showSpoolmanIcon": "Εμφάνιση Εικονιδίου Spoolman",
@@ -172,15 +141,8 @@
"printing.qrcode.showVendor": "Προμηθευτής",
"printing.qrcode.showVendorComment": "Σχόλια Προμηθευτή",
"printing.qrcode.spoolWeight": "Βάρος Νήματος: {{weight}}",
"printing.qrcode.template": "",
"printing.qrcode.templateHelp": "",
"printing.qrcode.textSize": "Μέγεθος Κειμένου Περιεχομένου",
"printing.qrcode.title": "Εκτύπωση Κωδικού QR",
"printing.qrcode.useHTTPUrl.label": "",
"printing.qrcode.useHTTPUrl.options.default": "",
"printing.qrcode.useHTTPUrl.options.url": "",
"printing.qrcode.useHTTPUrl.preview": "",
"printing.qrcode.useHTTPUrl.tooltip": "",
"printing.spoolSelect.description": "Επιλέξτε καρούλια για να τυπώσετε κωδικούς QR για αυτά.",
"printing.spoolSelect.noSpoolsSelected": "Δεν έχετε επιλέξει κάποιο καρούλι.",
"printing.spoolSelect.selectAll": "Επιλογή/Αποεπιλογή όλων",
@@ -224,19 +186,13 @@
"settings.extraFields.params.order": "Σειρά",
"settings.extraFields.params.unit": "Μονάδα",
"settings.extraFields.tab": "Επιπλέον Πεδία",
"settings.general.baseUrl.label": "",
"settings.general.baseUrl.tooltip": "",
"settings.general.currency.label": "Ετικέτα για τη ρύθμιση του νομίσματος",
"settings.general.roundPrices.label": "",
"settings.general.roundPrices.tooltip": "",
"settings.general.tab": "Γενικές",
"settings.header": "Ρυθμίσεις",
"settings.settings": "Ρυθμίσεις",
"spool.fields.archived": "Αρχειοθετημένο",
"spool.fields.comment": "Σχόλια",
"spool.fields.filament": "Νήμα",
"spool.fields.filamentExternal": "",
"spool.fields.filamentInternal": "",
"spool.fields.filamentName": "Νήμα",
"spool.fields.firstUsed": "Πρώτη Χρήση",
"spool.fields.id": "Α/Α",
@@ -254,27 +210,17 @@
"spool.fields.usedLength": "Χρησιμοποιημένο μήκος",
"spool.fields.usedWeight": "Χρησιμοποιημένο Βάρος",
"spool.fields.weightToUse": "Βάρος",
"spool.fieldsHelp.externalFilament": "",
"spool.fieldsHelp.initialWeight": "",
"spool.fieldsHelp.location": "Που βρίσκεται το καρούλι, εάν υπάρχουν πολλαπλά σημεία αποθήκευσης για τα καρούλια.",
"spool.fieldsHelp.lotNr": "Αριθμός παρτίδας κατασκευαστή. Μπορεί να χρησιμοποιηθεί για να διασφαλιστεί ότι μια εκτύπωση έχει σταθερό χρώμα εάν χρησιμοποιούνται πολλαπλά καρούλια.",
"spool.fieldsHelp.measuredWeight": "Πόσο ζυγίζουν το νήμα και το καρούλι.",
"spool.fieldsHelp.price": "Τιμή πλήρους καρουλιού. Εάν δεν οριστεί, η τιμή του νήματος θα θεωρηθεί αντ' αυτού.",
"spool.fieldsHelp.remainingWeight": "Πόσο νήμα έχει απομείνει στο καρούλι. Για ένα καινούργιο καρούλι αυτό πρέπει να συμπίπτει με το βάρος του καρουλιού.",
"spool.fieldsHelp.spoolWeight": "",
"spool.fieldsHelp.usedWeight": "Πόσο νήμα έχει χρησιμοποιηθεί από το καρούλι. Σε ένα καινούργιο καρούλι θα πρέπει να έχουν χρησιμοποιηθεί 0g.",
"spool.fieldsHelp.weightToUse": "Επιλέξτε ποια τιμή βάρους θα εισαγάγετε. Το μετρημένο βάρος είναι διαθέσιμο μόνο εάν το βάρος του καρουλιού έχει ρυθμιστεί για το επιλεγμένο νήμα.",
"spool.form.adjustFilamentHelp": "",
"spool.form.adjustFilamentValue": "",
"spool.form.measurementType.length": "",
"spool.form.measurementType.weight": "",
"spool.form.measurementTypeLabel": "",
"spool.form.newLocationPrompt": "Προσθήκη νέας τοποθεσίας αποθήκευσης",
"spool.form.spoolUpdated": "Αυτό το καρούλι έχει ενημερωθεί από κάποιον/κάτι άλλο από τότε που ανοίξατε αυτήν τη σελίδα. Η αποθήκευση θα αντικαταστήσει αυτές τις αλλαγές!",
"spool.formats.lastUsed": "",
"spool.messages.archive": "Είστε σίγουρος ότι θέλετε να αρχειοθετήσετε αυτό το καρούλι;",
"spool.spool": "Καρούλια",
"spool.titles.adjust": "",
"spool.titles.archive": "Αρχειοθέτηση Καρουλιού",
"spool.titles.clone": "Αντιγραφή Καρουλιού",
"spool.titles.create": "Δημιουργία Καρουλιού",
@@ -286,12 +232,9 @@
"tags.clone": "Αντιγραφή",
"unknown": "Άγνωστος",
"vendor.fields.comment": "Σχόλια",
"vendor.fields.emptySpoolWeight": "",
"vendor.fields.externalId": "",
"vendor.fields.id": "Α/Α",
"vendor.fields.name": "Όνομα",
"vendor.fields.registered": "Ημερομηνία Εγγραφής",
"vendor.fieldsHelp.emptySpoolWeight": "",
"vendor.form.vendorUpdated": "Αυτός ο προμηθευτής έχει ενημερωθεί από κάποιον/κάτι άλλο από τότε που ανοίξατε αυτήν τη σελίδα. Η αποθήκευση θα αντικαταστήσει αυτές τις αλλαγές!",
"vendor.titles.clone": "Αντιγραφή Προμηθευτή",
"vendor.titles.create": "Δημιουργία Προμηθευτή",
-10
View File
@@ -36,7 +36,6 @@
"documentTitle.filament.show": "نمایش فیلامنت شماره {{id}} | اسپول من",
"documentTitle.help.list": "راهنما | اسپول من",
"documentTitle.home.list": "صفحه اصلی | اسپول من",
"documentTitle.locations.list": "",
"documentTitle.spool.clone": "تکثیر رول فیلامنت شماره {{id}} | اسپول من",
"documentTitle.spool.create": "ساخت رول فیلامنت جدید | اسپول من",
"documentTitle.spool.edit": "ویرایش رول فیلامنت شماره {{id}} | اسپول من",
@@ -93,15 +92,9 @@
"help.resources.filament": "برندهای فیلامنت. آن‌ها دارای ویژگی‌هایی مانند نام، جنس، رنگ، قطر و موارد دیگر هستند.",
"help.resources.spool": "قرقره‌های فیزیکی جداگانه از یک فیلامنت خاص.",
"help.resources.vendor": "شرکت‌هایی که فیلامنت تولید می‌کنند.",
"home.description": "",
"home.home": "صفحه اصلی",
"home.welcome": "",
"kofi": "حمایت از سازنده این برنامه",
"loading": "در حال بارگذاری",
"locations.locations": "",
"locations.newLocation": "",
"locations.noLocation": "",
"locations.noLocationsHelp": "",
"no": "خیر",
"notifications.createError": "در زمان ساخت {{resource}} مشکل {{statusCode}} رخ داد",
"notifications.createSuccess": "{{resource}} با موفقیت ایجاد شد",
@@ -215,8 +208,6 @@
"settings.general.baseUrl.label": "لینک Spoolman",
"settings.general.baseUrl.tooltip": "آدرس پایگاه داده Spoolman که باید هنگام ایجاد ویژگی‌هایی مانند کدهای QR استفاده شود.",
"settings.general.currency.label": "ارز مورد استفاده",
"settings.general.roundPrices.label": "",
"settings.general.roundPrices.tooltip": "",
"settings.general.tab": "تنظیمات کلی",
"settings.header": "تنظیمات",
"settings.settings": "تنظیمات",
@@ -259,7 +250,6 @@
"spool.form.measurementTypeLabel": "اصلاح برحسب",
"spool.form.newLocationPrompt": "محل نگهداری جدید",
"spool.form.spoolUpdated": "این قرقره توسط شخص یا چیزی دیگر از زمانی که این صفحه را باز کرده‌اید، به‌روز شده است. ذخیره‌سازی تغییرات شما، آن تغییرات را نادیده می‌گیرد!",
"spool.formats.lastUsed": "",
"spool.messages.archive": "آیا مطمئن هستید که می‌خواهید این قرقره را بایگانی کنید؟",
"spool.spool": "رول های فیلامنت",
"spool.titles.adjust": "تنظیم کردن مقدار باقی مانده",
-3
View File
@@ -93,7 +93,6 @@
"help.resources.filament": "Marques de filament. Contient les propriétés comme le nom, le matériau, la couleur, le diamètre, etc.",
"help.resources.spool": "Les bobines physiques pour un filament spécifique.",
"help.resources.vendor": "Les sociétés qui fabriquent le filament.",
"home.description": "",
"home.home": "Accueil",
"home.welcome": "Bienvenue sur votre instance Spoolman !",
"kofi": "Me donner un pourboire sur Ko-fi",
@@ -228,8 +227,6 @@
"settings.general.baseUrl.label": "Base de l'URL",
"settings.general.baseUrl.tooltip": "La base de l'URL à utiliser lors de la génération de QR codes.",
"settings.general.currency.label": "Devise",
"settings.general.roundPrices.label": "",
"settings.general.roundPrices.tooltip": "",
"settings.general.tab": "Général",
"settings.header": "Paramètres",
"settings.settings": "Paramètres",
-11
View File
@@ -50,22 +50,18 @@
"documentTitle.vendor.show": "#{{id}} Gyártó megjelenítése | Spoolman",
"filament.buttons.addSpool": "Tekercs hozzáadása",
"filament.fields.articleNumber": "Cikkszám",
"filament.fields.coaxial": "",
"filament.fields.colorHex": "Szín",
"filament.fields.comment": "Megjegyzés",
"filament.fields.density": "Sűrűség",
"filament.fields.diameter": "Átmérő",
"filament.fields.externalId": "Külső ID",
"filament.fields.id": "ID",
"filament.fields.longitudinal": "",
"filament.fields.material": "Anyag",
"filament.fields.multiColor": "",
"filament.fields.name": "Név",
"filament.fields.price": "Ár",
"filament.fields.registered": "Nyilvántartott",
"filament.fields.settingsBedTemp": "Tárgyasztal hőmérséklet",
"filament.fields.settingsExtruderTemp": "Extruder hőmérséklet",
"filament.fields.singleColor": "",
"filament.fields.spoolWeight": "Tekercs súlya",
"filament.fields.spools": "Tekercsek megjelenítése",
"filament.fields.vendor": "Gyártó",
@@ -73,7 +69,6 @@
"filament.fields.weight": "Súly",
"filament.fieldsHelp.articleNumber": "Például. EAN, UPC stb.",
"filament.fieldsHelp.material": "Például. PLA, ABS, PETG stb.",
"filament.fieldsHelp.multiColorDirection": "",
"filament.fieldsHelp.name": "Filament neve, hogy megkülönböztesd ezt a filament típust másoktól ugyanattól a gyártótól. Tartalmazhatja például a színét.",
"filament.fieldsHelp.price": "A teljes tekercs ára.",
"filament.fieldsHelp.spoolWeight": "Az üres tekercs súlya. Ezt használják a tekercs mért súlyának meghatározásához.",
@@ -81,7 +76,6 @@
"filament.filament": "Szálak",
"filament.form.filamentUpdated": "Ez a szál anyag azóta frissült valaki/valami más által, mióta megnyitottad ezt az oldalt. A mentés felül fogja írni ezeket a változásokat!",
"filament.form.importExternal": "Importálás külső forrásból",
"filament.form.importExternalDescription": "",
"filament.titles.clone": "Filament másolása",
"filament.titles.create": "Filament létrehozása",
"filament.titles.edit": "Filament szerkesztése",
@@ -173,14 +167,12 @@
"printing.qrcode.showVendorComment": "Gyártó megjegyzés",
"printing.qrcode.spoolWeight": "Orsó súlya: {{weight}}",
"printing.qrcode.template": "Cimke Sablon",
"printing.qrcode.templateHelp": "",
"printing.qrcode.textSize": "Cimke Szövegméret",
"printing.qrcode.title": "Cimkenyomtatás",
"printing.qrcode.useHTTPUrl.label": "QR kód link",
"printing.qrcode.useHTTPUrl.options.default": "Alapbeállítás",
"printing.qrcode.useHTTPUrl.options.url": "URL",
"printing.qrcode.useHTTPUrl.preview": "Előnézet:",
"printing.qrcode.useHTTPUrl.tooltip": "",
"printing.spoolSelect.description": "Válassza ki a tekercseket a cimkék nyomtatásához.",
"printing.spoolSelect.noSpoolsSelected": "Nem választotál ki egyetlen tekercset sem.",
"printing.spoolSelect.selectAll": "Összes kijelölése/kijelölés törlése",
@@ -225,7 +217,6 @@
"settings.extraFields.params.unit": "Egység",
"settings.extraFields.tab": "Extra mezők",
"settings.general.baseUrl.label": "Alap URL",
"settings.general.baseUrl.tooltip": "",
"settings.general.currency.label": "Pénznem",
"settings.general.roundPrices.label": "Árak kerekítése",
"settings.general.roundPrices.tooltip": "Az árak legközelebbi egész számra való kerekítése.",
@@ -264,8 +255,6 @@
"spool.fieldsHelp.spoolWeight": "Az üres tekercs súlya. Hagyja üresen a szál vagy a gyártó értékének a használatához.",
"spool.fieldsHelp.usedWeight": "Mennyi filament használódott el a tekercsről. Egy új tekercsnek 0 gramm használt mennyiséggel kell rendelkeznie.",
"spool.fieldsHelp.weightToUse": "Válassza ki, hogy milyen súlyértéket kell beírni. A mérési súly csak akkor elérhető, ha a kiválasztott filamenthez be van állítva a tekercs súlya.",
"spool.form.adjustFilamentHelp": "",
"spool.form.adjustFilamentValue": "",
"spool.form.measurementType.length": "Hossz",
"spool.form.measurementType.weight": "Súly",
"spool.form.measurementTypeLabel": "Mérési típus",
-2
View File
@@ -93,9 +93,7 @@
"help.resources.filament": "フィラメントのブランド。名前、素材、色、直径などの特性を持っています。",
"help.resources.spool": "特定のフィラメントの個々の物理的なスプール。",
"help.resources.vendor": "フィラメントを製造している会社。",
"home.description": "",
"home.home": "ホーム",
"home.welcome": "",
"kofi": "Ko-fiでチップをください",
"loading": "読み込み中",
"locations.locations": "場所",
-2
View File
@@ -93,9 +93,7 @@
"help.resources.filament": "Merken filament. Ze hebben eigenschappen zoals naam, materiaal, kleur, diameter en meer.",
"help.resources.spool": "Individuele fysieke spoelen van een specifiek filament.",
"help.resources.vendor": "De bedrijven die het filament maken.",
"home.description": "",
"home.home": "Thuis",
"home.welcome": "",
"kofi": "Tip mij op Ko-fi",
"loading": "Laden",
"locations.locations": "Locaties",
-14
View File
@@ -36,7 +36,6 @@
"documentTitle.filament.show": "#{{id}} Visa filament | Spoolman",
"documentTitle.help.list": "Hjälp | Spoolman",
"documentTitle.home.list": "Hem | Spoolman",
"documentTitle.locations.list": "",
"documentTitle.spool.clone": "#{{id}} Klona spole | Spoolman",
"documentTitle.spool.create": "Skapa spole | Spoolman",
"documentTitle.spool.edit": "#{{id}} Redigera spole | Spoolman",
@@ -93,15 +92,11 @@
"help.resources.filament": "Märken av filament. De har egenskaper som namn, material, färg, diameter och mer.",
"help.resources.spool": "Individuella fysiska spolar av ett specifikt filament.",
"help.resources.vendor": "Företagen som tillverkar filamentet.",
"home.description": "",
"home.home": "Hem",
"home.welcome": "",
"kofi": "Dricksa mig på Ko-fi",
"loading": "Laddar",
"locations.locations": "",
"locations.newLocation": "Ny plats",
"locations.noLocation": "Ingen plats",
"locations.noLocationsHelp": "",
"no": "Nej",
"notifications.createError": "Det gick inte att skapa {{resource}} (statuskod: {{statusCode}})",
"notifications.createSuccess": "Lyckades skapa {{resource}}",
@@ -176,11 +171,6 @@
"printing.qrcode.templateHelp": "Använd {} för att infoga värden från spolobjektet som text. Till exempel kommer {id} att ersättas med spolens id, eller {filament.material} att ersättas med filamentets material. Omslut text med dubbla asterisker ** för att göra den fetstilad. Klicka på knappen för att visa en lista över alla tillgängliga taggar.",
"printing.qrcode.textSize": "Textstorlek",
"printing.qrcode.title": "Etikettutskrift",
"printing.qrcode.useHTTPUrl.label": "",
"printing.qrcode.useHTTPUrl.options.default": "",
"printing.qrcode.useHTTPUrl.options.url": "",
"printing.qrcode.useHTTPUrl.preview": "",
"printing.qrcode.useHTTPUrl.tooltip": "",
"printing.spoolSelect.description": "Välj spolar att skriva ut etiketter för.",
"printing.spoolSelect.noSpoolsSelected": "Du har inte valt några spolar.",
"printing.spoolSelect.selectAll": "Markera/avmarkera alla",
@@ -224,11 +214,7 @@
"settings.extraFields.params.order": "Ordning",
"settings.extraFields.params.unit": "Enhet",
"settings.extraFields.tab": "Extra Fält",
"settings.general.baseUrl.label": "",
"settings.general.baseUrl.tooltip": "",
"settings.general.currency.label": "Valuta",
"settings.general.roundPrices.label": "",
"settings.general.roundPrices.tooltip": "",
"settings.general.tab": "Allmänt",
"settings.header": "Inställningar",
"settings.settings": "Inställningar",
-4
View File
@@ -93,9 +93,7 @@
"help.resources.filament": "แบรนด์เส้นพลาสติก มีคุณสมบัติต่างๆ เช่น ชื่อ วัสดุ สี เส้นผ่านศูนย์กลาง และอื่นๆ",
"help.resources.spool": "ม้วนพลาสติกจริงสำหรับเส้นพลาสติกเฉพาะ",
"help.resources.vendor": "บริษัทที่ผลิตเส้นพลาสติก",
"home.description": "",
"home.home": "หน้าแรก",
"home.welcome": "",
"kofi": "สนับสนุนเราได้ที่ Ko-fi",
"loading": "กำลังโหลด",
"locations.locations": "สถานที่เก็บ",
@@ -214,8 +212,6 @@
"settings.general.baseUrl.label": "URLพื้นฐาน",
"settings.general.baseUrl.tooltip": "URLพื้นฐาน ที่ใช้สำหรับสร้างฟีเจอร์ต่างๆ เช่น QR code",
"settings.general.currency.label": "สกุลเงิน",
"settings.general.roundPrices.label": "",
"settings.general.roundPrices.tooltip": "",
"settings.general.tab": "ทั่วไป",
"settings.header": "การตั้งค่า",
"settings.settings": "การตั้งค่า",
-20
View File
@@ -36,7 +36,6 @@
"documentTitle.filament.show": "#{{id}} Показати Філамент | Spoolman",
"documentTitle.help.list": "Довідка | Spoolman",
"documentTitle.home.list": "Домашня | Spoolman",
"documentTitle.locations.list": "",
"documentTitle.spool.clone": "#{{id}} Клонувати Котушку | Spoolman",
"documentTitle.spool.create": "Створити Котушку | Spoolman",
"documentTitle.spool.edit": "#{{id}} Редагувати Котушку | Spoolman",
@@ -57,7 +56,6 @@
"filament.fields.diameter": "Діаметр",
"filament.fields.externalId": "Зовнішній ID",
"filament.fields.id": "ІД",
"filament.fields.longitudinal": "",
"filament.fields.material": "Матеріал",
"filament.fields.multiColor": "Багатокольоровий",
"filament.fields.name": "Назва",
@@ -73,7 +71,6 @@
"filament.fields.weight": "Вага",
"filament.fieldsHelp.articleNumber": "Напр. EAN, UPC тощо.",
"filament.fieldsHelp.material": "Наприклад PLA, ABS, PETG, тощо.",
"filament.fieldsHelp.multiColorDirection": "",
"filament.fieldsHelp.name": "Назва філаменту, щоб відрізнити цей філамент поміж інших від того самого виробника. Вона повинна містити, наприклад, колір.",
"filament.fieldsHelp.price": "Ціна всієї котушки.",
"filament.fieldsHelp.spoolWeight": "Вага порожньої котушки. Використовується для визначення виміряної ваги котушки.",
@@ -81,7 +78,6 @@
"filament.filament": "Філаменти",
"filament.form.filamentUpdated": "Цей філамент хтось/щось оновив після того, як ви відкрили цю сторінку. Збереження перезапише ці зміни!",
"filament.form.importExternal": "Імпортувати з зовнішнього джерела",
"filament.form.importExternalDescription": "",
"filament.titles.clone": "Клонувати Філамент",
"filament.titles.create": "Створити Філамент",
"filament.titles.edit": "Редагувати Філамент",
@@ -93,15 +89,9 @@
"help.resources.filament": "Бренди філаменту. Вони мають властивості такі як ім'я, матеріал, колір, діаметр та інше.",
"help.resources.spool": "Індивідуальні фізичні котушки філаменту.",
"help.resources.vendor": "Компанії, які роблять філамент.",
"home.description": "",
"home.home": "Домашня",
"home.welcome": "",
"kofi": "Задонатити на Ko-fi",
"loading": "Завантаження",
"locations.locations": "",
"locations.newLocation": "",
"locations.noLocation": "",
"locations.noLocationsHelp": "",
"no": "Ні",
"notifications.createError": "Трапилась помилка при створенні {{resource}} (код помилки: {{statusCode}})",
"notifications.createSuccess": "Успішно створено {{resource}}",
@@ -180,7 +170,6 @@
"printing.qrcode.useHTTPUrl.options.default": "За замовчуванням",
"printing.qrcode.useHTTPUrl.options.url": "URL",
"printing.qrcode.useHTTPUrl.preview": "Попередній перегляд:",
"printing.qrcode.useHTTPUrl.tooltip": "",
"printing.spoolSelect.description": "Виберіть котушки для друку етикеток",
"printing.spoolSelect.noSpoolsSelected": "Ви не вибрали жодної котушки.",
"printing.spoolSelect.selectAll": "Вибрати/Відмінити Всі",
@@ -226,10 +215,7 @@
"settings.extraFields.params.unit": "Одиниця",
"settings.extraFields.tab": "Додаткові поля",
"settings.general.baseUrl.label": "Базова URL-адреса",
"settings.general.baseUrl.tooltip": "",
"settings.general.currency.label": "Валюта",
"settings.general.roundPrices.label": "",
"settings.general.roundPrices.tooltip": "",
"settings.general.tab": "Загальне",
"settings.header": "Налаштування",
"settings.settings": "Налаштування",
@@ -255,24 +241,19 @@
"spool.fields.usedLength": "Використана Довжина",
"spool.fields.usedWeight": "Використана Вага",
"spool.fields.weightToUse": "Вага",
"spool.fieldsHelp.externalFilament": "",
"spool.fieldsHelp.initialWeight": "",
"spool.fieldsHelp.location": "Де розташована котушка, якщо у вас є кілька місць для зберігання котушок.",
"spool.fieldsHelp.lotNr": "Номер партії виробника. Може використовуватися для гарантування рівномірного кольору друку при використанні декількох котушок.",
"spool.fieldsHelp.measuredWeight": "Яка вага філаменту та котушки.",
"spool.fieldsHelp.price": "Ціна цілої котушки. Якщо не вказано, замість неї буде використано ціну філамента.",
"spool.fieldsHelp.remainingWeight": "Скільки філаменту залишилося на котушці. Для нової котушки це має відповідати вазі філаменту на котушці.",
"spool.fieldsHelp.spoolWeight": "",
"spool.fieldsHelp.usedWeight": "Скільки філаменту було використано з котушки. Для нової котушки слід використовувати 0g.",
"spool.fieldsHelp.weightToUse": "Виберіть значення ваги, яке ви хочете ввести. Виміряна вага доступна, лише якщо для вибраного філаменту встановлено вагу котушки.",
"spool.form.adjustFilamentHelp": "",
"spool.form.adjustFilamentValue": "Споживана кількість",
"spool.form.measurementType.length": "Довжина",
"spool.form.measurementType.weight": "Вага",
"spool.form.measurementTypeLabel": "Вид вимірювання",
"spool.form.newLocationPrompt": "Введіть нове розташування",
"spool.form.spoolUpdated": "Ця котушка була оновлена кимось/чимось після відкриття цієї сторінки. Збереження перезапише ці зміни!",
"spool.formats.lastUsed": "",
"spool.messages.archive": "Ви впевнені що хочете архівувати цю котушку?",
"spool.spool": "Котушки",
"spool.titles.adjust": "Відрегулюйте філамент з котушки",
@@ -292,7 +273,6 @@
"vendor.fields.id": "ІД",
"vendor.fields.name": "Назва",
"vendor.fields.registered": "Додано",
"vendor.fieldsHelp.emptySpoolWeight": "",
"vendor.form.vendorUpdated": "Цей виробник був оновлений кимось/чимось після відкриття цієї сторінки. Збереження перезапише ці зміни!",
"vendor.titles.clone": "Клонувати Виробника",
"vendor.titles.create": "Створити Виробника",
-2
View File
@@ -93,9 +93,7 @@
"help.resources.filament": "耗材品种。它们具有名称、材料、颜色、直径等属性。",
"help.resources.spool": "特定耗材的单个物理卷轴。",
"help.resources.vendor": "制造耗材的公司。",
"home.description": "",
"home.home": "主页",
"home.welcome": "",
"kofi": "在Ko-fi上给我打赏一点小费",
"loading": "加载中",
"locations.locations": "位置",
+3
View File
@@ -4,7 +4,10 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"strip-empty-locales": "node scripts/strip-empty-locales.mjs",
"predev": "node scripts/strip-empty-locales.mjs",
"dev": "vite dev",
"prebuild": "node scripts/strip-empty-locales.mjs",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env node
// Strip empty translation values from the non-base locale files before paraglide
// compiles them.
//
// node scripts/strip-empty-locales.mjs [--check]
//
// Why this exists
// ---------------
// Weblate writes an *empty string* for every key a translator hasn't filled in
// yet. Paraglide (via plugin-i18next) treats an empty string as a real, present
// translation: it compiles a message function that returns "" for that locale,
// so the site renders a blank instead of falling back to English. A key that is
// *absent* from a locale file, on the other hand, is re-exported from the base
// locale (`export { key } from "./en.js"`) and falls back correctly.
//
// So the fix is simply to delete empty (or whitespace-only) leaves from every
// non-base locale before compilation. This is a mechanical data-cleaning pass,
// not translation — it never invents or edits a real translation, it only drops
// the placeholder empties Weblate leaves behind. The base locale (en) is the
// source of truth and is left untouched.
//
// It runs automatically before `dev` and `build` (see package.json), so the
// compiled output is always correct even if empties reappear (e.g. from a future
// Weblate sync). Empties are a benign, expected input here — they're neutralised,
// not forbidden — so this is deliberately NOT a CI gate that would block partial
// translations. Pass `--check` for a read-only diagnostic that makes no changes
// and exits non-zero if any empty leaves remain.
import { readFileSync, writeFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..');
const SETTINGS = join(ROOT, 'project.inlang', 'settings.json');
const settings = JSON.parse(readFileSync(SETTINGS, 'utf8'));
const baseLocale = settings.baseLocale;
const locales = settings.locales ?? [];
// e.g. "./locales/{locale}/common.json"
const pathPattern = settings['plugin.inlang.i18next']?.pathPattern ?? './locales/{locale}/common.json';
const isEmpty = (v) => typeof v === 'string' && v.trim() === '';
/**
* Recursively drop empty string leaves and any object left empty as a result.
* Returns { value, removed }. Handles both the flat dotted-key files used today
* and nested objects, so it stays correct if the layout ever changes.
*/
function strip(node) {
if (Array.isArray(node) || node === null || typeof node !== 'object') {
return { value: node, removed: 0 };
}
const out = {};
let removed = 0;
for (const [key, value] of Object.entries(node)) {
if (isEmpty(value)) {
removed++;
continue;
}
if (value && typeof value === 'object' && !Array.isArray(value)) {
const res = strip(value);
removed += res.removed;
// Drop objects that became empty once their empty leaves were removed.
if (Object.keys(res.value).length === 0) continue;
out[key] = res.value;
} else {
out[key] = value;
}
}
return { value: out, removed };
}
const checkMode = process.argv.includes('--check');
let totalRemoved = 0;
const offenders = [];
for (const locale of locales) {
if (locale === baseLocale) continue;
const file = join(ROOT, pathPattern.replace('{locale}', locale));
let raw;
try {
raw = readFileSync(file, 'utf8');
} catch {
continue; // locale declared but no file on disk yet — nothing to strip.
}
const { value, removed } = strip(JSON.parse(raw));
if (removed === 0) continue;
totalRemoved += removed;
if (checkMode) {
offenders.push(`${locale}: ${removed} empty value(s)`);
continue;
}
// Match the existing formatting: 4-space indent, trailing newline. Key order
// is preserved because JSON.stringify walks own-property insertion order.
writeFileSync(file, JSON.stringify(value, null, 4) + '\n', 'utf8');
console.log(`stripped ${removed} empty value(s) from ${locale}`);
}
if (checkMode) {
if (offenders.length) {
console.error('Empty translation values found (run `node scripts/strip-empty-locales.mjs`):');
for (const line of offenders) console.error(` ${line}`);
process.exit(1);
}
console.log('No empty translation values found.');
} else {
console.log(
totalRemoved === 0
? 'No empty translation values to strip.'
: `Stripped ${totalRemoved} empty value(s) total.`
);
}