278:         self._btn_select = QPushButton(tr("✔ Select this image"))
314:             f"✅ {tr('Auto-selected')}: {d}  {cloud_str}")
334:             self._lbl_selected.setText(f"✅ {tr('Selected')}: {d}  {cloud_str}")
358:         self.setWindowTitle(tr("🌿 Forest Change Detective — LVT4U"))
372:         self.setWindowTitle(tr("🌿 Forest Change Detective — LVT4U"))
374:             "🌿  <b>" + tr("🌿 Forest Change Detective").replace("🌿 ", "") + "</b>"
375:             "  —  <span style='color:#74C69D'>" + tr("Forest Change Detection & Deforestation Alert") + "</span>"
378:         self.tabs.setTabText(0, tr("🛰️ Data"))
379:         self.tabs.setTabText(1, tr("📊 Analysis"))
380:         self.tabs.setTabText(2, tr("📋 Results"))
381:         self.tabs.setTabText(3, tr("⚙️ Settings"))
383:         self.btn_analyze.setText(tr("▶  Analyze Changes"))
384:         self.btn_cancel.setText(tr("✖ Cancel"))
391:         self.btn_use_extent.setText(tr("📐 Use current canvas extent"))
406:         self.grp_mode.setTitle(tr("Select T₁ and T₂ Images"))
407:         self.rb_mode_a.setText(tr("A — Manual search from catalog"))
408:         self.rb_mode_b.setText(tr("B — Enter date, system auto-selects best image"))
409:         self.rb_mode_c.setText(tr("C — Combined (T₁ auto, T₂ manual or vice versa)"))
414:             self.btn_search.setText(tr("🔍 Search Images"))
416:         self._img_t1._lbl_title.setText(f"<b>{tr('📅 Image T₁ (Before)')}</b>")
417:         self._img_t2._lbl_title.setText(f"<b>{tr('📅 Image T₂ (After)')}</b>")
418:         self._img_t1._btn_select.setText(tr("✔ Select this image"))
419:         self._img_t2._btn_select.setText(tr("✔ Select this image"))
422:             self.page_b_layout.labelForField(self.de_t1_auto).setText(tr("T₁ date (target):"))
424:             self.page_b_layout.labelForField(self.de_t2_auto).setText(tr("T₂ date (target):"))
426:             self.page_b_layout.labelForField(self.spin_window_days).setText(tr("Search window ±:"))
430:             self.btn_auto_find.setText(tr("🤖 Auto-find best images"))
433:             self.page_b_layout.labelForField(self._lbl_auto_t1).setText(tr("T₁ selected:"))
435:             self.page_b_layout.labelForField(self._lbl_auto_t2).setText(tr("T₂ selected:"))
441:         if "DEFORESTATION" in self._mode_radios: self._mode_radios["DEFORESTATION"].setText(tr("🌲 Deforestation / Forest Loss"))
442:         if "FIRE" in self._mode_radios: self._mode_radios["FIRE"].setText(tr("🔥 Fire / Burn Scar"))
443:         if "DEGRADATION" in self._mode_radios: self._mode_radios["DEGRADATION"].setText(tr("📉 Degradation / Vegetation Recovery"))
444:         if "MOISTURE" in self._mode_radios: self._mode_radios["MOISTURE"].setText(tr("💧 Canopy Moisture Change"))
448:         self.btn_reset_thresh.setText(tr("↺ Reset to defaults"))
454:                     dir_item.setText(tr("Giảm ≥"))
456:                     dir_item.setText(tr("Tăng ≥"))
465:         self.grp_firms.setTitle(tr("🔥 FIRMS Fire Hotspot Validation (NASA)"))
479:         self.btn_open_gpkg.setText(tr("📂 Open GeoPackage"))
480:         self.btn_export_kml.setText(tr("🗺 Export KML"))
481:         self.btn_export_json.setText(tr("📄 Export GeoJSON"))
482:         self.btn_load_canvas.setText(tr("🗺 Add to QGIS"))
503:         self.btn_save.setText(tr("💾 Save settings"))
606:             "🌿  <b>" + tr("🌿 Forest Change Detective").replace("🌿 ", "") + "</b>"
607:             "  —  <span style='color:#74C69D'>" + tr("Forest Change Detection & Deforestation Alert") + "</span>"
619:         self.tabs.addTab(self._build_tab_data(),     tr("🛰️ Data"))
620:         self.tabs.addTab(self._build_tab_analysis(), tr("📊 Analysis"))
621:         self.tabs.addTab(self._build_tab_results(),  tr("📋 Results"))
622:         self.tabs.addTab(self._build_tab_settings(), tr("⚙️ Settings"))
639:         self.btn_analyze = QPushButton(tr("▶  Analyze Changes"))
648:         self.btn_cancel = QPushButton(tr("✖ Cancel"))
655:         self.btn_help = QPushButton(tr("📖 Hướng dẫn"))
694:             tr("Chọn vùng nghiên cứu (ROI) — Region of Interest\n\n"
700:         self.roi_layout.addRow(tr("Layer từ QGIS:"), self.cb_roi_layer)
702:         self.btn_use_extent = QPushButton(tr("📐 Dùng vùng nhìn thấy trên bản đồ"))
704:             tr("Dùng vùng hiển thị hiện tại trên QGIS làm ROI\n\n"
722:             tr("Sentinel-2 L2A (Quang học, 10m — Khuyên dùng)"),
723:             tr("Sentinel-2 + Sentinel-1 SAR (Kết hợp radar)"),
724:             tr("Landsat 8/9 (30m — Vùng nhỏ hơn 500 ha)"),
727:             tr("Chọn nguồn ảnh vệ tinh\n\n"
737:         self.sensor_layout.addRow(tr("Ảnh vệ tinh:"), self.cb_sensor)
744:             tr("Tỉ lệ mây tối đa — Max Cloud Cover\n\n"
752:         self.sensor_layout.addRow(tr("☁️ Mây tối đa:"), self.spin_max_cloud)
757:         self.grp_mode = QGroupBox(tr("Select T₁ and T₂ Images"))
761:         self.rb_mode_a = QRadioButton(tr("A — Manual search from catalog"))
762:         self.rb_mode_b = QRadioButton(tr("B — Enter date, system auto-selects best image"))
763:         self.rb_mode_c = QRadioButton(tr("C — Combined (T₁ auto, T₂ manual or vice versa)"))
799:         self.btn_search = QPushButton(tr("🔍 Search Images"))
811:         self._img_t1 = _ImageListWidget(tr("📅 Image T₁ (Before)"))
812:         self._img_t2 = _ImageListWidget(tr("📅 Image T₂ (After)"))
843:         self.page_b_layout.addRow(tr("T₁ date (target):"), self.de_t1_auto)
844:         self.page_b_layout.addRow(tr("T₂ date (target):"), self.de_t2_auto)
845:         self.page_b_layout.addRow(tr("Search window ±:"), self.spin_window_days)
847:         self.btn_auto_find = QPushButton(tr("🤖 Auto-find best images"))
855:         self._lbl_auto_t1 = QLabel(tr("T₁: not searched yet"))
856:         self._lbl_auto_t2 = QLabel(tr("T₂: not searched yet"))
857:         self.page_b_layout.addRow(tr("T₁ selected:"), self._lbl_auto_t1)
858:         self.page_b_layout.addRow(tr("T₂ selected:"), self._lbl_auto_t2)
874:         btn_browse_forest = QPushButton(tr("📂"))
966:         self.grp_thresh = QGroupBox(tr("Threshold Settings — Ngưỡng phát hiện"))
969:             tr("Ngưỡng chỉ số (editable)\n\n"
980:             tr("Chỉ số"), tr("Chiều biến động"), tr("Ngưỡng tối thiểu"), tr("Đơn vị")])
984:             tr("Chỉ số: Tên chỉ số (NDVI, EVI2, dNBR...)\n"
994:             tr("Δ = Chi so(T2) − Chi so(T1)\n"
1013:         self.btn_reset_thresh = QPushButton(tr("\u21ba Đặt lại mặc định"))
1014:         self.btn_reset_thresh.setToolTip(tr("Khôi phục các ngưỡng về giá trị mặc định khoa học"))
1021:         self.grp_mmu = QGroupBox(tr("Filter Settings — Lọc kết quả"))
1030:             tr("Đơn vị Lập Bản Đồ Tối Thiểu (MMU)\n\n"
1038:         self.mmu_layout.addRow(tr("📍 Diện tích tối thiểu (MMU):"), self.spin_mmu)
1040:         self.cb_use_s1 = QCheckBox(tr("Kết hợp Sentinel-1 SAR (Radar xuyên mây)"))
1042:             tr("Thêm dữ liệu Sentinel-1 SAR vào phân tích\n\n"
1057:             tr("Ngưỡng độ dốc DEM — lọc nhiễu SAR trên núi\n\n"
1064:         self.mmu_layout.addRow(tr("⛰️ Lọc dốc DEM (SAR):"),
1070:         self.grp_firms = QGroupBox(tr("🔥 FIRMS Fire Hotspot Validation (NASA)"))
1133:         self.btn_preview = QPushButton(tr("🔴  Xem Preview — Nhấp nháy vùng biến động"))
1150:         self.btn_open_gpkg = QPushButton(tr("📂 Open GeoPackage"))
1151:         self.btn_export_kml = QPushButton(tr("🗺 Export KML"))
1152:         self.btn_export_json = QPushButton(tr("📄 Export GeoJSON"))
1153:         self.btn_load_canvas = QPushButton(tr("🗺 Add to QGIS"))
1200:         btn_dem = QPushButton(tr("📂"))
1214:         btn_out = QPushButton(tr("📂"))
1221:         self.btn_save = QPushButton(tr("💾 Save settings"))
1257:             dir_label = tr("Giảm ≥") if direction == "below" else tr("Tăng ≥")
1266:                 tr("Nhập số dương. Hệ thống sẽ tự áp dụng dấu theo chiều biến động.\n"
1290:                 if tr("Giảm") in dir_text or "Decrease" in dir_text or "below" in dir_text:
1384:         self.btn_search.setText(tr("🔄 Searching..."))
1400:         self.btn_search.setText(tr("🔍 Search Images"))
1413:         self.btn_search.setText(tr("🔍 Search Images"))
1438:         self.btn_auto_find.setText(tr("🤖 Searching T₁..."))
1467:         self.btn_auto_find.setText(tr("🤖 Searching T₂..."))
1481:         self.btn_auto_find.setText(tr("🤖 Auto-find best images"))
1505:                 self, tr("Missing Image T₁"),
1506:                 tr("Please select or auto-find image T₁ first."))
1511:                 self, tr("Missing Image T₂"),
1512:                 tr("Please select or auto-find image T₂ first."))
1566:                             "dry": tr("mùa khô (tháng 11–4)"),
1567:                             "wet": tr("mùa mưa (tháng 5–10)"),
1570:                             tr("⚠️  CẢNH BÁO: HAI ẢNH Ở 2 MÙA KHÁC NHAU!\n\n")
1571:                             + tr(f"T₁ ({t1_str}): {_s_label[s1]}\n")
1572:                             + tr(f"T₂ ({t2_str}): {_s_label[s2]}\n\n")
1573:                             + tr("VẤN ĐỀ:\n")
1574:                             + tr("Mùa khô: lá rụng, NDVI thấp (~0.45)\n")
1575:                             + tr("Mùa mưa: lá xanh đầy, NDVI cao (~0.65)\n")
1576:                             + tr("→ ΔNDVI = +0.20 (dương) → Rừng 'tăng' → Che khuất khai thác!\n\n")
1577:                             + tr("GIẢI PHÁP TỐT NHẤT:\n")
1578:                             + tr("  Chọn T₁ và T₂ CÙNG MÙA (cùng tháng, khác năm):\n")
1579:                             + tr("  Ví dụ: T₁ = Tháng 1/2025, T₂ = Tháng 1/2026\n\n")
1580:                             + tr("Bấm 'Tiếp tục' để chạy dù có cảnh báo\n"
1586:                             tr("⚠️ Cảnh báo Mùa vụ"),
1608:                         self, tr("⚠️ Khoảng cách quá ngắn"),
1609:                         tr(f"T1 và T2 chỉ cách nhau {interval_days} ngày!\n\n"
1660:         self._lbl_status.setText(tr("✅ Analysis complete!"))
1707:             "critical_count": tr("CRITICAL (≥1 ha)"),
1708:             "warning_count":  tr("WARNING (0.05–1 ha)"),
2098:             dlg.setWindowTitle(tr("📖 Forest Change Detective — User Guide"))
2100:             dlg.setWindowTitle(tr("📖 Forest Change Detective — Hướng dẫn sử dụng"))
2123:             tabs.addTab(_browser(tips_html),  tr("⚡ Quick Tips"))
2124:             tabs.addTab(_browser(guide_html), tr("📚 Full Guide"))
2125:             tabs.addTab(_browser(param_html), tr("📋 Parameter Reference"))
2127:             tabs.addTab(_browser(tips_html),  tr("⚡ Quick Tips"))
2128:             tabs.addTab(_browser(guide_html), tr("📚 Hướng dẫn đầy đủ"))
2129:             tabs.addTab(_browser(param_html), tr("📋 Bảng tham số"))
2174:                 self, tr("Không có kết quả"),
2175:                 tr(f"File kết quả không tồn tại:\n{gpkg}\n\nHãy chạy phân tích trước."))
2184:                     self, tr("File lỗi"),
2185:                     tr(f"Không thể mở file GPKG:\n{gpkg}\n\n"
2200:                 self, tr("Kết quả rỗng"),
2201:                 tr("Kết quả phân tích có 0 polygon.\n\n"
2208:             layer_display_name = tr(f"Forest Change — {mode}")
2238:                     self, tr("Thất bại"),
2239:                     tr(f"Không thể thêm layer tự động.\n\n"
2249:                     tr(f"Lỗi tương thích Python 3.14 (inspect.currentFrame).\n\n"
2253:                 QMessageBox.critical(self, tr("Lỗi"), tr(f"AttributeError: {ae}\nFile: {gpkg}"))
2257:                 self, tr("Lỗi Add to QGIS"),
2258:                 tr(f"Lỗi: {e}\n\nFile: {gpkg}"))
