Seera-Unified-UI/asm_app/scripts/fix_en_locale_arabic_mix.py

345 lines
13 KiB
Python

#!/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()