Spaces:
Running
Running
| import json | |
| import os | |
| CHINA_DATA_DIR = os.path.join( | |
| os.path.dirname(os.path.abspath(__file__)), "public", "china-city-data" | |
| ) | |
| _CHINA_INFO_CACHE = None | |
| def _load_china_info(): | |
| global _CHINA_INFO_CACHE | |
| if _CHINA_INFO_CACHE is None: | |
| info_path = os.path.join(CHINA_DATA_DIR, "info.json") | |
| try: | |
| with open(info_path, "r", encoding="utf-8") as f: | |
| _CHINA_INFO_CACHE = json.load(f) | |
| except Exception as e: | |
| print(f"Error loading China city data: {e}") | |
| _CHINA_INFO_CACHE = {} | |
| return _CHINA_INFO_CACHE | |
| CITIES = { | |
| "中国": { | |
| "北京": ["北京"], | |
| "上海": ["上海"], | |
| "天津": ["天津"], | |
| "重庆": ["重庆"], | |
| "广东": [ | |
| "广州", | |
| "深圳", | |
| "东莞", | |
| "佛山", | |
| "珠海", | |
| "惠州", | |
| "中山", | |
| "汕头", | |
| "湛江", | |
| "江门", | |
| ], | |
| "浙江": ["杭州", "宁波", "温州", "绍兴", "嘉兴", "金华", "台州", "湖州"], | |
| "江苏": ["南京", "苏州", "无锡", "常州", "南通", "徐州", "扬州", "镇江"], | |
| "山东": ["济南", "青岛", "烟台", "威海", "潍坊", "临沂", "济宁", "淄博"], | |
| "四川": ["成都", "绵阳", "德阳", "宜宾", "泸州", "南充", "乐山"], | |
| "湖北": ["武汉", "宜昌", "襄阳", "荆州", "黄石", "十堰"], | |
| "湖南": ["长沙", "株洲", "湘潭", "衡阳", "岳阳", "常德"], | |
| "河南": ["郑州", "洛阳", "开封", "新乡", "安阳", "焦作"], | |
| "河北": ["石家庄", "唐山", "秦皇岛", "邯郸", "保定", "沧州"], | |
| "福建": ["福州", "厦门", "泉州", "漳州", "莆田", "龙岩"], | |
| "安徽": ["合肥", "芜湖", "蚌埠", "马鞍山", "安庆", "黄山"], | |
| "江西": ["南昌", "九江", "景德镇", "赣州", "上饶", "吉安"], | |
| "陕西": ["西安", "咸阳", "宝鸡", "延安", "榆林", "汉中"], | |
| "山西": ["太原", "大同", "临汾", "运城", "晋中", "长治"], | |
| "辽宁": ["沈阳", "大连", "鞍山", "抚顺", "本溪", "营口"], | |
| "吉林": ["长春", "吉林", "四平", "通化", "延边"], | |
| "黑龙江": ["哈尔滨", "齐齐哈尔", "牡丹江", "佳木斯", "大庆"], | |
| "云南": ["昆明", "大理", "丽江", "西双版纳", "曲靖"], | |
| "贵州": ["贵阳", "遵义", "安顺", "六盘水", "毕节"], | |
| "甘肃": ["兰州", "天水", "嘉峪关", "酒泉", "张掖"], | |
| "海南": ["海口", "三亚", "儋州", "琼海"], | |
| "广西": ["南宁", "桂林", "柳州", "梧州", "北海", "玉林"], | |
| "内蒙古": ["呼和浩特", "包头", "鄂尔多斯", "赤峰", "呼伦贝尔"], | |
| "新疆": ["乌鲁木齐", "喀什", "吐鲁番", "阿克苏", "伊宁"], | |
| "西藏": ["拉萨", "日喀则", "林芝", "昌都"], | |
| "宁夏": ["银川", "石嘴山", "吴忠", "固原"], | |
| "青海": ["西宁", "格尔木", "玉树", "海东"], | |
| "香港": ["香港"], | |
| "澳门": ["澳门"], | |
| "台湾": ["台北", "高雄", "台中", "台南", "新竹", "基隆"], | |
| }, | |
| "USA": { | |
| "California": [ | |
| "Los Angeles", | |
| "San Francisco", | |
| "San Diego", | |
| "San Jose", | |
| "Sacramento", | |
| "Oakland", | |
| "Fresno", | |
| ], | |
| "New York": ["New York City", "Buffalo", "Rochester", "Albany", "Syracuse"], | |
| "Texas": [ | |
| "Houston", | |
| "Dallas", | |
| "Austin", | |
| "San Antonio", | |
| "Fort Worth", | |
| "El Paso", | |
| ], | |
| "Florida": ["Miami", "Orlando", "Tampa", "Jacksonville", "Fort Lauderdale"], | |
| "Illinois": ["Chicago", "Aurora", "Naperville", "Rockford"], | |
| "Pennsylvania": ["Philadelphia", "Pittsburgh", "Harrisburg"], | |
| "Arizona": ["Phoenix", "Tucson", "Mesa", "Scottsdale"], | |
| "Nevada": ["Las Vegas", "Reno", "Henderson"], | |
| "Washington": ["Seattle", "Tacoma", "Spokane", "Bellevue"], | |
| "Massachusetts": ["Boston", "Cambridge", "Worcester"], | |
| "Colorado": ["Denver", "Colorado Springs", "Aurora", "Boulder"], | |
| "Georgia": ["Atlanta", "Savannah", "Augusta"], | |
| "North Carolina": ["Charlotte", "Raleigh", "Durham"], | |
| "Michigan": ["Detroit", "Grand Rapids", "Ann Arbor"], | |
| "Oregon": ["Portland", "Salem", "Eugene"], | |
| "District of Columbia": ["Washington"], | |
| "Hawaii": ["Honolulu"], | |
| }, | |
| "Japan": { | |
| "関東": ["Tokyo", "Yokohama", "Kawasaki", "Saitama", "Chiba"], | |
| "関西": ["Osaka", "Kyoto", "Kobe", "Nara"], | |
| "中部": ["Nagoya", "Kanazawa", "Shizuoka"], | |
| "北海道": ["Sapporo", "Hakodate", "Asahikawa"], | |
| "九州": ["Fukuoka", "Nagasaki", "Kumamoto", "Kagoshima"], | |
| "東北": ["Sendai", "Aomori", "Akita"], | |
| "中国": ["Hiroshima", "Okayama"], | |
| "四国": ["Matsuyama", "Takamatsu"], | |
| "沖縄": ["Naha", "Okinawa"], | |
| }, | |
| "UK": { | |
| "England": [ | |
| "London", | |
| "Manchester", | |
| "Birmingham", | |
| "Liverpool", | |
| "Leeds", | |
| "Bristol", | |
| "Sheffield", | |
| "Newcastle", | |
| "Nottingham", | |
| "Cambridge", | |
| "Oxford", | |
| ], | |
| "Scotland": ["Edinburgh", "Glasgow", "Aberdeen", "Dundee"], | |
| "Wales": ["Cardiff", "Swansea", "Newport"], | |
| "Northern Ireland": ["Belfast", "Londonderry"], | |
| }, | |
| "France": { | |
| "Île-de-France": ["Paris"], | |
| "Provence-Alpes-Côte d'Azur": ["Marseille", "Nice", "Cannes", "Toulon"], | |
| "Auvergne-Rhône-Alpes": ["Lyon", "Grenoble", "Saint-Étienne"], | |
| "Nouvelle-Aquitaine": ["Bordeaux", "Limoges"], | |
| "Occitanie": ["Toulouse", "Montpellier", "Nîmes"], | |
| "Hauts-de-France": ["Lille", "Amiens"], | |
| "Grand Est": ["Strasbourg", "Reims", "Nancy", "Metz"], | |
| "Normandie": ["Rouen", "Le Havre", "Caen"], | |
| "Bretagne": ["Rennes", "Brest", "Nantes"], | |
| }, | |
| "Germany": { | |
| "Bayern": ["Munich", "Nuremberg", "Augsburg"], | |
| "Berlin": ["Berlin"], | |
| "Hamburg": ["Hamburg"], | |
| "Hessen": ["Frankfurt", "Wiesbaden"], | |
| "Baden-Württemberg": ["Stuttgart", "Heidelberg", "Freiburg", "Karlsruhe"], | |
| "Nordrhein-Westfalen": ["Cologne", "Düsseldorf", "Dortmund", "Essen", "Bonn"], | |
| "Niedersachsen": ["Hanover", "Braunschweig", "Oldenburg"], | |
| "Sachsen": ["Dresden", "Leipzig"], | |
| "Brandenburg": ["Potsdam"], | |
| }, | |
| "Italy": { | |
| "Lazio": ["Rome"], | |
| "Lombardia": ["Milan", "Bergamo", "Brescia"], | |
| "Veneto": ["Venice", "Verona", "Padua"], | |
| "Toscana": ["Florence", "Pisa", "Siena"], | |
| "Campania": ["Naples", "Salerno"], | |
| "Piemonte": ["Turin", "Genoa"], | |
| "Emilia-Romagna": ["Bologna", "Parma", "Modena"], | |
| "Sicilia": ["Palermo", "Catania", "Syracuse"], | |
| }, | |
| "Spain": { | |
| "Comunidad de Madrid": ["Madrid"], | |
| "Cataluña": ["Barcelona", "Tarragona", "Girona"], | |
| "Andalucía": ["Seville", "Granada", "Málaga", "Córdoba"], | |
| "Comunidad Valenciana": ["Valencia", "Alicante"], | |
| "País Vasco": ["Bilbao", "San Sebastián"], | |
| "Galicia": ["Santiago de Compostela", "A Coruña", "Vigo"], | |
| "Islas Baleares": ["Palma de Mallorca"], | |
| "Islas Canarias": ["Las Palmas", "Santa Cruz de Tenerife"], | |
| }, | |
| "Australia": { | |
| "New South Wales": ["Sydney", "Newcastle", "Wollongong"], | |
| "Victoria": ["Melbourne", "Geelong"], | |
| "Queensland": ["Brisbane", "Gold Coast", "Cairns"], | |
| "Western Australia": ["Perth", "Fremantle"], | |
| "South Australia": ["Adelaide"], | |
| "Tasmania": ["Hobart"], | |
| "Australian Capital Territory": ["Canberra"], | |
| "Northern Territory": ["Darwin"], | |
| }, | |
| "Canada": { | |
| "Ontario": ["Toronto", "Ottawa", "Hamilton", "Mississauga"], | |
| "Quebec": ["Montreal", "Quebec City", "Laval"], | |
| "British Columbia": ["Vancouver", "Victoria", "Surrey"], | |
| "Alberta": ["Calgary", "Edmonton"], | |
| "Manitoba": ["Winnipeg"], | |
| "Saskatchewan": ["Saskatoon", "Regina"], | |
| "Nova Scotia": ["Halifax"], | |
| }, | |
| "South Korea": { | |
| "서울특별시": ["Seoul"], | |
| "부산광역시": ["Busan"], | |
| "경기도": ["Incheon", "Suwon", "Seongnam"], | |
| "대구광역시": ["Daegu"], | |
| "대전광역시": ["Daejeon"], | |
| "광주광역시": ["Gwangju"], | |
| "제주특별자치도": ["Jeju"], | |
| }, | |
| "Singapore": { | |
| "Singapore": ["Singapore"], | |
| }, | |
| "India": { | |
| "Maharashtra": ["Mumbai", "Pune", "Nagpur"], | |
| "Delhi": ["New Delhi"], | |
| "Karnataka": ["Bangalore", "Mysore"], | |
| "Tamil Nadu": ["Chennai", "Coimbatore", "Madurai"], | |
| "West Bengal": ["Kolkata"], | |
| "Gujarat": ["Ahmedabad", "Surat", "Vadodara"], | |
| "Telangana": ["Hyderabad"], | |
| "Kerala": ["Kochi", "Trivandrum"], | |
| "Rajasthan": ["Jaipur", "Jodhpur", "Udaipur"], | |
| "Uttar Pradesh": ["Lucknow", "Agra", "Varanasi"], | |
| }, | |
| "Russia": { | |
| "Центральный": ["Moscow"], | |
| "Северо-Западный": ["Saint Petersburg", "Kaliningrad"], | |
| "Южный": ["Sochi", "Rostov-on-Don", "Krasnodar"], | |
| "Приволжский": ["Kazan", "Nizhny Novgorod", "Samara"], | |
| "Уральский": ["Yekaterinburg", "Chelyabinsk"], | |
| "Сибирский": ["Novosibirsk", "Krasnoyarsk", "Irkutsk"], | |
| "Дальневосточный": ["Vladivostok", "Khabarovsk"], | |
| }, | |
| "Brazil": { | |
| "São Paulo": ["São Paulo", "Campinas", "Santos"], | |
| "Rio de Janeiro": ["Rio de Janeiro", "Niterói"], | |
| "Minas Gerais": ["Belo Horizonte"], | |
| "Bahia": ["Salvador"], | |
| "Rio Grande do Sul": ["Porto Alegre"], | |
| "Paraná": ["Curitiba"], | |
| "Distrito Federal": ["Brasília"], | |
| "Ceará": ["Fortaleza"], | |
| "Pernambuco": ["Recife"], | |
| "Amazonas": ["Manaus"], | |
| }, | |
| "Mexico": { | |
| "Ciudad de México": ["Mexico City"], | |
| "Jalisco": ["Guadalajara"], | |
| "Nuevo León": ["Monterrey"], | |
| "Quintana Roo": ["Cancún", "Playa del Carmen"], | |
| "Baja California": ["Tijuana", "Ensenada"], | |
| "Yucatán": ["Mérida"], | |
| "Puebla": ["Puebla"], | |
| "Guanajuato": ["León", "Guanajuato"], | |
| }, | |
| "Netherlands": { | |
| "Noord-Holland": ["Amsterdam", "Haarlem"], | |
| "Zuid-Holland": ["Rotterdam", "The Hague", "Leiden"], | |
| "Utrecht": ["Utrecht"], | |
| "Noord-Brabant": ["Eindhoven", "Tilburg", "'s-Hertogenbosch"], | |
| "Gelderland": ["Arnhem", "Nijmegen"], | |
| "Limburg": ["Maastricht"], | |
| }, | |
| "Belgium": { | |
| "Brussels-Capital": ["Brussels"], | |
| "Flanders": ["Antwerp", "Ghent", "Bruges"], | |
| "Wallonia": ["Liège", "Charleroi", "Namur"], | |
| }, | |
| "Switzerland": { | |
| "Zürich": ["Zurich"], | |
| "Bern": ["Bern"], | |
| "Geneva": ["Geneva"], | |
| "Vaud": ["Lausanne"], | |
| "Basel-Stadt": ["Basel"], | |
| "Lucerne": ["Lucerne"], | |
| }, | |
| "Austria": { | |
| "Wien": ["Vienna"], | |
| "Salzburg": ["Salzburg"], | |
| "Tirol": ["Innsbruck"], | |
| "Steiermark": ["Graz"], | |
| "Oberösterreich": ["Linz"], | |
| }, | |
| "Portugal": { | |
| "Lisboa": ["Lisbon"], | |
| "Porto": ["Porto"], | |
| "Faro": ["Faro", "Albufeira"], | |
| "Coimbra": ["Coimbra"], | |
| }, | |
| "Greece": { | |
| "Attica": ["Athens", "Piraeus"], | |
| "Central Macedonia": ["Thessaloniki"], | |
| "Crete": ["Heraklion"], | |
| "South Aegean": ["Rhodes", "Mykonos", "Santorini"], | |
| }, | |
| "Turkey": { | |
| "Istanbul": ["Istanbul"], | |
| "Ankara": ["Ankara"], | |
| "Izmir": ["Izmir"], | |
| "Antalya": ["Antalya"], | |
| "Bursa": ["Bursa"], | |
| "Cappadocia": ["Nevşehir", "Göreme"], | |
| }, | |
| "UAE": { | |
| "Dubai": ["Dubai"], | |
| "Abu Dhabi": ["Abu Dhabi"], | |
| "Sharjah": ["Sharjah"], | |
| "Ras Al Khaimah": ["Ras Al Khaimah"], | |
| }, | |
| "Thailand": { | |
| "Bangkok": ["Bangkok"], | |
| "Chiang Mai": ["Chiang Mai"], | |
| "Phuket": ["Phuket"], | |
| "Chonburi": ["Pattaya"], | |
| "Krabi": ["Krabi"], | |
| }, | |
| "Vietnam": { | |
| "Hà Nội": ["Hanoi"], | |
| "Hồ Chí Minh": ["Ho Chi Minh City"], | |
| "Đà Nẵng": ["Da Nang"], | |
| "Khánh Hòa": ["Nha Trang"], | |
| "Quảng Ninh": ["Ha Long"], | |
| }, | |
| "Indonesia": { | |
| "DKI Jakarta": ["Jakarta"], | |
| "Bali": ["Bali", "Denpasar", "Ubud"], | |
| "Jawa Timur": ["Surabaya"], | |
| "Jawa Barat": ["Bandung"], | |
| "DI Yogyakarta": ["Yogyakarta"], | |
| }, | |
| "Malaysia": { | |
| "Kuala Lumpur": ["Kuala Lumpur"], | |
| "Penang": ["George Town"], | |
| "Selangor": ["Petaling Jaya", "Shah Alam"], | |
| "Johor": ["Johor Bahru"], | |
| "Sabah": ["Kota Kinabalu"], | |
| "Sarawak": ["Kuching"], | |
| }, | |
| "Philippines": { | |
| "Metro Manila": ["Manila", "Makati", "Quezon City"], | |
| "Cebu": ["Cebu City"], | |
| "Davao": ["Davao City"], | |
| "Palawan": ["Puerto Princesa"], | |
| }, | |
| "Egypt": { | |
| "Cairo Governorate": ["Cairo"], | |
| "Alexandria Governorate": ["Alexandria"], | |
| "Giza Governorate": ["Giza"], | |
| "Luxor Governorate": ["Luxor"], | |
| "Red Sea Governorate": ["Hurghada", "Sharm El Sheikh"], | |
| }, | |
| "South Africa": { | |
| "Gauteng": ["Johannesburg", "Pretoria"], | |
| "Western Cape": ["Cape Town"], | |
| "KwaZulu-Natal": ["Durban"], | |
| "Eastern Cape": ["Port Elizabeth"], | |
| }, | |
| "Morocco": { | |
| "Casablanca-Settat": ["Casablanca"], | |
| "Rabat-Salé-Kénitra": ["Rabat"], | |
| "Marrakech-Safi": ["Marrakech", "Essaouira"], | |
| "Fès-Meknès": ["Fes", "Meknes"], | |
| "Tanger-Tétouan-Al Hoceïma": ["Tangier", "Chefchaouen"], | |
| }, | |
| "Argentina": { | |
| "Buenos Aires": ["Buenos Aires", "La Plata"], | |
| "Córdoba": ["Córdoba"], | |
| "Mendoza": ["Mendoza"], | |
| "Santa Fe": ["Rosario"], | |
| "Tierra del Fuego": ["Ushuaia"], | |
| }, | |
| "Chile": { | |
| "Región Metropolitana": ["Santiago"], | |
| "Valparaíso": ["Valparaíso", "Viña del Mar"], | |
| "Biobío": ["Concepción"], | |
| "Los Lagos": ["Puerto Montt"], | |
| "Magallanes": ["Punta Arenas"], | |
| }, | |
| "Colombia": { | |
| "Bogotá D.C.": ["Bogotá"], | |
| "Antioquia": ["Medellín"], | |
| "Valle del Cauca": ["Cali"], | |
| "Atlántico": ["Barranquilla"], | |
| "Bolívar": ["Cartagena"], | |
| }, | |
| "Peru": { | |
| "Lima": ["Lima"], | |
| "Cusco": ["Cusco"], | |
| "Arequipa": ["Arequipa"], | |
| "La Libertad": ["Trujillo"], | |
| }, | |
| "New Zealand": { | |
| "Auckland": ["Auckland"], | |
| "Wellington": ["Wellington"], | |
| "Canterbury": ["Christchurch"], | |
| "Otago": ["Dunedin", "Queenstown"], | |
| }, | |
| "Ireland": { | |
| "Leinster": ["Dublin"], | |
| "Munster": ["Cork", "Limerick"], | |
| "Connacht": ["Galway"], | |
| }, | |
| "Sweden": { | |
| "Stockholms län": ["Stockholm"], | |
| "Västra Götalands län": ["Gothenburg"], | |
| "Skåne län": ["Malmö"], | |
| "Uppsala län": ["Uppsala"], | |
| }, | |
| "Norway": { | |
| "Oslo": ["Oslo"], | |
| "Vestland": ["Bergen"], | |
| "Troms og Finnmark": ["Tromsø"], | |
| "Trøndelag": ["Trondheim"], | |
| }, | |
| "Denmark": { | |
| "Hovedstaden": ["Copenhagen"], | |
| "Midtjylland": ["Aarhus"], | |
| "Syddanmark": ["Odense"], | |
| }, | |
| "Finland": { | |
| "Uusimaa": ["Helsinki", "Espoo"], | |
| "Pirkanmaa": ["Tampere"], | |
| "Southwest Finland": ["Turku"], | |
| "Lapland": ["Rovaniemi"], | |
| }, | |
| "Poland": { | |
| "Mazowieckie": ["Warsaw"], | |
| "Małopolskie": ["Kraków"], | |
| "Wielkopolskie": ["Poznań"], | |
| "Dolnośląskie": ["Wrocław"], | |
| "Pomorskie": ["Gdańsk"], | |
| }, | |
| "Czech Republic": { | |
| "Praha": ["Prague"], | |
| "Jihomoravský": ["Brno"], | |
| "Moravskoslezský": ["Ostrava"], | |
| "Plzeňský": ["Plzeň"], | |
| }, | |
| "Hungary": { | |
| "Budapest": ["Budapest"], | |
| "Pest": ["Szentendre"], | |
| "Baranya": ["Pécs"], | |
| "Hajdú-Bihar": ["Debrecen"], | |
| }, | |
| "Israel": { | |
| "Tel Aviv District": ["Tel Aviv", "Jaffa"], | |
| "Jerusalem District": ["Jerusalem"], | |
| "Haifa District": ["Haifa"], | |
| "Southern District": ["Eilat"], | |
| }, | |
| "Saudi Arabia": { | |
| "Riyadh": ["Riyadh"], | |
| "Makkah": ["Mecca", "Jeddah"], | |
| "Eastern Province": ["Dammam", "Dhahran"], | |
| "Medina": ["Medina"], | |
| }, | |
| "Qatar": { | |
| "Doha": ["Doha"], | |
| }, | |
| "Kenya": { | |
| "Nairobi": ["Nairobi"], | |
| "Mombasa": ["Mombasa"], | |
| "Nakuru": ["Nakuru"], | |
| }, | |
| "Nigeria": { | |
| "Lagos": ["Lagos"], | |
| "Abuja": ["Abuja"], | |
| "Kano": ["Kano"], | |
| }, | |
| "Pakistan": { | |
| "Punjab": ["Lahore", "Faisalabad"], | |
| "Sindh": ["Karachi"], | |
| "Islamabad": ["Islamabad"], | |
| "Khyber Pakhtunkhwa": ["Peshawar"], | |
| }, | |
| "Bangladesh": { | |
| "Dhaka Division": ["Dhaka"], | |
| "Chittagong Division": ["Chittagong"], | |
| "Sylhet Division": ["Sylhet"], | |
| }, | |
| "Sri Lanka": { | |
| "Western Province": ["Colombo"], | |
| "Central Province": ["Kandy"], | |
| "Southern Province": ["Galle"], | |
| }, | |
| "Nepal": { | |
| "Bagmati": ["Kathmandu"], | |
| "Gandaki": ["Pokhara"], | |
| }, | |
| } | |
| # Translation layer for dual-language support | |
| CN_TO_EN = { | |
| # Countries (CN -> EN) | |
| "中国": "China", | |
| "美国": "United States", | |
| "日本": "Japan", | |
| "英国": "United Kingdom", | |
| "法国": "France", | |
| "德国": "Germany", | |
| "意大利": "Italy", | |
| "西班牙": "Spain", | |
| "澳大利亚": "Australia", | |
| "加拿大": "Canada", | |
| "韩国": "South Korea", | |
| "新加坡": "Singapore", | |
| "印度": "India", | |
| "俄罗斯": "Russia", | |
| "巴西": "Brazil", | |
| "墨西哥": "Mexico", | |
| "荷兰": "Netherlands", | |
| "比利时": "Belgium", | |
| "瑞士": "Switzerland", | |
| "奥地利": "Austria", | |
| "葡萄牙": "Portugal", | |
| "希腊": "Greece", | |
| "土耳其": "Turkey", | |
| "阿联酋": "United Arab Emirates", | |
| "泰国": "Thailand", | |
| "越南": "Vietnam", | |
| "印度尼西亚": "Indonesia", | |
| "马来西亚": "Malaysia", | |
| "菲律宾": "Philippines", | |
| "埃及": "Egypt", | |
| "南非": "South Africa", | |
| "摩洛哥": "Morocco", | |
| "阿根廷": "Argentina", | |
| "智利": "Chile", | |
| "哥伦比亚": "Colombia", | |
| "秘鲁": "Peru", | |
| "新西兰": "New Zealand", | |
| "爱尔兰": "Ireland", | |
| "瑞典": "Sweden", | |
| "挪威": "Norway", | |
| "丹麦": "Denmark", | |
| "芬兰": "Finland", | |
| "波兰": "Poland", | |
| "捷克": "Czech Republic", | |
| "匈牙利": "Hungary", | |
| "以色列": "Israel", | |
| "沙特阿拉伯": "Saudi Arabia", | |
| "卡塔尔": "Qatar", | |
| "肯尼亚": "Kenya", | |
| "尼日利亚": "Nigeria", | |
| "巴基斯坦": "Pakistan", | |
| "孟加拉国": "Bangladesh", | |
| "斯里兰卡": "Sri Lanka", | |
| "尼泊尔": "Nepal", | |
| # Chinese Provinces (CN -> EN) | |
| "北京": "Beijing", | |
| "上海": "Shanghai", | |
| "天津": "Tianjin", | |
| "重庆": "Chongqing", | |
| "广东": "Guangdong", | |
| "浙江": "Zhejiang", | |
| "江苏": "Jiangsu", | |
| "山东": "Shandong", | |
| "四川": "Sichuan", | |
| "湖北": "Hubei", | |
| "湖南": "Hunan", | |
| "河南": "Henan", | |
| "河北": "Hebei", | |
| "福建": "Fujian", | |
| "安徽": "Anhui", | |
| "江西": "Jiangxi", | |
| "陕西": "Shaanxi", | |
| "山西": "Shanxi", | |
| "辽宁": "Liaoning", | |
| "吉林": "Jilin", | |
| "黑龙江": "Heilongjiang", | |
| "云南": "Yunnan", | |
| "贵州": "Guizhou", | |
| "甘肃": "Gansu", | |
| "海南": "Hainan", | |
| "广西": "Guangxi", | |
| "内蒙古": "Inner Mongolia", | |
| "新疆": "Xinjiang", | |
| "西藏": "Tibet", | |
| "宁夏": "Ningxia", | |
| "青海": "Qinghai", | |
| "香港": "Hong Kong", | |
| "澳门": "Macau", | |
| "台湾": "Taiwan", | |
| # Japanese Regions | |
| "関東": "Kanto", | |
| "関西": "Kansai", | |
| "中部": "Chubu", | |
| "北海道": "Hokkaido", | |
| "九州": "Kyushu", | |
| "東北": "Tohoku", | |
| "四国": "Shikoku", | |
| "沖縄": "Okinawa", | |
| # Chinese Cities (CN -> EN) | |
| "广州": "Guangzhou", | |
| "深圳": "Shenzhen", | |
| "东莞": "Dongguan", | |
| "佛山": "Foshan", | |
| "珠海": "Zhuhai", | |
| "惠州": "Huizhou", | |
| "中山": "Zhongshan", | |
| "汕头": "Shantou", | |
| "湛江": "Zhanjiang", | |
| "江门": "Jiangmen", | |
| "梧州": "Wuzhou", | |
| "杭州": "Hangzhou", | |
| "宁波": "Ningbo", | |
| "温州": "Wenzhou", | |
| "绍兴": "Shaoxing", | |
| "嘉兴": "Jiaxing", | |
| "金华": "Jinhua", | |
| "台州": "Taizhou", | |
| "湖州": "Huzhou", | |
| "南京": "Nanjing", | |
| "苏州": "Suzhou", | |
| "无锡": "Wuxi", | |
| "常州": "Changzhou", | |
| "南通": "Nantong", | |
| "徐州": "Xuzhou", | |
| "扬州": "Yangzhou", | |
| "镇江": "Zhenjiang", | |
| "济南": "Jinan", | |
| "青岛": "Qingdao", | |
| "烟台": "Yantai", | |
| "威海": "Weihai", | |
| "潍坊": "Weifang", | |
| "临沂": "Linyi", | |
| "济宁": "Jining", | |
| "淄博": "Zibo", | |
| "成都": "Chengdu", | |
| "绵阳": "Mianyang", | |
| "德阳": "Deyang", | |
| "宜宾": "Yibin", | |
| "泸州": "Luzhou", | |
| "南充": "Nanchong", | |
| "乐山": "Leshan", | |
| "武汉": "Wuhan", | |
| "宜昌": "Yichang", | |
| "襄阳": "Xiangyang", | |
| "荆州": "Jingzhou", | |
| "黄石": "Huangshi", | |
| "十堰": "Shiyan", | |
| "长沙": "Changsha", | |
| "株洲": "Zhuzhou", | |
| "湘潭": "Xiangtan", | |
| "衡阳": "Hengyang", | |
| "岳阳": "Yueyang", | |
| "常德": "Changde", | |
| "郑州": "Zhengzhou", | |
| "洛阳": "Luoyang", | |
| "开封": "Kaifeng", | |
| "新乡": "Xinxiang", | |
| "安阳": "Anyang", | |
| "焦作": "Jiaozuo", | |
| "石家庄": "Shijiazhuang", | |
| "唐山": "Tangshan", | |
| "秦皇岛": "Qinhuangdao", | |
| "邯郸": "Handan", | |
| "保定": "Baoding", | |
| "沧州": "Cangzhou", | |
| "福州": "Fuzhou", | |
| "厦门": "Xiamen", | |
| "泉州": "Quanzhou", | |
| "漳州": "Zhangzhou", | |
| "莆田": "Putian", | |
| "龙岩": "Longyan", | |
| "合肥": "Hefei", | |
| "芜湖": "Wuhu", | |
| "蚌埠": "Bengbu", | |
| "马鞍山": "Ma'anshan", | |
| "安庆": "Anqing", | |
| "黄山": "Huangshan", | |
| "南昌": "Nanchang", | |
| "九江": "Jiujiang", | |
| "景德镇": "Jingdezhen", | |
| "赣州": "Ganzhou", | |
| "上饶": "Shangrao", | |
| "吉安": "Ji'an", | |
| "西安": "Xi'an", | |
| "咸阳": "Xianyang", | |
| "宝鸡": "Baoji", | |
| "延安": "Yan'an", | |
| "榆林": "Yulin", | |
| "汉中": "Hanzhong", | |
| "太原": "Taiyuan", | |
| "大同": "Datong", | |
| "临汾": "Linfen", | |
| "运城": "Yuncheng", | |
| "晋中": "Jinzhong", | |
| "长治": "Changzhi", | |
| "沈阳": "Shenyang", | |
| "大连": "Dalian", | |
| "鞍山": "Anshan", | |
| "抚顺": "Fushun", | |
| "本溪": "Benxi", | |
| "营口": "Yingkou", | |
| "长春": "Changchun", | |
| "四平": "Siping", | |
| "通化": "Tonghua", | |
| "延边": "Yanbian", | |
| "哈尔滨": "Harbin", | |
| "齐齐哈尔": "Qiqihar", | |
| "牡丹江": "Mudanjiang", | |
| "佳木斯": "Jiamusi", | |
| "大庆": "Daqing", | |
| "昆明": "Kunming", | |
| "大理": "Dali", | |
| "丽江": "Lijiang", | |
| "西双版纳": "Xishuangbanna", | |
| "曲靖": "Qujing", | |
| "贵阳": "Guiyang", | |
| "遵义": "Zunyi", | |
| "安顺": "Anshun", | |
| "六盘水": "Liupanshui", | |
| "毕节": "Bijie", | |
| "兰州": "Lanzhou", | |
| "天水": "Tianshui", | |
| "嘉峪关": "Jiayuguan", | |
| "酒泉": "Jiuquan", | |
| "张掖": "Zhangye", | |
| "海口": "Haikou", | |
| "三亚": "Sanya", | |
| "儋州": "Danzhou", | |
| "琼海": "Qionghai", | |
| "南宁": "Nanning", | |
| "桂林": "Guilin", | |
| "柳州": "Liuzhou", | |
| "北海": "Beihai", | |
| "玉林": "Yulin", | |
| "呼和浩特": "Hohhot", | |
| "包头": "Baotou", | |
| "鄂尔多斯": "Ordos", | |
| "赤峰": "Chifeng", | |
| "呼伦贝尔": "Hulunbuir", | |
| "乌鲁木齐": "Urumqi", | |
| "喀什": "Kashgar", | |
| "吐鲁番": "Turpan", | |
| "阿克苏": "Aksu", | |
| "伊宁": "Yining", | |
| "拉萨": "Lhasa", | |
| "日喀则": "Shigatse", | |
| "林芝": "Nyingchi", | |
| "昌都": "Qamdo", | |
| "银川": "Yinchuan", | |
| "石嘴山": "Shizuishan", | |
| "吴忠": "Wuzhong", | |
| "固原": "Guyuan", | |
| "西宁": "Xining", | |
| "格尔木": "Golmud", | |
| "玉树": "Yushu", | |
| "海东": "Haidong", | |
| "台北": "Taipei", | |
| "高雄": "Kaohsiung", | |
| "台中": "Taichung", | |
| "台南": "Tainan", | |
| "新竹": "Hsinchu", | |
| "基隆": "Keelung", | |
| # Theme Names - MOVED TO EN_TO_CN | |
| } | |
| # English name to Chinese (for when UI/Poster is in Chinese) | |
| EN_TO_CN = { | |
| "China": "中国", | |
| "USA": "美国", | |
| "United States": "美国", | |
| "Japan": "日本", | |
| "UK": "英国", | |
| "United Kingdom": "英国", | |
| "France": "法国", | |
| "Germany": "德国", | |
| "Italy": "意大利", | |
| "Spain": "西班牙", | |
| "Australia": "澳大利亚", | |
| "Canada": "加拿大", | |
| "South Korea": "韩国", | |
| "Singapore": "新加坡", | |
| "India": "印度", | |
| "Russia": "俄罗斯", | |
| "Brazil": "巴西", | |
| "Mexico": "墨西哥", | |
| "Netherlands": "荷兰", | |
| "Belgium": "比利时", | |
| "Switzerland": "瑞士", | |
| "Austria": "奥地利", | |
| "Portugal": "葡萄牙", | |
| "Greece": "希腊", | |
| "Turkey": "土耳其", | |
| "UAE": "阿联酋", | |
| "United Arab Emirates": "阿联酋", | |
| "Thailand": "泰国", | |
| "Vietnam": "越南", | |
| "Indonesia": "印度尼西亚", | |
| "Malaysia": "马来西亚", | |
| "Philippines": "菲律宾", | |
| "Egypt": "埃及", | |
| "South Africa": "南非", | |
| "Morocco": "摩洛哥", | |
| "Argentina": "阿根廷", | |
| "Chile": "智利", | |
| "Colombia": "哥伦比亚", | |
| "Peru": "秘鲁", | |
| "New Zealand": "新西兰", | |
| "Ireland": "爱尔兰", | |
| "Sweden": "瑞典", | |
| "Norway": "挪威", | |
| "Denmark": "丹麦", | |
| "Finland": "芬兰", | |
| "Poland": "波兰", | |
| "Czech Republic": "捷克", | |
| "Hungary": "匈牙利", | |
| "Israel": "以色列", | |
| "Saudi Arabia": "沙特阿拉伯", | |
| "Qatar": "卡塔尔", | |
| "Kenya": "肯尼亚", | |
| "Nigeria": "尼日利亚", | |
| "Pakistan": "巴基斯坦", | |
| "Bangladesh": "孟加拉国", | |
| "Sri Lanka": "斯里兰卡", | |
| "Nepal": "尼泊尔", | |
| # Common Cities | |
| "New York City": "纽约", | |
| "London": "伦敦", | |
| "Paris": "巴黎", | |
| "Tokyo": "东京", | |
| "Guangzhou": "广州", | |
| "Shenzhen": "深圳", | |
| "Beijing": "北京", | |
| "Shanghai": "上海", | |
| # US States | |
| "California": "加利福尼亚州", | |
| "New York": "纽约州", | |
| "Texas": "得克萨斯州", | |
| "Florida": "佛罗里达州", | |
| "Illinois": "伊利诺伊州", | |
| "Pennsylvania": "宾夕法尼亚州", | |
| "Arizona": "亚利桑那州", | |
| "Nevada": "内华达州", | |
| "Washington": "华盛顿州", | |
| "Massachusetts": "马萨诸塞州", | |
| "Colorado": "科罗拉多州", | |
| "Georgia": "佐治亚州", | |
| "North Carolina": "北卡罗来纳州", | |
| "Michigan": "密歇根州", | |
| "Oregon": "俄勒冈州", | |
| "District of Columbia": "华盛顿哥伦比亚特区", | |
| "Hawaii": "夏威夷州", | |
| # Major Cities | |
| "Los Angeles": "洛杉矶", | |
| "San Francisco": "旧金山", | |
| "San Diego": "圣地亚哥", | |
| "San Jose": "圣何塞", | |
| "Sacramento": "萨克拉门托", | |
| "Oakland": "奥克兰", | |
| "Fresno": "弗雷斯诺", | |
| "Buffalo": "布法罗", | |
| "Rochester": "罗切斯特", | |
| "Albany": "奥尔巴尼", | |
| "Syracuse": "雪城", | |
| "Houston": "休斯敦", | |
| "Dallas": "达拉斯", | |
| "Austin": "奥斯汀", | |
| "San Antonio": "圣安东尼奥", | |
| "Fort Worth": "沃思堡", | |
| "El Paso": "埃尔帕索", | |
| "Miami": "迈阿密", | |
| "Orlando": "奥兰多", | |
| "Tampa": "坦帕", | |
| "Jacksonville": "杰克逊维尔", | |
| "Fort Lauderdale": "堡劳德代尔", | |
| "Chicago": "芝加哥", | |
| "Aurora": "奥罗拉", | |
| "Philadelphia": "费城", | |
| "Pittsburgh": "匹兹堡", | |
| "Harrisburg": "哈里斯堡", | |
| "Phoenix": "菲尼克斯", | |
| "Tucson": "图森", | |
| "Las Vegas": "拉斯维加斯", | |
| "Reno": "里诺", | |
| "Seattle": "西雅图", | |
| "Tacoma": "塔科马", | |
| "Spokane": "斯波坎", | |
| "Boston": "波士顿", | |
| "Denver": "丹佛", | |
| "Atlanta": "亚特兰大", | |
| "Detroit": "底特律", | |
| "Portland": "波特兰", | |
| "Osaka": "大阪", | |
| "Kyoto": "京都", | |
| "Kobe": "神户", | |
| "Nagoya": "名古屋", | |
| "Sapporo": "札幌", | |
| "Fukuoka": "福冈", | |
| "Hiroshima": "广岛", | |
| "Liverpool": "利物浦", | |
| "Manchester": "曼彻斯特", | |
| "Birmingham": "伯明翰", | |
| "Leeds": "利兹", | |
| "Edinburgh": "爱丁堡", | |
| "Glasgow": "格拉斯哥", | |
| "Marseille": "马赛", | |
| "Lyon": "里昂", | |
| "Nice": "尼斯", | |
| "Berlin": "柏林", | |
| "Munich": "慕尼黑", | |
| "Hamburg": "汉堡", | |
| "Frankfurt": "法兰克福", | |
| "Rome": "罗马", | |
| "Milan": "米兰", | |
| "Venice": "威尼斯", | |
| "Florence": "佛罗伦萨", | |
| "Naples": "那不勒斯", | |
| "Madrid": "马德里", | |
| "Barcelona": "巴塞罗那", | |
| "Seville": "塞维利亚", | |
| "Sydney": "悉尼", | |
| "Melbourne": "墨尔本", | |
| "Brisbane": "布里斯班", | |
| "Perth": "珀斯", | |
| "Toronto": "多伦多", | |
| "Montreal": "蒙特利尔", | |
| "Vancouver": "温哥华", | |
| "Seoul": "首尔", | |
| "Busan": "釜山", | |
| "Mumbai": "孟买", | |
| "New Delhi": "新德里", | |
| "Bangalore": "班加罗尔", | |
| "Moscow": "莫斯科", | |
| "Saint Petersburg": "圣彼得堡", | |
| "Sao Paulo": "圣保罗", | |
| "Rio de Janeiro": "里约热内卢", | |
| "Mexico City": "墨西哥城", | |
| "Amsterdam": "阿姆斯特丹", | |
| "Brussels": "布鲁塞尔", | |
| "Zurich": "苏黎世", | |
| "Vienna": "维也纳", | |
| "Lisbon": "里斯本", | |
| "Athens": "雅典", | |
| "Istanbul": "伊斯坦布尔", | |
| "Dubai": "迪拜", | |
| "Bangkok": "曼谷", | |
| "Hanoi": "河内", | |
| "Ho Chi Minh City": "胡志明市", | |
| "Jakarta": "雅加达", | |
| "Kuala Lumpur": "吉隆坡", | |
| "Manila": "马尼拉", | |
| "Cairo": "开罗", | |
| "Johannesburg": "约翰内斯堡", | |
| "Cape Town": "开普敦", | |
| # Theme Names | |
| "Feature-Based Shading": "特征着色", | |
| "Japanese Ink": "日式水墨", | |
| "Midnight Blue": "午夜蓝", | |
| "Terracotta": "陶土色", | |
| "Autumn": "秋日", | |
| "Blueprint": "蓝图", | |
| "Contrast Zones": "高对比度", | |
| "Copper Patina": "铜绿", | |
| "Forest": "森林", | |
| "Gradient Roads": "渐变道路", | |
| "Monochrome Blue": "单色蓝", | |
| "Neon Cyberpunk": "霓虹赛博", | |
| "Noir": "诺尔黑白", | |
| "Ocean": "海洋", | |
| "Pastel Dream": "蜡笔梦幻", | |
| "Sunset": "日落", | |
| "Warm Beige": "温暖米色", | |
| } | |
| # Add inverse mappings to ensure completeness | |
| for k, v in CN_TO_EN.items(): | |
| if v not in EN_TO_CN: | |
| EN_TO_CN[v] = k | |
| # Expose helpers | |
| __all__ = [ | |
| "CITIES", | |
| "get_countries", | |
| "get_provinces", | |
| "get_cities", | |
| "get_districts", | |
| "get_city_full_name", | |
| "translate", | |
| "get_country_key", | |
| "get_manual_coordinates", | |
| "get_china_adcode", | |
| ] | |
| # Manual overrides for city centers to ensure logical centering (e.g. Tiananmen for Beijing) | |
| CITY_CENTERS = { | |
| "北京": (39.9042, 116.4074), | |
| "Beijing": (39.9042, 116.4074), | |
| "上海": (31.2304, 121.4737), | |
| "Shanghai": (31.2304, 121.4737), | |
| "广州": (23.1291, 113.2644), | |
| "Guangzhou": (23.1291, 113.2644), | |
| "深圳": (22.5422, 114.0579), | |
| "Shenzhen": (22.5422, 114.0579), | |
| "杭州": (30.2741, 120.1551), | |
| "Hangzhou": (30.2741, 120.1551), | |
| "成都": (30.6570, 104.0660), | |
| "Chengdu": (30.6570, 104.0660), | |
| "南京": (32.0603, 118.7969), | |
| "Nanjing": (32.0603, 118.7969), | |
| "武汉": (30.5928, 114.3055), | |
| "Wuhan": (30.5928, 114.3055), | |
| "西安": (34.3416, 108.9398), | |
| "Xi'an": (34.3416, 108.9398), | |
| "苏州": (31.2990, 120.5853), | |
| "Suzhou": (31.2990, 120.5853), | |
| "重庆": (29.5630, 106.5516), | |
| "Chongqing": (29.5630, 106.5516), | |
| "天津": (39.1255, 117.1901), | |
| "Tianjin": (39.1255, 117.1901), | |
| "香港": (22.3193, 114.1694), | |
| "Hong Kong": (22.3193, 114.1694), | |
| "台北": (25.0330, 121.5654), | |
| "Taipei": (25.0330, 121.5654), | |
| "New York City": (40.7128, -74.0060), | |
| "纽约": (40.7128, -74.0060), | |
| "London": (51.5074, -0.1278), | |
| "伦敦": (51.5074, -0.1278), | |
| "Paris": (48.8566, 2.3522), | |
| "巴黎": (48.8566, 2.3522), | |
| "Tokyo": (35.6895, 139.6917), | |
| "东京": (35.6895, 139.6917), | |
| } | |
| def get_manual_coordinates(name, parent_adcode=None): | |
| """Return manual coordinates if available, else None.""" | |
| if not name: | |
| return None | |
| # Priority 1: Hardcoded overrides | |
| coords = CITY_CENTERS.get(name) | |
| if coords: | |
| return coords | |
| # Priority 2: Local China data if parent_adcode is known | |
| if parent_adcode: | |
| info = _load_china_info() | |
| parent_entry = info.get(str(parent_adcode)) | |
| if parent_entry and "children" in parent_entry: | |
| for child in parent_entry["children"]: | |
| if child["name"] == name: | |
| # center is [lng, lat] | |
| return (child["center"][1], child["center"][0]) | |
| return None | |
| def get_china_adcode(name, parent_adcode=None): | |
| """Search for adcode by name in local China data.""" | |
| if not name: | |
| return None | |
| # Strip convention-based suffixes | |
| search_name = name.replace("_WHOLE", "") | |
| info = _load_china_info() | |
| if not parent_adcode: | |
| # Check provinces | |
| china_entry = info.get("100000") | |
| if china_entry: | |
| for p in china_entry.get("children", []): | |
| if p["name"] == search_name or p["name"].rstrip("省").rstrip("市") == search_name: | |
| return p["adcode"] | |
| else: | |
| parent_entry = info.get(str(parent_adcode)) | |
| if parent_entry: | |
| for child in parent_entry.get("children", []): | |
| if child["name"] == search_name: | |
| return child["adcode"] | |
| return None | |
| def translate(text, target_lang="en"): | |
| """ | |
| Simple translation helper. | |
| target_lang: 'en' or 'cn' | |
| """ | |
| if not text: | |
| return text | |
| if target_lang == "en": | |
| # If text is already in CN_TO_EN keys (Chinese), return the English value | |
| res = CN_TO_EN.get(text) | |
| if res: | |
| return res | |
| # Try stripping suffixes | |
| suffixes = ["市", "省", "自治区", "特别行政区", "壮族自治区", "回族自治区", "维吾尔自治区", "地区", "盟", "自治州"] | |
| for suffix in suffixes: | |
| if text.endswith(suffix): | |
| stripped = text[: -len(suffix)] | |
| res = CN_TO_EN.get(stripped) | |
| if res: | |
| return res | |
| return text | |
| if target_lang == "cn": | |
| # If text is in EN_TO_CN keys (English), return the Chinese value | |
| return EN_TO_CN.get(text, text) | |
| return text | |
| def get_countries(lang="en"): | |
| """Return list of (Display, Key) tuples for countries.""" | |
| choices = [] | |
| for country_key in sorted(CITIES.keys()): | |
| display = translate(country_key, lang) | |
| choices.append((display, country_key)) | |
| # Sort by display name | |
| return sorted(choices, key=lambda x: x[0]) | |
| def get_country_key(name): | |
| """Map a localized name back to the CITIES dictionary key.""" | |
| # Check if it's already a key | |
| if name in CITIES: | |
| return name | |
| # Check if it's an English name that needs to be Chinese key | |
| cn_name = EN_TO_CN.get(name) | |
| if cn_name in CITIES: | |
| return cn_name | |
| # Check if it's a Chinese name that needs to be English key | |
| en_name = CN_TO_EN.get(name) | |
| if en_name in CITIES: | |
| return en_name | |
| return name | |
| def get_provinces(country_name, lang="en"): | |
| """Return list of (Display, Key) tuples for provinces/states.""" | |
| country_key = get_country_key(country_name) | |
| # Special handling for China using local data | |
| if country_key == "中国": | |
| info = _load_china_info() | |
| china_entry = info.get("100000") | |
| if china_entry: | |
| choices = [] | |
| for p in china_entry.get("children", []): | |
| name = p["name"] | |
| # For UI display, maybe strip '省' if in English? | |
| # Actually following current project style, we use Chinese keys for China | |
| display = translate(name, lang) | |
| choices.append((display, name)) | |
| return sorted(choices, key=lambda x: x[0]) | |
| if country_key in CITIES: | |
| choices = [] | |
| for p_key in CITIES[country_key].keys(): | |
| display = translate(p_key, lang) | |
| choices.append((display, p_key)) | |
| return sorted(choices, key=lambda x: x[0]) | |
| return [] | |
| def get_cities(country_name, province_name, lang="en"): | |
| """Return list of (Display, Key) tuples for cities.""" | |
| country_key = get_country_key(country_name) | |
| all_choices = [] | |
| # Add "Whole Province" option for China | |
| if country_key == "中国": | |
| whole_province_display = "整个省" if lang == "cn" else "Whole Province" | |
| all_choices.append((whole_province_display, province_name + "_WHOLE")) | |
| # Try local China GeoJSON data (info.json) | |
| if country_key == "中国": | |
| p_adcode = get_china_adcode(province_name) | |
| if p_adcode: | |
| info = _load_china_info() | |
| p_entry = info.get(str(p_adcode)) | |
| if p_entry: | |
| # For municipalities, return [Whole Province, Municipality Name] | |
| if ( | |
| p_entry.get("children") | |
| and p_entry["children"][0].get("level") == "district" | |
| ): | |
| all_choices.append((translate(province_name, lang), province_name)) | |
| return all_choices | |
| for c in p_entry.get("children", []): | |
| name = c["name"] | |
| display = translate(name, lang) | |
| all_choices.append((display, name)) | |
| # If we found cities in info.json, we return them (plus Whole Province) | |
| if len(all_choices) > 1: | |
| # Keep "Whole Province" at top, then sorted cities | |
| return [all_choices[0]] + sorted(all_choices[1:], key=lambda x: x[0]) | |
| # Fallback to CITIES dictionary | |
| province_key = province_name | |
| if country_key in CITIES: | |
| if province_key not in CITIES[country_key]: | |
| for p_key in CITIES[country_key].keys(): | |
| transl_p = translate(p_key, lang) | |
| if (transl_p == province_key or | |
| (country_key == "中国" and (transl_p in province_key or province_key in transl_p))): | |
| province_key = p_key | |
| break | |
| if province_key in CITIES[country_key]: | |
| start_idx = len(all_choices) | |
| for c_name in CITIES[country_key][province_key]: | |
| display = translate(c_name, lang) | |
| all_choices.append((display, c_name)) | |
| if country_key == "中国": | |
| # With Whole Province at top | |
| return [all_choices[0]] + sorted(all_choices[1:], key=lambda x: x[0]) | |
| else: | |
| return sorted(all_choices, key=lambda x: x[0]) | |
| return all_choices | |
| def get_districts(country_name, province_name, city_name, lang="en"): | |
| """Return list of (Display, Key) tuples for districts.""" | |
| country_key = get_country_key(country_name) | |
| if country_key != "中国" or not city_name or city_name.endswith("_WHOLE"): | |
| return [] | |
| p_adcode = get_china_adcode(province_name) | |
| if not p_adcode: | |
| return [] | |
| c_adcode = get_china_adcode(city_name, p_adcode) | |
| if not c_adcode: | |
| # Case: City is the same as Province (Municipality) | |
| if city_name == province_name: | |
| c_adcode = p_adcode | |
| else: | |
| return [] | |
| info = _load_china_info() | |
| c_entry = info.get(str(c_adcode)) | |
| if c_entry: | |
| choices = [] | |
| # Add "Whole City" option | |
| whole_city_display = "整个城市" if lang == "cn" else "Whole City" | |
| choices.append( | |
| (whole_city_display, city_name) | |
| ) # Use city name as key for 'whole city' | |
| for d in c_entry.get("children", []): | |
| name = d["name"] | |
| display = translate(name, lang) | |
| choices.append((display, name)) | |
| # Keep "Whole City" at top, then sorted districts | |
| return [choices[0]] + sorted(choices[1:], key=lambda x: x[0]) | |
| return [] | |
| def search_cities(query): | |
| """ | |
| Search for cities matching the query (checks both Native and English names). | |
| Returns list of tuples: (city, province, country) | |
| """ | |
| if not query or len(query) < 2: | |
| return [] | |
| query = query.lower() | |
| results = [] | |
| for country, provinces in CITIES.items(): | |
| for province, cities in provinces.items(): | |
| for city in cities: | |
| city_en = CN_TO_EN.get(city, "").lower() | |
| # Match against native name or English name | |
| if query in city.lower() or (city_en and query in city_en): | |
| results.append((city, province, country)) | |
| # Sort by city name | |
| return sorted(results, key=lambda x: x[0])[:20] | |
| def get_city_full_name(city, province, country, target_lang=None): | |
| """ | |
| Return formatted full name. | |
| If target_lang is provided ('en' or 'cn'), translates components. | |
| """ | |
| if target_lang: | |
| city = translate(city, target_lang) | |
| province = translate(province, target_lang) | |
| country = translate(country, target_lang) | |
| if province == city: # Direct-controlled municipalities | |
| return f"{city}, {country}" | |
| return f"{city}, {province}, {country}" | |