"""Language / script detection → region inference. Detects the script of OCR'd text and infers candidate countries/regions. Pure stdlib — uses Unicode code-point ranges. """ from __future__ import annotations from typing import List def detect_script(text: str) -> str: """Detect the dominant script in a text string. Returns one of: 'latin', 'cyrillic', 'arabic', 'cjk', 'hangul', 'devanagari', 'thai', 'hebrew', 'greek', 'unknown'. """ if not text: return "unknown" counts: dict[str, int] = {} for ch in text: cp = ord(ch) if 0x0041 <= cp <= 0x024F or 0x1E00 <= cp <= 0x1EFF: counts["latin"] = counts.get("latin", 0) + 1 elif 0x0400 <= cp <= 0x04FF: counts["cyrillic"] = counts.get("cyrillic", 0) + 1 elif 0x0600 <= cp <= 0x06FF or 0x0750 <= cp <= 0x077F: counts["arabic"] = counts.get("arabic", 0) + 1 elif 0x4E00 <= cp <= 0x9FFF or 0x3400 <= cp <= 0x4DBF: counts["cjk"] = counts.get("cjk", 0) + 1 elif 0xAC00 <= cp <= 0xD7AF: counts["hangul"] = counts.get("hangul", 0) + 1 elif 0x0900 <= cp <= 0x097F: counts["devanagari"] = counts.get("devanagari", 0) + 1 elif 0x0E00 <= cp <= 0x0E7F: counts["thai"] = counts.get("thai", 0) + 1 elif 0x0590 <= cp <= 0x05FF: counts["hebrew"] = counts.get("hebrew", 0) + 1 elif 0x0370 <= cp <= 0x03FF: counts["greek"] = counts.get("greek", 0) + 1 if not counts: return "unknown" return max(counts, key=counts.get) def script_to_regions(script: str) -> List[dict]: """Map a script to candidate countries/regions with confidence. Returns a list of {"country": str, "confidence": float}. """ mapping = { "latin": [ {"country": "United States", "confidence": 0.15}, {"country": "United Kingdom", "confidence": 0.15}, {"country": "France", "confidence": 0.1}, {"country": "Germany", "confidence": 0.1}, {"country": "Spain", "confidence": 0.1}, {"country": "Italy", "confidence": 0.1}, {"country": "Brazil", "confidence": 0.1}, {"country": "Mexico", "confidence": 0.1}, ], "cyrillic": [ {"country": "Russia", "confidence": 0.4}, {"country": "Ukraine", "confidence": 0.15}, {"country": "Belarus", "confidence": 0.1}, {"country": "Bulgaria", "confidence": 0.1}, {"country": "Serbia", "confidence": 0.1}, ], "arabic": [ {"country": "Saudi Arabia", "confidence": 0.15}, {"country": "Egypt", "confidence": 0.15}, {"country": "Iraq", "confidence": 0.1}, {"country": "Algeria", "confidence": 0.1}, {"country": "Morocco", "confidence": 0.1}, {"country": "United Arab Emirates", "confidence": 0.1}, ], "cjk": [ {"country": "China", "confidence": 0.5}, {"country": "Taiwan", "confidence": 0.15}, {"country": "Japan", "confidence": 0.1}, ], "hangul": [ {"country": "South Korea", "confidence": 0.7}, {"country": "North Korea", "confidence": 0.1}, ], "devanagari": [ {"country": "India", "confidence": 0.6}, {"country": "Nepal", "confidence": 0.15}, ], "thai": [ {"country": "Thailand", "confidence": 0.85}, ], "hebrew": [ {"country": "Israel", "confidence": 0.8}, ], "greek": [ {"country": "Greece", "confidence": 0.8}, {"country": "Cyprus", "confidence": 0.1}, ], "unknown": [], } return mapping.get(script, []) def text_to_country_hints(text: str) -> List[dict]: """Detect country hints from text content (script + keywords). Returns a list of {"source": "script"|"keyword", "value": str, "country": str, "confidence": float}. """ if not text: return [] hints: list[dict] = [] # Script-based script = detect_script(text) for region in script_to_regions(script): hints.append({ "source": "script", "value": script, "country": region["country"], "confidence": region["confidence"], }) # Keyword-based (very minimal — extend as needed) text_lower = text.lower() keyword_map = { "united states": ("United States", 0.6), "u.s.a": ("United States", 0.5), "london": ("United Kingdom", 0.7), "paris": ("France", 0.7), "berlin": ("Germany", 0.7), "tokyo": ("Japan", 0.7), "beijing": ("China", 0.7), "moscow": ("Russia", 0.7), "sydney": ("Australia", 0.7), "cairo": ("Egypt", 0.7), "mumbai": ("India", 0.7), } for keyword, (country, conf) in keyword_map.items(): if keyword in text_lower: hints.append({ "source": "keyword", "value": keyword, "country": country, "confidence": conf, }) return hints