Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/docs/WORKFLOW_I18N_SYSTEM.md
2026-04-14 19:34:55 +03:30

24 KiB
Executable file

🌍 سیستم ترجمه (i18n) برای نودهای ورک‌فلو

📋 خلاصه اجرایی

این مستند یک سیستم جامع برای چند زبانه کردن نودهای ورک‌فلو ارائه می‌دهد. این سیستم امکان ترجمه تمام رشته‌های استفاده شده در:

  • نام و توضیحات نودها
  • نام فیلدها و توضیحات آن‌ها
  • گزینه‌های enum
  • پیام‌های کمکی (help texts)
  • پیام‌های خطا (validation errors)

را فراهم می‌کند.


🏗️ معماری سیستم

کامپوننت‌ها:

┌─────────────────────────────────────────────────────────────┐
│                        Backend (Python)                      │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  workflow_translations.py                                    │
│  ├─ COMMON_TRANSLATIONS                                      │
│  ├─ CREATE_INVOICE_TRANSLATIONS                             │
│  ├─ SEND_TELEGRAM_TRANSLATIONS                              │
│  ├─ SEND_EMAIL_TRANSLATIONS                                 │
│  └─ OTHER_ACTIONS_TRANSLATIONS                              │
│                                                              │
│  API Endpoints:                                              │
│  ├─ GET /workflows/metadata/actions?lang=fa                 │
│  ├─ GET /workflows/metadata/triggers?lang=fa                │
│  ├─ GET /workflows/translations?lang=fa                     │
│  └─ GET /workflows/translations/export?lang=fa              │
│                                                              │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                      Frontend (Flutter)                      │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  WorkflowTranslationService                                  │
│  ├─ getTranslations(lang)                                    │
│  ├─ getActionsMetadata(lang)                                 │
│  ├─ getTriggersMetadata(lang)                                │
│  └─ getFieldTranslation(action, field, type)                │
│                                                              │
│  WorkflowLocalizations Extension                             │
│  ├─ workflowCreateInvoiceActionName                         │
│  ├─ workflowSendTelegramFieldMessage                        │
│  └─ ... (تمام ترجمه‌ها)                                     │
│                                                              │
│  UI Components:                                              │
│  └─ WorkflowNodeConfigDialog                                │
│     ├─ استفاده از ترجمه‌ها در labels                       │
│     ├─ استفاده از ترجمه‌ها در descriptions                 │
│     ├─ استفاده از ترجمه‌ها در placeholders                 │
│     └─ استفاده از ترجمه‌ها در enum labels                  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

🔧 نحوه کار

1. Backend (Python)

ساختار ترجمه‌ها:

CREATE_INVOICE_TRANSLATIONS = {
    "fa": {
        "action_name": "ایجاد فاکتور",
        "action_description": "ایجاد فاکتور فروش، خرید یا...",
        
        "group_basic_info": "اطلاعات پایه",
        "group_items": "آیتم‌های فاکتور",
        
        "field_invoice_type": "نوع فاکتور",
        "field_invoice_type_desc": "نوع فاکتور",
        
        "invoice_sales": "🛒 فاکتور فروش",
        "invoice_purchase": "🛍️ فاکتور خرید",
        
        "error_min_items": "حداقل یک آیتم باید وارد شود",
    },
    "en": {
        "action_name": "Create Invoice",
        "action_description": "Create sales, purchase or...",
        
        "group_basic_info": "Basic Information",
        "group_items": "Invoice Items",
        
        "field_invoice_type": "Invoice Type",
        "field_invoice_type_desc": "Invoice Type",
        
        "invoice_sales": "🛒 Sales Invoice",
        "invoice_purchase": "🛍️ Purchase Invoice",
        
        "error_min_items": "At least one item is required",
    }
}

API Endpoints:

1. دریافت ترجمه‌ها:

GET /api/v1/workflows/translations?lang=fa

Response:
{
  "language": "fa",
  "translations": {
    "settings": "تنظیمات",
    "create_invoice": {
      "action_name": "ایجاد فاکتور",
      "field_invoice_type": "نوع فاکتور"
    }
  }
}

2. دریافت metadata با ترجمه:

GET /api/v1/workflows/metadata/actions?lang=en

