diff --git a/debug.log b/debug.log index 5347587..9b24c40 100644 --- a/debug.log +++ b/debug.log @@ -123,3 +123,14 @@ 2026-03-20 21:56:39 - Items case hit 2026-03-20 21:56:47 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} 2026-03-20 21:56:51 - Items case hit +2026-05-02 17:20:57 - Items case hit +2026-05-02 17:23:36 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:29:22 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:29:31 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:29:39 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:29:45 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:32:00 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:32:02 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:32:10 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:32:11 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} +2026-05-02 17:32:54 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true} diff --git a/includes/translation_helper.php b/includes/translation_helper.php new file mode 100644 index 0000000..bbf4bbd --- /dev/null +++ b/includes/translation_helper.php @@ -0,0 +1,181 @@ + false, + 'error' => 'No text provided', + ]; + } + + if (function_exists('mb_substr')) { + $cleanText = mb_substr($cleanText, 0, 500); + } else { + $cleanText = substr($cleanText, 0, 500); + } + + $skipAiUntil = isset($_SESSION['translation_ai_disabled_until']) ? (int) $_SESSION['translation_ai_disabled_until'] : 0; + $shouldTryAi = $skipAiUntil <= time(); + $aiResult = [ + 'success' => false, + 'error' => 'AI translation unavailable', + 'provider' => 'ai', + ]; + + if ($shouldTryAi) { + $aiResult = app_translate_text_with_ai($cleanText, $cleanTarget); + if (!empty($aiResult['success']) && !empty($aiResult['translated'])) { + unset($_SESSION['translation_ai_disabled_until']); + return $aiResult; + } + + $aiError = strtolower((string) ($aiResult['error'] ?? '')); + if ($aiError !== '' && (strpos($aiError, '401') !== false || strpos($aiError, 'unauthorized') !== false || strpos($aiError, 'timeout') !== false)) { + $_SESSION['translation_ai_disabled_until'] = time() + 900; + } + } + + $fallbackResult = app_translate_text_with_google($cleanText, $cleanTarget); + if (!empty($fallbackResult['success']) && !empty($fallbackResult['translated'])) { + if (!empty($aiResult['error']) && $aiResult['error'] !== 'AI translation unavailable') { + $fallbackResult['warning'] = (string) $aiResult['error']; + } + return $fallbackResult; + } + + if (!empty($aiResult['error']) && $aiResult['error'] !== 'AI translation unavailable') { + return $aiResult; + } + + return $fallbackResult; + } +} + +if (!function_exists('app_translate_text_with_ai')) { + function app_translate_text_with_ai(string $text, string $target): array + { + $prompt = "Translate the following product name to " . ($target === 'ar' ? 'Arabic' : 'English') . ". Return ONLY the translation, no extra text or explanations.\n\nText: " . $text; + + $resp = LocalAIApi::createResponse([ + 'input' => [ + ['role' => 'system', 'content' => 'You are a translation assistant. You translate product names between English and Arabic. Keep brand names, sizes, numbers, and SKUs accurate. Return only the translated text.'], + ['role' => 'user', 'content' => $prompt], + ], + ], [ + 'poll_interval' => 1, + 'poll_timeout' => 12, + 'timeout' => 20, + ]); + + if (!empty($resp['success'])) { + $translated = trim(LocalAIApi::extractText($resp)); + if ($translated !== '') { + return [ + 'success' => true, + 'translated' => $translated, + 'provider' => 'ai', + ]; + } + } + + return [ + 'success' => false, + 'error' => (string) ($resp['error'] ?? 'AI translation failed'), + 'provider' => 'ai', + ]; + } +} + +if (!function_exists('app_translate_text_with_google')) { + function app_translate_text_with_google(string $text, string $target): array + { + if (!function_exists('curl_init')) { + return [ + 'success' => false, + 'error' => 'Translation service unavailable: PHP cURL is missing', + 'provider' => 'fallback', + ]; + } + + $query = http_build_query([ + 'client' => 'gtx', + 'sl' => 'auto', + 'tl' => $target, + 'dt' => 't', + 'q' => $text, + ]); + $url = 'https://translate.googleapis.com/translate_a/single?' . $query; + + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 15, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_HTTPGET => true, + CURLOPT_FAILONERROR => false, + CURLOPT_HTTPHEADER => [ + 'Accept: application/json', + 'User-Agent: Mozilla/5.0', + ], + ]); + + $body = curl_exec($ch); + if ($body === false) { + $error = curl_error($ch) ?: 'Unknown translation request error'; + curl_close($ch); + return [ + 'success' => false, + 'error' => $error, + 'provider' => 'fallback', + ]; + } + + $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($status < 200 || $status >= 300 || $body === '' || $body === null) { + return [ + 'success' => false, + 'error' => 'Fallback translation request failed', + 'provider' => 'fallback', + ]; + } + + $decoded = json_decode($body, true); + if (!is_array($decoded) || empty($decoded[0]) || !is_array($decoded[0])) { + return [ + 'success' => false, + 'error' => 'Fallback translation returned an unexpected response', + 'provider' => 'fallback', + ]; + } + + $parts = []; + foreach ($decoded[0] as $segment) { + if (is_array($segment) && isset($segment[0]) && is_string($segment[0])) { + $parts[] = $segment[0]; + } + } + + $translated = trim(implode('', $parts)); + if ($translated === '') { + return [ + 'success' => false, + 'error' => 'Fallback translation returned empty text', + 'provider' => 'fallback', + ]; + } + + return [ + 'success' => true, + 'translated' => $translated, + 'provider' => 'fallback', + ]; + } +} diff --git a/index.php b/index.php index cd43f37..655c2a2 100644 --- a/index.php +++ b/index.php @@ -1112,30 +1112,27 @@ if (isset($_GET['action']) || isset($_POST['action'])) { if ($action === 'translate') { header('Content-Type: application/json'); - require_once __DIR__ . '/ai/LocalAIApi.php'; - $text = $_POST['text'] ?? ''; - $target = $_POST['target'] ?? 'ar'; - - if (empty($text)) { - echo json_encode(['success' => false, 'error' => 'No text provided']); + require_once __DIR__ . '/includes/translation_helper.php'; + + $text = trim((string) ($_POST['text'] ?? '')); + $target = strtolower(trim((string) ($_POST['target'] ?? ''))) === 'en' ? 'en' : 'ar'; + + if ($text === '') { + echo json_encode(['success' => false, 'error' => 'No text provided'], JSON_UNESCAPED_UNICODE); exit; } - $prompt = "Translate the following product name to " . ($target === 'ar' ? 'Arabic' : 'English') . ". Return ONLY the translation, no extra text or explanations.\n\nText: " . $text; - - $resp = LocalAIApi::createResponse([ - 'input' => [ - ['role' => 'system', 'content' => 'You are a translation assistant. You translate product names between English and Arabic.'], - ['role' => 'user', 'content' => $prompt], - ], - ]); - - if (!empty($resp['success'])) { - $translated = trim(LocalAIApi::extractText($resp)); - echo json_encode(['success' => true, 'translated' => $translated]); + $translationResult = app_translate_text($text, $target); + if (!empty($translationResult['success']) && !empty($translationResult['translated'])) { + echo json_encode([ + 'success' => true, + 'translated' => $translationResult['translated'], + 'provider' => $translationResult['provider'] ?? 'unknown', + ], JSON_UNESCAPED_UNICODE); } else { - error_log("Translation failed for text '$text': " . json_encode($resp)); - echo json_encode(['success' => false, 'error' => $resp['error'] ?? 'Translation failed']); + $errorMessage = (string) ($translationResult['error'] ?? 'Translation failed'); + error_log("Translation failed for text '$text': " . json_encode($translationResult, JSON_UNESCAPED_UNICODE)); + echo json_encode(['success' => false, 'error' => $errorMessage], JSON_UNESCAPED_UNICODE); } exit; } @@ -1407,11 +1404,18 @@ function getPromotionalPrice($item) { db()->query("UPDATE stock_items SET is_promotion = 0 WHERE is_promotion = 1 AND promotion_end IS NOT NULL AND promotion_end < '" . date('Y-m-d') . "'"); if (isset($_POST['add_category'])) { - db()->prepare("INSERT INTO stock_categories (name_en, name_ar, outlet_id) VALUES (?, ?, ?)")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', current_outlet_id()]); + $current_oid = current_outlet_id(); + db()->prepare("INSERT INTO stock_categories (name_en, name_ar, outlet_id) VALUES (?, ?, ?)") + ->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', $current_oid]); redirectWithMessage("Category added!"); } if (isset($_POST['add_unit'])) { - db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar, outlet_id) VALUES (?, ?, ?, ?, ?)")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', $_POST['short_en'] ?? '', $_POST['short_ar'] ?? '', current_outlet_id()]); + $current_oid = current_outlet_id(); + $unitNameEn = trim((string)($_POST['name_en'] ?? '')); + $unitNameAr = trim((string)($_POST['name_ar'] ?? '')); + if ($unitNameAr === '') $unitNameAr = $unitNameEn; + db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar, outlet_id) VALUES (?, ?, ?, ?, ?)") + ->execute([$unitNameEn, $unitNameAr, $unitNameEn, $unitNameAr, $current_oid]); redirectWithMessage("Unit added!"); } @@ -1424,7 +1428,10 @@ function getPromotionalPrice($item) { redirectWithMessage("Category deleted!"); } if (isset($_POST['edit_unit'])) { - db()->prepare("UPDATE stock_units SET name_en = ?, name_ar = ?, short_name_en = ?, short_name_ar = ? WHERE id = ?")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', $_POST['short_en'] ?? '', $_POST['short_ar'] ?? '', (int)$_POST['id']]); + $unitNameEn = trim((string)($_POST['name_en'] ?? '')); + $unitNameAr = trim((string)($_POST['name_ar'] ?? '')); + if ($unitNameAr === '') $unitNameAr = $unitNameEn; + db()->prepare("UPDATE stock_units SET name_en = ?, name_ar = ?, short_name_en = ?, short_name_ar = ? WHERE id = ?")->execute([$unitNameEn, $unitNameAr, $unitNameEn, $unitNameAr, (int)$_POST['id']]); redirectWithMessage("Unit updated!"); } if (isset($_POST['delete_unit'])) { @@ -2089,13 +2096,14 @@ function getPromotionalPrice($item) { } if (isset($rows[0][0]) && stripos($rows[0][0], 'name') !== false) array_shift($rows); + $current_oid = current_outlet_id(); foreach ($rows as $row) { if (empty($row[0])) continue; $name_en = trim((string)$row[0]); $name_ar = trim((string)($row[1] ?? $name_en)); db()->prepare("INSERT INTO stock_categories (name_en, name_ar, outlet_id) VALUES (?, ?, ?)") - ->execute([$name_en, $name_ar, current_outlet_id()]); + ->execute([$name_en, $name_ar, $current_oid]); $count++; } redirectWithMessage("Import categories completed! $count processed.", "index.php?page=categories"); @@ -2121,12 +2129,11 @@ function getPromotionalPrice($item) { foreach ($rows as $row) { if (empty($row[0])) continue; $name_en = trim((string)$row[0]); - $name_ar = trim((string)($row[1] ?? $name_en)); - $short_en = trim((string)($row[2] ?? '')); - $short_ar = trim((string)($row[3] ?? '')); + $name_ar = trim((string)($row[1] ?? '')); + if ($name_ar === '') $name_ar = $name_en; db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar, outlet_id) VALUES (?, ?, ?, ?, ?)") - ->execute([$name_en, $name_ar, $short_en, $short_ar, current_outlet_id()]); + ->execute([$name_en, $name_ar, $name_en, $name_ar, current_outlet_id()]); $count++; } redirectWithMessage("Import units completed! $count processed.", "index.php?page=units"); @@ -3678,12 +3685,14 @@ if ($page === 'export') { $headers = ['LPO #', 'Supplier', 'Date', 'Total', 'Status']; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row; } elseif ($type === 'categories') { - $stmt = db()->query("SELECT id, name_en, name_ar FROM stock_categories WHERE outlet_id = ".current_outlet_id()." ORDER BY id DESC"); + $stmt = db()->prepare("SELECT id, name_en, name_ar FROM stock_categories WHERE outlet_id = ? ORDER BY id DESC"); + $stmt->execute([current_outlet_id()]); $headers = ['ID', 'Name (EN)', 'Name (AR)']; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row; } elseif ($type === 'units') { - $stmt = db()->query("SELECT id, name_en, name_ar, short_name_en, short_name_ar FROM stock_units WHERE outlet_id = ".current_outlet_id()." ORDER BY id DESC"); - $headers = ['ID', 'Name (EN)', 'Name (AR)', 'Short (EN)', 'Short (AR)']; + $stmt = db()->prepare("SELECT id, name_en, name_ar FROM stock_units WHERE outlet_id = ? ORDER BY id DESC"); + $stmt->execute([current_outlet_id()]); + $headers = ['ID', 'Name (EN)', 'Name (AR)']; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row; } elseif ($type === 'sales_returns') { $salesReturnReferenceColumn = sales_return_reference_column(); @@ -3736,9 +3745,16 @@ if ($page === 'export') { } // Global data for modals -$data['categories'] = db()->query("SELECT * FROM stock_categories WHERE outlet_id = ".current_outlet_id()." ORDER BY name_en ASC")->fetchAll(); -$data['units'] = db()->query("SELECT * FROM stock_units WHERE outlet_id = ".current_outlet_id()." ORDER BY name_en ASC")->fetchAll(); -$data['suppliers'] = db()->query("SELECT * FROM suppliers WHERE outlet_id = ".current_outlet_id()." ORDER BY name ASC")->fetchAll(); +$current_oid = current_outlet_id(); +$stmt = db()->prepare("SELECT * FROM stock_categories WHERE outlet_id = ? ORDER BY name_en ASC"); +$stmt->execute([$current_oid]); +$data['categories'] = $stmt->fetchAll(); +$stmt = db()->prepare("SELECT * FROM stock_units WHERE outlet_id = ? ORDER BY name_en ASC"); +$stmt->execute([$current_oid]); +$data['units'] = $stmt->fetchAll(); +$stmt = db()->prepare("SELECT * FROM suppliers WHERE outlet_id = ? ORDER BY name ASC"); +$stmt->execute([$current_oid]); +$data['suppliers'] = $stmt->fetchAll(); $data['accounts'] = db()->query("SELECT * FROM acc_accounts ORDER BY code ASC")->fetchAll(); $data['customers_list'] = db()->query("SELECT * FROM customers ORDER BY name ASC")->fetchAll(); $customers = $data['customers_list']; // For backward compatibility in some modals @@ -3861,7 +3877,7 @@ switch ($page) { $data['current_page'] = $page_num; $oid = current_outlet_id(); - $stmt = db()->prepare("SELECT i.*, i.stock_quantity, c.name_en as cat_en, c.name_ar as cat_ar, u.short_name_en as unit_en, u.short_name_ar as unit_ar, s.name as supplier_name + $stmt = db()->prepare("SELECT i.*, i.stock_quantity, c.name_en as cat_en, c.name_ar as cat_ar, COALESCE(NULLIF(u.name_en, ''), u.short_name_en) as unit_en, COALESCE(NULLIF(u.name_ar, ''), u.short_name_ar, u.name_en, u.short_name_en) as unit_ar, s.name as supplier_name FROM stock_items i LEFT JOIN stock_categories c ON i.category_id = c.id @@ -5895,231 +5911,152 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; - -
-
-
-
-
- Stock setup -
Units built for clean inventory, receipts, and invoices
-

Keep names bilingual and short labels compact so item forms, tables, and printouts stay consistent.

-
-
- - - Export - - - - -
-
-
-
- Total units - - Reusable across items and reports -
-
- Arabic-ready - - 0 ? $unitsArabicReady . ' / ' . $unitsTotal : '0 / 0' ?> -
-
- Receipt-ready - - Has both short labels -
-
-
+
+
+
+
Units ()
+

Manage the English and Arabic unit names used across items, invoices, and reports.

+
+ +
+ + Export + + + +
+ +
-
-
-
-
Units catalog
-

Use clear full names and compact short codes for faster item entry.

-
- Tip: keep short labels under 6 characters -
-
- - - - - - - - - - - - - - - - - - - - - - + + + + +
UnitShort labelsReadinessActions
-
-
-
No units yet
-

Add your first unit so items can reuse the same labels in invoices and reports.

- - - -
-
-
- - -
-
-
- - -
-
-
- Bilingual - Receipt ready -
-
-
- - - - -
- - -
- -
+
+ + + + + + + + + + + + + + + + + + + + - - - - -
Name (EN)Name (AR)Actions
+
+
+
No units yet
+

Add your first unit so items can reuse the same labels in invoices and reports.

+ + + +
+
+
+ + + + +
+ + +
+ +
-
-
- + + +
@@ -6311,7 +6248,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
13-digit scale barcodes with the reserved prefixes are not allowed here. Save the 5-digit scale item code instead.
-
+
@@ -11320,7 +11257,7 @@ function loadSessionReport(id) {
Reserved 13-digit scale barcodes cannot be saved here. Use the 5-digit scale item code for weighing products.
-
+
@@ -11479,9 +11416,9 @@ function loadSessionReport(id) {
Supported columns
    -
  • Name (EN), Name (AR), Short (EN), Short (AR)
  • +
  • Name (EN), Name (AR)
  • CSV and XLSX files are accepted.
  • -
  • Keep short labels concise for invoices and receipts.
  • +
  • If Arabic is blank, the English name will be reused automatically.
@@ -11548,7 +11485,7 @@ function loadSessionReport(id) {
Create unit -

Use full bilingual names and short labels that stay readable on invoices and item cards.

+

Use the full unit name in English and Arabic so stock screens stay consistent.

@@ -11589,30 +11526,16 @@ function loadSessionReport(id) {
-
Short labels
-

Keep these compact for receipts, tables, and barcode labels.

+
Name preview
+

This is how the unit name will appear across the app.

- Receipts + Live label
-
-
- - -
Best for item tables and invoice lines.
-
-
- - -
Keep it easy to read when printed.
-
-
-
+
Example preview
Kilogram - Kg كيلوغرام - كجم
diff --git a/post_debug.log b/post_debug.log index ef61d0a..cb7a48b 100644 --- a/post_debug.log +++ b/post_debug.log @@ -174,3 +174,13 @@ 2026-05-02 04:56:12 - POST: {"username":"admin","password":"admin","login":"1"} 2026-05-02 04:56:19 - POST: {"username":"admin","password":"admin","login":"1"} 2026-05-02 04:56:42 - POST: {"id":"3","name_en":"Bank Transfer","name_ar":"\u062a\u062d\u0648\u064a\u0644 \u0628\u0646\u0643\u064a","edit_payment_method":""} +2026-05-02 17:20:51 - POST: {"name_en":"piece","name_ar":"\u062d\u0628\u0629","add_category":""} +2026-05-02 17:23:36 - POST: {"action":"translate","text":"piece","target":"ar"} +2026-05-02 17:29:22 - POST: {"action":"translate","text":"piece","target":"ar"} +2026-05-02 17:29:31 - POST: {"action":"translate","text":"piece","target":"ar"} +2026-05-02 17:29:44 - POST: {"name_en":"piece","name_ar":"\u062d\u0628\u0629","add_unit":""} +2026-05-02 17:32:00 - POST: {"action":"translate","text":"piece","target":"ar"} +2026-05-02 17:32:02 - POST: {"action":"translate","text":"%D9%82%D8%B7%D8%B9%D8%A9","target":"en"} +2026-05-02 17:32:10 - POST: {"action":"translate","text":"\u0642\u0637\u0639\u0629","target":"en"} +2026-05-02 17:32:11 - POST: {"action":"translate","text":"LAMING PINEAPPLE 454G","target":"ar"} +2026-05-02 17:32:54 - POST: {"action":"translate","text":"onion","target":"ar"}