345 lines
13 KiB
Python
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()
|