#!/usr/bin/env python3 """Replace Arabic strings mistakenly stored under locales/en/translation.json.""" from __future__ import annotations import json import re from pathlib import Path ROOT = Path(__file__).resolve().parents[1] EN_PATH = ROOT / "src/locales/en/translation.json" AR_RE = re.compile(r"[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]") # Hand-authored English for sections that were entirely Arabic in en file SECTIONS: dict = { "items": { "title": "Items", "itemDetails": "Item details", "newItem": "New item", "addItem": "Add new item", "itemId": "Item ID", "itemCode": "Item code", "itemName": "Item name", "itemGroup": "Item group", "stockUOM": "Stock UOM", "partDescription": "Part description", "brand": "Brand", "valuationRate": "Valuation rate", "openingStock": "Opening stock", "lastCalibrationDate": "Last calibration date", "nextCalibrationDate": "Next calibration date", "selectItem": "Select item", "selectItemGroup": "Select item group", "selectHospital": "Select hospital", "serialNo": "Serial no.", "dateIn": "Date in", "watts": "Watts", "volts": "Volts", "type": "Type", "code": "Code", "viewDetails": "View details", "editItem": "Edit item", "duplicateItem": "Duplicate item", "deleteItem": "Delete item", "basicInformation": "Basic information", "inventoryDetails": "Inventory details", "stockInformation": "Stock information", "isStockItem": "Is stock item", "isFixedAsset": "Is fixed asset", "balanceQty": "Balance qty", "calibrationInformation": "Calibration information", "additionalInformation": "Additional information", "refreshBalanceQty": "Refresh balance qty", "warrantyMonths": "Warranty (months)", "errorLoadingItems": "Error loading items", "loadingItems": "Loading items...", "deleteConfirmMessage": "Are you sure you want to delete this item? This cannot be undone.", "backToInventory": "Back to inventory", "loadingItem": "Loading item...", "errorLoadingItem": "Error loading item", "createNewItem": "Create new item", "itemCodeLabel": "Item code", "itemUpdatedSuccessfully": "Item updated successfully!", "pleaseSaveFirst": "Please save the item first before submitting.", "submittedSuccessfully": "Item submitted successfully!", "failedToSave": "Failed to save", "failedToSubmit": "Failed to submit", "noItemsFound": "No items found", "createFirstItem": "Create your first item", "listTitle": "Inventory", "listTotal": "Total: {{count}} item(s)", "failedToLoadItems": "Failed to load items.", "listAddItem": "Add item", "export": { "title": "Export items", "selectData": "Select data to export", "selectedRows": "Selected rows", "selectedCount": "Export {{count}} selected item(s)", "currentPage": "Current page", "currentPageCount": "Export {{count}} item(s) on current page", "allWithFilters": "All records (current filters)", "allWithFiltersCount": "Export all {{count}} item(s) matching filters", "csvDesc": "Comma-separated values", "excelDesc": "XLSX spreadsheet", "columnsToExport": "Columns to export", "selectAll": "Select all", "resetToDefault": "Reset to default", "columnsSelected": "{{count}} column(s) selected", "exporting": "Exporting...", "exportButton": "Export", "exportingSelected": "Exporting {{count}} selected row(s)", "exportingPage": "Exporting {{count}} row(s) from current page", "exportingAll": "Exporting all {{count}} row(s)", }, }, "issues": { "title": "Issues", "issueDetails": "Issue details", "newIssue": "New ticket", "addIssue": "Add new ticket", "issueId": "Ticket ID", "subject": "Subject", "raisedBy": "Raised by", "contact": "Contact", "issueType": "Issue type", "openingDate": "Opening date", "resolutionDate": "Resolution date", "resolvedBy": "Resolved by", "firstRespondedOn": "First responded on", "resolutionDetails": "Resolution details", "selectIssue": "Select issue", "allPriorities": "All priorities", "allCompanies": "All companies", "viewDetails": "View details", "editIssue": "Edit issue", "deleteIssue": "Delete issue", "enterSubject": "Enter subject", "selectPriority": "Select priority", "selectIssueType": "Select issue type", "describeIssue": "Describe the issue...", "contactInformation": "Contact information", "createNewIssue": "Create new support ticket", "resolution": "Resolution", "describeResolution": "Describe how the issue was resolved...", "selectCompany": "Select company", "statusInformation": "Status information", "currentStatus": "Current status", "timeline": "Timeline", "loadingIssues": "Loading tickets...", "errorLoadingIssues": "Error loading tickets", "deleteConfirmMessage": "Are you sure you want to delete this ticket? This cannot be undone.", "deletedSuccessfully": "Ticket deleted successfully!", "createWorkOrderFromIssue": "Create work order", "listTitle": "Support tickets", "listTotal": "Total", "listSelected": "selected", "statsTotalIssues": "Total tickets", "statsOpen": "Open", "statsResolved": "Resolved", "statsClosed": "Closed", "noIssuesFound": "No tickets found", "createFirstIssue": "Create your first ticket", "export": { "title": "Export tickets", "selectData": "Select data to export", "selectedRows": "Selected rows", "selectedCount": "Export {{count}} selected ticket(s)", "currentPage": "Current page", "currentPageCount": "Export {{count}} ticket(s) on current page", "allWithFilters": "All records (current filters)", "allWithFiltersCount": "Export all {{count}} ticket(s) matching filters", "exportFormat": "Export format", "csv": "CSV", "csvDesc": "Comma-separated values", "excel": "Excel", "excelDesc": "XLSX spreadsheet", "columnsToExport": "Columns to export", "selectAll": "Select all", "resetToDefault": "Reset to default", "columnsSelected": "{{count}} column(s) selected", "exporting": "Exporting...", "exportButton": "Export", "exportingSelected": "Exporting {{count}} selected row(s)", "exportingPage": "Exporting {{count}} row(s) from current page", "exportingAll": "Export all {{count}} row(s)", }, "status": { "open": "Open", "replied": "Replied", "on_hold": "On hold", "resolved": "Resolved", "closed": "Closed", }, "priority": { "low": "Low", "medium": "Medium", "high": "High", "critical": "Critical", }, }, "users": { "title": "Users", "userDetails": "User details", "newUser": "New user", "addUser": "Add new user", "searchUsers": "Search users...", "manageUsers": "Manage user accounts and permissions", "noUsersFound": "No users found", "tryAdjustingSearch": "Try adjusting your search.", "noUsersAvailable": "No users available.", "backToDashboard": "Back to dashboard", "refresh": "Refresh", "active": "Active", "inactive": "Inactive", "noEmail": "No email", "created": "Created", }, "events": { "title": "Events", "eventDetails": "Event details", "newEvent": "New event", "addEvent": "Add event", "upcomingEvents": "Upcoming events", "eventsFromFrappe": "Events from Frappe", "noEventsFound": "No events found", "noEventsScheduled": "No events scheduled.", "refreshEvents": "Refresh events", }, "pagination": { "previous": "Previous", "next": "Next", "goTo": "Go to", "go": "Go", "page": "Page", "showingToOf": "Showing {{start}} to {{end}} of {{total}} {{label}}", "showingTo": "Showing {{start}} to {{end}} {{label}}", "items": "items", "assets": "assets", "workOrders": "work orders", "issues": "tickets", "teams": "teams", "inspections": "inspections", "plans": "plans", }, "linkField": { "loading": "Loading...", "noResultsFound": "No results found", "createNewDoctype": "Create new {{doctype}}", "selectLabel": "Select {{label}}", }, "supportPlans": { "loadingSupportPlans": "Loading support plans...", "errorLoadingSupportPlans": "Error loading support plans", "deleteConfirmMessage": "Are you sure you want to delete this support plan? This cannot be undone.", "planId": "Plan ID", "deletedSuccessfully": "Support plan deleted successfully!", "deleteSupportPlan": "Delete support plan", "noSupportPlansFound": "No support plans found", "createFirstSupportPlan": "Create your first support plan", "table": { "planName": "Plan name", "type": "Type", "frequency": "Frequency", "contractValue": "Contract value", "warrantyStatus": "Warranty status", "contractStatus": "Contract status", }, "status": { "active": "Active", "expired": "Expired", "pending": "Pending", "terminated": "Terminated early", }, "statusLabel": { "warrantyPrefix": "W:", "contractPrefix": "C:", }, "viewDetails": "View support plan", "editSupportPlan": "Edit support plan", "listTitle": "Support plans", "statsTotalPlans": "Total plans", "statsContracts": "Contracts", "statsWarranties": "Warranties", "statsActive": "Active", "export": { "title": "Export support plans", "selectData": "Select data to export", "selectedRows": "Selected rows", "selectedCount": "Export {{count}} selected plan(s)", "currentPage": "Current page", "currentPageCount": "Export {{count}} plan(s) on current page", "allWithFilters": "All records (current filters)", "allWithFiltersCount": "Export all {{count}} plan(s) matching filters", "exportFormat": "Export format", "csv": "CSV", "csvDesc": "Comma-separated values", "excel": "Excel", "excelDesc": "XLSX spreadsheet", "columnsToExport": "Columns to export", "selectAll": "Select all", "resetToDefault": "Reset to default", "columnsSelected": "{{count}} column(s) selected", "exporting": "Exporting...", "exportButton": "Export", "exportingSelected": "Exporting {{count}} selected row(s)", "exportingPage": "Exporting {{count}} row(s) from current page", "exportingAll": "Export all {{count}} row(s)", }, }, } def humanize_key(key: str) -> str: s = re.sub(r"([a-z])([A-Z])", r"\1 \2", key) s = s.replace("_", " ").strip() if not s: return key words = s.split() out = [] for w in words: wl = w.lower() if wl == "wo": out.append("WO") elif wl == "id": out.append("ID") elif wl == "url": out.append("URL") else: out.append(w.capitalize()) text = " ".join(out) lk = key.lower() if "loading" in lk: return text if text.endswith("...") else f"{text}..." if lk.startswith("error") or "failed" in lk: return f"Error: {text.lower()}" if "error" not in text.lower() else text return text def replace_arabic_strings(obj, path: tuple[str, ...] = ()): if isinstance(obj, dict): return {k: replace_arabic_strings(v, path + (k,)) for k, v in obj.items()} if isinstance(obj, list): return [replace_arabic_strings(v, path) for v in obj] if isinstance(obj, str) and AR_RE.search(obj): leaf = path[-1] if path else "text" return humanize_key(leaf) return obj def main() -> None: data = json.loads(EN_PATH.read_text(encoding="utf-8")) for name, patch in SECTIONS.items(): data[name] = patch for sec in ("inspections", "activeMap", "maintenanceCalendarPage"): if sec in data: data[sec] = replace_arabic_strings(data[sec], (sec,)) EN_PATH.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") print(f"Wrote {EN_PATH}") if __name__ == "__main__": main()