Response:
{
  "data": [
    {
      "key": "create_invoice",
      "name": "Create Invoice",  // ترجمه شده
      "description": "Create sales invoice...",  // ترجمه شده
      "config_schema": {
        "invoice_type": {
          "description": "Invoice Type",  // ترجمه شده
          "enum": ["invoice_sales", "invoice_purchase"],
          "ui_config": {
            "labels": {
              "invoice_sales": "Sales Invoice",  // ترجمه شده
              "invoice_purchase": "Purchase Invoice"  // ترجمه شده
            }
          }
        }
      }
    }
  ]
}

3. صادرات به فرمت arb:

GET /api/v1/workflows/translations/export?lang=fa

Response:
{
  "format": "arb",
  "translations": {
    "workflowSettings": "تنظیمات",
    "workflowCreateInvoiceActionName": "ایجاد فاکتور",
    "workflowCreateInvoiceFieldInvoiceType": "نوع فاکتور"
  }
}

2. Frontend (Flutter)

استفاده در UI:

import '../../services/workflow_translation_service.dart';
import '../../extensions/workflow_localizations_extension.dart';

class _WorkflowNodeConfigDialogState extends State<WorkflowNodeConfigDialog> {
  final WorkflowTranslationService _translationService = WorkflowTranslationService();
  Map<String, dynamic>? _translations;
  
  @override
  void initState() {
    super.initState();
    _loadTranslations();
  }
  
  Future<void> _loadTranslations() async {
    final locale = Localizations.localeOf(context);
    final translations = await _translationService.getTranslations(lang: locale.languageCode);
    setState(() {
      _translations = translations;
    });
  }
  
  Widget build(BuildContext context) {
    // استفاده از extension
    final t = AppLocalizations.of(context);
    final label = t.workflowCreateInvoiceFieldInvoiceType;
    
    // یا استفاده از service
    final description = _translationService.getFieldTranslation(
      'create_invoice',
      'invoice_type',
      type: 'desc',
    );
    
    return TextFormField(
      decoration: InputDecoration(
        labelText: label,
        helperText: description,
      ),
    );
  }
}

📝 نحوه افزودن ترجمه برای نود جدید

مرحله 1: افزودن ترجمه‌ها در Python

فایل: app/services/workflow/i18n/workflow_translations.py

# ترجمه‌های نود جدید
MY_NEW_ACTION_TRANSLATIONS = {
    "fa": {
        "action_name": "نام فارسی اکشن",
        "action_description": "توضیحات فارسی اکشن",
        
        "field_my_field": "نام فیلد",
        "field_my_field_desc": "توضیحات فیلد",
        "field_my_field_placeholder": "متن placeholder",
        "field_my_field_help": "متن کمکی",
        
        "my_enum_value": "لیبل فارسی",
        
        "error_my_error": "پیام خطا",
    },
    "en": {
        "action_name": "English Action Name",
        "action_description": "English action description",
        
        "field_my_field": "Field Name",
        "field_my_field_desc": "Field description",
        "field_my_field_placeholder": "Placeholder text",
        "field_my_field_help": "Help text",
        
        "my_enum_value": "English Label",
        
        "error_my_error": "Error message",
    }
}

مرحله 2: اضافه کردن به exports

# در همان فایل
__all__ = [
    ...,
    "MY_NEW_ACTION_TRANSLATIONS",
]

# در تابع get_translation
translations_map = {
    ...,
    "my_new_action": MY_NEW_ACTION_TRANSLATIONS,
}

مرحله 3: استخراج و صادرات

cd /var/www/ark/hesabixAPI
source venv/bin/activate
python scripts/extract_workflow_translations.py

مرحله 4: بازسازی Flutter

cd /var/www/ark/hesabixUI/hesabix_ui
flutter pub run build_runner build --delete-conflicting-outputs

🎨 الگوهای نام‌گذاری

Backend (Python):

نوع الگو مثال
نام action action_name "ایجاد فاکتور"
توضیحات action action_description "ایجاد فاکتور..."
نام گروه group_{group_key} group_basic_info
نام فیلد field_{field_key} field_invoice_type
توضیحات فیلد field_{field_key}_desc field_invoice_type_desc
Placeholder فیلد field_{field_key}_placeholder field_message_placeholder
راهنمای فیلد field_{field_key}_help field_items_help
Enum value {enum_value} invoice_sales
پیام خطا error_{error_key} error_min_items

Frontend (Dart):

نوع الگو مثال
نام action workflow{Action}ActionName workflowCreateInvoiceActionName
توضیحات action workflow{Action}ActionDescription workflowCreateInvoiceActionDescription
نام فیلد workflow{Action}Field{Field} workflowCreateInvoiceFieldInvoiceType
Enum value workflow{Action}{EnumValue} workflowCreateInvoiceInvoiceSales

