1303 lines
50 KiB
HTML
1303 lines
50 KiB
HTML
<!doctype html>
|
|
<html lang="fa" dir="rtl">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>مدیریت منابع زبانی xFramework</title>
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
body {
|
|
font-family: "Segoe UI", Tahoma, sans-serif;
|
|
background: linear-gradient(135deg, #1e1e2f 0%, #2a2a3e 100%);
|
|
color: #eee;
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
header {
|
|
text-align: center;
|
|
padding: 30px 20px;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border-radius: 16px;
|
|
margin-bottom: 30px;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
header h1 {
|
|
font-size: 2.2em;
|
|
margin-bottom: 10px;
|
|
color: #7ec8ff;
|
|
}
|
|
header p {
|
|
opacity: 0.8;
|
|
}
|
|
header .meta {
|
|
margin-top: 12px;
|
|
font-size: 0.85em;
|
|
opacity: 0.6;
|
|
}
|
|
.container {
|
|
max-width: 1500px;
|
|
margin: 0 auto;
|
|
}
|
|
.section {
|
|
background: rgba(255, 255, 255, 0.04);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
margin-bottom: 24px;
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
}
|
|
.section h2 {
|
|
margin-bottom: 20px;
|
|
color: #7ec8ff;
|
|
border-bottom: 2px solid rgba(126, 200, 255, 0.3);
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
/* Source Selection */
|
|
.source-tabs {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-bottom: 20px;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
padding: 6px;
|
|
border-radius: 10px;
|
|
}
|
|
.source-tab {
|
|
flex: 1;
|
|
padding: 12px 16px;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
font-weight: 600;
|
|
transition: all 0.2s;
|
|
border: 1px solid transparent;
|
|
font-size: 14px;
|
|
}
|
|
.source-tab:hover {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
}
|
|
.source-tab.active {
|
|
background: rgba(126, 200, 255, 0.15);
|
|
border-color: rgba(126, 200, 255, 0.4);
|
|
color: #7ec8ff;
|
|
}
|
|
.source-content {
|
|
display: none;
|
|
}
|
|
.source-content.active {
|
|
display: block;
|
|
}
|
|
|
|
/* Upload */
|
|
.upload-section {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border: 2px dashed rgba(126, 200, 255, 0.3);
|
|
border-radius: 12px;
|
|
padding: 24px;
|
|
text-align: center;
|
|
transition: all 0.3s;
|
|
}
|
|
.upload-section:hover {
|
|
border-color: rgba(126, 200, 255, 0.6);
|
|
background: rgba(0, 0, 0, 0.3);
|
|
}
|
|
.upload-btn {
|
|
display: inline-block;
|
|
padding: 10px 24px;
|
|
background: #7ec8ff;
|
|
color: #1e1e2f;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
transition: all 0.2s;
|
|
}
|
|
.upload-btn:hover {
|
|
background: #5ab0f0;
|
|
transform: translateY(-2px);
|
|
}
|
|
.upload-status {
|
|
margin-top: 12px;
|
|
font-size: 13px;
|
|
min-height: 18px;
|
|
}
|
|
.upload-status.success {
|
|
color: #4caf50;
|
|
}
|
|
.upload-status.error {
|
|
color: #f44336;
|
|
}
|
|
|
|
/* Language Management */
|
|
.lang-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
margin-bottom: 16px;
|
|
}
|
|
.lang-chip {
|
|
background: rgba(126, 200, 255, 0.1);
|
|
border: 1px solid rgba(126, 200, 255, 0.3);
|
|
padding: 8px 16px;
|
|
border-radius: 20px;
|
|
font-size: 13px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.lang-chip .remove-lang {
|
|
cursor: pointer;
|
|
color: #f44336;
|
|
font-weight: bold;
|
|
margin-right: 4px;
|
|
}
|
|
.add-lang-form {
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
margin-top: 12px;
|
|
padding: 12px;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 8px;
|
|
}
|
|
.add-lang-form input,
|
|
.add-lang-form select {
|
|
padding: 8px 12px;
|
|
background: rgba(255, 255, 255, 0.08);
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 6px;
|
|
color: #fff;
|
|
font-size: 13px;
|
|
flex: 1;
|
|
min-width: 120px;
|
|
}
|
|
|
|
/* Identifier Editor Layout */
|
|
.editor-layout {
|
|
display: grid;
|
|
grid-template-columns: 350px 1fr;
|
|
gap: 20px;
|
|
}
|
|
@media (max-width: 900px) {
|
|
.editor-layout {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* Identifier List */
|
|
.id-list-container {
|
|
max-height: 500px;
|
|
overflow-y: auto;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 10px;
|
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
}
|
|
.id-list-container::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
.id-list-container::-webkit-scrollbar-track {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 4px;
|
|
}
|
|
.id-list-container::-webkit-scrollbar-thumb {
|
|
background: rgba(126, 200, 255, 0.4);
|
|
border-radius: 4px;
|
|
}
|
|
.id-item {
|
|
padding: 10px 14px;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
cursor: pointer;
|
|
transition: background 0.2s;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 13px;
|
|
font-family: monospace;
|
|
}
|
|
.id-item:hover {
|
|
background: rgba(126, 200, 255, 0.1);
|
|
}
|
|
.id-item.active {
|
|
background: rgba(126, 200, 255, 0.2);
|
|
border-right: 3px solid #7ec8ff;
|
|
}
|
|
.id-item .warning-badge {
|
|
background: #ff9800;
|
|
color: #000;
|
|
font-size: 10px;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* Edit Form */
|
|
.edit-form {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
}
|
|
.edit-row {
|
|
margin-bottom: 16px;
|
|
}
|
|
.edit-row label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
color: #a8d0ff;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
}
|
|
.edit-row input,
|
|
.edit-row textarea {
|
|
width: 100%;
|
|
padding: 10px 12px;
|
|
background: rgba(255, 255, 255, 0.08);
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 6px;
|
|
color: #fff;
|
|
font-size: 14px;
|
|
font-family: "Segoe UI", Tahoma, sans-serif;
|
|
}
|
|
.edit-row input:focus,
|
|
.edit-row textarea:focus {
|
|
outline: none;
|
|
border-color: #7ec8ff;
|
|
background: rgba(255, 255, 255, 0.12);
|
|
}
|
|
.edit-row textarea {
|
|
min-height: 80px;
|
|
resize: vertical;
|
|
}
|
|
|
|
/* JSON Output */
|
|
.json-output {
|
|
background: #0d1117;
|
|
border-radius: 8px;
|
|
padding: 14px;
|
|
max-height: 380px;
|
|
overflow: auto;
|
|
font-family: "Consolas", monospace;
|
|
font-size: 11px;
|
|
color: #c9d1d9;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
direction: ltr;
|
|
text-align: left;
|
|
white-space: pre;
|
|
}
|
|
.json-output::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
.json-output::-webkit-scrollbar-track {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
}
|
|
.json-output::-webkit-scrollbar-thumb {
|
|
background: rgba(126, 200, 255, 0.4);
|
|
border-radius: 4px;
|
|
}
|
|
.tabs {
|
|
display: flex;
|
|
gap: 4px;
|
|
margin-bottom: 12px;
|
|
}
|
|
.tab {
|
|
padding: 8px 16px;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border-radius: 6px 6px 0 0;
|
|
cursor: pointer;
|
|
border: 1px solid transparent;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
}
|
|
.tab.active {
|
|
background: rgba(126, 200, 255, 0.15);
|
|
border-color: rgba(126, 200, 255, 0.3);
|
|
color: #7ec8ff;
|
|
}
|
|
|
|
/* Action Buttons */
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
margin-top: 16px;
|
|
padding-top: 16px;
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
.btn {
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
font-size: 13px;
|
|
transition: all 0.2s;
|
|
}
|
|
.btn-primary {
|
|
background: #7ec8ff;
|
|
color: #1e1e2f;
|
|
}
|
|
.btn-primary:hover {
|
|
background: #5ab0f0;
|
|
}
|
|
.btn-success {
|
|
background: #4caf50;
|
|
color: #fff;
|
|
}
|
|
.btn-success:hover {
|
|
background: #3d9140;
|
|
}
|
|
.btn-warning {
|
|
background: #ff9800;
|
|
color: #fff;
|
|
}
|
|
.btn-warning:hover {
|
|
background: #e08600;
|
|
}
|
|
.btn-info {
|
|
background: #2196f3;
|
|
color: #fff;
|
|
}
|
|
.btn-info:hover {
|
|
background: #1976d2;
|
|
}
|
|
.btn-danger {
|
|
background: #f44336;
|
|
color: #fff;
|
|
}
|
|
.btn-danger:hover {
|
|
background: #d32f2f;
|
|
}
|
|
|
|
.search-box {
|
|
width: 100%;
|
|
padding: 10px 14px;
|
|
background: rgba(255, 255, 255, 0.08);
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 8px;
|
|
color: #fff;
|
|
font-size: 14px;
|
|
margin-bottom: 12px;
|
|
}
|
|
.search-box:focus {
|
|
outline: none;
|
|
border-color: #7ec8ff;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>🌐 مدیریت منابع زبانی xFramework</h1>
|
|
<p>
|
|
مدیریت، ویرایش و اعتبارسنجی فایلهای ترجمه برای برنامههای کاربردی مبتنی
|
|
بر xFramework
|
|
</p>
|
|
<div class="meta">
|
|
توسعهدهنده: هادی خزاعی اصل | شرکت: فن آوران ساحر علم | تاریخ: ۱ شهریور
|
|
۱۴۰۵
|
|
</div>
|
|
</header>
|
|
|
|
<div class="container">
|
|
<!-- Source Section -->
|
|
<div class="section">
|
|
<h2>📂 منبع دادهها</h2>
|
|
<div class="source-tabs">
|
|
<div class="source-tab active" onclick="switchSource('default')">
|
|
📚 منابع پیشفرض سیستم
|
|
</div>
|
|
<div class="source-tab" onclick="switchSource('upload')">
|
|
📁 بارگذاری فایل JSON
|
|
</div>
|
|
</div>
|
|
|
|
<div id="source-default" class="source-content active">
|
|
<p style="opacity: 0.7; font-size: 14px; padding: 10px">
|
|
✅ در حال حاضر از منابع پیشفرض xFramework استفاده میشود. میتوانید
|
|
مستقیماً ویرایش را شروع کنید.
|
|
</p>
|
|
</div>
|
|
|
|
<div id="source-upload" class="source-content">
|
|
<div class="upload-section">
|
|
<label for="jsonFileInput" class="upload-btn"
|
|
>📁 انتخاب فایل JSON منابع</label
|
|
>
|
|
<input
|
|
type="file"
|
|
id="jsonFileInput"
|
|
accept=".json"
|
|
style="display: none"
|
|
onchange="handleFileUpload(event)"
|
|
/>
|
|
<div id="uploadStatus" class="upload-status"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Language Management Section -->
|
|
<div class="section">
|
|
<h2>🌍 مدیریت زبانها</h2>
|
|
<div class="lang-list" id="langList"></div>
|
|
|
|
<div class="add-lang-form">
|
|
<input
|
|
type="text"
|
|
id="newLangName"
|
|
placeholder="نام زبان (مثال: arabic)"
|
|
/>
|
|
<input
|
|
type="text"
|
|
id="newLangLocale"
|
|
placeholder="لوکال (مثال: ar-SA)"
|
|
/>
|
|
<input
|
|
type="text"
|
|
id="newLangCode"
|
|
placeholder="کد زبان (مثال: ar)"
|
|
/>
|
|
<select id="newLangDir">
|
|
<option value="rtl">RTL</option>
|
|
<option value="ltr">LTR</option>
|
|
</select>
|
|
<button class="btn btn-primary" onclick="addNewLanguage()">
|
|
➕ افزودن زبان
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Identifier Editor Section -->
|
|
<div class="section">
|
|
<h2>✏️ ویرایش شناسهها و ترجمهها</h2>
|
|
<div class="editor-layout">
|
|
<!-- Left: Identifier List -->
|
|
<div>
|
|
<input
|
|
type="text"
|
|
class="search-box"
|
|
id="idSearch"
|
|
placeholder="🔍 جستجوی شناسه..."
|
|
oninput="renderIdList()"
|
|
/>
|
|
<div class="id-list-container" id="idListContainer"></div>
|
|
<button
|
|
class="btn btn-info"
|
|
style="width: 100%; margin-top: 12px"
|
|
onclick="addNewIdentifier()"
|
|
>
|
|
➕ ایجاد شناسه جدید
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Right: Edit Form -->
|
|
<div class="edit-form" id="editForm">
|
|
<p style="opacity: 0.5; text-align: center; padding: 40px">
|
|
یک شناسه را از لیست سمت راست انتخاب کنید تا ترجمههای آن را ویرایش
|
|
کنید.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- JSON Output & Actions -->
|
|
<div class="section">
|
|
<h2>📄 خروجی JSON</h2>
|
|
<div class="tabs">
|
|
<div class="tab active" onclick="switchJsonTab('full')" id="tab-full">
|
|
📄 Full JSON
|
|
</div>
|
|
</div>
|
|
<pre class="json-output" id="jsonOutput"></pre>
|
|
|
|
<div class="action-buttons">
|
|
<button class="btn btn-success" onclick="downloadJSON()">
|
|
⬇️ دانلود JSON
|
|
</button>
|
|
<button class="btn btn-warning" onclick="copyJSON()">
|
|
📋 کپی JSON
|
|
</button>
|
|
<button class="btn btn-danger" onclick="resetToDefault()">
|
|
🔄 بازنشانی به پیشفرض
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// ============ Default Data ============
|
|
const DEFAULT_RESOURCES = [
|
|
{
|
|
name: "persian",
|
|
locale: "fa-IR",
|
|
language: "fa",
|
|
direction: "rtl",
|
|
resources: [
|
|
{ id: "app_name", value: "ساحر علم" },
|
|
{ id: "company", value: "فن آوران ساحر علم" },
|
|
{ id: "id", value: "شناسه" },
|
|
{ id: "file_name", value: "نام فایل" },
|
|
{ id: "error", value: "خطا" },
|
|
{ id: "required", value: "الزامی است." },
|
|
{ id: "loading", value: "بارگزاری" },
|
|
{ id: "username", value: "نام کاربری" },
|
|
{ id: "password", value: "کلمه عبور" },
|
|
{ id: "first_name", value: "نام" },
|
|
{ id: "last_name", value: "نام خانوادگی" },
|
|
{ id: "verified", value: "تایید شده" },
|
|
{ id: "unverified", value: "تایید نشده" },
|
|
{ id: "thumbnail", value: "پیش نمایش" },
|
|
{ id: "language", value: "زبان" },
|
|
{ id: "take_screenshot", value: "تصویر برداری ازمحیط" },
|
|
{ id: "enable_capture_mode", value: "فعالسازی حالت تصویر برداری" },
|
|
{
|
|
id: "disable_capture_mode",
|
|
value: "غیرفعالسازی حالت تصویر برداری",
|
|
},
|
|
{ id: "be", value: "باشد" },
|
|
{ id: "not_to_be", value: "نباشد" },
|
|
{ id: "enabled", value: "فعال" },
|
|
{ id: "disabled", value: "غیر فعال" },
|
|
{ id: "banned", value: "مسدود" },
|
|
{ id: "un_banned", value: "غیر مسدود" },
|
|
{ id: "audio", value: "صوت" },
|
|
{ id: "video", value: "فیلم" },
|
|
{ id: "cover_image", value: "تصویر" },
|
|
{ id: "image", value: "تصویر" },
|
|
{ id: "avatar", value: "شکلک" },
|
|
{ id: "unknown", value: "ناشناس" },
|
|
{ id: "location", value: "موقعیت جغرافیای" },
|
|
{ id: "are_you_sure", value: "اطمینان دارید ؟" },
|
|
{ id: "success_msg", value: "عملیات با موفقیت انجام شد" },
|
|
{
|
|
id: "locked_msg",
|
|
value:
|
|
"برای ترک این صفحه باید فعالیت جاری را لغو کرده و یا به اتمام برسانید",
|
|
},
|
|
{ id: "empty_content", value: "خالی است" },
|
|
{ id: "no_item", value: "محتوایی ثبت نشده است" },
|
|
{ id: "drag_drop_here", value: "محل رها کردن فایل" },
|
|
{ id: "map_dialog_title", value: "نمایش موقعیت مکانی" },
|
|
{ id: "map_dialog_select_title", value: "انتخاب موقعیت جغرافیایی" },
|
|
{ id: "color_picker_dialog_title", value: "انتخاب رنگ" },
|
|
{ id: "dismiss", value: "رد کردن" },
|
|
{ id: "accept", value: "می پذیرم" },
|
|
{ id: "ok", value: "بسیار خب" },
|
|
{ id: "save", value: "ذخیره" },
|
|
{ id: "cancel", value: "لغو" },
|
|
{ id: "retry", value: "سعی مجدد" },
|
|
{ id: "edit", value: "ویرایش" },
|
|
{ id: "reset", value: "باز نشاندن" },
|
|
{ id: "change", value: "تغییر" },
|
|
{ id: "start", value: "آغاز" },
|
|
{ id: "yes", value: "بله" },
|
|
{ id: "no", value: "خیر" },
|
|
{ id: "verify", value: "تایید کردن" },
|
|
{ id: "confirm", value: "تاییدیه" },
|
|
{ id: "crop", value: "برش" },
|
|
{ id: "clear", value: "پاک کردن" },
|
|
{ id: "set", value: "تنظیم" },
|
|
{ id: "select", value: "انتخاب" },
|
|
{ id: "send", value: "ارسال" },
|
|
{ id: "show", value: "نمایش دادن" },
|
|
{ id: "hide", value: "پنهان کردن" },
|
|
{ id: "search", value: "جستجو" },
|
|
{ id: "delete", value: "حذف" },
|
|
{ id: "clone", value: "نمونه گیری" },
|
|
{ id: "select_all", value: "انتخاب همه" },
|
|
{ id: "select_file", value: "انتخاب فایل(ها)" },
|
|
{ id: "select_all_group", value: "انتخاب همه موارد فیلتر شده" },
|
|
{ id: "deselect_all", value: "لغو همه انتخاب ها" },
|
|
{
|
|
id: "deselect_all_group",
|
|
value: "لغو انتخاب همه موارد فیلتر شده",
|
|
},
|
|
{ id: "inverse_selection", value: "انتخاب معکوس" },
|
|
{ id: "selected_location", value: "انتخاب محل" },
|
|
{ id: "preset_colors", value: "رنگ های پیش فرض" },
|
|
{ id: "copy_to_clipboard", value: "ثبت در حافظه موقت" },
|
|
{ id: "color", value: "رنگ" },
|
|
{ id: "colors", value: "رنگها" },
|
|
{ id: "upload", value: "بارگزاری" },
|
|
{ id: "import", value: "وارد کردن" },
|
|
{ id: "continue", value: "ادامه" },
|
|
{ id: "finish", value: "پایان" },
|
|
{ id: "enable", value: "فعال" },
|
|
{ id: "disable", value: "غیر فعال" },
|
|
{ id: "all", value: "همه" },
|
|
{ id: "new", value: "جدید" },
|
|
{ id: "add", value: "افزودن" },
|
|
{ id: "update", value: "بروز رسانی" },
|
|
{ id: "details", value: "جزئیات" },
|
|
{ id: "translation", value: "ترجمه" },
|
|
{ id: "download", value: "دریافت" },
|
|
{ id: "list", value: "فهرست" },
|
|
{ id: "options", value: "انتخاب ها" },
|
|
{ id: "descriptions", value: "توضیحات" },
|
|
{ id: "type", value: "نوع" },
|
|
{ id: "media", value: "رسانه" },
|
|
{ id: "medias", value: "رسانه ها" },
|
|
{ id: "summary", value: "خلاصه" },
|
|
{ id: "go", value: "رفتن" },
|
|
{ id: "input", value: "ورود" },
|
|
{ id: "preview", value: "پیش نمایش" },
|
|
{ id: "close", value: "بستن" },
|
|
{ id: "undo", value: "لغو کردن" },
|
|
{ id: "redo", value: "انجام دوباره" },
|
|
{ id: "refresh", value: "بازیابی" },
|
|
{ id: "submit", value: "ثبت" },
|
|
{ id: "first", value: "نخستین" },
|
|
{ id: "last", value: "آخرین" },
|
|
{ id: "previous", value: "قبلی" },
|
|
{ id: "next", value: "بعدی" },
|
|
{ id: "back", value: "بازگشت" },
|
|
{ id: "x_markdown_preview", value: "پیش نمایش" },
|
|
{ id: "x_markdown_image", value: "تصویر" },
|
|
{ id: "x_markdown_link", value: "پیوند" },
|
|
{ id: "x_markdown_ol", value: "فهرست ترتیبی" },
|
|
{ id: "x_markdown_ul", value: "فهرست بدون ترتیب" },
|
|
{ id: "x_markdown_quote", value: "نقل قول" },
|
|
{ id: "x_markdown_code", value: "کد منبع" },
|
|
{ id: "x_markdown_heading", value: "عنوان" },
|
|
{ id: "x_markdown_italic", value: "ایتالیک" },
|
|
{ id: "x_markdown_bold", value: "پر رنگ" },
|
|
{ id: "x_markdown_guide", value: "راهنمای نگارش" },
|
|
{ id: "theme", value: "پوسته" },
|
|
{ id: "theme_mode", value: "حالت نمایشی" },
|
|
{ id: "theme_dark", value: "تیره" },
|
|
{ id: "theme_light", value: "روشن" },
|
|
{ id: "theme_name", value: "نام پوسته" },
|
|
{ id: "theme_name_description", value: "نام پوسته را وارد کنید" },
|
|
{ id: "theme_clone_title", value: "نمونه گیری از پوسته" },
|
|
{ id: "input_color_title", value: "تعیین رنگ" },
|
|
{ id: "input_color_description", value: "مقدار رنگ را وارد کنید" },
|
|
{ id: "ide_mode", value: "نوع کد" },
|
|
{ id: "ide_format", value: "حالت دهی کد" },
|
|
{ id: "print", value: "چاپ" },
|
|
{ id: "export_png", value: "خروجی PNG" },
|
|
{ id: "export_svg", value: "خروجی SVG" },
|
|
{ id: "export_csv", value: "خروجی CSV" },
|
|
{ id: "zoom_in", value: "بزرگنمایی" },
|
|
{ id: "zoom_out", value: "کوچک نمایی" },
|
|
{ id: "zoom_selection", value: "بزرگنمایی ناحیه" },
|
|
{ id: "x_axis", value: "محور افقی" },
|
|
{ id: "y_axis", value: "محور عمودی" },
|
|
{ id: "xy_axis", value: "محورهای عمودی و افقی" },
|
|
{ id: "pan", value: "حرکت آزاد" },
|
|
{ id: "full_screen", value: "حالت تمام صفحه" },
|
|
{ id: "ex_100", value: "داده نا معتبر" },
|
|
{ id: "ex_101", value: "تاریخ نامعتبر" },
|
|
{ id: "ex_102", value: "درخواست اشتباه" },
|
|
{ id: "ex_103", value: "ورودی نامعتبر" },
|
|
{ id: "ex_104", value: "پیام نامعتبر" },
|
|
{ id: "ex_105", value: "نشانه نامعتبر" },
|
|
{ id: "ex_106", value: "کد اععتبارسنجی نامعتبر" },
|
|
{ id: "ex_107", value: "پست الکترونیک نامعتبر" },
|
|
{ id: "ex_108", value: "شماره تلفن همراه نامعتبر" },
|
|
{ id: "ex_109", value: "کلمه عبور نامعتبر" },
|
|
{ id: "ex_110", value: "آدرس اینترنتی نامعتبر" },
|
|
{ id: "ex_111", value: "دستگاه نامعتبر" },
|
|
{ id: "ex_112", value: "تصویر پروفایل نامعتبر" },
|
|
{ id: "ex_113", value: "فایل نامعتبر" },
|
|
{ id: "ex_114", value: "نام کاربری نامعتبر" },
|
|
{ id: "ex_200", value: "یافت نشد" },
|
|
{ id: "ex_201", value: "عدم تایید جواز" },
|
|
{ id: "ex_202", value: "سطح دسترسی نامعتبر" },
|
|
{ id: "ex_203", value: "غیر قابل نمایش" },
|
|
{ id: "ex_300", value: "حساب کاربری قفل شده است" },
|
|
{ id: "ex_301", value: "خطا در ورود" },
|
|
{ id: "ex_302", value: "کاربر قبلا ثبت نام کرده است" },
|
|
{ id: "ex_303", value: "کاربر غیرفعال است" },
|
|
{ id: "ex_304", value: "دستگاه شما قفل شده است تا : (0)" },
|
|
{
|
|
id: "ex_305",
|
|
value: "امکان درخواست کد اعتبارسنجی وجود ندارد تا : (0)",
|
|
},
|
|
{ id: "ex_306", value: "کلمه عبورها یکسان هستند" },
|
|
{ id: "ex_307", value: "شماره های تلفن یکسان هستند" },
|
|
{ id: "ex_308", value: "آدرس های پست الکترونیک یکسان هستند" },
|
|
{ id: "ex_309", value: "شماره تلفن قبلا ثبت شده است" },
|
|
{ id: "ex_310", value: "پست الکترونیک قبلا ثبت شده است" },
|
|
{ id: "ex_311", value: "تلفن همراه تایید نشده است" },
|
|
{ id: "ex_312", value: "پست الکترونیک تایید نشده است" },
|
|
{ id: "ex_313", value: "مطلب ممنوعه" },
|
|
{ id: "ex_314", value: "کاربر ممنوعه" },
|
|
{ id: "ex_400", value: "خطای نا شناخته" },
|
|
{ id: "ex_401", value: "مورد تکراری" },
|
|
{ id: "ex_402", value: "شکست عملیات" },
|
|
{ id: "ex_403", value: "تاریخ مصرف منقضی شده است" },
|
|
{ id: "ex_404", value: "غیر قابل اسفاده" },
|
|
{ id: "ex_405", value: "تعداد برچسب ها بیش از جد مجاز است" },
|
|
{ id: "ex_406", value: "اتمام مهلت زمانی" },
|
|
{ id: "ex_500", value: "پرونده خالی است" },
|
|
{ id: "ex_501", value: "اندازه بیش از حد مجاز" },
|
|
{ id: "ex_502", value: "گونه نامعتبر فایل" },
|
|
{ id: "ex_503", value: "تعداد رسانه ها بیش از حد مجاز است" },
|
|
{ id: "ex_600", value: "پیکربندی اشتباه" },
|
|
{ id: "ex_601", value: "خطا در پیکربندی سامانه ذخیره سازی" },
|
|
{ id: "ex_602", value: "خطا در پیکربندی سامانه ذخیره سازی" },
|
|
{ id: "ex_900", value: "تعداد فایل ها بیش از حد مجاز است" },
|
|
{ id: "ex_901", value: "مجموع اندازه فایل ها کمتر از حد مجاز است" },
|
|
{ id: "ex_902", value: "مجموع اندازه فایل ها بیش از حد مجاز است" },
|
|
{ id: "ex_903", value: "شرایط و ضوابط باید پذیرفته شوند" },
|
|
{ id: "ex_904", value: "عدم پشتیبانی از سیستم موقعیت یاب" },
|
|
{
|
|
id: "ex_905",
|
|
value: "مجوز دسترسی به موقعیت خود را صادر نکرده اید",
|
|
},
|
|
{
|
|
id: "ex_906",
|
|
value: "دسترسی به موقعیت جغرافیایی امکان پذیر نیست",
|
|
},
|
|
{ id: "ex_907", value: "عدم پشتیبانی از سیستم ذخیره سازی" },
|
|
{
|
|
id: "ex_908",
|
|
value: "برای دسترسی به محتوا باید از سامانه خارج شوید",
|
|
},
|
|
{ id: "ex_909", value: "زبان انتخاب شده نامعتبر است" },
|
|
{ id: "ex_910", value: "داده نامعتبر برای انتخاب کاربر" },
|
|
],
|
|
},
|
|
{
|
|
name: "english",
|
|
locale: "en-US",
|
|
language: "en",
|
|
direction: "ltr",
|
|
resources: [
|
|
{ id: "app_name", value: "SaherElm" },
|
|
{ id: "company", value: "SaherElm IT Center" },
|
|
{ id: "id", value: "ID" },
|
|
{ id: "file_name", value: "File Name" },
|
|
{ id: "error", value: "Error" },
|
|
{ id: "required", value: "required" },
|
|
{ id: "loading", value: "loading" },
|
|
{ id: "username", value: "Username" },
|
|
{ id: "password", value: "Password" },
|
|
{ id: "first_name", value: "Firstname" },
|
|
{ id: "last_name", value: "Lastname" },
|
|
{ id: "verified", value: "Verified" },
|
|
{ id: "unverified", value: "Unverified" },
|
|
{ id: "thumbnail", value: "Thumbnail" },
|
|
{ id: "language", value: "Language" },
|
|
{ id: "take_screenshot", value: "Take Screenshot" },
|
|
{ id: "enable_capture_mode", value: "Enable Capture Mode" },
|
|
{ id: "disable_capture_mode", value: "Disable Capture Mode" },
|
|
{ id: "be", value: "Be" },
|
|
{ id: "not_to_be", value: "Not to be" },
|
|
{ id: "enabled", value: "Enabled" },
|
|
{ id: "disabled", value: "Disabled" },
|
|
{ id: "banned", value: "Banned" },
|
|
{ id: "un_banned", value: "UnBanned" },
|
|
{ id: "audio", value: "Audio" },
|
|
{ id: "video", value: "Video" },
|
|
{ id: "cover_image", value: "Cover Image" },
|
|
{ id: "image", value: "Image" },
|
|
{ id: "avatar", value: "Avatar" },
|
|
{ id: "unknown", value: "Unknown" },
|
|
{ id: "location", value: "Location" },
|
|
{ id: "are_you_sure", value: "Are You Sure ?" },
|
|
{ id: "success_msg", value: "Operation done Successfully ..." },
|
|
{
|
|
id: "locked_msg",
|
|
value: "You have to finish/cancel current proccess for leaving",
|
|
},
|
|
{ id: "empty_content", value: "Empty" },
|
|
{ id: "no_item", value: "there is no Content" },
|
|
{ id: "drag_drop_here", value: "Drop File Here" },
|
|
{ id: "map_dialog_title", value: "Show Location" },
|
|
{ id: "map_dialog_select_title", value: "Select Location" },
|
|
{ id: "color_picker_dialog_title", value: "Select Color" },
|
|
{ id: "dismiss", value: "dismiss" },
|
|
{ id: "accept", value: "Accept" },
|
|
{ id: "ok", value: "Ok" },
|
|
{ id: "save", value: "Save" },
|
|
{ id: "cancel", value: "Cancel" },
|
|
{ id: "retry", value: "Retry" },
|
|
{ id: "edit", value: "Edit" },
|
|
{ id: "reset", value: "Reset" },
|
|
{ id: "change", value: "Change" },
|
|
{ id: "start", value: "Start" },
|
|
{ id: "yes", value: "Yes" },
|
|
{ id: "no", value: "No" },
|
|
{ id: "verify", value: "Verify" },
|
|
{ id: "confirm", value: "Confirm" },
|
|
{ id: "crop", value: "Crop" },
|
|
{ id: "clear", value: "Clear" },
|
|
{ id: "set", value: "Set" },
|
|
{ id: "select", value: "Select" },
|
|
{ id: "send", value: "Send" },
|
|
{ id: "show", value: "Show" },
|
|
{ id: "hide", value: "Hide" },
|
|
{ id: "search", value: "Search" },
|
|
{ id: "delete", value: "Delete" },
|
|
{ id: "clone", value: "Clone" },
|
|
{ id: "select_all", value: "Select All" },
|
|
{ id: "select_file", value: "Select File(s)" },
|
|
{ id: "select_all_group", value: "Select All Filtered Items" },
|
|
{ id: "deselect_all", value: "Deselect All" },
|
|
{ id: "deselect_all_group", value: "Deselect All Filtered Items" },
|
|
{ id: "inverse_selection", value: "Inverse Selection" },
|
|
{ id: "selected_location", value: "Selected Location" },
|
|
{ id: "preset_colors", value: "Preset Colors" },
|
|
{ id: "copy_to_clipboard", value: "Copy to Clipboard" },
|
|
{ id: "color", value: "Color" },
|
|
{ id: "colors", value: "Colors" },
|
|
{ id: "upload", value: "Upload" },
|
|
{ id: "import", value: "Import" },
|
|
{ id: "continue", value: "Continue" },
|
|
{ id: "finish", value: "Finish" },
|
|
{ id: "enable", value: "Enable" },
|
|
{ id: "disable", value: "Disable" },
|
|
{ id: "all", value: "All" },
|
|
{ id: "new", value: "New" },
|
|
{ id: "add", value: "Add" },
|
|
{ id: "update", value: "Update" },
|
|
{ id: "details", value: "Details" },
|
|
{ id: "translation", value: "Translation" },
|
|
{ id: "download", value: "Download" },
|
|
{ id: "list", value: "List" },
|
|
{ id: "options", value: "Options" },
|
|
{ id: "descriptions", value: "Description" },
|
|
{ id: "type", value: "Type" },
|
|
{ id: "media", value: "Media" },
|
|
{ id: "medias", value: "Medias" },
|
|
{ id: "summary", value: "Summary" },
|
|
{ id: "go", value: "Go" },
|
|
{ id: "input", value: "Input" },
|
|
{ id: "preview", value: "Preview" },
|
|
{ id: "close", value: "Close" },
|
|
{ id: "undo", value: "Undo" },
|
|
{ id: "redo", value: "Redo" },
|
|
{ id: "refresh", value: "Refresh" },
|
|
{ id: "submit", value: "Submit" },
|
|
{ id: "first", value: "First" },
|
|
{ id: "last", value: "Last" },
|
|
{ id: "previous", value: "Previous" },
|
|
{ id: "next", value: "Next" },
|
|
{ id: "back", value: "Back" },
|
|
{ id: "x_markdown_preview", value: "Preview" },
|
|
{ id: "x_markdown_image", value: "Image" },
|
|
{ id: "x_markdown_link", value: "Link" },
|
|
{ id: "x_markdown_ol", value: "Ordered List" },
|
|
{ id: "x_markdown_ul", value: "Unordered List" },
|
|
{ id: "x_markdown_quote", value: "Quote" },
|
|
{ id: "x_markdown_code", value: "Code" },
|
|
{ id: "x_markdown_heading", value: "Heading" },
|
|
{ id: "x_markdown_italic", value: "Italic" },
|
|
{ id: "x_markdown_bold", value: "Bold" },
|
|
{ id: "x_markdown_guide", value: "Markdown Guide" },
|
|
{ id: "theme", value: "Theme" },
|
|
{ id: "theme_mode", value: "Theme Mode" },
|
|
{ id: "theme_dark", value: "Dark" },
|
|
{ id: "theme_light", value: "Light" },
|
|
{ id: "theme_name", value: "Theme Name" },
|
|
{ id: "theme_name_description", value: "Enter Theme name" },
|
|
{ id: "theme_clone_title", value: "Theme Cloning" },
|
|
{ id: "input_color_title", value: "Color Value" },
|
|
{ id: "input_color_description", value: "input color value" },
|
|
{ id: "ide_mode", value: "Code Style" },
|
|
{ id: "ide_format", value: "Code Formatting" },
|
|
{ id: "print", value: "Print" },
|
|
{ id: "export_png", value: "Export PNG" },
|
|
{ id: "export_svg", value: "Export SVG" },
|
|
{ id: "export_csv", value: "Export CSV" },
|
|
{ id: "zoom_in", value: "Zoom In" },
|
|
{ id: "zoom_out", value: "Zoom Out" },
|
|
{ id: "zoom_selection", value: "Zoom Selection" },
|
|
{ id: "x_axis", value: "X Axis" },
|
|
{ id: "y_axis", value: "Y Axis" },
|
|
{ id: "xy_axis", value: "XY Axis" },
|
|
{ id: "pan", value: "Pan" },
|
|
{ id: "full_screen", value: "Full Screen" },
|
|
{ id: "ex_100", value: "Invalid Data ..." },
|
|
{ id: "ex_101", value: "Invalid Date ..." },
|
|
{ id: "ex_102", value: "Bad Request ..." },
|
|
{ id: "ex_103", value: "Invalid Args ..." },
|
|
{ id: "ex_104", value: "Invalid Message ..." },
|
|
{ id: "ex_105", value: "Invalid Token ..." },
|
|
{ id: "ex_106", value: "Invalid Verification Code ..." },
|
|
{ id: "ex_107", value: "Invalid Email Address ..." },
|
|
{ id: "ex_108", value: "Invalid Mobile Number ..." },
|
|
{ id: "ex_109", value: "Invalid Password ..." },
|
|
{ id: "ex_110", value: "Invalid URL ..." },
|
|
{ id: "ex_111", value: "Invalid Device ..." },
|
|
{ id: "ex_112", value: "Invalid Profile Image ..." },
|
|
{ id: "ex_113", value: "Invalid File ..." },
|
|
{ id: "ex_114", value: "Invalid Username ..." },
|
|
{ id: "ex_200", value: "Not Found ..." },
|
|
{ id: "ex_201", value: "Not Allowed ..." },
|
|
{ id: "ex_202", value: "Not Authorized ..." },
|
|
{ id: "ex_203", value: "Not Visible ..." },
|
|
{ id: "ex_300", value: "Account Locked out ..." },
|
|
{ id: "ex_301", value: "Login Failed ..." },
|
|
{ id: "ex_302", value: "User Registered Before ..." },
|
|
{ id: "ex_303", value: "User Disabled ..." },
|
|
{ id: "ex_304", value: "Device Banned Until:(0)" },
|
|
{
|
|
id: "ex_305",
|
|
value: "You can not Request Verification code Until:(0)",
|
|
},
|
|
{ id: "ex_306", value: "Passwords Same ..." },
|
|
{ id: "ex_307", value: "Mobiles Same ..." },
|
|
{ id: "ex_308", value: "Emails Same ..." },
|
|
{ id: "ex_309", value: "Mobile In Used ..." },
|
|
{ id: "ex_310", value: "Email In Used ..." },
|
|
{ id: "ex_311", value: "Mobile Not Confirmed ..." },
|
|
{ id: "ex_312", value: "Email Not Confirmed ..." },
|
|
{ id: "ex_313", value: "Post Banned ..." },
|
|
{ id: "ex_314", value: "User Banned ..." },
|
|
{ id: "ex_400", value: "Unknown Error ..." },
|
|
{ id: "ex_401", value: "Duplicate ..." },
|
|
{ id: "ex_402", value: "Action Failed ..." },
|
|
{ id: "ex_403", value: "Expired Date ..." },
|
|
{ id: "ex_404", value: "Unavailable ..." },
|
|
{ id: "ex_405", value: "Max Allowed Tag Exceeded ..." },
|
|
{ id: "ex_406", value: "Timeout ..." },
|
|
{ id: "ex_500", value: "Empty File ..." },
|
|
{ id: "ex_501", value: "max File size exceeded ..." },
|
|
{ id: "ex_502", value: "Unsupported File Type ..." },
|
|
{ id: "ex_503", value: "Max Allowed Media Exceeded ..." },
|
|
{ id: "ex_600", value: "Invalid Configuration ..." },
|
|
{
|
|
id: "ex_601",
|
|
value: "Storage Service Initiallization Failed ...",
|
|
},
|
|
{
|
|
id: "ex_602",
|
|
value: "Message Service Initialization Failed ...",
|
|
},
|
|
{ id: "ex_900", value: "Max Allowed Files Exceeded ..." },
|
|
{ id: "ex_901", value: "Min Files Size Exceeded ..." },
|
|
{ id: "ex_902", value: "Max Files Size Exceeded ..." },
|
|
{ id: "ex_903", value: "Must Accept Terms And Conditions ..." },
|
|
{
|
|
id: "ex_904",
|
|
value: "Browser does not support Location Services ...",
|
|
},
|
|
{
|
|
id: "ex_905",
|
|
value: "You have rejected access to your Location ...",
|
|
},
|
|
{ id: "ex_906", value: "Unable to determine your Location ..." },
|
|
{
|
|
id: "ex_907",
|
|
value: "Browser does not support Storage Services ...",
|
|
},
|
|
{ id: "ex_908", value: "Must Logout to access content ..." },
|
|
{ id: "ex_909", value: "Invalid Locale ..." },
|
|
{ id: "ex_910", value: "Invalid User Selector ..." },
|
|
],
|
|
},
|
|
];
|
|
|
|
// ============ State ============
|
|
let resourcesData = JSON.parse(JSON.stringify(DEFAULT_RESOURCES));
|
|
let selectedId = null;
|
|
let jsonTab = "full";
|
|
|
|
// ============ Initialization ============
|
|
function init() {
|
|
renderLanguages();
|
|
renderIdList();
|
|
renderJson();
|
|
}
|
|
|
|
// ============ Language Management ============
|
|
function renderLanguages() {
|
|
const container = document.getElementById("langList");
|
|
container.innerHTML = "";
|
|
resourcesData.forEach((lang, index) => {
|
|
const chip = document.createElement("div");
|
|
chip.className = "lang-chip";
|
|
chip.innerHTML = `
|
|
<strong>${lang.name}</strong> (${lang.locale})
|
|
${resourcesData.length > 1 ? `<span class="remove-lang" onclick="removeLanguage(${index})" title="حذف زبان">✕</span>` : ""}
|
|
`;
|
|
container.appendChild(chip);
|
|
});
|
|
}
|
|
|
|
function addNewLanguage() {
|
|
const name = document.getElementById("newLangName").value.trim();
|
|
const locale = document.getElementById("newLangLocale").value.trim();
|
|
const code = document.getElementById("newLangCode").value.trim();
|
|
const dir = document.getElementById("newLangDir").value;
|
|
|
|
if (!name || !locale || !code) {
|
|
alert("لطفاً تمام فیلدهای زبان جدید را پر کنید.");
|
|
return;
|
|
}
|
|
|
|
if (resourcesData.find((l) => l.name === name)) {
|
|
alert("زبانی با این نام از قبل وجود دارد.");
|
|
return;
|
|
}
|
|
|
|
// Create empty resources for all existing IDs
|
|
const allIds = getAllIdentifiers();
|
|
const newResources = allIds.map((id) => ({ id, value: "" }));
|
|
|
|
resourcesData.push({
|
|
name,
|
|
locale,
|
|
language: code,
|
|
direction: dir,
|
|
resources: newResources,
|
|
});
|
|
|
|
document.getElementById("newLangName").value = "";
|
|
document.getElementById("newLangLocale").value = "";
|
|
document.getElementById("newLangCode").value = "";
|
|
|
|
renderLanguages();
|
|
renderIdList();
|
|
renderJson();
|
|
alert(`زبان ${name} با موفقیت اضافه شد.`);
|
|
}
|
|
|
|
function removeLanguage(index) {
|
|
if (
|
|
confirm(`آیا از حذف زبان ${resourcesData[index].name} اطمینان دارید؟`)
|
|
) {
|
|
resourcesData.splice(index, 1);
|
|
renderLanguages();
|
|
renderIdList();
|
|
renderJson();
|
|
if (selectedId) selectIdentifier(selectedId);
|
|
}
|
|
}
|
|
|
|
// ============ Identifier Management ============
|
|
function getAllIdentifiers() {
|
|
const ids = new Set();
|
|
resourcesData.forEach((lang) => {
|
|
lang.resources.forEach((res) => ids.add(res.id));
|
|
});
|
|
return Array.from(ids).sort();
|
|
}
|
|
|
|
function renderIdList() {
|
|
const container = document.getElementById("idListContainer");
|
|
const search = document.getElementById("idSearch").value.toLowerCase();
|
|
container.innerHTML = "";
|
|
|
|
const allIds = getAllIdentifiers();
|
|
const filteredIds = allIds.filter((id) =>
|
|
id.toLowerCase().includes(search),
|
|
);
|
|
|
|
filteredIds.forEach((id) => {
|
|
const item = document.createElement("div");
|
|
item.className = `id-item ${selectedId === id ? "active" : ""}`;
|
|
|
|
// Check for missing or empty translations
|
|
let hasMissing = false;
|
|
resourcesData.forEach((lang) => {
|
|
const res = lang.resources.find((r) => r.id === id);
|
|
if (!res || !res.value.trim()) {
|
|
hasMissing = true;
|
|
}
|
|
});
|
|
|
|
item.innerHTML = `
|
|
<span>${id}</span>
|
|
${hasMissing ? '<span class="warning-badge" title="فاقد ترجمه در یک یا چند زبان">⚠️</span>' : ""}
|
|
`;
|
|
item.onclick = () => selectIdentifier(id);
|
|
container.appendChild(item);
|
|
});
|
|
}
|
|
|
|
function selectIdentifier(id) {
|
|
selectedId = id;
|
|
renderIdList(); // to update active state
|
|
|
|
const formContainer = document.getElementById("editForm");
|
|
formContainer.innerHTML = `<h3 style="margin-bottom: 16px; color: #7ec8ff;">ویرایش شناسه: <span style="font-family: monospace; color: #fff;">${id}</span></h3>`;
|
|
|
|
resourcesData.forEach((lang, langIndex) => {
|
|
const res = lang.resources.find((r) => r.id === id);
|
|
const currentValue = res ? res.value : "";
|
|
|
|
const row = document.createElement("div");
|
|
row.className = "edit-row";
|
|
row.innerHTML = `
|
|
<label>${lang.name} (${lang.locale}) ${!res ? '<span style="color:#f44336">(جدید - فاقد مقدار)</span>' : ""}</label>
|
|
<textarea
|
|
data-lang-index="${langIndex}"
|
|
data-res-id="${id}"
|
|
oninput="updateTranslation(this)"
|
|
placeholder="ترجمه را وارد کنید..."
|
|
>${currentValue}</textarea>
|
|
`;
|
|
formContainer.appendChild(row);
|
|
});
|
|
|
|
const addBtn = document.createElement("button");
|
|
addBtn.className = "btn btn-primary";
|
|
addBtn.style.width = "100%";
|
|
addBtn.style.marginTop = "12px";
|
|
addBtn.textContent = "💾 ذخیره تغییرات و بهروزرسانی JSON";
|
|
addBtn.onclick = () => {
|
|
renderJson();
|
|
renderIdList(); // update warning badges
|
|
alert("تغییرات با موفقیت ذخیره شد.");
|
|
};
|
|
formContainer.appendChild(addBtn);
|
|
}
|
|
|
|
function updateTranslation(textarea) {
|
|
const langIndex = parseInt(textarea.dataset.langIndex);
|
|
const id = textarea.dataset.resId;
|
|
const newValue = textarea.value;
|
|
|
|
const lang = resourcesData[langIndex];
|
|
const resIndex = lang.resources.findIndex((r) => r.id === id);
|
|
|
|
if (resIndex !== -1) {
|
|
lang.resources[resIndex].value = newValue;
|
|
} else {
|
|
lang.resources.push({ id, value: newValue });
|
|
}
|
|
}
|
|
|
|
function addNewIdentifier() {
|
|
const newId = prompt("شناسه جدید را وارد کنید (مثال: my_new_key):");
|
|
if (!newId || !newId.trim()) return;
|
|
|
|
const trimmedId = newId.trim();
|
|
if (getAllIdentifiers().includes(trimmedId)) {
|
|
alert("این شناسه از قبل وجود دارد.");
|
|
return;
|
|
}
|
|
|
|
// Add to all languages with empty value
|
|
resourcesData.forEach((lang) => {
|
|
lang.resources.push({ id: trimmedId, value: "" });
|
|
});
|
|
|
|
renderIdList();
|
|
renderJson();
|
|
selectIdentifier(trimmedId);
|
|
}
|
|
|
|
// ============ JSON & Actions ============
|
|
function switchJsonTab(tab) {
|
|
jsonTab = tab;
|
|
document
|
|
.querySelectorAll(".tab")
|
|
.forEach((t) => t.classList.remove("active"));
|
|
document.getElementById(`tab-${tab}`).classList.add("active");
|
|
renderJson();
|
|
}
|
|
|
|
function renderJson() {
|
|
const output = document.getElementById("jsonOutput");
|
|
if (jsonTab === "full") {
|
|
output.textContent = JSON.stringify(resourcesData, null, 2);
|
|
}
|
|
}
|
|
|
|
function downloadJSON() {
|
|
const blob = new Blob([JSON.stringify(resourcesData, null, 2)], {
|
|
type: "application/json",
|
|
});
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = "x-framework-translations.json";
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
function copyJSON() {
|
|
navigator.clipboard
|
|
.writeText(JSON.stringify(resourcesData, null, 2))
|
|
.then(() => alert("✅ JSON در کلیپبورد کپی شد."))
|
|
.catch(() => alert("❌ خطا در کپی کردن."));
|
|
}
|
|
|
|
function resetToDefault() {
|
|
if (
|
|
confirm(
|
|
"آیا اطمینان دارید؟ تمام تغییرات شما از بین خواهد رفت و به حالت پیشفرض بازمیگردد.",
|
|
)
|
|
) {
|
|
resourcesData = JSON.parse(JSON.stringify(DEFAULT_RESOURCES));
|
|
selectedId = null;
|
|
document.getElementById("editForm").innerHTML =
|
|
'<p style="opacity: 0.5; text-align: center; padding: 40px;">یک شناسه را از لیست سمت راست انتخاب کنید تا ترجمههای آن را ویرایش کنید.</p>';
|
|
renderLanguages();
|
|
renderIdList();
|
|
renderJson();
|
|
}
|
|
}
|
|
|
|
// ============ File Upload ============
|
|
function handleFileUpload(event) {
|
|
const file = event.target.files[0];
|
|
if (!file) return;
|
|
|
|
const reader = new FileReader();
|
|
reader.onload = function (e) {
|
|
try {
|
|
const parsed = JSON.parse(e.target.result);
|
|
if (!Array.isArray(parsed)) {
|
|
throw new Error("فایل JSON باید یک آرایه از اشیاء زبان باشد.");
|
|
}
|
|
|
|
// Basic validation
|
|
parsed.forEach((lang) => {
|
|
if (
|
|
!lang.name ||
|
|
!lang.locale ||
|
|
!Array.isArray(lang.resources)
|
|
) {
|
|
throw new Error(
|
|
"ساختار هر زبان باید شامل name، locale و resources باشد.",
|
|
);
|
|
}
|
|
});
|
|
|
|
resourcesData = parsed;
|
|
selectedId = null;
|
|
document.getElementById("editForm").innerHTML =
|
|
'<p style="opacity: 0.5; text-align: center; padding: 40px;">یک شناسه را از لیست سمت راست انتخاب کنید تا ترجمههای آن را ویرایش کنید.</p>';
|
|
|
|
document.getElementById("uploadStatus").className =
|
|
"upload-status success";
|
|
document.getElementById("uploadStatus").textContent =
|
|
`✅ فایل با موفقیت بارگذاری شد. (${resourcesData.length} زبان)`;
|
|
|
|
renderLanguages();
|
|
renderIdList();
|
|
renderJson();
|
|
} catch (err) {
|
|
document.getElementById("uploadStatus").className =
|
|
"upload-status error";
|
|
document.getElementById("uploadStatus").textContent =
|
|
`❌ خطا: ${err.message}`;
|
|
}
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
|
|
function switchSource(source) {
|
|
document
|
|
.querySelectorAll(".source-tab")
|
|
.forEach((t) => t.classList.remove("active"));
|
|
event.target.classList.add("active");
|
|
document
|
|
.querySelectorAll(".source-content")
|
|
.forEach((c) => c.classList.remove("active"));
|
|
document.getElementById(`source-${source}`).classList.add("active");
|
|
}
|
|
|
|
// Start
|
|
init();
|
|
</script>
|
|
</body>
|
|
</html>
|