Spaces:
Sleeping
Sleeping
Commit ·
bb193ea
1
Parent(s): 5761a4f
Complete Malaysian translation support for all results and PDF content
Browse files- Added comprehensive translations for executive summary, nutrient status tables, economic forecast
- Updated step titles and descriptions with Malaysian translations
- Added table column headers translations (Parameter, Average, Status, etc.)
- Updated PDF generation to use translated headers and status labels
- Added economic forecast labels translation (Land Size, Current Yield, etc.)
- Updated investment scenarios with Malaysian translations
- Added status labels translation (Deficient, Optimal, Excessive)
- modules/results.py +119 -65
- utils/pdf_utils.py +26 -20
- utils/translations.py +162 -0
modules/results.py
CHANGED
|
@@ -3116,19 +3116,28 @@ def display_leaf_data_table(leaf_data):
|
|
| 3116 |
def display_summary_section(results_data):
|
| 3117 |
"""Display a comprehensive Executive Summary with farmer-friendly explanations"""
|
| 3118 |
st.markdown("---")
|
| 3119 |
-
st.markdown("## 📝 **Executive Summary**")
|
| 3120 |
-
st.markdown("*Key insights from your soil and leaf analysis in simple terms*")
|
| 3121 |
|
| 3122 |
# Add farmer-friendly introduction
|
| 3123 |
-
|
| 3124 |
-
|
|
|
|
|
|
|
| 3125 |
|
| 3126 |
-
|
| 3127 |
-
|
| 3128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3129 |
|
| 3130 |
st.markdown("---")
|
| 3131 |
-
st.markdown("### 📊 **Executive Summary**")
|
| 3132 |
|
| 3133 |
# Get analysis data with type checking
|
| 3134 |
analysis_results = get_analysis_results_from_data(results_data)
|
|
@@ -5690,23 +5699,34 @@ def display_analysis_components(analysis_results):
|
|
| 5690 |
# Check for economic forecast data
|
| 5691 |
economic_forecast = analysis_results.get('economic_forecast')
|
| 5692 |
if economic_forecast:
|
| 5693 |
-
st.markdown("## 📈 Economic Forecast")
|
| 5694 |
display_economic_forecast(economic_forecast)
|
| 5695 |
|
| 5696 |
def display_step_block(step_result, step_number, step_title):
|
| 5697 |
"""Display step results in a professional block format with clear visual hierarchy"""
|
| 5698 |
|
| 5699 |
-
# Define step-specific colors and icons
|
| 5700 |
-
|
| 5701 |
-
|
| 5702 |
-
|
| 5703 |
-
|
| 5704 |
-
|
| 5705 |
-
|
| 5706 |
-
|
| 5707 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5708 |
|
| 5709 |
-
config = step_configs.get(step_number, {"color": "#667eea", "icon": "📋", "description":
|
| 5710 |
|
| 5711 |
# Create a prominent step header with step-specific styling
|
| 5712 |
st.markdown(f"""
|
|
@@ -6367,7 +6387,7 @@ def display_table(table_data, title):
|
|
| 6367 |
|
| 6368 |
def display_step1_data_analysis(analysis_data):
|
| 6369 |
"""Display Step 1: Data Analysis content"""
|
| 6370 |
-
st.markdown("### 📊 Data Analysis Results")
|
| 6371 |
|
| 6372 |
# Display nutrient comparisons
|
| 6373 |
if 'nutrient_comparisons' in analysis_data:
|
|
@@ -6407,7 +6427,7 @@ def display_step1_data_analysis(analysis_data):
|
|
| 6407 |
|
| 6408 |
def display_step3_solution_recommendations(analysis_data):
|
| 6409 |
"""Display Step 3: Solution Recommendations content"""
|
| 6410 |
-
st.markdown("### 💡 Solution Recommendations")
|
| 6411 |
|
| 6412 |
# Display solutions
|
| 6413 |
if 'solutions' in analysis_data and analysis_data['solutions']:
|
|
@@ -6477,7 +6497,7 @@ def display_step3_solution_recommendations(analysis_data):
|
|
| 6477 |
|
| 6478 |
def display_step2_issue_diagnosis(analysis_data):
|
| 6479 |
"""Display Step 2: Issue Diagnosis content with consistent formatting"""
|
| 6480 |
-
st.markdown("### 🔍 Issue Diagnosis")
|
| 6481 |
|
| 6482 |
# Display summary if available
|
| 6483 |
if 'summary' in analysis_data and analysis_data['summary']:
|
|
@@ -6567,7 +6587,7 @@ def display_step2_issue_diagnosis(analysis_data):
|
|
| 6567 |
def display_step5_economic_forecast(analysis_data):
|
| 6568 |
"""Display Step 5: Economic Impact Forecast content with consistent formatting"""
|
| 6569 |
try:
|
| 6570 |
-
st.markdown("### 💰 Economic Impact Forecast")
|
| 6571 |
|
| 6572 |
# Display accurate tables directly from backend data if available
|
| 6573 |
# Try multiple locations for economic_forecast data
|
|
@@ -8113,21 +8133,32 @@ def display_analysis_components(analysis_results):
|
|
| 8113 |
economic_forecast = analysis_results.get('economic_forecast', {})
|
| 8114 |
if economic_forecast:
|
| 8115 |
st.markdown("---")
|
| 8116 |
-
st.markdown("## 📈 Economic Forecast")
|
| 8117 |
display_economic_forecast(economic_forecast)
|
| 8118 |
|
| 8119 |
def display_step_block(step_result, step_number, step_title):
|
| 8120 |
"""Display step results in a professional block format with clear visual hierarchy"""
|
| 8121 |
|
| 8122 |
-
# Define step-specific colors and icons
|
| 8123 |
-
|
| 8124 |
-
|
| 8125 |
-
|
| 8126 |
-
|
| 8127 |
-
|
| 8128 |
-
|
| 8129 |
-
|
| 8130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8131 |
|
| 8132 |
config = step_configs.get(step_number, {"color": "#667eea", "icon": "📋", "description": "Analysis Step"})
|
| 8133 |
|
|
@@ -11528,7 +11559,7 @@ def create_soil_vs_mpob_visualization_with_robust_mapping(analysis_data):
|
|
| 11528 |
|
| 11529 |
return {
|
| 11530 |
'type': 'actual_vs_optimal_bar',
|
| 11531 |
-
'title':
|
| 11532 |
'subtitle': 'REAL values from your current data - Observed (Average) vs Recommended (MPOB)',
|
| 11533 |
'data': {
|
| 11534 |
'categories': categories,
|
|
@@ -11661,7 +11692,7 @@ def create_leaf_vs_mpob_visualization_with_robust_mapping(analysis_data):
|
|
| 11661 |
|
| 11662 |
return {
|
| 11663 |
'type': 'actual_vs_optimal_bar',
|
| 11664 |
-
'title':
|
| 11665 |
'subtitle': 'REAL values from your current data - Observed (Average) vs Recommended (MPOB)',
|
| 11666 |
'data': {
|
| 11667 |
'categories': categories,
|
|
@@ -11843,7 +11874,7 @@ def create_sample_step1_visualizations():
|
|
| 11843 |
# Create sample soil vs MPOB visualization with exact values from image tables
|
| 11844 |
sample_soil_viz = {
|
| 11845 |
'type': 'actual_vs_optimal_bar',
|
| 11846 |
-
'title':
|
| 11847 |
'subtitle': 'Demonstration of soil nutrient analysis visualization',
|
| 11848 |
'data': {
|
| 11849 |
'categories': ['pH', 'N (%)', 'Org. C (%)', 'Total P (mg/kg)', 'Avail P (mg/kg)', 'Exch. K (meq/100 g)', 'Exch. Ca (meq/100 g)', 'Exch. Mg (meq/100 g)', 'CEC (meq/100 g)'],
|
|
@@ -11863,7 +11894,7 @@ def create_sample_step1_visualizations():
|
|
| 11863 |
# Create sample leaf vs MPOB visualization with exact values from image tables
|
| 11864 |
sample_leaf_viz = {
|
| 11865 |
'type': 'actual_vs_optimal_bar',
|
| 11866 |
-
'title':
|
| 11867 |
'subtitle': 'Demonstration of leaf nutrient analysis visualization',
|
| 11868 |
'data': {
|
| 11869 |
'categories': ['N (%)', 'P (%)', 'K (%)', 'Mg (%)', 'Ca (%)', 'B (mg/kg)', 'Cu (mg/kg)', 'Zn (mg/kg)'],
|
|
@@ -11980,7 +12011,7 @@ def create_soil_vs_mpob_visualization(soil_params):
|
|
| 11980 |
|
| 11981 |
return {
|
| 11982 |
'type': 'actual_vs_optimal_bar',
|
| 11983 |
-
'title':
|
| 11984 |
'subtitle': 'EXACT values copied from Soil Nutrient Status table',
|
| 11985 |
'data': {
|
| 11986 |
'categories': categories,
|
|
@@ -12089,7 +12120,7 @@ def create_leaf_vs_mpob_visualization(leaf_params):
|
|
| 12089 |
|
| 12090 |
return {
|
| 12091 |
'type': 'actual_vs_optimal_bar',
|
| 12092 |
-
'title':
|
| 12093 |
'subtitle': 'EXACT values copied from Leaf Nutrient Status table',
|
| 12094 |
'data': {
|
| 12095 |
'categories': categories,
|
|
@@ -12945,7 +12976,7 @@ def display_nutrient_status_tables(analysis_data):
|
|
| 12945 |
|
| 12946 |
# Display Soil Nutrient Status table - BULLETPROOF VERSION
|
| 12947 |
if soil_params and 'parameter_statistics' in soil_params:
|
| 12948 |
-
st.markdown("### 🌱 Soil Nutrient Status (Average vs. MPOB Standard)")
|
| 12949 |
|
| 12950 |
# Create soil data list with BULLETPROOF validation
|
| 12951 |
soil_data = []
|
|
@@ -13051,7 +13082,7 @@ def display_nutrient_status_tables(analysis_data):
|
|
| 13051 |
|
| 13052 |
# Display Leaf Nutrient Status table - BULLETPROOF VERSION
|
| 13053 |
if leaf_params and 'parameter_statistics' in leaf_params:
|
| 13054 |
-
st.markdown("### 🍃 Leaf Nutrient Status (Average vs. MPOB Standard)")
|
| 13055 |
|
| 13056 |
# Create leaf data list with BULLETPROOF validation
|
| 13057 |
leaf_data = []
|
|
@@ -15073,12 +15104,12 @@ def display_economic_impact_content(analysis_data):
|
|
| 15073 |
# Display key metrics
|
| 15074 |
col1, col2, col3 = st.columns(3)
|
| 15075 |
with col1:
|
| 15076 |
-
st.metric("🌾 Current Yield", f"{current_yield:.1f} tonnes/ha")
|
| 15077 |
with col2:
|
| 15078 |
-
st.metric("🏞️ Land Size", f"{land_size:.1f} hectares")
|
| 15079 |
with col3:
|
| 15080 |
# Do not derive ROI from scenarios; show placeholder or omit
|
| 15081 |
-
st.metric("💰 Estimated ROI", "N/A")
|
| 15082 |
|
| 15083 |
# Display any additional tables from analysis data
|
| 15084 |
display_analysis_tables(analysis_data.get('tables'), "Economic Analysis Data Tables")
|
|
@@ -15110,19 +15141,28 @@ def display_economic_impact_content(analysis_data):
|
|
| 15110 |
# Fallback to LLM-generated economic analysis
|
| 15111 |
col1, col2, col3 = st.columns(3)
|
| 15112 |
with col1:
|
| 15113 |
-
st.metric(
|
| 15114 |
with col2:
|
| 15115 |
-
st.metric(
|
| 15116 |
with col3:
|
| 15117 |
-
st.metric(
|
| 15118 |
|
| 15119 |
if 'cost_benefit' in econ_data:
|
| 15120 |
-
|
| 15121 |
-
|
| 15122 |
-
st.markdown(
|
| 15123 |
-
|
| 15124 |
-
|
| 15125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15126 |
|
| 15127 |
# Display any additional tables from analysis data
|
| 15128 |
display_analysis_tables(analysis_data.get('tables'), "Economic Analysis Data Tables")
|
|
@@ -15147,17 +15187,31 @@ def display_economic_impact_content(analysis_data):
|
|
| 15147 |
with col3:
|
| 15148 |
st.metric("⏱️ Payback Period", "24-36 months")
|
| 15149 |
|
| 15150 |
-
|
| 15151 |
-
|
| 15152 |
-
|
| 15153 |
-
|
| 15154 |
-
|
| 15155 |
-
|
| 15156 |
-
|
| 15157 |
-
|
| 15158 |
-
|
| 15159 |
-
|
| 15160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15161 |
apply_table_styling()
|
| 15162 |
st.dataframe(df, use_container_width=True)
|
| 15163 |
|
|
@@ -15269,7 +15323,7 @@ def display_forecast_graph_content(analysis_data, step_number=None, step_title=N
|
|
| 15269 |
break
|
| 15270 |
|
| 15271 |
if baseline_yield > 0:
|
| 15272 |
-
st.markdown(f"**Current Yield Baseline:** {baseline_yield:.1f} tonnes/hectare")
|
| 15273 |
st.markdown("")
|
| 15274 |
|
| 15275 |
try:
|
|
|
|
| 3116 |
def display_summary_section(results_data):
|
| 3117 |
"""Display a comprehensive Executive Summary with farmer-friendly explanations"""
|
| 3118 |
st.markdown("---")
|
| 3119 |
+
st.markdown(f"## 📝 **{t('executive_summary', 'Executive Summary')}**")
|
| 3120 |
+
st.markdown(f"*{t('executive_summary_desc', 'Key insights from your soil and leaf analysis in simple terms')}*")
|
| 3121 |
|
| 3122 |
# Add farmer-friendly introduction
|
| 3123 |
+
current_lang = get_language()
|
| 3124 |
+
if current_lang == 'ms':
|
| 3125 |
+
st.markdown("""
|
| 3126 |
+
**🌱 Laporan Kesihatan Kelapa Sawit Anda**
|
| 3127 |
|
| 3128 |
+
Ringkasan ini memberikan anda **gambaran besar** tentang apa yang berlaku dengan tanah dan pokok sawit anda.
|
| 3129 |
+
Kami telah menganalisis keputusan makmal anda dan mencipta gambaran keseluruhan yang mudah difahami ini.
|
| 3130 |
+
""")
|
| 3131 |
+
else:
|
| 3132 |
+
st.markdown("""
|
| 3133 |
+
**🌱 Your Oil Palm Health Report**
|
| 3134 |
+
|
| 3135 |
+
This summary gives you the **big picture** of what's happening with your soil and palm trees.
|
| 3136 |
+
We've analyzed your lab results and created this easy-to-understand overview.
|
| 3137 |
+
""")
|
| 3138 |
|
| 3139 |
st.markdown("---")
|
| 3140 |
+
st.markdown(f"### 📊 **{t('executive_summary', 'Executive Summary')}**")
|
| 3141 |
|
| 3142 |
# Get analysis data with type checking
|
| 3143 |
analysis_results = get_analysis_results_from_data(results_data)
|
|
|
|
| 5699 |
# Check for economic forecast data
|
| 5700 |
economic_forecast = analysis_results.get('economic_forecast')
|
| 5701 |
if economic_forecast:
|
| 5702 |
+
st.markdown(f"## 📈 {t('economic_forecast', 'Economic Forecast')}")
|
| 5703 |
display_economic_forecast(economic_forecast)
|
| 5704 |
|
| 5705 |
def display_step_block(step_result, step_number, step_title):
|
| 5706 |
"""Display step results in a professional block format with clear visual hierarchy"""
|
| 5707 |
|
| 5708 |
+
# Define step-specific colors and icons with translations
|
| 5709 |
+
current_lang = get_language()
|
| 5710 |
+
if current_lang == 'ms':
|
| 5711 |
+
step_configs = {
|
| 5712 |
+
1: {"color": "#667eea", "icon": "📊", "description": "Analisis Data & Interpretasi"},
|
| 5713 |
+
2: {"color": "#f093fb", "icon": "🔍", "description": "Diagnosis Isu & Pengenalan Masalah"},
|
| 5714 |
+
3: {"color": "#4facfe", "icon": "💡", "description": "Cadangan Penyelesaian & Strategi"},
|
| 5715 |
+
4: {"color": "#43e97b", "icon": "🌱", "description": "Integrasi Pertanian Regeneratif"},
|
| 5716 |
+
5: {"color": "#fa709a", "icon": "💰", "description": "Kesan Ekonomi & Analisis ROI"},
|
| 5717 |
+
6: {"color": "#000000", "icon": "📈", "description": "Ramalan & Unjuran Hasil"}
|
| 5718 |
+
}
|
| 5719 |
+
else:
|
| 5720 |
+
step_configs = {
|
| 5721 |
+
1: {"color": "#667eea", "icon": "📊", "description": "Data Analysis & Interpretation"},
|
| 5722 |
+
2: {"color": "#f093fb", "icon": "🔍", "description": "Issue Diagnosis & Problem Identification"},
|
| 5723 |
+
3: {"color": "#4facfe", "icon": "💡", "description": "Solution Recommendations & Strategies"},
|
| 5724 |
+
4: {"color": "#43e97b", "icon": "🌱", "description": "Regenerative Agriculture Integration"},
|
| 5725 |
+
5: {"color": "#fa709a", "icon": "💰", "description": "Economic Impact & ROI Analysis"},
|
| 5726 |
+
6: {"color": "#000000", "icon": "📈", "description": "Yield Forecast & Projections"}
|
| 5727 |
+
}
|
| 5728 |
|
| 5729 |
+
config = step_configs.get(step_number, {"color": "#667eea", "icon": "📋", "description": t('results_step_analysis', 'Analysis Step')})
|
| 5730 |
|
| 5731 |
# Create a prominent step header with step-specific styling
|
| 5732 |
st.markdown(f"""
|
|
|
|
| 6387 |
|
| 6388 |
def display_step1_data_analysis(analysis_data):
|
| 6389 |
"""Display Step 1: Data Analysis content"""
|
| 6390 |
+
st.markdown(f"### 📊 {t('results_data_analysis', 'Data Analysis Results')}")
|
| 6391 |
|
| 6392 |
# Display nutrient comparisons
|
| 6393 |
if 'nutrient_comparisons' in analysis_data:
|
|
|
|
| 6427 |
|
| 6428 |
def display_step3_solution_recommendations(analysis_data):
|
| 6429 |
"""Display Step 3: Solution Recommendations content"""
|
| 6430 |
+
st.markdown(f"### 💡 {t('results_solution_recommendations', 'Solution Recommendations')}")
|
| 6431 |
|
| 6432 |
# Display solutions
|
| 6433 |
if 'solutions' in analysis_data and analysis_data['solutions']:
|
|
|
|
| 6497 |
|
| 6498 |
def display_step2_issue_diagnosis(analysis_data):
|
| 6499 |
"""Display Step 2: Issue Diagnosis content with consistent formatting"""
|
| 6500 |
+
st.markdown(f"### 🔍 {t('results_issue_diagnosis', 'Issue Diagnosis')}")
|
| 6501 |
|
| 6502 |
# Display summary if available
|
| 6503 |
if 'summary' in analysis_data and analysis_data['summary']:
|
|
|
|
| 6587 |
def display_step5_economic_forecast(analysis_data):
|
| 6588 |
"""Display Step 5: Economic Impact Forecast content with consistent formatting"""
|
| 6589 |
try:
|
| 6590 |
+
st.markdown(f"### 💰 {t('results_economic_impact', 'Economic Impact Forecast')}")
|
| 6591 |
|
| 6592 |
# Display accurate tables directly from backend data if available
|
| 6593 |
# Try multiple locations for economic_forecast data
|
|
|
|
| 8133 |
economic_forecast = analysis_results.get('economic_forecast', {})
|
| 8134 |
if economic_forecast:
|
| 8135 |
st.markdown("---")
|
| 8136 |
+
st.markdown(f"## 📈 {t('economic_forecast', 'Economic Forecast')}")
|
| 8137 |
display_economic_forecast(economic_forecast)
|
| 8138 |
|
| 8139 |
def display_step_block(step_result, step_number, step_title):
|
| 8140 |
"""Display step results in a professional block format with clear visual hierarchy"""
|
| 8141 |
|
| 8142 |
+
# Define step-specific colors and icons with translations
|
| 8143 |
+
current_lang = get_language()
|
| 8144 |
+
if current_lang == 'ms':
|
| 8145 |
+
step_configs = {
|
| 8146 |
+
1: {"color": "#667eea", "icon": "📊", "description": "Analisis Data & Interpretasi"},
|
| 8147 |
+
2: {"color": "#f093fb", "icon": "🔍", "description": "Diagnosis Isu & Pengenalan Masalah"},
|
| 8148 |
+
3: {"color": "#4facfe", "icon": "💡", "description": "Cadangan Penyelesaian & Strategi"},
|
| 8149 |
+
4: {"color": "#43e97b", "icon": "🌱", "description": "Integrasi Pertanian Regeneratif"},
|
| 8150 |
+
5: {"color": "#fa709a", "icon": "💰", "description": "Kesan Ekonomi & Analisis ROI"},
|
| 8151 |
+
6: {"color": "#000000", "icon": "📈", "description": "Ramalan & Unjuran Hasil"}
|
| 8152 |
+
}
|
| 8153 |
+
else:
|
| 8154 |
+
step_configs = {
|
| 8155 |
+
1: {"color": "#667eea", "icon": "📊", "description": "Data Analysis & Interpretation"},
|
| 8156 |
+
2: {"color": "#f093fb", "icon": "🔍", "description": "Issue Diagnosis & Problem Identification"},
|
| 8157 |
+
3: {"color": "#4facfe", "icon": "💡", "description": "Solution Recommendations & Strategies"},
|
| 8158 |
+
4: {"color": "#43e97b", "icon": "🌱", "description": "Regenerative Agriculture Integration"},
|
| 8159 |
+
5: {"color": "#fa709a", "icon": "💰", "description": "Economic Impact & ROI Analysis"},
|
| 8160 |
+
6: {"color": "#000000", "icon": "📈", "description": "Yield Forecast & Projections"}
|
| 8161 |
+
}
|
| 8162 |
|
| 8163 |
config = step_configs.get(step_number, {"color": "#667eea", "icon": "📋", "description": "Analysis Step"})
|
| 8164 |
|
|
|
|
| 11559 |
|
| 11560 |
return {
|
| 11561 |
'type': 'actual_vs_optimal_bar',
|
| 11562 |
+
'title': f"🌱 {t('soil_nutrient_status_title', 'Soil Nutrient Status (Average vs. MPOB Standard)')}",
|
| 11563 |
'subtitle': 'REAL values from your current data - Observed (Average) vs Recommended (MPOB)',
|
| 11564 |
'data': {
|
| 11565 |
'categories': categories,
|
|
|
|
| 11692 |
|
| 11693 |
return {
|
| 11694 |
'type': 'actual_vs_optimal_bar',
|
| 11695 |
+
'title': f"🍃 {t('leaf_nutrient_status_title', 'Leaf Nutrient Status (Average vs. MPOB Standard)')}",
|
| 11696 |
'subtitle': 'REAL values from your current data - Observed (Average) vs Recommended (MPOB)',
|
| 11697 |
'data': {
|
| 11698 |
'categories': categories,
|
|
|
|
| 11874 |
# Create sample soil vs MPOB visualization with exact values from image tables
|
| 11875 |
sample_soil_viz = {
|
| 11876 |
'type': 'actual_vs_optimal_bar',
|
| 11877 |
+
'title': f"🌱 {t('soil_nutrient_status_title', 'Soil Nutrient Status (Average vs. MPOB Standard)')}",
|
| 11878 |
'subtitle': 'Demonstration of soil nutrient analysis visualization',
|
| 11879 |
'data': {
|
| 11880 |
'categories': ['pH', 'N (%)', 'Org. C (%)', 'Total P (mg/kg)', 'Avail P (mg/kg)', 'Exch. K (meq/100 g)', 'Exch. Ca (meq/100 g)', 'Exch. Mg (meq/100 g)', 'CEC (meq/100 g)'],
|
|
|
|
| 11894 |
# Create sample leaf vs MPOB visualization with exact values from image tables
|
| 11895 |
sample_leaf_viz = {
|
| 11896 |
'type': 'actual_vs_optimal_bar',
|
| 11897 |
+
'title': f"🍃 {t('leaf_nutrient_status_title', 'Leaf Nutrient Status (Average vs. MPOB Standard)')}",
|
| 11898 |
'subtitle': 'Demonstration of leaf nutrient analysis visualization',
|
| 11899 |
'data': {
|
| 11900 |
'categories': ['N (%)', 'P (%)', 'K (%)', 'Mg (%)', 'Ca (%)', 'B (mg/kg)', 'Cu (mg/kg)', 'Zn (mg/kg)'],
|
|
|
|
| 12011 |
|
| 12012 |
return {
|
| 12013 |
'type': 'actual_vs_optimal_bar',
|
| 12014 |
+
'title': f"🌱 {t('soil_nutrient_status_title', 'Soil Nutrient Status (Average vs. MPOB Standard)')}",
|
| 12015 |
'subtitle': 'EXACT values copied from Soil Nutrient Status table',
|
| 12016 |
'data': {
|
| 12017 |
'categories': categories,
|
|
|
|
| 12120 |
|
| 12121 |
return {
|
| 12122 |
'type': 'actual_vs_optimal_bar',
|
| 12123 |
+
'title': f"🍃 {t('leaf_nutrient_status_title', 'Leaf Nutrient Status (Average vs. MPOB Standard)')}",
|
| 12124 |
'subtitle': 'EXACT values copied from Leaf Nutrient Status table',
|
| 12125 |
'data': {
|
| 12126 |
'categories': categories,
|
|
|
|
| 12976 |
|
| 12977 |
# Display Soil Nutrient Status table - BULLETPROOF VERSION
|
| 12978 |
if soil_params and 'parameter_statistics' in soil_params:
|
| 12979 |
+
st.markdown(f"### 🌱 {t('soil_nutrient_status_title', 'Soil Nutrient Status (Average vs. MPOB Standard)')}")
|
| 12980 |
|
| 12981 |
# Create soil data list with BULLETPROOF validation
|
| 12982 |
soil_data = []
|
|
|
|
| 13082 |
|
| 13083 |
# Display Leaf Nutrient Status table - BULLETPROOF VERSION
|
| 13084 |
if leaf_params and 'parameter_statistics' in leaf_params:
|
| 13085 |
+
st.markdown(f"### 🍃 {t('leaf_nutrient_status_title', 'Leaf Nutrient Status (Average vs. MPOB Standard)')}")
|
| 13086 |
|
| 13087 |
# Create leaf data list with BULLETPROOF validation
|
| 13088 |
leaf_data = []
|
|
|
|
| 15104 |
# Display key metrics
|
| 15105 |
col1, col2, col3 = st.columns(3)
|
| 15106 |
with col1:
|
| 15107 |
+
st.metric(f"🌾 {t('current_yield_tonnes_ha', 'Current Yield')}", f"{current_yield:.1f} {t('tonnes_per_hectare', 'tonnes/ha')}")
|
| 15108 |
with col2:
|
| 15109 |
+
st.metric(f"🏞️ {t('land_size_hectares', 'Land Size')}", f"{land_size:.1f} {t('hectares', 'hectares')}")
|
| 15110 |
with col3:
|
| 15111 |
# Do not derive ROI from scenarios; show placeholder or omit
|
| 15112 |
+
st.metric(f"💰 {t('results_roi', 'Estimated ROI')}", "N/A")
|
| 15113 |
|
| 15114 |
# Display any additional tables from analysis data
|
| 15115 |
display_analysis_tables(analysis_data.get('tables'), "Economic Analysis Data Tables")
|
|
|
|
| 15141 |
# Fallback to LLM-generated economic analysis
|
| 15142 |
col1, col2, col3 = st.columns(3)
|
| 15143 |
with col1:
|
| 15144 |
+
st.metric(t('current_yield_tonnes_ha', 'Current Yield'), f"{econ_data.get('current_yield', 0):.1f} {t('tonnes_per_hectare', 'tons/ha')}")
|
| 15145 |
with col2:
|
| 15146 |
+
st.metric(t('results_yield_improvement', 'Projected Improvement'), f"{econ_data.get('yield_improvement', 0):.1f}%")
|
| 15147 |
with col3:
|
| 15148 |
+
st.metric(t('results_roi', 'ROI Estimate'), f"{econ_data.get('roi', 0):.1f}%")
|
| 15149 |
|
| 15150 |
if 'cost_benefit' in econ_data:
|
| 15151 |
+
current_lang = get_language()
|
| 15152 |
+
if current_lang == 'ms':
|
| 15153 |
+
st.markdown("#### Analisis Kos-Faedah")
|
| 15154 |
+
for scenario in econ_data['cost_benefit']:
|
| 15155 |
+
st.markdown(f"**{scenario.get('scenario', 'Unknown')}:**")
|
| 15156 |
+
st.markdown(f"- Pelaburan: RM {scenario.get('investment', 0):,.0f}")
|
| 15157 |
+
st.markdown(f"- Pulangan: RM {scenario.get('return', 0):,.0f}")
|
| 15158 |
+
st.markdown(f"- ROI: {scenario.get('roi', 0):.1f}%")
|
| 15159 |
+
else:
|
| 15160 |
+
st.markdown("#### Cost-Benefit Analysis")
|
| 15161 |
+
for scenario in econ_data['cost_benefit']:
|
| 15162 |
+
st.markdown(f"**{scenario.get('scenario', 'Unknown')}:**")
|
| 15163 |
+
st.markdown(f"- Investment: RM {scenario.get('investment', 0):,.0f}")
|
| 15164 |
+
st.markdown(f"- Return: RM {scenario.get('return', 0):,.0f}")
|
| 15165 |
+
st.markdown(f"- ROI: {scenario.get('roi', 0):.1f}%")
|
| 15166 |
|
| 15167 |
# Display any additional tables from analysis data
|
| 15168 |
display_analysis_tables(analysis_data.get('tables'), "Economic Analysis Data Tables")
|
|
|
|
| 15187 |
with col3:
|
| 15188 |
st.metric("⏱️ Payback Period", "24-36 months")
|
| 15189 |
|
| 15190 |
+
current_lang = get_language()
|
| 15191 |
+
if current_lang == 'ms':
|
| 15192 |
+
st.markdown("#### Senario Pelaburan")
|
| 15193 |
+
investment_data = [
|
| 15194 |
+
["Pelaburan Rendah", "RM 2,000 - 3,000", "RM 8,000 - 12,000", "250-300%", "24-36 bulan"],
|
| 15195 |
+
["Pelaburan Sederhana", "RM 4,000 - 6,000", "RM 15,000 - 20,000", "275-350%", "24-36 bulan"],
|
| 15196 |
+
["Pelaburan Tinggi", "RM 8,000 - 12,000", "RM 25,000 - 35,000", "200-300%", "36-48 bulan"]
|
| 15197 |
+
]
|
| 15198 |
+
|
| 15199 |
+
import pandas as pd
|
| 15200 |
+
df = pd.DataFrame(investment_data, columns=[
|
| 15201 |
+
"Tahap Pelaburan", "Jumlah Pelaburan (RM)", "Pulangan Dijangka (RM)", "ROI (%)", "Tempoh Pulangan Modal"
|
| 15202 |
+
])
|
| 15203 |
+
else:
|
| 15204 |
+
st.markdown("#### Investment Scenarios")
|
| 15205 |
+
investment_data = [
|
| 15206 |
+
["Low Investment", "RM 2,000 - 3,000", "RM 8,000 - 12,000", "250-300%", "24-36 months"],
|
| 15207 |
+
["Medium Investment", "RM 4,000 - 6,000", "RM 15,000 - 20,000", "275-350%", "24-36 months"],
|
| 15208 |
+
["High Investment", "RM 8,000 - 12,000", "RM 25,000 - 35,000", "200-300%", "36-48 months"]
|
| 15209 |
+
]
|
| 15210 |
+
|
| 15211 |
+
import pandas as pd
|
| 15212 |
+
df = pd.DataFrame(investment_data, columns=[
|
| 15213 |
+
"Investment Level", "Total Investment (RM)", "Expected Return (RM)", "ROI (%)", "Payback Period"
|
| 15214 |
+
])
|
| 15215 |
apply_table_styling()
|
| 15216 |
st.dataframe(df, use_container_width=True)
|
| 15217 |
|
|
|
|
| 15323 |
break
|
| 15324 |
|
| 15325 |
if baseline_yield > 0:
|
| 15326 |
+
st.markdown(f"**{t('baseline_yield', 'Current Yield Baseline')}:** {baseline_yield:.1f} {t('tonnes_per_hectare', 'tonnes/hectare')}")
|
| 15327 |
st.markdown("")
|
| 15328 |
|
| 15329 |
try:
|
utils/pdf_utils.py
CHANGED
|
@@ -1737,7 +1737,7 @@ class PDFReportGenerator:
|
|
| 1737 |
}
|
| 1738 |
|
| 1739 |
# Always show Soil Nutrient Status section header
|
| 1740 |
-
story.append(Paragraph("🌱 Soil Nutrient Status", self.styles['Heading4']))
|
| 1741 |
story.append(Spacer(1, 4))
|
| 1742 |
|
| 1743 |
# Create Soil Nutrient Status table
|
|
@@ -1756,13 +1756,13 @@ class PDFReportGenerator:
|
|
| 1756 |
|
| 1757 |
min_val, max_val = soil_mpob_standards.get(param_name, (0, 0))
|
| 1758 |
|
| 1759 |
-
# Determine status
|
| 1760 |
if avg_value < min_val:
|
| 1761 |
-
status =
|
| 1762 |
elif avg_value > max_val:
|
| 1763 |
-
status =
|
| 1764 |
else:
|
| 1765 |
-
status =
|
| 1766 |
|
| 1767 |
soil_data.append([
|
| 1768 |
param_name,
|
|
@@ -1772,20 +1772,22 @@ class PDFReportGenerator:
|
|
| 1772 |
])
|
| 1773 |
|
| 1774 |
if soil_data:
|
| 1775 |
-
headers = [
|
| 1776 |
table_data = [headers] + soil_data
|
| 1777 |
pdf_table = self._create_table_with_proper_layout(table_data)
|
| 1778 |
story.append(pdf_table)
|
| 1779 |
story.append(Spacer(1, 8))
|
| 1780 |
else:
|
| 1781 |
-
|
|
|
|
| 1782 |
story.append(Spacer(1, 8))
|
| 1783 |
else:
|
| 1784 |
-
|
|
|
|
| 1785 |
story.append(Spacer(1, 8))
|
| 1786 |
|
| 1787 |
# Always show Leaf Nutrient Status section header
|
| 1788 |
-
story.append(Paragraph("🍃 Leaf Nutrient Status", self.styles['Heading4']))
|
| 1789 |
story.append(Spacer(1, 4))
|
| 1790 |
|
| 1791 |
# Create Leaf Nutrient Status table
|
|
@@ -1804,13 +1806,13 @@ class PDFReportGenerator:
|
|
| 1804 |
|
| 1805 |
min_val, max_val = leaf_mpob_standards.get(param_name, (0, 0))
|
| 1806 |
|
| 1807 |
-
# Determine status
|
| 1808 |
if avg_value < min_val:
|
| 1809 |
-
status =
|
| 1810 |
elif avg_value > max_val:
|
| 1811 |
-
status =
|
| 1812 |
else:
|
| 1813 |
-
status =
|
| 1814 |
|
| 1815 |
leaf_data.append([
|
| 1816 |
param_name,
|
|
@@ -1820,16 +1822,18 @@ class PDFReportGenerator:
|
|
| 1820 |
])
|
| 1821 |
|
| 1822 |
if leaf_data:
|
| 1823 |
-
headers = [
|
| 1824 |
table_data = [headers] + leaf_data
|
| 1825 |
pdf_table = self._create_table_with_proper_layout(table_data)
|
| 1826 |
story.append(pdf_table)
|
| 1827 |
story.append(Spacer(1, 8))
|
| 1828 |
else:
|
| 1829 |
-
|
|
|
|
| 1830 |
story.append(Spacer(1, 8))
|
| 1831 |
else:
|
| 1832 |
-
|
|
|
|
| 1833 |
story.append(Spacer(1, 8))
|
| 1834 |
|
| 1835 |
except Exception as e:
|
|
@@ -1894,7 +1898,8 @@ class PDFReportGenerator:
|
|
| 1894 |
return
|
| 1895 |
|
| 1896 |
# Always show Raw Soil Sample Data section header
|
| 1897 |
-
|
|
|
|
| 1898 |
story.append(Spacer(1, 4))
|
| 1899 |
|
| 1900 |
# Create Soil Sample Data table
|
|
@@ -1925,7 +1930,8 @@ class PDFReportGenerator:
|
|
| 1925 |
story.append(Spacer(1, 8))
|
| 1926 |
|
| 1927 |
# Always show Raw Leaf Sample Data section header
|
| 1928 |
-
|
|
|
|
| 1929 |
story.append(Spacer(1, 4))
|
| 1930 |
|
| 1931 |
# Create Leaf Sample Data table
|
|
@@ -2041,7 +2047,7 @@ class PDFReportGenerator:
|
|
| 2041 |
|
| 2042 |
# Display Soil Nutrient Status table
|
| 2043 |
if soil_params and 'parameter_statistics' in soil_params:
|
| 2044 |
-
story.append(Paragraph("🌱 Soil Nutrient Status (Average vs. MPOB Standard)", self.styles['Heading4']))
|
| 2045 |
|
| 2046 |
# Create soil data list
|
| 2047 |
soil_data = []
|
|
@@ -2111,7 +2117,7 @@ class PDFReportGenerator:
|
|
| 2111 |
|
| 2112 |
# Display Leaf Nutrient Status table
|
| 2113 |
if leaf_params and 'parameter_statistics' in leaf_params:
|
| 2114 |
-
story.append(Paragraph("🍃 Leaf Nutrient Status (Average vs. MPOB Standard)", self.styles['Heading4']))
|
| 2115 |
|
| 2116 |
# Create leaf data list
|
| 2117 |
leaf_data = []
|
|
|
|
| 1737 |
}
|
| 1738 |
|
| 1739 |
# Always show Soil Nutrient Status section header
|
| 1740 |
+
story.append(Paragraph(f"🌱 {self._t('soil_nutrient_status_title', 'Soil Nutrient Status')}", self.styles['Heading4']))
|
| 1741 |
story.append(Spacer(1, 4))
|
| 1742 |
|
| 1743 |
# Create Soil Nutrient Status table
|
|
|
|
| 1756 |
|
| 1757 |
min_val, max_val = soil_mpob_standards.get(param_name, (0, 0))
|
| 1758 |
|
| 1759 |
+
# Determine status with translation
|
| 1760 |
if avg_value < min_val:
|
| 1761 |
+
status = self._t('status_deficient', 'Deficient')
|
| 1762 |
elif avg_value > max_val:
|
| 1763 |
+
status = self._t('status_excess', 'Excessive')
|
| 1764 |
else:
|
| 1765 |
+
status = self._t('status_optimal', 'Optimal')
|
| 1766 |
|
| 1767 |
soil_data.append([
|
| 1768 |
param_name,
|
|
|
|
| 1772 |
])
|
| 1773 |
|
| 1774 |
if soil_data:
|
| 1775 |
+
headers = [self._t('table_parameter', 'Parameter'), self._t('table_value', 'Current Value'), self._t('table_mpob_standard', 'MPOB Range'), self._t('table_status', 'Status')]
|
| 1776 |
table_data = [headers] + soil_data
|
| 1777 |
pdf_table = self._create_table_with_proper_layout(table_data)
|
| 1778 |
story.append(pdf_table)
|
| 1779 |
story.append(Spacer(1, 8))
|
| 1780 |
else:
|
| 1781 |
+
no_data_text = self._t('pdf_no_soil_data', 'No soil nutrient data available (all values are 0)')
|
| 1782 |
+
story.append(Paragraph(no_data_text, self.styles['CustomBody']))
|
| 1783 |
story.append(Spacer(1, 8))
|
| 1784 |
else:
|
| 1785 |
+
no_data_text = self._t('pdf_no_soil_data', 'No soil nutrient data available (all values are 0)')
|
| 1786 |
+
story.append(Paragraph(no_data_text, self.styles['CustomBody']))
|
| 1787 |
story.append(Spacer(1, 8))
|
| 1788 |
|
| 1789 |
# Always show Leaf Nutrient Status section header
|
| 1790 |
+
story.append(Paragraph(f"🍃 {self._t('leaf_nutrient_status_title', 'Leaf Nutrient Status')}", self.styles['Heading4']))
|
| 1791 |
story.append(Spacer(1, 4))
|
| 1792 |
|
| 1793 |
# Create Leaf Nutrient Status table
|
|
|
|
| 1806 |
|
| 1807 |
min_val, max_val = leaf_mpob_standards.get(param_name, (0, 0))
|
| 1808 |
|
| 1809 |
+
# Determine status with translation
|
| 1810 |
if avg_value < min_val:
|
| 1811 |
+
status = self._t('status_deficient', 'Deficient')
|
| 1812 |
elif avg_value > max_val:
|
| 1813 |
+
status = self._t('status_excess', 'Excessive')
|
| 1814 |
else:
|
| 1815 |
+
status = self._t('status_optimal', 'Optimal')
|
| 1816 |
|
| 1817 |
leaf_data.append([
|
| 1818 |
param_name,
|
|
|
|
| 1822 |
])
|
| 1823 |
|
| 1824 |
if leaf_data:
|
| 1825 |
+
headers = [self._t('table_parameter', 'Parameter'), self._t('table_value', 'Current Value'), self._t('table_mpob_standard', 'MPOB Range'), self._t('table_status', 'Status')]
|
| 1826 |
table_data = [headers] + leaf_data
|
| 1827 |
pdf_table = self._create_table_with_proper_layout(table_data)
|
| 1828 |
story.append(pdf_table)
|
| 1829 |
story.append(Spacer(1, 8))
|
| 1830 |
else:
|
| 1831 |
+
no_data_text = self._t('pdf_no_leaf_data', 'No leaf nutrient data available (all values are 0)')
|
| 1832 |
+
story.append(Paragraph(no_data_text, self.styles['CustomBody']))
|
| 1833 |
story.append(Spacer(1, 8))
|
| 1834 |
else:
|
| 1835 |
+
no_data_text = self._t('pdf_no_leaf_data', 'No leaf nutrient data available (all values are 0)')
|
| 1836 |
+
story.append(Paragraph(no_data_text, self.styles['CustomBody']))
|
| 1837 |
story.append(Spacer(1, 8))
|
| 1838 |
|
| 1839 |
except Exception as e:
|
|
|
|
| 1898 |
return
|
| 1899 |
|
| 1900 |
# Always show Raw Soil Sample Data section header
|
| 1901 |
+
raw_soil_title = self._t('pdf_raw_soil_data', 'Raw Soil Sample Data') if self.language == 'ms' else 'Raw Soil Sample Data'
|
| 1902 |
+
story.append(Paragraph(f"🌱 {raw_soil_title}", self.styles['Heading4']))
|
| 1903 |
story.append(Spacer(1, 4))
|
| 1904 |
|
| 1905 |
# Create Soil Sample Data table
|
|
|
|
| 1930 |
story.append(Spacer(1, 8))
|
| 1931 |
|
| 1932 |
# Always show Raw Leaf Sample Data section header
|
| 1933 |
+
raw_leaf_title = self._t('pdf_raw_leaf_data', 'Raw Leaf Sample Data') if self.language == 'ms' else 'Raw Leaf Sample Data'
|
| 1934 |
+
story.append(Paragraph(f"🍃 {raw_leaf_title}", self.styles['Heading4']))
|
| 1935 |
story.append(Spacer(1, 4))
|
| 1936 |
|
| 1937 |
# Create Leaf Sample Data table
|
|
|
|
| 2047 |
|
| 2048 |
# Display Soil Nutrient Status table
|
| 2049 |
if soil_params and 'parameter_statistics' in soil_params:
|
| 2050 |
+
story.append(Paragraph(f"🌱 {self._t('soil_nutrient_status_title', 'Soil Nutrient Status (Average vs. MPOB Standard)')}", self.styles['Heading4']))
|
| 2051 |
|
| 2052 |
# Create soil data list
|
| 2053 |
soil_data = []
|
|
|
|
| 2117 |
|
| 2118 |
# Display Leaf Nutrient Status table
|
| 2119 |
if leaf_params and 'parameter_statistics' in leaf_params:
|
| 2120 |
+
story.append(Paragraph(f"🍃 {self._t('leaf_nutrient_status_title', 'Leaf Nutrient Status (Average vs. MPOB Standard)')}", self.styles['Heading4']))
|
| 2121 |
|
| 2122 |
# Create leaf data list
|
| 2123 |
leaf_data = []
|
utils/translations.py
CHANGED
|
@@ -170,6 +170,83 @@ TRANSLATIONS = {
|
|
| 170 |
'results_baseline_yield': 'Baseline Yield',
|
| 171 |
'results_projected_yield': 'Projected Yield',
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
# Admin
|
| 174 |
'admin_title': 'Admin Panel',
|
| 175 |
'admin_restricted': 'Admin access only',
|
|
@@ -259,6 +336,10 @@ TRANSLATIONS = {
|
|
| 259 |
'pdf_generating': 'Generating PDF report...',
|
| 260 |
'pdf_generated_success': 'PDF report generated successfully!',
|
| 261 |
'pdf_generated_error': 'Failed to generate PDF report',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
},
|
| 263 |
'ms': {
|
| 264 |
# Common
|
|
@@ -422,6 +503,83 @@ TRANSLATIONS = {
|
|
| 422 |
'results_baseline_yield': 'Hasil Asas',
|
| 423 |
'results_projected_yield': 'Hasil Unjuran',
|
| 424 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
# Admin
|
| 426 |
'admin_title': 'Panel Admin',
|
| 427 |
'admin_restricted': 'Akses admin sahaja',
|
|
@@ -511,6 +669,10 @@ TRANSLATIONS = {
|
|
| 511 |
'pdf_generating': 'Menjana laporan PDF...',
|
| 512 |
'pdf_generated_success': 'Laporan PDF berjaya dijana!',
|
| 513 |
'pdf_generated_error': 'Gagal menjana laporan PDF',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
}
|
| 515 |
}
|
| 516 |
|
|
|
|
| 170 |
'results_baseline_yield': 'Baseline Yield',
|
| 171 |
'results_projected_yield': 'Projected Yield',
|
| 172 |
|
| 173 |
+
# Executive Summary
|
| 174 |
+
'executive_summary': 'Executive Summary',
|
| 175 |
+
'executive_summary_desc': 'Key insights from your soil and leaf analysis in simple terms',
|
| 176 |
+
'executive_summary_intro': 'This comprehensive agronomic analysis evaluates key nutritional parameters from both soil and leaf tissue samples to assess the current fertility status and plant health of the oil palm plantation.',
|
| 177 |
+
|
| 178 |
+
# Nutrient Status Tables
|
| 179 |
+
'soil_nutrient_status_title': 'Soil Nutrient Status (Average vs. MPOB Standard)',
|
| 180 |
+
'leaf_nutrient_status_title': 'Leaf Nutrient Status (Average vs. MPOB Standard)',
|
| 181 |
+
'nutrient_gap_analysis_title': 'Nutrient Gap Analysis',
|
| 182 |
+
'parameter_analysis_matrix': 'Parameter Analysis Matrix',
|
| 183 |
+
'data_quality_assessment': 'Data Quality Assessment',
|
| 184 |
+
|
| 185 |
+
# Table Column Headers
|
| 186 |
+
'table_parameter': 'Parameter',
|
| 187 |
+
'table_average': 'Average',
|
| 188 |
+
'table_mpob_standard': 'MPOB Standard',
|
| 189 |
+
'table_status': 'Status',
|
| 190 |
+
'table_gap': 'Gap',
|
| 191 |
+
'table_severity': 'Severity',
|
| 192 |
+
'table_source': 'Source',
|
| 193 |
+
'table_min': 'Min',
|
| 194 |
+
'table_max': 'Max',
|
| 195 |
+
'table_std_dev': 'Std Dev',
|
| 196 |
+
'table_optimal_range': 'Optimal Range',
|
| 197 |
+
'table_unit': 'Unit',
|
| 198 |
+
'table_value': 'Value',
|
| 199 |
+
|
| 200 |
+
# Economic Forecast Labels
|
| 201 |
+
'economic_forecast': 'Economic Forecast',
|
| 202 |
+
'economic_forecast_source': 'Economic Forecast Source',
|
| 203 |
+
'land_size_hectares': 'Land Size (Hectares)',
|
| 204 |
+
'current_yield_tonnes_ha': 'Current Yield (Tonnes/Ha)',
|
| 205 |
+
'palm_density_per_hectare': 'Palm Density Per Hectare',
|
| 206 |
+
'total_palms': 'Total Palms',
|
| 207 |
+
'oil_palm_price_range': 'Oil Palm Price Range (RM/Tonne)',
|
| 208 |
+
'investment_scenario': 'Investment Scenario',
|
| 209 |
+
'yield_improvement_tha': 'Yield Improvement (t/ha)',
|
| 210 |
+
'revenue_rm_ha': 'Revenue (RM/ha)',
|
| 211 |
+
'input_cost_rm_ha': 'Input Cost (RM/ha)',
|
| 212 |
+
'net_profit_rm_ha': 'Net Profit (RM/ha)',
|
| 213 |
+
'cumulative_net_profit': 'Cumulative Net Profit (RM/ha)',
|
| 214 |
+
'roi_percent': 'ROI (%)',
|
| 215 |
+
'payback_period': 'Payback Period',
|
| 216 |
+
'months': 'months',
|
| 217 |
+
|
| 218 |
+
# Step Titles
|
| 219 |
+
'step_1_title': 'Step 1: Data Analysis',
|
| 220 |
+
'step_2_title': 'Step 2: Issue Diagnosis',
|
| 221 |
+
'step_3_title': 'Step 3: Solution Recommendations',
|
| 222 |
+
'step_4_title': 'Step 4: Regenerative Agriculture',
|
| 223 |
+
'step_5_title': 'Step 5: Economic Impact Forecast',
|
| 224 |
+
'step_6_title': 'Step 6: Yield Forecast & Projections',
|
| 225 |
+
|
| 226 |
+
# Yield Forecast
|
| 227 |
+
'yield_forecast_title': 'Yield Forecast & Projections',
|
| 228 |
+
'baseline_yield': 'Baseline Yield',
|
| 229 |
+
'projected_yield': 'Projected Yield',
|
| 230 |
+
'year_1': 'Year 1',
|
| 231 |
+
'year_2': 'Year 2',
|
| 232 |
+
'year_3': 'Year 3',
|
| 233 |
+
'year_4': 'Year 4',
|
| 234 |
+
'year_5': 'Year 5',
|
| 235 |
+
'high_investment': 'High Investment',
|
| 236 |
+
'medium_investment': 'Medium Investment',
|
| 237 |
+
'low_investment': 'Low Investment',
|
| 238 |
+
'tonnes_per_hectare': 'tonnes/hectare',
|
| 239 |
+
'hectares': 'hectares',
|
| 240 |
+
|
| 241 |
+
# Status Labels
|
| 242 |
+
'status_optimal': 'Optimal',
|
| 243 |
+
'status_deficient': 'Deficient',
|
| 244 |
+
'status_excess': 'Excess',
|
| 245 |
+
'status_critical': 'Critical',
|
| 246 |
+
'status_low': 'Low',
|
| 247 |
+
'status_balanced': 'Balanced',
|
| 248 |
+
'status_adequate': 'Adequate',
|
| 249 |
+
|
| 250 |
# Admin
|
| 251 |
'admin_title': 'Admin Panel',
|
| 252 |
'admin_restricted': 'Admin access only',
|
|
|
|
| 336 |
'pdf_generating': 'Generating PDF report...',
|
| 337 |
'pdf_generated_success': 'PDF report generated successfully!',
|
| 338 |
'pdf_generated_error': 'Failed to generate PDF report',
|
| 339 |
+
'pdf_no_soil_data': 'No soil nutrient data available',
|
| 340 |
+
'pdf_no_leaf_data': 'No leaf nutrient data available',
|
| 341 |
+
'pdf_raw_soil_data': 'Raw Soil Sample Data',
|
| 342 |
+
'pdf_raw_leaf_data': 'Raw Leaf Sample Data',
|
| 343 |
},
|
| 344 |
'ms': {
|
| 345 |
# Common
|
|
|
|
| 503 |
'results_baseline_yield': 'Hasil Asas',
|
| 504 |
'results_projected_yield': 'Hasil Unjuran',
|
| 505 |
|
| 506 |
+
# Executive Summary (Malaysian)
|
| 507 |
+
'executive_summary': 'Ringkasan Eksekutif',
|
| 508 |
+
'executive_summary_desc': 'Pandangan utama daripada analisis tanah dan daun anda dalam istilah mudah',
|
| 509 |
+
'executive_summary_intro': 'Analisis agronomi komprehensif ini menilai parameter pemakanan utama daripada sampel tisu tanah dan daun untuk menilai status kesuburan semasa dan kesihatan tumbuhan ladang kelapa sawit.',
|
| 510 |
+
|
| 511 |
+
# Nutrient Status Tables (Malaysian)
|
| 512 |
+
'soil_nutrient_status_title': 'Status Nutrien Tanah (Purata vs. Standard MPOB)',
|
| 513 |
+
'leaf_nutrient_status_title': 'Status Nutrien Daun (Purata vs. Standard MPOB)',
|
| 514 |
+
'nutrient_gap_analysis_title': 'Analisis Jurang Nutrien',
|
| 515 |
+
'parameter_analysis_matrix': 'Matriks Analisis Parameter',
|
| 516 |
+
'data_quality_assessment': 'Penilaian Kualiti Data',
|
| 517 |
+
|
| 518 |
+
# Table Column Headers (Malaysian)
|
| 519 |
+
'table_parameter': 'Parameter',
|
| 520 |
+
'table_average': 'Purata',
|
| 521 |
+
'table_mpob_standard': 'Standard MPOB',
|
| 522 |
+
'table_status': 'Status',
|
| 523 |
+
'table_gap': 'Jurang',
|
| 524 |
+
'table_severity': 'Keterukan',
|
| 525 |
+
'table_source': 'Sumber',
|
| 526 |
+
'table_min': 'Min',
|
| 527 |
+
'table_max': 'Maks',
|
| 528 |
+
'table_std_dev': 'Sisihan Piawai',
|
| 529 |
+
'table_optimal_range': 'Julat Optimum',
|
| 530 |
+
'table_unit': 'Unit',
|
| 531 |
+
'table_value': 'Nilai',
|
| 532 |
+
|
| 533 |
+
# Economic Forecast Labels (Malaysian)
|
| 534 |
+
'economic_forecast': 'Ramalan Ekonomi',
|
| 535 |
+
'economic_forecast_source': 'Sumber Ramalan Ekonomi',
|
| 536 |
+
'land_size_hectares': 'Saiz Tanah (Hektar)',
|
| 537 |
+
'current_yield_tonnes_ha': 'Hasil Semasa (Tan/Ha)',
|
| 538 |
+
'palm_density_per_hectare': 'Kepadatan Sawit Per Hektar',
|
| 539 |
+
'total_palms': 'Jumlah Pokok Sawit',
|
| 540 |
+
'oil_palm_price_range': 'Julat Harga Kelapa Sawit (RM/Tan)',
|
| 541 |
+
'investment_scenario': 'Senario Pelaburan',
|
| 542 |
+
'yield_improvement_tha': 'Peningkatan Hasil (t/ha)',
|
| 543 |
+
'revenue_rm_ha': 'Pendapatan (RM/ha)',
|
| 544 |
+
'input_cost_rm_ha': 'Kos Input (RM/ha)',
|
| 545 |
+
'net_profit_rm_ha': 'Untung Bersih (RM/ha)',
|
| 546 |
+
'cumulative_net_profit': 'Untung Bersih Kumulatif (RM/ha)',
|
| 547 |
+
'roi_percent': 'ROI (%)',
|
| 548 |
+
'payback_period': 'Tempoh Pulangan Modal',
|
| 549 |
+
'months': 'bulan',
|
| 550 |
+
|
| 551 |
+
# Step Titles (Malaysian)
|
| 552 |
+
'step_1_title': 'Langkah 1: Analisis Data',
|
| 553 |
+
'step_2_title': 'Langkah 2: Diagnosis Isu',
|
| 554 |
+
'step_3_title': 'Langkah 3: Cadangan Penyelesaian',
|
| 555 |
+
'step_4_title': 'Langkah 4: Pertanian Regeneratif',
|
| 556 |
+
'step_5_title': 'Langkah 5: Ramalan Kesan Ekonomi',
|
| 557 |
+
'step_6_title': 'Langkah 6: Ramalan & Unjuran Hasil',
|
| 558 |
+
|
| 559 |
+
# Yield Forecast (Malaysian)
|
| 560 |
+
'yield_forecast_title': 'Ramalan & Unjuran Hasil',
|
| 561 |
+
'baseline_yield': 'Hasil Asas',
|
| 562 |
+
'projected_yield': 'Hasil Unjuran',
|
| 563 |
+
'year_1': 'Tahun 1',
|
| 564 |
+
'year_2': 'Tahun 2',
|
| 565 |
+
'year_3': 'Tahun 3',
|
| 566 |
+
'year_4': 'Tahun 4',
|
| 567 |
+
'year_5': 'Tahun 5',
|
| 568 |
+
'high_investment': 'Pelaburan Tinggi',
|
| 569 |
+
'medium_investment': 'Pelaburan Sederhana',
|
| 570 |
+
'low_investment': 'Pelaburan Rendah',
|
| 571 |
+
'tonnes_per_hectare': 'tan/hektar',
|
| 572 |
+
'hectares': 'hektar',
|
| 573 |
+
|
| 574 |
+
# Status Labels (Malaysian)
|
| 575 |
+
'status_optimal': 'Optimum',
|
| 576 |
+
'status_deficient': 'Kekurangan',
|
| 577 |
+
'status_excess': 'Berlebihan',
|
| 578 |
+
'status_critical': 'Kritikal',
|
| 579 |
+
'status_low': 'Rendah',
|
| 580 |
+
'status_balanced': 'Seimbang',
|
| 581 |
+
'status_adequate': 'Mencukupi',
|
| 582 |
+
|
| 583 |
# Admin
|
| 584 |
'admin_title': 'Panel Admin',
|
| 585 |
'admin_restricted': 'Akses admin sahaja',
|
|
|
|
| 669 |
'pdf_generating': 'Menjana laporan PDF...',
|
| 670 |
'pdf_generated_success': 'Laporan PDF berjaya dijana!',
|
| 671 |
'pdf_generated_error': 'Gagal menjana laporan PDF',
|
| 672 |
+
'pdf_no_soil_data': 'Tiada data nutrien tanah tersedia',
|
| 673 |
+
'pdf_no_leaf_data': 'Tiada data nutrien daun tersedia',
|
| 674 |
+
'pdf_raw_soil_data': 'Data Sampel Tanah Mentah',
|
| 675 |
+
'pdf_raw_leaf_data': 'Data Sampel Daun Mentah',
|
| 676 |
}
|
| 677 |
}
|
| 678 |
|