📊 ساختار فایل‌ها

Backend:

hesabixAPI/
├── app/
│   └── services/
│       └── workflow/
│           ├── i18n/
│           │   ├── __init__.py
│           │   └── workflow_translations.py  ← ترجمه‌ها
│           └── actions/
│               ├── communication_actions.py  ← استفاده از ترجمه
│               ├── document_actions.py       ← استفاده از ترجمه
│               └── utility_actions.py        ← استفاده از ترجمه
├── adapters/
│   └── api/
│       └── v1/
│           └── workflows.py  ← API endpoints برای ترجمه
└── scripts/
    └── extract_workflow_translations.py  ← اسکریپت استخراج

Frontend:

hesabixUI/hesabix_ui/
├── lib/
│   ├── services/
│   │   └── workflow_translation_service.dart  ← سرویس ترجمه
│   ├── extensions/
│   │   └── workflow_localizations_extension.dart  ← Extension
│   ├── widgets/
│   │   └── workflow/
│   │       └── workflow_node_config_dialog.dart  ← استفاده از ترجمه
│   └── l10n/
│       ├── workflow_fa.arb  ← ترجمه‌های فارسی (auto-generated)
│       └── workflow_en.arb  ← ترجمه‌های انگلیسی (auto-generated)

🚀 نحوه استفاده

1. در Backend:

افزودن ترجمه به metadata:

from app.services.workflow.i18n import translate_metadata

class MyAction(ActionHandler):
    def get_metadata(self) -> Dict[str, Any]:
        return {
            "name": "نام پیش‌فرض",  # این جایگزین می‌شود
            "description": "توضیحات پیش‌فرض",  # این جایگزین می‌شود
            "config_schema": {
                "my_field": {
                    "type": "string",
                    "description": "توضیحات فیلد",  # این جایگزین می‌شود
                    "enum": ["value1", "value2"],
                    "ui_config": {
                        # labels جایگزین می‌شوند
                    }
                }
            }
        }

استفاده در API endpoint:

@router.get("/workflows/metadata/actions")
async def get_actions_metadata(lang: str = "fa"):
    action_registry = ActionRegistry()
    all_actions = action_registry.get_all_metadata()
    
    translated_actions = []
    for action in all_actions:
        translated = translate_metadata(action, lang, action.get("key"))
        translated_actions.append(translated)
    
    return success_response(data=translated_actions)

2. در Frontend:

استفاده از Extension:

import '../../extensions/workflow_localizations_extension.dart';

@override
Widget build(BuildContext context) {
  final t = AppLocalizations.of(context);
  
  return Column(
    children: [
      Text(t.workflowCreateInvoiceActionName),
      Text(t.workflowCreateInvoiceFieldInvoiceType),
      Text(t.workflowSendTelegramFieldMessage),
    ],
  );
}

استفاده از Service:

final translationService = WorkflowTranslationService();

// بارگذاری ترجمه‌ها
final translations = await translationService.getTranslations(lang: 'fa');

// دریافت metadata با ترجمه
final actionsMetadata = await translationService.getActionsMetadata(lang: 'fa');

// دریافت ترجمه یک فیلد خاص
final fieldLabel = translationService.getFieldTranslation(
  'create_invoice',
  'invoice_type',
  type: 'name',
);

در Dialog تنظیمات نود:

class _WorkflowNodeConfigDialogState extends State<WorkflowNodeConfigDialog> {
  Map<String, dynamic>? _translations;
  
  @override
  void initState() {
    super.initState();
    _loadTranslations();
  }
  
  Future<void> _loadTranslations() async {
    final locale = Localizations.localeOf(context);
    final translations = await _translationService.getTranslations(
      lang: locale.languageCode
    );
    setState(() {
      _translations = translations;
    });
  }
  
  String _formatKey(String key) {
    // استفاده از ترجمه (اگر موجود باشد)
    if (_translations != null && widget.node.key != null) {
      final actionKey = widget.node.key;
      final fieldKey = 'field_$key';
      
      if (_translations![actionKey]?[fieldKey] != null) {
        return _translations![actionKey][fieldKey];
      }
    }
    
    // Fallback
    return _formatFieldName(key);
  }
}

📚 مثال کامل: نود "ایجاد فاکتور"

Backend:

# در workflow_translations.py
CREATE_INVOICE_TRANSLATIONS = {
    "fa": {
        "action_name": "ایجاد فاکتور",
        "field_invoice_type": "نوع فاکتور",
        "invoice_sales": "🛒 فاکتور فروش",
        "invoice_purchase": "🛍️ فاکتور خرید",
    },
    "en": {
        "action_name": "Create Invoice",
        "field_invoice_type": "Invoice Type",
        "invoice_sales": "🛒 Sales Invoice",
        "invoice_purchase": "🛍️ Purchase Invoice",
    }
}

# در document_actions.py
class CreateInvoiceAction(ActionHandler):
    def get_metadata(self) -> Dict[str, Any]:
        return {
            "name": "ایجاد فاکتور",  # جایگزین می‌شود با ترجمه
            "config_schema": {
                "invoice_type": {
                    "enum": ["invoice_sales", "invoice_purchase"],
                    "ui_config": {
                        "labels": {
                            # این labels از ترجمه می‌آید
                            "invoice_sales": "فاکتور فروش",
                            "invoice_purchase": "فاکتور خرید"
                        }
                    }
                }
            }
        }

Frontend:

// استفاده از extension
final t = AppLocalizations.of(context);

DropdownButtonFormField<String>(
  decoration: InputDecoration(
    labelText: t.workflowCreateInvoiceFieldInvoiceType,
  ),
  items: [
    DropdownMenuItem(
      value: 'invoice_sales',
      child: Text(t.workflowCreateInvoiceInvoiceSales),
    ),
    DropdownMenuItem(
      value: 'invoice_purchase',
      child: Text(t.workflowCreateInvoiceInvoicePurchase),
    ),
  ],
)

🔄 فرآیند توسعه

هنگام افزودن نود جدید:

1. ایجاد Action Handler
   ↓
2. افزودن ترجمه‌ها در workflow_translations.py
   ↓
3. اجرای extract_workflow_translations.py
   ↓
4. ری‌استارت API
   ↓
5. بازسازی Flutter (build_runner)
   ↓
6. استفاده در UI

هنگام تغییر ترجمه:

1. ویرایش workflow_translations.py
   ↓
2. اجرای extract_workflow_translations.py
   ↓
3. بازسازی Flutter (اگر لازم باشد)
   ↓
4. ری‌استارت API

🧪 تست

تست ترجمه‌ها در Backend:

from app.services.workflow.i18n import get_translation, translate_metadata

# تست دریافت ترجمه
assert get_translation("action_name", "fa", "create_invoice") == "ایجاد فاکتور"
assert get_translation("action_name", "en", "create_invoice") == "Create Invoice"

# تست ترجمه metadata
metadata = {
    "name": "default name",
    "config_schema": {...}
}

translated_fa = translate_metadata(metadata, "fa", "create_invoice")
assert translated_fa["name"] == "ایجاد فاکتور"

translated_en = translate_metadata(metadata, "en", "create_invoice")
assert translated_en["name"] == "Create Invoice"

تست در Frontend:

// تست extension
test('WorkflowLocalizations extension', () {
  final t = AppLocalizations.delegate.load(Locale('fa'));
  expect(t.workflowCreateInvoiceActionName, equals('ایجاد فاکتور'));
});

// تست service
test('WorkflowTranslationService', () async {
  final service = WorkflowTranslationService();
  final translations = await service.getTranslations(lang: 'fa');
  expect(translations['create_invoice']['action_name'], equals('ایجاد فاکتور'));
});

📋 لیست کامل کلیدهای ترجمه

نودهای فعلی:

1. Create Invoice (17 فیلد + 20 ترجمه)

  • ✅ action_name, action_description
  • ✅ 6 group (basic_info, items, financial, payment, warehouse, advanced)
  • ✅ 17 field (invoice_type, person_id, document_date, ...)
  • ✅ 4 enum label (invoice_sales, invoice_purchase, ...)
  • ✅ 4 help_text
  • ✅ 3 error_message

جمع کل: ~54 کلید ترجمه

2. Send Telegram (7 فیلد + 10 ترجمه)

  • ✅ action_name, action_description
  • ✅ 7 field (user_id, message, parse_mode, ...)
  • ✅ 3 enum label (HTML, Markdown, None)
  • ✅ 2 help_text

جمع کل: ~22 کلید ترجمه

3. Send Email (8 فیلد + 10 ترجمه)

  • ✅ action_name, action_description
  • ✅ 8 field (to, cc, bcc, subject, body, ...)
  • ✅ 2 help_text

جمع کل: ~20 کلید ترجمه

4. سایر نودها (6 نود)

  • Create Notification
  • Set Variable
  • Log
  • HTTP Request
  • Create Document
  • Update Inventory

جمع کل هر نود: ~10-15 کلید


📊 آمار کلی

زبان تعداد کلیدهای مشترک تعداد کلیدها per نود جمع کل (تخمینی)
فارسی 15 ~20-50 200+
انگلیسی 15 ~20-50 200+

💡 بهترین شیوه‌ها (Best Practices)

1. نام‌گذاری:

  • ✅ از snake_case استفاده کنید
  • ✅ نام‌های توصیفی و واضح
  • ✅ پیشوند مناسب (field_, group_, error_)

2. سازماندهی:

  • ✅ هر action یک dictionary جداگانه
  • ✅ گروه‌بندی منطقی (basic, advanced, ...)
  • ✅ ترتیب منطقی کلیدها

3. محتوا:

  • ✅ متن‌های کوتاه و واضح
  • ✅ از آیکون‌های Emoji در جای مناسب
  • ✅ راهنماها و مثال‌ها

4. مدیریت:

  • ✅ همیشه هر دو زبان را به‌روز نگه دارید
  • ✅ از اسکریپت extract برای صادرات استفاده کنید
  • ✅ Cache کردن در frontend

5. Testing:

  • ✅ تست ترجمه‌ها در هر دو زبان
  • ✅ بررسی کلیدهای گمشده
  • ✅ مقایسه با UI واقعی

🔍 Debugging

مشکل: ترجمه نمایش داده نمی‌شود

چک‌لیست:

  1. ✅ آیا ترجمه در workflow_translations.py وجود دارد؟
  2. ✅ آیا API ری‌استارت شده؟
  3. ✅ آیا cache در frontend پاک شده؟
  4. ✅ آیا زبان صحیح انتخاب شده؟
  5. ✅ آیا کلید ترجمه صحیح است؟

مشکل: ترجمه انگلیسی نمایش داده نمی‌شود

// بررسی locale
final locale = Localizations.localeOf(context);
print('Current locale: ${locale.languageCode}');

// بررسی ترجمه‌های دریافت شده
final translations = await _translationService.getTranslations(lang: 'en');
print('Translations: $translations');

مشکل: کلید ترجمه یافت نمی‌شود

# در Python
from app.services.workflow.i18n import get_all_translation_keys

keys = get_all_translation_keys("create_invoice")
print("Available keys (fa):", keys["fa"])
print("Available keys (en):", keys["en"])

🎯 مزایای این سیستم

1. مدیریت متمرکز:

  • ✅ تمام ترجمه‌ها در یک مکان
  • ✅ راحتی به‌روزرسانی
  • ✅ کاهش تکرار

2. Type-Safe:

  • ✅ Extension با type-safety کامل
  • ✅ Autocomplete در IDE
  • ✅ Compile-time checking

3. Performance:

  • ✅ Cache در سمت client
  • ✅ دریافت یک‌باره ترجمه‌ها
  • ✅ Lazy loading

4. Scalability:

  • ✅ افزودن زبان جدید آسان است
  • ✅ افزودن نود جدید ساده است
  • ✅ صادرات به فرمت‌های مختلف

5. Developer Experience:

  • ✅ API ساده و واضح
  • ✅ مستندات کامل
  • ✅ اسکریپت‌های کمکی
  • ✅ Fallback برای ترجمه‌های گمشده

📦 فایل‌های ایجاد شده

فایل نوع توضیحات
workflow_translations.py Backend ترجمه‌های پایه
workflows.py (updated) Backend API endpoints
extract_workflow_translations.py Script استخراج و صادرات
workflow_translation_service.dart Frontend سرویس دریافت ترجمه
workflow_localizations_extension.dart Frontend Extension برای راحتی
workflow_node_config_dialog.dart (updated) Frontend استفاده از ترجمه در UI

✅ نتیجه‌گیری

سیستم i18n کامل و حرفه‌ای برای نودهای ورک‌فلو پیاده‌سازی شد که:

✅ 200+ رشته را پشتیبانی می‌کند
✅ 2 زبان (فارسی و انگلیسی)
✅ قابل توسعه برای زبان‌های بیشتر
✅ مدیریت آسان با اسکریپت‌های کمکی
✅ Type-Safe در Flutter
✅ Cache برای performance بهتر
✅ Fallback برای ترجمه‌های گمشده

این سیستم امکان ایجاد یک تجربه کاربری یکپارچه و چند زبانه را فراهم می‌کند! 🌍🎉